Merge lp:~ibelieve/ubuntu-filemanager-app/code-refactoring into lp:ubuntu-filemanager-app

Proposed by Michael Spencer on 2014-06-16
Status: Work in progress
Proposed branch: lp:~ibelieve/ubuntu-filemanager-app/code-refactoring
Merge into: lp:ubuntu-filemanager-app
Diff against target: 1747 lines (+777/-627)
14 files modified
src/app/qml/backend/FolderModel.qml (+303/-0)
src/app/qml/components/FolderIconDelegate.qml (+1/-1)
src/app/qml/components/FolderListDelegate.qml (+1/-1)
src/app/qml/components/OverlayStandard.qml (+55/-0)
src/app/qml/components/PathBar.qml (+1/-1)
src/app/qml/components/PlacesSidebar.qml (+6/-40)
src/app/qml/filemanager.qml (+9/-24)
src/app/qml/ui/FileActionsPopover.qml (+114/-0)
src/app/qml/ui/FileDetailsPopover.qml (+2/-2)
src/app/qml/ui/FileOperationProgressDialog.qml (+1/-1)
src/app/qml/ui/FolderActionsPopover.qml (+116/-0)
src/app/qml/ui/FolderListPage.qml (+160/-515)
src/app/qml/ui/GoToDialog.qml (+2/-2)
src/app/qml/ui/PlacesPopover.qml (+6/-40)
To merge this branch: bzr merge lp:~ibelieve/ubuntu-filemanager-app/code-refactoring
Reviewer Review Type Date Requested Status
Ubuntu File Manager Developers 2014-06-16 Pending
Review via email: mp+223305@code.launchpad.net

Commit message

Cleaned up the code, reenabled multi-tab support, and fixed a bug

Description of the change

Lots of work on refactoring code, including:

 * Moved all of the model code from FolderListPage into a separate FolderModel class
 * Moved some of the popovers from FOlderListPage into separate classes
 * Moved the places model from PlacesSidbar and PlacesPopover into FolderModel, removing the extra duplicate

Also, fixed some other bugs:

 * Renabled multi-tab support, as this was fixed in the SDK
 * Expanding the app's window automatically expands the sidebar if it was previously expanded

To post a comment you must log in.

Unmerged revisions

207. By Michael Spencer on 2014-06-16

Sidebar expands automatically when changing the size of the app on the desktop

206. By Michael Spencer on 2014-06-16

Re-enabled multi-tab support

205. By Michael Spencer on 2014-06-16

More code refactoring
 * split some popovers into separate classes
 * Use local icons from the Suru theme
 * Moved the places list into one location in the FolderModel

204. By Michael Spencer on 2014-06-16

