403
404
* High-level task tree manipulation
406
addItem: function tTV_addItem(aItem, aDontSort) {
407
if (aItem.isCompleted && !this.binding.showCompleted) {
411
let index = this.binding.mHash2Index[aItem.hashId];
412
if (index === undefined) {
413
index = this.binding.mTaskArray.length;
414
this.binding.mTaskArray.push(aItem);
415
this.binding.mHash2Index[aItem.hashId] = index;
416
// The rowCountChanged function takes two arguments, the index where the
417
// first row was inserted and the number of rows to insert.
418
this.treebox.rowCountChanged(index, 1);
422
this.binding.recreateHashTable();
424
this.binding.sortItems();
427
index = this.binding.mHash2Index[aItem.hashId];
428
this.tree.view.selection.select(index);
429
this.treebox.ensureRowIsVisible(index);
432
removeItem: function tTV_removeItem(aItem, aDontSort) {
433
var index = this.binding.mHash2Index[aItem.hashId];
434
if (index != undefined) {
435
delete this.binding.mHash2Index[aItem.hashId];
407
// Adds an array of items to the list if they match the currently applied filter.
408
addItems: function tTV_addItems(aItems, aDontSort) {
409
this.modifyItems(aItems, [], aDontSort, true);
412
// Removes an array of items from the list.
413
removeItems: function tTV_removeItems(aItems) {
414
this.modifyItems([], aItems, true, false);
417
// Removes an array of old items from the list, and adds an array of new items if
418
// they match the currently applied filter.
419
modifyItems: function tTV_modifyItems(aNewItems, aOldItems, aDontSort, aSelectNew) {
420
let selItem = this.binding.currentTask;
421
let selIndex = this.tree.currentIndex;
422
let firstHash = null;
425
aNewItems = aNewItems || [];
426
aOldItems = aOldItems || [];
428
this.treebox.beginUpdateBatch();
430
let idiff = new itemDiff();
431
idiff.load(aOldItems);
432
idiff.difference(aNewItems);
434
let delItems = idiff.deletedItems;
435
let addItems = idiff.addedItems;
436
let modItems = idiff.modifiedItems;
438
// find the indexes of the old items that need to be removed
439
delItems.mArray.forEach(function(item) {
440
if (item.hashId in this.binding.mHash2Index) {
441
// the old item needs to be removed
442
remIndexes.push(this.binding.mHash2Index[item.hashId]);
443
delete this.binding.mHash2Index[item.hashId];
447
// modified items may need to be added, update, or removed
448
modItems.mArray.forEach(function(item) {
449
let inFilter = this.binding.mFilter.isItemInFilters(item) &&
450
!(item.isCompleted && !this.binding.showCompleted);
452
if (item.hashId in this.binding.mHash2Index) {
454
// make sure we're using the new version of a modified item
455
this.binding.mTaskArray[this.binding.mHash2Index[item.hashId]] = item;
457
// the modified item needs to be removed, it no longer matches the
459
remIndexes.push(this.binding.mHash2Index[item.hashId]);
460
delete this.binding.mHash2Index[item.hashId];
462
} else if (inFilter) {
463
// the modified item needs to be added, it now matches the applied filter.
464
itemsToAdd.push(item);
468
// find new items that need to be added
469
itemsToAdd = itemsToAdd.concat(addItems.mArray.filter(function(item) {
470
return this.binding.mFilter.isItemInFilters(item) &&
471
!(item.isCompleted && !this.binding.showCompleted);
474
// remove the old items working backward from the end so the indexes stay valid
475
remIndexes.sort(function(a, b) {return b - a;}).forEach(function(index) {
436
476
this.binding.mTaskArray.splice(index, 1);
437
477
this.treebox.rowCountChanged(index, -1);
439
if (index == this.rowCount) {
481
itemsToAdd.forEach(function(item) {
482
if (!(item.hashId in this.binding.mHash2Index)) {
483
let index = this.binding.mTaskArray.length;
484
this.binding.mTaskArray.push(item);
485
this.binding.mHash2Index[item.hashId] = index;
486
this.treebox.rowCountChanged(index, 1);
487
firstHash = firstHash || item.hashId;
443
this.tree.view.selection.select(index);
445
492
this.binding.recreateHashTable();
449
modifyItem: function tTV_modifyItem(aNewItem, aOldItem, aDontSort) {
450
var index = this.binding.mHash2Index[aOldItem.hashId];
451
if (index != undefined) {
452
// if a filter is installed we need to make sure that
453
// the item still belongs to the set of valid items before
454
// moving forward. if the filter cuts this item off, we
455
// need to act accordingly.
456
if (!this.binding.mFilter.isItemInFilters(aNewItem)) {
457
this.removeItem(aNewItem);
460
// same holds true for the completed filter, which is
461
// currently modeled as an explicit boolean.
462
if (aNewItem.isCompleted != aOldItem.isCompleted) {
463
if (aNewItem.isCompleted && !this.binding.showCompleted) {
464
this.removeItem(aNewItem);
468
delete this.binding.mHash2Index[aOldItem.hashId];
469
this.binding.mHash2Index[aNewItem.hashId] = index;
470
this.binding.mTaskArray[index] = aNewItem;
471
this.tree.view.selection.select(index);
474
this.treebox.invalidateRow(index);
476
this.binding.sortItems();
494
this.binding.sortItems();
497
if (aSelectNew && firstHash && firstHash in this.binding.mHash2Index) {
498
// select the first item added into the list
499
selIndex = this.binding.mHash2Index[firstHash];
500
} else if (selItem && selItem.hashId in this.binding.mHash2Index) {
501
// select the previously selected item
502
selIndex = this.binding.mHash2Index[selItem.hashId];
503
} else if (selIndex >= this.binding.mTaskArray.length) {
504
// make sure the previously selected index is valid
505
selIndex = this.binding.mTaskArray.length - 1;
508
this.tree.view.selection.select(selIndex);
509
this.treebox.ensureRowIsVisible(selIndex);
511
this.treebox.endUpdateBatch();
481
514
clear: function tTV_clear() {
857
890
onAddItem: function tTO_onAddItem(aItem) {
858
891
if (cal.isToDo(aItem)) {
859
// get occurrences of repeating items
861
893
if (this.binding.mFilter.endDate) {
894
// get occurrences of repeating items
862
895
occs = aItem.getOccurrencesBetween(this.binding.mFilter.startDate,
863
896
this.binding.mFilter.endDate,
868
for each (let occ in occs) {
869
if (this.binding.mFilter.isItemInFilters(occ)) {
870
this.binding.mTreeView.addItem(occ);
899
this.binding.mTreeView.addItems(occs);
876
903
onModifyItem: function tTO_onModifyItem(aNewItem, aOldItem) {
877
904
if ((cal.isToDo(aNewItem) || cal.isToDo(aOldItem))) {
879
if ((this.binding.mFilter.endDate) &&
880
(aOldItem.recurrenceInfo || aNewItem.recurrenceInfo)) {
882
// if item is repeating refresh to updated all modified occurrences
883
this.binding.refresh();
885
// forward the call to the view which will in turn
886
// update the internal reference and the view.
887
this.binding.mTreeView.modifyItem(aNewItem, aOldItem);
905
let newOccs = [aNewItem];
906
let oldOccs = [aOldItem];
907
if (this.binding.mFilter.endDate) {
908
newOccs = aNewItem.getOccurrencesBetween(this.binding.mFilter.startDate,
909
this.binding.mFilter.endDate,
911
oldOccs = aOldItem.getOccurrencesBetween(this.binding.mFilter.startDate,
912
this.binding.mFilter.endDate,
915
this.binding.mTreeView.modifyItems(newOccs, oldOccs);
890
917
// we also need to notify potential listeners.
891
918
var event = document.createEvent('Events');