class QPlayListModel (QAbstractTableModel): def __init__ (self, collaggr=None, songs=None, view=None): QAbstractTableModel.__init__ (self, view) # TODO: different delegate for editing tags: one with completion self.view_= view self.playlist= view.playlist self.edited= False if songs is None: self.collaggr= collaggr self.collections= self.collaggr.collections self.signalMapper= QSignalMapper () for collNo, collection in enumerate (self.collections): collection.newSongs.connect (self.signalMapper.map) self.signalMapper.setMapping (collection, collNo) self.signalMapper.mapped.connect (self.addRows) else: self.collaggr= CollectionAggregator (self, songs=songs) self.attrNames= ('artist', 'year', 'collection', 'diskno', 'album', 'trackno', 'title', 'length', 'filepath') self.headers= (u'Artist', u'Year', u'Collection', u'CD', u'Album', u'Track', u'Title', u'Length', u'Path') # FIXME: kinda hacky self.fontMetrics= QFontMetrics (KGlobalSettings.generalFont ()) # FIXME: (even more) hackish self.columnWidths= ("M"*15, "M"*4, "M"*15, "M"*3, "M"*20, "M"*3, "M"*25, "M"*5, "M"*200) logger.debug ("QPLM: ", self) def data (self, modelIndex, role): # TODO: allow to enter to edit mode in filepath but don't save any changes # so we can copy the path, that is... if modelIndex.isValid () and modelIndex.row ()<self.collaggr.count: song= self.collaggr.songForIndex (modelIndex.row ()) if role==Qt.DisplayRole or role==Qt.EditRole: attr= self.attrNames [modelIndex.column ()] rawData= song[attr] if attr=='filepath': # filenames as they are rawData= QFile.decodeName (rawData) elif attr=='title' and role!=Qt.EditRole: # don't (even try to) add the [#] to the title try: queueIndex= self.playlist.indexQueue.index (modelIndex.row ()) # make it show as starting in 1, otherwise it's confusing rawData= "[%d] %s" % (queueIndex+1, rawData) except ValueError: pass data= QVariant (rawData) elif role==Qt.SizeHintRole: size= self.fontMetrics.size (Qt.TextSingleLine, self.columnWidths[modelIndex.column ()]) data= QVariant (size) elif role==Qt.BackgroundRole and self.view_.modelIndex is not None: if modelIndex.row ()==self.view_.modelIndex.row (): # highlight the current song # must return a QBrush data= QVariant (QApplication.palette ().dark ()) else: try: queueIndex= self.playlist.indexQueue.index (modelIndex.row ()) data= QVariant (QApplication.palette ().mid ()) except ValueError: data= QVariant () elif role==Qt.ForegroundRole and self.view_.modelIndex is not None: if modelIndex.row ()==self.view_.modelIndex.row (): # highlight the current song # must return a QBrush data= QVariant (QApplication.palette ().brightText ()) else: data= QVariant () else: data= QVariant () else: data= QVariant () return data def flags (self, modelIndex): ans= QAbstractTableModel.flags (self, modelIndex) if modelIndex.column ()<7: # length or filepath are not editable ans= ans|Qt.ItemIsEditable|Qt.ItemIsEditable return ans def match (self, start, role, value, hits=1, flags=None): # when you press a key on an uneditable cell, QTableView tries to search # calling this function for matching. we already have a way for searching # and it loads the metadata of all the songs anyways # so we disable it by constantly returning an empty list return [] def setData (self, modelIndex, variant, role=Qt.EditRole): # not length or filepath and editing if modelIndex.column ()<7 and role==Qt.EditRole: logger.debug ("QPLM.setData()", modelIndex.row (), modelIndex.column(), role, ">%s<", unicode (variant.toString ())) song= self.collaggr.songForIndex (modelIndex.row ()) attr= self.attrNames[modelIndex.column ()] try: song[attr]= unicode (variant.toString ()) # TODO: make a list of dirty songs and commit them later song.saveMetadata () except TagWriteError: # it failed ans= False else: self.edited= True self.dataChanged.emit (modelIndex, modelIndex) ans= True else: ans= QAbstractTableModel.setData (self, modelIndex, variant, role) logger.debug ("QPLM.setData():", modelIndex.row(), modelIndex.column (), role, ans) return ans def headerData (self, section, direction, role=Qt.DisplayRole): if direction==Qt.Horizontal and role==Qt.DisplayRole: data= QVariant (self.headers[section]) elif direction==Qt.Vertical: if role==Qt.SizeHintRole: # TODO: support bold fonts # again, hacky. 5 for enough witdh for 5 digits size= self.fontMetrics.size (Qt.TextSingleLine, "M"*5) data= QVariant (size) elif role==Qt.TextAlignmentRole: data= QVariant (Qt.AlignRight|Qt.AlignVCenter) else: data= QAbstractTableModel.headerData (self, section, direction, role) else: data= QAbstractTableModel.headerData (self, section, direction, role) return data def addRows (self, collNo): collection= self.collections[collNo] for index, filepath in collection.newSongs_: self.beginInsertRows (QModelIndex (), index, index) self.endInsertRows () modelIndex= self.index (index, 0) self.dataChanged.emit (modelIndex, modelIndex) def dirtyRow (self, index): logger.debug ("QLMP.dirtyRow():", index) columns= self.columnCount () start= self.index (index, 0) end= self.index (index, columns) self.edited= False self.dataChanged.emit (start, end) def rowCount (self, parent=None): return self.collaggr.count def columnCount (self, parent=None): return len (self.attrNames)