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(['artist', '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
):
97 class LibrarySongItem(QtGui
.QStandardItem
):
102 class LibraryModel(QtGui
.QStandardItemModel
):
103 def fill(self
, songs
, grouping
):
106 tree
= [{},self
.invisibleRootItem()]
109 for part
in grouping
:
113 if tag
in cur_item
[0]:
114 cur_item
= cur_item
[0][tag
]
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
):
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
):
140 for index
in self
.selectedIndexes():
141 ret
.append(self
.model().itemFromIndex(index
))
145 def __init__(self
, plugin
):
146 QtGui
.QWidget
.__init
__(self
)
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()
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)
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
):
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
)
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()))