From efe86e5b588cf171e0779f0d834eef7b1809efc6 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Mon, 20 Apr 2015 06:14:55 +0200 Subject: [PATCH] copy tree/ResourceTree.js from manager to manager5 --- www/manager5/tree/ResourceTree.js | 431 ++++++++++++++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 www/manager5/tree/ResourceTree.js diff --git a/www/manager5/tree/ResourceTree.js b/www/manager5/tree/ResourceTree.js new file mode 100644 index 00000000..377e01c4 --- /dev/null +++ b/www/manager5/tree/ResourceTree.js @@ -0,0 +1,431 @@ +Ext.define('PVE.tree.ResourceTree', { + extend: 'Ext.tree.TreePanel', + alias: ['widget.pveResourceTree'], + + statics: { + typeDefaults: { + node: { + iconCls: 'x-tree-node-server', + text: gettext('Node list') + }, + pool: { + iconCls: 'x-tree-node-pool', + text: gettext('Resource Pool') + }, + storage: { + iconCls: 'x-tree-node-harddisk', + text: gettext('Storage list') + }, + qemu: { + iconCls: 'x-tree-node-computer', + text: gettext('Virtual Machine') + }, + lxc: { + iconCls: 'x-tree-node-openvz', + text: gettext('LXC Container') + } + } + }, + + // private + nodeSortFn: function(node1, node2) { + var n1 = node1.data; + var n2 = node2.data; + + if ((n1.groupbyid && n2.groupbyid) || + !(n1.groupbyid || n2.groupbyid)) { + + var tcmp; + + var v1 = n1.type; + var v2 = n2.type; + + if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { + return tcmp; + } + + // numeric compare for VM IDs + // sort templates after regular VMs + if (v1 === 'qemu' || v1 === 'lxc') { + if (n1.template && !n2.template) { + return 1; + } else if (n2.template && !n1.template) { + return -1; + } + v1 = n1.vmid; + v2 = n2.vmid; + if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) { + return tcmp; + } + } + + return n1.text > n2.text ? 1 : (n1.text < n2.text ? -1 : 0); + } else if (n1.groupbyid) { + return -1; + } else if (n2.groupbyid) { + return 1; + } + }, + + // private: fast binary search + findInsertIndex: function(node, child, start, end) { + var me = this; + + var diff = end - start; + + var mid = start + (diff>>1); + + if (diff <= 0) { + return start; + } + + var res = me.nodeSortFn(child, node.childNodes[mid]); + if (res <= 0) { + return me.findInsertIndex(node, child, start, mid); + } else { + return me.findInsertIndex(node, child, mid + 1, end); + } + }, + + setIconCls: function(info) { + var me = this; + + var defaults = PVE.tree.ResourceTree.typeDefaults[info.type]; + if (defaults && defaults.iconCls) { + var running = info.running ? '-running' : ''; + var template = info.template ? '-template' : ''; + info.iconCls = defaults.iconCls + running + template; + } + }, + + // private + addChildSorted: function(node, info) { + var me = this; + + me.setIconCls(info); + + var defaults; + if (info.groupbyid) { + info.text = info.groupbyid; + if (info.type === 'type') { + defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid]; + if (defaults && defaults.text) { + info.text = defaults.text; + } + } + } + var child = Ext.ModelMgr.create(info, 'PVETree', info.id); + + var cs = node.childNodes; + var pos; + if (cs) { + pos = cs[me.findInsertIndex(node, child, 0, cs.length)]; + } + + node.insertBefore(child, pos); + + return child; + }, + + // private + groupChild: function(node, info, groups, level) { + var me = this; + + var groupby = groups[level]; + var v = info[groupby]; + + if (v) { + var group = node.findChild('groupbyid', v); + if (!group) { + var groupinfo; + if (info.type === groupby) { + groupinfo = info; + } else { + groupinfo = { + type: groupby, + id : groupby + "/" + v + }; + if (groupby !== 'type') { + groupinfo[groupby] = v; + } + } + groupinfo.leaf = false; + groupinfo.groupbyid = v; + group = me.addChildSorted(node, groupinfo); + // fixme: remove when EXTJS has fixed those bugs?! + group.expand(); group.collapse(); + } + if (info.type === groupby) { + return group; + } + if (group) { + return me.groupChild(group, info, groups, level + 1); + } + } + + return me.addChildSorted(node, info); + }, + + initComponent : function() { + var me = this; + + var rstore = PVE.data.ResourceStore; + var sp = Ext.state.Manager.getProvider(); + + if (!me.viewFilter) { + me.viewFilter = {}; + } + + var pdata = { + dataIndex: {}, + updateCount: 0 + }; + + var store = Ext.create('Ext.data.TreeStore', { + model: 'PVETree', + root: { + expanded: true, + id: 'root', + text: gettext('Datacenter') + } + }); + + var stateid = 'rid'; + + var updateTree = function() { + var tmp; + + // fixme: suspend events ? + + var rootnode = me.store.getRootNode(); + + // remember selected node (and all parents) + var sm = me.getSelectionModel(); + + var lastsel = sm.getSelection()[0]; + var parents = []; + var p = lastsel; + while (p && !!(p = p.parentNode)) { + parents.push(p); + } + + var index = pdata.dataIndex; + + var groups = me.viewFilter.groups || []; + var filterfn = me.viewFilter.filterfn; + + // remove vanished or changed items + var key; + for (key in index) { + if (index.hasOwnProperty(key)) { + var olditem = index[key]; + + // getById() use find(), which is slow (ExtJS4 DP5) + //var item = rstore.getById(olditem.data.id); + var item = rstore.data.get(olditem.data.id); + + var changed = false; + if (item) { + // test if any grouping attributes changed + var i, len; + for (i = 0, len = groups.length; i < len; i++) { + var attr = groups[i]; + if (item.data[attr] != olditem.data[attr]) { + //console.log("changed " + attr); + changed = true; + break; + } + } + if ((item.data.text !== olditem.data.text) || + (item.data.node !== olditem.data.node) || + (item.data.running !== olditem.data.running) || + (item.data.template !== olditem.data.template)) { + //console.log("changed node/text/running " + olditem.data.id); + changed = true; + } + + // fixme: also test filterfn()? + } + + if (!item || changed) { + //console.log("REM UID: " + key + " ITEM " + olditem.data.id); + if (olditem.isLeaf()) { + delete index[key]; + var parentNode = olditem.parentNode; + parentNode.removeChild(olditem, true); + } else { + if (item && changed) { + olditem.beginEdit(); + //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running); + var info = olditem.data; + Ext.apply(info, item.data); + me.setIconCls(info); + olditem.commit(); + } + } + } + } + } + + // add new items + rstore.each(function(item) { + var olditem = index[item.data.id]; + if (olditem) { + return; + } + + if (filterfn && !filterfn(item)) { + return; + } + + //console.log("ADD UID: " + item.data.id); + + var info = Ext.apply({ leaf: true }, item.data); + + var child = me.groupChild(rootnode, info, groups, 0); + if (child) { + index[item.data.id] = child; + } + }); + + // select parent node is selection vanished + if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) { + lastsel = rootnode; + while (!!(p = parents.shift())) { + if (!!(tmp = rootnode.findChild('id', p.data.id, true))) { + lastsel = tmp; + break; + } + } + me.selectById(lastsel.data.id); + } + + if (!pdata.updateCount) { + rootnode.collapse(); + rootnode.expand(); + me.applyState(sp.get(stateid)); + } + + pdata.updateCount++; + }; + + var statechange = function(sp, key, value) { + if (key === stateid) { + me.applyState(value); + } + }; + + sp.on('statechange', statechange); + + Ext.apply(me, { + store: store, + viewConfig: { + // note: animate cause problems with applyState + animate: false + }, + //useArrows: true, + //rootVisible: false, + //title: 'Resource Tree', + listeners: { + itemcontextmenu: function(v, record, item, index, event) { + event.stopEvent(); + //v.select(record); + var menu; + + if (record.data.type === 'qemu' && !record.data.template) { + menu = Ext.create('PVE.qemu.CmdMenu', { + pveSelNode: record + }); + } else if (record.data.type === 'qemu' && record.data.template) { + menu = Ext.create('PVE.qemu.TemplateMenu', { + pveSelNode: record + }); + } else if (record.data.type === 'lxc') { + menu = Ext.create('PVE.lxc.CmdMenu', { + pveSelNode: record + }); + } else { + return; + } + + menu.showAt(event.getXY()); + }, + destroy: function() { + rstore.un("load", updateTree); + } + }, + setViewFilter: function(view) { + me.viewFilter = view; + me.clearTree(); + updateTree(); + }, + clearTree: function() { + pdata.updateCount = 0; + var rootnode = me.store.getRootNode(); + rootnode.collapse(); + rootnode.removeAll(true); + pdata.dataIndex = {}; + me.getSelectionModel().deselectAll(); + }, + selectExpand: function(node) { + var sm = me.getSelectionModel(); + if (!sm.isSelected(node)) { + sm.select(node); + var cn = node; + while (!!(cn = cn.parentNode)) { + if (!cn.isExpanded()) { + cn.expand(); + } + } + } + }, + selectById: function(nodeid) { + var rootnode = me.store.getRootNode(); + var sm = me.getSelectionModel(); + var node; + if (nodeid === 'root') { + node = rootnode; + } else { + node = rootnode.findChild('id', nodeid, true); + } + if (node) { + me.selectExpand(node); + } + }, + checkVmMigration: function(record) { + if (!(record.data.type === 'qemu' || record.data.type === 'lxc')) { + throw "not a vm type"; + } + + var rootnode = me.store.getRootNode(); + var node = rootnode.findChild('id', record.data.id, true); + + if (node && node.data.type === record.data.type && + node.data.node !== record.data.node) { + // defer select (else we get strange errors) + Ext.defer(function() { me.selectExpand(node); }, 100, me); + } + }, + applyState : function(state) { + var sm = me.getSelectionModel(); + if (state && state.value) { + me.selectById(state.value); + } else { + sm.deselectAll(); + } + } + }); + + me.callParent(); + + var sm = me.getSelectionModel(); + sm.on('select', function(sm, n) { + sp.set(stateid, { value: n.data.id}); + }); + + rstore.on("load", updateTree); + rstore.startUpdate(); + //rstore.stopUpdate(); + } + +}); -- 2.39.5