artistalbumswidget.py 7.54 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
# Copyright (c) 2016 The GNOME Music Developers
#
# GNOME Music is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# GNOME Music is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with GNOME Music; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# The GNOME Music authors hereby grant permission for non-GPL compatible
# GStreamer plugins to be used and distributed together with GStreamer
# and GNOME Music.  This permission is above and beyond the permissions
# granted by the GPL license by which GNOME Music is covered.  If you
# modify this code, you may extend this exception to your version of the
# code, but you are not obligated to do so.  If you do not wish to do so,
# delete this exception statement from your version.

import logging

27
from gi.repository import GObject, Gtk
28 29

from gnomemusic import log
30
from gnomemusic.player import PlayerPlaylist
31
from gnomemusic.widgets.artistalbumwidget import ArtistAlbumWidget
32
from gnomemusic.widgets.songwidget import SongWidget
33 34 35 36

logger = logging.getLogger(__name__)


37
@Gtk.Template(resource_path='/org/gnome/Music/ui/ArtistAlbumsWidget.ui')
38
class ArtistAlbumsWidget(Gtk.Box):
39 40 41 42 43 44
    """Widget containing all albums by an artist

    A vertical list of ArtistAlbumWidget, containing all the albums
    by one artist. Contains the model for all the song widgets of
    the album(s).
    """
45

46 47
    __gtype_name__ = 'ArtistAlbumsWidget'

48
    _artist_label = Gtk.Template.Child()
49

50 51
    selection_mode = GObject.Property(type=bool, default=False)

52 53 54 55
    def __repr__(self):
        return '<ArtistAlbumsWidget>'

    @log
56 57 58
    def __init__(
            self, artist, albums, player, header_bar, selection_toolbar,
            window, selection_mode_allowed=False):
59
        super().__init__(orientation=Gtk.Orientation.VERTICAL)
60
        self._player = player
61
        self._artist = artist
62 63 64 65 66
        self._window = window
        self._selection_mode_allowed = selection_mode_allowed
        self._selection_toolbar = selection_toolbar
        self._header_bar = header_bar

67
        self._artist_label.props.label = self._artist
68 69

        self._widgets = []
70

71 72
        self._create_model()

73
        self._model.connect('row-changed', self._model_row_changed)
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

        hbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        self._album_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,
                                  spacing=48)
        hbox.pack_start(self._album_box, False, False, 16)

        self._scrolled_window = Gtk.ScrolledWindow()
        self._scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                         Gtk.PolicyType.AUTOMATIC)
        self._scrolled_window.add(hbox)
        self.pack_start(self._scrolled_window, True, True, 0)

        self._cover_size_group = Gtk.SizeGroup.new(
            Gtk.SizeGroupMode.HORIZONTAL)
        self._songs_grid_size_group = Gtk.SizeGroup.new(
            Gtk.SizeGroupMode.HORIZONTAL)

91 92 93 94 95
        self.bind_property(
            'selection-mode', self._header_bar, 'selection-mode',
            GObject.BindingFlags.BIDIRECTIONAL |
            GObject.BindingFlags.SYNC_CREATE)

96
        self._window.notifications_popup.push_loading()
97

98
        self._albums_to_load = len(albums)
99
        for album in albums:
100
            self._add_album(album)
101

102
        self._player.connect('song-changed', self._update_model)
103

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
    @log
    def _create_model(self):
        """Create the ListStore model for this widget."""
        self._model = Gtk.ListStore(
            GObject.TYPE_STRING,  # title
            GObject.TYPE_STRING,
            GObject.TYPE_STRING,
            GObject.TYPE_STRING,
            GObject.TYPE_STRING,    # placeholder
            GObject.TYPE_OBJECT,  # song object
            GObject.TYPE_BOOLEAN,  # item selected
            GObject.TYPE_STRING,
            GObject.TYPE_BOOLEAN,
            GObject.TYPE_INT,  # icon shown
            GObject.TYPE_BOOLEAN,
            GObject.TYPE_INT
        )

122
    @log
123 124 125 126 127
    def _on_album_displayed(self, data=None):
        self._albums_to_load -= 1
        if self._albums_to_load == 0:
            self._window.notifications_popup.pop_loading()
            self.show_all()
128 129

    @log
130
    def _add_album(self, album):
131
        widget = ArtistAlbumWidget(
132 133 134
            album, self._player, self._model, self._header_bar,
            self._selection_mode_allowed, self._songs_grid_size_group,
            self._cover_size_group)
135

136 137 138 139 140
        self.bind_property(
            'selection-mode', widget, 'selection-mode',
            GObject.BindingFlags.BIDIRECTIONAL |
            GObject.BindingFlags.SYNC_CREATE)

141 142
        self._album_box.pack_start(widget, False, False, 0)
        self._widgets.append(widget)
143

144
        widget.connect('songs-loaded', self._on_album_displayed)
145 146

    @log
147 148 149 150 151 152 153 154
    def _update_model(self, player, position):
        """Updates model when the song changes

        :param Player player: The main player object
        :param int position: current song position
        """
        if not player.playing_playlist(
                PlayerPlaylist.Type.ARTIST, self._artist):
155
            self._clean_model()
156 157
            return False

158
        current_song = player.props.current_song
159
        song_passed = False
160
        itr = self._model.get_iter_first()
161 162

        while itr:
163
            song = self._model[itr][5]
164 165
            song_widget = song.song_widget

166
            if (song.get_id() == current_song.get_id()):
167
                song_widget.props.state = SongWidget.State.PLAYING
168 169
                song_passed = True
            elif (song_passed):
170
                # Counter intuitive, but this is due to call order.
171
                song_widget.props.state = SongWidget.State.UNPLAYED
172
            else:
173
                song_widget.props.state = SongWidget.State.PLAYED
174

175
            itr = self._model.iter_next(itr)
176

177 178 179
        return False

    @log
180
    def _clean_model(self):
181
        itr = self._model.get_iter_first()
182

183
        while itr:
184
            song = self._model[itr][5]
185
            song_widget = song.song_widget
186
            song_widget.props.state = SongWidget.State.UNPLAYED
187

188
            itr = self._model.iter_next(itr)
189

190 191 192
        return False

    @log
193
    def _model_row_changed(self, model, path, itr):
194
        if not self.props.selection_mode:
195
            return
196

197 198 199 200
        selected_items = 0
        for row in model:
            if row[6]:
                selected_items += 1
201

202
        self._selection_toolbar.props.items_selected = selected_items
203
        self._header_bar.props.items_selected = selected_items
204

205 206
    @log
    def select_all(self):
207 208
        """Select all items"""
        for widget in self._widgets:
209
            widget.select_all()
210 211 212

    @log
    def select_none(self):
213 214
        """Deselect all items"""
        for widget in self._widgets:
215
            widget.select_none()
216 217 218 219 220

    @GObject.Property(type=str, flags=GObject.ParamFlags.READABLE)
    def artist(self):
        """Artist name"""
        return self._artist
221 222 223 224 225 226 227 228 229 230 231 232

    @log
    def get_selected_songs(self):
        """Return a list of selected songs.

        :returns: selected songs
        :rtype: list
        """
        songs = []
        for widget in self._widgets:
            songs += widget.get_selected_songs()
        return songs