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 DEFAULTS
= {'modes' : ['artist',
30 'genre/artist/album']}
33 self
.o
= LibraryWidget(self
)
38 return "List showing all the songs allowing filtering and grouping."
40 def _get_dock_widget(self
):
41 return self
._create
_dock
(self
.o
)
43 def fill_library(self
, params
):
48 class SettingsWidgetLibrary(Plugin
.SettingsWidget
):
50 def __init__(self
, plugin
):
51 Plugin
.SettingsWidget
.__init
__(self
, plugin
)
52 self
.setLayout(QtGui
.QVBoxLayout())
54 self
.modes
= QtGui
.QComboBox()
55 self
.modes
.setEditable(True)
56 for mode
in self
.settings().value(self
.plugin
.name() + '/modes').toStringList():
57 self
.modes
.addItem(mode
)
58 self
._add
_widget
(self
.modes
, 'Modes', 'How should the songs in library be grouped.\n'
59 'Should be written in form tag1/tag2/...,\n'
60 'using tags supported by MPD.')
62 def save_settings(self
):
64 for i
in range(0, self
.modes
.count()):
65 modes
.append(self
.modes
.itemText(i
))
66 self
.settings().setValue(self
.plugin
.name() + '/modes', QVariant(modes
))
67 self
.plugin
.o
.refresh_modes()
69 def get_settings_widget(self
):
70 return self
.SettingsWidgetLibrary(self
)
73 class LibraryWidget(QtGui
.QWidget
):
82 class LibraryModel(QtGui
.QStandardItemModel
):
83 def fill(self
, songs
, mode
):
86 tree
= [{},self
.invisibleRootItem()]
89 for part
in mode
.split('/'):
91 if isinstance(tag
, list):
92 tag
= tag
[0] #FIXME hack to make songs with multiple genres work.
95 if tag
in cur_item
[0]:
96 cur_item
= cur_item
[0][tag
]
98 it
= QtGui
.QStandardItem(tag
)
99 it
.setFlags(QtCore
.Qt
.ItemIsSelectable|QtCore
.Qt
.ItemIsEnabled
)
100 cur_item
[1].appendRow(it
)
101 cur_item
[0][tag
] = [{}, it
]
102 cur_item
= cur_item
[0][tag
]
103 it
= QtGui
.QStandardItem('%s%02d %s'%(song
.tag('disc') + '/' if song
.tag('disc') else '',
104 song
.track() if song
.track() else 0,
105 song
.title() if song
.title() else song
.filepath()))
106 it
.setFlags(QtCore
.Qt
.ItemIsSelectable|QtCore
.Qt
.ItemIsEnabled
)
107 it
.setData(QVariant(song
), QtCore
.Qt
.UserRole
)
108 cur_item
[1].appendRow(it
)
110 self
.sort(0, QtCore
.Qt
.AscendingOrder
)
112 class LibraryView(QtGui
.QTreeView
):
114 QtGui
.QTreeView
.__init
__(self
)
116 self
.setAlternatingRowColors(True)
117 self
.setSelectionMode(QtGui
.QAbstractItemView
.ExtendedSelection
)
118 self
.setUniformRowHeights(True)
119 self
.setHeaderHidden(True)
121 def selectedItems(self
):
123 for index
in self
.selectedIndexes():
124 ret
.append(self
.model().itemFromIndex(index
))
128 def __init__(self
, plugin
):
129 QtGui
.QWidget
.__init
__(self
)
131 self
.logger
= plugin
.logger
132 self
.settings
= QtCore
.QSettings()
133 self
.settings
.beginGroup(self
.plugin
.name())
135 self
.modes
= QtGui
.QComboBox()
137 self
.connect(self
.modes
, QtCore
.SIGNAL('activated(int)'), self
.modes_activated
)
139 self
.search_txt
= QtGui
.QLineEdit()
140 self
.connect(self
.search_txt
, QtCore
.SIGNAL('textChanged(const QString&)'),
142 self
.connect(self
.search_txt
, QtCore
.SIGNAL('returnPressed()'), self
.add_filtered
)
144 #construct the library
145 self
.library_model
= self
.LibraryModel()
148 self
.library_view
= self
.LibraryView()
149 self
.library_view
.setModel(self
.library_model
)
150 self
.connect(self
.library_view
, QtCore
.SIGNAL('activated(const QModelIndex &)'), self
.add_selection
)
152 self
.setLayout(QtGui
.QVBoxLayout())
153 self
.layout().setSpacing(2)
154 self
.layout().setMargin(0)
155 self
.layout().addWidget(self
.modes
)
156 self
.layout().addWidget(self
.search_txt
)
157 self
.layout().addWidget(self
.library_view
)
159 self
.connect(self
.plugin
.mpclient(), QtCore
.SIGNAL('connected'), self
.fill_library
)
160 self
.connect(self
.plugin
.mpclient(), QtCore
.SIGNAL('disconnected'), self
.fill_library
)
161 self
.connect(self
.plugin
.mpclient(), QtCore
.SIGNAL('db_updated'), self
.fill_library
)
163 def refresh_modes(self
):
165 for mode
in self
.settings
.value('/modes').toStringList():
166 self
.modes
.addItem(mode
)
167 self
.modes
.setCurrentIndex(self
.settings
.value('current_mode').toInt()[0])
169 def fill_library(self
):
170 self
.logger
.info('Refreshing library.')
171 self
.library_model
.fill(self
.plugin
.mpclient().library(), str(self
.modes
.currentText()))
173 def filter_changed(self
, text
):
174 items
= self
.library_model
.findItems(text
, QtCore
.Qt
.MatchContains|QtCore
.Qt
.MatchRecursive
)
175 for i
in range(self
.library_model
.rowCount()):
176 self
.library_view
.setRowHidden(i
, QtCore
.QModelIndex(), True)
180 self
.library_view
.setRowHidden(item
.row(), QtCore
.QModelIndex(), False)
181 self
.filtered_items
= items
183 def add_filtered(self
):
184 self
.add_items(self
.filtered_items
)
185 self
.search_txt
.clear()
187 def add_selection(self
):
188 self
.add_items(self
.library_view
.selectedItems())
190 def add_items(self
, items
):
193 self
.item_to_playlist(item
, paths
)
194 self
.plugin
.mpclient().add(paths
)
196 def item_to_playlist(self
, item
, add_queue
):
197 if not item
.hasChildren():
198 add_queue
.append(item
.data(QtCore
.Qt
.UserRole
).toPyObject().filepath())
200 for i
in range(item
.rowCount()):
201 self
.item_to_playlist(item
.child(i
), add_queue
)
203 def modes_activated(self
):
204 self
.settings
.setValue('current_mode', QVariant(self
.modes
.currentIndex()))