Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

QML UI: Basic episode list filters (bug 1527)

This still needs some work (i.e. the list might
need refreshing after a download has finished
or an episode has been played/marked as old).
  • Loading branch information...
commit 83a541837f445e03547b0eeb88f6810a38ba6273 1 parent 17bd235
Thomas Perl authored January 14, 2012
48  data/ui/qml/EpisodeList.qml
@@ -7,20 +7,41 @@ import 'config.js' as Config
7 7
 
8 8
 Item {
9 9
     id: episodeList
  10
+    property string currentFilterText
10 11
 
11 12
     property alias model: listView.model
12 13
     property alias moving: listView.moving
  14
+    property alias count: listView.count
13 15
 
14 16
     signal episodeContextMenu(variant episode)
15 17
 
  18
+    function showFilterDialog() {
  19
+        filterDialog.open();
  20
+    }
  21
+
16 22
     function resetSelection() {
17 23
         listView.openedIndex = -1
18 24
     }
19 25
 
  26
+    Text {
  27
+        anchors.centerIn: parent
  28
+        color: 'white'
  29
+        font.pixelSize: 30
  30
+        horizontalAlignment: Text.AlignHCenter
  31
+        text: '<big>' + _('No episodes') + '</big>' + '<br><small>' + _('Touch to change filter') + '</small>'
  32
+        visible: !listView.visible
  33
+
  34
+        MouseArea {
  35
+            anchors.fill: parent
  36
+            onClicked: episodeList.showFilterDialog()
  37
+        }
  38
+    }
  39
+
20 40
     ListView {
21 41
         id: listView
22 42
         anchors.fill: parent
23 43
         property int openedIndex: -1
  44
+        visible: count > 0
24 45
 
25 46
         delegate: Item {
26 47
             id: listItem
@@ -91,5 +112,32 @@ Item {
91 112
     ScrollDecorator {
92 113
         flickableItem: listView
93 114
     }
  115
+
  116
+    SelectionDialog {
  117
+        id: filterDialog
  118
+        titleText: _('Show episodes')
  119
+
  120
+        function resetSelection() {
  121
+            selectedIndex = 0;
  122
+            accepted();
  123
+        }
  124
+
  125
+        onAccepted: {
  126
+            episodeList.currentFilterText = model.get(selectedIndex).name;
  127
+            episodeList.model.setFilter(selectedIndex);
  128
+        }
  129
+
  130
+        model: ListModel {}
  131
+
  132
+        Component.onCompleted: {
  133
+            var filters = controller.getEpisodeListFilterNames();
  134
+
  135
+            for (var index in filters) {
  136
+                model.append({name: filters[index]});
  137
+            }
  138
+
  139
+            resetSelection();
  140
+        }
  141
+    }
94 142
 }
95 143
 
8  data/ui/qml/Main.qml
@@ -21,12 +21,14 @@ Image {
21 21
     property alias currentEpisode: mediaPlayer.episode
22 22
     property variant currentPodcast: undefined
23 23
     property bool hasPodcasts: podcastList.hasItems
  24
+    property alias currentFilterText: episodeList.currentFilterText
24 25
 
25 26
     property bool playing: mediaPlayer.playing
26 27
     property bool canGoBack: (closeButton.isRequired || mediaPlayer.visible) && !progressIndicator.opacity && !myGpoSheetVisible
27 28
     property bool hasPlayButton: nowPlayingThrobber.shouldAppear && !progressIndicator.opacity && !myGpoSheetVisible
28 29
     property bool hasSearchButton: searchButton.visible && !mediaPlayer.visible && !progressIndicator.opacity && !myGpoSheetVisible
29 30
     property bool myGpoSheetVisible: false
  31
+    property bool hasFilterButton: state == 'episodes' && !mediaPlayer.visible
30 32
 
31 33
     function openMyGpo() {
32 34
         myGpoEnableSwitch.checked = controller.myGpoEnabled
@@ -45,6 +47,10 @@ Image {
45 47
         }
46 48
     }
47 49
 
  50
+    function showFilterDialog() {
  51
+        episodeList.showFilterDialog()
  52
+    }
  53
+
48 54
     function clickPlayButton() {
49 55
         nowPlayingThrobber.clicked()
50 56
     }
@@ -407,7 +413,7 @@ Image {
407 413
             anchors.right: searchButton.visible?searchButton.left:searchButton.right
408 414
             wrapMode: Text.NoWrap
409 415
             clip: true
410  
-            text: (contextMenu.state == 'opened')?(contextMenu.subscribeMode?_('Add a new podcast'):_('Context menu')):((main.state == 'episodes' || main.state == 'shownotes')?controller.episodeListTitle:"gPodder")
  416
+            text: (contextMenu.state == 'opened')?(contextMenu.subscribeMode?_('Add a new podcast'):_('Context menu')):((main.state == 'episodes' || main.state == 'shownotes')?(controller.episodeListTitle + ' (' + episodeList.count + ')'):"gPodder")
411 417
             color: 'white'
412 418
             font.pixelSize: parent.height * .5
413 419
             font.bold: false
13  data/ui/qml/main_harmattan.qml
@@ -66,6 +66,19 @@ PageStackWindow {
66 66
                 //anchors.right: toolPlay.visible?toolPlay.left:toolPlay.right
67 67
             }
68 68
 
  69
+            ToolButton {
  70
+                id: toolFilter
  71
+                visible: mainObject.hasFilterButton
  72
+                onClicked: mainObject.showFilterDialog()
  73
+                anchors.centerIn: parent
  74
+
  75
+                Label {
  76
+                    color: 'white'
  77
+                    text: mainObject.currentFilterText
  78
+                    anchors.centerIn: parent
  79
+                }
  80
+            }
  81
+
69 82
             ToolIcon {
70 83
                 id: toolPlay
71 84
                 iconId: "icon-m-toolbar-content-audio-white"
2  src/gpodder/model.py
@@ -709,7 +709,7 @@ def is_finished(self):
709 709
         current position is greater than 99 percent of the
710 710
         total time or inside the last 10 seconds of a track.
711 711
         """
712  
-        return self.current_position > 0 and \
  712
+        return self.current_position > 0 and self.total_time > 0 and \
713 713
                 (self.current_position + 10 >= self.total_time or \
714 714
                  self.current_position >= self.total_time*.99)
715 715
 
52  src/gpodder/qmlui/__init__.py
@@ -39,6 +39,7 @@
39 39
 from gpodder import core
40 40
 from gpodder import util
41 41
 from gpodder import my
  42
+from gpodder import query
42 43
 
43 44
 from gpodder.model import Model
44 45
 
@@ -49,6 +50,19 @@
49 50
 import logging
50 51
 logger = logging.getLogger("qmlui")
51 52
 
  53
+
  54
+EPISODE_LIST_FILTERS = [
  55
+    # (UI label, EQL expression)
  56
+    (_('All'), None),
  57
+    (_('Hide deleted'), 'not deleted'),
  58
+    (_('New'), 'new or downloading'),
  59
+    (_('Downloaded'), 'downloaded or downloading'),
  60
+    (_('Deleted'), 'deleted'),
  61
+    (_('Finished'), 'finished'),
  62
+    (_('Archived'), 'downloaded and archive'),
  63
+    (_('Videos'), 'video'),
  64
+]
  65
+
52 66
 class Controller(QObject):
53 67
     def __init__(self, root):
54 68
         QObject.__init__(self)
@@ -83,6 +97,10 @@ def getEpisodeListTitle(self):
83 97
     episodeListTitle = Property(unicode, getEpisodeListTitle, \
84 98
             setEpisodeListTitle, notify=episodeListTitleChanged)
85 99
 
  100
+    @Slot(result='QStringList')
  101
+    def getEpisodeListFilterNames(self):
  102
+        return [caption for caption, eql in EPISODE_LIST_FILTERS]
  103
+
86 104
     @Slot(str, result=str)
87 105
     def translate(self, x):
88 106
         return _(x)
@@ -528,7 +546,7 @@ def get_object(self, index):
528 546
         return self._objects[index.row()]
529 547
 
530 548
     def rowCount(self, parent=QModelIndex()):
531  
-        return len(self._objects)
  549
+        return len(self.get_objects())
532 550
 
533 551
     def data(self, index, role):
534 552
         if index.isValid():
@@ -553,6 +571,36 @@ def sort(self):
553 571
         self._objects = sorted(self._objects, key=model.QPodcast.sort_key)
554 572
         self.reset()
555 573
 
  574
+class gPodderEpisodeListModel(gPodderListModel):
  575
+    def __init__(self):
  576
+        gPodderListModel.__init__(self)
  577
+        self._filter = 0
  578
+        self._filtered = []
  579
+
  580
+    def sort(self):
  581
+        caption, eql = EPISODE_LIST_FILTERS[self._filter]
  582
+
  583
+        if eql is None:
  584
+            self._filtered = self._objects
  585
+        else:
  586
+            eql = query.EQL(eql)
  587
+            match = lambda episode: eql.match(episode._episode)
  588
+            self._filtered = filter(match, self._objects)
  589
+
  590
+        self.reset()
  591
+
  592
+    def get_objects(self):
  593
+        return self._filtered
  594
+
  595
+    def get_object(self, index):
  596
+        return self._filtered[index.row()]
  597
+
  598
+    @Slot(int)
  599
+    def setFilter(self, filter_index):
  600
+        self._filter = filter_index
  601
+        self.sort()
  602
+
  603
+
556 604
 def QML(filename):
557 605
     for folder in gpodder.ui_folders:
558 606
         filename = os.path.join(folder, filename)
@@ -605,7 +653,7 @@ def __init__(self, args, gpodder_core):
605 653
         self.controller = Controller(self)
606 654
         self.media_buttons_handler = helper.MediaButtonsHandler()
607 655
         self.podcast_model = gPodderPodcastListModel()
608  
-        self.episode_model = gPodderListModel()
  656
+        self.episode_model = gPodderEpisodeListModel()
609 657
         self.last_episode = None
610 658
 
611 659
         # A dictionary of episodes that are currently active
2  src/gpodder/query.py
@@ -56,6 +56,8 @@ def __getitem__(self, k):
56 56
             return episode.state == gpodder.STATE_DELETED
57 57
         elif k == 'played':
58 58
             return not episode.is_new
  59
+        elif k == 'downloading':
  60
+            return episode.downloading
59 61
         elif k == 'archive':
60 62
             return episode.archive
61 63
         elif k in ('finished', 'fin'):

0 notes on commit 83a5418

Please sign in to comment.
Something went wrong with that request. Please try again.