diff --git a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js index 35837fff2c7..93df9367450 100644 --- a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js +++ b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js @@ -49,6 +49,8 @@ qx.Class.define("osparc.file.FileLabelWithActions", { const deleteBtn = this.getChildControl("delete-button"); deleteBtn.addListener("execute", () => this.__deleteSelected(), this); + + this.__selection = []; }, events: { @@ -61,13 +63,12 @@ qx.Class.define("osparc.file.FileLabelWithActions", { init: false, nullable: false, event: "changeMultiSelect", - apply: "__enableMultiSelection", + apply: "__changeMultiSelection", }, }, members: { __selection: null, - __multiSelection: null, _createChildControlImpl: function(id) { let control; @@ -99,9 +100,10 @@ qx.Class.define("osparc.file.FileLabelWithActions", { return control || this.base(arguments, id); }, - __enableMultiSelection: function() { - this.resetItemSelected(); - this.__multiSelection = []; + __changeMultiSelection: function() { + if (this.__selection.length > 1) { + this.resetSelection(); + } }, setItemSelected: function(selectedItem) { @@ -111,25 +113,25 @@ qx.Class.define("osparc.file.FileLabelWithActions", { this.getChildControl("delete-button").setEnabled(isFile); const selectedLabel = this.getChildControl("selected-label"); if (isFile) { - this.__selection = selectedItem; + this.__selection = [selectedItem]; selectedLabel.set({ value: selectedItem.getLabel(), toolTipText: selectedItem.getFileId() }); } else { - this.__selection = null; + this.__selection = []; selectedLabel.set({ value: "", toolTipText: "" }); } } else { - this.resetItemSelected(); + this.resetSelection(); } }, setMultiItemSelected: function(multiSelectionData) { - this.__multiSelection = multiSelectionData; + this.__selection = multiSelectionData; if (multiSelectionData && multiSelectionData.length) { if (multiSelectionData.length === 1) { this.setItemSelected(multiSelectionData[0]); @@ -140,34 +142,27 @@ qx.Class.define("osparc.file.FileLabelWithActions", { }); } } else { - this.resetItemSelected(); + this.resetSelection(); } }, - resetItemSelected: function() { - this.__selection = null; - this.__multiSelection = []; + resetSelection: function() { + this.__selection = []; this.getChildControl("download-button").setEnabled(false); this.getChildControl("delete-button").setEnabled(false); this.getChildControl("selected-label").resetValue(); }, - getItemSelected: function() { - const selectedItem = this.__selection; - if (selectedItem && osparc.file.FilesTree.isFile(selectedItem)) { - return selectedItem; - } - return null; - }, - __retrieveURLAndDownloadSelected: function() { if (this.isMultiSelect()) { - this.__multiSelection.forEach(selection => { - this.__retrieveURLAndDownloadFile(selection); + this.__selection.forEach(selection => { + if (selection && osparc.file.FilesTree.isFile(selection)) { + this.__retrieveURLAndDownloadFile(selection); + } }); - } else { - const selection = this.getItemSelected(); - if (selection) { + } else if (this.__selection.length) { + const selection = this.__selection[0]; + if (selection && osparc.file.FilesTree.isFile(selection)) { this.__retrieveURLAndDownloadFile(selection); } } @@ -176,10 +171,12 @@ qx.Class.define("osparc.file.FileLabelWithActions", { __deleteSelected: function() { if (this.isMultiSelect()) { const requests = []; - this.__multiSelection.forEach(selection => { - const request = this.__deleteFile(selection); - if (request) { - requests.push(request); + this.__selection.forEach(selection => { + if (selection && osparc.file.FilesTree.isFile(selection)) { + const request = this.__deleteFile(selection); + if (request) { + requests.push(request); + } } }); Promise.all(requests) @@ -190,9 +187,9 @@ qx.Class.define("osparc.file.FileLabelWithActions", { } }); requests - } else { - const selection = this.getItemSelected(); - if (selection) { + } else if (this.__selection.length) { + const selection = this.__selection[0]; + if (selection && osparc.file.FilesTree.isFile(selection)) { const request = this.__deleteFile(selection); if (request) { request diff --git a/services/static-webserver/client/source/class/osparc/file/FolderContent.js b/services/static-webserver/client/source/class/osparc/file/FolderContent.js index ced22a52c1a..badadbadcf2 100644 --- a/services/static-webserver/client/source/class/osparc/file/FolderContent.js +++ b/services/static-webserver/client/source/class/osparc/file/FolderContent.js @@ -47,7 +47,7 @@ qx.Class.define("osparc.file.FolderContent", { init: false, nullable: false, event: "changeMultiSelect", - apply: "__reloadFolderContent" + apply: "__changeMultiSelect" }, }, @@ -227,26 +227,43 @@ qx.Class.define("osparc.file.FolderContent", { } else if (this.getMode() === "icons") { const iconsLayout = this.getChildControl("icons-layout"); iconsLayout.removeAll(); - const iconsGroup = new qx.ui.form.RadioGroup().set({ - allowEmptySelection: true - }); entries.forEach(entry => { - if (!this.isMultiSelect()) { - iconsGroup.add(entry); - } iconsLayout.add(entry); }); } this.setSelection([this.getSelectables()[this.getMode() === "icons" ? 0 : 1]]); }, - __itemTapped: function(entry, buttonSelected) { + __changeMultiSelect: function() { + if (this.getMode() === "icons" && this.getMultiSelect() === false) { + const iconsLayout = this.getChildControl("icons-layout"); + const selectedButtons = iconsLayout.getChildren().filter(btn => btn.getValue()); + if (selectedButtons.length > 1) { + // reset selection + selectedButtons.forEach(btn => btn.setValue(false)); + } + } else if (this.getMode() === "list") { + const selectionModel = this.getChildControl("table").getSelectionModel(); + let selection = null; + if (selectionModel.getSelectedCount() === 1) { + // keep selection + selection = selectionModel.getSelectedRanges()[0]; + } + selectionModel.setSelectionMode(this.isMultiSelect() ? + qx.ui.table.selection.Model.MULTIPLE_INTERVAL_SELECTION_TOGGLE : + qx.ui.table.selection.Model.SINGLE_SELECTION + ); + if (selection) { + selectionModel.setSelectionInterval(selection.minIndex, selection.maxIndex); + } + } + }, + + __selectionChanged: function(selection) { if (this.isMultiSelect()) { - this.fireDataEvent("multiSelectionChanged", entry); - } else if (buttonSelected === false) { - this.fireDataEvent("selectionChanged", null); + this.fireDataEvent("multiSelectionChanged", selection); } else { - this.fireDataEvent("selectionChanged", entry); + this.fireDataEvent("selectionChanged", (selection && selection.length) ? selection[0] : null); } }, @@ -255,7 +272,10 @@ qx.Class.define("osparc.file.FolderContent", { }, __attachListenersToGridItem: function(gridItem) { - gridItem.addListener("tap", () => { + gridItem.addListener("tap", e => { + if (e.getNativeEvent().ctrlKey) { + this.setMultiSelect(true); + } if (this.isMultiSelect()) { // pass all buttons that are selected const selectedFiles = []; @@ -265,9 +285,14 @@ qx.Class.define("osparc.file.FolderContent", { selectedFiles.push(btn.entry); } }); - this.__itemTapped(selectedFiles, gridItem.getValue()); + this.__selectionChanged(selectedFiles); } else { - this.__itemTapped(gridItem.entry, gridItem.getValue()); + // unselect the other items + const iconsLayout = this.getChildControl("icons-layout"); + iconsLayout.getChildren().forEach(btn => { + btn.setValue(btn === gridItem); + }); + this.__selectionChanged(gridItem.getValue() ? [gridItem.entry] : null); } // folders can't be selected if (osparc.file.FilesTree.isDir(gridItem.entry)) { @@ -281,11 +306,20 @@ qx.Class.define("osparc.file.FolderContent", { __attachListenersToTableItem: function(table) { table.addListener("cellTap", e => { - const selectedRow = e.getRow(); - const rowData = table.getTableModel().getRowData(selectedRow); - if ("entry" in rowData) { - this.__itemTapped(rowData.entry); + if (e.getNativeEvent().ctrlKey) { + this.setMultiSelect(true); } + const selectedFiles = []; + const selectionRanges = table.getSelectionModel().getSelectedRanges(); + selectionRanges.forEach(range => { + for (let i=range.minIndex; i<=range.maxIndex; i++) { + const row = table.getTableModel().getRowData(i); + if (osparc.file.FilesTree.isFile(row.entry)) { + selectedFiles.push(row.entry); + } + } + }); + this.__selectionChanged(selectedFiles); }, this); table.addListener("cellDbltap", e => { const selectedRow = e.getRow(); diff --git a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js index c44f48cbe27..af2ca15fb79 100644 --- a/services/static-webserver/client/source/class/osparc/file/FolderViewer.js +++ b/services/static-webserver/client/source/class/osparc/file/FolderViewer.js @@ -52,18 +52,21 @@ qx.Class.define("osparc.file.FolderViewer", { this.bind("folder", folderContent, "folder"); if (allowMultiselection) { + multiSelectButton.bind("value", folderContent, "multiSelect"); + folderContent.bind("multiSelect", multiSelectButton, "value"); multiSelectButton.addListener("changeValue", e => { - folderContent.setMultiSelect(e.getData()); selectedFileLayout.setMultiSelect(e.getData()); }); } gridViewButton.addListener("execute", () => { folderContent.setMode("icons"); - selectedFileLayout.resetItemSelected(); + selectedFileLayout.resetSelection(); + multiSelectButton.setValue(false); }); listViewButton.addListener("execute", () => { folderContent.setMode("list"); - selectedFileLayout.resetItemSelected(); + selectedFileLayout.resetSelection(); + multiSelectButton.setValue(false); }); folderContent.addListener("requestDatasetFiles", e => this.fireDataEvent("requestDatasetFiles", e.getData())); @@ -176,7 +179,7 @@ qx.Class.define("osparc.file.FolderViewer", { }, __applyFolder: function() { - this.getChildControl("selected-file-layout").resetItemSelected(); + this.getChildControl("selected-file-layout").resetSelection(); } } });