Library: safer system for grouping.
[nephilim.git] / nephilim / plugins / Library.py
blobf59d2978e6fe77ca00d5acab257ae93fc8ea6171
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 isinstance(tag, list):
112 tag = tag[0] #FIXME is this the best solution?
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.settings.beginGroup(self.plugin.name)
154 self.grouping = QtGui.QLabel()
156 self.search_txt = QtGui.QLineEdit()
157 self.search_txt.textChanged.connect(self.filter_changed)
158 self.search_txt.returnPressed.connect(self.add_filtered)
160 #construct the library
161 self.library_model = self.LibraryModel()
162 self.fill_library()
164 self.library_view = self.LibraryView()
165 self.library_view.setModel(self.library_model)
166 self.library_view.activated.connect(self.add_selection)
168 self.setLayout(QtGui.QVBoxLayout())
169 self.layout().setSpacing(2)
170 self.layout().setMargin(0)
171 self.layout().addWidget(self.grouping)
172 self.layout().addWidget(self.search_txt)
173 self.layout().addWidget(self.library_view)
175 self.plugin.mpclient.connect_changed.connect(self.fill_library)
176 self.plugin.mpclient.db_updated.connect(self.fill_library)
178 def fill_library(self):
179 self.logger.info('Refreshing library.')
180 self.grouping.setText(self.settings.value('grouping').toStringList().join('/'))
181 self.library_model.fill(self.plugin.mpclient.library(), self.settings.value('grouping').toStringList())
183 def filter_changed(self, text):
184 items = self.library_model.findItems(text, QtCore.Qt.MatchContains|QtCore.Qt.MatchRecursive)
185 for i in range(self.library_model.rowCount()):
186 self.library_view.setRowHidden(i, QtCore.QModelIndex(), True)
187 for item in items:
188 while item.parent():
189 item = item.parent()
190 self.library_view.setRowHidden(item.row(), QtCore.QModelIndex(), False)
191 self.filtered_items = items
193 def add_filtered(self):
194 self.add_items(self.filtered_items)
195 self.search_txt.clear()
197 def add_selection(self):
198 self.add_items(self.library_view.selectedItems())
200 def add_items(self, items):
201 paths = []
202 for item in items:
203 self.item_to_playlist(item, paths)
204 self.plugin.mpclient.add(paths)
206 def item_to_playlist(self, item, add_queue):
207 if isinstance(item, self.LibrarySongItem):
208 add_queue.append(item.path)
209 else:
210 for i in range(item.rowCount()):
211 self.item_to_playlist(item.child(i), add_queue)
213 def modes_activated(self):
214 self.settings.setValue('current_mode', QVariant(self.modes.currentIndex()))
215 self.fill_library()