Warning, /maui/vvave/src/main.qml is written in an unsupported language. File is not indexed.

0001 import QtQuick 2.14
0002 import QtQuick.Controls 2.14
0003 import QtQuick.Window 2.15
0004 
0005 import Qt.labs.settings 1.0
0006 
0007 import org.mauikit.controls 1.3 as Maui
0008 import org.mauikit.filebrowsing 1.3 as FB
0009 import org.mauikit.accounts 1.0 as MA
0010 
0011 import org.maui.vvave 1.0
0012 
0013 import "widgets"
0014 import "widgets/PlaylistsView"
0015 import "widgets/MainPlaylist"
0016 import "widgets/SettingsView"
0017 import "widgets/CloudView"
0018 import "widgets/FoldersView"
0019 
0020 import "utils/Player.js" as Player
0021 
0022 Maui.ApplicationWindow
0023 {
0024     id: root
0025 
0026     title: currentTrack.url ? currentTrack.title + " - " +  currentTrack.artist + " | " + currentTrack.album : ""
0027 
0028     Maui.Style.styleType: focusView ? Maui.Style.Adaptive : (Maui.Handy.isAndroid ? settings.darkMode ? Maui.Style.Dark : Maui.Style.Light : undefined)
0029     //    flags: miniMode ? Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Popup | Qt.BypassWindowManagerHint : undefined
0030 
0031     readonly property int preferredMiniModeSize: 200
0032     //    minimumHeight: miniMode ? preferredMiniModeSize : 300
0033     //    minimumWidth: miniMode ? preferredMiniModeSize : 200
0034 
0035     //    maximumWidth: miniMode ? minimumWidth : Screen.desktopAvailableWidth
0036     //    maximumHeight: miniMode ? minimumHeight : Screen.desktopAvailableHeight
0037 
0038     /***************************************************/
0039     /******************** ALIASES ********************/
0040     /*************************************************/
0041     readonly property alias selectionBar: _selectionBar
0042     readonly property alias dialog : _dialogLoader.item
0043     readonly property alias playlistManager : playlist
0044 
0045     /***************************************************/
0046     /******************** PLAYBACK ********************/
0047     /*************************************************/
0048     readonly property alias currentTrack : playlist.currentTrack
0049     readonly property alias currentTrackIndex: playlist.currentIndex
0050 
0051     readonly property alias isPlaying: player.playing
0052     property alias mainPlaylist : _mainPlaylistLoader.item
0053     readonly property bool mainlistEmpty: mainPlaylist ? mainPlaylist.listModel.list.count === 0 : false
0054 
0055     /***************************************************/
0056     /******************** HANDLERS ********************/
0057     /*************************************************/
0058     readonly property var viewsIndex: ({ tracks: 0,
0059                                            albums: 1,
0060                                            artists: 2,
0061                                            playlists: 3,
0062                                            folders: 4,
0063                                            cloud: 5 })
0064 
0065     property string syncPlaylist: ""
0066     property bool sync: false
0067     property string sleepOption : "none"
0068     property bool closeAfterSleep: false
0069 
0070     readonly property bool focusView : _stackView.currentItem.objectName === "FocusView"
0071     readonly property bool miniMode : _miniModeComponent.visible
0072 
0073     property bool selectionMode : false
0074 
0075     /***************************************************/
0076     /******************** UI COLORS *******************/
0077     /*************************************************/
0078     readonly property color babeColor: "#f84172"
0079 
0080     /*HANDLE EVENTS*/
0081     signal contextualPlayNext()
0082 
0083     onClosing: (close) =>
0084                {
0085                    playlist.save()
0086                    close.accepted = true
0087                }
0088 
0089     onFocusViewChanged: setAndroidStatusBarColor()
0090 
0091     Loader
0092     {
0093         id: _timerLoader
0094         active: false
0095 
0096         sourceComponent: Timer
0097         {
0098             onTriggered:
0099             {
0100                 Player.stop()
0101                 if(closeAfterSleep)
0102                     root.close()
0103             }
0104         }
0105     }
0106 
0107     // NOTE: Anything in `.dialogLabel` or `.dialogCategory` get dynamically passed to `i18n` in ShortcutsDialog.qml, and thus should have translations. They are not translated here in case that affects their uniqueness as object keys.
0108     property list<Shortcut> shortcuts: [
0109         Shortcut
0110         {
0111             readonly property string dialogLabel: "Play/Pause"
0112             readonly property string dialogCategory: "Playback"
0113             sequence: "Space"
0114             onActivated: {
0115                 if(player.playing)
0116                     player.pause()
0117                 else
0118                     player.play()
0119             }
0120         },
0121 
0122         Shortcut
0123         {
0124             readonly property string dialogLabel: "Previous"
0125             readonly property string dialogCategory: "Playback"
0126             sequence: "P"
0127             onActivated: Player.previousTrack()
0128         },
0129 
0130         Shortcut
0131         {
0132             readonly property string dialogLabel: "Next"
0133             readonly property string dialogCategory: "Playback"
0134             sequence: "N"
0135             onActivated: Player.nextTrack()
0136         },
0137 
0138         Shortcut
0139         {
0140             readonly property string dialogLabel: "Rewind 10 seconds"
0141             readonly property string dialogCategory: "Playback"
0142             sequence: "Left"
0143             enabled: !(activeFocusItem instanceof Maui.GridBrowser || activeFocusItem instanceof GridView)
0144             onActivated: player.pos -= 10000
0145         },
0146 
0147         Shortcut
0148         {
0149             readonly property string dialogLabel: "Skip 10 seconds"
0150             readonly property string dialogCategory: "Playback"
0151             sequence: "Right"
0152             enabled: !(activeFocusItem instanceof Maui.GridBrowser || activeFocusItem instanceof GridView)
0153             onActivated: player.pos += 10000
0154         },
0155 
0156         Shortcut
0157         {
0158             readonly property string dialogLabel: "Increase Volume"
0159             readonly property string dialogCategory: "Playback"
0160             sequence: "+"
0161             sequences: ["="]
0162             onActivated: player.volume += 5
0163         },
0164 
0165         Shortcut
0166         {
0167             readonly property string dialogLabel: "Decrease Volume"
0168             readonly property string dialogCategory: "Playback"
0169             sequence: "-"
0170             onActivated: player.volume -= 5
0171         },
0172 
0173         Shortcut
0174         {
0175             readonly property string dialogLabel: "Filter"
0176             readonly property string dialogCategory: "Navigation"
0177             sequence: StandardKey.Find
0178             onActivated: {
0179                 console.log("FOCUS FILTER")
0180 
0181                 let filterField = getFilterField()
0182 
0183                 if (!filterField)
0184                     return
0185 
0186                 if (!filterField.activeFocus)
0187                     filterField.forceActiveFocus()
0188                 else
0189                     filterField.focus = false
0190             }
0191         },
0192 
0193         Shortcut
0194         {
0195             readonly property string dialogLabel: "Focus View"
0196             readonly property string dialogCategory: "Navigation"
0197             sequence: StandardKey.Cancel
0198             onActivated: {
0199                 // I couldn't get Keys.onShortcutOverride in each view to work. I guess this is more dynamic anyway.
0200                 let func = getGoBackFunc()
0201                 if (func) {
0202                     func()
0203                     return
0204                 }
0205                 toggleFocusView()
0206             }
0207         },
0208 
0209         Shortcut
0210         {
0211             readonly property string dialogLabel: "Go Back"
0212             readonly property string dialogCategory: "Navigation"
0213             sequence: StandardKey.Back
0214             onActivated: {
0215                 // I couldn't get Keys.onShortcutOverride in each view to work. I guess this is more dynamic anyway.
0216                 let func = getGoBackFunc()
0217                 if (func) {
0218                     func()
0219                     return
0220                 }
0221             }
0222         },
0223 
0224         Shortcut
0225         {
0226             readonly property string dialogLabel: "Next Category"
0227             readonly property string dialogCategory: "Navigation"
0228             sequence: "Ctrl+Tab" // StandardKey.NextChild and .PreviousChild seem broken on Linux.
0229             onActivated: swipeView.currentIndex = ((swipeView.currentIndex + 1) % swipeView.count + swipeView.count) % swipeView.count
0230         },
0231 
0232         Shortcut
0233         {
0234             readonly property string dialogLabel: "Previous Category"
0235             readonly property string dialogCategory: "Navigation"
0236             sequence: "Ctrl+Shift+Tab"
0237             onActivated: swipeView.currentIndex = ((swipeView.currentIndex - 1) % swipeView.count + swipeView.count) % swipeView.count
0238         },
0239 
0240         Shortcut
0241         {
0242             readonly property string dialogLabel: "Queue Track"
0243             readonly property string dialogCategory: "Navigation"
0244             sequence: "Shift+Return"
0245             sequences: ["Shift+Enter"]
0246             // StandardKey.InsertLineSeparator only gets "Enter", not "Return".
0247             onActivated: contextualPlayNext()
0248         },
0249 
0250         Shortcut
0251         {
0252             readonly property string dialogLabel: "Context Actions"
0253             readonly property string dialogCategory: "Navigation"
0254             sequence: "Menu"
0255             onActivated: {
0256                 if (activeFocusItem) {
0257                     let func = (activeFocusItem.currentItem ?? activeFocusItem).tryOpenContextMenu
0258                     if (func) {
0259                         func()
0260                         return
0261                     }
0262                 }
0263                 console.log("NO CONTEXT MENU", activeFocusItem, activeFocusItem.currentItem)
0264             }
0265         }]
0266 
0267     Loader
0268     {
0269         active: (root.isPlaying && !root.mainlistEmpty)
0270         asynchronous: true
0271         sourceComponent: FloatingDisk {}
0272     }
0273 
0274     Settings
0275     {
0276         id: settings
0277         category: "Settings"
0278         property bool fetchArtwork: true
0279         property bool autoScan: true
0280         property bool darkMode : true
0281         property bool focusViewDefault: false
0282         property alias sideBarWidth : _sideBarView.sideBar.preferredWidth
0283         property bool showArtwork: false
0284         property bool showTitles: true
0285         property bool volumeControl: true
0286     }
0287 
0288     Mpris2
0289     {
0290         playListModel: playlist
0291         audioPlayer: player
0292         playerName: 'vvave'
0293 
0294         onRaisePlayer:
0295         {
0296             root.raise()
0297         }
0298     }
0299 
0300     Playlist
0301     {
0302         id: playlist
0303 
0304         model: mainPlaylist.listModel.list
0305         onCurrentTrackChanged: Player.playTrack()
0306 
0307         onMissingFile: (track) =>
0308                        {
0309                            var message = i18n("Missing file")
0310                            var messageBody = track.title + " by " + track.artist + " is missing.\nDo you want to remove it from your collection?"
0311                            notify("dialog-question", message, messageBody, function ()
0312                            {
0313                                console.log("REMOVE TIU MSISING", mainPlaylist.table.currentIndex)
0314                                mainPlaylist.table.list.removeMissing(mainPlaylist.table.currentIndex)
0315                                console.log("REMOVE TIU MSISING 2", mainPlaylist.table.currentIndex)
0316                            })
0317                        }
0318     }
0319 
0320     Player
0321     {
0322         id: player
0323         volume: 100
0324         onFinished:
0325         {
0326             if (!mainlistEmpty)
0327             {
0328                 if (currentTrack && currentTrack.url)
0329                 {
0330                     mainPlaylist.listModel.list.countUp(currentTrackIndex)
0331                 }
0332 
0333                 switch(root.sleepOption)
0334                 {
0335                 case "eot":
0336                 {
0337                     Player.stop()
0338                     if(closeAfterSleep)
0339                         root.close()
0340                     break;
0341                 }
0342 
0343                 case "eop":
0344                 {
0345                     if(currentTrackIndex === mainPlaylist.listView.count-1)
0346                     {
0347                         Player.stop();
0348                         if(closeAfterSleep)
0349                             root.close()
0350                     }else
0351                     {
0352                         Player.nextTrack();
0353                     }
0354                     break;
0355                 }
0356                 case "none":
0357                 default:
0358                     Player.nextTrack();
0359                 }
0360             }
0361         }
0362     }
0363 
0364     Loader
0365     {
0366         id: _dialogLoader
0367     }
0368 
0369     Component
0370     {
0371         id: _fileDialogComponent
0372         FB.FileDialog {}
0373     }
0374 
0375     Component
0376     {
0377         id: _shortcutsDialogComponent
0378         ShortcutsDialog {}
0379     }
0380 
0381     Component
0382     {
0383         id: _settingsDialogComponent
0384         SettingsDialog {}
0385     }
0386 
0387     Component
0388     {
0389         id: _removeDialogComponent
0390 
0391         Maui.FileListingDialog
0392         {
0393             id: _removeDialog
0394 
0395             urls: selectionBar.uris
0396 
0397             title: i18np("Remove track", "Remove %1 tracks", urls.length)
0398             message: i18n("Are you sure you want to remove these files? This action can not be undone.")
0399 
0400             onAccepted: close()
0401 
0402             onRejected:
0403             {
0404                 FB.FM.removeFiles(_removeDialog.urls)
0405                 close()
0406             }
0407         }
0408     }
0409 
0410     Component
0411     {
0412         id: _playlistDialogComponent
0413 
0414         FB.TagsDialog
0415         {
0416             onTagsReady: (tags) => composerList.updateToUrls(tags)
0417             composerList.strict: false
0418         }
0419     }
0420 
0421     Component
0422     {
0423         id: _sleepTimerDialogComponent
0424 
0425         SleepTimerDialog
0426         {
0427 
0428         }
0429     }
0430 
0431     Maui.SideBarView
0432     {
0433         id: _sideBarView
0434         sideBar.preferredWidth: Maui.Style.units.gridUnit * 16
0435         anchors.fill: parent
0436 
0437         sideBarContent: Item
0438         {
0439             id: _drawer
0440             anchors.fill: parent
0441             Loader
0442             {
0443                 id: _mainPlaylistLoader
0444                 anchors.fill: parent
0445 
0446                 asynchronous: false
0447                 sourceComponent: MainPlaylist {}
0448             }
0449         }
0450 
0451         Maui.Page
0452         {
0453             id: _mainPage
0454             anchors.fill: parent
0455             headBar.visible: false
0456 
0457             footer: Loader
0458             {
0459                 id: _playbackBarLoader
0460                 asynchronous: true
0461                 width: parent.width
0462                 active: visible
0463                 sourceComponent: PlaybackBar {}
0464             }
0465 
0466             StackView
0467             {
0468                 id: _stackView
0469                 focus: true
0470                 anchors.fill: parent
0471                 initialItem: _focusViewComponent
0472 
0473                 Component.onCompleted:
0474                 {
0475                     if(!settings.focusViewDefault)
0476                     {
0477                         toggleFocusView()
0478                     }
0479                 }
0480 
0481                 pushExit: Transition
0482                 {
0483                     ParallelAnimation
0484                     {
0485                         PropertyAnimation
0486                         {
0487                             property: "y"
0488                             from: 0
0489                             to:  _stackView.height
0490                             duration: 200
0491                             easing.type: Easing.InOutCubic
0492                         }
0493 
0494                         NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 300; easing.type: Easing.InOutCubic }
0495                     }
0496                 }
0497 
0498                 pushEnter: null
0499 
0500                 popExit: null
0501 
0502                 popEnter: Transition
0503                 {
0504                     ParallelAnimation
0505                     {
0506                         PropertyAnimation
0507                         {
0508                             property: "y"
0509                             from: _stackView.height
0510                             to: 0
0511                             duration: 200
0512                             easing.type: Easing.InOutCubic
0513                         }
0514 
0515                         NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 200; easing.type: Easing.OutCubic }
0516                     }
0517                 } //OK
0518 
0519                 Maui.AppViews
0520                 {
0521                     id: swipeView
0522                     maxViews: 3
0523 
0524                     floatingFooter: true
0525                     flickable: swipeView.currentItem.flickable || swipeView.currentItem.item.flickable
0526                     altHeader: Maui.Handy.isMobile
0527                     showCSDControls: true
0528 
0529                     headBar.leftContent: Loader
0530                     {
0531                         asynchronous: true
0532 
0533                         sourceComponent: Maui.ToolButtonMenu
0534                         {
0535                             icon.name: "application-menu"
0536 
0537                             MA.AccountsMenuItem{}
0538 
0539                             MenuItem
0540                             {
0541                                 text: i18n("Sleep Timer")
0542                                 icon.name: "player-time"
0543                                 onTriggered: openSleepTimerDialog()
0544                             }
0545 
0546                             MenuSeparator{}
0547 
0548                             MenuItem
0549                             {
0550                                 text: i18n("Shortcuts")
0551                                 icon.name: "configure-shortcuts"
0552                                 onTriggered: openShortcutsDialog()
0553                             }
0554 
0555                             MenuItem
0556                             {
0557                                 text: i18n("Settings")
0558                                 icon.name: "settings-configure"
0559                                 onTriggered: openSettingsDialog()
0560                             }
0561 
0562                             MenuItem
0563                             {
0564                                 text: i18n("About")
0565                                 icon.name: "documentinfo"
0566                                 onTriggered: root.about()
0567                             }
0568                         }
0569                     }
0570 
0571                     footer: SelectionBar
0572                     {
0573                         id: _selectionBar
0574                         anchors.horizontalCenter: parent.horizontalCenter
0575                         width: Math.min(parent.width-(Maui.Style.space.medium*2), implicitWidth)
0576 
0577                         maxListHeight: swipeView.height - Maui.Style.space.medium
0578                         display: ToolButton.IconOnly
0579 
0580                         onExitClicked:
0581                         {
0582                             root.selectionMode = false
0583                             clear()
0584                         }
0585 
0586                         onVisibleChanged:
0587                         {
0588                             if(!visible)
0589                             {
0590                                 root.selectionMode = false
0591                             }
0592                         }
0593                     }
0594 
0595                     Maui.AppViewLoader
0596                     {
0597                         Maui.AppView.title: i18n("Songs")
0598                         Maui.AppView.iconName: "view-media-track"
0599 
0600                         TracksView
0601                         {
0602                             Component.onCompleted:
0603                             {
0604                                 if(settings.autoScan)
0605                                 {
0606                                     Vvave.rescan()
0607                                 }
0608                             }
0609                         }
0610                     }
0611 
0612                     Maui.AppViewLoader
0613                     {
0614                         id: _albumsViewLoader
0615 
0616                         Maui.AppView.title: i18n("Albums")
0617                         Maui.AppView.iconName: "view-media-album-cover"
0618 
0619                         property var pendingAlbum : ({})
0620 
0621                         AlbumsView
0622                         {
0623                             holder.title : i18n("No Albums!")
0624                             holder.body: i18n("Add new music sources")
0625                             list.query: Albums.ALBUMS
0626 
0627                             Component.onCompleted:
0628                             {
0629                                 if(Object.keys(_albumsViewLoader.pendingAlbum).length)
0630                                 {
0631                                     populateTable(_albumsViewLoader.pendingAlbum.album, _albumsViewLoader.pendingAlbum.artist)
0632                                 }
0633                             }
0634                         }
0635                     }
0636 
0637                     Maui.AppViewLoader
0638                     {
0639                         id: _artistViewLoader
0640                         Maui.AppView.title: i18n("Artists")
0641                         Maui.AppView.iconName: "view-media-artist"
0642 
0643                         property string pendingArtist
0644 
0645                         AlbumsView
0646                         {
0647                             holder.title : i18n("No Artists!")
0648                             holder.body: i18n("Add new music sources")
0649                             list.query : Albums.ARTISTS
0650 
0651                             Component.onCompleted:
0652                             {
0653                                 if(_artistViewLoader.pendingArtist.length)
0654                                 {
0655                                     populateTable(undefined, _artistViewLoader.pendingArtist)
0656 
0657                                 }
0658                             }
0659                         }
0660                     }
0661 
0662                     Maui.AppViewLoader
0663                     {
0664                         Maui.AppView.title: i18n("Tags")
0665                         Maui.AppView.iconName: "tag"
0666 
0667                         PlaylistsView {}
0668                     }
0669 
0670                     Maui.AppViewLoader
0671                     {
0672                         Maui.AppView.title: i18n("Folders")
0673                         Maui.AppView.iconName: "folder"
0674 
0675                         FoldersView {}
0676                     }
0677 
0678                     Maui.AppViewLoader
0679                     {
0680                         Maui.AppView.title: i18n("Cloud")
0681                         Maui.AppView.iconName: "folder-cloud"
0682 
0683                         CloudView {}
0684                     }
0685 
0686 
0687                     data: Loader
0688                     {
0689                         width: parent.width
0690                         anchors.bottom: parent.bottom
0691                         active: Vvave.scanning
0692                         visible: active
0693                         sourceComponent: Maui.ProgressIndicator {}
0694                     }
0695 
0696                     function getFilterField() : Item
0697                     {
0698                         return currentItem.item.getFilterField()
0699                     }
0700 
0701                     /**
0702                           * Check if the "go back" function exists in the current view and return the reference to the function
0703                           */
0704                     function getGoBackFunc() : Function
0705                     {
0706                         return 'getGoBackFunc' in currentItem.item ?
0707                                     currentItem.item.getGoBackFunc() :
0708                                     null
0709                     }
0710                 }
0711 
0712                 Component
0713                 {
0714                     id: _focusViewComponent
0715 
0716                     FocusView
0717                     {
0718                         objectName: "FocusView"
0719                     }
0720                 }
0721 
0722                 Loader
0723                 {
0724                     id: _miniModeComponent
0725                     visible: active
0726                     active: StackView.status === StackView.Active
0727                     MiniMode
0728                     {
0729                         anchors.fill: parent
0730                     }
0731                 }
0732             }
0733         }
0734     }
0735 
0736     Component.onCompleted:
0737     {
0738         Vvave.fetchArtwork = settings.fetchArtwork
0739         setAndroidStatusBarColor()
0740     }
0741 
0742     function setAndroidStatusBarColor()
0743     {
0744         if(Maui.Handy.isAndroid)
0745         {
0746             const isDark = Maui.ColorUtils.brightnessForColor(Maui.Theme.backgroundColor) === Maui.ColorUtils.Dark
0747             Maui.Android.statusbarColor(Maui.Theme.backgroundColor, !isDark)
0748             Maui.Android.navBarColor(Maui.Theme.backgroundColor, !isDark)
0749         }
0750     }
0751 
0752     function toggleFocusView()
0753     {
0754         if(focusView)
0755         {
0756             _stackView.push(swipeView)
0757 
0758         }else
0759         {
0760             _stackView.pop()
0761         }
0762 
0763         _stackView.currentItem.forceActiveFocus()
0764     }
0765 
0766     property int oldH : root. height
0767     property int oldW : root.width
0768     property point oldP : Qt.point(root.x, root.y)
0769 
0770     function toggleMiniMode()
0771     {
0772         if(Maui.Handy.isMobile)
0773         {
0774             return
0775         }
0776 
0777         if(miniMode)
0778         {
0779             _stackView.pop(StackView.Immediate)
0780 
0781             root.width = oldW
0782             root.height = oldH
0783 
0784             root.x = oldP.x
0785             root.y = oldP.y
0786         }else
0787         {
0788             root.oldH = root.height
0789             root.oldW = root.width
0790             root.oldP = Qt.point(root.x, root.y)
0791 
0792             _stackView.push(_miniModeComponent, StackView.Immediate)
0793 
0794             root.x = Screen.desktopAvailableWidth - root.preferredMiniModeSize - Maui.Style.space.big
0795             root.y = Screen.desktopAvailableHeight - root.preferredMiniModeSize - Maui.Style.space.big
0796         }
0797     }
0798 
0799     function openShortcutsDialog() : undefined
0800     {
0801         _dialogLoader.sourceComponent = _shortcutsDialogComponent
0802         dialog.open()
0803     }
0804 
0805     function openSettingsDialog()
0806     {
0807         _dialogLoader.sourceComponent = _settingsDialogComponent
0808         dialog.open()
0809     }
0810 
0811     function goToAlbum(artist, album)
0812     {
0813         if(root.focusView)
0814         {
0815             toggleFocusView()
0816         }
0817 
0818         swipeView.currentIndex = viewsIndex.albums
0819         if(_albumsViewLoader.item)
0820         {
0821             _albumsViewLoader.item.populateTable(album, artist)
0822         }else
0823         {
0824             _albumsViewLoader.pendingAlbum = ({'artist': artist, 'album': album})
0825         }
0826     }
0827 
0828     function goToArtist(artist)
0829     {
0830         if(root.focusView)
0831         {
0832             toggleFocusView()
0833         }
0834 
0835         swipeView.currentIndex = viewsIndex.artists
0836         if(_artistViewLoader.item)
0837         {
0838             _artistViewLoader.item.populateTable(undefined, artist)
0839         }else
0840         {
0841             _artistViewLoader.pendingArtist = artist
0842         }
0843     }
0844 
0845     function openFiles(urls)
0846     {
0847         console.log("APPEND URLS", urls)
0848         Player.appendUrlsAt(urls, 0)
0849         Player.playAt(0)
0850     }
0851 
0852     function isUrlOpen(url : string) : bool
0853     {
0854         return false;
0855     }
0856 
0857     function getFilterField() : Item
0858     {
0859         return ('getFilterField' in _stackView.currentItem) ?
0860                     _stackView.currentItem.getFilterField() :
0861                     null
0862     }
0863 
0864     function getGoBackFunc() : Function
0865     {
0866         let filterField = getFilterField()
0867         if (filterField && filterField.activeFocus) {
0868             return () => { filterField.focus = false }
0869         } else {
0870             return ('getGoBackFunc' in _stackView.currentItem) ?
0871                         _stackView.currentItem.getGoBackFunc() :
0872                         null
0873         }
0874     }
0875 
0876     function openSleepTimerDialog()
0877     {
0878         _dialogLoader.sourceComponent = _sleepTimerDialogComponent
0879         dialog.open()
0880     }
0881 
0882     function setSleepTimer(option)
0883     {
0884         console.log("Setting sleep timer to ", option)
0885         const timerFunc = (min) =>
0886                         {
0887             _timerLoader.active = true
0888             _timerLoader.item.interval = min * 60 * 1000
0889         };
0890 
0891         switch(option)
0892         {
0893         case "15m" : ; timerFunc(15); break;
0894         case "30m" : timerFunc(30); break;
0895         case "60m" : timerFunc(60); break;
0896         case "eot" : root.sleepOption = "eot"; break;
0897         case "eop" : root.sleepOption = "eop"; break;
0898         case "none" :
0899         default: root.sleepOption = "none"; _timerLoader.active=false; break;
0900         }
0901     }
0902 }