I am trying to build a view in my RCP application, the view just contains a TreeViewer.
The tree can contain folders or leafs, a folder can contain folders and leafs. when I add a folder to the back-end data model of the root folder, the UI is updated automatically, but if I add a folder to any branch folder, the UI will not be updated automatically. please tell me what's wrong with my code.
The model class:
public class TreeNode extends BindableObject {
private Folder parent;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
super.firePropertyChange("name", this.name, this.name = name);
}
public Folder getParent() {
return parent;
}
public void setParent(Folder parent) {
this.parent = parent;
}
}
public class Folder extends TreeNode {
private List<TreeNode> children = new ArrayList<TreeNode>();
public List<TreeNode> getChildren() {
return children;
}
public void setChildren(List<TreeNode> children) {
this.children = children;
}
public void add(TreeNode node){
children.add(node);
}
}
the view :
public class ExplorerView extends ViewPart {
private WritableList data;
private TreeViewer treeViewer;
public WritableList getData() {
return data;
}
public TreeViewer getViewer(){
return treeViewer;
}
public ExplorerView() {
// TODO Auto-generated constructor stub
}
#Override
public void createPartControl(Composite parent) {
parent.setLayout(new FillLayout(SWT.HORIZONTAL));
treeViewer = new TreeViewer(parent, SWT.BORDER);
treeViewer.setContentProvider(new ObservableListTreeContentProvider(new ExplorerObservableFactory(), new ExploerTreeStructureAdvisor()));
treeViewer.setLabelProvider(new ExplorerTreeLabelProvider());
ArrayList<TreeNode> list = new ArrayList<TreeNode>();
list.add(new ExplorerDataModel().getElements()[0]);
data = new WritableList(list, TreeNode.class);
treeViewer.setInput(data);
}
#Override
public void setFocus() {
}
}
the ObservableFactory:
public class ExplorerObservableFactory implements IObservableFactory {
#Override
public IObservable createObservable(Object target) {
System.out.println(target.getClass().getName());
if(target instanceof WritableList){
return (WritableList)target;
}
else if(target instanceof Folder){
List<TreeNode> children = ((Folder)target).getChildren();
return new WritableList(children, TreeNode.class);
}
return null;
}
}
the TreeStructureAdvisor:
public class ExploerTreeStructureAdvisor extends TreeStructureAdvisor {
#Override
public Object getParent(Object element) {
return ((TreeNode)element).getParent();
}
#Override
public Boolean hasChildren(Object element) {
if(element instanceof Folder){
return true;
}else{
return false;
}
}
}
the data :
public class ExplorerDataModel {
public TreeNode[] getElements() {
Folder f1 = new Folder();
f1.setName("Database Connections");
Folder f11 = new Folder();
f11.setName("Credit Test");
TreeNode t1 = new TreeNode();
t1.setName("bank#localhost");
f11.add(t1);
t1.setParent(f11);
TreeNode t2 = new TreeNode();
t2.setName("credit#localhost");
f11.add(t2);
t2.setParent(f11);
Folder f12 = new Folder();
f12.setName("Credit Product");
TreeNode t3 = new TreeNode();
t3.setName("nbcbcredit#localhost");
f12.add(t3);
t3.setParent(f12);
TreeNode t4 = new TreeNode();
t4.setName("nbcbcredit_bak#localhost");
f12.add(t4);
t4.setParent(f12);
f1.add(f11);
f11.setParent(f1);
f1.add(f12);
f12.setParent(f1);
return new TreeNode[] { f1 };
}
}
the test command handler:
public Object execute(ExecutionEvent event) throws ExecutionException {
ExplorerView v =(ExplorerView) HandlerUtil.getActiveWorkbenchWindow(event).getActivePage().findView("com.amarsoft.dmp.explorer.explorerView");
Folder f = new Folder();
f.setName("ODA Flat Files");
v.getData().add(f);
Folder f1 = (Folder) v.getData().get(0);
f1.setName("Database Connections (3)");
Folder f2 = new Folder();
f2.setName("Report Test");
f1.add(f2);
return null;
}
if I execute above command, the added folder "ODA Flat Files" will appear in the tree immediately, but the added folder "Report Test" will not be there, if call TreeViewer#refresh() everything is ok, but I want to know why.
Modifying your model does not notify your tree. Refresh is one way of telling the tree that the data has changed and it needs to update. If you go through the java doc for jface viewers in eclipse you will find the following quote
To handle structural changes, use the refresh methods instead.
It seems you misconfigured the data binding...
Have a look at the official snippets:
With a List Factory
With a Set Factory
With a more than a list as Factory
Have fun! (without any refresh ;-) )
ps. for EMF just look at this: http://tomsondev.bestsolution.at/2009/06/08/galileo-emf-databinding-%E2%80%93-part-3/
In your ExplorerObservableFactory replace
... else if(target instanceof Folder){
List<TreeNode> children = ((Folder)target).getChildren();
return new WritableList(children, TreeNode.class);
}
by following
else if(target instanceof Folder){
return BeansObservables.observeList(target, "children");
}
If you return WritableList here, the contentProvider's listener is registered on it (it should be registered on the Folder bean instead)
Related
I am facing a problem with the update of a list that is filtered depending on the value of a dropdown.
This is a description of my model:
I have a list of users
When I click on a user, another list of orders of this user is
displayed
The list of orders is filtered according to the value of a dropdown
containig a list of status
Please, see the image below:
Users With orders
The filter is working well, but the problem that I am facing is that once I choose an element from the dropdown, the list of orders is no longer updated when the user is changed.
This is a snippet of my code:
Construction of the Order Panel and instantiation of the list:
public OrdersPanel(String id)
{
super(id);
this.setOutputMarkupId(true);
setOutputMarkupPlaceholderTag(true);
IModel<List<Order>> orderListModel = new OrderListModel();
orderListView = new orderListView("orderListView", orderListModel);
//...
}
OrderListModel:
private final class OrderListModel extends LoadableDetachableModel<List<Order>> {
private static final long serialVersionUID = 1L;
#Override
protected List<Order> load() {
//...
//We set the variable allOrders in order to be used later in the filtering process
//...
}
}
Construction of the dropdown:
private class StatusDropDown extends CustomDropDown<String> implements IAjaxIndicatorAware {
private static final long serialVersionUID = 1L;
private StatusDropDown(String id) {
super(id);
this.setNullValid(true);
StatusListModel statusModel = new StatusListModel();
setChoices(statusModel);
setChoiceRenderer(new StatusChoiceRenderer(statusModel));
}
#Override
protected void onUpdate(AjaxRequestTarget target) {
super.onUpdate(target);
if (target != null) {
new StatusDropDownRefreshEvent(this, target).fire();
target.addComponent(this);
}
}
/**
* disable ajax marker for the form fields
*/
public String getAjaxIndicatorMarkupId() {
return null;
}
}
CustomDropDown (Must be used by the context of the project on which I am working):
public class CustomDropDown<V> extends DropDownChoice<V> {
private static final long serialVersionUID = 1L;
public CustomDropDown(String id) {
this(id, id);
}
public CustomDropDown(String id, String property) {
super(id);
setOutputMarkupId(true);
setModel(new CustomComponentPropertyModel<V>(property));
add(new AjaxFormComponentUpdatingBehavior("onchange") {
private static final long serialVersionUID = 1L;
#Override
protected void onUpdate(AjaxRequestTarget target) {
new UpdateEvent(CustomDropDown.this, target).fire();
if (target != null) {
target.addComponent(CustomDropDown.this);
}
CustomDropDown.this.onUpdate(target);
}
});
}
#Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
if (!isValid()) {
tag.append("class", "invalid", " ");
FeedbackMessage message = getFeedbackMessage();
if (message != null) {
tag.put("title", message.getMessage().toString());
message.markRendered();
}
} else if (isRequired()) {
tag.append("class", "red-background", " ");
}
}
public void setWidth(String width) {
this.add(new AttributeAppender("style", true, new Model<String>("width:" + width), ";"));
}
public CustomDropDown<V> setChoices(V... choices) {
this.setChoices(Arrays.asList(choices));
return this;
}
protected void onUpdate(AjaxRequestTarget target) {
}
}
StatusDropDownRefreshEvent listener:
#AjaxUpdateEventListener
public void statusDropDownRefreshPanel(StatusDropDownRefreshEvent event){
if (event.getTarget() != null) {
orderListView.setList(getOrdersByStatus(allOrders));
event.getTarget().addComponent(this);
}
}
Changing of the user:
When the user is changed, an update event is fired from the users panel, and then cached in the orders panel:
#AjaxUpdateEventListener
public void refreshPanel(CustomerOrderRefreshEvent event) {
if (event.getTarget() != null) {
event.getTarget().addComponent(this);
this.onBeforeRender();
}
}
onBeforeRender() to determin the visibility of the panel (if no order is available then the orders panel is not visible)
#Override
public void onBeforeRender() {
setVisibilityAllowed(checkVisibility());
super.onBeforeRender();
}
Finally, the checkVisibility Method:
private boolean checkVisibility() {
if (isUserChanged()) {
List<Order> src = orderListView.getModelObject();
statusDropDown.setDefaultModelObject(null);
return CollectionUtils.isNotEmpty(src);
}
return true;
}
My main problem is that the changing of the selected user doesn't update the list of orders once a status is chosen from the list.
Thank you very much for your replies and your time.
Best regards.
I found a solution to my problem.
The list of orders wasn't updated because The method getObject was called on the wrong object.
The call of the load() method can be done via getObject(), but the condition is: The object must be detached (See the implementation of getObject() at this link)
The detached object in my case is the orderListModel and not the orderListView, so this is what I added to my code:
//Set the content of the list model
List<Order> orders = orderListModel.getObject(); //This invokes the load method
//Update the content of the list
orderListView.setList(orders);
I tried to run my Eclipse RCP code to run in Eclipse RAP environment. In my Eclipse RCP code, there is functionality to add the rows in to table. But
adding the code does not work in Eclipse RAP. I am using TableViewer.
Following is my code.
public class BasicEntryPoint extends AbstractEntryPoint {
private static final int COLUMNS = 2;
private TableViewer viewer;
private class ViewContentProvider implements IStructuredContentProvider {
public Object[] getElements(Object inputElement) {
List<Person> list = (List<Person>) inputElement;
return list.toArray();
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
private class ViewLabelProvider extends LabelProvider implements
ITableLabelProvider {
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
public String getColumnText(Object element, int columnIndex) {
Person p = (Person) element;
if (columnIndex == 0) {
return p.getName();
}
return p.getPlace();
}
}
private class Person{
String name;
String place;
public void setName(String name) {
this.name = name;
}
public void setPlace(String place) {
this.place = place;
}
public String getName() {
return name;
}
public String getPlace() {
return place;
}
}
public List<Person> persons() {
List<Person> list = new ArrayList<Person>();
Person person = new Person();
person.setName("bb");
person.setPlace("jjj");
list.add(person);
person = new Person();
person.setName("sss");
person.setPlace("fff");
list.add(person);
return list;
}
#Override
protected void createContents(Composite parent) {
parent.setLayout(new GridLayout(2, false));
viewer = new TableViewer(parent, SWT.NONE);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
final Table table = viewer.getTable();
viewer.setColumnProperties(initColumnProperties(table));
viewer.setInput(persons());
viewer.getTable().setHeaderVisible(true);
Button checkbox = new Button(parent, SWT.CHECK);
checkbox.setText("Hello");
Button button = new Button(parent, SWT.PUSH);
button.setText("World");
button.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
System.out.println("Button clicked");
Person p = new Person();
p.setName("Dee");
p.setPlace("TCR");
persons().add(p);
String prop[] ={"name","place"};
viewer.update(p, prop);
//viewer.refresh();
}
});
}
private String[] initColumnProperties(Table table) {
String[] result = new String[COLUMNS];
for (int i = 0; i < COLUMNS; i++) {
TableColumn tableColumn = new TableColumn(table, SWT.NONE);
result[i] = "Column" + i;
tableColumn.setText(result[i]);
if (i == 2) {
tableColumn.setWidth(190);
} else {
tableColumn.setWidth(70);
}
}
return result;
}
}
You should use:
viewer.add(p);
rather than update to add a new item to a table (both for SWT and RAP).
You must also update your model to contain the new item.
I have searched around and been trying to figure out if I can use the editor framework with polymorphic types. I found this post at Using GWT Editors with a complex usecase which is close to what I am trying to do.
I am fairly new to the editor framework, so any help would be much appreciated.
For example, here is some of the code,
Data Transfer Objects:
public class Employee{
public List<Contact> contacts
}
public class Contact {
public class ContactEmail extends Contact {}
public class ContactAddress extends Contact {}
public class ContactPhoneNumber extends Contact {}
Editors:
public interface ContactBaseEditor<T extends Contact> extends Editor<T> {}
public class AddressEditor extends Composite implements Editor<ContactAddress>, ContactBaseEditor<ContactAddress>{}
public class EmailEditor extends Composite implements Editor<ContactEmail>, ContactBaseEditor<ContactEmail>{)
public class PhoneNumberEditor extends Composite implements Editor<ContactPhoneNumber>, ContactBaseEditor<ContactPhoneNumber>{}
ContactEditor class:
public class ContactEditor extends Composite implements IsEditor<ListEditor<Contact, ContactEditorWrapper>> {
private class ContactEditorSource extends EditorSource<ContactEditorWrapper> {
#Override
public ContactEditorWrapper create(final int index) {
ContactEditorWrapper contactEditor = new ContactEditorWrapper();
communicationContactsPanel.add(contactEditor);
return contactEditor;
}
#Override
public void dispose(ContactEditorWrapper subEditor) {
subEditor.removeFromParent();
}
#Override
public void setIndex(ContactEditorWrapper editor, int index) {
communicationContactsPanel.insert(editor, index);
}
}
private ListEditor<Contact, ContactEditorWrapper> editor = ListEditor.of(new ContactEditorSource());
public ListEditor<Contact, ContactEditorWrapper> asEditor() {
return editor;
}
}
ContactEditorWrapper:
class ContactEditorWrapper extends Composite implements ContactBaseEditor<Contact>, ValueAwareEditor<Contact> {
private SimplePanel panel = new SimplePanel();
#Path("") ContactBaseEditor<Contact> realEditor;
public ContactEditor() {
initWidget(panel);
}
#Override
public void setValue(Contact value) {
if (value instanceof Address) {
realEditor = new AddressEditor();
panel.setWidget((AddressEditor)realEditor);
}
else if (value instanceof Email) {
realEditor = new EmailEditor();
panel.setWidget((EmailEditor)realEditor);
}
else if (value instanceof PhoneNumber) {
realEditor = new PhoneNumberEditor();
panel.setWidget((PhoneNumberEditor)realEditor);
}
else {
realEditor = null;
}
}
}
Main Editor class:
public class AddEmployeeEditor extends Composite implements Editor<Employee> {
#UiField
ContactEditor contacts;
interface Driver extends SimpleBeanEditorDriver<Employee, AddEmployeeEditor> {
}
public AddEmployeeEditor(final Binder binder) {
driver = GWT.create(Driver.class);
driver.initialize(this);
List<Contact> list = new ArrayList<Contact>();
list.add(new Address());
list.add(new Email());
list.add(new PhoneNumber());
list.add(new PhoneNumber());
Employee employee = new Employee();
employee.setContacts(list);
driver.edit(employee);
}
}
Can anyone tell me if this will work, am I going in the right direction or ?
Thanks in advance,
Mac
I have updated the code above to now contain the ContactEditorWrapper class that Thomas advised.
That code has good chances to break: there's no guarantee that an editor returned by the EditorSource won't be used to edit another value in the list.
You should create a wrapper editor implementing ValueAwareEditor and with the actual editor as a child editor with #Path(""); and create the appropriate ContactBaseEditor in the setValue method.
class ContactEditor extends Composite implements ValueAwareEditor<Contact> {
private SimplePanel panel = new SimplePanel();
#Path("") ContactBaseEditor realEditor;
public ContactEditor() {
initWidget(panel);
}
#Override
public void setValue(Contact value) {
if (contact instanceof ContactAddress) {
realEditor = new AddressEditor();
}
else if (contact instanceof ContactEmail) {
realEditor = new EmailEditor();
}
else if (contact instanceof ContactPhoneNumber) {
realEditor = new PhoneNumberEditor();
}
else {
realEditor = null;
}
panel.setWidget(realEditor);
}
Note however that only the field/sub-editors from ContactBaseEditor will be edited, whichever the actual implementation being used. If there are additional fields/sub-editors in some ContactBaseEditor subclass, you'd have to implement ValueAwareEditor and handle things by-hand in the setValue and flush methods.
i have created a sample view in eclipse using the following code.i want the view to be
automatically refereshed.the part of code in quotes "" gives refresh option but it is done
manually.can anyone help me know how it can be done automatically
public class SampleView extends ViewPart {
public static final String ID = "tab.views.SampleView";
private TableViewer viewer;
class ViewContentProvider implements IStructuredContentProvider {
public void inputChanged(Viewer v, Object oldInput, Object newInput) {
}
public void dispose() {
}
public Object[] getElements(Object parent) {
return new String[] { "Status of your hudson build is: " +hudson.d};
}
}
class ViewLabelProvider extends LabelProvider implements ITableLabelProvider {
public String getColumnText(Object obj, int index) {
return getText(obj);
}
public Image getColumnImage(Object obj, int index) {
return getImage(obj);
}
public Image getImage(Object obj) {
return PlatformUI.getWorkbench().
getSharedImages().getImage(ISharedImages.IMG_OBJ_ADD);
}
}
public SampleView() {
}
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());
viewer.setInput(getViewSite());
PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer.getControl(), "Tab.viewer");
hookContextMenu();
}
" private void hookContextMenu() {
MenuManager menuMgr = new MenuManager("#PopupMenu");
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
Action refresh =new Action() {
public void run() {
// initialize();
viewer.refresh();
}
};
refresh.setText("Refresh");
menuMgr.add(refresh);
}"
public void setFocus() {
viewer.getControl().setFocus();
}
}
It is only possible to refresh the tree contents automatically, if you fill it using JFace Data Binding, that would not work with remote build results.
I recommend either using a model with notification support: when the model changes, its listeners are notified. Then your view could listen for these notifications and refresh itself.
If for some reason this is not possible, you have to poll your models manually. For that I recommend creating a Job that is executed in the background automatically (its last step is to reschedule itself some times later), that checks whether the model changed and refreshes the view.
does anyone have any examples of how to using Places without using activities for history management. I knocked something up quickly and can see the url changing with browser-back and browser-forward clicks but the display doesn't go anywhere.
I'm using a DecoratedTabPanel and have a SelectionHandler that fires off getPlaceController().goTo(place).
Any ideas would be useful.
Here is a simple piece of code that I've made to demonstrate what you expected. It's based on the GWT and MVP Development document (GWT and MVP)
In this example you navigate between two tabs. On selection, a new history item is created (without any activity). As long as you use browser buttons to go back/forward the page will be updated correctly.
I have defined one place, one activity and its view. I've adjusted AppActivityMapper, AppActivityManager and ClientFactory to my needs. The code is lightweight and doesn't need comments to be understood. I've only put some explanations when it was needed, but if it's not clear do not hesitate to ask.
ExampleView.java
public interface ExampleView extends IsWidget {
void selectTab(int index);
}
ExampleViewImpl.java
public class ExampleViewImpl extends Composite implements ExampleView, SelectionHandler<Integer> {
private DecoratedTabPanel panel;
public ExampleViewImpl() {
panel = new DecoratedTabPanel();
initComposite();
initWidget(panel);
}
private void initComposite() {
panel.add(new HTML("Content 1"), "Tab 1");
panel.add(new HTML("Content 2"), "Tab 2");
panel.selectTab(0);
panel.addSelectionHandler(this);
}
#Override
public void selectTab(int index) {
if (index >=0 && index < panel.getWidgetCount()) {
if (index != panel.getTabBar().getSelectedTab()) {
panel.selectTab(index);
}
}
}
#Override
public void onSelection(SelectionEvent<Integer> event) {
// Fire an history event corresponding to the tab selected
switch (event.getSelectedItem()) {
case 0:
History.newItem("thetabplace:1");
break;
case 1:
History.newItem("thetabplace:2");
break;
}
}
}
ClientFactory.java
public class ClientFactory {
private final EventBus eventBus = new SimpleEventBus();
private final PlaceController placeController = new PlaceController(eventBus);
private final ExampleViewImpl example = new ExampleViewImpl();
public EventBus getEventBus() {
return this.eventBus;
}
public PlaceController getPlaceController() {
return this.placeController;
}
public ExampleViewImpl getExampleView() {
return example;
}
}
ExampleActivity.java
public class ExampleActivity extends AbstractActivity {
private ExampleView view;
private ClientFactory factory;
public ExampleActivity(ExamplePlace place, ClientFactory factory) {
// Get the factory reference
this.factory = factory;
// Get the reference to the view
view = this.factory.getExampleView();
// Select the tab corresponding to the token value
if (place.getToken() != null) {
// By default the first tab is selected
if (place.getToken().equals("") || place.getToken().equals("1")) {
view.selectTab(0);
} else if (place.getToken().equals("2")) {
view.selectTab(1);
}
}
}
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
// Attach this view to the application container
panel.setWidget(view);
}
}
ExamplePlace.java
/**
* Just an very basic place
*/
public class ExamplePlace extends Place {
// The token corresponding to an action
private String token;
// This place should use a token to identify a view behavior
public ExamplePlace(String token) {
this.token = token;
}
// Return the current token
public String getToken() {
return token;
}
// Custom prefix to break the default name : ExamplePlace
// So that the history token will be thetabplace:token
// and not any more : ExamplePlace:token
#Prefix(value="thetabplace")
public static class Tokenizer implements PlaceTokenizer<ExamplePlace> {
#Override
public String getToken(ExamplePlace place) {
return place.getToken();
}
#Override
public ExamplePlace getPlace(String token) {
return new ExamplePlace(token);
}
}
}
AppActivityMapper.java
public class AppActivityMapper implements ActivityMapper {
private ClientFactory clientFactory;
public AppActivityMapper(ClientFactory clientFactory) {
super();
this.clientFactory = clientFactory;
}
#Override
public Activity getActivity(Place place) {
if (place instanceof ExamplePlace) {
return new ExampleActivity((ExamplePlace) place, clientFactory);
}
return null;
}
}
AppPlaceHistoryMapper.java
#WithTokenizers({ExamplePlace.Tokenizer.class})
public interface AppPlaceHistoryMapper extends PlaceHistoryMapper
{
}
All together
private Place defaultPlace = new ExamplePlace("1");
private SimplePanel appWidget = new SimplePanel();
public void onModuleLoad() {
ClientFactory clientFactory = new ClientFactory();
EventBus eventBus = clientFactory.getEventBus();
PlaceController placeController = clientFactory.getPlaceController();
// Start ActivityManager for the main widget with our ActivityMapper
ActivityMapper activityMapper = new AppActivityMapper(clientFactory);
ActivityManager activityManager = new ActivityManager(activityMapper, eventBus);
activityManager.setDisplay(appWidget);
// Start PlaceHistoryHandler with our PlaceHistoryMapper
AppPlaceHistoryMapper historyMapper= GWT.create(AppPlaceHistoryMapper.class);
PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(historyMapper);
historyHandler.register(placeController, eventBus, defaultPlace);
RootPanel.get().add(appWidget);
// Goes to the place represented on URL else default place
historyHandler.handleCurrentHistory();
}