Source

VPlayer / collectionmodel.py

# -*- coding: utf-8 -*-

import pickle

from PyQt4 import QtCore, QtGui, QtSql

from logger import log
from track import Track
from vplayer import queries

class Lazy(object):
    "Decorator for storing counted value"
    def __init__(self, func):
        self._func = func
        self.__name__ = func.__name__
        self.__doc__ = func.__doc__

    def __get__(self, obj, klass=None):
        if obj is None: return None
        result = obj.__dict__[self.__name__] = self._func(obj)
        return result

class ColRoot:

    def __init__(self, filter_word = None):
        self.data = None
        self.index = QtCore.QModelIndex()
        self.filter_word = '' if not filter_word else filter_word

    @Lazy
    def children(self):
        res = []
        for item in queries.get_artist_list(search = self.filter_word):
            artist = ColArtist(item['name'], self, self.filter_word, item['id'])
            res.append(artist)
        return res

    @Lazy
    def childcount(self):
        return len(self.children)

class ColArtist:

    def __init__(self, data, parent, filter_word, idno):
        self.data = data
        self.name = data
        self.index = QtCore.QModelIndex()
        self.parent = parent
        self.id = idno
        if not filter_word or filter_word.upper() in self.name.upper():
            self.filter_word = ''
        else:
            self.filter_word = filter_word

    @Lazy
    def children(self):
        res = []
        for item in queries.get_album_list(self.id, search = self.filter_word):
            album = ColAlbum( ( item['title'], item['year'] ), self,
                              self.filter_word, item['id'])
            res.append(album)
        return res

    @Lazy
    def childcount(self):
        return len(self.children)

class ColAlbum:

    def __init__(self, data, artist, filter_word, idno):
        self.data = data
        self.artist = artist.data
        self.parent = artist
        self.name = data[0]
        self.year = data[1]
        self.index = QtCore.QModelIndex()
        self.id = idno
        if not filter_word or filter_word.upper() in self.name.upper():
            self.filter_word = ''
        else:
            self.filter_word = filter_word

    @Lazy
    def children(self):
        tracks = []
        for item in queries.get_track_list(self.parent.id, self.id,
                                           search = self.filter_word):
            track = Track( artist = self.artist, title = item['title'],
                           album = self.name, url = item['url'],
                           local = True, trackno = item['trackno'],
                           year = self.year, length = item['length'] )
            tracks.append(ColTrack(track, self, item['id']))
        return tracks

    @Lazy
    def childcount(self):
        return len(self.children)

class ColTrack:

    def __init__(self, data, album, idno):
        self.data = data
        self.artist = album.artist
        self.album = album.name
        self.parent = album
        self.index = QtCore.QModelIndex()
        self.id = idno

    @Lazy
    def children(self):
        return []

    @Lazy
    def childcount(self):
        return 0


