mxGraph源码学习:mxGraphModel

Posted remo0x

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mxGraph源码学习:mxGraphModel相关的知识,希望对你有一定的参考价值。

1. 概览

mxGraphModel继承自mxEventSource以实现graph model。graph model是负责存储graph数据结构的包装器。graph model充当事务包装器,其中包含所有更改的事件通知,而cell包含用于更新实际数据结构的原子操作。

1.1 图层

模型中的cell层次结构必须具有顶级根cell,其中包含图层(通常是一个默认图层),而这些图层又包含图层的顶级cell。这意味着每个cell都包含在一个图层中。如果不需要图层,则应将所有新cell添加到默认图层。

图层可用于隐藏和显示cell组,或用于将cell组放置在显示中的其他cell之上。要识别图层,可以使用isLayer方法。如果给定cell的父级是模型的根,则返回true。

1.2 事件

有关详细信息,参阅事件部分。有一组新事件可用于跟踪事件发生的变化。这些事件称为初始beginUpdate的startEdit,为每个执行的更改执行,endEdit为endUpdate执行。执行的事件包含一个名为change的属性,表示执行后的更改。

1.3 编码模型

使用如下代码编码graph model,这将创建一个包含所有模型信息的XML节点:

var enc = new mxCodec();
var node = enc.encode(graph.getModel());

对于更改的编码,需要graph model监听器来编码来自给定更改数组的每个更改:

model.addListener(mxEvent.CHANGE, function(sender, evt) 
  var changes = evt.getProperty('edit').changes;
  var nodes = [];
  var codec = new mxCodec();

  for (var i = 0; i < changes.length; i++) 
    nodes.push(codec.encode(changes[i]));
  
  // do something with the nodes
);

对于解码和执行更改,编解码器需要一个查找功能,允许它按如下方式解析单元ID:

var codec = new mxCodec();
codec.lookup = function(id) 
  return model.getCell(id);

对于每个编码的变化(由节点表示),可以使用以下代码来执行解码并创建变更对象:

var changes = [];
var change = codec.decode(node);
change.model = model;
change.execute();
changes.push(change);

然后可以使用模型如下调度更改:

var edit = new mxUndoableEdit(model, false);
edit.changes = changes;

edit.notify = function() 
  edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
     'edit', edit, 'changes', edit.changes));
  edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
     'edit', edit, 'changes', edit.changes));


model.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
model.fireEvent(new mxEventObject(mxEvent.CHANGE,
     'edit', edit, 'changes', changes));

2. 构造

mxGraphModel的构造函数如下:

/**
 * root - 表示根cell的mxCell
 */
function mxGraphModel(root) 
    this.currentEdit = this.createUndoableEdit();

    if (root != null) 
        this.setRoot(root);
     else 
        this.clear();
    

mxGraphModel与mxGraph一样,都是采用原型链方式继承自mxEventSource:

mxGraphModel.prototype = new mxEventSource();
mxGraphModel.prototype.constructor = mxGraphModel;

3. 原型属性

下面列举一些重要的原型属性:

// 保存根cell,而根cell又包含表示图层作为子cell的cell。也就是说,图中的实际元素应该存在于第三代cell中
mxGraphModel.prototype.root = null;

// 从id到cell的map
mxGraphModel.prototype.cells = null;

// 指定下一个被创建的id。初始值为0
mxGraphModel.prototype.nextId = 0;

// 保存当前事务的更改。如果事务已关闭,则使用createUndoableEdit为此变量创建新对象
mxGraphModel.prototype.currentEdit = null;

// 计算嵌套事务的深度。每次调用beginUpdate都会增加这个数字,每次调用endUpdate都会减少它
// 当计数器达到0时,事务将关闭并触发相应的事件。初始值为0
mxGraphModel.prototype.updateLevel = 0;

4. 原型方法

mxGraphModel中有许多原型方法和类,这里选择一些了解其联系及实现

4.1 创建根cell

创建根cell有两种方式,第一种就是在创建mxGraphModel时指定一个cell作为根cell,如果没有指定,则使用第二种方法,如下所示。

调用createRoot方法创建一个根cell,然后设置为当前model的根cell,并会触发根cell更改的事件:

/**
 * 调用createRoot创建一个根cell
 */
mxGraphModel.prototype.clear = function () 
    this.setRoot(this.createRoot());
;

/**
 * 使用默认图层(第一个cell)创建新的根cell
 */
mxGraphModel.prototype.createRoot = function () 
    var cell = new mxCell();
    cell.insert(new mxCell());
    return cell;
;

/**
 * 使用mxRootChange设置模型的root,并将更改添加到当前事务。
 * 这将重置模型中的所有数据结构,并且是清除现有模型的首选方法。返回新根。
 *
 * root - 根cell
 */
