blob: b1aa210f4a8f365a52f8146664ab6b2262e0515c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014-2015 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.ui.html.json.desktop;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.scout.rt.client.ui.action.menu.IMenu;
import org.eclipse.scout.rt.client.ui.action.menu.root.IContextMenu;
import org.eclipse.scout.rt.client.ui.basic.table.ITable;
import org.eclipse.scout.rt.client.ui.basic.tree.ITreeNode;
import org.eclipse.scout.rt.client.ui.basic.tree.TreeEvent;
import org.eclipse.scout.rt.client.ui.desktop.IDesktop;
import org.eclipse.scout.rt.client.ui.desktop.outline.IOutline;
import org.eclipse.scout.rt.client.ui.desktop.outline.OutlineEvent;
import org.eclipse.scout.rt.client.ui.desktop.outline.pages.IPage;
import org.eclipse.scout.rt.client.ui.desktop.outline.pages.IPageWithNodes;
import org.eclipse.scout.rt.client.ui.desktop.outline.pages.IPageWithTable;
import org.eclipse.scout.rt.client.ui.form.IForm;
import org.eclipse.scout.rt.platform.BEANS;
import org.eclipse.scout.rt.platform.util.ObjectUtility;
import org.eclipse.scout.rt.ui.html.IUiSession;
import org.eclipse.scout.rt.ui.html.json.IJsonAdapter;
import org.eclipse.scout.rt.ui.html.json.InspectorInfo;
import org.eclipse.scout.rt.ui.html.json.JsonProperty;
import org.eclipse.scout.rt.ui.html.json.form.fields.JsonAdapterProperty;
import org.eclipse.scout.rt.ui.html.json.form.fields.JsonAdapterPropertyConfig;
import org.eclipse.scout.rt.ui.html.json.form.fields.JsonAdapterPropertyConfigBuilder;
import org.eclipse.scout.rt.ui.html.json.menu.JsonContextMenu;
import org.eclipse.scout.rt.ui.html.json.table.JsonOutlineTable;
import org.eclipse.scout.rt.ui.html.json.tree.JsonTree;
import org.json.JSONObject;
public class JsonOutline<OUTLINE extends IOutline> extends JsonTree<OUTLINE> {
private static final String PROP_DETAIL_FORM = IOutline.PROP_DETAIL_FORM;
private static final String PROP_DETAIL_TABLE = IOutline.PROP_DETAIL_TABLE;
private static final String PROP_DETAIL_FORM_VISIBLE = "detailFormVisible";
private static final String PROP_DETAIL_TABLE_VISIBLE = "detailTableVisible";
private Set<IJsonAdapter<?>> m_jsonDetailTables = new HashSet<IJsonAdapter<?>>();
private final IDesktop m_desktop;
public JsonOutline(OUTLINE outline, IUiSession uiSession, String id, IJsonAdapter<?> parent) {
super(outline, uiSession, id, parent);
m_desktop = uiSession.getClientSession().getDesktop();
}
@Override
public String getObjectType() {
return "Outline";
}
@Override
protected void initJsonProperties(OUTLINE model) {
super.initJsonProperties(model);
putJsonProperty(new JsonProperty<OUTLINE>(IOutline.PROP_NAVIGATE_BUTTONS_VISIBLE, model) {
@Override
protected Boolean modelValue() {
return getModel().isNavigateButtonsVisible();
}
});
putJsonProperty(new JsonAdapterProperty<OUTLINE>(IOutline.PROP_DEFAULT_DETAIL_FORM, model, getUiSession()) {
@Override
protected IForm modelValue() {
return getModel().getDefaultDetailForm();
}
@Override
protected JsonAdapterPropertyConfig createConfig() {
return JsonAdapterPropertyConfigBuilder.globalConfig();
}
});
putJsonProperty(new JsonProperty<OUTLINE>(IOutline.PROP_OUTLINE_OVERVIEW_VISIBLE, model) {
@Override
protected Boolean modelValue() {
return getModel().isOutlineOverviewVisible();
}
});
}
@Override
public JSONObject toJson() {
JSONObject json = super.toJson();
putAdapterIdsProperty(json, "views", m_desktop.getViews(getModel()));
if (m_desktop.getSelectedViews(getModel()).size() > 0) {
putAdapterIdsProperty(json, "selectedViewTabs", m_desktop.getSelectedViews(getModel()));
}
putAdapterIdsProperty(json, "dialogs", m_desktop.getDialogs(getModel(), false));
putAdapterIdsProperty(json, "messageBoxes", m_desktop.getMessageBoxes(getModel()));
putAdapterIdsProperty(json, "fileChoosers", m_desktop.getFileChoosers(getModel()));
return json;
}
@Override
protected void attachChildAdapters() {
super.attachChildAdapters();
attachAdapter(getModel().getDefaultDetailForm());
attachGlobalAdapters(m_desktop.getViews(getModel()));
attachGlobalAdapters(m_desktop.getSelectedViews(getModel()));
attachGlobalAdapters(m_desktop.getDialogs(getModel(), false));
attachGlobalAdapters(m_desktop.getMessageBoxes(getModel()));
attachGlobalAdapters(m_desktop.getFileChoosers(getModel()));
}
@Override
protected JsonContextMenu<IContextMenu> createJsonContextMenu() {
return new JsonContextMenu<IContextMenu>(getModel().getContextMenu(), this, new OutlineMenuFilter<IMenu>());
}
@Override
protected void attachNode(ITreeNode node, boolean attachChildren) {
if (!(node instanceof IPage)) {
throw new IllegalArgumentException("Expected node to be a page. " + node);
}
super.attachNode(node, attachChildren);
IPage<?> page = (IPage) node;
if (hasDetailForm(page)) {
attachGlobalAdapter(page.getDetailForm());
}
if (page.isTableVisible()) {
attachDetailTable(page);
}
}
@Override
protected void handleModelTreeEvent(TreeEvent event) {
if (!acceptModelTreeEvent(event)) {
return;
}
super.handleModelTreeEvent(event);
// When nodes are deleted, immediately detach the detail table from the deleted nodes. If we would do
// this later when the event buffer is processed, there could have been other events in the meantime
// (e.g. table events) which fail when the nodes (and everything that is attached to them, namely detail
// tables) are still existing. (Disposing the detail table right away is correct, because no matter what
// the event buffer does, the nodes are definitively deleted.)
if (ObjectUtility.isOneOf(event.getType(), TreeEvent.TYPE_NODES_DELETED, TreeEvent.TYPE_ALL_CHILD_NODES_DELETED)) {
detachDetailTables(event.getNodes(), true);
}
}
protected boolean acceptModelTreeEvent(TreeEvent event) {
// Don't fill the event buffer with events that are currently not relevant for the UI
if (event instanceof OutlineEvent && ObjectUtility.isOneOf(((OutlineEvent) event).getType(),
OutlineEvent.TYPE_PAGE_BEFORE_DATA_LOADED,
OutlineEvent.TYPE_PAGE_AFTER_DATA_LOADED,
OutlineEvent.TYPE_PAGE_AFTER_TABLE_INIT,
OutlineEvent.TYPE_PAGE_AFTER_PAGE_INIT,
OutlineEvent.TYPE_PAGE_AFTER_SEARCH_FORM_START,
OutlineEvent.TYPE_PAGE_AFTER_DISPOSE,
OutlineEvent.TYPE_PAGE_ACTIVATED)) {
return false;
}
return true;
}
@Override
protected JSONObject treeNodeToJson(ITreeNode node) {
if (!(node instanceof IPage)) {
throw new IllegalArgumentException("Expected node to be a page. " + node);
}
IPage page = (IPage) node;
JSONObject json = super.treeNodeToJson(node);
putDetailFormAndTable(json, page);
putNodeType(json, node);
BEANS.get(InspectorInfo.class).put(getUiSession(), json, page);
return json;
}
protected void putNodeType(JSONObject json, ITreeNode node) {
String nodeType = null;
if (node instanceof IPageWithNodes) {
nodeType = "nodes";
}
else if (node instanceof IPageWithTable) {
nodeType = "table";
}
if (nodeType != null) {
putProperty(json, "nodeType", nodeType);
}
}
protected void putDetailFormAndTable(JSONObject json, IPage page) {
putProperty(json, PROP_DETAIL_FORM_VISIBLE, page.isDetailFormVisible());
if (page.isDetailFormVisible() && hasDetailForm(page)) {
putAdapterIdProperty(json, PROP_DETAIL_FORM, page.getDetailForm());
}
putProperty(json, PROP_DETAIL_TABLE_VISIBLE, page.isTableVisible());
if (page.isTableVisible()) {
ITable table = page.getTable(false);
if (table != null) {
putAdapterIdProperty(json, PROP_DETAIL_TABLE, table);
}
}
}
@Override
protected void disposeNode(ITreeNode node, boolean disposeChildren) {
detachDetailTable(node, false);
super.disposeNode(node, disposeChildren);
// No need to dispose detail form (it will be disposed automatically when it is closed)
}
protected void attachDetailTable(IPage page) {
ITable table = page.getTable(false);
if (table == null) {
return;
}
table.setProperty(JsonOutlineTable.PROP_PAGE, page);
IJsonAdapter<?> detailTableAdapter = attachGlobalAdapter(table);
m_jsonDetailTables.add(detailTableAdapter);
}
protected void detachDetailTable(IPage page) {
ITable table = page.getTable(false);
if (table != null) {
table.setProperty(JsonOutlineTable.PROP_PAGE, null);
IJsonAdapter<?> jsonTableAdapter = getGlobalAdapter(table);
if (jsonTableAdapter != null) {
m_jsonDetailTables.remove(jsonTableAdapter);
if (!jsonTableAdapter.isDisposed()) {
jsonTableAdapter.dispose();
}
}
}
}
protected void detachDetailTable(ITreeNode node, boolean disposeChildren) {
if (disposeChildren) {
detachDetailTables(getChildNodes(node), disposeChildren);
}
if (!(node instanceof IPage)) {
throw new IllegalArgumentException("Expected node to be a page. " + node);
}
detachDetailTable((IPage) node);
}
protected void detachDetailTables(Collection<ITreeNode> nodes, boolean disposeChildren) {
for (ITreeNode node : nodes) {
detachDetailTable(node, disposeChildren);
}
}
@Override
protected void handleModelOtherTreeEvent(TreeEvent event) {
switch (event.getType()) {
case OutlineEvent.TYPE_PAGE_CHANGED:
handleModelPageChanged((OutlineEvent) event);
break;
// Note: Check acceptModelTreeEvent() before adding new cases here
default:
//NOP
}
}
protected void handleModelPageChanged(OutlineEvent event) {
IPage page = (IPage) event.getNode();
attachNode(page, false);
if (page.isStatusDeleted() || !page.isFilterAccepted()) { // Ignore deleted or filtered nodes, because for the UI, they don't exist
return;
}
String nodeId = optNodeId(page);
if (nodeId == null) { // Ignore nodes that are not yet sent to the UI (may happen due to asynchronous event processing)
return;
}
JSONObject jsonEvent = new JSONObject();
putProperty(jsonEvent, PROP_NODE_ID, getOrCreateNodeId(page));
putDetailFormAndTable(jsonEvent, page);
addActionEvent("pageChanged", jsonEvent);
}
/**
* @return <code>true</code> if the page has a detail form that is not closed, <code>false</code> otherwise (closed
* forms should not be attached, because the close event causes the JSON adapter to be disposed)
*/
protected boolean hasDetailForm(IPage page) {
return (page.getDetailForm() != null && !page.getDetailForm().isFormClosed());
}
}