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 }