diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js index 31d1418132638fd9ab352bfa9e7f5f26545d7d35..9f7d79c7c86e303ccaadb36129ff3cbd5f1c1039 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/datacontroller.js @@ -33,7 +33,7 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.selected_resource_id; self.selected_resourceGroup_id; - self.selected_task_id; + self.selected_task_ids = []; self.selected_project_id; self.selected_resourceClaim_id; @@ -48,6 +48,36 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, self.viewTimeSpan = {from: new Date(), to: new Date() }; self.autoFollowNow = true; + self.isTaskIdSelected = function(task_id) { + return self.selected_task_ids.indexOf(task_id) != -1; + } + + self.toggleTaskSelection = function(task_id) { + if(self.isTaskIdSelected(task_id)) { + self.removeSelectedTaskId(task_id); + } else { + self.addSelectedTaskId(task_id); + } + } + + self.addSelectedTaskId = function(task_id) { + if(self.selected_task_ids.indexOf(task_id) == -1) { + self.selected_task_ids.push(task_id); + } + } + + self.removeSelectedTaskId = function(task_id) { + var idx = self.selected_task_ids.indexOf(task_id); + if(idx != -1) { + self.selected_task_ids.splice(idx, 1); + } + } + + self.setSelectedTaskId = function(task_id) { + self.selected_task_ids.splice(0, self.selected_task_ids.length); + self.selected_task_ids.push(task_id); + } + self.floorDate = function(date, hourMod=1, minMod=1) { var min = date.getMinutes(); min = date.getMinutes()/minMod; @@ -246,25 +276,27 @@ angular.module('raeApp').factory("dataService", ['$http', '$q', function($http, if(initialTaskLoad && self.tasks.length > 0) { setTimeout(function() { + self.selected_task_ids.splice(0,self.selected_task_ids.length); + //try to select current task var currentTasks = self.tasks.filter(function(t) { return t.starttime <= self.lofarTime && t.endtime >= self.lofarTime; }); if(currentTasks.length > 0) { - self.selected_task_id = currentTasks[0].id; + self.selected_task_ids.push(currentTasks[0].id); } else { //try to select next task var nextTasks = self.tasks.filter(function(t) { return t.starttime >= self.lofarTime; }).sort(); if(nextTasks.length > 0) { - self.selected_task_id = nextTasks[0].id; + self.selected_task_ids.push(nextTasks[0].id); } else { //try to select most recent task var prevTasks = self.tasks.filter(function(t) { return t.endtime <= self.lofarTime; }).sort(); if(prevTasks.length > 0) { - self.selected_task_id = prevTasks[prevTasks.length-1].id; + self.selected_task_ids.push(prevTasks[prevTasks.length-1].id); } else { - self.selected_task_id = self.tasks[0].id; + self.selected_task_ids.push(self.tasks[0].id); } } } @@ -730,24 +762,27 @@ dataControllerMod.controller('DataController', $scope.selectCurrentTask = function() { var currentTasks = dataService.tasks.filter(function(t) { return t.starttime <= dataService.viewTimeSpan.to && t.endime >= dataService.viewTimeSpan.from; }); if(currentTasks.lenght > 0) { - dataService.selected_task_id = currentTasks[0].id; + dataService.setSelectedTaskId(currentTasks[0].id); } }; $scope.jumpToSelectedTask = function() { - if(dataService.selected_task_id == undefined) + if(dataService.selected_task_ids == undefined) return; - var task = dataService.taskDict[dataService.selected_task_id]; + var tasks = dataService.selected_task_ids.map(function(t_id) { return dataService.taskDict[t_id]; }); - if(task == undefined) + if(tasks.lenght == 0) return; - var taskDurationInmsec = task.endtime.getTime() - task.starttime.getTime(); - var taskDurationInMinutes = taskDurationInmsec/60000; - var viewSpanInMinutes = taskDurationInMinutes; + var minStarttime = new Date(Math.min.apply(null, tasks.map(function(t) { return t.starttime; }))); + var maxEndtime = new Date(Math.max.apply(null, tasks.map(function(t) { return t.endtime; }))); - var fittingSpans = $scope.zoomTimespans.filter(function(w) { return w.value >= taskDurationInMinutes; }); + var selectedTasksDurationInmsec = maxEndtime.getTime() - minStarttime.getTime(); + var selectedTasksDurationInMinutes = selectedTasksDurationInmsec/60000; + var viewSpanInMinutes = selectedTasksDurationInMinutes; + + var fittingSpans = $scope.zoomTimespans.filter(function(w) { return w.value >= selectedTasksDurationInMinutes; }); if(fittingSpans.length > 0) { $scope.zoomTimespan = fittingSpans[0]; //select one span larger if possible @@ -756,7 +791,7 @@ dataControllerMod.controller('DataController', viewSpanInMinutes = $scope.zoomTimespan.value; } - var focusTime = new Date(task.starttime.getTime() + 0.5*taskDurationInmsec); + var focusTime = new Date(minStarttime.getTime() + 0.5*selectedTasksDurationInmsec); dataService.viewTimeSpan = { from: dataService.floorDate(new Date(focusTime.getTime() - 0.4*viewSpanInMinutes*60*1000), 1, 5), @@ -788,11 +823,14 @@ dataControllerMod.controller('DataController', if(dataService.autoFollowNow) { focusTime = dataService.floorDate(dataService.lofarTime, 1, 5); - } else if(dataService.selected_task_id != undefined) { - var task = dataService.taskDict[dataService.selected_task_id]; + } else { + var tasks = dataService.selected_task_ids.map(function(t_id) { return dataService.taskDict[t_id]; }); + + if(tasks.lenght > 0) { + var minStarttime = new Date(Math.min.apply(null, tasks.map(function(t) { return t.starttime; }))); + var maxEndtime = new Date(Math.max.apply(null, tasks.map(function(t) { return t.endtime; }))); - if(task) { - focusTime = dataService.floorDate(task.starttime, 1, 5); + focusTime = dataService.floorDate(new Date(0.5*(minStarttime.getTime() + maxEndtime.getTime())), 1, 5); } } diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js index ef5752e6ffe17d26e4a29d0466e0ee4953bd441f..878ab4b7f9a09d0f62dcde970bff23d705ccab6b 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttprojectcontroller.js @@ -59,53 +59,19 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS } else if (directiveName === 'ganttTask') { directiveElement.bind('click', function(event) { if(directiveScope.task.model.raTask) { - $scope.dataService.selected_task_id = directiveScope.task.model.raTask.id; + if(event.ctrlKey) { + $scope.dataService.toggleTaskSelection(directiveScope.task.model.raTask.id); + } else { + $scope.dataService.setSelectedTaskId(directiveScope.task.model.raTask.id); + } } }); directiveElement.bind('dblclick', function(event) { if(directiveScope.task.model.raTask) { - $scope.dataService.selected_task_id = directiveScope.task.model.raTask.id; + $scope.dataService.setSelectedTaskId(directiveScope.task.model.raTask.id); $scope.jumpToSelectedTask(); } }); -// directiveElement.bind('contextmenu', function(event) { -// if(directiveScope.task.model.raTask) { -// $scope.dataService.selected_task_id = directiveScope.task.model.raTask.id; -// } -// -// //search for already existing contextmenu element -// if(directiveElement.find('#gantt-project-context-menu').length) { -// //found, remove it, so we can create a fresh one -// directiveElement.find('#gantt-project-context-menu')[0].remove(); -// } -// -// //create contextmenu element -// //with list of menu items, -// //each with it's own action -// var contextmenuElement = angular.element('<div id="gantt-project-context-menu"></div>'); -// ulElement = angular.element('<ul style="z-index:10000; position:fixed; top:initial; left:initial; display:block;" role="menu" class="dropdown-menu"></ul>'); -// contextmenuElement.append(ulElement); -// liElement = angular.element('<li><a href="#">Copy Task</a></li>'); -// ulElement.append(liElement); -// liElement.on('click', function() { -// $scope.dataService.copyTask(directiveScope.task.model.raTask); -// closeContextMenu(); -// }); -// -// var closeContextMenu = function() { -// contextmenuElement.remove(); -// angular.element(document).unbind('click', closeContextMenu); -// }; -// -// //click anywhere to remove the contextmenu -// angular.element(document).bind('click', closeContextMenu); -// -// //add contextmenu to clicked element -// directiveElement.append(contextmenuElement); -// -// //prevent bubbling event upwards -// return false; -// }); } }); @@ -115,7 +81,6 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS } if (directiveName === 'ganttTask') { directiveElement.unbind('dblclick'); -// directiveElement.unbind('contextmenu'); } }); } @@ -235,7 +200,7 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS movable: $.inArray(task.status_id, editableTaskStatusIds) > -1 }; - if(task.id == dataService.selected_task_id) { + if(dataService.isTaskIdSelected(task.id)) { rowTask.classes += ' task-selected-task'; } @@ -262,7 +227,7 @@ ganttProjectControllerMod.controller('GanttProjectController', ['$scope', 'dataS }; $scope.$watch('dataService.initialLoadComplete', function() { $scope.$evalAsync(updateGanttData); }); - $scope.$watch('dataService.selected_task_id', function() { $scope.$evalAsync(updateGanttData); }); + $scope.$watch('dataService.selected_task_ids', function() { $scope.$evalAsync(updateGanttData); }, true); $scope.$watch('dataService.viewTimeSpan', function() { $scope.$evalAsync(updateGanttData); }, true); $scope.$watch('dataService.filteredTaskChangeCntr', function() { $scope.$evalAsync(updateGanttData); }); $scope.$watch('dataService.lofarTime', function() { diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js index 9ec685de16ea6e07b287373f288b284197c50370..de24e59997d4c9b94d3ac194532b4ff45f49f7f3 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/ganttresourcecontroller.js @@ -58,17 +58,17 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat } else if (directiveName === 'ganttTask') { directiveElement.bind('click', function(event) { if(directiveScope.task.model.raTask) { - $scope.dataService.selected_task_id = directiveScope.task.model.raTask.id; + if(event.ctrlKey) { + $scope.dataService.toggleTaskSelection(directiveScope.task.model.raTask.id); + } else { + $scope.dataService.setSelectedTaskId(directiveScope.task.model.raTask.id); + } } if(directiveScope.task.model.claim) { $scope.dataService.selected_resourceClaim_id = directiveScope.task.model.claim.id; } }); directiveElement.bind('contextmenu', function(event) { - if(directiveScope.task.model.raTask) { - $scope.dataService.selected_task_id = directiveScope.task.model.raTask.id; - } - //search for already existing contextmenu element if(directiveElement.find('#gantt-resource-context-menu').length) { //found, remove it, so we can create a fresh one @@ -335,10 +335,9 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat movable: $.inArray(task.status_id, editableTaskStatusIds) > -1 }; - if(claim.id == dataService.selected_resourceClaim_id) { claimTask.classes += ' claim-selected-claim'; - } else if(task.id == dataService.selected_task_id) { + } else if(dataService.isTaskIdSelected(task.id)) { claimTask.classes += ' claim-selected-task'; } @@ -469,7 +468,7 @@ ganttResourceControllerMod.controller('GanttResourceController', ['$scope', 'dat }; $scope.$watch('dataService.initialLoadComplete', function() { $scope.$evalAsync(updateGanttData); }); - $scope.$watch('dataService.selected_task_id', function() { $scope.$evalAsync(updateGanttData); }); + $scope.$watch('dataService.selected_task_ids', function() { $scope.$evalAsync(updateGanttData); }, true); $scope.$watch('dataService.viewTimeSpan', function() { $scope.$evalAsync(updateGanttData); }, true); $scope.$watch('dataService.claimChangeCntr', function() { $scope.$evalAsync(updateGanttData); }); $scope.$watch('dataService.filteredTaskChangeCntr', function() { $scope.$evalAsync(updateGanttData); }); diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js index a431de9c97c7cd90641d9a4aeb3147830d802417..a79e11891ed4907fd0523b30bcfcec6ceb495ebf 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/controllers/gridcontroller.js @@ -113,8 +113,9 @@ $scope.columns = [ enableRowSelection: true, enableRowHeaderSelection: true, enableFullRowSelection: false, + modifierKeysToMultiSelect: true, + multiSelect:true, enableSelectionBatchEvent:false, - multiSelect:false, gridMenuShowHideColumns: false, columnDefs: $scope.columns, data: [], @@ -135,10 +136,13 @@ $scope.columns = [ var row = rows[i]; if(row.visible) { - var task = taskDict[row.entity.id]; + var task_id = row.entity.id; + var task = taskDict[task_id]; if(task) { $scope.dataService.filteredTasks.push(task); } + + row.setSelected($scope.dataService.selected_task_ids.indexOf(task_id) != -1); } } @@ -154,8 +158,12 @@ $scope.columns = [ }); gridApi.selection.on.rowSelectionChanged($scope,function(row){ - if(row.entity.id && row.isSelected) { - $scope.dataService.selected_task_id = row.entity.id; + if(row.entity.id) { + if(row.isSelected) { + $scope.dataService.addSelectedTaskId(row.entity.id); + } else if(!row.isSelected) { + $scope.dataService.removeSelectedTaskId(row.entity.id); + } } }); } @@ -222,11 +230,20 @@ $scope.columns = [ }; function jumpToSelectedTaskRow() { - var taskIdx = $scope.gridOptions.data.findIndex(function(row) {return row.id == dataService.selected_task_id}); +// var taskIdx = $scope.gridOptions.data.findIndex(function(row) {return row.id == dataService.selected_task_id}); +// +// if(taskIdx > -1) { +// $scope.gridApi.selection.selectRow($scope.gridOptions.data[taskIdx]); +// $scope.gridApi.core.scrollTo($scope.gridOptions.data[taskIdx], null); +// } + }; + + function onSelectedTaskIdsChanged() { + var selected_task_ids = $scope.dataService.selected_task_ids; + var rows = $scope.gridApi.grid.rows; - if(taskIdx > -1) { - $scope.gridApi.selection.selectRow($scope.gridOptions.data[taskIdx]); - $scope.gridApi.core.scrollTo($scope.gridOptions.data[taskIdx], null); + for(var row of rows) { + row.setSelected(selected_task_ids.indexOf(row.entity.id) != -1); } }; @@ -281,7 +298,7 @@ $scope.columns = [ fillColumFilterSelectOptions(groupSelectOptions, $scope.columns[7]); }; - $scope.$watch('dataService.selected_task_id', jumpToSelectedTaskRow);} + $scope.$watch('dataService.selected_task_ids', onSelectedTaskIdsChanged, true);} ]); gridControllerMod.directive('contextMenu', ['$document', function($document) { diff --git a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js index ed879020df5f79e439c8150c7319b2b1c7078852..ad9e1fbac9d2a6dcbf8345f4ba40e32da9c60d96 100644 --- a/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js +++ b/SAS/ResourceAssignment/ResourceAssignmentEditor/lib/static/app/gantt-plugins/angular-gantt-contextmenu-plugin.js @@ -31,7 +31,9 @@ var docElement = angular.element($document); if(dScope.task.model.raTask) { - dataService.selected_task_id = dScope.task.model.raTask.id; + if(!dataService.isTaskIdSelected(dScope.task.model.raTask.id)) { + dataService.setSelectedTaskId(dScope.task.model.raTask.id); + } } //search for already existing contextmenu element