Source: widgets/chem/editor/kekule.chemEditor.nexus.js

/**
 * @fileoverview
 * Containing a nexus class which can link all essential editor component
 * (including editor, structure tree view and object inspector) together.
 * @author Partridge Jiang
 */

/*
 * requires /lan/classes.js
 * requires /widgets/chem/editor/kekule.chemEditor.baseEditors.js
 * requires /widgets/chem/structureTreeView/kekule.chemWidget/structureTreeViews.js
 * requires 'widgets/advCtrls/objInspector/kekule.widget.objInspectors.js',
 */

(function(){
"use strict";

/**
 * An tree view widget to display internal relationship of chem objects.
 * @class
 * @augments ObjectEx
 *
 * @param {Hash} components A list of related editor components, including
 * 	{editor, structureTreeView, objInspector}
 *
 * @property {Kekule.ChemObject} rootObj Root chem object to be displayed in tree view.
 * @property {Bool} enableLiveUpdate If set to true, the tree view will automatically updated when chem objects changed.
 */
Kekule.Editor.EditorNexus = Class.create(ObjectEx,
/** @lends Kekule.Editor.EditorNexus# */
{
	/** @private */
	CLASS_NAME: 'Kekule.Editor.EditorNexus',
	/** @construct */
	initialize: function($super, components)
	{
		$super();
		this._currInvoker = null;
		if (components)
		{
			if (components.editor)
				this.setEditor(components.editor);
			if (components.structureTreeView)
				this.setStructureTreeView(components.structureTreeView);
			if (components.objectInspector)
			{
				this.setObjectInspector(components.objectInspector);
			}
		}
	},
	/** @private */
	initProperties: function()
	{
		this.defineProp('editor', {'dataType': 'Kekule.Editor.BaseEditor', 'serializable': false,
			'setter': function(value)
			{
				var oldObj = this.getPropStoreFieldValue('editor');
				this.setPropStoreFieldValue('editor', value);
				this.editorChanged(value, oldObj);
			}
		});
		this.defineProp('structureTreeView', {'dataType': 'Kekule.ChemWidget.StructureTreeView', 'serializable': false,
			'setter': function(value)
			{
				var oldObj = this.getPropStoreFieldValue('structureTreeView');
				this.setPropStoreFieldValue('structureTreeView', value);
				this.structureTreeViewChanged(value, oldObj);
			}
		});
		this.defineProp('objectInspector', {'dataType': 'Kekule.Widget.ObjectInspector', 'serializable': false,
			'setter': function(value)
			{
				var oldObj = this.getPropStoreFieldValue('objectInspector');
				this.setPropStoreFieldValue('objectInspector', value);
				this.objectInspectorChanged(value, oldObj);
			}
		});
	},
	/** @private */
	editorChanged: function(newValue, oldValue)
	{
		if (oldValue)
			this._uninstallEditorEventHandler(oldValue);
		if (newValue)
			this._installEditorEventHandler(newValue);
	},
	/** @private */
	structureTreeViewChanged: function(newValue, oldValue)
	{
		if (oldValue)
			this._uninstallStructureTreeViewEventHandler(oldValue);
		if (newValue)
		{
			this._installStructureTreeViewEventHandler(newValue);
			this._updateByEditor();
		}
	},
	/** @private */
	objectInspectorChanged: function(newValue, oldValue)
	{
		var editor = this.getEditor();
		if (editor)
		{
			if (oldValue)
			{
				if (oldValue.getOperHistory() === editor.getOperHistory())
					oldValue.setOperHistory(null);
			}
			if (newValue)
			{
				newValue.setOperHistory(editor.getOperHistory());
				newValue.setEnableOperHistory(editor.getEnableOperHistory());
				this._updateByEditor();
			}
		}
	},
	/** @private */
	_installEditorEventHandler: function(editor)
	{
		//editor.setEnablePropValueSetEvent(true);
		editor.addEventListener('change', this._reactEditorChanged, this);
		editor.addEventListener('beginUpdateObject', this._reactEditorBeginUpdateObject, this);
		editor.addEventListener('endUpdateObject', this._reactEditorEndUpdateObject, this);
	},
	_uninstallEditorEventHandler: function(editor)
	{
		editor.removeEventListener('change', this._reactEditorChanged, this);
		editor.removeEventListener('beginUpdateObject', this._reactEditorBeginUpdateObject, this);
		editor.removeEventListener('endUpdateObject', this._reactEditorEndUpdateObject, this);
	},
	_installStructureTreeViewEventHandler: function(treeView)
	{
		//treeView.setEnablePropValueSetEvent(true);
		treeView.addEventListener('change', this._reactStructureTreeViewChanged, this);
	},
	_uninstallStructureTreeViewEventHandler: function(treeView)
	{
		treeView.removeEventListener('change', this._reactStructureTreeViewChanged, this);
	},
	/** @private */
	_reactEditorChanged: function(e)
	{
		if (this._currInvoker)  // IMPORTANT, or cause recursion
			return;
		var target = e.target;
		if (target !== this.getEditor())
			return;
		var propNames = e.changedPropNames;
		if (propNames.indexOf('chemObj') >= 0)  // react to root change
		{
			this.changeRootObj(target.getChemObj(), target);
		}
		if (propNames.indexOf('selection') >= 0) // react to selection change
		{
			this.changeSelection(target.getSelection(), target);
		}
		if ((propNames.indexOf('operHistory') >= 0) || (propNames.indexOf('enableOperHistory') >= 0))
		{
			var objInspector = this.getObjectInspector();
			if (objInspector)
			{
				objInspector.setOperHistory(target.getOperHistory());
				objInspector.setEnableOperHistory(target.getEnableOperHistory());
			}
		}
	},
	/** @private */
	_reactEditorBeginUpdateObject: function(e)
	{
		// disable live update of structure tree view
		this.pauseLiveUpdate();
	},
	/** @private */
	_reactEditorEndUpdateObject: function(e)
	{
		// restore live update of structure tree view
		this.resumeLiveUpdate();
	},
	/** @private */
	_updateByEditor: function()
	{
		this._reactEditorChanged({'target': this.getEditor(), 'changedPropNames': ['chemObj', 'selection', 'operHistory']});
	},
	/** @private */
	_reactStructureTreeViewChanged: function(e)
	{
		if (this._currInvoker)  // IMPORTANT, or cause recursion
			return;
		var target = e.target;
		if (target !== this.getStructureTreeView())
			return;
		// react to selection change
		var propNames = e.changedPropNames;
		if (propNames.indexOf('selection') >= 0)
		{
			var objs = target.getSelectedChemObjs();
			this.changeSelection(objs, target);
		}
	},

	/**
	 * Pause live update of structure tree view.
	 * @private
	 */
	pauseLiveUpdate: function()
	{
		var treeView = this.getStructureTreeView();
		if (treeView)
		{
			treeView.pauseLiveUpdate();
		}
	},
	/**
	 * Resume the live update status of structure tree view.
	 * @private
	 */
	resumeLiveUpdate: function()
	{
		var treeView = this.getStructureTreeView();
		if (treeView)
			treeView.resumeLiveUpdate();
	},

	/**
	 * Change selection objects in all components (editor, structureTreeView and objInspector).
	 * @param {Array} selection Selected objects
	 * @param {Object} invokerComponent Which component made the selection change.
	 * @private
	 */
	changeSelection: function(selection, invokerComponent)
	{
		this._currInvoker = invokerComponent;
		try
		{
			// change editor
			var editor = this.getEditor();
			if (editor)
			{
				if (this._currInvoker !== editor)
					editor.setSelection(selection);
			}
			// and tree view
			var treeView = this.getStructureTreeView();
			if (treeView)
			{
				if (this._currInvoker !== treeView)
					treeView.selectChemObjs(selection);
			}
			// and object inspector
			var objInspector = this.getObjectInspector();
			if (objInspector)
			{
				if (this._currInvoker !== objInspector)
				{
					objInspector.setObjects(selection);
				}
			}
		}
		finally
		{
			this._currInvoker = null;
		}
	},
	/**
	 * Change root object in all components (editor, structureTreeView and objInspector).
	 * @param {Array} selection Selected objects
	 * @param {Object} invokerComponent Which component made the root change.
	 * @private
	 */
	changeRootObj: function(root, invokerComponent)
	{
		this._currInvoker = invokerComponent;
		try
		{
			// change editor
			var editor = this.getEditor();
			if (editor && this._currInvoker !== editor)
				editor.setChemObj(root);
			var treeView = this.getStructureTreeView();
			if (treeView && this._currInvoker !== treeView)
				treeView.setRootObj(root);
		}
		finally
		{
			this._currInvoker = null;
		}
	}
});

})();