1# Copyright 2013 Christoph Reiter
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7
8from quodlibet import app
9from quodlibet import config
10from quodlibet.qltk.models import ObjectStore, ObjectModelFilter
11from quodlibet.qltk.models import ObjectModelSort
12
13
14class AlbumItem(object):
15
16    cover = None
17    scanned = False
18
19    def __init__(self, album):
20        self.album = album
21
22    @property
23    def COVER_SIZE(self):
24        size = config.getint("browsers", "cover_size")
25        if size <= 0:
26            size = 48
27        return size
28
29    def scan_cover(self, force=False, scale_factor=1,
30            callback=None, cancel=None):
31        if (self.scanned and not force) or not self.album or \
32                not self.album.songs:
33            return
34        self.scanned = True
35
36        def set_cover_cb(pixbuf):
37            self.cover = pixbuf
38            callback()
39
40        s = self.COVER_SIZE * scale_factor
41        app.cover_manager.get_pixbuf_many_async(
42            self.album.songs, s, s, cancel, set_cover_cb)
43
44    def __repr__(self):
45        return repr(self.album)
46
47
48class AlbumModelMixin(object):
49
50    def get_items(self, paths):
51        items = []
52        for path in paths:
53            item = self.get_value(self.get_iter(path))
54            if item.album is None:
55                return [i for i in self.itervalues() if i.album is not None]
56            items.append(item)
57        return items
58
59    def get_albums(self, paths):
60        return [i.album for i in self.get_items(paths)]
61
62    def get_album(self, iter_):
63        return self.get_value(iter_, 0).album
64
65
66class AlbumModel(ObjectStore, AlbumModelMixin):
67
68    def __init__(self, library):
69        super(AlbumModel, self).__init__()
70        self.__library = library
71
72        albums = library.albums
73        self.__sigs = [
74            albums.connect("added", self._add_albums),
75            albums.connect("removed", self._remove_albums),
76            albums.connect("changed", self._change_albums)
77        ]
78
79        self.append(row=[AlbumItem(None)])
80        self.append_many((AlbumItem(a) for a in albums.values()))
81
82    def refresh_all(self):
83        """Trigger redraws for all rows"""
84
85        for iter_, value in self.iterrows():
86            self.row_changed(self.get_path(iter_), iter_)
87
88    def destroy(self):
89        library = self.__library
90        for sig in self.__sigs:
91            library.albums.disconnect(sig)
92        self.__library = None
93        self.clear()
94
95    def _update_all(self):
96        if not self.is_empty():
97            row = self[0]
98            self.row_changed(row.path, row.iter)
99
100    def _add_albums(self, library, added):
101        self.append_many((AlbumItem(a) for a in added))
102        self._update_all()
103
104    def _remove_albums(self, library, removed):
105        removed_albums = removed.copy()
106        iters_remove = []
107        for iter_, item in self.iterrows():
108            album = item.album
109            if album is not None and album in removed_albums:
110                removed_albums.remove(album)
111                iters_remove.append(iter_)
112                if not removed_albums:
113                    break
114
115        for iter_ in iters_remove:
116            self.remove(iter_)
117
118        self._update_all()
119
120    def _change_albums(self, library, changed):
121        """Trigger a row redraw for each album that changed"""
122
123        changed_albums = changed.copy()
124        for iter_, item in self.iterrows():
125            album = item.album
126            if album is not None and album in changed_albums:
127                changed_albums.remove(album)
128                self.row_changed(self.get_path(iter_), iter_)
129                if not changed_albums:
130                    break
131
132
133class AlbumFilterModel(ObjectModelFilter, AlbumModelMixin):
134
135    def contains_all(self, paths):
136        values = (self.get_value(self.get_iter(p), 0).album for p in paths)
137        return None in values
138
139
140class AlbumSortModel(ObjectModelSort, AlbumModelMixin):
141    pass
142