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