mxGraphModel.prototype.setRoot = function (root) 
    this.execute(new mxRootChange(this, root));
    return root;
;

/**
 * 如果需要,执行给定的编辑并触发事件。编辑对象需要一个被调用的执行函数。编辑将添加到beginUpdate
 * 和endUpdate调用之间的currentEdit,以便在此执行是单个事务时触发事件,即如果没有调用
 * endUpdate之前没有调用startUpdate调用。此实现在执行给定更改之前触发execute事件。
 * 
 * change - 描述更改的对象。
 */
mxGraphModel.prototype.execute = function (change) 
    change.execute();
    this.beginUpdate();
    this.currentEdit.add(change);
    this.fireEvent(new mxEventObject(mxEvent.EXECUTE, 'change', change));
    // 新的全局执行事件
    this.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
    this.endUpdate();
;

设置新的根cell时,是通过创建一个新的mxRootChange来实现的:

/**
 * 更改模型中的根的操作。
 * 通过构造函数指定模型中根的更改。
 */
function mxRootChange(model, root) 
    this.model = model;
    this.root = root;
    this.previous = root;


/**
 * 使用mxGraphModel.rootChanged执行根的更改
 */
mxRootChange.prototype.execute = function () 
    this.root = this.previous;
    this.previous = this.model.rootChanged(this.previous);
;

/**
 * 内部回调以更改模型的根并更新内部数据结构,例如cells和nextId。返回先前的根。
 *
 * root - 根cell
 */
mxGraphModel.prototype.rootChanged = function (root) 
    var oldRoot = this.root;
    this.root = root;

    // 重置计数器和数据结构
    this.nextId = 0;
    this.cells = null;
    this.cellAdded(root);

    return oldRoot;
;

4.2 事务管理

mxGraphModel的事务管理简单而强大,主要通过beginUpdate和endUpdate实现:

/**
 * 将updateLevel加1。事件通知排队,直到updateLevel使用endUpdate减到0。
 */
mxGraphModel.prototype.beginUpdate = function () 
    this.updateLevel++;
    this.fireEvent(new mxEventObject(mxEvent.BEGIN_UPDATE));

    if (this.updateLevel === 1) 
        this.fireEvent(new mxEventObject(mxEvent.START_EDIT));
    
;

/**
 * 将updateLevel减1,并在updateLevel减到0时触发undo事件。此函数通过调用currentEdit
 * 上的notify方法间接触发change事件,然后使用createUndoableEdit创建新的currentEdit。
 *
 * 每次编辑时只会触发一次undo事件,而每当调用notify函数时,都会触发change事件,即在编辑的
 * 撤消和重做时触发。
 */
mxGraphModel.prototype.endUpdate = function () 
    this.updateLevel--;

    if (this.updateLevel === 0) 
        this.fireEvent(new mxEventObject(mxEvent.END_EDIT));
    

    if (!this.endingUpdate) 
        this.endingUpdate = this.updateLevel === 0;
        this.fireEvent(new mxEventObject(mxEvent.END_UPDATE, 'edit', this.currentEdit));

        try 
            if (this.endingUpdate && !this.currentEdit.isEmpty()) 
                this.fireEvent(new mxEventObject(mxEvent.BEFORE_UNDO, 'edit', this.currentEdit));
                var tmp = this.currentEdit;
                this.currentEdit = this.createUndoableEdit();
                tmp.notify();
                this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', tmp));
            
         finally 
            this.endingUpdate = false;
        
    
;

/**
 * 创建一个新的mxUndoableEdit,它实现notify方法以通过mxUndoableEdit的源触发change和notify事件。
 *
 * significant - 可选布尔值,指定要创建的编辑是否重要。默认为true。
 */
mxGraphModel.prototype.createUndoableEdit = function (significant) 
    var edit = new mxUndoableEdit(this, (significant != null) ? significant : true);

    edit.notify = function () 
        // LATER: Remove changes property (deprecated)
        edit.source.fireEvent(new mxEventObject(mxEvent.CHANGE,
            'edit', edit, 'changes', edit.changes));
        edit.source.fireEvent(new mxEventObject(mxEvent.NOTIFY,
            'edit', edit, 'changes', edit.changes));
    ;

    return edit;
;

以上是关于mxGraph源码学习:mxGraphModel的主要内容,如果未能解决你的问题,请参考以下文章

mxGraph源码学习:mxGraph库

mxGraph源码学习:mxGraph

mxGraph源码学习:mxGraph

mxGraph源码学习:mxEventSource

mxGraph源码学习:mxClient

mxGraph源码学习:mxCell