839 lines · 29.2 KB · Text
RawDownload
////  FileDialog.qml////  Created by Bradley Austin Davis on 14 Jan 2016//  Copyright 2015 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// import QtQuick 2.7import Qt.labs.folderlistmodel 2.2import Qt.labs.settings 1.0import QtQuick.Dialogs 1.2 as OriginalDialogsimport QtQuick.Controls 1.4 as QQC1import QtQuick.Controls 2.3 import ".."import controlsUit 1.0import stylesUit 1.0import "../windows" import "fileDialog" //FIXME implement shortcuts for favorite locationModalWindow {    id: root    resizable: true    implicitWidth: 480    implicitHeight: 360 + (fileDialogItem.keyboardEnabled && fileDialogItem.keyboardRaised ? keyboard.raisedHeight + hifi.dimensions.contentSpacing.y : 0)     minSize: Qt.vector2d(360, 240)    draggable: true     HifiConstants { id: hifi }     property var filesModel: ListModel { }     Settings {        category: "FileDialog"        property alias width: root.width        property alias height: root.height        property alias x: root.x        property alias y: root.y    }      // Set from OffscreenUi::getOpenFile()    property alias caption: root.title;    // Set from OffscreenUi::getOpenFile()    property alias dir: fileTableModel.folder;    // Set from OffscreenUi::getOpenFile()    property alias filter: selectionType.filtersString;    // Set from OffscreenUi::getOpenFile()    property int options; // <-- FIXME unused     property string iconText: root.title !== "" ? hifi.glyphs.scriptUpload : ""    property int iconSize: 40     property bool selectDirectory: false;    property bool showHidden: true;    // FIXME implement    property bool multiSelect: false;    property bool saveDialog: false;    property var helper: fileDialogHelper    property alias model: fileTableView.model    property var drives: helper.drives()     property int titleWidth: 0     signal selectedFile(var file);    signal canceled();    signal selected(int button);    function click(button) {        clickedButton = button;        selected(button);        destroy();    }	    property int clickedButton: OriginalDialogs.StandardButton.NoButton;	    Component.onCompleted: {        fileDialogItem.keyboardEnabled = HMD.active;         // HACK: The following lines force the model to initialize properly such that the go-up button        // works properly from the initial screen.        var initialFolder = folderListModel.folder;        fileTableModel.folder = helper.pathToUrl(drives[0]);        fileTableModel.folder = initialFolder;         iconText = root.title !== "" ? hifi.glyphs.scriptUpload : "";         // Clear selection when click on external frame.        frameClicked.connect(function() { d.clearSelection(); });         if (selectDirectory) {            currentSelection.text = d.capitalizeDrive(helper.urlToPath(initialFolder));            d.currentSelectionIsFolder = true;            d.currentSelectionUrl = initialFolder;        }         helper.contentsChanged.connect(function() {            if (folderListModel) {                // Make folderListModel refresh.                var save = folderListModel.folder;                folderListModel.folder = "";                folderListModel.folder = save;            }        });         focusTimer.start();    }        Timer {        id: focusTimer        interval: 10        running: false        repeat: false        onTriggered: {            fileTableView.contentItem.forceActiveFocus();        }    }     Item {        id: fileDialogItem        clip: true        width: pane.width        height: pane.height        anchors.margins: 0         property bool keyboardEnabled: false        property bool keyboardRaised: false        property bool punctuationMode: false         MouseArea {            // Clear selection when click on internal unused area.            anchors.fill: parent            drag.target: root            onClicked: {                d.clearSelection();                // Defocus text field so that the keyboard gets hidden.                // Clicking also breaks keyboard navigation apart from backtabbing to cancel                 frame.forceActiveFocus();            }        }         Row {            id: navControls            anchors {                top: parent.top                topMargin: hifi.dimensions.contentMargin.y                left: parent.left            }            spacing: hifi.dimensions.contentSpacing.x             GlyphButton {                id: upButton                glyph: hifi.glyphs.levelUp                width: height                size: 30                enabled: fileTableModel.parentFolder && fileTableModel.parentFolder !== ""                onClicked: d.navigateUp();                Keys.onReturnPressed: { d.navigateUp(); }                KeyNavigation.tab: homeButton                KeyNavigation.backtab: upButton                KeyNavigation.left: upButton                KeyNavigation.right: homeButton            }             GlyphButton {                id: homeButton                property var destination: helper.home();                glyph: hifi.glyphs.home                size: 28                width: height                enabled: d.homeDestination ? true : false                onClicked: d.navigateHome();                Keys.onReturnPressed: { d.navigateHome(); }                KeyNavigation.tab: fileTableView.contentItem                KeyNavigation.backtab: upButton                KeyNavigation.left: upButton            }        }         ComboBox {            id: pathSelector            anchors {                top: parent.top                topMargin: hifi.dimensions.contentMargin.y                left: navControls.right                leftMargin: hifi.dimensions.contentSpacing.x                right: parent.right            }             property var lastValidFolder: helper.urlToPath(fileTableModel.folder)             function calculatePathChoices(folder) {                var folders = folder.split("/"),                    choices = [],                    i, length;                 if (folders[folders.length - 1] === "") {                    folders.pop();                }                 choices.push(folders[0]);                 for (i = 1, length = folders.length; i < length; i++) {                    choices.push(choices[i - 1] + "/" + folders[i]);                }                 if (folders[0] === "") {                    // Special handling for OSX root dir.                    choices[0] = "/";                }                 choices.reverse();                 if (drives && drives.length > 1) {                    choices.push("This PC");                }                 if (choices.length > 0) {                    pathSelector.model = choices;                }            }             onLastValidFolderChanged: {                var folder = d.capitalizeDrive(lastValidFolder);                calculatePathChoices(folder);            }             onCurrentTextChanged: {                var folder = currentText;                 if (/^[a-zA-z]:$/.test(folder)) {                    folder = "file:///" + folder + "/";                } else if (folder === "This PC") {                    folder = "file:///";                } else {                    folder = helper.pathToUrl(folder);                }                 if (helper.urlToPath(folder).toLowerCase() !== helper.urlToPath(fileTableModel.folder).toLowerCase()) {                    if (root.selectDirectory) {                        currentSelection.text = currentText !== "This PC" ? currentText : "";                        d.currentSelectionUrl = helper.pathToUrl(currentText);                    }                    fileTableModel.folder = folder;                }            }             KeyNavigation.up: fileTableView.contentItem            KeyNavigation.down: fileTableView.contentItem            KeyNavigation.tab: fileTableView.contentItem            KeyNavigation.backtab: fileTableView.contentItem            KeyNavigation.left: fileTableView.contentItem            KeyNavigation.right: fileTableView.contentItem        }         QtObject {            id: d            property var currentSelectionUrl;            readonly property string currentSelectionPath: helper.urlToPath(currentSelectionUrl);            property bool currentSelectionIsFolder;            property var backStack: []            property var tableViewConnection: Connections { target: fileTableView; function onCurrentRowChanged() { d.update(); } }            property var modelConnection: Connections { target: fileTableModel; function onFolderChanged() { d.update(); } }            property var homeDestination: helper.home();             function capitalizeDrive(path) {                // Consistently capitalize drive letter for Windows.                if (/[a-zA-Z]:/.test(path)) {                    return path.charAt(0).toUpperCase() + path.slice(1);                }                return path;            }             function update() {                var row = fileTableView.currentRow;                 if (row === -1) {                    if (!root.selectDirectory) {                        currentSelection.text = "";                        currentSelectionIsFolder = false;                    }                    return;                }                 currentSelectionUrl = helper.pathToUrl(fileTableView.model.get(row).filePath);                currentSelectionIsFolder = fileTableView.model !== filesModel ?                            fileTableView.model.isFolder(row) :                            fileTableModel.isFolder(row);                if (root.selectDirectory || !currentSelectionIsFolder) {                    currentSelection.text = capitalizeDrive(helper.urlToPath(currentSelectionUrl));                } else {                    currentSelection.text = "";                }            }             function navigateUp() {                if (fileTableModel.parentFolder && fileTableModel.parentFolder !== "") {                    fileTableModel.folder = fileTableModel.parentFolder;                    return true;                }            }             function navigateHome() {                fileTableModel.folder = homeDestination;                return true;            }             function clearSelection() {                fileTableView.selection.clear();                fileTableView.currentRow = -1;                update();            }        }         FolderListModel {            id: folderListModel            nameFilters: selectionType.currentFilter            caseSensitive: false            showDirsFirst: true            showDotAndDotDot: false            showFiles: !root.selectDirectory            showHidden: root.showHidden            Component.onCompleted: {                showFiles = !root.selectDirectory                showHidden = root.showHidden            }             onFolderChanged: {                d.clearSelection();                fileTableModel.update();  // Update once the data from the folder change is available.            }             function getItem(index, field) {                return get(index, field);            }        }         ListModel {            // Emulates FolderListModel but contains drive data.            id: driveListModel             property int count: 1             Component.onCompleted: initialize();             function initialize() {                var drive,                    i;                 count = drives.length;                 for (i = 0; i < count; i++) {                    drive = drives[i].slice(0, -1);  // Remove trailing "/".                    append({                       fileName: drive,                       fileModified: new Date(0),                       fileSize: 0,                       filePath: drive + "/",                       fileIsDir: true,                       fileNameSort: drive.toLowerCase()                    });                }            }             function getItem(index, field) {                return get(index)[field];            }        }         Component {            id: filesModelBuilder            ListModel { }        }         QtObject {            id: fileTableModel             // FolderListModel has a couple of problems:            // 1) Files and directories sort case-sensitively: https://bugreports.qt.io/browse/QTBUG-48757            // 2) Cannot browse up to the "computer" level to view Windows drives: https://bugreports.qt.io/browse/QTBUG-42901            //            // To solve these problems an intermediary ListModel is used that implements proper sorting and can be populated with            // drive information when viewing at the computer level.             property var folder            property int sortOrder: Qt.AscendingOrder            property int sortColumn: 0            property var model: folderListModel            property string parentFolder: calculateParentFolder();             readonly property string rootFolder: "file:///"             function calculateParentFolder() {                if (model === folderListModel) {                    if (folderListModel.parentFolder.toString() === "" && driveListModel.count > 1) {                        return rootFolder;                    } else {                        return folderListModel.parentFolder;                    }                } else {                    return "";                }            }             onFolderChanged: {                if (folder === rootFolder) {                    model = driveListModel;                    helper.monitorDirectory("");                    update();                } else {                    var needsUpdate = model === driveListModel && folder === folderListModel.folder;                     model = folderListModel;                    folderListModel.folder = folder;                    helper.monitorDirectory(helper.urlToPath(folder));                     if (needsUpdate) {                        update();                    }                }            }             function isFolder(row) {                if (row === -1) {                    return false;                }                return filesModel.get(row).fileIsDir;            }             function get(row) {                return filesModel.get(row)            }             function update() {                var dataFields = ["fileName", "fileModified", "fileSize"],                    sortFields = ["fileNameSort", "fileModified", "fileSize"],                    dataField = dataFields[sortColumn],                    sortField = sortFields[sortColumn],                    sortValue,                    fileName,                    fileIsDir,                    comparisonFunction,                    lower,                    middle,                    upper,                    rows = 0,                    i;                 filesModel = filesModelBuilder.createObject(root);                 comparisonFunction = sortOrder === Qt.AscendingOrder                    ? function(a, b) { return a < b; }                    : function(a, b) { return a > b; }                 for (i = 0; i < model.count; i++) {                    fileName = model.getItem(i, "fileName");                    fileIsDir = model.getItem(i, "fileIsDir");                     sortValue = model.getItem(i, dataField);                    if (dataField === "fileName") {                        // Directories first by prefixing a "*".                        // Case-insensitive.                        sortValue = (fileIsDir ? "*" : "") + sortValue.toLowerCase();                    }                     lower = 0;                    upper = rows;                    while (lower < upper) {                        middle = Math.floor((lower + upper) / 2);                        var lessThan;                        if (comparisonFunction(sortValue, filesModel.get(middle)[sortField])) {                            lessThan = true;                            upper = middle;                        } else {                            lessThan = false;                            lower = middle + 1;                        }                    }                     filesModel.insert(lower, {                       fileName: fileName,                       fileModified: (fileIsDir ? new Date(0) : model.getItem(i, "fileModified")),                       fileSize: model.getItem(i, "fileSize"),                       filePath: model.getItem(i, "filePath"),                       fileIsDir: fileIsDir,                       fileNameSort: (fileIsDir ? "*" : "") + fileName.toLowerCase()                    });                     rows++;                }            }        }         Table {            id: fileTableView

Lines 1–500 of 839.