Warning, /maui/mauikit-filebrowsing/src/controls.6/FileBrowser.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 * Copyright 2018 Camilo Higuita <milo.h@aol.com> 0003 * 0004 * This program is free software; you can redistribute it and/or modify 0005 * it under the terms of the GNU Library General Public License as 0006 * published by the Free Software Foundation; either version 2, or 0007 * (at your option) any later version. 0008 * 0009 * This program is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 * GNU General Public License for more details 0013 * 0014 * You should have received a copy of the GNU Library General Public 0015 * License along with this program; if not, write to the 0016 * Free Software Foundation, Inc., 0017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0018 */ 0019 0020 import QtQuick 0021 import QtQml 0022 0023 import QtQuick.Controls 0024 import QtQuick.Layouts 0025 0026 import org.mauikit.controls 1.3 as Maui 0027 import org.mauikit.filebrowsing 1.3 as FB 0028 0029 import "private" as Private 0030 0031 /** 0032 * @inherit MauiKit::Controls::Page 0033 * @brief This control displays the file system entries in a given directory, allows subsequent browsing of the file hierarchy and exposes basic file handling functionality. 0034 * 0035 * This controls inherits from MauiKit Page, to checkout its inherited properties refer to the docs. 0036 * @see MauiKit::Page 0037 * 0038 * @image html filebrowser2.png "FileBrowser control" 0039 * 0040 * @section features Features 0041 * 0042 * This control exposes a series of convenient properties and functions for filtering and sorting, for creating new entries, perform searches, and modify the entries. 0043 * 0044 * There are two different possible ways to display the contents: Grid and List, using the `settings.viewType` property. 0045 * @see FMList::VIEW_TYPE 0046 * 0047 * More browsing properties can be tweaked via the exposed `settings` alias property. 0048 * @see settings 0049 * 0050 * Some basic file item actions are implemented by default, like copy, cut, rename and remove, and exposed as methods. 0051 * @see copy 0052 * @see cut 0053 * @see paste 0054 * @see remove 0055 * 0056 * @note This component functionality can be easily expanded to be more feature rich, see for example the Maui Index application. 0057 * 0058 * This control allows to perform recursive searches, or simple filtering of elements in the current location by using the search bar. Besides these two, the user can quickly start fly-typing within the browser, to jump quickly to a matching entry. 0059 * 0060 * @subsection shorcuts Shortcuts 0061 * This control supports multiple keyboard shortcuts, for selecting multiple elements, creating new entries, copying, deleting and renaming files. 0062 * 0063 * The supported keyboard shortcuts are: 0064 * 0065 * - `Ctrl + N` Create a new entry 0066 * - `Ctrl + F` Open the search bar 0067 * - `F5` Refresh/reload the content 0068 * - `F2` Rename an entry 0069 * - `Ctrl + A` Select all the entries 0070 * - `Ctrl + N` Create a new entry 0071 * - `Ctrl + Shift + ← ↑ → ↓` Select items in any direction 0072 * - `Ctrl + Return` Open an entry 0073 * - `Ctrl + V` Paste element in the clipboard to the current location 0074 * - `Ctrl + X` Cut an entry 0075 * - `Ctrl + C` Copy an entry 0076 * - `Ctrl + Delete` Remove an entry 0077 * - `Ctrl + Backspace` Clear selection/Go back to previous location 0078 * - `Ctrl + Esc` Clear the selection/Clear the filter 0079 * 0080 * @section structure Structure 0081 * 0082 * This control inherits from MauiKit Page, so it has a header and footer. The header bar has a search field for performing searches, by default it is hidden. 0083 * 0084 * The footer bar by default has contextual buttons, depending on the current location. For example, for the trash location, it has contextual actions for emptying the trash can, etc. 0085 * 0086 * @warning It is recommended to not add elements to the footer or header bars, instead - if needed - wrap this control into a MauiKit Page, and utilize its toolbars. 0087 * 0088 * @code 0089 * FB.FileBrowser 0090 * { 0091 * Maui.Controls.showCSD: true 0092 * headBar.visible: true 0093 * anchors.fill: parent 0094 * currentPath: FB.FM.homePath() 0095 * settings.viewType: FB.FMList.GRID_VIEW 0096 * } 0097 * @endcode 0098 * 0099 * <a href="https://invent.kde.org/maui/mauikit-filebrowser/examples/FileBrowser.qml">You can find a more complete example at this link.</a> 0100 * 0101 */ 0102 Maui.Page 0103 { 0104 id: control 0105 0106 onGoBackTriggered: control.goBack() 0107 onGoForwardTriggered: control.goForward() 0108 0109 title: view.title 0110 0111 focus: true 0112 0113 flickable: control.currentView.flickable 0114 0115 floatingFooter: false 0116 0117 showTitle: false 0118 0119 /** 0120 * @brief The current URL path for the directory or location. 0121 * To list a directory path, or other location, use the right schema, some of them are `file://`, `webdav://`, `trash://`/, `tags://`, `fish://`. 0122 * By default a URL without a well defined schema will be assumed as a local file path. 0123 * 0124 * @note KDE KIO is used as the back-end technology, so any of the supported plugins by KIO will also work here. 0125 * 0126 * @property string FileBrowser::currentPath 0127 */ 0128 property alias currentPath : _browser.path 0129 onCurrentPathChanged : _searchField.clear() 0130 0131 /** 0132 * @brief A group of properties for controlling the sorting, listing and other behaviour of the browser. 0133 * For more details check the BrowserSettings documentation. 0134 * @see BrowserSettings 0135 * 0136 * @code 0137 * settings.onlyDirs: true 0138 * settings.sortBy: FB.FMList.LABEL 0139 * settings.showThumbnails: false 0140 * @endcode 0141 * 0142 * @property BrowserSettings FileBrowser::settings 0143 */ 0144 readonly property alias settings : _browser.settings 0145 0146 /** 0147 * @brief The browser could be in two different view states: [1]the file browsing or [2]the search view. 0148 * This property gives access to the current view in use. 0149 */ 0150 readonly property alias view : _stackView.currentItem 0151 0152 /** 0153 * @brief An alias to the control listing the entries. 0154 * This component is handled by BrowserView, and exposed for fine tuning its properties 0155 * @property BrowserView FileBrowser::browser 0156 */ 0157 readonly property alias browser : _browser 0158 0159 /** 0160 * @brief Drop area component, for dropping files. 0161 * By default some of the drop actions are handled, for other type of URIs this alias can be used to handle those. 0162 * 0163 * The urlsDropped signal is emitted once one or multiple valid file URLs are dropped, and a popup contextual menu is opened with the supported default actions. 0164 * @property DropArea FileBrowser::dropArea 0165 */ 0166 readonly property alias dropArea : _dropArea 0167 0168 /** 0169 * @brief Current index of the item selected in the file browser. 0170 */ 0171 property int currentIndex : -1 0172 0173 Binding on currentIndex 0174 { 0175 value: currentView.currentIndex 0176 } 0177 0178 /** 0179 * @brief Current view of the file browser. Possible views are: 0180 * - List = MauiKit::ListBrowser 0181 * - Grid = MauiKit::GridBrowser 0182 * @property Item FileBrowser::currentView 0183 */ 0184 readonly property QtObject currentView : _stackView.currentItem.browser 0185 0186 /** 0187 * @brief The file browser model list. 0188 * The List and Grid views use the same FMList. 0189 * @see FMList 0190 */ 0191 readonly property FB.FMList currentFMList : view.currentFMList 0192 0193 /** 0194 * @brief The file browser model controller. This is the controller which allows for filtering, index mapping, and sorting. 0195 * @see MauiModel 0196 */ 0197 readonly property Maui.BaseModel currentFMModel : view.currentFMModel 0198 0199 /** 0200 * @brief Whether the file browser current view is the search view. 0201 */ 0202 readonly property bool isSearchView : _stackView.currentItem.objectName === "searchView" 0203 0204 /** 0205 * @brief Whether the file browser enters selection mode, allowing the selection of multiple items. 0206 * This will make all the browsing entries checkable. 0207 * @property bool FileBrowser::selectionMode 0208 */ 0209 property alias selectionMode: _browser.selectionMode 0210 0211 /** 0212 * @brief Size of the items in the grid view. 0213 * The size is for the combined thumbnail/icon and the title label. 0214 * 0215 * @property int FileBrowser::gridItemSize 0216 */ 0217 property alias gridItemSize : _browser.gridItemSize 0218 0219 /** 0220 * @brief Size of the items in the list view. 0221 * @note The size is actually taken as the size of the icon thumbnail. And it is mapped to the nearest size of the standard icon sizes. 0222 * @property int FileBrowser::listItemSize 0223 */ 0224 property alias listItemSize : _browser.listItemSize 0225 0226 /** 0227 * @brief The SelectionBar to be used for adding items to the selected group. 0228 * This is optional, but if it is not set, then some feature will not work, such as selection mode, selection shortcuts etc. 0229 * 0230 * @see MauiKit::SelectionBar 0231 * 0232 * @code 0233 * Maui.Page 0234 { 0235 Maui.Controls.showCSD: true 0236 anchors.fill: parent 0237 floatingFooter: true 0238 0239 FB.FileBrowser 0240 { 0241 Maui.Controls.showCSD: true 0242 0243 anchors.fill: parent 0244 currentPath: FB.FM.homePath() 0245 settings.viewType: FB.FMList.GRID_VIEW 0246 selectionBar: _selectionBar 0247 } 0248 0249 footer: Maui.SelectionBar 0250 { 0251 id: _selectionBar 0252 anchors.horizontalCenter: parent.horizontalCenter 0253 width: Math.min(parent.width-(Maui.Style.space.medium*2), implicitWidth) 0254 maxListHeight: root.height - (Maui.Style.contentMargins*2) 0255 0256 Action 0257 { 0258 icon.name: "love" 0259 onTriggered: console.log(_selectionBar.getSelectedUrisString()) 0260 } 0261 0262 Action 0263 { 0264 icon.name: "folder" 0265 } 0266 0267 Action 0268 { 0269 icon.name: "list-add" 0270 } 0271 0272 onExitClicked: clear() 0273 } 0274 } 0275 * @endcode 0276 */ 0277 property Maui.SelectionBar selectionBar : null 0278 0279 /** 0280 * @brief An alias to the currently loaded dialog, if not dialog is loaded then null. 0281 * 0282 * @property Dialog FileBrowser::dialog 0283 */ 0284 readonly property alias dialog : dialogLoader.item 0285 0286 /** 0287 * @brief Whether the browser is on a read only mode, and modifications are now allowed, such as pasting, moving, removing or renaming. 0288 * @property bool FileBrowser::readOnly 0289 */ 0290 property alias readOnly : _browser.readOnly 0291 0292 0293 /** 0294 * @brief Emitted when an item has been clicked. 0295 * @param index the index position of the item 0296 */ 0297 signal itemClicked(int index) 0298 0299 /** 0300 * @brief Emitted when an item has been double clicked. 0301 * @param index the index position of the item 0302 */ 0303 signal itemDoubleClicked(int index) 0304 0305 /** 0306 * @brief Emitted when an item has been right clicked. On mobile devices this is translated from a long press and release. 0307 * @param index the index position of the item 0308 */ 0309 signal itemRightClicked(int index) 0310 0311 /** 0312 * @brief Emitted when an item left emblem badge has been clicked. 0313 * This is actually a signal to checkable item being toggled. 0314 * @param index the index position of the item 0315 */ 0316 signal itemLeftEmblemClicked(int index) 0317 0318 0319 /** 0320 * @brief Emitted when an empty area of the browser has been right clicked. 0321 */ 0322 signal rightClicked() 0323 0324 /** 0325 * @brief Emitted when an empty area of the browser has been clicked. 0326 * @param mouse the object with the event information 0327 */ 0328 signal areaClicked(var mouse) 0329 0330 0331 /** 0332 * @brief A key, physical or not, has been pressed. 0333 * @param event the event object contains the relevant information 0334 */ 0335 signal keyPress(var event) 0336 0337 /** 0338 * @brief Emitted when a list of file URLS has been dropped onto the file browser area. 0339 * @param urls the list of URLs of the files dropped 0340 */ 0341 signal urlsDropped(var urls) 0342 0343 headBar.forceCenterMiddleContent: isWide 0344 headBar.visible: control.settings.searchBarVisible 0345 headBar.leftContent: Loader 0346 { 0347 asynchronous: true 0348 active: control.isSearchView 0349 visible: active 0350 sourceComponent: ToolButton 0351 { 0352 text: i18nd("mauikitfilebrowsing", "Back") 0353 icon.name: "go-previous" 0354 onClicked: control.quitSearch() 0355 } 0356 } 0357 0358 headBar.middleContent: Maui.SearchField 0359 { 0360 id: _searchField 0361 focus: true 0362 Layout.fillWidth: true 0363 Layout.maximumWidth: 500 0364 Layout.alignment: Qt.AlignCenter 0365 placeholderText: _filterButton.checked ? i18nd("mauikitfilebrowsing", "Filter") : ("Search") 0366 inputMethodHints: Qt.ImhNoAutoUppercase 0367 0368 onAccepted: 0369 { 0370 if(_filterButton.checked) 0371 { 0372 if(text.includes(",")) 0373 { 0374 control.view.filters = text.split(",") 0375 }else 0376 { 0377 control.view.filter = text 0378 } 0379 0380 }else 0381 { 0382 control.search(text) 0383 } 0384 } 0385 0386 onCleared: 0387 { 0388 if(_filterButton.checked) 0389 { 0390 control.currentFMModel.clearFilters() 0391 } 0392 } 0393 0394 onTextChanged: 0395 { 0396 if(_filterButton.checked) 0397 _searchField.accepted() 0398 } 0399 0400 Keys.enabled: _filterButton.checked 0401 Keys.onPressed: 0402 { 0403 // Shortcut for clearing selection 0404 if(event.key == Qt.Key_Up) 0405 { 0406 control.currentView.forceActiveFocus() 0407 } 0408 } 0409 0410 actions: Action 0411 { 0412 id: _filterButton 0413 icon.name: "view-filter" 0414 // text: i18nd("mauikitfilebrowsing", "Filter") 0415 checkable: true 0416 checked: true 0417 onTriggered: 0418 { 0419 control.view.filter = "" 0420 _searchField.clear() 0421 _searchField.forceActiveFocus() 0422 } 0423 } 0424 } 0425 0426 footBar.visible: control.currentPath.startsWith("trash:/") 0427 0428 footerPositioning: ListView.InlineFooter 0429 0430 footBar.rightContent: Button 0431 { 0432 visible: control.currentPath.startsWith("trash:/") 0433 icon.name: "trash-empty" 0434 text: i18nd("mauikitfilebrowsing", "Empty Trash") 0435 onClicked: FB.FM.emptyTrash() 0436 } 0437 0438 Loader 0439 { 0440 id: dialogLoader 0441 } 0442 0443 Component 0444 { 0445 id: _quitSearchDialogComponent 0446 0447 Maui.InfoDialog 0448 { 0449 title: i18n("Quit") 0450 message: i18n("Are you sure you want to quit the current search in progress?") 0451 onAccepted: 0452 { 0453 _stackView.pop() 0454 _browser.forceActiveFocus() 0455 } 0456 0457 onRejected: close() 0458 } 0459 } 0460 0461 Component 0462 { 0463 id: removeDialogComponent 0464 0465 Maui.FileListingDialog 0466 { 0467 id: _removeDialog 0468 0469 property double freedSpace : calculateFreedSpace(urls) 0470 0471 title: i18nd("mauikitfilebrowsing", "Removing %1 files", urls.length) 0472 message: i18nd("mauikitfilebrowsing", "Delete %1 \nTotal freed space %2", (Maui.Handy.isLinux ? "or move to trash?" : "? This action can not be undone."), Maui.Handy.formatSize(freedSpace)) 0473 0474 actions: [ 0475 Action 0476 { 0477 text: i18nd("mauikitfilebrowsing", "Cancel") 0478 onTriggered: _removeDialog.close() 0479 }, 0480 0481 Action 0482 { 0483 text: i18nd("mauikitfilebrowsing", "Delete") 0484 onTriggered: 0485 { 0486 control.currentFMList.removeFiles(urls) 0487 close() 0488 } 0489 }, 0490 Action 0491 { 0492 text: i18nd("mauikitfilebrowsing", "Trash") 0493 enabled: Maui.Handy.isLinux 0494 onTriggered: 0495 { 0496 control.currentFMList.moveToTrash(urls) 0497 close() 0498 } 0499 } 0500 ] 0501 0502 function calculateFreedSpace(urls) 0503 { 0504 var size = 0 0505 for(var url of urls) 0506 { 0507 size += parseFloat(FB.FM.getFileInfo(url).size) 0508 } 0509 0510 return size 0511 } 0512 } 0513 } 0514 0515 Component 0516 { 0517 id: newDialogComponent 0518 // 0519 Maui.InputDialog 0520 { 0521 id: _newDialog 0522 0523 title: _newActions.currentIndex === 0 ? i18nd("mauikitfilebrowsing", "New folder") : i18nd("mauikitfilebrowsing", "New file") 0524 message: i18nd("mauikitfilebrowsing", "Create a new folder or a file with a custom name.") 0525 0526 template.iconSource: FB.FM.getIconName(textEntry.text) 0527 template.iconVisible: true 0528 0529 onFinished: (text) => 0530 { 0531 if(_newDirOp.checked) 0532 { 0533 control.currentFMList.createDir(text) 0534 return 0535 } 0536 if(_newFileOp.checked) 0537 { 0538 control.currentFMList.createFile(text) 0539 return 0540 } 0541 } 0542 0543 textEntry.placeholderText: i18nd("mauikitfilebrowsing", "Name") 0544 0545 Maui.ToolActions 0546 { 0547 id: _newActions 0548 expanded: true 0549 autoExclusive: true 0550 display: ToolButton.TextBesideIcon 0551 0552 Action 0553 { 0554 id: _newDirOp 0555 icon.name: "folder-new" 0556 text: i18nd("mauikitfilebrowsing", "Folder") 0557 checked: String(_newDialog.textEntry.text).indexOf(".") < 0 0558 } 0559 0560 Action 0561 { 0562 id: _newFileOp 0563 icon.name: "document-new" 0564 text: i18nd("mauikitfilebrowsing", "File") 0565 checked: String(_newDialog.textEntry.text).indexOf(".") >= 0 0566 } 0567 } 0568 } 0569 } 0570 0571 Component 0572 { 0573 id: renameDialogComponent 0574 0575 Maui.InputDialog 0576 { 0577 id: _renameDialog 0578 0579 property var item : ({}) 0580 0581 title: i18nd("mauikitfilebrowsing", "Rename") 0582 message: i18nd("mauikitfilebrowsing", "Change the name of a file or folder. Write a new name and click Rename to apply the change.") 0583 0584 // headBar.visible: false 0585 0586 template.iconSource: item.icon 0587 template.imageSource: item.thumbnail 0588 template.iconSizeHint: Maui.Style.iconSizes.huge 0589 0590 textEntry.text: item.label 0591 textEntry.placeholderText: i18nd("mauikitfilebrowsing", "New name") 0592 0593 onFinished: control.currentFMList.renameFile(item.path, textEntry.text) 0594 onRejected: close() 0595 0596 // acceptButton.text: i18nd("mauikitfilebrowsing", "Rename") 0597 // rejectButton.text: i18nd("mauikitfilebrowsing", "Cancel") 0598 0599 onOpened: 0600 { 0601 item = control.currentFMModel.get(control.currentIndex) 0602 0603 if(_renameDialog.textEntry.text.lastIndexOf(".") >= 0) 0604 { 0605 _renameDialog.textEntry.select(0, _renameDialog.textEntry.text.lastIndexOf(".")) 0606 }else 0607 { 0608 _renameDialog.textEntry.selectAll() 0609 } 0610 } 0611 } 0612 } 0613 0614 Component 0615 { 0616 id: _newTagDialogComponent 0617 FB.NewTagDialog {} 0618 } 0619 0620 /** 0621 * @private 0622 */ 0623 property string typingQuery 0624 0625 Maui.Chip 0626 { 0627 z: control.z + 99999 0628 Maui.Theme.colorSet:Maui.Theme.Complementary 0629 visible: _typingTimer.running 0630 label.text: typingQuery 0631 anchors.left: parent.left 0632 anchors.top: parent.top 0633 showCloseButton: false 0634 anchors.margins: Maui.Style.space.medium 0635 } 0636 0637 Timer 0638 { 0639 id: _typingTimer 0640 interval: 250 0641 onTriggered: 0642 { 0643 const index = control.currentFMList.indexOfName(typingQuery) 0644 if(index > -1) 0645 { 0646 console.log("FOUDN TRYPIGN IDNEX", index) 0647 control.currentIndex = control.currentFMModel.mappedFromSource(index) 0648 } 0649 0650 typingQuery = "" 0651 } 0652 } 0653 0654 Connections 0655 { 0656 target: control.currentView 0657 ignoreUnknownSignals: true 0658 0659 function onKeyPress(event) 0660 { 0661 const index = control.currentIndex 0662 const item = control.currentFMModel.get(index) 0663 0664 var pat = /^([a-zA-Z0-9 _-]+)$/ 0665 0666 if(event.count === 1 && pat.test(event.text)) 0667 { 0668 typingQuery += event.text 0669 _typingTimer.restart() 0670 event.accepted = true 0671 } 0672 0673 // Shortcuts for refreshing 0674 if((event.key === Qt.Key_F5)) 0675 { 0676 control.currentFMList.refresh() 0677 event.accepted = true 0678 } 0679 0680 // Shortcuts for renaming 0681 if((event.key === Qt.Key_F2)) 0682 { 0683 dialogLoader.sourceComponent = renameDialogComponent 0684 dialog.open() 0685 event.accepted = true 0686 } 0687 0688 // Shortcuts for selecting file 0689 if((event.key === Qt.Key_A) && (event.modifiers & Qt.ControlModifier)) 0690 { 0691 control.selectAll() 0692 event.accepted = true 0693 } 0694 0695 if((event.key === Qt.Key_Left || event.key === Qt.Key_Right || event.key === Qt.Key_Down || event.key === Qt.Key_Up) && (event.modifiers & Qt.ControlModifier) && (event.modifiers & Qt.ShiftModifier)) 0696 { 0697 if(control.selectionBar && control.selectionBar.contains(item.path)) 0698 { 0699 control.selectionBar.removeAtUri(item.path) 0700 }else 0701 { 0702 control.addToSelection(item) 0703 } 0704 //event.accepted = true 0705 } 0706 0707 //shortcut for opening files 0708 if(event.key === Qt.Key_Return) 0709 { 0710 control.openItem(index) 0711 event.accepted = true 0712 } 0713 0714 // Shortcut for pasting an item 0715 if((event.key == Qt.Key_V) && (event.modifiers & Qt.ControlModifier)) 0716 { 0717 control.paste() 0718 event.accepted = true 0719 } 0720 0721 // Shortcut for cutting an item 0722 if((event.key == Qt.Key_X) && (event.modifiers & Qt.ControlModifier)) 0723 { 0724 const urls = filterSelection(control.currentPath, item.path) 0725 control.cut(urls) 0726 event.accepted = true 0727 } 0728 0729 // Shortcut for copying an item 0730 if((event.key == Qt.Key_C) && (event.modifiers & Qt.ControlModifier)) 0731 { 0732 const urls = filterSelection(control.currentPath, item.path) 0733 control.copy(urls) 0734 event.accepted = true 0735 } 0736 0737 // Shortcut for removing an item 0738 if(event.key === Qt.Key_Delete) 0739 { 0740 const urls = filterSelection(control.currentPath, item.path) 0741 control.remove(urls) 0742 event.accepted = true 0743 } 0744 0745 // Shortcut for going back in browsing history 0746 if(event.key === Qt.Key_Backspace || event.key == Qt.Key_Back) 0747 { 0748 if(control.selectionBar && control.selectionBar.count> 0) 0749 { 0750 control.selectionBar.clear() 0751 } 0752 else 0753 { 0754 control.goBack() 0755 } 0756 event.accepted = true 0757 } 0758 0759 // Shortcut for clearing selection and filtering 0760 if(event.key === Qt.Key_Escape) //TODO not working, the event is not catched or emitted or is being accepted else where? 0761 { 0762 if(control.selectionBar && control.selectionBar.count > 0) 0763 { 0764 control.selectionBar.clear() 0765 } 0766 0767 control.view.filter = "" 0768 event.accepted = true 0769 } 0770 0771 //Shortcut for opening filtering 0772 if((event.key === Qt.Key_F) && (event.modifiers & Qt.ControlModifier)) 0773 { 0774 control.toggleSearchBar() 0775 event.accepted = true 0776 } 0777 0778 control.keyPress(event) 0779 } 0780 0781 function onItemsSelected(indexes) 0782 { 0783 if(indexes.length) 0784 { 0785 control.currentIndex = indexes[0] 0786 control.selectIndexes(indexes) 0787 } 0788 } 0789 0790 function onItemClicked(index) 0791 { 0792 control.currentIndex = index 0793 control.itemClicked(index) 0794 control.currentView.forceActiveFocus() 0795 } 0796 0797 function onItemDoubleClicked(index) 0798 { 0799 control.currentIndex = index 0800 control.itemDoubleClicked(index) 0801 control.currentView.forceActiveFocus() 0802 } 0803 0804 function onItemRightClicked(index) 0805 { 0806 control.currentIndex = index 0807 control.itemRightClicked(index) 0808 control.currentView.forceActiveFocus() 0809 } 0810 0811 function onItemToggled(index) 0812 { 0813 const item = control.currentFMModel.get(index) 0814 0815 if(control.selectionBar && control.selectionBar.contains(item.path)) 0816 { 0817 control.selectionBar.removeAtUri(item.path) 0818 }else 0819 { 0820 control.addToSelection(item) 0821 } 0822 control.itemLeftEmblemClicked(index) 0823 control.currentView.forceActiveFocus() 0824 } 0825 0826 function onAreaClicked(mouse) 0827 { 0828 if(control.isSearchView) 0829 return 0830 0831 if(!Maui.Handy.isMobile && mouse.button === Qt.RightButton) 0832 { 0833 control.rightClicked() 0834 } 0835 0836 control.areaClicked(mouse) 0837 control.currentView.forceActiveFocus() 0838 } 0839 } 0840 0841 Maui.ContextualMenu 0842 { 0843 id: _dropMenu 0844 property string urls 0845 enabled: !control.isSearchView 0846 0847 MenuItem 0848 { 0849 enabled: !control.readOnly 0850 text: i18nd("mauikitfilebrowsing", "Copy Here") 0851 icon.name: "edit-copy" 0852 onTriggered: 0853 { 0854 const urls = _dropMenu.urls.split(",") 0855 control.currentFMList.copyInto(urls) 0856 } 0857 } 0858 0859 MenuItem 0860 { 0861 enabled: !control.readOnly 0862 text: i18nd("mauikitfilebrowsing", "Move Here") 0863 icon.name: "edit-move" 0864 onTriggered: 0865 { 0866 const urls = _dropMenu.urls.split(",") 0867 control.currentFMList.cutInto(urls) 0868 } 0869 } 0870 0871 MenuItem 0872 { 0873 enabled: !control.readOnly 0874 0875 text: i18nd("mauikitfilebrowsing", "Link Here") 0876 icon.name: "edit-link" 0877 onTriggered: 0878 { 0879 const urls = _dropMenu.urls.split(",") 0880 for(var i in urls) 0881 control.currentFMList.createSymlink(urls[i]) 0882 } 0883 } 0884 0885 MenuItem 0886 { 0887 enabled: FB.FM.isDir(_dropMenu.urls.split(",")[0]) 0888 text: i18nd("mauikitfilebrowsing", "Open Here") 0889 icon.name: "folder-open" 0890 onTriggered: 0891 { 0892 const urls = _dropMenu.urls.split(",") 0893 control.browser.path = urls[0] 0894 } 0895 } 0896 0897 MenuSeparator {} 0898 0899 MenuItem 0900 { 0901 text: i18nd("mauikitfilebrowsing", "Cancel") 0902 icon.name: "dialog-cancel" 0903 onTriggered: _dropMenu.close() 0904 } 0905 } 0906 0907 StackView 0908 { 0909 id: _stackView 0910 anchors.fill: parent 0911 0912 initialItem: DropArea 0913 { 0914 id: _dropArea 0915 0916 readonly property alias browser : _browser 0917 readonly property alias currentFMList : _browser.currentFMList 0918 readonly property alias currentFMModel: _browser.currentFMModel 0919 property alias filter: _browser.filter 0920 property alias filters: _browser.filters 0921 readonly property alias title : _browser.title 0922 0923 onDropped: 0924 { 0925 if(drop.urls) 0926 { 0927 _dropMenu.urls = drop.urls.join(",") 0928 _dropMenu.show() 0929 control.urlsDropped(drop.urls) 0930 } 0931 } 0932 0933 opacity: _dropArea.containsDrag ? 0.5 : 1 0934 0935 Private.BrowserView 0936 { 0937 id: _browser 0938 anchors.fill: parent 0939 0940 Binding on currentIndex 0941 { 0942 value: control.currentIndex 0943 restoreMode: Binding.RestoreBindingOrValue 0944 } 0945 0946 Loader 0947 { 0948 active: (control.currentPath === "tags://" || control.currentPath === "tags:///") && !control.readOnly 0949 visible: active 0950 asynchronous: true 0951 0952 anchors.right: parent.right 0953 anchors.bottom: parent.bottom 0954 anchors.rightMargin: Maui.Style.toolBarHeight 0955 anchors.bottomMargin: Maui.Style.toolBarHeight + control.flickable.bottomMargin 0956 0957 sourceComponent: Maui.FloatingButton 0958 { 0959 icon.name : "list-add" 0960 onClicked: 0961 { 0962 dialogLoader.sourceComponent = _newTagDialogComponent 0963 dialog.open() 0964 } 0965 } 0966 } 0967 } 0968 } 0969 0970 Component 0971 { 0972 id: _searchBrowserComponent 0973 0974 Private.BrowserView 0975 { 0976 id: _searchBrowser 0977 property alias browser : _searchBrowser 0978 readOnly: true 0979 path: control.currentPath 0980 Binding on currentIndex 0981 { 0982 value: control.currentIndex 0983 restoreMode: Binding.RestoreBindingOrValue 0984 } 0985 0986 objectName: "searchView" 0987 gridItemSize: control.gridItemSize 0988 listItemSize: control.listItemSize 0989 0990 currentFMList.autoLoad: false 0991 settings.viewType: control.settings.viewType 0992 settings.sortBy: control.settings.sortBy 0993 settings.showHiddenFiles: control.settings.showHiddenFiles 0994 settings.group: control.settings.group 0995 settings.foldersFirst: control.settings.foldersFirst 0996 0997 } 0998 } 0999 } 1000 1001 Component.onCompleted: 1002 { 1003 control.currentView.forceActiveFocus() 1004 } 1005 1006 /** 1007 * @brief Copy the given file URLs to the clipboard 1008 * @param urls the set of URLs to be copied 1009 **/ 1010 function copy(urls) 1011 { 1012 if(urls.length <= 0) 1013 { 1014 return 1015 } 1016 1017 Maui.Handy.copyToClipboard({"urls": urls}, false) 1018 } 1019 1020 /** 1021 * @brief Add the given URLs to the clipboard and mark them as a cut operation 1022 * @param urls the set of URLs to cut 1023 **/ 1024 function cut(urls) 1025 { 1026 if(control.readOnly) 1027 return 1028 1029 if(urls.length <= 0) 1030 { 1031 return 1032 } 1033 1034 Maui.Handy.copyToClipboard({"urls": urls}, true) 1035 } 1036 1037 /** 1038 * Paste the contents of the clipboard into the current location, if supported. 1039 **/ 1040 function paste() 1041 { 1042 control.currentFMList.paste() 1043 } 1044 1045 /** 1046 * Remove the given URLs.Array()this will launch a dialog to confirm this action. 1047 * @param urls the set of URLs to be removed 1048 **/ 1049 function remove(urls) 1050 { 1051 if(urls.length <= 0) 1052 { 1053 return 1054 } 1055 1056 dialogLoader.sourceComponent = removeDialogComponent 1057 dialog.urls = urls 1058 dialog.open() 1059 } 1060 1061 /** 1062 * @brief Given an index position of a element, try to open it, it can be a directory, a file or an executable. 1063 * 1064 **/ 1065 function openItem(index) 1066 { 1067 const item = control.currentFMModel.get(index) 1068 const path = item.path 1069 1070 switch(control.currentFMList.pathType) 1071 { 1072 case FB.FMList.CLOUD_PATH: //TODO deprecrated and needs to be removed or clean up for 1.1 1073 if(item.isdir === "true") 1074 { 1075 control.openFolder(path) 1076 } 1077 else 1078 { 1079 FB.FM.openCloudItem(item) 1080 } 1081 break; 1082 default: 1083 if(control.selectionMode && item.isdir == "false") 1084 { 1085 if(control.selectionBar && control.selectionBar.contains(item.path)) 1086 { 1087 control.selectionBar.removeAtPath(item.path) 1088 }else 1089 { 1090 control.addToSelection(item) 1091 } 1092 } 1093 else 1094 { 1095 if(item.isdir == "true") 1096 { 1097 control.openFolder(path) 1098 } 1099 else 1100 { 1101 control.openFile(path) 1102 } 1103 } 1104 } 1105 } 1106 1107 /** 1108 * @brief Open a file of the given path URL in the dedicated application 1109 * @param path The URL of the file 1110 **/ 1111 function openFile(path) 1112 { 1113 FB.FM.openUrl(path) 1114 } 1115 1116 /** 1117 * @brief Open a folder location 1118 * @param path the URL of the folder location 1119 **/ 1120 function openFolder(path) 1121 { 1122 if(!String(path).length) 1123 { 1124 return; 1125 } 1126 1127 if(control.isSearchView) 1128 { 1129 control.quitSearch() 1130 } 1131 1132 control.currentPath = path 1133 _browser.forceActiveFocus() 1134 } 1135 1136 /** 1137 * @brief Go back to the previous location 1138 **/ 1139 function goBack() 1140 { 1141 openFolder(control.currentFMList.previousPath()) 1142 } 1143 1144 /** 1145 * @brief Go forward to the location before going back 1146 **/ 1147 function goForward() 1148 { 1149 openFolder(control.currentFMList.posteriorPath()) 1150 } 1151 1152 /** 1153 * @brief Go to the location parent directory 1154 **/ 1155 function goUp() 1156 { 1157 openFolder(control.currentFMList.parentPath) 1158 } 1159 1160 /** 1161 * @brief Add an item to the selection 1162 * @param item the item object/map representing the file to be added to the selection 1163 * @warning For this to work the implementation needs to have passed a `selectionBar` 1164 * @see selectionBar 1165 **/ 1166 function addToSelection(item) 1167 { 1168 if(control.selectionBar == null || item.path.startsWith("tags://") || item.path.startsWith("applications://")) 1169 { 1170 return 1171 } 1172 1173 control.selectionBar.append(item.path, item) 1174 } 1175 1176 /** 1177 * @brief Given a list of indexes add them to the selection 1178 * @param indexes list of index positions 1179 **/ 1180 function selectIndexes(indexes) 1181 { 1182 if(control.selectionBar == null) 1183 { 1184 return 1185 } 1186 1187 for(var i in indexes) 1188 addToSelection(control.currentFMModel.get(indexes[i])) 1189 } 1190 1191 /** 1192 * @brief Forces to select all the entries 1193 * 1194 * @bug If there are too many entries, this could freeze the UI 1195 **/ 1196 function selectAll() //TODO for now dont select more than 100 items so things dont freeze or break 1197 { 1198 if(control.selectionBar == null) 1199 { 1200 return 1201 } 1202 1203 selectIndexes([...Array( control.currentFMList.count ).keys()]) 1204 } 1205 1206 /** 1207 * @brief Add a bookmark to a given list of paths 1208 * @param paths a group of directory URLs to be bookmarked 1209 **/ 1210 function bookmarkFolder(paths) //multiple paths 1211 { 1212 for(var i in paths) 1213 { 1214 FB.FM.bookmark(paths[i]) 1215 } 1216 } 1217 1218 /** 1219 * @brief Open/close the search bar 1220 */ 1221 function toggleSearchBar() //only opens the searchbar toolbar, not the search view page 1222 { 1223 if(control.settings.searchBarVisible) 1224 { 1225 control.settings.searchBarVisible = false 1226 quitSearch() 1227 _browser.forceActiveFocus() 1228 }else 1229 { 1230 control.settings.searchBarVisible = true 1231 _searchField.forceActiveFocus() 1232 } 1233 } 1234 1235 /** 1236 * @brief Forces to open the search view and start a search. 1237 **/ 1238 function openSearch() //opens the search view and focuses the search field 1239 { 1240 if(!control.isSearchView) 1241 { 1242 _stackView.push(_searchBrowserComponent) 1243 } 1244 control.settings.searchBarVisible = true 1245 _searchField.forceActiveFocus() 1246 } 1247 1248 /** 1249 * @brief Forces to close the search view, and return to the browsing view. 1250 **/ 1251 function quitSearch() 1252 { 1253 if(control.currentView.loading) 1254 { 1255 dialogLoader.sourceComponent = _quitSearchDialogComponent 1256 control.dialog.open() 1257 return 1258 } 1259 1260 _stackView.pop() 1261 _browser.forceActiveFocus() 1262 } 1263 1264 /** 1265 * @brief Perform a recursive search starting form the current location and down to all the children directories. 1266 * @param query the text query to match against 1267 **/ 1268 function search(query) 1269 { 1270 openSearch() 1271 _searchField.text = query 1272 1273 _stackView.currentItem.title = i18nd("mauikitfilebrowsing", "Search: %1", query) 1274 _stackView.currentItem.currentFMList.search(query, true) 1275 1276 _stackView.currentItem.forceActiveFocus() 1277 } 1278 1279 /** 1280 * @brief Opens a dialog for typing the name of the new item. 1281 * The new item can be a file or directory. 1282 **/ 1283 function newItem() 1284 { 1285 if(control.isSearchView) 1286 return; 1287 1288 dialogLoader.sourceComponent = newDialogComponent 1289 dialog.open() 1290 dialog.forceActiveFocus() 1291 } 1292 1293 /** 1294 * @brief Opens a dialog for typing the new name of the item 1295 * This will target the current selected item in the browser view 1296 **/ 1297 function renameItem() 1298 { 1299 if(control.isSearchView) 1300 return; 1301 1302 dialogLoader.sourceComponent= renameDialogComponent 1303 dialog.open() 1304 dialog.forceActiveFocus() 1305 } 1306 1307 /** 1308 * @brief Opens a dialog to confirm this operation 1309 * This will target the current selected item in the browser view 1310 **/ 1311 function removeItem() 1312 { 1313 if(control.isSearchView) 1314 return; 1315 1316 dialogLoader.sourceComponent= renameDialogComponent 1317 dialog.open() 1318 dialog.forceActiveFocus() 1319 } 1320 1321 /** 1322 * @brief Filters the contents of the selection to the current path. 1323 * @note Keep in mind that the selection bar can have entries from multiple different locations. With this method only the entries which are inside the `currentPath` will be returned. 1324 * 1325 * @param currentPath The currentPath must be a directory, so the selection entries can be compared as its parent directory. 1326 * @param itemPath The itemPath is a default item path in case the selectionBar is empty 1327 * 1328 * @return the list of entries in the selection that match the currentPath as their parent directory 1329 **/ 1330 function filterSelection(currentPath, itemPath) 1331 { 1332 var res = [] 1333 1334 if(selectionBar && selectionBar.count > 0 && selectionBar.contains(itemPath)) 1335 { 1336 const uris = selectionBar.uris 1337 for(var uri of uris) 1338 { 1339 if(String(FB.FM.parentDir(uri)) === currentPath) 1340 { 1341 res.push(uri) 1342 } 1343 } 1344 1345 } else 1346 { 1347 res = [itemPath] 1348 } 1349 1350 return res 1351 } 1352 1353 /** 1354 * @brief Forces to focus the current view. 1355 */ 1356 function forceActiveFocus() 1357 { 1358 control.currentView.forceActiveFocus() 1359 } 1360 }