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
):
25 info
= 'Display MPD database as a tree.'
31 DEFAULTS
= {'grouping' : QtCore
.QStringList(['albumartist', 'album'])}
34 self
.o
= LibraryWidget(self
)
38 def _get_dock_widget(self
):
39 return self
._create
_dock
(self
.o
)
41 def fill_library(self
):
46 class SettingsWidgetLibrary(Plugin
.SettingsWidget
):
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'
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
):
99 class LibrarySongItem(QtGui
.QStandardItem
):
104 class LibraryModel(QtGui
.QStandardItemModel
):
105 def fill(self
, songs
, grouping
):
108 tree
= [{},self
.invisibleRootItem()]
111 for part
in grouping
:
115 if tag
in cur_item
[0]:
116 cur_item
= cur_item
[0][tag
]
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
):
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
):
142 for index
in self
.selectedIndexes():
143 ret
.append(self
.model().itemFromIndex(index
))
147 def __init__(self
, plugin
):
148 QtGui
.QWidget
.__init
__(self
)
150 self
.logger
= plugin
.logger
151 self
.settings
= QtCore
.QSettings()
152 self
.filter = QtCore
.QString()
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()
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(self
.settings
.value('grouping').toStringList().join('/'))
184 self
.library_model
.fill(self
.plugin
.mpclient
.library(), self
.settings
.value('grouping').toStringList())
186 @QtCore.pyqtSlot(QtCore
.QString
)
187 def filter_library(self
, text
):
188 """Hide all items that don't contain text."""
192 if not text
: # show all items
193 to_show
= self
.library_model
.findItems('*', QtCore
.Qt
.MatchWildcard|QtCore
.Qt
.MatchRecursive
)
194 elif self
.filter and text
.contains(self
.filter, QtCore
.Qt
.CaseInsensitive
):
195 for item
in self
.filtered_items
:
196 if item
.text().contains(text
, QtCore
.Qt
.CaseInsensitive
):
197 filtered_items
.append(item
)
206 for item
in self
.library_model
.findItems('*', QtCore
.Qt
.MatchWildcard|QtCore
.Qt
.MatchRecursive
):
207 if item
.text().contains(text
, QtCore
.Qt
.CaseInsensitive
):
208 filtered_items
.append(item
)
217 self
.library_view
.setRowHidden(item
.row(), self
.library_model
.indexFromItem(item
.parent()), True)
219 self
.library_view
.setRowHidden(item
.row(), self
.library_model
.indexFromItem(item
.parent()), False)
221 if len(filtered_items
) < 5:
222 for item
in filtered_items
:
225 self
.library_view
.setExpanded(self
.library_model
.indexFromItem(item
), True)
227 self
.filtered_items
= filtered_items
230 def add_filtered(self
):
231 self
.add_items(self
.filtered_items
)
232 self
.search_txt
.clear()
234 def add_selection(self
):
235 self
.add_items(self
.library_view
.selectedItems())
237 def add_items(self
, items
):
240 self
.item_to_playlist(item
, paths
)
241 self
.plugin
.mpclient
.add(paths
)
243 def item_to_playlist(self
, item
, add_queue
):
244 if isinstance(item
, self
.LibrarySongItem
):
245 add_queue
.append(item
.path
)
247 for i
in range(item
.rowCount()):
248 self
.item_to_playlist(item
.child(i
), add_queue
)