--- /dev/null
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import harbour.file.browser.SearchEngine 1.0
+import "functions.js" as Functions
+import "../components"
+
+Page {
+ id: page
+ allowedOrientations: Orientation.All
+ showNavigationIndicator: false // hide back indicator because it would be on top of search field
+ property string dir: "/"
+ property string currentDirectory: ""
+
+ // this and its bg worker thread will be destroyed when page in popped from stack
+ SearchEngine {
+ id: searchEngine
+ dir: page.dir
+
+ onProgressChanged: page.currentDirectory = directory
+ onMatchFound: listModel.append({ fullname: fullname, filename: filename,
+ absoluteDir: absoluteDir,
+ fileIcon: fileIcon, fileKind: fileKind });
+ onWorkerDone: { /* Nothing to do */ }
+ onWorkerErrorOccurred: { notificationPanel.showText(message, filename); }
+ }
+
+ SilicaListView {
+ id: fileList
+ anchors.fill: parent
+
+ // prevent newly added list delegates from stealing focus away from the search field
+ currentIndex: -1
+
+ model: ListModel {
+ id: listModel
+
+ function update(txt) {
+ if (txt === "")
+ searchEngine.cancel();
+
+ clear();
+ if (txt !== "") {
+ searchEngine.search(txt);
+ }
+ }
+
+ Component.onCompleted: update("")
+ }
+
+ VerticalScrollDecorator { flickable: fileList }
+
+ PullDownMenu {
+ MenuItem {
+ text: qsTr("Settings")
+ onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
+ }
+ }
+
+ header: Item {
+ width: parent.width
+ height: 110
+
+ SearchField {
+ id: searchField
+ anchors.left: parent.left
+ anchors.right: cancelSearchButton.left
+ placeholderText: qsTr("Search %1").arg(Functions.formatPathForSearch(page.dir))
+ inputMethodHints: Qt.ImhNoAutoUppercase
+
+ // get focus when page is shown for the first time
+ Component.onCompleted: forceActiveFocus()
+
+ // return key on virtual keyboard starts or restarts search
+ EnterKey.enabled: true
+ EnterKey.onClicked: {
+ notificationPanel.hide();
+ listModel.update(searchField.text);
+ foundText.visible = true;
+ searchField.focus = false;
+ }
+ }
+ // our own "IconButton" to make the mouse area large and easier to tap
+ Rectangle {
+ id: cancelSearchButton
+ anchors.right: parent.right
+ anchors.top: searchField.top
+ width: Theme.iconSizeMedium+Theme.paddingLarge
+ height: searchField.height
+ color: cancelSearchMouseArea.pressed ? Theme.secondaryHighlightColor : "transparent"
+ MouseArea {
+ id: cancelSearchMouseArea
+ anchors.fill: parent
+ onClicked: {
+ if (!searchEngine.running) {
+ listModel.update(searchField.text);
+ foundText.visible = true;
+ } else {
+ searchEngine.cancel()
+ }
+ }
+ enabled: true
+ Image {
+ id: cancelSearchButtonImage
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.paddingLarge
+ source: searchEngine.running ? "image://theme/icon-m-clear" :
+ "image://theme/icon-m-right"
+ }
+ BusyIndicator {
+ id: searchBusy
+ anchors.centerIn: cancelSearchButtonImage
+ running: searchEngine.running
+ size: BusyIndicatorSize.Small
+ }
+ }
+ }
+ Label {
+ id: foundText
+ visible: false
+ anchors.left: parent.left
+ anchors.leftMargin: searchField.textLeftMargin
+ anchors.top: searchField.bottom
+ anchors.topMargin: -26
+ text: qsTr("%1 hits").arg(listModel.count)
+ font.pixelSize: Theme.fontSizeTiny
+ color: Theme.secondaryColor
+ }
+ Label {
+ anchors.left: parent.left
+ anchors.leftMargin: 240
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.paddingLarge
+ anchors.top: searchField.bottom
+ anchors.topMargin: -26
+ text: page.currentDirectory
+ font.pixelSize: Theme.fontSizeTiny
+ color: Theme.secondaryColor
+ elide: Text.ElideRight
+ }
+ }
+
+ delegate: ListItem {
+ id: fileItem
+ menu: contextMenu
+ width: ListView.view.width
+ contentHeight: listLabel.height+listAbsoluteDir.height + 13
+
+ Image {
+ id: listIcon
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.paddingLarge
+ anchors.top: parent.top
+ anchors.topMargin: 11
+ source: "../images/small-"+fileIcon+".png"
+ }
+ Label {
+ id: listLabel
+ anchors.left: listIcon.right
+ anchors.leftMargin: 10
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.paddingLarge
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ text: filename
+ elide: Text.ElideRight
+ }
+ Label {
+ id: listAbsoluteDir
+ anchors.left: listIcon.right
+ anchors.leftMargin: 10
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.paddingLarge
+ anchors.top: listLabel.bottom
+ text: absoluteDir
+ color: Theme.secondaryColor
+ font.pixelSize: Theme.fontSizeExtraSmall
+ elide: Text.ElideLeft
+ }
+
+ onClicked: {
+ if (model.fileKind === "d")
+ pageStack.push(Qt.resolvedUrl("DirectoryPage.qml"),
+ { dir: model.fullname });
+ else
+ pageStack.push(Qt.resolvedUrl("FilePage.qml"),
+ { file: model.fullname });
+ }
+
+ // delete file after remorse time
+ ListView.onRemove: animateRemoval(fileItem)
+ function deleteFile(deleteFilename) {
+ remorseAction(qsTr("Deleting"), function() {
+ progressPanel.showText(qsTr("Deleting"));
+ engine.deleteFiles([ deleteFilename ]);
+ }, 5000)
+ }
+
+ // context menu is activated with long press, visible if search is not running
+ Component {
+ id: contextMenu
+ ContextMenu {
+ MenuItem {
+ text: qsTr("Go to containing folder")
+ onClicked: Functions.goToFolder(model.absoluteDir)
+ }
+ MenuItem {
+ text: qsTr("Cut")
+ onClicked: engine.cutFiles([ model.fullname ]);
+ }
+ MenuItem {
+ text: qsTr("Copy")
+ onClicked: engine.copyFiles([ model.fullname ]);
+ }
+ MenuItem {
+ text: qsTr("Delete")
+ onClicked: deleteFile(model.fullname);
+ }
+ }
+ }
+ }
+
+ }
+
+ // connect signals from engine to panels
+ Connections {
+ target: engine
+ onProgressChanged: progressPanel.text = engine.progressFilename
+ onWorkerDone: progressPanel.hide()
+ onWorkerErrorOccurred: {
+ // the error signal goes to all pages in pagestack, show it only in the active one
+ if (progressPanel.open) {
+ progressPanel.hide();
+ notificationPanel.showText(message, filename);
+ }
+ }
+
+ // item got deleted by worker, so remove it from list
+ onFileDeleted: {
+ for (var i = 0; i < listModel.count; ++i) {
+ var item = listModel.get(i);
+ if (item.fullname === fullname) {
+ listModel.remove(i)
+ return;
+ }
+ }
+ }
+ }
+
+ NotificationPanel {
+ id: notificationPanel
+ page: page
+ }
+
+ ProgressPanel {
+ id: progressPanel
+ page: page
+ onCancelled: engine.cancel()
+ }
+}
+
+