Warning, /maui/mauikit-filebrowsing/src/controls.5/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 2.14
0021 import QtQml 2.14
0022 
0023 import QtQuick.Controls 2.14
0024 import QtQuick.Layouts 1.3
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  * A control to list and browse the file system, with convinient properties
0033  * for filtering and sorting its contents
0034  *
0035  * There are three different possible ways to display the contents: Grid, List and Miller views.
0036  * Some basic file item actions are implemented by default, like copy, cut, rename and remove.
0037  *
0038  * This component functionality can be easily expanded to be more feature rich.
0039  */
0040 Maui.Page
0041 {
0042     id: control
0043 
0044     onGoBackTriggered: control.goBack()
0045     onGoForwardTriggered: control.goForward()
0046 
0047     title: view.title
0048 
0049     focus: true
0050 
0051     flickable: control.currentView.flickable
0052 
0053     floatingFooter: false
0054 
0055     showTitle: false
0056 
0057     /*!
0058      *      \qmlproperty FileBrowser::currentPath
0059      *
0060      *      The current path of the directory URL.
0061      *      To list a directory path, or other location, use the right schemas,
0062      *      some of them are file://, webdav://, trash:///, tags://
0063      */
0064     property alias currentPath : _browser.path
0065     onCurrentPathChanged : _searchField.clear()
0066 
0067     /*!
0068      *      \qmlproperty BrowserSettings FileBrowser::settings
0069      *
0070      *      A group of properties for controlling the sorting, listing and behaviour of the file browser.
0071      *      For more details check the BrowserSettings documentation.
0072      */
0073     property alias settings : _browser.settings
0074 
0075     /*!
0076      *      \qmlproperty Item FileBrowser::view
0077      *
0078      *      The browser can be in two different view states: the file browsing or the search view, this
0079      *      property gives access to the current view in use.
0080      */
0081     property alias view : _stackView.currentItem
0082 
0083     property alias browser : _browser
0084     /*!
0085      *      \qmlproperty DropArea FileBrowser::dropArea
0086      *
0087      *      Drop area component, for dropping files.
0088      *      By default sonme drop actions are handled, for other type of uris this property can be used to handle those.
0089      */
0090     property alias dropArea : _dropArea
0091 
0092     /*!
0093      *      \qmlproperty int FileBrowser::currentIndex
0094      *
0095      *      Current index of the item selected in the file browser.
0096      */
0097     property int currentIndex  : -1
0098 
0099     Binding on currentIndex
0100     {
0101         value: currentView.currentIndex
0102         //         restoreMode: Binding.RestoreBindingOrValue
0103     }
0104 
0105     /*!
0106      *      \qmlproperty Item FileBrowser::currentView
0107      *
0108      *      Current view of the file browser. Possible views are List = ListBrowser
0109      *      Grid = GridView
0110      *      Miller = ListView
0111      */
0112     readonly property QtObject currentView : _stackView.currentItem.browser
0113 
0114     /*!
0115      *      The file browser model list controller being used. The List and Grid views use the same FMList, the
0116      *      Miller columns use several different models, one for each column.
0117      */
0118     readonly property FB.FMList currentFMList : view.currentFMList
0119 
0120     /*!
0121      *      The file browser data model being used. The List and Grid views use the same model, the
0122      *      Miller columns use several different FMList controllers, one for each column.
0123      */
0124     readonly property Maui.BaseModel currentFMModel : view.currentFMModel
0125 
0126     /*!
0127      *      isSearchView : bool
0128      *      If the file browser current view is the search view.
0129      */
0130     readonly property bool isSearchView : _stackView.currentItem.objectName === "searchView"
0131 
0132     /*!
0133      *      If the file browser enters selection mode, allowing the selection of multiple items.
0134      */
0135     property bool selectionMode: false
0136 
0137     /*!
0138      *      \qmlproperty int FileBrowser::gridItemSize
0139      *
0140      *      Size of the items in the grid view. The size is for the combined thumbnail/icon and the title label.
0141      */
0142     property alias gridItemSize : _browser.gridItemSize
0143 
0144     /*!
0145      *      \qmlproperty int FileBrowser::listItemSize
0146      *
0147      *      Size of the items in the grid view. The size is for the combined thumbnail/icon and the title label.
0148      */
0149     property alias listItemSize : _browser.listItemSize
0150 
0151     /*!
0152      *     \qmlproperty var FileBrowser::indexHistory
0153      *
0154      *     History of the items indexes.
0155      */
0156     property var indexHistory : []
0157 
0158     // need to be set by the implementation as features
0159     /*!
0160      *
0161      */
0162     property Maui.SelectionBar selectionBar : null //TODO remove
0163 
0164 
0165     //access to the loaded the dialog components
0166     /*!
0167      *      \qmlproperty Dialog FileBrowser::dialog
0168      *      The message and action dialogs are loaded when needed.
0169      *      This property gives access to the current dialog opened.
0170      */
0171     property alias dialog : dialogLoader.item
0172 
0173     property alias readOnly : _browser.readOnly
0174 
0175 
0176     //signals
0177     /*!
0178      *      An item was clicked.
0179      */
0180     signal itemClicked(int index)
0181 
0182     /**
0183      *     An item was double clicked.
0184      */
0185     signal itemDoubleClicked(int index)
0186 
0187     /*!
0188      *      An item was right clicked, on mobile devices this is translated from a long press and relase.
0189      */
0190     signal itemRightClicked(int index)
0191 
0192     /*!
0193      *      The left emblem of the item was clicked.
0194      */
0195     signal itemLeftEmblemClicked(int index)
0196 
0197     /*!
0198      *      The right emblem of the item was clicked.
0199      */
0200     signal itemRightEmblemClicked(int index)
0201 
0202     /*!
0203      *      The file browser empty area was right clicked.
0204      */
0205     signal rightClicked()
0206 
0207     /*!
0208      *      The file browser empty area was right clicked.
0209      */
0210     signal areaClicked(var mouse)
0211 
0212 
0213     /*!
0214      *      A key, physical or not, was pressed.
0215      *      The event contains the relevant information.
0216      */
0217     signal keyPress(var event)
0218 
0219     /*!
0220      *      File URLS were dropped onto the file browser area.
0221      */
0222     signal urlsDropped(var urls)
0223 
0224     headBar.forceCenterMiddleContent: isWide
0225     headBar.visible: control.settings.searchBarVisible
0226     headBar.leftContent: Loader
0227     {
0228         asynchronous: true
0229         active: control.isSearchView
0230         visible: active
0231         sourceComponent: ToolButton
0232         {
0233             text: i18nd("mauikitfilebrowsing", "Back")
0234             icon.name: "go-previous"
0235             onClicked: control.quitSearch()
0236         }
0237     }
0238 
0239     Maui.InfoDialog
0240     {
0241         id: _quitSearchDialog
0242         title: i18n("Quit")
0243         message: i18n("Are you sure you want to quit the current search in progress?")
0244         onAccepted:
0245         {
0246             _stackView.pop()
0247             _browser.forceActiveFocus()
0248         }
0249 
0250         onRejected: close()
0251     }
0252 
0253     headBar.middleContent: Maui.SearchField
0254     {
0255         id: _searchField
0256         focus: true
0257         Layout.fillWidth: true
0258         Layout.maximumWidth: 500
0259         Layout.alignment: Qt.AlignCenter
0260         placeholderText: _filterButton.checked ? i18nd("mauikitfilebrowsing", "Filter") : ("Search")
0261         inputMethodHints: Qt.ImhNoAutoUppercase
0262 
0263         onAccepted:
0264         {
0265             if(_filterButton.checked)
0266             {
0267                 if(text.includes(","))
0268                 {
0269                     control.view.filters = text.split(",")
0270                 }else
0271                 {
0272                     control.view.filter = text
0273                 }
0274 
0275             }else
0276             {
0277                 control.search(text)
0278             }
0279         }
0280 
0281         onCleared:
0282         {
0283             if(_filterButton.checked)
0284             {
0285                 control.currentFMModel.clearFilters()
0286             }
0287         }
0288 
0289         onTextChanged:
0290         {
0291             if(_filterButton.checked)
0292                 _searchField.accepted()
0293         }
0294 
0295         Keys.enabled: _filterButton.checked
0296         Keys.onPressed:
0297         {
0298             // Shortcut for clearing selection
0299             if(event.key == Qt.Key_Up)
0300             {
0301                 control.currentView.forceActiveFocus()
0302             }
0303         }
0304 
0305         actions: Action
0306         {
0307             id: _filterButton
0308             icon.name: "view-filter"
0309             //            text: i18nd("mauikitfilebrowsing", "Filter")
0310             checkable: true
0311             checked: true
0312             onTriggered:
0313             {
0314                 control.view.filter = ""
0315                 _searchField.clear()
0316                 _searchField.forceActiveFocus()
0317             }
0318         }
0319     }
0320 
0321     footBar.visible: control.currentPath.startsWith("trash:/")
0322 
0323     footerPositioning: ListView.InlineFooter
0324 
0325     footBar.rightContent: Button
0326     {
0327         visible: control.currentPath.startsWith("trash:/")
0328         icon.name: "trash-empty"
0329         text: i18nd("mauikitfilebrowsing", "Empty Trash")
0330         onClicked: FB.FM.emptyTrash()
0331     }
0332 
0333     Loader
0334     {
0335         id: dialogLoader
0336     }
0337 
0338     Component
0339     {
0340         id: removeDialogComponent
0341 
0342         Maui.FileListingDialog
0343         {
0344             id: _removeDialog
0345 
0346             property double freedSpace : calculateFreedSpace(urls)
0347 
0348             title:  i18nd("mauikitfilebrowsing", "Removing %1 files", urls.length)
0349             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))
0350 
0351             actions: [
0352                 Action
0353                 {
0354                     text: i18nd("mauikitfilebrowsing", "Cancel")
0355                     onTriggered: _removeDialog.close()
0356                 },
0357 
0358                 Action
0359                 {
0360                     text: i18nd("mauikitfilebrowsing", "Delete")
0361                     onTriggered:
0362                     {
0363                         control.currentFMList.removeFiles(urls)
0364                         close()
0365                     }
0366                 },
0367                 Action
0368                 {
0369                     text: i18nd("mauikitfilebrowsing", "Trash")
0370                     enabled: Maui.Handy.isLinux
0371                     onTriggered:
0372                     {
0373                         control.currentFMList.moveToTrash(urls)
0374                         close()
0375                     }
0376                 }
0377             ]
0378 
0379             function calculateFreedSpace(urls)
0380             {
0381                 var size = 0
0382                 for(var url of urls)
0383                 {
0384                     size += parseFloat(FB.FM.getFileInfo(url).size)
0385                 }
0386 
0387                 return size
0388             }
0389         }
0390     }
0391 
0392     Component
0393     {
0394         id: newDialogComponent
0395         //
0396         Maui.InputDialog
0397         {
0398             id: _newDialog
0399 
0400             title:  _newActions.currentIndex === 0  ? i18nd("mauikitfilebrowsing", "New folder") : i18nd("mauikitfilebrowsing", "New file")
0401             message: i18nd("mauikitfilebrowsing", "Create a new folder or a file with a custom name.")
0402 
0403             template.iconSource: FB.FM.getIconName(textEntry.text)
0404             template.iconVisible: true
0405 
0406             onFinished:
0407             {
0408                 if(_newDirOp.checked)
0409                 {
0410                     control.currentFMList.createDir(text)
0411                     return
0412                 }
0413 
0414                 if(_newFileOp.checked)
0415                 {
0416                     control.currentFMList.createFile(text)
0417                     return
0418                 }
0419             }
0420 
0421             textEntry.placeholderText: i18nd("mauikitfilebrowsing", "Name")
0422 
0423             Maui.ToolActions
0424             {
0425                 id: _newActions
0426                 expanded: true
0427                 autoExclusive: true
0428                 display: ToolButton.TextBesideIcon
0429 
0430                 Action
0431                 {
0432                     id: _newDirOp
0433                     icon.name: "folder-new"
0434                     text: i18nd("mauikitfilebrowsing", "Folder")
0435                     checked: String(_newDialog.textEntry.text).indexOf(".") < 0
0436                 }
0437 
0438                 Action
0439                 {
0440                     id: _newFileOp
0441                     icon.name: "document-new"
0442                     text: i18nd("mauikitfilebrowsing", "File")
0443                     checked: String(_newDialog.textEntry.text).indexOf(".") >= 0
0444                 }
0445             }
0446         }
0447     }
0448 
0449     Component
0450     {
0451         id: renameDialogComponent
0452 
0453         Maui.InputDialog
0454         {
0455             id: _renameDialog
0456 
0457             property var item : ({})
0458 
0459             title: i18nd("mauikitfilebrowsing", "Rename")
0460             message: i18nd("mauikitfilebrowsing", "Change the name of a file or folder. Write a new name and click Rename to apply the change.")
0461 
0462             //            headBar.visible: false
0463 
0464             template.iconSource: item.icon
0465             template.imageSource: item.thumbnail
0466             template.iconSizeHint: Maui.Style.iconSizes.huge
0467 
0468             textEntry.text: item.label
0469             textEntry.placeholderText: i18nd("mauikitfilebrowsing", "New name")
0470 
0471             onFinished: control.currentFMList.renameFile(item.path, textEntry.text)
0472             onRejected: close()
0473 
0474             //            acceptButton.text: i18nd("mauikitfilebrowsing", "Rename")
0475             //            rejectButton.text: i18nd("mauikitfilebrowsing", "Cancel")
0476 
0477             onOpened:
0478             {
0479                 item = control.currentFMModel.get(control.currentIndex)
0480 
0481                 if(_renameDialog.textEntry.text.lastIndexOf(".") >= 0)
0482                 {
0483                     _renameDialog.textEntry.select(0, _renameDialog.textEntry.text.lastIndexOf("."))
0484                 }else
0485                 {
0486                     _renameDialog.textEntry.selectAll()
0487                 }
0488             }
0489         }
0490     }
0491 
0492     Component
0493     {
0494         id: _newTagDialogComponent
0495         FB.NewTagDialog {}
0496     }
0497 
0498     property string typingQuery
0499 
0500     Maui.Chip
0501     {
0502         z: control.z + 99999
0503         Maui.Theme.colorSet:Maui.Theme.Complementary
0504         visible: _typingTimer.running
0505         label.text: typingQuery
0506         anchors.left: parent.left
0507         anchors.top: parent.top
0508         showCloseButton: false
0509         anchors.margins: Maui.Style.space.medium
0510     }
0511 
0512     Timer
0513     {
0514         id: _typingTimer
0515         interval: 250
0516         onTriggered:
0517         {
0518             const index = control.currentFMList.indexOfName(typingQuery)
0519             if(index > -1)
0520             {
0521                 console.log("FOUDN TRYPIGN IDNEX", index)
0522                 control.currentIndex = control.currentFMModel.mappedFromSource(index)
0523             }
0524 
0525             typingQuery = ""
0526         }
0527     }
0528 
0529     Connections
0530     {
0531         target: control.currentView
0532         ignoreUnknownSignals: true
0533 
0534         function onKeyPress(event)
0535         {
0536             const index = control.currentIndex
0537             const item = control.currentFMModel.get(index)
0538 
0539             var pat = /^([a-zA-Z0-9 _-]+)$/
0540 
0541             if(event.count === 1 && pat.test(event.text))
0542             {
0543                 typingQuery += event.text
0544                 _typingTimer.restart()
0545                 event.accepted = true
0546             }
0547 
0548             // Shortcuts for refreshing
0549             if((event.key === Qt.Key_F5))
0550             {
0551                 control.currentFMList.refresh()
0552                 event.accepted = true
0553             }
0554 
0555             // Shortcuts for renaming
0556             if((event.key === Qt.Key_F2))
0557             {
0558                 dialogLoader.sourceComponent = renameDialogComponent
0559                 dialog.open()
0560                 event.accepted = true
0561             }
0562 
0563             // Shortcuts for selecting file
0564             if((event.key === Qt.Key_A) && (event.modifiers & Qt.ControlModifier))
0565             {
0566                 control.selectAll()
0567                 event.accepted = true
0568             }
0569 
0570             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))
0571             {
0572                 if(control.selectionBar && control.selectionBar.contains(item.path))
0573                 {
0574                     control.selectionBar.removeAtUri(item.path)
0575                 }else
0576                 {
0577                     control.addToSelection(item)
0578                 }
0579                 //event.accepted = true
0580             }
0581 
0582             //shortcut for opening files
0583             if(event.key === Qt.Key_Return)
0584             {
0585                 indexHistory.push(index)
0586                 control.openItem(index)
0587                 event.accepted = true
0588             }
0589 
0590             // Shortcut for pasting an item
0591             if((event.key == Qt.Key_V) && (event.modifiers & Qt.ControlModifier))
0592             {
0593                 control.paste()
0594                 event.accepted = true
0595             }
0596 
0597             // Shortcut for cutting an item
0598             if((event.key == Qt.Key_X) && (event.modifiers & Qt.ControlModifier))
0599             {
0600                 const urls = filterSelection(control.currentPath, item.path)
0601                 control.cut(urls)
0602                 event.accepted = true
0603             }
0604 
0605             // Shortcut for copying an item
0606             if((event.key == Qt.Key_C) && (event.modifiers & Qt.ControlModifier))
0607             {
0608                 const urls = filterSelection(control.currentPath, item.path)
0609                 control.copy(urls)
0610                 event.accepted = true
0611             }
0612 
0613             // Shortcut for removing an item
0614             if(event.key === Qt.Key_Delete)
0615             {
0616                 const urls = filterSelection(control.currentPath, item.path)
0617                 control.remove(urls)
0618                 event.accepted = true
0619             }
0620 
0621             // Shortcut for going back in browsing history
0622             if(event.key === Qt.Key_Backspace || event.key == Qt.Key_Back)
0623             {
0624                 if(control.selectionBar && control.selectionBar.count> 0)
0625                 {
0626                     control.selectionBar.clear()
0627                 }
0628                 else
0629                 {
0630                     control.goBack()
0631                 }
0632                 event.accepted = true
0633             }
0634 
0635             // Shortcut for clearing selection and filtering
0636             if(event.key === Qt.Key_Escape) //TODO not working, the event is not catched or emitted or is being accepted else where?
0637             {
0638                 if(control.selectionBar && control.selectionBar.count > 0)
0639                 {
0640                     control.selectionBar.clear()
0641                 }
0642 
0643                 control.view.filter = ""
0644                 event.accepted = true
0645             }
0646 
0647             //Shortcut for opening filtering
0648             if((event.key === Qt.Key_F) && (event.modifiers & Qt.ControlModifier))
0649             {
0650                 control.toggleSearchBar()
0651                 event.accepted = true
0652             }
0653 
0654             control.keyPress(event)
0655         }
0656 
0657         function onItemsSelected(indexes)
0658         {
0659             if(indexes.length)
0660             {
0661                 control.currentIndex = indexes[0]
0662                 control.selectIndexes(indexes)
0663             }
0664         }
0665 
0666         function onItemClicked(index)
0667         {
0668             control.currentIndex = index
0669             indexHistory.push(index)
0670             control.itemClicked(index)
0671             control.currentView.forceActiveFocus()
0672         }
0673 
0674         function onItemDoubleClicked(index)
0675         {
0676             control.currentIndex = index
0677             indexHistory.push(index)
0678             control.itemDoubleClicked(index)
0679             control.currentView.forceActiveFocus()
0680         }
0681 
0682         function onItemRightClicked(index)
0683         {
0684             control.currentIndex = index
0685             control.itemRightClicked(index)
0686             control.currentView.forceActiveFocus()
0687         }
0688 
0689         function onItemToggled(index)
0690         {
0691             const item = control.currentFMModel.get(index)
0692 
0693             if(control.selectionBar && control.selectionBar.contains(item.path))
0694             {
0695                 control.selectionBar.removeAtUri(item.path)
0696             }else
0697             {
0698                 control.addToSelection(item)
0699             }
0700             control.itemLeftEmblemClicked(index)
0701             control.currentView.forceActiveFocus()
0702         }
0703 
0704 
0705         function onAreaClicked(mouse)
0706         {
0707             if(control.isSearchView)
0708                 return
0709 
0710             if(!Maui.Handy.isMobile && mouse.button === Qt.RightButton)
0711             {
0712                 control.rightClicked()
0713             }
0714 
0715             control.areaClicked(mouse)
0716             control.currentView.forceActiveFocus()
0717         }
0718     }
0719 
0720     Maui.ContextualMenu
0721     {
0722         id: _dropMenu
0723         property string urls
0724         enabled: !control.isSearchView
0725 
0726         MenuItem
0727         {
0728             enabled: !control.readOnly
0729             text: i18nd("mauikitfilebrowsing", "Copy Here")
0730             icon.name: "edit-copy"
0731             onTriggered:
0732             {
0733                 const urls = _dropMenu.urls.split(",")
0734                 control.currentFMList.copyInto(urls)
0735             }
0736         }
0737 
0738         MenuItem
0739         {
0740             enabled: !control.readOnly
0741             text: i18nd("mauikitfilebrowsing", "Move Here")
0742             icon.name: "edit-move"
0743             onTriggered:
0744             {
0745                 const urls = _dropMenu.urls.split(",")
0746                 control.currentFMList.cutInto(urls)
0747             }
0748         }
0749 
0750         MenuItem
0751         {
0752             enabled: !control.readOnly
0753 
0754             text: i18nd("mauikitfilebrowsing", "Link Here")
0755             icon.name: "edit-link"
0756             onTriggered:
0757             {
0758                 const urls = _dropMenu.urls.split(",")
0759                 for(var i in urls)
0760                     control.currentFMList.createSymlink(urls[i])
0761             }
0762         }
0763 
0764         MenuItem
0765         {
0766             enabled: FB.FM.isDir(_dropMenu.urls.split(",")[0])
0767             text: i18nd("mauikitfilebrowsing", "Open Here")
0768             icon.name: "folder-open"
0769             onTriggered:
0770             {
0771                 const urls = _dropMenu.urls.split(",")
0772                 control.browser.path = urls[0]
0773             }
0774         }
0775 
0776         MenuSeparator {}
0777 
0778         MenuItem
0779         {
0780             text: i18nd("mauikitfilebrowsing", "Cancel")
0781             icon.name: "dialog-cancel"
0782             onTriggered: _dropMenu.close()
0783         }
0784     }
0785 
0786     StackView
0787     {
0788         id: _stackView
0789         anchors.fill: parent
0790 
0791         initialItem: DropArea
0792         {
0793             id: _dropArea
0794             property alias browser : _browser
0795             property alias currentFMList : _browser.currentFMList
0796             property alias currentFMModel: _browser.currentFMModel
0797             property alias filter: _browser.filter
0798             property alias filters: _browser.filters
0799             property alias title : _browser.title
0800 
0801             onDropped:
0802             {
0803                 if(drop.urls)
0804                 {
0805                     _dropMenu.urls = drop.urls.join(",")
0806                     _dropMenu.show()
0807                     control.urlsDropped(drop.urls)
0808                 }
0809             }
0810 
0811             opacity:  _dropArea.containsDrag ? 0.5 : 1
0812 
0813             Private.BrowserView
0814             {
0815                 id: _browser
0816                 anchors.fill: parent
0817                 selectionMode: control.selectionMode
0818 
0819                 Binding on currentIndex
0820                 {
0821                     value: control.currentIndex
0822                     restoreMode: Binding.RestoreBindingOrValue
0823                 }
0824 
0825                 Loader
0826                 {
0827                     active: (control.currentPath === "tags://" ||  control.currentPath === "tags:///") && !control.readOnly
0828                     visible: active
0829                     asynchronous: true
0830 
0831                     anchors.right: parent.right
0832                     anchors.bottom: parent.bottom
0833                     anchors.rightMargin: Maui.Style.toolBarHeight
0834                     anchors.bottomMargin: Maui.Style.toolBarHeight + control.flickable.bottomMargin
0835 
0836                     sourceComponent: Maui.FloatingButton
0837                     {
0838                         icon.name : "list-add"
0839                         onClicked:
0840                         {
0841                             dialogLoader.sourceComponent = _newTagDialogComponent
0842                             dialog.open()
0843                         }
0844                     }
0845                 }
0846             }
0847         }
0848 
0849         Component
0850         {
0851             id: _searchBrowserComponent
0852 
0853             Private.BrowserView
0854             {
0855                 id: _searchBrowser
0856                 property alias browser : _searchBrowser
0857                 readOnly: true
0858                 path: control.currentPath
0859                 Binding on currentIndex
0860                 {
0861                     value: control.currentIndex
0862                     restoreMode: Binding.RestoreBindingOrValue
0863                 }
0864 
0865                 objectName: "searchView"
0866                 gridItemSize: control.gridItemSize
0867                 listItemSize: control.listItemSize
0868 
0869                 currentFMList.autoLoad: false
0870                 settings.viewType: control.settings.viewType
0871                 settings.sortBy: control.settings.sortBy
0872                 settings.showHiddenFiles: control.settings.showHiddenFiles
0873                 settings.group: control.settings.group
0874                 settings.foldersFirst: control.settings.foldersFirst
0875 
0876             }
0877         }
0878     }
0879 
0880     Component.onCompleted:
0881     {
0882         control.currentView.forceActiveFocus()
0883     }
0884 
0885     /**
0886      *
0887      **/
0888     function copy(urls)
0889     {
0890         if(urls.length <= 0)
0891         {
0892             return
0893         }
0894 
0895         Maui.Handy.copyToClipboard({"urls": urls}, false)
0896     }
0897 
0898     /**
0899      *
0900      **/
0901     function cut(urls)
0902     {
0903         if(control.readOnly)
0904             return
0905 
0906         if(urls.length <= 0)
0907         {
0908             return
0909         }
0910 
0911         Maui.Handy.copyToClipboard({"urls": urls}, true)
0912     }
0913 
0914     /**
0915      *
0916      **/
0917     function paste()
0918     {
0919         control.currentFMList.paste()
0920     }
0921 
0922     /**
0923      *
0924      **/
0925     function remove(urls)
0926     {
0927         if(urls.length <= 0)
0928         {
0929             return
0930         }
0931 
0932         dialogLoader.sourceComponent = removeDialogComponent
0933         dialog.urls = urls
0934         dialog.open()
0935     }
0936 
0937     /**
0938      *
0939      **/
0940     function openItem(index)
0941     {
0942         const item = control.currentFMModel.get(index)
0943         const path = item.path
0944 
0945         switch(control.currentFMList.pathType)
0946         {
0947         case FB.FMList.CLOUD_PATH: //TODO deprecrated and needs to be removed or clean up for 1.1
0948             if(item.isdir === "true")
0949             {
0950                 control.openFolder(path)
0951             }
0952             else
0953             {
0954                 FB.FM.openCloudItem(item)
0955             }
0956             break;
0957         default:
0958             if(control.selectionMode && item.isdir == "false")
0959             {
0960                 if(control.selectionBar && control.selectionBar.contains(item.path))
0961                 {
0962                     control.selectionBar.removeAtPath(item.path)
0963                 }else
0964                 {
0965                     control.addToSelection(item)
0966                 }
0967             }
0968             else
0969             {
0970                 if(item.isdir == "true")
0971                 {
0972                     control.openFolder(path)
0973                 }
0974                 else
0975                 {
0976                     control.openFile(path)
0977                 }
0978             }
0979         }
0980     }
0981 
0982     /**
0983      *
0984      **/
0985     function openFile(path)
0986     {
0987         FB.FM.openUrl(path)
0988     }
0989 
0990     /**
0991      *
0992      **/
0993     function openFolder(path)
0994     {
0995         if(!String(path).length)
0996         {
0997             return;
0998         }
0999 
1000         if(control.isSearchView)
1001         {
1002             control.quitSearch()
1003         }
1004 
1005         control.currentPath = path
1006         _browser.forceActiveFocus()
1007     }
1008 
1009     /**
1010      *
1011      **/
1012     function goBack()
1013     {
1014         openFolder(control.currentFMList.previousPath())
1015         //        control.currentIndex = indexHistory.pop()
1016     }
1017 
1018     /**
1019      *
1020      **/
1021     function goForward()
1022     {
1023         openFolder(control.currentFMList.posteriorPath())
1024     }
1025 
1026     /**
1027      *
1028      **/
1029     function goUp()
1030     {
1031         openFolder(control.currentFMList.parentPath)
1032     }
1033 
1034     /**
1035      * For this to work the implementation needs to have passed a selectionBar
1036      **/
1037     function addToSelection(item)
1038     {
1039         if(control.selectionBar == null || item.path.startsWith("tags://") || item.path.startsWith("applications://"))
1040         {
1041             return
1042         }
1043 
1044         control.selectionBar.append(item.path, item)
1045     }
1046 
1047 
1048     /**
1049      * Given a list of indexes add them to the selectionBar
1050      **/
1051     function selectIndexes(indexes)
1052     {
1053         if(control.selectionBar == null)
1054         {
1055             return
1056         }
1057 
1058         for(var i in indexes)
1059             addToSelection(control.currentFMModel.get(indexes[i]))
1060     }
1061 
1062     /**
1063      *
1064      **/
1065     function selectAll() //TODO for now dont select more than 100 items so things dont freeze or break
1066     {
1067         if(control.selectionBar == null)
1068         {
1069             return
1070         }
1071 
1072         selectIndexes([...Array( control.currentFMList.count ).keys()])
1073     }
1074 
1075     /**
1076      *
1077      **/
1078     function bookmarkFolder(paths) //multiple paths
1079     {
1080         for(var i in paths)
1081         {
1082             FB.FM.bookmark(paths[i])
1083         }
1084     }
1085 
1086     function toggleSearchBar() //only opens the searchbar toolbar, not the search view page
1087     {
1088         if(control.settings.searchBarVisible)
1089         {
1090             control.settings.searchBarVisible = false
1091             quitSearch()
1092             _browser.forceActiveFocus()
1093         }else
1094         {
1095             control.settings.searchBarVisible = true
1096             _searchField.forceActiveFocus()
1097         }
1098     }
1099 
1100     /**
1101      *
1102      **/
1103     function openSearch() //opens the search view and focuses the search field
1104     {
1105         if(!control.isSearchView)
1106         {
1107             _stackView.push(_searchBrowserComponent)
1108         }
1109         control.settings.searchBarVisible = true
1110         _searchField.forceActiveFocus()
1111     }
1112 
1113     /**
1114      *
1115      **/
1116     function quitSearch()
1117     {
1118         if(control.currentView.loading)
1119         {
1120             _quitSearchDialog.open()
1121             return
1122         }
1123 
1124         _stackView.pop()
1125         _browser.forceActiveFocus()
1126     }
1127 
1128     /**
1129      *
1130      **/
1131     function search(query)
1132     {
1133         openSearch()
1134         _searchField.text = query
1135 
1136         _stackView.currentItem.title = i18nd("mauikitfilebrowsing", "Search: %1", query)
1137         _stackView.currentItem.currentFMList.search(query, true)
1138 
1139         _stackView.currentItem.forceActiveFocus()
1140     }
1141 
1142     /**
1143      *
1144      **/
1145     function newItem()
1146     {
1147         if(control.isSearchView)
1148             return;
1149 
1150         dialogLoader.sourceComponent = newDialogComponent
1151         dialog.open()
1152         dialog.forceActiveFocus()
1153     }
1154 
1155     /**
1156      *
1157      **/
1158     function renameItem()
1159     {
1160         if(control.isSearchView)
1161             return;
1162 
1163         dialogLoader.sourceComponent= renameDialogComponent
1164         dialog.open()
1165         dialog.forceActiveFocus()
1166     }
1167 
1168     /**
1169      *
1170      **/
1171     function removeItem()
1172     {
1173         if(control.isSearchView)
1174             return;
1175 
1176         dialogLoader.sourceComponent= renameDialogComponent
1177         dialog.open()
1178         dialog.forceActiveFocus()
1179     }
1180 
1181     /**
1182      * Filters the content of the selection to the current path. The currentPath must be a directory, so the selection can be compared if it is its parent directory. The itemPath is a default item path in case the selectionBar is empty
1183      **/
1184     function filterSelection(currentPath, itemPath)
1185     {
1186         var res = []
1187 
1188         if(selectionBar && selectionBar.count > 0 && selectionBar.contains(itemPath))
1189         {
1190             const uris = selectionBar.uris
1191             for(var uri of uris)
1192             {
1193                 if(String(FB.FM.parentDir(uri)) === currentPath)
1194                 {
1195                     res.push(uri)
1196                 }
1197             }
1198 
1199         } else
1200         {
1201             res = [itemPath]
1202         }
1203 
1204         return res
1205     }
1206 
1207     function forceActiveFocus()
1208     {
1209         control.currentView.forceActiveFocus()
1210     }
1211 }