]> git.proxmox.com Git - pve-manager-legacy.git/blame - www/manager/tree/ResourceTree.js
use utf8 encoding
[pve-manager-legacy.git] / www / manager / tree / ResourceTree.js
CommitLineData
5910d3b7
DM
1Ext.define('PVE.tree.ResourceTree', {
2 extend: 'Ext.tree.TreePanel',
3 requires: ['Ext.tree.*',
4 'Ext.state.Manager',
5 'PVE.Utils',
6 'PVE.data.ResourceStore'],
7 alias: ['widget.pveResourceTree'],
8
9 statics: {
10 typeDefaults: {
11 node: {
12 iconCls: 'x-tree-node-server',
50c1c8a5 13 text: gettext('Node list')
5910d3b7
DM
14 },
15 storage: {
16 iconCls: 'x-tree-node-harddisk',
50c1c8a5 17 text: gettext('Storage list')
5910d3b7
DM
18 },
19 qemu: {
20 iconCls: 'x-tree-node-computer',
6c37b50f 21 text: gettext('Virtual Machine')
5910d3b7
DM
22 },
23 openvz: {
52943683 24 iconCls: 'x-tree-node-openvz',
6c37b50f 25 text: gettext('OpenVZ Container')
5910d3b7
DM
26 }
27 }
28 },
29
30 // private
31 nodeSortFn: function(node1, node2) {
32 var n1 = node1.data;
33 var n2 = node2.data;
34
35 if ((n1.groupbyid && n2.groupbyid) ||
36 !(n1.groupbyid || n2.groupbyid)) {
37
38 var tcmp;
39
40 var v1 = n1.type;
41 var v2 = n2.type;
42
43 if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) {
44 return tcmp;
45 }
46
47 // numeric compare for VM IDs
48 if (v1 === 'qemu' || v1 === 'openvz') {
49 v1 = n1.vmid;
50 v2 = n2.vmid;
51 if ((tcmp = v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)) != 0) {
52 return tcmp;
53 }
54 }
55
56 return n1.text > n2.text ? 1 : (n1.text < n2.text ? -1 : 0);
57 } else if (n1.groupbyid) {
58 return -1;
59 } else if (n2.groupbyid) {
60 return 1;
61 }
62 },
63
64 // private: fast binary search
65 findInsertIndex: function(node, child, start, end) {
66 var me = this;
67
68 var diff = end - start;
69
70 var mid = start + (diff>>1);
71
72 if (diff <= 0) {
73 return start;
74 }
75
76 var res = me.nodeSortFn(child, node.childNodes[mid]);
77 if (res <= 0) {
78 return me.findInsertIndex(node, child, start, mid);
79 } else {
80 return me.findInsertIndex(node, child, mid + 1, end);
81 }
82 },
83
d30bec0a 84 setIconCls: function(info) {
5910d3b7
DM
85 var me = this;
86
87 var defaults = PVE.tree.ResourceTree.typeDefaults[info.type];
88 if (defaults && defaults.iconCls) {
89 if (info.running) {
90 info.iconCls = defaults.iconCls + "-running";
91 } else {
92 info.iconCls = defaults.iconCls;
93 }
94 }
d30bec0a
DM
95 },
96
97 // private
98 addChildSorted: function(node, info) {
99 var me = this;
100
101 me.setIconCls(info);
5910d3b7 102
d30bec0a 103 var defaults;
5910d3b7
DM
104 if (info.groupbyid) {
105 info.text = info.groupbyid;
106 if (info.type === 'type') {
107 defaults = PVE.tree.ResourceTree.typeDefaults[info.groupbyid];
108 if (defaults && defaults.text) {
109 info.text = defaults.text;
110 }
111 }
112 }
113 var child = Ext.ModelMgr.create(info, 'PVETree', info.id);
114
115 var cs = node.childNodes;
116 var pos;
117 if (cs) {
118 pos = cs[me.findInsertIndex(node, child, 0, cs.length)];
119 }
120
5910d3b7 121 node.insertBefore(child, pos);
5910d3b7
DM
122
123 return child;
124 },
125
126 // private
127 groupChild: function(node, info, groups, level) {
128 var me = this;
129
130 var groupby = groups[level];
131 var v = info[groupby];
132
133 if (v) {
134 var group = node.findChild('groupbyid', v);
135 if (!group) {
136 var groupinfo;
137 if (info.type === groupby) {
138 groupinfo = info;
139 } else {
140 groupinfo = {
141 type: groupby,
142 id : groupby + "/" + v
143 };
144 if (groupby !== 'type') {
145 groupinfo[groupby] = v;
146 }
147 }
148 groupinfo.leaf = false;
149 groupinfo.groupbyid = v;
150 group = me.addChildSorted(node, groupinfo);
151 // fixme: remove when EXTJS has fixed those bugs?!
152 group.expand(); group.collapse();
153 }
154 if (info.type === groupby) {
155 return group;
156 }
157 if (group) {
158 return me.groupChild(group, info, groups, level + 1);
159 }
160 }
161
162 return me.addChildSorted(node, info);
163 },
164
165 initComponent : function() {
166 var me = this;
167
168 var rstore = PVE.data.ResourceStore;
169 var sp = Ext.state.Manager.getProvider();
170
171 if (!me.viewFilter) {
172 me.viewFilter = {};
173 }
174
175 var pdata = {
176 dataIndex: {},
177 updateCount: 0
178 };
179
180 var store = Ext.create('Ext.data.TreeStore', {
181 model: 'PVETree',
182 root: {
183 expanded: true,
184 id: 'root',
50c1c8a5 185 text: gettext('Datacenter')
5910d3b7
DM
186 }
187 });
188
189 var stateid = 'rid';
190
191 var updateTree = function() {
192 var tmp;
193
194 // fixme: suspend events ?
195
196 var rootnode = me.store.getRootNode();
197
198 // remember selected node (and all parents)
199 var sm = me.getSelectionModel();
200
201 var lastsel = sm.getSelection()[0];
202 var parents = [];
203 var p = lastsel;
204 while (p && !!(p = p.parentNode)) {
205 parents.push(p);
206 }
207
208 var index = pdata.dataIndex;
209
210 var groups = me.viewFilter.groups || [];
211 var filterfn = me.viewFilter.filterfn;
212
213 // remove vanished or changed items
214 var key;
215 for (key in index) {
216 if (index.hasOwnProperty(key)) {
217 var olditem = index[key];
218
219 // getById() use find(), which is slow (ExtJS4 DP5)
220 //var item = rstore.getById(olditem.data.id);
221 var item = rstore.data.get(olditem.data.id);
222
223 var changed = false;
224 if (item) {
225 // test if any grouping attributes changed
226 var i, len;
227 for (i = 0, len = groups.length; i < len; i++) {
228 var attr = groups[i];
229 if (item.data[attr] != olditem.data[attr]) {
230 //console.log("changed " + attr);
231 changed = true;
232 break;
233 }
234 }
235 if ((item.data.text !== olditem.data.text) ||
2a0bc39f 236 (item.data.node !== olditem.data.node) ||
5910d3b7 237 (item.data.running !== olditem.data.running)) {
2a0bc39f 238 //console.log("changed node/text/running " + olditem.data.id);
5910d3b7
DM
239 changed = true;
240 }
241
242 // fixme: also test filterfn()?
243 }
244
245 if (!item || changed) {
246 //console.log("REM UID: " + key + " ITEM " + olditem.data.id);
d30bec0a
DM
247 if (olditem.isLeaf()) {
248 delete index[key];
249 var parentNode = olditem.parentNode;
250 parentNode.removeChild(olditem, true);
251 } else {
252 if (item && changed) {
253 olditem.beginEdit();
254 //console.log("REM UPDATE UID: " + key + " ITEM " + item.data.running);
255 var info = olditem.data;
256 Ext.apply(info, item.data);
257 me.setIconCls(info);
258 olditem.commit();
259 }
260 }
5910d3b7
DM
261 }
262 }
263 }
264
265 // add new items
266 rstore.each(function(item) {
267 var olditem = index[item.data.id];
268 if (olditem) {
269 return;
270 }
271
272 if (filterfn && !filterfn(item)) {
273 return;
274 }
275
276 //console.log("ADD UID: " + item.data.id);
277
278 var info = Ext.apply({ leaf: true }, item.data);
279
280 var child = me.groupChild(rootnode, info, groups, 0);
281 if (child) {
282 index[item.data.id] = child;
283 }
284 });
285
286 // select parent node is selection vanished
287 if (lastsel && !rootnode.findChild('id', lastsel.data.id, true)) {
288 lastsel = rootnode;
289 while (!!(p = parents.shift())) {
290 if (!!(tmp = rootnode.findChild('id', p.data.id, true))) {
291 lastsel = tmp;
292 break;
293 }
294 }
295 me.selectById(lastsel.data.id);
296 }
297
298 if (!pdata.updateCount) {
299 rootnode.collapse();
300 rootnode.expand();
301 me.applyState(sp.get(stateid));
302 }
303
304 pdata.updateCount++;
305 };
306
307 var statechange = function(sp, key, value) {
308 if (key === stateid) {
309 me.applyState(value);
310 }
311 };
312
313 sp.on('statechange', statechange);
314
315 Ext.apply(me, {
316 store: store,
317 viewConfig: {
318 // note: animate cause problems with applyState
319 animate: false
320 },
321 //useArrows: true,
322 //rootVisible: false,
a9a1e2f2 323 //title: 'Resource Tree',
5910d3b7 324 listeners: {
b82d8626
DM
325 itemcontextmenu: function(v, record, item, index, event) {
326 event.stopEvent();
327 //v.select(record);
328 var menu;
329
330 if (record.data.type === 'qemu') {
331 menu = Ext.create('PVE.qemu.CmdMenu', {
47b80718 332 pveSelNode: record
b82d8626
DM
333 });
334 } else if (record.data.type === 'openvz') {
335 menu = Ext.create('PVE.openvz.CmdMenu', {
47b80718 336 pveSelNode: record
b82d8626
DM
337 });
338 } else {
339 return;
340 }
341
342 menu.showAt(event.getXY());
343 },
5910d3b7
DM
344 destroy: function() {
345 rstore.un("load", updateTree);
346 }
347 },
348 setViewFilter: function(view) {
349 me.viewFilter = view;
350 me.clearTree();
351 updateTree();
352 },
353 clearTree: function() {
354 pdata.updateCount = 0;
355 var rootnode = me.store.getRootNode();
356 rootnode.collapse();
357 rootnode.removeAll(true);
358 pdata.dataIndex = {};
359 me.getSelectionModel().deselectAll();
360 },
31cd039b
DM
361 selectExpand: function(node) {
362 var sm = me.getSelectionModel();
363 if (!sm.isSelected(node)) {
364 sm.select(node);
365 var cn = node;
366 while (!!(cn = cn.parentNode)) {
367 if (!cn.isExpanded()) {
368 cn.expand();
369 }
370 }
371 }
372 },
5910d3b7
DM
373 selectById: function(nodeid) {
374 var rootnode = me.store.getRootNode();
375 var sm = me.getSelectionModel();
376 var node;
377 if (nodeid === 'root') {
378 node = rootnode;
379 } else {
380 node = rootnode.findChild('id', nodeid, true);
381 }
382 if (node) {
31cd039b
DM
383 me.selectExpand(node);
384 }
385 },
2a0bc39f 386 checkVmMigration: function(record) {
31cd039b
DM
387 if (!(record.data.type === 'qemu' || record.data.type === 'openvz')) {
388 throw "not a vm type";
389 }
390
391 var rootnode = me.store.getRootNode();
392 var node = rootnode.findChild('id', record.data.id, true);
393
394 if (node && node.data.type === record.data.type &&
395 node.data.node !== record.data.node) {
396 // defer select (else we get strange errors)
f6e64492 397 Ext.defer(function() { me.selectExpand(node); }, 100, me);
5910d3b7
DM
398 }
399 },
400 applyState : function(state) {
401 var sm = me.getSelectionModel();
402 if (state && state.value) {
403 me.selectById(state.value);
404 } else {
405 sm.deselectAll();
406 }
407 }
408 });
409
410 me.callParent();
411
412 var sm = me.getSelectionModel();
413 sm.on('select', function(sm, n) {
414 sp.set(stateid, { value: n.data.id});
415 });
416
417 rstore.on("load", updateTree);
418 rstore.startUpdate();
419 //rstore.stopUpdate();
420 }
421
422});