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