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