song: if a tag has multiple values, choose first.
[nephilim.git] / nephilim / plugins / Library.py
blob80909222bc0a94bb860ac9e74331ddaa4bdf1b76
2 # Copyright (C) 2009 Anton Khirnov <wyskas@gmail.com>
4 # Nephilim is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
9 # Nephilim is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Nephilim. If not, see <http://www.gnu.org/licenses/>.
18 from PyQt4 import QtGui, QtCore
19 from PyQt4.QtCore import QVariant
21 from ..plugin import Plugin
23 class Library(Plugin):
24 # public, const
25 info = 'Display MPD database as a tree.'
27 # public, read-only
28 o = None
30 # private
31 DEFAULTS = {'grouping' : QtCore.QStringList(['artist', 'album'])}
33 def _load(self):
34 self.o = LibraryWidget(self)
35 def _unload(self):
36 self.o = None
38 def _get_dock_widget(self):
39 return self._create_dock(self.o)
41 def fill_library(self):
42 if not self.o:
43 return
44 self.o.fill_library()
46 class SettingsWidgetLibrary(Plugin.SettingsWidget):
47 taglist = None
48 def __init__(self, plugin):
49 Plugin.SettingsWidget.__init__(self, plugin)
50 self.settings.beginGroup(self.plugin.name)
52 tags_enabled = self.settings.value('grouping').toStringList()
53 tags = self.plugin.mpclient.tagtypes()
54 self.taglist = QtGui.QListWidget(self)
55 self.taglist.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
56 for tag in [tag for tag in tags_enabled if tag in tags]:
57 it = QtGui.QListWidgetItem(tag)
58 it.setCheckState(QtCore.Qt.Checked)
59 self.taglist.addItem(it)
60 for tag in [tag for tag in tags if tag not in tags_enabled]:
61 it = QtGui.QListWidgetItem(tag)
62 it.setCheckState(QtCore.Qt.Unchecked)
63 self.taglist.addItem(it)
65 self.setLayout(QtGui.QVBoxLayout())
66 self._add_widget(self.taglist, label = 'Group', tooltip = 'Checked items and their order determines,\n'
67 'by what tags will songs be grouped in Library. Use drag and drop to change the\n'
68 'order of tags.')
70 self.settings.endGroup()
72 def save_settings(self):
73 self.settings.beginGroup(self.plugin.name)
75 tags = QtCore.QStringList()
76 for i in range(self.taglist.count()):
77 it = self.taglist.item(i)
78 if it.checkState() == QtCore.Qt.Checked:
79 tags.append(it.text())
80 self.settings.setValue('grouping', QtCore.QVariant(tags))
82 self.settings.endGroup()
83 self.plugin.fill_library()
85 def get_settings_widget(self):
86 return self.SettingsWidgetLibrary(self)
88 class LibraryWidget(QtGui.QWidget):
89 library_view = None
90 library_model = None
91 search_txt = None
92 grouping = None
93 settings = None
94 plugin = None
95 logger = None
97 class LibrarySongItem(QtGui.QStandardItem):
98 # public
99 "Song path"
100 path = None
102 class LibraryModel(QtGui.QStandardItemModel):
103 def fill(self, songs, grouping):
104 self.clear()
106 tree = [{},self.invisibleRootItem()]
107 for song in songs:
108 cur_item = tree
109 for part in grouping:
110 tag = song[part]
111 if not tag:
112 tag = 'Unknown'
113 if tag in cur_item[0]:
114 cur_item = cur_item[0][tag]
115 else:
116 it = QtGui.QStandardItem(tag)
117 it.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
118 cur_item[1].appendRow(it)
119 cur_item[0][tag] = [{}, it]
120 cur_item = cur_item[0][tag]
121 it = LibraryWidget.LibrarySongItem('%s%02d %s'%(song['disc'] + '/' if 'disc' in song else '',
122 song['tracknum'], song['title']))
123 it.path = song['file']
124 it.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
125 cur_item[1].appendRow(it)
127 self.sort(0, QtCore.Qt.AscendingOrder)
129 class LibraryView(QtGui.QTreeView):
130 def __init__(self):
131 QtGui.QTreeView.__init__(self)
133 self.setAlternatingRowColors(True)
134 self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
135 self.setUniformRowHeights(True)
136 self.setHeaderHidden(True)
138 def selectedItems(self):
139 ret = []
140 for index in self.selectedIndexes():
141 ret.append(self.model().itemFromIndex(index))
143 return ret
145 def __init__(self, plugin):
146 QtGui.QWidget.__init__(self)
147 self.plugin = plugin
148 self.logger = plugin.logger
149 self.settings = QtCore.QSettings()
150 self.settings.beginGroup(self.plugin.name)
152 self.grouping = QtGui.QLabel()
154 self.search_txt = QtGui.QLineEdit()
155 self.search_txt.textChanged.connect(self.filter_changed)
156 self.search_txt.returnPressed.connect(self.add_filtered)
158 #construct the library
159 self.library_model = self.LibraryModel()
160 self.fill_library()
162 self.library_view = self.LibraryView()
163 self.library_view.setModel(self.library_model)
164 self.library_view.activated.connect(self.add_selection)
166 self.setLayout(QtGui.QVBoxLayout())
167 self.layout().setSpacing(2)
168 self.layout().setMargin(0)
169 self.layout().addWidget(self.grouping)
170 self.layout().addWidget(self.search_txt)
171 self.layout().addWidget(self.library_view)
173 self.plugin.mpclient.connect_changed.connect(self.fill_library)
174 self.plugin.mpclient.db_updated.connect(self.fill_library)
176 def fill_library(self):
177 self.logger.info('Refreshing library.')
178 self.grouping.setText(self.settings.value('grouping').toStringList().join('/'))
179 self.library_model.fill(self.plugin.mpclient.library(), self.settings.value('grouping').toStringList())
181 def filter_changed(self, text):
182 items = self.library_model.findItems(text, QtCore.Qt.MatchContains|QtCore.Qt.MatchRecursive)
183 for i in range(self.library_model.rowCount()):
184 self.library_view.setRowHidden(i, QtCore.QModelIndex(), True)
185 for item in items:
186 while item.parent():
187 item = item.parent()
188 self.library_view.setRowHidden(item.row(), QtCore.QModelIndex(), False)
189 self.filtered_items = items
191 def add_filtered(self):
192 self.add_items(self.filtered_items)
193 self.search_txt.clear()
195 def add_selection(self):
196 self.add_items(self.library_view.selectedItems())
198 def add_items(self, items):
199 paths = []
200 for item in items:
201 self.item_to_playlist(item, paths)
202 self.plugin.mpclient.add(paths)
204 def item_to_playlist(self, item, add_queue):
205 if isinstance(item, self.LibrarySongItem):
206 add_queue.append(item.path)
207 else:
208 for i in range(item.rowCount()):
209 self.item_to_playlist(item.child(i), add_queue)
211 def modes_activated(self):
212 self.settings.setValue('current_mode', QVariant(self.modes.currentIndex()))
213 self.fill_library()