Integrated file selection dialogue with the main code. Improved the
[openvpnui.git] / qml / filebrowse / pages / SearchPage.qml
1 import QtQuick 2.0
2 import Sailfish.Silica 1.0
3 import harbour.file.browser.SearchEngine 1.0
4 import "functions.js" as Functions
5 import "../components"
6
7 Page {
8     id: page
9     allowedOrientations: Orientation.All
10     showNavigationIndicator: false // hide back indicator because it would be on top of search field
11     property string dir: "/"
12     property string currentDirectory: ""
13
14     // this and its bg worker thread will be destroyed when page in popped from stack
15     SearchEngine {
16         id: searchEngine
17         dir: page.dir
18
19         onProgressChanged: page.currentDirectory = directory
20         onMatchFound: listModel.append({ fullname: fullname, filename: filename,
21                                            absoluteDir: absoluteDir,
22                                            fileIcon: fileIcon, fileKind: fileKind });
23         onWorkerDone: { /* Nothing to do */ }
24         onWorkerErrorOccurred: { notificationPanel.showText(message, filename); }
25     }
26
27     SilicaListView {
28         id: fileList
29         anchors.fill: parent
30
31         // prevent newly added list delegates from stealing focus away from the search field
32         currentIndex: -1
33
34         model: ListModel {
35             id: listModel
36
37             function update(txt) {
38                 if (txt === "")
39                     searchEngine.cancel();
40
41                 clear();
42                 if (txt !== "") {
43                     searchEngine.search(txt);
44                 }
45             }
46
47             Component.onCompleted: update("")
48         }
49
50         VerticalScrollDecorator { flickable: fileList }
51
52         PullDownMenu {
53             MenuItem {
54                 text: qsTr("Settings")
55                 onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
56             }
57         }
58
59         header: Item {
60             width: parent.width
61             height: 110
62
63             SearchField {
64                 id: searchField
65                 anchors.left: parent.left
66                 anchors.right: cancelSearchButton.left
67                 placeholderText: qsTr("Search %1").arg(Functions.formatPathForSearch(page.dir))
68                 inputMethodHints: Qt.ImhNoAutoUppercase
69
70                 // get focus when page is shown for the first time
71                 Component.onCompleted: forceActiveFocus()
72
73                 // return key on virtual keyboard starts or restarts search
74                 EnterKey.enabled: true
75                 EnterKey.onClicked: {
76                     notificationPanel.hide();
77                     listModel.update(searchField.text);
78                     foundText.visible = true;
79                     searchField.focus = false;
80                 }
81             }
82             // our own "IconButton" to make the mouse area large and easier to tap
83             Rectangle {
84                 id: cancelSearchButton
85                 anchors.right: parent.right
86                 anchors.top: searchField.top
87                 width: Theme.iconSizeMedium+Theme.paddingLarge
88                 height: searchField.height
89                 color: cancelSearchMouseArea.pressed ? Theme.secondaryHighlightColor : "transparent"
90                 MouseArea {
91                     id: cancelSearchMouseArea
92                     anchors.fill: parent
93                     onClicked: {
94                         if (!searchEngine.running) {
95                             listModel.update(searchField.text);
96                             foundText.visible = true;
97                         } else {
98                             searchEngine.cancel()
99                         }
100                     }
101                     enabled: true
102                     Image {
103                         id: cancelSearchButtonImage
104                         anchors.verticalCenter: parent.verticalCenter
105                         anchors.right: parent.right
106                         anchors.rightMargin: Theme.paddingLarge
107                         source: searchEngine.running ? "image://theme/icon-m-clear" :
108                                                        "image://theme/icon-m-right"
109                     }
110                     BusyIndicator {
111                         id: searchBusy
112                         anchors.centerIn: cancelSearchButtonImage
113                         running: searchEngine.running
114                         size: BusyIndicatorSize.Small
115                     }
116                 }
117             }
118             Label {
119                 id: foundText
120                 visible: false
121                 anchors.left: parent.left
122                 anchors.leftMargin: searchField.textLeftMargin
123                 anchors.top: searchField.bottom
124                 anchors.topMargin: -26
125                 text: qsTr("%1 hits").arg(listModel.count)
126                 font.pixelSize: Theme.fontSizeTiny
127                 color: Theme.secondaryColor
128             }
129             Label {
130                 anchors.left: parent.left
131                 anchors.leftMargin: 240
132                 anchors.right: parent.right
133                 anchors.rightMargin: Theme.paddingLarge
134                 anchors.top: searchField.bottom
135                 anchors.topMargin: -26
136                 text: page.currentDirectory
137                 font.pixelSize: Theme.fontSizeTiny
138                 color: Theme.secondaryColor
139                 elide: Text.ElideRight
140             }
141         }
142
143         delegate: ListItem {
144             id: fileItem
145             menu: contextMenu
146             width: ListView.view.width
147             contentHeight: listLabel.height+listAbsoluteDir.height + 13
148
149             Image {
150                 id: listIcon
151                 anchors.left: parent.left
152                 anchors.leftMargin: Theme.paddingLarge
153                 anchors.top: parent.top
154                 anchors.topMargin: 11
155                 source: "../images/small-"+fileIcon+".png"
156             }
157             Label {
158                 id: listLabel
159                 anchors.left: listIcon.right
160                 anchors.leftMargin: 10
161                 anchors.right: parent.right
162                 anchors.rightMargin: Theme.paddingLarge
163                 anchors.top: parent.top
164                 anchors.topMargin: 5
165                 text: filename
166                 elide: Text.ElideRight
167             }
168             Label {
169                 id: listAbsoluteDir
170                 anchors.left: listIcon.right
171                 anchors.leftMargin: 10
172                 anchors.right: parent.right
173                 anchors.rightMargin: Theme.paddingLarge
174                 anchors.top: listLabel.bottom
175                 text: absoluteDir
176                 color: Theme.secondaryColor
177                 font.pixelSize: Theme.fontSizeExtraSmall
178                 elide: Text.ElideLeft
179             }
180
181             onClicked: {
182                 if (model.fileKind === "d")
183                     pageStack.push(Qt.resolvedUrl("DirectoryPage.qml"),
184                                    { dir: model.fullname });
185                 else
186                     pageStack.push(Qt.resolvedUrl("FilePage.qml"),
187                                    { file: model.fullname });
188             }
189
190             // delete file after remorse time
191             ListView.onRemove: animateRemoval(fileItem)
192             function deleteFile(deleteFilename) {
193                 remorseAction(qsTr("Deleting"), function() {
194                     progressPanel.showText(qsTr("Deleting"));
195                     engine.deleteFiles([ deleteFilename ]);
196                 }, 5000)
197             }
198
199             // context menu is activated with long press, visible if search is not running
200             Component {
201                  id: contextMenu
202                  ContextMenu {
203                      MenuItem {
204                          text: qsTr("Go to containing folder")
205                          onClicked: Functions.goToFolder(model.absoluteDir)
206                      }
207                      MenuItem {
208                          text: qsTr("Cut")
209                          onClicked: engine.cutFiles([ model.fullname ]);
210                      }
211                      MenuItem {
212                          text: qsTr("Copy")
213                          onClicked: engine.copyFiles([ model.fullname ]);
214                      }
215                      MenuItem {
216                          text: qsTr("Delete")
217                          onClicked: deleteFile(model.fullname);
218                      }
219                  }
220              }
221         }
222
223     }
224
225     // connect signals from engine to panels
226     Connections {
227         target: engine
228         onProgressChanged: progressPanel.text = engine.progressFilename
229         onWorkerDone: progressPanel.hide()
230         onWorkerErrorOccurred: {
231             // the error signal goes to all pages in pagestack, show it only in the active one
232             if (progressPanel.open) {
233                 progressPanel.hide();
234                 notificationPanel.showText(message, filename);
235             }
236         }
237
238         // item got deleted by worker, so remove it from list
239         onFileDeleted: {
240             for (var i = 0; i < listModel.count; ++i) {
241                 var item = listModel.get(i);
242                 if (item.fullname === fullname) {
243                     listModel.remove(i)
244                     return;
245                 }
246             }
247         }
248     }
249
250     NotificationPanel {
251         id: notificationPanel
252         page: page
253     }
254
255     ProgressPanel {
256         id: progressPanel
257         page: page
258         onCancelled: engine.cancel()
259     }
260 }
261
262