class CollectionModel(QtCore.QAbstractItemModel):
    def __init__(self):
        QtCore.QAbstractItemModel.__init__(self)
        self.root = ColRoot()
        self.filter_word = ""

    def data(self, index, role):
        if role == QtCore.Qt.SizeHintRole:
            if index.parent().isValid() and not index.parent().parent().isValid():
                return QtCore.QVariant(QtCore.QSize(0,38))
            else:
                return QtCore.QVariant(QtCore.QSize(0,20))
        if not index.isValid():
            return QtCore.QVariant()
        if role == QtCore.Qt.DecorationRole:
            if not index.parent().isValid():
                pic = QtGui.QPixmap()
                pic.load(':/icons/images/unk_artist.png')
                icon = QtGui.QIcon(pic.scaled(20,20))
                return QtCore.QVariant(icon)
            if index.parent().isValid() and not index.parent().parent().isValid():
                query = QtSql.QSqlQuery()
                artist = index.parent().internalPointer().name
                album = index.internalPointer().name
                year = index.internalPointer().year
                cover = queries.get_album_cover(artist, album, year)
                if cover:
                    pic = QtGui.QPixmap()
                    pic.load(cover)
                else:
                    pic = QtGui.QPixmap()
                    pic.load(':/icons/images/unk_album.png')
                return QtCore.QVariant(pic.scaled(35,35,
                  QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation))
        if role != QtCore.Qt.DisplayRole :
            return QtCore.QVariant()
        data = index.internalPointer().data
        if isinstance(data, Track):
            return QtCore.QVariant(data.title)
        elif isinstance(data, tuple):
            return QtCore.QVariant(unicode(data[1]) + ' - ' + data[0])
        else:
            return QtCore.QVariant(data)

    def headerData(self, section, orientation, role):
        if role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()
        if orientation == QtCore.Qt.Horizontal:
            return QtCore.QVariant(self.tr('Collection'))
        if orientation == QtCore.Qt.Vertical:
            return QtCore.QVariant()

    def update(self):
        self.root = ColRoot(self.filter_word)
        self.reset()

    def index(self, row, column, parent = QtCore.QModelIndex()):
        if not parent.isValid():
            child = self.root.children[row]
            index = self.createIndex(row, column, child)
            child.index = index
            return index
        else:
            p = parent.internalPointer()
            child = p.children[row]
            index = self.createIndex(row, column, child)
            child.index = index
            return index

    def parent(self, index):
        if not index.isValid():
            return QtCore.QModelIndex()
        else:
            child = index.internalPointer()
            parent = child.parent
            if parent:
                return parent.index
        return QtCore.QModelIndex()

    def rowCount(self, parent = QtCore.QModelIndex()):
        if not parent.isValid():
            return self.root.childcount
        else:
            par = parent.internalPointer()
            if par:
                if par.children:
                    return par.childcount
                else: return 0
            else: return 0

    def columnCount(self, parent = QtCore.QModelIndex()):
        return 1

    def supportedDropActions(self):
        return QtCore.Qt.MoveAction

    def supportedDragActions(self):
        return QtCore.Qt.MoveAction

    def flags(self, index):
        defaultFlags = QtCore.QAbstractItemModel.flags(self, index)
        if index.isValid():
            return QtCore.Qt.ItemIsDragEnabled | defaultFlags#| QtCore.Qt.ItemIsDropEnabled
        else:
            return defaultFlags#QtCore.Qt.ItemIsDropEnabled | defaultFlags

    def mimeTypes(self):
        types = QtCore.QStringList()
        types << "application/x-tracks"
        return types

    def get_tracks_by_index(self, index):
        if isinstance(index.internalPointer().data, Track):
            return [index.internalPointer().data]
        else:
            res = []
            for row in range(self.rowCount(index)):
                res += self.get_tracks_by_index(self.index(row, 0, index))
            return res

    def mimeData(self, indexes):

#        def resolve_tracks(index):
#            if isinstance(index.internalPointer().data, Track):
#                return [index.internalPointer().data]
#            else:
#                res = []
#                for row in range(self.rowCount(index)):
#                    res += resolve_tracks(self.index(row, 0, index))
#                return res

        mimeData = QtCore.QMimeData()

        data = []
        rows = []
        for index in indexes:
            if (index.row(), index.parent()) not in rows:
                rows.append(index)

        tracks = []
        for row in rows:
            tracks_res = self.get_tracks_by_index(row)
            for track in tracks_res:
                if track not in tracks:
                    tracks.append(track)

        def compare(t1, t2):
            acmp = cmp(t1.artist, t2.artist)
            if acmp == 0:
                alcmp = cmp(t1.album, t2.album)
                if alcmp == 0:
                    ncmp = cmp(t1.trackno, t2.trackno)
                    if ncmp == 0:
                        return cmp(t1.title, t2.title)
                    else: return ncmp
                else: return alcmp
            else: return acmp
        tracks = sorted(tracks, cmp=compare)

        data = pickle.dumps(tracks)

        mimeData.setData("application/x-tracks", data)
        return mimeData

    def dropMimeData(self, data, action, row, column, parent):
        return

    def update_filter(self, word):
        self.filter_word = unicode(word)
        self.root = ColRoot(unicode(word))
        self.reset()