3 import com.nokia.meego 1.0
5 import 'config.js' as Config
12 return controller.translate(x)
15 function n_(x, y, z) {
16 return controller.ntranslate(x, y, z)
19 property alias podcastModel: podcastList.model
20 property alias episodeModel: episodeList.model
21 property alias currentEpisode: mediaPlayer.episode
22 property variant currentPodcast: undefined
23 property bool hasPodcasts: podcastList.hasItems
24 property alias currentFilterText: episodeList.currentFilterText
26 property bool playing: mediaPlayer.playing
27 property bool canGoBack: (closeButton.isRequired || mediaPlayer.visible) && !progressIndicator.opacity
28 property bool hasPlayButton: nowPlayingThrobber.shouldAppear && !progressIndicator.opacity
29 property bool hasSearchButton: searchButton.visible && !mediaPlayer.visible && !progressIndicator.opacity
30 property bool hasFilterButton: state == 'episodes' && !mediaPlayer.visible
33 if (nowPlayingThrobber.opened) {
34 nowPlayingThrobber.opened = false
40 function showFilterDialog() {
41 episodeList.showFilterDialog()
44 function clickPlayButton() {
45 nowPlayingThrobber.clicked()
48 function showMultiEpisodesSheet(label, action) {
49 multiEpisodesSheet.acceptButtonText = label;
50 multiEpisodesSheet.action = action;
51 multiEpisodesList.selected = [];
52 multiEpisodesSheet.open();
55 function clickSearchButton() {
56 searchButton.clicked()
60 console.log(event.key)
61 if (event.key == Qt.Key_Escape) {
64 if (event.key == Qt.Key_F && event.modifiers & Qt.ControlModifier) {
65 searchButton.clicked()
72 property bool useEmptyBackground: !podcastList.hasItems
74 anchors.topMargin: useEmptyBackground?-35:0
75 fillMode: useEmptyBackground?Image.Tile:Image.Stretch
77 if (useEmptyBackground) {
78 '/usr/share/themes/blanco/meegotouch/images/backgrounds/meegotouch-empty-application-background-black-portrait.png'
80 'artwork/background-harmattan.png'
86 function enqueueEpisode(episode) {
87 if (currentEpisode === undefined) {
88 togglePlayback(episode);
90 mediaPlayer.enqueueEpisode(episode);
94 function removeQueuedEpisodesForPodcast(podcast) {
95 mediaPlayer.removeQueuedEpisodesForPodcast(podcast);
98 function removeQueuedEpisode(episode) {
99 mediaPlayer.removeQueuedEpisode(episode);
102 function togglePlayback(episode) {
103 if (episode !== undefined && episode.qfiletype == 'video') {
104 controller.playVideo(episode)
106 mediaPlayer.togglePlayback(episode)
110 function openShowNotes(episode) {
111 showNotes.episode = episode
112 main.state = 'shownotes'
115 function openContextMenu(items) {
116 hrmtnContextMenu.items = items
117 hrmtnContextMenu.open()
120 function startProgress(text) {
121 progressIndicator.text = text
122 progressIndicator.opacity = 1
125 function endProgress() {
126 progressIndicator.opacity = 0
138 anchors.leftMargin: 100
146 script: episodeList.resetSelection()
158 anchors.leftMargin: -100
163 anchors.leftMargin: main.width
169 target: listContainer
175 anchors.leftMargin: 0
183 anchors.topMargin: titleBar.height
192 controller.podcastSelected(podcast)
193 main.currentPodcast = podcast
195 onPodcastContextMenu: controller.podcastContextMenu(podcast)
196 onSubscribe: contextMenu.showSubscribe()
198 Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
199 Behavior on anchors.leftMargin { NumberAnimation { duration: Config.slowTransition } }
209 onEpisodeContextMenu: controller.episodeContextMenu(episode)
211 Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
212 Behavior on anchors.leftMargin { NumberAnimation { duration: Config.slowTransition } }
215 Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
216 Behavior on scale { NumberAnimation { duration: Config.fadeTransition } }
225 bottom: parent.bottom
229 Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
230 Behavior on anchors.leftMargin { NumberAnimation { duration: Config.slowTransition } }
234 id: overlayInteractionBlockWall
236 anchors.topMargin: (nowPlayingThrobber.opened || messageDialog.opacity > 0 || inputDialog.opacity > 0 || progressIndicator.opacity > 0)?0:titleBar.height
237 z: (contextMenu.state != 'opened')?2:0
239 opacity: (nowPlayingThrobber.opened || contextMenu.state == 'opened' || messageDialog.opacity || inputDialog.opacity || progressIndicator.opacity)?1:0
240 Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
245 if (contextMenu.state == 'opened') {
247 } else if (progressIndicator.opacity) {
249 } else if (inputDialog.opacity) {
251 } else if (messageDialog.opacity) {
252 messageDialog.opacity = 0
254 nowPlayingThrobber.opened = false
267 source: 'artwork/mask.png'
274 z: (contextMenu.state == 'opened')?2:0
275 tab: 'artwork/back-tab.png'
276 icon: 'artwork/back.png'
278 anchors.bottom: parent.bottom
279 anchors.left: parent.left
280 onClicked: closeButton.clicked()
281 opened: !(!Config.hasCloseButton && closeButton.isRequired)
287 property bool shouldAppear: ((contextMenu.state != 'opened') && (mediaPlayer.episode !== undefined))
289 id: nowPlayingThrobber
291 anchors.bottom: parent.bottom
292 anchors.right: parent.right
293 opacity: shouldAppear
295 caption: (mediaPlayer.episode!=undefined)?mediaPlayer.episode.qtitle:''
298 onClicked: { opened = !opened }
303 visible: nowPlayingThrobber.opened
307 anchors.top: parent.bottom
308 anchors.left: parent.left
309 anchors.right: parent.right
310 anchors.topMargin: nowPlayingThrobber.opened?-(height+(parent.height-height)/2):0
312 Behavior on anchors.topMargin { PropertyAnimation { duration: Config.quickTransition; easing.type: Easing.OutCirc } }
317 property variant items: []
321 model: hrmtnContextMenu.items
324 text: modelData.caption
326 hrmtnContextMenu.close()
327 controller.contextMenuResponse(index)
342 bottom: parent.bottom
345 onClose: contextMenu.state = 'closed'
346 onResponse: controller.contextMenuResponse(index)
350 Behavior on opacity { NumberAnimation { duration: Config.fadeTransition } }
361 anchors.right: main.right
372 anchors.right: main.left
375 script: controller.contextMenuClosed()
380 transitions: Transition {
381 AnchorAnimation { duration: Config.slowTransition }
387 visible: podcastList.hasItems
388 height: visible?taskSwitcher.height*.8:0
389 anchors.left: parent.left
390 anchors.right: parent.right
391 anchors.top: parent.top
393 //anchors.topMargin: mediaPlayer.fullscreen?-height:0
394 //opacity: mediaPlayer.fullscreen?0:1
396 Behavior on opacity { PropertyAnimation { } }
397 Behavior on anchors.topMargin { PropertyAnimation { } }
405 // clicks should not fall through!
412 visible: contextMenu.state != 'opened' && Config.hasTaskSwitcher
413 anchors.left: parent.left
414 anchors.top: parent.top
415 width: Config.switcherWidth
416 height: Config.headerHeight
420 onClicked: controller.switcher()
425 verticalCenter: parent.verticalCenter
427 leftMargin: (parent.width * .8 - width) / 2
429 source: 'artwork/switch.png'
435 anchors.verticalCenter: parent.verticalCenter
436 anchors.left: taskSwitcher.visible?taskSwitcher.right:taskSwitcher.left
437 anchors.leftMargin: (contextMenu.state == 'opened')?(Config.largeSpacing):(Config.hasTaskSwitcher?0:Config.largeSpacing)
438 anchors.right: searchButton.visible?searchButton.left:searchButton.right
439 wrapMode: Text.NoWrap
441 text: (contextMenu.state == 'opened')?(contextMenu.subscribeMode?_('Add a new podcast'):_('Context menu')):((main.state == 'episodes' || main.state == 'shownotes')?(controller.episodeListTitle + ' (' + episodeList.count + ')'):"gPodder")
443 font.pixelSize: parent.height * .5
449 property: 'windowTitle'
450 value: titleBarText.text
455 anchors.right: closeButton.visible?closeButton.left:closeButton.right
457 source: 'artwork/subscriptions.png'
459 onClicked: contextMenu.showSubscribe()
461 visible: (contextMenu.state == 'closed' && main.state == 'podcasts')
467 anchors.right: parent.right
468 property bool isRequired: main.state != 'podcasts' || contextMenu.state != 'closed'
469 visible: extraCloseButton.opened && (Config.hasCloseButton || isRequired)
471 source: (main.state == 'podcasts' && contextMenu.state == 'closed')?'artwork/close.png':'artwork/back.png'
475 if (contextMenu.state == 'opened') {
476 contextMenu.state = 'closed'
477 } else if (main.state == 'podcasts') {
480 } else if (main.state == 'episodes') {
481 main.state = 'podcasts'
482 main.currentPodcast = undefined
483 } else if (main.state == 'shownotes') {
484 main.state = 'episodes'
490 function showMessage(message) {
491 messageDialogText.text = message
492 messageDialog.opacity = 1
501 Behavior on opacity { PropertyAnimation { } }
504 id: messageDialogText
505 anchors.centerIn: parent
512 function showInputDialog(message, value, accept, reject, textInput) {
513 inputDialogText.text = message
514 inputDialogField.text = value
515 inputDialogAccept.text = accept
516 inputDialogReject.text = reject
517 inputDialogField.visible = textInput
529 acceptButtonText: inputDialogAccept.text
530 rejectButtonText: inputDialogReject.text
532 message: inputDialogText.text
534 onAccepted: inputDialog.accept()
535 onRejected: inputDialog.close()
539 id: multiEpisodesSheet
540 property string action: 'delete'
541 acceptButtonText: _('Delete')
543 rejectButtonText: _('Cancel')
545 visualParent: rootWindow
548 controller.multiEpisodeAction(multiEpisodesList.selected, action);
554 id: multiEpisodesList
555 property variant selected: []
558 anchors.bottomMargin: Config.largeSpacing
559 model: episodeList.model
561 delegate: EpisodeItem {
562 inSelection: multiEpisodesList.selected.indexOf(index) !== -1
564 var newSelection = [];
567 for (var i=0; i<multiEpisodesList.selected.length; i++) {
568 var value = multiEpisodesList.selected[i];
569 if (value === index) {
572 newSelection.push(value);
577 if (multiEpisodesSheet.action === 'delete' && item.qarchive) {
578 // cannot delete archived episodes
580 newSelection.push(index);
584 multiEpisodesList.selected = newSelection;
589 ScrollDecorator { flickableItem: multiEpisodesList }
596 acceptButtonText: inputDialogAccept.text
597 rejectButtonText: inputDialogReject.text
604 onClicked: console.log('caught')
609 anchors.margins: Config.smallSpacing
610 spacing: Config.smallSpacing
619 anchors.margins: Config.smallSpacing
635 actionName: inputDialogAccept.text
640 onAccepted: inputDialog.accept()
641 onRejected: inputDialog.close()
652 controller.inputDialogResponse(true, inputDialogField.text,
653 inputDialogField.visible)
659 controller.inputDialogResponse(false, inputDialogField.text,
660 inputDialogField.visible)
664 id: inputDialogReject
665 width: parent.width / 2
666 onClicked: inputDialog.close()
670 id: inputDialogAccept
671 width: parent.width / 2
672 onClicked: inputDialog.accept()
677 id: progressIndicator
678 property string text: '...'
679 anchors.centerIn: parent
681 spacing: Config.largeSpacing * 2
684 Behavior on opacity { NumberAnimation { duration: Config.slowTransition } }
688 anchors.horizontalCenter: parent.horizontalCenter
692 anchors.horizontalCenter: parent.horizontalCenter
693 running: parent.opacity > 0