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
22 from ..common
import MIMETYPES
, SongsMimeData
24 class Library(Plugin
):
26 info
= 'Display MPD database as a tree.'
32 DEFAULTS
= {'grouping' : ['albumartist', 'album']}
35 self
.o
= LibraryWidget(self
)
39 def _get_dock_widget(self
):
40 return self
._create
_dock
(self
.o
)
42 def fill_library(self
):
47 class SettingsWidgetLibrary(Plugin
.SettingsWidget
):
49 def __init__(self
, plugin
):
50 Plugin
.SettingsWidget
.__init
__(self
, plugin
)
51 self
.settings
.beginGroup(self
.plugin
.name
)
53 tags_enabled
= self
.settings
.value('grouping').toStringList()
54 tags
= self
.plugin
.mpclient
.tagtypes
55 self
.taglist
= QtGui
.QListWidget(self
)
56 self
.taglist
.setDragDropMode(QtGui
.QAbstractItemView
.InternalMove
)
57 for tag
in [tag
for tag
in tags_enabled
if tag
in tags
]:
58 it
= QtGui
.QListWidgetItem(tag
)
59 it
.setCheckState(QtCore
.Qt
.Checked
)
60 self
.taglist
.addItem(it
)
61 for tag
in [tag
for tag
in tags
if tag
not in tags_enabled
]:
62 it
= QtGui
.QListWidgetItem(tag
)
63 it
.setCheckState(QtCore
.Qt
.Unchecked
)
64 self
.taglist
.addItem(it
)
66 self
.setLayout(QtGui
.QVBoxLayout())
67 self
._add
_widget
(self
.taglist
, label
= 'Group', tooltip
= 'Checked items and their order determines,\n'
68 'by what tags will songs be grouped in Library. Use drag and drop to change the\n'
71 self
.settings
.endGroup()
73 def save_settings(self
):
74 self
.settings
.beginGroup(self
.plugin
.name
)
77 for i
in range(self
.taglist
.count()):
78 it
= self
.taglist
.item(i
)
79 if it
.checkState() == QtCore
.Qt
.Checked
:
80 tags
.append(it
.text())
81 self
.settings
.setValue('grouping', QtCore
.QVariant(tags
))
83 self
.settings
.endGroup()
84 self
.plugin
.fill_library()
86 def get_settings_widget(self
):
87 return self
.SettingsWidgetLibrary(self
)
89 class LibraryWidget(QtGui
.QWidget
):
101 class LibrarySongItem(QtGui
.QStandardItem
):
106 class LibraryModel(QtGui
.QStandardItemModel
):
107 def fill(self
, songs
, grouping
):
110 tree
= [{},self
.invisibleRootItem()]
113 for part
in grouping
:
118 if tag
in cur_item
[0]:
119 cur_item
= cur_item
[0][tag
]
121 it
= QtGui
.QStandardItem(tag
)
122 it
.setFlags(QtCore
.Qt
.ItemIsSelectable|QtCore
.Qt
.ItemIsEnabled
)
123 cur_item
[1].appendRow(it
)
124 cur_item
[0][tag
] = [{}, it
]
125 cur_item
= cur_item
[0][tag
]
126 it
= LibraryWidget
.LibrarySongItem('%s%02d %s'%(song
['disc'] + '/' if 'disc' in song
else '',
127 song
['tracknum'] if 'tracknum' in song
else 0,
129 it
.path
= song
['?file']
130 it
.setFlags(QtCore
.Qt
.ItemIsSelectable|QtCore
.Qt
.ItemIsEnabled
)
131 cur_item
[1].appendRow(it
)
133 self
.sort(0, QtCore
.Qt
.AscendingOrder
)
135 def walk_tree(self
, indices
):
136 """Returns a generator over all songs that are children of indices."""
137 for index
in indices
:
138 if self
.hasChildren(index
):
139 for song
in self
.walk_tree([self
.index(i
, 0, index
) for i
in range(self
.rowCount(index
))]):
142 yield self
.itemFromIndex(index
).path
144 def flags(self
, index
):
145 return (QtCore
.Qt
.ItemIsDragEnabled
if index
.isValid() else 0) | QtGui
.QStandardItemModel
.flags(self
, index
)
148 return MIMETYPES
['songs']
150 def mimeData(self
, indices
):
151 data
= SongsMimeData()
154 for song
in self
.walk_tree(indices
):
157 data
.set_songs(songs
)
160 class LibraryView(QtGui
.QTreeView
):
162 QtGui
.QTreeView
.__init
__(self
)
164 self
.setAlternatingRowColors(True)
165 self
.setSelectionMode(QtGui
.QAbstractItemView
.ExtendedSelection
)
166 self
.setUniformRowHeights(True)
167 self
.setHeaderHidden(True)
168 self
.setDragEnabled(True)
170 def __init__(self
, plugin
):
171 QtGui
.QWidget
.__init
__(self
)
173 self
.logger
= plugin
.logger
174 self
.settings
= QtCore
.QSettings()
176 self
.filtered_items
= []
177 self
.settings
.beginGroup(self
.plugin
.name
)
179 self
.grouping
= QtGui
.QLabel()
181 self
.search_txt
= QtGui
.QLineEdit()
182 self
.search_txt
.setToolTip('Filter library')
183 self
.search_txt
.textChanged
.connect(self
.filter_library
)
184 self
.search_txt
.returnPressed
.connect(self
.add_filtered
)
186 #construct the library
187 self
.library_model
= self
.LibraryModel()
190 self
.library_view
= self
.LibraryView()
191 self
.library_view
.setModel(self
.library_model
)
192 self
.library_view
.activated
.connect(lambda : self
.add_indices(self
.library_view
.selectedIndexes()))
194 self
.setLayout(QtGui
.QVBoxLayout())
195 self
.layout().setSpacing(2)
196 self
.layout().setMargin(0)
197 self
.layout().addWidget(self
.grouping
)
198 self
.layout().addWidget(self
.search_txt
)
199 self
.layout().addWidget(self
.library_view
)
201 self
.plugin
.mpclient
.connect_changed
.connect(self
.fill_library
)
202 self
.plugin
.mpclient
.db_updated
.connect(self
.fill_library
)
204 def fill_library(self
):
205 self
.logger
.info('Refreshing library.')
206 self
.grouping
.setText('/'.join(self
.settings
.value('grouping').toStringList()))
207 self
.library_model
.fill(self
.plugin
.mpclient
.library(), self
.settings
.value('grouping').toStringList())
209 @QtCore.pyqtSlot(unicode)
210 def filter_library(self
, text
):
211 """Hide all items that don't contain text."""
216 if not text
: # show all items
217 to_show
= self
.library_model
.findItems('*', QtCore
.Qt
.MatchWildcard|QtCore
.Qt
.MatchRecursive
)
218 elif self
.filter and self
.filter in text
:
219 for item
in self
.filtered_items
:
220 if text
in item
.text().lower():
221 filtered_items
.append(item
)
230 for item
in self
.library_model
.findItems('*', QtCore
.Qt
.MatchWildcard|QtCore
.Qt
.MatchRecursive
):
231 if text
in item
.text().lower():
232 filtered_items
.append(item
)
241 self
.library_view
.setRowHidden(item
.row(), self
.library_model
.indexFromItem(item
.parent()), True)
243 self
.library_view
.setRowHidden(item
.row(), self
.library_model
.indexFromItem(item
.parent()), False)
245 if len(filtered_items
) < 5:
246 for item
in filtered_items
:
249 self
.library_view
.setExpanded(self
.library_model
.indexFromItem(item
), True)
251 self
.filtered_items
= filtered_items
255 def add_filtered(self
):
256 self
.add_indices([self
.library_model
.indexFromItem(index
) for index
in self
.filtered_items
])
257 self
.search_txt
.clear()
259 def add_indices(self
, indices
):
261 for song
in self
.library_model
.walk_tree(indices
):
263 self
.plugin
.mpclient
.add(paths
)