switch to PyQt4 API v2 for QStrings
[nephilim.git] / nephilim / plugins / Library.py
blob89573e6ed26e38e76703bd984aecf940ce64dbfc
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' : ['albumartist', '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 = []
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
96 filtered_items = None
97 filter = None
99 class LibrarySongItem(QtGui.QStandardItem):
100 # public
101 "Song path"
102 path = None
104 class LibraryModel(QtGui.QStandardItemModel):
105 def fill(self, songs, grouping):
106 self.clear()
108 tree = [{},self.invisibleRootItem()]
109 for song in songs:
110 cur_item = tree
111 for part in grouping:
112 tag = song[part]
113 if not tag:
114 tag = 'Unknown'
115 if tag in cur_item[0]:
116 cur_item = cur_item[0][tag]
117 else:
118 it = QtGui.QStandardItem(tag)
119 it.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
120 cur_item[1].appendRow(it)
121 cur_item[0][tag] = [{}, it]
122 cur_item = cur_item[0][tag]
123 it = LibraryWidget.LibrarySongItem('%s%02d %s'%(song['disc'] + '/' if 'disc' in song else '',
124 song['tracknum'], song['title']))
125 it.path = song['file']
126 it.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled)
127 cur_item[1].appendRow(it)
129 self.sort(0, QtCore.Qt.AscendingOrder)
131 class LibraryView(QtGui.QTreeView):
132 def __init__(self):
133 QtGui.QTreeView.__init__(self)
135 self.setAlternatingRowColors(True)
136 self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
137 self.setUniformRowHeights(True)
138 self.setHeaderHidden(True)
140 def selectedItems(self):
141 ret = []
142 for index in self.selectedIndexes():
143 ret.append(self.model().itemFromIndex(index))
145 return ret
147 def __init__(self, plugin):
148 QtGui.QWidget.__init__(self)
149 self.plugin = plugin
150 self.logger = plugin.logger
151 self.settings = QtCore.QSettings()
152 self.filter = ''
153 self.filtered_items = []
154 self.settings.beginGroup(self.plugin.name)
156 self.grouping = QtGui.QLabel()
158 self.search_txt = QtGui.QLineEdit()
159 self.search_txt.setToolTip('Filter library')
160 self.search_txt.textChanged.connect(self.filter_library)
161 self.search_txt.returnPressed.connect(self.add_filtered)
163 #construct the library
164 self.library_model = self.LibraryModel()
165 self.fill_library()
167 self.library_view = self.LibraryView()
168 self.library_view.setModel(self.library_model)
169 self.library_view.activated.connect(self.add_selection)
171 self.setLayout(QtGui.QVBoxLayout())
172 self.layout().setSpacing(2)
173 self.layout().setMargin(0)
174 self.layout().addWidget(self.grouping)
175 self.layout().addWidget(self.search_txt)
176 self.layout().addWidget(self.library_view)
178 self.plugin.mpclient.connect_changed.connect(self.fill_library)
179 self.plugin.mpclient.db_updated.connect(self.fill_library)
181 def fill_library(self):
182 self.logger.info('Refreshing library.')
183 self.grouping.setText('/'.join(self.settings.value('grouping').toStringList()))
184 self.library_model.fill(self.plugin.mpclient.library(), self.settings.value('grouping').toStringList())
186 @QtCore.pyqtSlot(unicode)
187 def filter_library(self, text):
188 """Hide all items that don't contain text."""
189 to_hide = []
190 to_show = []
191 filtered_items = []
192 text = text.lower()
193 if not text: # show all items
194 to_show = self.library_model.findItems('*', QtCore.Qt.MatchWildcard|QtCore.Qt.MatchRecursive)
195 elif self.filter and self.filter in text:
196 for item in self.filtered_items:
197 if text in item.text().lower():
198 filtered_items.append(item)
199 while item:
200 to_show.append(item)
201 item = item.parent()
202 else:
203 while item:
204 to_hide.append(item)
205 item = item.parent()
206 else:
207 for item in self.library_model.findItems('*', QtCore.Qt.MatchWildcard|QtCore.Qt.MatchRecursive):
208 if text in item.text().lower():
209 filtered_items.append(item)
210 while item:
211 to_show.append(item)
212 item = item.parent()
213 else:
214 while item:
215 to_hide.append(item)
216 item = item.parent()
217 for item in to_hide:
218 self.library_view.setRowHidden(item.row(), self.library_model.indexFromItem(item.parent()), True)
219 for item in to_show:
220 self.library_view.setRowHidden(item.row(), self.library_model.indexFromItem(item.parent()), False)
222 if len(filtered_items) < 5:
223 for item in filtered_items:
224 while item:
225 item = item.parent()
226 self.library_view.setExpanded(self.library_model.indexFromItem(item), True)
228 self.filtered_items = filtered_items
229 self.filter = text
231 def add_filtered(self):
232 self.add_items(self.filtered_items)
233 self.search_txt.clear()
235 def add_selection(self):
236 self.add_items(self.library_view.selectedItems())
238 def add_items(self, items):
239 paths = []
240 for item in items:
241 self.item_to_playlist(item, paths)
242 self.plugin.mpclient.add(paths)
244 def item_to_playlist(self, item, add_queue):
245 if isinstance(item, self.LibrarySongItem):
246 add_queue.append(item.path)
247 else:
248 for i in range(item.rowCount()):
249 self.item_to_playlist(item.child(i), add_queue)