Initial refactoring of the model code into a separate backend class

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'src/app/qml/backend'
2=== added file 'src/app/qml/backend/FolderModel.qml'
3--- src/app/qml/backend/FolderModel.qml 1970-01-01 00:00:00 +0000
4+++ src/app/qml/backend/FolderModel.qml 2014-06-16 21:32:33 +0000
5@@ -0,0 +1,303 @@
6+/*
7+ * Copyright (C) 2014 Michael Spencer <sonrisesoftware@gmail.com>
8+ *
9+ * This program is free software: you can redistribute it and/or modify
10+ * it under the terms of the GNU General Public License version 3 as
11+ * published by the Free Software Foundation.
12+ *
13+ * This program is distributed in the hope that it will be useful,
14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+ * GNU General Public License for more details.
17+ *
18+ * You should have received a copy of the GNU General Public License
19+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
20+ */
21+import QtQuick 2.0
22+import Ubuntu.Components 0.1
23+import org.nemomobile.folderlistmodel 1.0
24+
25+Object {
26+ id: model
27+
28+ property bool loading: folderModel.awaitingResults
29+
30+ property alias folderModel: folderModel
31+
32+ /*!
33+ * The user-friendly title for the current directory
34+ */
35+ property string title: folderName(folder)
36+
37+ /*!
38+ * Set to <code>true</code> true to show hidden files begining with a .
39+ * in the current directory
40+ */
41+ property bool showHiddenFiles: false
42+
43+ // TODO: Can this be replaced by a property binding?
44+ onShowHiddenFilesChanged: {
45+ folderModel.showHiddenFiles = folderListPage.showHiddenFiles
46+ }
47+
48+ /*!
49+ * The sorting mode for the current directory.
50+ *
51+ * Can be one of: Name or Date
52+ */
53+ property string sortingMethod: "Name"
54+
55+ // TODO: Can this be replaced by a property binding?
56+ onSortingMethodChanged: {
57+ console.log("Sorting by: " + sortingMethod)
58+ if (sortingMethod === "Name") {
59+ folderModel.sortBy = FolderListModel.SortByName
60+ } else if (sortingMethod === "Date") {
61+ folderModel.sortBy = FolderListModel.SortByDate
62+ } else {
63+ // Something fatal happened!
64+ console.log("ERROR: Invalid sort type:", sortingMethod)
65+ }
66+ }
67+ /*!
68+ * Set to <code>true</code> true to sort A-Z, or <code>false</code>
69+ * to sort in reverse, from Z to A.
70+ */
71+ property bool sortAccending: true
72+
73+ // TODO: Can this be replaced by a property binding?
74+ onSortAccendingChanged: {
75+ console.log("Sorting accending: " + sortAccending)
76+
77+ if (sortAccending) {
78+ folderModel.sortOrder = FolderListModel.SortAscending
79+ } else {
80+ folderModel.sortOrder = FolderListModel.SortDescending
81+ }
82+ }
83+
84+ // This stores the location using ~ to represent home
85+ property string folder: homeFolder
86+ property string homeFolder: "~"
87+
88+ // This replaces ~ with the actual home folder, since the
89+ // plugin doesn't recognize the ~
90+ property string path: folder.replace("~", folderModel.homePath())
91+ property string homePath: folderModel.homePath()
92+
93+
94+ /*!
95+ * Switches directories to the user's home directory
96+ */
97+ function goHome() {
98+ goTo(model.homeFolder)
99+ }
100+
101+ /*!
102+ * Switches directories to the specified path. Can use either /home/<user> or ~
103+ * to represent the user's home folder
104+ */
105+ function goTo(location) {
106+ // Since the FolderListModel returns paths using the actual
107+ // home folder, this replaces with ~ before actually going
108+ // to the specified folder
109+ while (location !== '/' && location.substring(location.lastIndexOf('/')+1) === "") {
110+ location = location.substring(0, location.length - 1)
111+ }
112+
113+ model.folder = location.replace(homePath, "~")
114+ refresh()
115+ }
116+
117+ /*!
118+ * Opens the specified file in the default app for the file's type
119+ */
120+ function openFile(filePath) {
121+ if (!folderModel.openPath(filePath)) {
122+ error(i18n.tr("File operation error"), i18n.tr("Unable to open '%11").arg(filePath))
123+ }
124+ }
125+
126+ /*!
127+ * Refreshes the folder model
128+ */
129+ // TODO: Is this needed?
130+ function refresh() {
131+ folderModel.refresh()
132+ }
133+
134+ /*!
135+ * Returns the user-friendly name for the specified path. Can use either
136+ * /home/<user> or ~ to represent the user's home folder
137+ */
138+ function folderName(folder) {
139+ folder = folder.replace(homePath, "~")
140+
141+ if (folder === homeFolder) {
142+ return i18n.tr("Home")
143+ } else if (folder === "/") {
144+ return i18n.tr("File System")
145+ } else {
146+ return pathName(folder)
147+ }
148+ }
149+
150+ /*!
151+ * Returns the last element of the specified path, or / for the
152+ * root folder.
153+ */
154+ function pathName(folder) {
155+ if (folder === "/") {
156+ return "/"
157+ } else {
158+ return folder.substr(folder.lastIndexOf('/') + 1)
159+ }
160+ }
161+
162+ function pathAccessedDate() {
163+ console.log("calling method folderModel.curPathAccessedDate()")
164+ return folderModel.curPathAccessedDate()
165+ }
166+
167+ function pathModifiedDate() {
168+ console.log("calling method folderModel.curPathModifiedDate()")
169+ return folderModel.curPathModifiedDate()
170+ }
171+
172+ function pathIsWritable() {
173+ console.log("calling method folderModel.curPathIsWritable()")
174+ return folderModel.curPathIsWritable()
175+ }
176+
177+ /*!
178+ * Returns true if the specified path exists in the file system.
179+ */
180+ function pathExists(path) {
181+ path = path.replace("~", folderModel.homePath())
182+
183+ if (path === '/')
184+ return true
185+
186+ if(path.charAt(0) === '/') {
187+ console.log("Directory: " + path.substring(0, path.lastIndexOf('/')+1))
188+ repeaterModel.path = path.substring(0, path.lastIndexOf('/')+1)
189+ console.log("Sub dir: " + path.substring(path.lastIndexOf('/')+1))
190+ if (path.substring(path.lastIndexOf('/')+1) !== "" && !repeaterModel.cdIntoPath(path.substring(path.lastIndexOf('/')+1))) {
191+ return false
192+ } else {
193+ return true
194+ }
195+ } else {
196+ return false
197+ }
198+ }
199+
200+
201+ /*!
202+ * Returns the icon for the specified file or folder.
203+ */
204+ // FIXME: hard coded path for icon, assumes Ubuntu desktop icon available.
205+ // Nemo mobile has icon provider. Have to figure out what's the proper way
206+ // to get "system wide" icons in Ubuntu Touch, or if we have to use
207+ // icons packaged into the application. Both folder and individual
208+ // files will need an icon.
209+ // TODO: Remove isDir parameter and use new model functions
210+ function fileIcon(file, isDir) {
211+ file = file.replace(folderModel.homePath(), "~")
212+ var iconPath = isDir ? "/usr/share/icons/Humanity/places/48/folder.svg"
213+ : "/usr/share/icons/Humanity/mimes/48/empty.svg"
214+
215+ if (file === "~") {
216+ iconPath = "../icons/folder-home.svg"
217+ } else if (file === i18n.tr("~/Desktop")) {
218+ iconPath = "/usr/share/icons/Humanity/places/48/user-desktop.svg"
219+ } else if (file === i18n.tr("~/Documents")) {
220+ iconPath = "/usr/share/icons/Humanity/places/48/folder-documents.svg"
221+ } else if (file === i18n.tr("~/Downloads")) {
222+ iconPath = "/usr/share/icons/Humanity/places/48/folder-downloads.svg"
223+ } else if (file === i18n.tr("~/Music")) {
224+ iconPath = "/usr/share/icons/Humanity/places/48/folder-music.svg"
225+ } else if (file === i18n.tr("~/Pictures")) {
226+ iconPath = "/usr/share/icons/Humanity/places/48/folder-pictures.svg"
227+ } else if (file === i18n.tr("~/Public")) {
228+ iconPath = "/usr/share/icons/Humanity/places/48/folder-publicshare.svg"
229+ } else if (file === i18n.tr("~/Programs")) {
230+ iconPath = "/usr/share/icons/Humanity/places/48/folder-system.svg"
231+ } else if (file === i18n.tr("~/Templates")) {
232+ iconPath = "/usr/share/icons/Humanity/places/48/folder-templates.svg"
233+ } else if (file === i18n.tr("~/Videos")) {
234+ iconPath = "/usr/share/icons/Humanity/places/48/folder-videos.svg"
235+ } else if (file === "/") {
236+ iconPath = "/usr/share/icons/Humanity/devices/48/drive-harddisk.svg"
237+ }
238+
239+ return Qt.resolvedUrl(iconPath)
240+ }
241+
242+ /*!
243+ * The places list for the sidebar
244+ */
245+ property ListModel places: ListModel {
246+ ListElement {
247+ objectName: "placeHome"
248+ path: "~"
249+ }
250+
251+ ListElement {
252+ path: "~/Documents"
253+ }
254+
255+ ListElement {
256+ path: "~/Downloads"
257+ }
258+
259+ ListElement {
260+ path: "~/Music"
261+ }
262+
263+ ListElement {
264+ path: "~/Pictures"
265+ }
266+
267+ ListElement {
268+ path: "~/Videos"
269+ }
270+
271+ ListElement {
272+ objectName: "placeRoot"
273+ path: "/"
274+ }
275+ }
276+
277+ /*!
278+ * The folder model used to represent the currently selected folder
279+ */
280+ FolderListModel {
281+ id: folderModel
282+
283+ path: model.path
284+
285+ enableExternalFSWatcher: true
286+
287+ // Properties to emulate a model entry for use by FileDetailsPopover
288+ property bool isDir: true
289+ property string fileName: pathName(folderModel.path)
290+ property string fileSize: (folderListView.count === 1
291+ ? i18n.tr("1 file")
292+ : i18n.tr("%1 files").arg(folderListView.count))
293+ property bool isReadable: true
294+ property bool isExecutable: true
295+ }
296+
297+ /*!
298+ * The folder model used for checking to see if a specified path exists
299+ */
300+ FolderListModel {
301+ id: repeaterModel
302+ path: model.folder
303+
304+ onPathChanged: {
305+ console.log("Path: " + repeaterModel.path)
306+ }
307+ }
308+}
309
310=== modified file 'src/app/qml/components/FolderIconDelegate.qml'
311--- src/app/qml/components/FolderIconDelegate.qml 2014-03-29 09:20:11 +0000
312+++ src/app/qml/components/FolderIconDelegate.qml 2014-06-16 21:32:33 +0000
313@@ -52,7 +52,7 @@
314 property string text: fileName
315 property string subText: Qt.formatDateTime(model.modifiedDate, Qt.DefaultLocaleShortDate) + (!model.isDir ? ", " + fileSize : "")
316
317- property var icon: fileIcon(filePath, model.isDir)
318+ property var icon: folderModel.fileIcon(filePath, model.isDir)
319
320 Item {
321 anchors {
322
323=== modified file 'src/app/qml/components/FolderListDelegate.qml'
324--- src/app/qml/components/FolderListDelegate.qml 2014-03-29 09:20:11 +0000
325+++ src/app/qml/components/FolderListDelegate.qml 2014-06-16 21:32:33 +0000
326@@ -30,7 +30,7 @@
327 subText: Qt.formatDateTime(model.modifiedDate, Qt.DefaultLocaleShortDate) + (!model.isDir ? ", " + fileSize : "")
328
329 property string path: fileView.path + '/' + model.fileName
330- iconSource: fileIcon(path, model.isDir)
331+ iconSource: folderModel.fileIcon(path, model.isDir)
332
333 progression: model.isDir
334 iconFrame: false
335
336=== added file 'src/app/qml/components/OverlayStandard.qml'
337--- src/app/qml/components/OverlayStandard.qml 1970-01-01 00:00:00 +0000
338+++ src/app/qml/components/OverlayStandard.qml 2014-06-16 21:32:33 +0000
339@@ -0,0 +1,55 @@
340+/***************************************************************************
341+ * Ubuntu UI Extras - A collection of QML widgets not available *
342+ * in the default Ubuntu UI Toolkit *
343+ * Copyright (C) 2013 Michael Spencer <sonrisesoftware@gmail.com> *
344+ * *
345+ * This program is free software: you can redistribute it and/or modify *
346+ * it under the terms of the GNU General Public License as published by *
347+ * the Free Software Foundation, either version 2 of the License, or *
348+ * (at your option) any later version. *
349+ * *
350+ * This program is distributed in the hope that it will be useful, *
351+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
352+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
353+ * GNU General Public License for more details. *
354+ * *
355+ * You should have received a copy of the GNU General Public License *
356+ * along with this program. If not, see <http://www.gnu.org/licenses/>. *
357+ ***************************************************************************/
358+import QtQuick 2.0
359+import Ubuntu.Components 1.1
360+import Ubuntu.Components.ListItems 1.0
361+
362+Standard {
363+ id: standard
364+
365+ property alias text: label.text
366+ property alias iconSource: image.source
367+
368+ Image {
369+ id: image
370+ width: units.gu(3.5)
371+ height: width
372+
373+ anchors {
374+ verticalCenter: parent.verticalCenter
375+ left: parent.left
376+ leftMargin: units.gu(2)
377+ }
378+ opacity: standard.enabled ? 1 : 0.5
379+ }
380+
381+ //FIXME: Hack because of Suru theme!
382+ Label {
383+ id: label
384+ anchors {
385+ verticalCenter: parent.verticalCenter
386+ left: parent.left
387+ leftMargin: image.source === "" ? units.gu(2) : image.width + units.gu(4)
388+ }
389+
390+ fontSize: "medium"
391+ color: standard.selected ? UbuntuColors.orange : "#666"
392+ opacity: standard.enabled ? 1 : 0.5
393+ }
394+}
395
396=== modified file 'src/app/qml/components/PathBar.qml'
397--- src/app/qml/components/PathBar.qml 2014-06-03 23:27:22 +0000
398+++ src/app/qml/components/PathBar.qml 2014-06-16 21:32:33 +0000
399@@ -87,7 +87,7 @@
400
401 Repeater {
402 id: repeater
403- model: folder === "/" ? [""] : folder.split("/")
404+ model: folderModel.folder === "/" ? [""] : folderModel.folder.split("/")
405 delegate: Rectangle {
406 MouseArea {
407 id: mouseArea
408
409=== modified file 'src/app/qml/components/PlacesSidebar.qml'
410--- src/app/qml/components/PlacesSidebar.qml 2014-06-10 02:34:07 +0000
411+++ src/app/qml/components/PlacesSidebar.qml 2014-06-16 21:32:33 +0000
412@@ -27,7 +27,7 @@
413 //color: Qt.rgba(0.5,0.5,0.5,0.3)
414 width: collapsed ? units.gu(8) : units.gu(22)
415
416- property bool collapsed: collapsedSidebar
417+ property bool collapsed: collapsedSidebar || !allowSidebarExpanded
418
419 MouseArea {
420 anchors.fill: parent
421@@ -42,40 +42,6 @@
422 UbuntuNumberAnimation {}
423 }
424
425- ListModel {
426- id: places
427-
428- ListElement {
429- objectName: "placeHome"
430- path: "~"
431- }
432-
433- ListElement {
434- path: "~/Documents"
435- }
436-
437- ListElement {
438- path: "~/Downloads"
439- }
440-
441- ListElement {
442- path: "~/Music"
443- }
444-
445- ListElement {
446- path: "~/Pictures"
447- }
448-
449- ListElement {
450- path: "~/Videos"
451- }
452-
453- ListElement {
454- objectName: "placeRoot"
455- path: "/"
456- }
457- }
458-
459 Column {
460 anchors {
461 left: parent.left
462@@ -91,11 +57,11 @@
463 id: placesList
464 objectName: "placesList"
465
466- model: places
467+ model: folderModel.places
468
469 delegate: Standard {
470 objectName: model.objectName
471- text: folderName(path)
472+ text: folderModel.folderName(path)
473
474 Image {
475 anchors {
476@@ -114,16 +80,16 @@
477 }
478 }
479
480- iconSource: model.icon || fileIcon(model.path, true)
481+ iconSource: model.icon || folderModel.fileIcon(model.path, true)
482
483 onClicked: {
484- goTo(model.path)
485+ folderModel.goTo(model.path)
486 }
487
488 height: units.gu(5)
489 showDivider: !collapsed
490
491- selected: folder === path
492+ selected: folderModel.folder === path
493 iconFrame: false
494 }
495 }
496
497=== modified file 'src/app/qml/filemanager.qml'
498--- src/app/qml/filemanager.qml 2014-04-29 18:05:45 +0000
499+++ src/app/qml/filemanager.qml 2014-06-16 21:32:33 +0000
500@@ -47,11 +47,6 @@
501
502 property bool allowSidebarExpanded: width >= units.gu(80)
503
504- onAllowSidebarExpandedChanged: {
505- if (!allowSidebarExpanded)
506- saveSetting("collapsedSidebar", true)
507- }
508-
509 property bool showSidebar: width >= units.gu(50)
510
511 property bool showToolbar: width >= units.gu(80)
512@@ -95,27 +90,17 @@
513 Tabs {
514 id: tabs
515
516- Tab {
517- title: page.title
518- page: FolderListPage {
519- objectName: "folderPage"
520+ Repeater {
521+ model: folderTabs
522+ delegate: Tab {
523+ title: page.title
524+ page: FolderListPage {
525+ objectName: "folderPage"
526
527- folder: "~"//modelData
528+ folder: modelData
529+ }
530 }
531 }
532-
533- // TODO: Temporarily disabled tabs support since this is broken in the SDK (lp:1295242)
534-// Repeater {
535-// model: folderTabs
536-// delegate: Tab {
537-// title: page.title
538-// page: FolderListPage {
539-// objectName: "folderPage"
540-
541-// folder: modelData
542-// }
543-// }
544-// }
545 }
546
547 Component.onCompleted: {
548@@ -192,7 +177,7 @@
549 }
550
551 function getIcon(name) {
552- return "/usr/share/icons/ubuntu-mobile/actions/scalable/" + name + ".svg" //Qt.resolvedUrl("icons/" + name + ".png")
553+ return Qt.resolvedUrl("icons/" + name + ".png")
554 }
555
556 function error(title, message) {
557
558=== added file 'src/app/qml/icons/add.png'
559Binary files src/app/qml/icons/add.png 1970-01-01 00:00:00 +0000 and src/app/qml/icons/add.png 2014-06-16 21:32:33 +0000 differ
560=== added file 'src/app/qml/icons/delete.png'
561Binary files src/app/qml/icons/delete.png 1970-01-01 00:00:00 +0000 and src/app/qml/icons/delete.png 2014-06-16 21:32:33 +0000 differ
562=== added file 'src/app/qml/icons/edit-copy.png'
563Binary files src/app/qml/icons/edit-copy.png 1970-01-01 00:00:00 +0000 and src/app/qml/icons/edit-copy.png 2014-06-16 21:32:33 +0000 differ
564=== added file 'src/app/qml/icons/edit-cut.png'
565Binary files src/app/qml/icons/edit-cut.png 1970-01-01 00:00:00 +0000 and src/app/qml/icons/edit-cut.png 2014-06-16 21:32:33 +0000 differ
566=== added file 'src/app/qml/icons/edit-paste.png'
567Binary files src/app/qml/icons/edit-paste.png 1970-01-01 00:00:00 +0000 and src/app/qml/icons/edit-paste.png 2014-06-16 21:32:33 +0000 differ
568=== modified file 'src/app/qml/icons/edit.png'
569Binary files src/app/qml/icons/edit.png 2013-09-17 16:47:57 +0000 and src/app/qml/icons/edit.png 2014-06-16 21:32:33 +0000 differ
570=== added file 'src/app/qml/icons/info.png'
571Binary files src/app/qml/icons/info.png 1970-01-01 00:00:00 +0000 and src/app/qml/icons/info.png 2014-06-16 21:32:33 +0000 differ
572=== modified file 'src/app/qml/icons/location.png'
573Binary files src/app/qml/icons/location.png 2013-09-17 16:47:57 +0000 and src/app/qml/icons/location.png 2014-06-16 21:32:33 +0000 differ
574=== modified file 'src/app/qml/icons/navigation-menu.png'
575Binary files src/app/qml/icons/navigation-menu.png 2013-11-28 01:55:21 +0000 and src/app/qml/icons/navigation-menu.png 2014-06-16 21:32:33 +0000 differ
576=== modified file 'src/app/qml/icons/properties.png'
577Binary files src/app/qml/icons/properties.png 2013-12-04 19:05:26 +0000 and src/app/qml/icons/properties.png 2014-06-16 21:32:33 +0000 differ
578=== added file 'src/app/qml/ui/FileActionsPopover.qml'
579--- src/app/qml/ui/FileActionsPopover.qml 1970-01-01 00:00:00 +0000
580+++ src/app/qml/ui/FileActionsPopover.qml 2014-06-16 21:32:33 +0000
581@@ -0,0 +1,114 @@
582+/*
583+ * Copyright (C) 2014 Michael Spencer <sonrisesoftware@gmail.com>
584+ *
585+ * This program is free software: you can redistribute it and/or modify
586+ * it under the terms of the GNU General Public License version 3 as
587+ * published by the Free Software Foundation.
588+ *
589+ * This program is distributed in the hope that it will be useful,
590+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
591+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
592+ * GNU General Public License for more details.
593+ *
594+ * You should have received a copy of the GNU General Public License
595+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
596+ */
597+import QtQuick 2.0
598+import Ubuntu.Components 0.1
599+import Ubuntu.Components.Popups 0.1
600+import Ubuntu.Components.ListItems 0.1 as ListItem
601+import "../components"
602+
603+Popover {
604+ id: root
605+
606+ property var model
607+
608+ property list<Action> actions: [
609+ Action {
610+ text: i18n.tr("Cut")
611+ iconSource: getIcon("edit-cut")
612+ onTriggered: {
613+ console.log("Cut on row called for", root.model.fileName, root.model.index)
614+ folderModel.folderModel.cutIndex(root.model.index)
615+ console.log("CliboardUrlsCounter after copy", folderModel.folderModel.clipboardUrlsCounter)
616+ }
617+ },
618+
619+ Action {
620+ text: i18n.tr("Copy")
621+ iconSource: getIcon("edit-copy")
622+
623+ onTriggered: {
624+ console.log("Copy on row called for", root.model.fileName, root.model.index)
625+ folderModel.folderModel.copyIndex(root.model.index)
626+ console.log("CliboardUrlsCounter after copy", folderModel.folderModel.clipboardUrlsCounter)
627+ }
628+ },
629+
630+ Action {
631+ text: i18n.tr("Delete")
632+ iconSource: getIcon("delete")
633+ onTriggered: {
634+ print(text)
635+ PopupUtils.open(confirmSingleDeleteDialog, root.caller,
636+ { "filePath" : root.model.filePath,
637+ "fileName" : root.model.fileName }
638+ )
639+ }
640+ },
641+
642+ Action {
643+ text: i18n.tr("Rename")
644+ iconSource: getIcon("edit")
645+ onTriggered: {
646+ print(text)
647+ PopupUtils.open(confirmRenameDialog, root.caller,
648+ { "modelRow" : root.model.index,
649+ "inputText" : root.model.fileName
650+ })
651+ }
652+ },
653+
654+ Action {
655+ text: i18n.tr("Properties")
656+ iconSource: getIcon("info")
657+ onTriggered: {
658+ print(text)
659+ PopupUtils.open(Qt.resolvedUrl("FileDetailsPopover.qml"),
660+ value,
661+ { "model": root.model
662+ }
663+ )
664+ }
665+ }
666+ ]
667+
668+ Column {
669+ anchors {
670+ left: parent.left
671+ right: parent.right
672+ }
673+
674+ Repeater {
675+ id: repeater
676+ model: actions
677+ delegate: OverlayStandard {
678+ property Action action: modelData
679+
680+ visible: action.visible
681+ enabled: action.enabled
682+ text: action.text
683+ iconSource: action.iconSource
684+ iconFrame: false
685+
686+ onClicked: {
687+ action.triggered(root.caller)
688+ PopupUtils.close(root)
689+ }
690+
691+ showDivider: index < repeater.count - 1
692+ }
693+ }
694+ }
695+}
696
697=== modified file 'src/app/qml/ui/FileDetailsPopover.qml'
698--- src/app/qml/ui/FileDetailsPopover.qml 2014-02-19 00:00:15 +0000
699+++ src/app/qml/ui/FileDetailsPopover.qml 2014-06-16 21:32:33 +0000
700@@ -71,13 +71,13 @@
701 anchors.verticalCenter: parent.verticalCenter
702
703 // TODO: how to get proper icon?
704- source: fileIcon(root.path, model.isDir)
705+ source: folderModel.fileIcon(root.path, model.isDir)
706 }
707
708 Label {
709 anchors.verticalCenter: parent.verticalCenter
710
711- text: folderName(root.path)
712+ text: folderModel.folderName(root.path)
713 color: Theme.palette.normal.overlayText
714 font.bold: true
715 }
716
717=== modified file 'src/app/qml/ui/FileOperationProgressDialog.qml'
718--- src/app/qml/ui/FileOperationProgressDialog.qml 2014-02-19 00:00:15 +0000
719+++ src/app/qml/ui/FileOperationProgressDialog.qml 2014-06-16 21:32:33 +0000
720@@ -78,7 +78,7 @@
721
722 // Errors from model
723 Connections {
724- target: pageModel
725+ target: folderModel
726 onError: {
727 PopupUtils.close(root)
728 }
729
730=== added file 'src/app/qml/ui/FolderActionsPopover.qml'
731--- src/app/qml/ui/FolderActionsPopover.qml 1970-01-01 00:00:00 +0000
732+++ src/app/qml/ui/FolderActionsPopover.qml 2014-06-16 21:32:33 +0000
733@@ -0,0 +1,116 @@
734+/*
735+ * Copyright (C) 2014 Michael Spencer <sonrisesoftware@gmail.com>
736+ *
737+ * This program is free software: you can redistribute it and/or modify
738+ * it under the terms of the GNU General Public License version 3 as
739+ * published by the Free Software Foundation.
740+ *
741+ * This program is distributed in the hope that it will be useful,
742+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
743+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
744+ * GNU General Public License for more details.
745+ *
746+ * You should have received a copy of the GNU General Public License
747+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
748+ */
749+import QtQuick 2.0
750+import Ubuntu.Components 0.1
751+import Ubuntu.Components.Popups 0.1
752+import Ubuntu.Components.ListItems 0.1 as ListItem
753+import "../components"
754+
755+Popover {
756+ id: root
757+
758+ property list<Action> actions: [
759+ Action {
760+ text: i18n.tr("Create New Folder")
761+ iconSource: getIcon("add")
762+ onTriggered: {
763+ print(text)
764+
765+ PopupUtils.open(createFolderDialog, folderListPage)
766+ }
767+ },
768+
769+ // TODO: Disabled until backend supports creating files
770+// Action {
771+// text: i18n.tr("Create New File")
772+// onTriggered: {
773+// print(text)
774+
775+// PopupUtils.open(createFileDialog, root)
776+// }
777+// },
778+
779+ Action {
780+ text: folderModel.folderModel.clipboardUrlsCounter === 0
781+ ? i18n.tr("Paste")
782+ : folderModel.folderModel.clipboardUrlsCounter === 1
783+ ? i18n.tr("Paste %1 File").arg(folderModel.folderModel.clipboardUrlsCounter)
784+ : i18n.tr("Paste %1 Files").arg(folderModel.folderModel.clipboardUrlsCounter)
785+ onTriggered: {
786+ console.log("Pasting to current folder items of count " + folderModel.folderModel.clipboardUrlsCounter)
787+ fileOperationDialog.startOperation(i18n.tr("Paste files"))
788+ folderModel.folderModel.paste()
789+ }
790+ iconSource: getIcon("edit-paste")
791+
792+ enabled: folderModel.folderModel.clipboardUrlsCounter > 0
793+ },
794+
795+ // TODO: Disabled until support for opening apps is added
796+ Action {
797+ text: i18n.tr("Open in Terminal")
798+ onTriggered: {
799+ print(text)
800+
801+ // Is this the way it will work??
802+ Qt.openUrlExternally("app://terminal")
803+ }
804+
805+ visible: showAdvancedFeatures && false
806+ },
807+
808+ Action {
809+ text: i18n.tr("Properties")
810+ iconSource: getIcon("info")
811+ onTriggered: {
812+ print(text)
813+ PopupUtils.open(Qt.resolvedUrl("FileDetailsPopover.qml"),
814+ folderListPage,
815+ { "model": folderModel.folderModel
816+ }
817+ )
818+ }
819+ }
820+ ]
821+
822+ Column {
823+ anchors {
824+ left: parent.left
825+ right: parent.right
826+ }
827+
828+ Repeater {
829+ id: repeater
830+ model: actions
831+ delegate: OverlayStandard {
832+ property Action action: modelData
833+
834+ visible: action.visible
835+ enabled: action.enabled
836+ text: action.text
837+ iconSource: action.iconSource
838+ iconFrame: false
839+
840+ onClicked: {
841+ action.triggered(root.caller)
842+ PopupUtils.close(root)
843+ }
844+
845+ showDivider: index < repeater.count - 1
846+ }
847+ }
848+ }
849+}
850
851=== modified file 'src/app/qml/ui/FolderListPage.qml'
852--- src/app/qml/ui/FolderListPage.qml 2014-04-30 08:54:08 +0000
853+++ src/app/qml/ui/FolderListPage.qml 2014-06-16 21:32:33 +0000
854@@ -19,339 +19,105 @@
855 import Ubuntu.Components 0.1
856 import Ubuntu.Components.Popups 0.1
857 import Ubuntu.Components.ListItems 0.1
858-import org.nemomobile.folderlistmodel 1.0
859 import "../components"
860+import "../backend"
861
862 Page {
863 id: folderListPage
864
865- title: folderName(folder)
866+ title: folderModel.title
867+
868+ property alias folder: folderModel.folder
869
870 property variant fileView: folderListPage
871
872- property bool showHiddenFiles: false
873-
874 property bool showingListView: folderListView.visible
875
876- onShowHiddenFilesChanged: {
877- pageModel.showHiddenFiles = folderListPage.showHiddenFiles
878- }
879-
880- property string sortingMethod: "Name"
881-
882- onSortingMethodChanged: {
883- console.log("Sorting by: " + sortingMethod)
884- if (sortingMethod === "Name") {
885- pageModel.sortBy = FolderListModel.SortByName
886- } else if (sortingMethod === "Date") {
887- pageModel.sortBy = FolderListModel.SortByDate
888- } else {
889- // Something fatal happened!
890- console.log("ERROR: Invalid sort type:", sortingMethod)
891- }
892- }
893-
894- property bool sortAccending: true
895-
896- onSortAccendingChanged: {
897- console.log("Sorting accending: " + sortAccending)
898-
899- if (sortAccending) {
900- pageModel.sortOrder = FolderListModel.SortAscending
901- } else {
902- pageModel.sortOrder = FolderListModel.SortDescending
903- }
904- }
905-
906- // This stores the location using ~ to represent home
907- property string folder
908- property string homeFolder: "~"
909-
910- // This replaces ~ with the actual home folder, since the
911- // plugin doesn't recognize the ~
912- property string path: folder.replace("~", pageModel.homePath())
913-
914- function goHome() {
915- goTo(folderListPage.homeFolder)
916- }
917-
918- function goTo(location) {
919- // Since the FolderListModel returns paths using the actual
920- // home folder, this replaces with ~ before actually going
921- // to the specified folder
922- while (location !== '/' && location.substring(location.lastIndexOf('/')+1) === "") {
923- location = location.substring(0, location.length - 1)
924- }
925-
926- folderListPage.folder = location.replace(pageModel.homePath(), "~")
927- refresh()
928- }
929-
930- function refresh() {
931- pageModel.refresh()
932- }
933-
934- function pathAccessedDate() {
935- console.log("calling method pageModel.curPathAccessedDate()")
936- return pageModel.curPathAccessedDate()
937- }
938-
939- function pathModifiedDate() {
940- console.log("calling method pageModel.curPathModifiedDate()")
941- return pageModel.curPathModifiedDate()
942- }
943-
944- function pathIsWritable() {
945- console.log("calling method pageModel.curPathIsWritable()")
946- return pageModel.curPathIsWritable()
947- }
948-
949- // FIXME: hard coded path for icon, assumes Ubuntu desktop icon available.
950- // Nemo mobile has icon provider. Have to figure out what's the proper way
951- // to get "system wide" icons in Ubuntu Touch, or if we have to use
952- // icons packaged into the application. Both folder and individual
953- // files will need an icon.
954- // TODO: Remove isDir parameter and use new model functions
955- function fileIcon(file, isDir) {
956- file = file.replace(pageModel.homePath(), "~")
957- var iconPath = isDir ? "/usr/share/icons/Humanity/places/48/folder.svg"
958- : "/usr/share/icons/Humanity/mimes/48/empty.svg"
959-
960- if (file === "~") {
961- iconPath = "../icons/folder-home.svg"
962- } else if (file === i18n.tr("~/Desktop")) {
963- iconPath = "/usr/share/icons/Humanity/places/48/user-desktop.svg"
964- } else if (file === i18n.tr("~/Documents")) {
965- iconPath = "/usr/share/icons/Humanity/places/48/folder-documents.svg"
966- } else if (file === i18n.tr("~/Downloads")) {
967- iconPath = "/usr/share/icons/Humanity/places/48/folder-downloads.svg"
968- } else if (file === i18n.tr("~/Music")) {
969- iconPath = "/usr/share/icons/Humanity/places/48/folder-music.svg"
970- } else if (file === i18n.tr("~/Pictures")) {
971- iconPath = "/usr/share/icons/Humanity/places/48/folder-pictures.svg"
972- } else if (file === i18n.tr("~/Public")) {
973- iconPath = "/usr/share/icons/Humanity/places/48/folder-publicshare.svg"
974- } else if (file === i18n.tr("~/Programs")) {
975- iconPath = "/usr/share/icons/Humanity/places/48/folder-system.svg"
976- } else if (file === i18n.tr("~/Templates")) {
977- iconPath = "/usr/share/icons/Humanity/places/48/folder-templates.svg"
978- } else if (file === i18n.tr("~/Videos")) {
979- iconPath = "/usr/share/icons/Humanity/places/48/folder-videos.svg"
980- } else if (file === "/") {
981- iconPath = "/usr/share/icons/Humanity/devices/48/drive-harddisk.svg"
982- }
983-
984- return Qt.resolvedUrl(iconPath)
985- }
986-
987- function folderName(folder) {
988- folder = folder.replace(pageModel.homePath(), "~")
989-
990- if (folder === folderListPage.homeFolder) {
991- return i18n.tr("Home")
992- } else if (folder === "/") {
993- return i18n.tr("File System")
994- } else {
995- return folder.substr(folder.lastIndexOf('/') + 1)
996- }
997- }
998-
999- function pathName(folder) {
1000- if (folder === "/") {
1001- return "/"
1002- } else {
1003- return folder.substr(folder.lastIndexOf('/') + 1)
1004- }
1005- }
1006-
1007- function pathExists(path) {
1008- path = path.replace("~", pageModel.homePath())
1009-
1010- if (path === '/')
1011- return true
1012-
1013- if(path.charAt(0) === '/') {
1014- console.log("Directory: " + path.substring(0, path.lastIndexOf('/')+1))
1015- repeaterModel.path = path.substring(0, path.lastIndexOf('/')+1)
1016- console.log("Sub dir: " + path.substring(path.lastIndexOf('/')+1))
1017- if (path.substring(path.lastIndexOf('/')+1) !== "" && !repeaterModel.cdIntoPath(path.substring(path.lastIndexOf('/')+1))) {
1018- return false
1019- } else {
1020- return true
1021- }
1022- } else {
1023- return false
1024- }
1025- }
1026-
1027- property bool loading: pageModel.awaitingResults
1028-
1029- FolderListModel {
1030- id: pageModel
1031-
1032- path: folderListPage.path
1033-
1034- enableExternalFSWatcher: true
1035-
1036- // Properties to emulate a model entry for use by FileDetailsPopover
1037- property bool isDir: true
1038- property string fileName: pathName(pageModel.path)
1039- property string fileSize: (folderListView.count === 1
1040- ? i18n.tr("1 file")
1041- : i18n.tr("%1 files").arg(folderListView.count))
1042- property bool isReadable: true
1043- property bool isExecutable: true
1044- }
1045-
1046- FolderListModel {
1047- id: repeaterModel
1048- path: folderListPage.folder
1049-
1050- onPathChanged: {
1051- console.log("Path: " + repeaterModel.path)
1052- }
1053- }
1054-
1055- Component {
1056- id: tabsPopover
1057- ActionSelectionPopover {
1058- objectName: "tabsPopover"
1059-
1060- property var tab
1061-
1062- grabDismissAreaEvents: true
1063-
1064- actions: ActionList {
1065- Action {
1066- text: i18n.tr("Open in a new tab")
1067- onTriggered: {
1068- openTab(folderListPage.folder)
1069- }
1070- }
1071-
1072- // The current tab can be closed as long as there is at least one tab remaining
1073- Action {
1074- text: i18n.tr("Close this tab")
1075- onTriggered: {
1076- closeTab(tab.index)
1077- }
1078- enabled: tabs.count > 1
1079- }
1080- }
1081- }
1082- }
1083-
1084- Component {
1085- id: folderActionsPopoverComponent
1086- ActionSelectionPopover {
1087- id: folderActionsPopover
1088- objectName: "folderActionsPopover"
1089-
1090- grabDismissAreaEvents: true
1091-
1092- actions: ActionList {
1093- Action {
1094- text: i18n.tr("Create New Folder")
1095- onTriggered: {
1096- print(text)
1097-
1098- PopupUtils.open(createFolderDialog, folderListPage)
1099- }
1100- }
1101-
1102- // TODO: Disabled until backend supports creating files
1103- // Action {
1104- // text: i18n.tr("Create New File")
1105- // onTriggered: {
1106- // print(text)
1107-
1108- // PopupUtils.open(createFileDialog, root)
1109- // }
1110- // }
1111-
1112- Action {
1113- text: pageModel.clipboardUrlsCounter === 0
1114- ? i18n.tr("Paste")
1115- : pageModel.clipboardUrlsCounter === 1
1116- ? i18n.tr("Paste %1 File").arg(pageModel.clipboardUrlsCounter)
1117- : i18n.tr("Paste %1 Files").arg(pageModel.clipboardUrlsCounter)
1118- onTriggered: {
1119- console.log("Pasting to current folder items of count " + pageModel.clipboardUrlsCounter)
1120- fileOperationDialog.startOperation(i18n.tr("Paste files"))
1121- pageModel.paste()
1122- }
1123-
1124- // FIXME: This property is depreciated and doesn't seem to work!
1125- //visible: pageModel.clipboardUrlsCounter > 0
1126-
1127- enabled: pageModel.clipboardUrlsCounter > 0
1128- }
1129-
1130- // TODO: Disabled until support for opening apps is added
1131- Action {
1132- text: i18n.tr("Open in Terminal")
1133- onTriggered: {
1134- print(text)
1135-
1136- // Is this the way it will work??
1137- Qt.openUrlExternally("app://terminal")
1138- }
1139-
1140- enabled: showAdvancedFeatures && false
1141- }
1142-
1143- Action {
1144- text: i18n.tr("Properties")
1145- onTriggered: {
1146- print(text)
1147- PopupUtils.open(Qt.resolvedUrl("FileDetailsPopover.qml"),
1148- folderListPage,
1149- { "model": pageModel
1150- }
1151- )
1152- }
1153- }
1154- }
1155- }
1156- }
1157-
1158- Component {
1159- id: createFolderDialog
1160- ConfirmDialogWithInput {
1161- title: i18n.tr("Create folder")
1162- text: i18n.tr("Enter name for new folder")
1163-
1164- onAccepted: {
1165- console.log("Create folder accepted", inputText)
1166- if (inputText !== '') {
1167- pageModel.mkdir(inputText)
1168- } else {
1169- console.log("Empty directory name, ignored")
1170- }
1171- }
1172- }
1173- }
1174-
1175- Component {
1176- id: createFileDialog
1177- ConfirmDialogWithInput {
1178- title: i18n.tr("Create file")
1179- text: i18n.tr("Enter name for new file")
1180-
1181- onAccepted: {
1182- console.log("Create file accepted", inputText)
1183- if (inputText !== '') {
1184- //FIXME: Actually create a new file!
1185- } else {
1186- console.log("Empty file name, ignored")
1187- }
1188- }
1189- }
1190- }
1191-
1192- function openFile(filePath) {
1193- if (!pageModel.openPath(filePath)) {
1194- error(i18n.tr("File operation error"), i18n.tr("Unable to open '%11").arg(filePath))
1195+ FolderModel {
1196+ id: folderModel
1197+ }
1198+
1199+ flickable: !sidebar.expanded ? folderListView.visible ? folderListView : folderIconView.flickable : null
1200+
1201+ onFlickableChanged: {
1202+ if (flickable === null) {
1203+ folderListView.topMargin = 0
1204+ folderIconView.flickable.topMargin = 0
1205+ } else {
1206+ folderListView.topMargin = units.gu(9.5)
1207+ folderIconView.flickable.topMargin = units.gu(9.5)
1208+ }
1209+ }
1210+
1211+ PlacesSidebar {
1212+ id: sidebar
1213+ objectName: "placesSidebar"
1214+
1215+// anchors {
1216+// top: parent.top
1217+// bottom: parent.bottom
1218+// bottomMargin: units.gu(-2)
1219+// }
1220+
1221+ expanded: showSidebar
1222+ }
1223+
1224+ FolderIconView {
1225+ id: folderIconView
1226+
1227+ clip: true
1228+
1229+ folderListModel: folderModel.folderModel
1230+ anchors {
1231+ top: parent.top
1232+ bottom: parent.bottom
1233+ left: sidebar.right
1234+ right: parent.right
1235+ }
1236+ smallMode: !sidebar.expanded
1237+ visible: viewMethod === i18n.tr("Icons")
1238+ }
1239+
1240+ FolderListView {
1241+ id: folderListView
1242+
1243+ clip: true
1244+
1245+ folderListModel: folderModel.folderModel
1246+ anchors {
1247+ top: parent.top
1248+ bottom: parent.bottom
1249+ left: sidebar.right
1250+ right: parent.right
1251+ }
1252+ smallMode: !sidebar.expanded
1253+ visible: viewMethod === i18n.tr("List")
1254+ }
1255+
1256+ Item {
1257+ id: contents
1258+
1259+ anchors {
1260+ top: parent.top
1261+ bottom: parent.bottom
1262+ left: sidebar.right
1263+ right: parent.right
1264+ }
1265+
1266+
1267+ Label {
1268+ text: i18n.tr("No files")
1269+ fontSize: "large"
1270+ opacity: 0.5
1271+ anchors.centerIn: parent
1272+ visible: folderListView.count == 0 && !folderModel.folderModel.awaitingResults
1273+ }
1274+
1275+ ActivityIndicator {
1276+ running: folderModel.folderModel.awaitingResults
1277+ width: units.gu(8)
1278+ height: units.gu(8)
1279+ anchors.centerIn: parent
1280 }
1281 }
1282
1283@@ -366,16 +132,15 @@
1284 objectName: "up"
1285 text: "Up"
1286 iconSource: getIcon("keyboard-caps")
1287- enabled: folder != "/"
1288+ enabled: folderModel.folder != "/"
1289 onTriggered: {
1290- goTo(pageModel.parentPath)
1291+ goTo(folderModel.folderModel.parentPath)
1292 }
1293 }
1294
1295 Item {
1296 id: pathItem
1297- // TODO: Uncomment after re-enabling tab support (caused by lp:1295242)
1298- width: folderListPage.width - units.gu(31)//folderListPage.width - units.gu(37)
1299+ width: folderListPage.width - units.gu(37)
1300 height: units.gu(5)
1301 anchors.verticalCenter: parent.verticalCenter
1302 PathBar {
1303@@ -398,7 +163,7 @@
1304
1305 onTriggered: {
1306 print(text)
1307- PopupUtils.open(folderActionsPopoverComponent, actionsButton)
1308+ PopupUtils.open(Qt.resolvedUrl("FolderActionsPopover.qml"), actionsButton)
1309 }
1310 }
1311
1312@@ -427,21 +192,20 @@
1313 }
1314 }
1315
1316- // TODO: Uncomment after re-enabling tab support (caused by lp:1295242)
1317-// ToolbarButton {
1318-// id: tabsButton
1319-// objectName: "tabs"
1320-// text: i18n.tr("Tabs")
1321-// iconSource: getIcon("browser-tabs")
1322-
1323-// onTriggered: {
1324-// print(text)
1325-
1326-// PopupUtils.open(tabsPopover, tabsButton, {
1327-// tab: folderListPage.parent
1328-// })
1329-// }
1330-// }
1331+ ToolbarButton {
1332+ id: tabsButton
1333+ objectName: "tabs"
1334+ text: i18n.tr("Tabs")
1335+ iconSource: getIcon("browser-tabs")
1336+
1337+ onTriggered: {
1338+ print(text)
1339+
1340+ PopupUtils.open(tabsPopover, tabsButton, {
1341+ tab: folderListPage.parent
1342+ })
1343+ }
1344+ }
1345
1346 ToolbarButton {
1347 id: settingsButton
1348@@ -451,90 +215,6 @@
1349 }
1350 }
1351
1352- flickable: !sidebar.expanded ? folderListView.visible ? folderListView : folderIconView.flickable : null
1353-
1354- onFlickableChanged: {
1355- if (flickable === null) {
1356- folderListView.topMargin = 0
1357- folderIconView.flickable.topMargin = 0
1358- } else {
1359- folderListView.topMargin = units.gu(9.5)
1360- folderIconView.flickable.topMargin = units.gu(9.5)
1361- }
1362- }
1363-
1364- PlacesSidebar {
1365- id: sidebar
1366- objectName: "placesSidebar"
1367-
1368-// anchors {
1369-// top: parent.top
1370-// bottom: parent.bottom
1371-// bottomMargin: units.gu(-2)
1372-// }
1373-
1374- expanded: showSidebar
1375- }
1376-
1377- FolderIconView {
1378- id: folderIconView
1379-
1380- clip: true
1381-
1382- folderListModel: pageModel
1383- anchors {
1384- top: parent.top
1385- bottom: parent.bottom
1386- left: sidebar.right
1387- right: parent.right
1388- }
1389- smallMode: !sidebar.expanded
1390- visible: viewMethod === i18n.tr("Icons")
1391- }
1392-
1393- FolderListView {
1394- id: folderListView
1395-
1396- clip: true
1397-
1398- folderListModel: pageModel
1399- anchors {
1400- top: parent.top
1401- bottom: parent.bottom
1402- left: sidebar.right
1403- right: parent.right
1404- }
1405- smallMode: !sidebar.expanded
1406- visible: viewMethod === i18n.tr("List")
1407- }
1408-
1409- Item {
1410- id: contents
1411-
1412- anchors {
1413- top: parent.top
1414- bottom: parent.bottom
1415- left: sidebar.right
1416- right: parent.right
1417- }
1418-
1419-
1420- Label {
1421- text: i18n.tr("No files")
1422- fontSize: "large"
1423- opacity: 0.5
1424- anchors.centerIn: parent
1425- visible: folderListView.count == 0 && !pageModel.awaitingResults
1426- }
1427-
1428- ActivityIndicator {
1429- running: pageModel.awaitingResults
1430- width: units.gu(8)
1431- height: units.gu(8)
1432- anchors.centerIn: parent
1433- }
1434- }
1435-
1436 Component {
1437 id: confirmSingleDeleteDialog
1438 ConfirmDialog {
1439@@ -548,7 +228,53 @@
1440
1441 fileOperationDialog.startOperation("Deleting files")
1442 console.log("Doing delete")
1443- pageModel.rm(filePath)
1444+ folderModel.folderModel.rm(filePath)
1445+ }
1446+ }
1447+ }
1448+
1449+ Component {
1450+ id: tabsPopover
1451+ ActionSelectionPopover {
1452+ objectName: "tabsPopover"
1453+
1454+ property var tab
1455+
1456+ grabDismissAreaEvents: true
1457+
1458+ actions: ActionList {
1459+ Action {
1460+ text: i18n.tr("Open in a new tab")
1461+ onTriggered: {
1462+ openTab(folderListPage.folder)
1463+ }
1464+ }
1465+
1466+ // The current tab can be closed as long as there is at least one tab remaining
1467+ Action {
1468+ text: i18n.tr("Close this tab")
1469+ onTriggered: {
1470+ closeTab(tab.index)
1471+ }
1472+ enabled: tabs.count > 1
1473+ }
1474+ }
1475+ }
1476+ }
1477+
1478+ Component {
1479+ id: createFolderDialog
1480+ ConfirmDialogWithInput {
1481+ title: i18n.tr("Create folder")
1482+ text: i18n.tr("Enter name for new folder")
1483+
1484+ onAccepted: {
1485+ console.log("Create folder accepted", inputText)
1486+ if (inputText !== '') {
1487+ folderModel.folderModel.mkdir(inputText)
1488+ } else {
1489+ console.log("Empty directory name, ignored")
1490+ }
1491 }
1492 }
1493 }
1494@@ -571,7 +297,7 @@
1495 console.log("Rename accepted", inputText)
1496 if (inputText !== '') {
1497 console.log("Rename commensed, modelRow/inputText", modelRow, inputText)
1498- if (pageModel.rename(modelRow, inputText) === false) {
1499+ if (folderModel.folderModel.rename(modelRow, inputText) === false) {
1500 PopupUtils.open(Qt.resolvedUrl("NotifyDialog.qml"), delegate,
1501 {
1502 title: i18n.tr("Could not rename"),
1503@@ -586,84 +312,9 @@
1504 }
1505 }
1506
1507- Component {
1508- id: actionSelectionPopoverComponent
1509-
1510- ActionSelectionPopover {
1511- id: actionSelectionPopover
1512- objectName: "fileActionsPopover"
1513-
1514- grabDismissAreaEvents: true
1515-
1516- property var model
1517- actions: ActionList {
1518- Action {
1519- text: i18n.tr("Cut")
1520- // TODO: temporary
1521- iconSource: "/usr/share/icons/Humanity/actions/48/edit-cut.svg"
1522- onTriggered: {
1523- console.log("Cut on row called for", actionSelectionPopover.model.fileName, actionSelectionPopover.model.index)
1524- pageModel.cutIndex(actionSelectionPopover.model.index)
1525- console.log("CliboardUrlsCounter after copy", pageModel.clipboardUrlsCounter)
1526- }
1527- }
1528-
1529- Action {
1530- text: i18n.tr("Copy")
1531- // TODO: temporary.
1532- iconSource: "/usr/share/icons/Humanity/actions/48/edit-copy.svg"
1533-
1534- onTriggered: {
1535- console.log("Copy on row called for", actionSelectionPopover.model.fileName, actionSelectionPopover.model.index)
1536- pageModel.copyIndex(actionSelectionPopover.model.index)
1537- console.log("CliboardUrlsCounter after copy", pageModel.clipboardUrlsCounter)
1538- }
1539- }
1540-
1541- Action {
1542- text: i18n.tr("Delete")
1543- // TODO: temporary
1544- iconSource: "/usr/share/icons/Humanity/actions/48/edit-delete.svg"
1545- onTriggered: {
1546- print(text)
1547- PopupUtils.open(confirmSingleDeleteDialog, actionSelectionPopover.caller,
1548- { "filePath" : actionSelectionPopover.model.filePath,
1549- "fileName" : actionSelectionPopover.model.fileName }
1550- )
1551- }
1552- }
1553-
1554- Action {
1555- text: i18n.tr("Rename")
1556- // TODO: temporary
1557- iconSource: "/usr/share/icons/Humanity/actions/48/rotate.svg"
1558- onTriggered: {
1559- print(text)
1560- PopupUtils.open(confirmRenameDialog, actionSelectionPopover.caller,
1561- { "modelRow" : actionSelectionPopover.model.index,
1562- "inputText" : actionSelectionPopover.model.fileName
1563- })
1564- }
1565- }
1566-
1567- Action {
1568- text: i18n.tr("Properties")
1569- onTriggered: {
1570- print(text)
1571- PopupUtils.open(Qt.resolvedUrl("FileDetailsPopover.qml"),
1572- actionSelectionPopover.caller,
1573- { "model": actionSelectionPopover.model
1574- }
1575- )
1576- }
1577- }
1578- }
1579- }
1580- }
1581-
1582 // Errors from model
1583 Connections {
1584- target: pageModel
1585+ target: folderModel.folderModel
1586 onError: {
1587 console.log("FolderListModel Error Title/Description", errorTitle, errorMessage)
1588 error(i18n.tr("File operation error"), errorTitle + ": " + errorMessage)
1589@@ -674,19 +325,19 @@
1590 id: fileOperationDialog
1591
1592 page: folderListPage
1593- model: pageModel
1594+ model: folderModel.folderModel
1595 }
1596
1597 function itemClicked(model) {
1598 if (model.isDir) {
1599 if (model.isReadable && model.isExecutable) {
1600 console.log("Changing to dir", model.filePath)
1601- goTo(model.filePath)
1602+ folderModel.goTo(model.filePath)
1603 } else {
1604 PopupUtils.open(Qt.resolvedUrl("NotifyDialog.qml"), delegate,
1605 {
1606 title: i18n.tr("Folder not accessible"),
1607- // TRANSLATORS: this refers to a folder name
1608+ // TRANSLATORS: this refers to a folder name
1609 text: i18n.tr("Can not access %1").arg(model.fileName)
1610
1611 })
1612@@ -694,18 +345,12 @@
1613 } else {
1614 console.log("Non dir clicked")
1615 openFile(model.fileName)
1616-// PopupUtils.open(Qt.resolvedUrl("FileActionDialog.qml"), root,
1617-// {
1618-// fileName: model.fileName,
1619-// filePath: model.filePath,
1620-// folderListModel: root.folderListModel
1621-// })
1622 }
1623 }
1624
1625 function itemLongPress(delegate, model) {
1626 console.log("FolderListDelegate onPressAndHold")
1627- PopupUtils.open(actionSelectionPopoverComponent, delegate,
1628+ PopupUtils.open(Qt.resolvedUrl("FileActionsPopover.qml"), delegate,
1629 {
1630 model: model
1631 })
1632
1633=== modified file 'src/app/qml/ui/GoToDialog.qml'
1634--- src/app/qml/ui/GoToDialog.qml 2014-02-19 00:00:15 +0000
1635+++ src/app/qml/ui/GoToDialog.qml 2014-06-16 21:32:33 +0000
1636@@ -37,7 +37,7 @@
1637
1638 inputMethodHints: Qt.ImhNoAutoUppercase
1639
1640- property bool valid: pathExists(text)
1641+ property bool valid: folderModel.pathExists(text)
1642
1643 text: fileView.path
1644
1645@@ -55,7 +55,7 @@
1646
1647 onClicked: {
1648 print("User switched to:", locationField.text)
1649- goTo(locationField.text)
1650+ folderModel.goTo(locationField.text)
1651 PopupUtils.close(root)
1652 }
1653 }
1654
1655=== modified file 'src/app/qml/ui/PlacesPopover.qml'
1656--- src/app/qml/ui/PlacesPopover.qml 2014-06-10 02:34:07 +0000
1657+++ src/app/qml/ui/PlacesPopover.qml 2014-06-16 21:32:33 +0000
1658@@ -24,40 +24,6 @@
1659 id: root
1660 objectName: "placesPopover"
1661
1662- ListModel {
1663- id: places
1664-
1665- ListElement {
1666- objectName: 'placeHome'
1667- path: "~"
1668- }
1669-
1670- ListElement {
1671- path: "~/Documents"
1672- }
1673-
1674- ListElement {
1675- path: "~/Downloads"
1676- }
1677-
1678- ListElement {
1679- path: "~/Music"
1680- }
1681-
1682- ListElement {
1683- path: "~/Pictures"
1684- }
1685-
1686- ListElement {
1687- path: "~/Videos"
1688- }
1689-
1690- ListElement {
1691- objectName: "placeRoot"
1692- path: "/"
1693- }
1694- }
1695-
1696 Column {
1697 anchors {
1698 left: parent.left
1699@@ -79,7 +45,7 @@
1700
1701 inputMethodHints: Qt.ImhNoAutoUppercase
1702
1703- property bool valid: pathExists(text)
1704+ property bool valid: folderModel.pathExists(text)
1705
1706 text: fileView.path
1707
1708@@ -103,7 +69,7 @@
1709
1710 onClicked: {
1711 print("User switched to:", locationField.text)
1712- goTo(locationField.text)
1713+ folderModel.goTo(locationField.text)
1714 PopupUtils.close(root)
1715 }
1716 }
1717@@ -113,7 +79,7 @@
1718 id: placesList
1719 objectName: "placesList"
1720
1721- model: places
1722+ model: folderModel.places
1723
1724 delegate: Standard {
1725 objectName: model.objectName
1726@@ -123,18 +89,18 @@
1727 anchors.left: parent.left
1728 anchors.leftMargin: units.gu(8)
1729 anchors.verticalCenter: parent.verticalCenter
1730- text: folderName(path)
1731+ text: folderModel.folderName(path)
1732 color: selected ? UbuntuColors.orange : Theme.palette.normal.overlayText
1733 }
1734
1735- iconSource: model.icon || fileIcon(model.path, true)
1736+ iconSource: model.icon || folderModel.fileIcon(model.path, true)
1737
1738 onClicked: {
1739 PopupUtils.close(root)
1740 goTo(model.path)
1741 }
1742
1743- selected: folder === path
1744+ selected: folderModel.folder === path
1745 iconFrame: false
1746 showDivider: index < (placesList.count - 1)
1747 }

Subscribers

People subscribed via source and target branches