Merge lp:~mixxxdevelopers/mixxx/features_rhythmbox into lp:~mixxxdevelopers/mixxx/trunk
- features_rhythmbox
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 2646 | ||||
Proposed branch: | lp:~mixxxdevelopers/mixxx/features_rhythmbox | ||||
Merge into: | lp:~mixxxdevelopers/mixxx/trunk | ||||
Diff against target: |
4495 lines (+1341/-1470) 38 files modified
mixxx/build/depends.py (+19/-12) mixxx/res/schema.xml (+29/-0) mixxx/src/library/abstractxmltrackmodel.cpp (+0/-218) mixxx/src/library/abstractxmltrackmodel.h (+0/-100) mixxx/src/library/autodjfeature.cpp (+1/-1) mixxx/src/library/basesqltablemodel.cpp (+13/-2) mixxx/src/library/basesqltablemodel.h (+3/-0) mixxx/src/library/cratefeature.h (+1/-1) mixxx/src/library/itunes/itunesfeature.cpp (+107/-59) mixxx/src/library/itunes/itunesfeature.h (+20/-11) mixxx/src/library/itunes/itunesplaylistmodel.cpp (+3/-2) mixxx/src/library/itunes/itunesplaylistmodel.h (+1/-1) mixxx/src/library/itunes/itunestrackmodel.cpp (+3/-2) mixxx/src/library/itunes/itunestrackmodel.h (+1/-1) mixxx/src/library/library.cpp (+6/-4) mixxx/src/library/libraryfeature.cpp (+1/-0) mixxx/src/library/libraryfeature.h (+4/-0) mixxx/src/library/mixxxlibraryfeature.cpp (+1/-1) mixxx/src/library/promotracksfeature.cpp (+0/-1) mixxx/src/library/proxytrackmodel.cpp (+0/-130) mixxx/src/library/proxytrackmodel.h (+0/-53) mixxx/src/library/rhythmbox/rhythmboxfeature.cpp (+365/-50) mixxx/src/library/rhythmbox/rhythmboxfeature.h (+38/-12) mixxx/src/library/rhythmbox/rhythmboxplaylistmodel.cpp (+169/-254) mixxx/src/library/rhythmbox/rhythmboxplaylistmodel.h (+32/-74) mixxx/src/library/rhythmbox/rhythmboxtrackmodel.cpp (+127/-167) mixxx/src/library/rhythmbox/rhythmboxtrackmodel.h (+41/-44) mixxx/src/library/sidebarmodel.cpp (+123/-100) mixxx/src/library/sidebarmodel.h (+8/-1) mixxx/src/library/trackcollection.cpp (+1/-1) mixxx/src/library/traktor/traktorfeature.cpp (+141/-101) mixxx/src/library/traktor/traktorfeature.h (+18/-10) mixxx/src/library/traktor/traktorplaylistmodel.cpp (+25/-28) mixxx/src/library/traktor/traktorplaylistmodel.h (+7/-7) mixxx/src/library/traktor/traktortablemodel.cpp (+17/-16) mixxx/src/library/traktor/traktortablemodel.h (+7/-6) mixxx/src/widget/wlibrarysidebar.cpp (+7/-0) mixxx/src/widget/wlibrarysidebar.h (+2/-0) |
||||
To merge this branch: | bzr merge lp:~mixxxdevelopers/mixxx/features_rhythmbox | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
RJ Skerry-Ryan | Approve | ||
Review via email: mp+45645@code.launchpad.net |
Commit message
Description of the change
This is a rewrite of the Rhythmbox feature which is sql-powered.
I have removed the files "abstractxmltra
RAFFI TEA (raffitea) wrote : | # |
Thanks RJ fort your review.
I have anticipated the issue with BaseSqlTableMod
I'll fix the problem soon.
- 2627. By Raffitea
-
Merging from trunk.
- 2628. By Raffitea
-
* Merging from trunk manually.
*Readded ProxyTrackModel. Thought it was useless but the revised BrowseFeature makes use of them. - 2629. By Raffitea
-
added setCaching(bool) method to BaseSqlModel.
disabled caching for iTunes, Traktor and Rhythmbox
-->disconnectes SIGNAL(trackChaged) - 2630. By Raffitea
-
* Added to worker thread to parse Rhythmbox XML.
- 2631. By Raffitea
-
* Change Rhythmbox title to 'Rhythmbox (loading)' when worker thread imports music collection.
- 2632. By Raffitea
-
Added a worker thread for iTunes parsing
- 2633. By Raffitea
-
Removed a comment
- 2634. By Raffitea
-
Added a worker thread for Traktor parsing.
RAFFI TEA (raffitea) wrote : | # |
Hey RJ,
I have addressed the problems. Can you confirm that your problems have gone?
From now on Mixxx uses a worker thread to parse iTunes, Traktor and Rhythmbox. This keeps the GUI responsive especially on large music collections. While the worker thread is active the sidebar item of the feature has '(loading)' appended. I have removed the 'activate' message boxes. They are not usefully at all especially when navigating through the sidebar with MIDI.
- 2635. By Raffitea
-
Make sure ThreadPool provides enough Threads slots
RJ Skerry-Ryan (rryan) wrote : | # |
Things are looking good. I'm confused about the thread-pool issues you ran into. I asked in #qt on FreeNode and they said that the GUI thread does not count as a thread in the global ThreadPool. I'm not sure what would be taking up that extra slot in the thread pool such that the importer would only run on Mixxx shutdown.
I think since you set the thread pool size to the same thing in different places, maybe we should add that to the main Mixxx startup sequence instead and just add comments there that indicate that it's related to these library features.
I ran into another issue, when I click Rhythmbox, it switches to 'Loading' and then when it's done the Rhythmbox view doesn't load. I experimented with adding a showTrackModel signal to Rhythmbox as if the view was activated after-load in trunk and that switched the track table, but the sidebar didn't show Rhythmbox as selected. I'm working on adding a fix for this in the branch now.
- 2636. By RJ Skerry-Ryan
-
Adding support to switch the sidebar selection after Rhythmbox/
iTunes/ Traktor models are finished loading. Update RhythmBox/ iTunes/ Traktor features to activate() themselves after loading. - 2637. By RJ Skerry-Ryan
-
Merging from lp:mixxx. Increment RB table version to 11 in schema.xml and min-version to 11 since the recent merge to trunk used version 10.
RJ Skerry-Ryan (rryan) wrote : | # |
OK, I fixed the issue with the selection not highlighting once the loading process is done. I tested RhythmBox but not iTunes/Traktor, though I don't see why the fixes I made for those wouldn't work.
RAFFI TEA (raffitea) wrote : | # |
> Things are looking good. I'm confused about the thread-pool issues you ran
> into. I asked in #qt on FreeNode and they said that the GUI thread does not
> count as a thread in the global ThreadPool. I'm not sure what would be taking
> up that extra slot in the thread pool such that the importer would only run on
> Mixxx shutdown.
I think I know the reason. The old "BrowseThread" was realized by QtConcurrent:
>
> I ran into another issue, when I click Rhythmbox, it switches to 'Loading' and
> then when it's done the Rhythmbox view doesn't load.
hmmm. It should work on. Is there a emit(showTrackM
I experimented with
> adding a showTrackModel signal to Rhythmbox as if the view was activated
> after-load in trunk and that switched the track table, but the sidebar didn't
> show Rhythmbox as selected. I'm working on adding a fix for this in the branch
> now.
RJ, this can be solved very quickly. Rather than resetting the model in onTrackCollecti
RJ Skerry-Ryan (rryan) wrote : | # |
Everything looks good -- merge away Tobias!
- 2638. By RJ Skerry-Ryan
-
Update comment
- 2639. By RJ Skerry-Ryan
-
Move Rhythmbox, iTunes, and Traktor feature files into their own sub-folders within mixxx/src/library
Preview Diff
1 | === modified file 'mixxx/build/depends.py' |
2 | --- mixxx/build/depends.py 2011-02-21 03:59:46 +0000 |
3 | +++ mixxx/build/depends.py 2011-02-21 23:55:30 +0000 |
4 | @@ -188,7 +188,7 @@ |
5 | '$QTDIR/include/QtWebKit', |
6 | '$QTDIR/include/Qt']) |
7 | |
8 | - # Set the rpath for linux/bsd/osx. |
9 | + # Set the rpath for linux/bsd/osx. |
10 | # This is not support on OS X before the 10.5 SDK. |
11 | using_104_sdk = (str(build.env["CCFLAGS"]).find("10.4") >= 0) |
12 | compiling_on_104 = False |
13 | @@ -451,20 +451,30 @@ |
14 | "library/preparelibrarytablemodel.cpp", |
15 | "library/browsetablemodel.cpp", |
16 | "library/missingtablemodel.cpp", |
17 | + |
18 | "library/proxytrackmodel.cpp", |
19 | - "library/abstractxmltrackmodel.cpp", |
20 | - "library/rhythmboxtrackmodel.cpp", |
21 | - "library/rhythmboxplaylistmodel.cpp", |
22 | - "library/itunestrackmodel.cpp", |
23 | - "library/itunesplaylistmodel.cpp", |
24 | + |
25 | + |
26 | "library/playlisttablemodel.cpp", |
27 | "library/libraryfeature.cpp", |
28 | "library/preparefeature.cpp", |
29 | "library/autodjfeature.cpp", |
30 | "library/mixxxlibraryfeature.cpp", |
31 | "library/playlistfeature.cpp", |
32 | - "library/rhythmboxfeature.cpp", |
33 | - "library/itunesfeature.cpp", |
34 | + |
35 | + # External Library Features |
36 | + "library/rhythmbox/rhythmboxfeature.cpp", |
37 | + "library/rhythmbox/rhythmboxtrackmodel.cpp", |
38 | + "library/rhythmbox/rhythmboxplaylistmodel.cpp", |
39 | + |
40 | + "library/itunes/itunesfeature.cpp", |
41 | + "library/itunes/itunestrackmodel.cpp", |
42 | + "library/itunes/itunesplaylistmodel.cpp", |
43 | + |
44 | + "library/traktor/traktorfeature.cpp", |
45 | + "library/traktor/traktortablemodel.cpp", |
46 | + "library/traktor/traktorplaylistmodel.cpp", |
47 | + |
48 | "library/browsefeature.cpp", |
49 | "library/cratefeature.cpp", |
50 | "library/sidebarmodel.cpp", |
51 | @@ -491,13 +501,10 @@ |
52 | "library/stardelegate.cpp", |
53 | "library/stareditor.cpp", |
54 | "audiotagger.cpp", |
55 | - |
56 | + |
57 | "library/treeitemmodel.cpp", |
58 | "library/treeitem.cpp", |
59 | "library/foldertreemodel.cpp", |
60 | - "library/traktorfeature.cpp", |
61 | - "library/traktortablemodel.cpp", |
62 | - "library/traktorplaylistmodel.cpp", |
63 | "library/browsethread.cpp", |
64 | |
65 | "xmlparse.cpp", |
66 | |
67 | === modified file 'mixxx/res/schema.xml' |
68 | --- mixxx/res/schema.xml 2011-01-20 19:50:26 +0000 |
69 | +++ mixxx/res/schema.xml 2011-02-21 23:55:30 +0000 |
70 | @@ -237,4 +237,33 @@ |
71 | ALTER TABLE playlists ADD COLUMN locked integer DEFAULT 0; |
72 | </sql> |
73 | </revision> |
74 | + <revision version="11" min_compatible="3"> |
75 | + <description> |
76 | + Tables for Rhythmbox library feature |
77 | + </description> |
78 | + <sql> |
79 | + CREATE TABLE IF NOT EXISTS rhythmbox_library ( |
80 | + id INTEGER primary key AUTOINCREMENT, |
81 | + artist varchar(48), title varchar(48), |
82 | + album varchar(48), year varchar(16), |
83 | + genre varchar(32), tracknumber varchar(3), |
84 | + location varchar(512) UNIQUE, |
85 | + comment varchar(60), |
86 | + duration integer, |
87 | + bitrate integer, |
88 | + bpm float, |
89 | + key varchar(6), |
90 | + rating integer |
91 | + ); |
92 | + CREATE TABLE IF NOT EXISTS rhythmbox_playlists ( |
93 | + id INTEGER primary key AUTOINCREMENT, |
94 | + name varchar(100) UNIQUE |
95 | + ); |
96 | + CREATE TABLE IF NOT EXISTS rhythmbox_playlist_tracks ( |
97 | + id INTEGER primary key AUTOINCREMENT, |
98 | + playlist_id INTEGER REFERENCES rhythmbox_playlist(id), |
99 | + track_id INTEGER REFERENCES rhythmbox_library(id) |
100 | + ); |
101 | + </sql> |
102 | + </revision> |
103 | </schema> |
104 | |
105 | === removed file 'mixxx/src/library/abstractxmltrackmodel.cpp' |
106 | --- mixxx/src/library/abstractxmltrackmodel.cpp 2010-10-28 06:41:43 +0000 |
107 | +++ mixxx/src/library/abstractxmltrackmodel.cpp 1970-01-01 00:00:00 +0000 |
108 | @@ -1,218 +0,0 @@ |
109 | -/*************************************************************************** |
110 | - abstractxmltrackmodel.h |
111 | - ------------------- |
112 | - begin : 8/23/2009 |
113 | - copyright : (C) 2009 Phillip Whelan |
114 | - email : pwhelan@mixxx.org |
115 | -***************************************************************************/ |
116 | - |
117 | -/*************************************************************************** |
118 | - * * |
119 | - * This program is free software; you can redistribute it and/or modify * |
120 | - * it under the terms of the GNU General Public License as published by * |
121 | - * the Free Software Foundation; either version 2 of the License, or * |
122 | - * (at your option) any later version. * |
123 | - * * |
124 | - ***************************************************************************/ |
125 | - |
126 | -/*************************************************************************** |
127 | - * * |
128 | - * This is the class implementation for the base utility class used for * |
129 | - * parsing external XML track and playlist databases. * |
130 | - * * |
131 | - ***************************************************************************/ |
132 | - |
133 | - |
134 | -#include <QtCore> |
135 | -#include <QtGui> |
136 | -#include <QtSql> |
137 | -#include <QtDebug> |
138 | -#include <QtXmlPatterns/QXmlQuery> |
139 | - |
140 | - |
141 | -#include "abstractxmltrackmodel.h" |
142 | -#include "xmlparse.h" |
143 | -#include "trackinfoobject.h" |
144 | -#include "defs.h" |
145 | - |
146 | - |
147 | -AbstractXmlTrackModel::AbstractXmlTrackModel(QString settingsNamespace) |
148 | - : TrackModel(QSqlDatabase::database("QSQLITE"), |
149 | - settingsNamespace) { |
150 | -} |
151 | - |
152 | -AbstractXmlTrackModel::~AbstractXmlTrackModel() |
153 | -{ |
154 | -} |
155 | - |
156 | -Qt::ItemFlags AbstractXmlTrackModel::flags ( const QModelIndex & index ) const |
157 | -{ |
158 | - Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); |
159 | - |
160 | - if (!index.isValid()) |
161 | - return Qt::ItemIsEnabled; |
162 | - |
163 | - defaultFlags |= Qt::ItemIsDragEnabled; |
164 | - |
165 | - return defaultFlags; |
166 | -} |
167 | - |
168 | -QMimeData* AbstractXmlTrackModel::mimeData(const QModelIndexList &indexes) const { |
169 | - QMimeData *mimeData = new QMimeData(); |
170 | - QList<QUrl> urls; |
171 | - |
172 | - //Ok, so the list of indexes we're given contains separates indexes for |
173 | - //each column, so even if only one row is selected, we'll have like 7 indexes. |
174 | - //We need to only count each row once: |
175 | - QList<int> rows; |
176 | - |
177 | - foreach (QModelIndex index, indexes) { |
178 | - if (index.isValid()) { |
179 | - if (!rows.contains(index.row())) { |
180 | - rows.push_back(index.row()); |
181 | - QUrl url = QUrl::fromLocalFile(getTrackLocation(index)); |
182 | - if (!url.isValid()) |
183 | - qDebug() << "ERROR invalid url\n"; |
184 | - else |
185 | - urls.append(url); |
186 | - } |
187 | - } |
188 | - } |
189 | - mimeData->setUrls(urls); |
190 | - return mimeData; |
191 | -} |
192 | - |
193 | - |
194 | -QVariant AbstractXmlTrackModel::data ( const QModelIndex & index, int role ) const |
195 | -{ |
196 | - if (!index.isValid()) |
197 | - return QVariant(); |
198 | - |
199 | - if (m_trackNodes.size() < index.row()) |
200 | - return QVariant(); |
201 | - |
202 | - QDomNode songNode = m_trackNodes.at(index.row()); |
203 | - |
204 | - // tooltips and the display role should be the same thing. |
205 | - if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { |
206 | - if ( index.column() > m_ColumnNames.size()) |
207 | - return QVariant(); |
208 | - |
209 | - return getTrackColumnData(songNode, index); |
210 | - } |
211 | - |
212 | - return QVariant(); |
213 | -} |
214 | - |
215 | -QVariant AbstractXmlTrackModel::headerData ( int section, Qt::Orientation orientation, int role ) const |
216 | -{ |
217 | - /* Only respond to requests for column header display names */ |
218 | - if ( role != Qt::DisplayRole ) |
219 | - return QVariant(); |
220 | - |
221 | - if (orientation == Qt::Horizontal) |
222 | - { |
223 | - if ( section > m_ColumnNames.size()) |
224 | - return QVariant(); |
225 | - |
226 | - return m_ColumnNames[section]; |
227 | - } |
228 | - |
229 | - return QVariant(); |
230 | -} |
231 | - |
232 | -int AbstractXmlTrackModel::rowCount ( const QModelIndex & parent ) const |
233 | -{ |
234 | - return m_trackNodes.count(); |
235 | -} |
236 | - |
237 | -int AbstractXmlTrackModel::columnCount(const QModelIndex& parent) const |
238 | -{ |
239 | - if (parent != QModelIndex()) //Some weird thing for table-based models. |
240 | - return 0; |
241 | - |
242 | - return m_ColumnNames.size(); |
243 | -} |
244 | - |
245 | -bool AbstractXmlTrackModel::addTrack(const QModelIndex& index, QString location) |
246 | -{ |
247 | - //Should do nothing... hmmm |
248 | - return false; |
249 | -} |
250 | - |
251 | -/** Removes a track from the library track collection. */ |
252 | -void AbstractXmlTrackModel::removeTrack(const QModelIndex& index) |
253 | -{ |
254 | - //Should do nothing... hmmm |
255 | -} |
256 | - |
257 | -void AbstractXmlTrackModel::removeTracks(const QModelIndexList& indices) |
258 | -{ |
259 | -} |
260 | - |
261 | -void AbstractXmlTrackModel::moveTrack(const QModelIndex& sourceIndex, |
262 | - const QModelIndex& destIndex) |
263 | -{ |
264 | - //Should do nothing... hmmm |
265 | -} |
266 | - |
267 | -QString AbstractXmlTrackModel::getTrackLocation(const QModelIndex& index) const |
268 | -{ |
269 | - TrackPointer track = getTrack(index); |
270 | - QString location = track->getLocation(); |
271 | - // track is auto-deleted |
272 | - return location; |
273 | -} |
274 | - |
275 | -TrackPointer AbstractXmlTrackModel::getTrack(const QModelIndex& index) const |
276 | -{ |
277 | - QDomNode songNode = m_trackNodes.at(index.row()); |
278 | - return parseTrackNode(songNode); |
279 | -} |
280 | - |
281 | -TrackPointer AbstractXmlTrackModel::getTrackByLocation(const QString& location) const |
282 | -{ |
283 | - if ( !m_mTracksByLocation.contains(location)) |
284 | - return TrackPointer(); |
285 | - |
286 | - QDomNode songNode = m_mTracksByLocation[location]; |
287 | - return parseTrackNode(songNode); |
288 | -} |
289 | - |
290 | -/* |
291 | -TrackPointer AbstractXmlTrackModel::parseTrackNode(QDomNode node) |
292 | -{ |
293 | - qDebug() << "Child class has not implemented parseTrackNode"; |
294 | - return NULL; |
295 | -} |
296 | -*/ |
297 | - |
298 | -void AbstractXmlTrackModel::search(const QString& searchText) |
299 | -{ |
300 | - m_currentSearch = searchText; |
301 | - // TODO(XXX) Implement searching |
302 | -} |
303 | - |
304 | -const QList<int>& AbstractXmlTrackModel::searchColumns() const { |
305 | - return m_searchColumns; |
306 | -} |
307 | - |
308 | -const QString AbstractXmlTrackModel::currentSearch() |
309 | -{ |
310 | - return m_currentSearch; |
311 | -} |
312 | - |
313 | -void AbstractXmlTrackModel::addColumnName(int index, QString name) |
314 | -{ |
315 | - m_ColumnNames.insert(index, name); |
316 | -} |
317 | - |
318 | -void AbstractXmlTrackModel::addSearchColumn(int index) { |
319 | - m_searchColumns.push_back(index); |
320 | -} |
321 | - |
322 | -TrackModel::CapabilitiesFlags AbstractXmlTrackModel::getCapabilities() const { |
323 | - return TRACKMODELCAPS_ADDTOPLAYLIST | TRACKMODELCAPS_ADDTOCRATE | |
324 | - TRACKMODELCAPS_ADDTOAUTODJ; |
325 | -} |
326 | - |
327 | |
328 | === removed file 'mixxx/src/library/abstractxmltrackmodel.h' |
329 | --- mixxx/src/library/abstractxmltrackmodel.h 2010-11-16 21:01:45 +0000 |
330 | +++ mixxx/src/library/abstractxmltrackmodel.h 1970-01-01 00:00:00 +0000 |
331 | @@ -1,100 +0,0 @@ |
332 | -/*************************************************************************** |
333 | - abstractxmltrackmodel.h |
334 | - ------------------- |
335 | - begin : 8/23/2009 |
336 | - copyright : (C) 2009 Phillip Whelan |
337 | - email : pwhelan@mixxx.org |
338 | -***************************************************************************/ |
339 | - |
340 | -/*************************************************************************** |
341 | - * * |
342 | - * This program is free software; you can redistribute it and/or modify * |
343 | - * it under the terms of the GNU General Public License as published by * |
344 | - * the Free Software Foundation; either version 2 of the License, or * |
345 | - * (at your option) any later version. * |
346 | - * * |
347 | - ***************************************************************************/ |
348 | - |
349 | -/*************************************************************************** |
350 | - * * |
351 | - * This is the class definition for a base utility class used for parsing * |
352 | - * external XML track and playlist databases. The abstract implementation * |
353 | - * includes helper methods and default implementations to help reduce code * |
354 | - * rewriting. * |
355 | - * * |
356 | - * This class also assumes that the constructor loads the track nodes as a * |
357 | - * QDomeNodesList. The Class must implement parseTrackNode to convert the * |
358 | - * QDomNode into a TrackInfoObject. * |
359 | - ***************************************************************************/ |
360 | - |
361 | - |
362 | -#ifndef TRACKMODELXML_H |
363 | -#define TRACKMODELXML_H |
364 | - |
365 | -#include <QtSql> |
366 | -#include <QtXml> |
367 | -#include "trackmodel.h" |
368 | - |
369 | -class AbstractXmlTrackModel : public QAbstractTableModel, public TrackModel |
370 | -{ |
371 | - Q_OBJECT |
372 | - public: |
373 | - AbstractXmlTrackModel(QString settingsNamespace); |
374 | - virtual ~AbstractXmlTrackModel(); |
375 | - |
376 | - //QAbstractTableModel stuff |
377 | - virtual Qt::ItemFlags flags ( const QModelIndex & index ) const; |
378 | - virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const; |
379 | - virtual QMimeData* mimeData(const QModelIndexList &indexes) const; |
380 | - virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; |
381 | - virtual int rowCount ( const QModelIndex & parent = QModelIndex() ) const; |
382 | - virtual int columnCount(const QModelIndex& parent) const; |
383 | - |
384 | - //Track Model stuff |
385 | - virtual TrackPointer getTrack(const QModelIndex& index) const; |
386 | - virtual QString getTrackLocation(const QModelIndex& index) const; |
387 | - virtual TrackPointer getTrackByLocation(const QString& location) const; |
388 | - virtual void search(const QString& searchText); |
389 | - virtual const QList<int>& searchColumns() const; |
390 | - virtual const QString currentSearch(); |
391 | - virtual bool isColumnInternal(int column) = 0; |
392 | - virtual bool isColumnHiddenByDefault(int column) = 0; |
393 | - virtual void removeTrack(const QModelIndex& index); |
394 | - virtual void removeTracks(const QModelIndexList& indices); |
395 | - virtual bool addTrack(const QModelIndex& index, QString location); |
396 | - virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex); |
397 | - virtual TrackModel::CapabilitiesFlags getCapabilities() const; |
398 | - |
399 | -public slots: |
400 | - |
401 | - |
402 | -signals: |
403 | - void startedLoading(); |
404 | - void progressLoading(QString path); |
405 | - void finishedLoading(); |
406 | - |
407 | -private: |
408 | - QList<QString> m_ColumnNames; |
409 | - |
410 | -protected: |
411 | - /* Implemented by AbstractXmlTrackModel implementations to parse the DOM Node into a TrackInfoObject */ |
412 | - virtual TrackPointer parseTrackNode(QDomNode node) const = 0; |
413 | - /* Implemented by AbstractXmlTrackModel implementations to return the data for song columns */ |
414 | - virtual QVariant getTrackColumnData(QDomNode node, const QModelIndex& index) const = 0; |
415 | - /* Called by AbstractXmlTrackModel implementations to enumerate their columns */ |
416 | - void addColumnName(int index, QString name); |
417 | - |
418 | - // Add a column to be searched when searching occurs |
419 | - void addSearchColumn(int index); |
420 | - |
421 | - /* The list of song nodes */ |
422 | - QDomNodeList m_trackNodes; |
423 | - /* A map of the song DOM nodes by track location */ |
424 | - QMap <QString, QDomNode> m_mTracksByLocation; |
425 | - // A list of columns that the implementation wants searched |
426 | - QList<int> m_searchColumns; |
427 | - // The most recent search executed. |
428 | - QString m_currentSearch; |
429 | -}; |
430 | - |
431 | -#endif |
432 | |
433 | === modified file 'mixxx/src/library/autodjfeature.cpp' |
434 | --- mixxx/src/library/autodjfeature.cpp 2010-12-23 18:55:54 +0000 |
435 | +++ mixxx/src/library/autodjfeature.cpp 2011-02-21 23:55:30 +0000 |
436 | @@ -6,7 +6,7 @@ |
437 | |
438 | #include "library/autodjfeature.h" |
439 | #include "library/playlisttablemodel.h" |
440 | -#include "library/proxytrackmodel.h" |
441 | + |
442 | #include "library/trackcollection.h" |
443 | #include "dlgautodj.h" |
444 | #include "widget/wlibrary.h" |
445 | |
446 | === modified file 'mixxx/src/library/basesqltablemodel.cpp' |
447 | --- mixxx/src/library/basesqltablemodel.cpp 2010-11-17 21:02:24 +0000 |
448 | +++ mixxx/src/library/basesqltablemodel.cpp 2011-02-21 23:55:30 +0000 |
449 | @@ -18,6 +18,7 @@ |
450 | m_trackDAO(m_pTrackCollection->getTrackDAO()) { |
451 | connect(&m_trackDAO, SIGNAL(trackChanged(int)), |
452 | this, SLOT(trackChanged(int))); |
453 | + m_isCachedModel = true; |
454 | m_iSortColumn = 0; |
455 | m_eSortOrder = Qt::AscendingOrder; |
456 | } |
457 | @@ -100,10 +101,13 @@ |
458 | int col = index.column(); |
459 | |
460 | Q_ASSERT(m_rowToTrackId.contains(row)); |
461 | - if (!m_rowToTrackId.contains(row)) { |
462 | + if (!m_isCachedModel // iTunes and Rhythmbox are not cached |
463 | + || !m_rowToTrackId.contains(row)) { |
464 | + //look-up data from the database directly |
465 | return QSqlTableModel::data(index, role); |
466 | } |
467 | - |
468 | + // This is only ececuted for cached models where |
469 | + // view and underlying database have diverged |
470 | int trackId = m_rowToTrackId[row]; |
471 | |
472 | /* |
473 | @@ -377,3 +381,10 @@ |
474 | { |
475 | return readWriteFlags(index); |
476 | } |
477 | +void BaseSqlTableModel::setCaching(bool isCachedModel){ |
478 | + m_isCachedModel = isCachedModel; |
479 | + if(!m_isCachedModel){ |
480 | + disconnect(&m_trackDAO, SIGNAL(trackChanged(int)), |
481 | + this, SLOT(trackChanged(int))); |
482 | + } |
483 | +} |
484 | |
485 | === modified file 'mixxx/src/library/basesqltablemodel.h' |
486 | --- mixxx/src/library/basesqltablemodel.h 2010-10-19 01:28:00 +0000 |
487 | +++ mixxx/src/library/basesqltablemodel.h 2011-02-21 23:55:30 +0000 |
488 | @@ -33,11 +33,14 @@ |
489 | protected: |
490 | virtual QString orderByClause() const; |
491 | virtual void initHeaderData(); |
492 | + void setCaching(bool isActive); |
493 | private slots: |
494 | void trackChanged(int trackId); |
495 | private: |
496 | QVariant getBaseValue(const QModelIndex& index, int role = Qt::DisplayRole) const; |
497 | |
498 | + |
499 | + bool m_isCachedModel; |
500 | QString m_qTableName; |
501 | int m_iSortColumn; |
502 | Qt::SortOrder m_eSortOrder; |
503 | |
504 | === modified file 'mixxx/src/library/cratefeature.h' |
505 | --- mixxx/src/library/cratefeature.h 2011-01-26 16:29:42 +0000 |
506 | +++ mixxx/src/library/cratefeature.h 2011-02-21 23:55:30 +0000 |
507 | @@ -7,7 +7,7 @@ |
508 | |
509 | #include "library/libraryfeature.h" |
510 | #include "library/cratetablemodel.h" |
511 | -#include "library/proxytrackmodel.h" |
512 | + |
513 | #include "treeitemmodel.h" |
514 | |
515 | class TrackCollection; |
516 | |
517 | === added directory 'mixxx/src/library/itunes' |
518 | === renamed file 'mixxx/src/library/itunesfeature.cpp' => 'mixxx/src/library/itunes/itunesfeature.cpp' |
519 | --- mixxx/src/library/itunesfeature.cpp 2011-02-11 18:36:31 +0000 |
520 | +++ mixxx/src/library/itunes/itunesfeature.cpp 2011-02-21 23:55:30 +0000 |
521 | @@ -5,10 +5,11 @@ |
522 | #include <QFileDialog> |
523 | #include <QMenu> |
524 | #include <QAction> |
525 | -#include "library/itunesfeature.h" |
526 | - |
527 | -#include "library/itunestrackmodel.h" |
528 | -#include "library/itunesplaylistmodel.h" |
529 | + |
530 | +#include "library/itunes/itunesfeature.h" |
531 | + |
532 | +#include "library/itunes/itunestrackmodel.h" |
533 | +#include "library/itunes/itunesplaylistmodel.h" |
534 | #include "library/dao/settingsdao.h" |
535 | |
536 | const QString ITunesFeature::ITDB_PATH_KEY = "mixxx.itunesfeature.itdbpath"; |
537 | @@ -16,13 +17,26 @@ |
538 | |
539 | ITunesFeature::ITunesFeature(QObject* parent, TrackCollection* pTrackCollection) |
540 | : LibraryFeature(parent), |
541 | - m_pTrackCollection(pTrackCollection), |
542 | - m_database(pTrackCollection->getDatabase()) { |
543 | + m_pTrackCollection(pTrackCollection) { |
544 | m_pITunesTrackModel = new ITunesTrackModel(this, m_pTrackCollection); |
545 | m_pITunesPlaylistModel = new ITunesPlaylistModel(this, m_pTrackCollection); |
546 | m_isActivated = false; |
547 | - |
548 | - m_rootItem = new TreeItem(); |
549 | + m_title = tr("iTunes"); |
550 | + |
551 | + if (!m_database.isOpen()) { |
552 | + m_database = QSqlDatabase::addDatabase("QSQLITE", "ITUNES_SCANNER"); |
553 | + m_database.setHostName("localhost"); |
554 | + m_database.setDatabaseName(MIXXX_DB_PATH); |
555 | + m_database.setUserName("mixxx"); |
556 | + m_database.setPassword("mixxx"); |
557 | + |
558 | + //Open the database connection in this thread. |
559 | + if (!m_database.open()) { |
560 | + qDebug() << "Failed to open database for iTunes scanner." << m_database.lastError(); |
561 | + } |
562 | + } |
563 | + |
564 | + connect(&m_future_watcher, SIGNAL(finished()), this, SLOT(onTrackCollectionLoaded())); |
565 | } |
566 | |
567 | ITunesFeature::~ITunesFeature() { |
568 | @@ -45,7 +59,7 @@ |
569 | |
570 | |
571 | QVariant ITunesFeature::title() { |
572 | - return tr("iTunes"); |
573 | + return m_title; |
574 | } |
575 | |
576 | QIcon ITunesFeature::getIcon() { |
577 | @@ -56,53 +70,53 @@ |
578 | activate(false); |
579 | } |
580 | |
581 | -void ITunesFeature::activate(bool forceReload, bool askToLoad /* = true */) { |
582 | +void ITunesFeature::activate(bool forceReload) { |
583 | //qDebug("ITunesFeature::activate()"); |
584 | |
585 | if (!m_isActivated || forceReload) { |
586 | - if (askToLoad && QMessageBox::question( |
587 | - NULL, |
588 | - tr("Load iTunes Library?"), |
589 | - tr("Would you like to load your iTunes library?"), |
590 | - QMessageBox::Ok, |
591 | - QMessageBox::Cancel) |
592 | - == QMessageBox::Cancel) { |
593 | - return; |
594 | - } |
595 | |
596 | // first, assume we should use the default |
597 | - QString dbfile = getiTunesMusicPath(); |
598 | - SettingsDAO settings(m_database); |
599 | + m_dbfile = getiTunesMusicPath(); |
600 | + |
601 | + SettingsDAO settings(m_pTrackCollection->getDatabase()); |
602 | QString dbSetting(settings.getValue(ITDB_PATH_KEY)); |
603 | // if a path exists in the database, use it |
604 | if (!dbSetting.isEmpty() && QFile::exists(dbSetting)) { |
605 | - dbfile = dbSetting; |
606 | + m_dbfile = dbSetting; |
607 | } |
608 | // if the path we got between the default and the database doesn't |
609 | // exist, ask for a new one and use/save it if it exists |
610 | - if (!QFile::exists(dbfile)) { |
611 | - dbfile = QFileDialog::getOpenFileName(NULL, |
612 | + if (!QFile::exists(m_dbfile)) { |
613 | + m_dbfile = QFileDialog::getOpenFileName(NULL, |
614 | tr("Select your iTunes library"), |
615 | QDir::homePath(), "*.xml"); |
616 | - if (dbfile.isEmpty() || !QFile::exists(dbfile)) { |
617 | + if (m_dbfile.isEmpty() || !QFile::exists(m_dbfile)) { |
618 | return; |
619 | } |
620 | - settings.setValue(ITDB_PATH_KEY, dbfile); |
621 | - } |
622 | - if (importLibrary(dbfile)) { |
623 | - m_isActivated = true; |
624 | - } else { |
625 | - QMessageBox::warning( |
626 | - NULL, |
627 | - tr("Error Loading iTunes Library"), |
628 | - tr("There was an error loading your iTunes library. Some of " |
629 | - "your iTunes tracks or playlists may not have loaded.")); |
630 | - } |
631 | - //set the root item for the childmodel. |
632 | - m_childModel.setRootItem(m_rootItem); |
633 | + settings.setValue(ITDB_PATH_KEY, m_dbfile); |
634 | + } |
635 | + m_isActivated = true; |
636 | + /* Ususally the maximum number of threads |
637 | + * is > 2 depending on the CPU cores |
638 | + * Unfortunately, within VirtualBox |
639 | + * the maximum number of allowed threads |
640 | + * is 1 at all times We'll need to increase |
641 | + * the number to > 1, otherwise importing the music collection |
642 | + * takes place when the GUI threads terminates, i.e., on |
643 | + * Mixxx shutdown. |
644 | + */ |
645 | + QThreadPool::globalInstance()->setMaxThreadCount(4); //Tobias decided to use 4 |
646 | + // Let a worker thread do the XML parsing |
647 | + m_future = QtConcurrent::run(this, &ITunesFeature::importLibrary); |
648 | + m_future_watcher.setFuture(m_future); |
649 | + m_title = tr("iTunes (loading)"); |
650 | + //calls a slot in the sidebar model such that 'iTunes (isLoading)' is displayed. |
651 | + emit (featureIsLoading(this)); |
652 | + } |
653 | + else{ |
654 | + emit(showTrackModel(m_pITunesTrackModel)); |
655 | } |
656 | |
657 | - emit(showTrackModel(m_pITunesTrackModel)); |
658 | } |
659 | |
660 | void ITunesFeature::activateChild(const QModelIndex& index) { |
661 | @@ -127,7 +141,7 @@ |
662 | if (chosen == &useDefault) { |
663 | SettingsDAO settings(m_database); |
664 | settings.setValue(ITDB_PATH_KEY, QString()); |
665 | - activate(true, false); // clears tables before parsing |
666 | + activate(true); // clears tables before parsing |
667 | } else if (chosen == &chooseNew) { |
668 | SettingsDAO settings(m_database); |
669 | QString dbfile = QFileDialog::getOpenFileName(NULL, |
670 | @@ -137,7 +151,7 @@ |
671 | return; |
672 | } |
673 | settings.setValue(ITDB_PATH_KEY, dbfile); |
674 | - activate(true, false); // clears tables before parsing |
675 | + activate(true); // clears tables before parsing |
676 | } |
677 | } |
678 | |
679 | @@ -174,7 +188,15 @@ |
680 | qDebug() << "ITunesLibrary=[" << musicFolder << "]"; |
681 | return musicFolder; |
682 | } |
683 | -bool ITunesFeature::importLibrary(QString file) { |
684 | +/* |
685 | + * This method is executed in a separate thread |
686 | + * via QtConcurrent::run |
687 | + */ |
688 | +TreeItem* ITunesFeature::importLibrary() { |
689 | + //Give thread a low priority |
690 | + QThread* thisThread = QThread::currentThread(); |
691 | + thisThread->setPriority(QThread::LowestPriority); |
692 | + |
693 | //Delete all table entries of iTunes feature |
694 | m_database.transaction(); |
695 | clearTable("itunes_playlist_tracks"); |
696 | @@ -182,28 +204,27 @@ |
697 | clearTable("itunes_playlists"); |
698 | m_database.commit(); |
699 | |
700 | - qDebug() << "Import iTunes library"; |
701 | + qDebug() << "ITunesFeature::importLibrary() "; |
702 | + |
703 | |
704 | m_database.transaction(); |
705 | |
706 | //Parse iTunes XML file using SAX (for performance) |
707 | - QFile itunes_file(file); |
708 | + QFile itunes_file(m_dbfile); |
709 | if (!itunes_file.open(QIODevice::ReadOnly | QIODevice::Text)) { |
710 | qDebug() << "Cannot open iTunes music collection"; |
711 | return false; |
712 | } |
713 | QXmlStreamReader xml(&itunes_file); |
714 | - |
715 | + TreeItem* playlist_root = NULL; |
716 | while (!xml.atEnd()) { |
717 | xml.readNext(); |
718 | if (xml.isStartElement()) { |
719 | if (xml.name() == "key") { |
720 | if (xml.readElementText() == "Tracks") { |
721 | parseTracks(xml); |
722 | - qDebug() << "Finished Parsing TrackList"; |
723 | - parsePlaylists(xml); |
724 | - qDebug() << "Finished Parsing Playlists"; |
725 | - } |
726 | + playlist_root = parsePlaylists(xml); |
727 | + } |
728 | } |
729 | } |
730 | } |
731 | @@ -213,15 +234,17 @@ |
732 | // Even if an error occured, commit the transaction. The file may have been |
733 | // half-parsed. |
734 | m_database.commit(); |
735 | - m_pITunesTrackModel->select(); |
736 | |
737 | if (xml.hasError()) { |
738 | // do error handling |
739 | qDebug() << "Cannot process iTunes music collection"; |
740 | qDebug() << "XML ERROR: " << xml.errorString(); |
741 | + if(playlist_root) |
742 | + delete playlist_root; |
743 | + playlist_root = NULL; |
744 | return false; |
745 | } |
746 | - return true; |
747 | + return playlist_root; |
748 | } |
749 | |
750 | void ITunesFeature::parseTracks(QXmlStreamReader &xml) { |
751 | @@ -238,7 +261,7 @@ |
752 | bool in_container_dictionary = false; |
753 | bool in_track_dictionary = false; |
754 | |
755 | - qDebug() << "Parse Tracks"; |
756 | + qDebug() << "Parse iTunes music collection"; |
757 | |
758 | //read all sunsequent <dict> until we reach the closing ENTRY tag |
759 | while (!xml.atEnd()) { |
760 | @@ -399,9 +422,9 @@ |
761 | } |
762 | } |
763 | |
764 | -void ITunesFeature::parsePlaylists(QXmlStreamReader &xml) { |
765 | - qDebug() << "Parse Playlists"; |
766 | - |
767 | +TreeItem* ITunesFeature::parsePlaylists(QXmlStreamReader &xml) { |
768 | + qDebug() << "Parse iTunes playlists"; |
769 | + TreeItem* rootItem = new TreeItem(); |
770 | QSqlQuery query_insert_to_playlists(m_database); |
771 | query_insert_to_playlists.prepare("INSERT INTO itunes_playlists (id, name) " |
772 | "VALUES (:id, :name)"); |
773 | @@ -414,7 +437,10 @@ |
774 | xml.readNext(); |
775 | //We process and iterate the <dict> tags holding playlist summary information here |
776 | if (xml.isStartElement() && xml.name() == "dict") { |
777 | - parsePlaylist(xml, query_insert_to_playlists, query_insert_to_playlist_tracks); |
778 | + parsePlaylist(xml, |
779 | + query_insert_to_playlists, |
780 | + query_insert_to_playlist_tracks, |
781 | + rootItem); |
782 | continue; |
783 | } |
784 | if (xml.isEndElement()) { |
785 | @@ -422,6 +448,7 @@ |
786 | break; |
787 | } |
788 | } |
789 | + return rootItem; |
790 | } |
791 | |
792 | bool ITunesFeature::readNextStartElement(QXmlStreamReader& xml) { |
793 | @@ -436,7 +463,7 @@ |
794 | } |
795 | |
796 | void ITunesFeature::parsePlaylist(QXmlStreamReader &xml, QSqlQuery &query_insert_to_playlists, |
797 | - QSqlQuery &query_insert_to_playlist_tracks) { |
798 | + QSqlQuery &query_insert_to_playlist_tracks, TreeItem* root) { |
799 | //qDebug() << "Parse Playlist"; |
800 | |
801 | QString playlistname; |
802 | @@ -493,8 +520,8 @@ |
803 | return; |
804 | } |
805 | //append the playlist to the child model |
806 | - TreeItem *item = new TreeItem(playlistname, playlistname, this, m_rootItem); |
807 | - m_rootItem->appendChild(item); |
808 | + TreeItem *item = new TreeItem(playlistname, playlistname, this, root); |
809 | + root->appendChild(item); |
810 | |
811 | } |
812 | /* |
813 | @@ -539,3 +566,24 @@ |
814 | else |
815 | qDebug() << "iTunes table entries of '" << table_name <<"' have been cleared."; |
816 | } |
817 | +void ITunesFeature::onTrackCollectionLoaded(){ |
818 | + TreeItem* root = m_future.result(); |
819 | + if(root){ |
820 | + m_childModel.setRootItem(root); |
821 | + m_pITunesTrackModel->select(); |
822 | + emit(showTrackModel(m_pITunesTrackModel)); |
823 | + qDebug() << "Itunes library loaded: success"; |
824 | + } |
825 | + else{ |
826 | + QMessageBox::warning( |
827 | + NULL, |
828 | + tr("Error Loading iTunes Library"), |
829 | + tr("There was an error loading your iTunes library. Some of " |
830 | + "your iTunes tracks or playlists may not have loaded.")); |
831 | + } |
832 | + //calls a slot in the sidebarmodel such that 'isLoading' is removed from the feature title. |
833 | + m_title = tr("iTunes"); |
834 | + emit(featureLoadingFinished(this)); |
835 | + activate(); |
836 | +} |
837 | + |
838 | |
839 | === renamed file 'mixxx/src/library/itunesfeature.h' => 'mixxx/src/library/itunes/itunesfeature.h' |
840 | --- mixxx/src/library/itunesfeature.h 2011-02-05 07:35:15 +0000 |
841 | +++ mixxx/src/library/itunes/itunesfeature.h 2011-02-21 23:55:30 +0000 |
842 | @@ -1,16 +1,19 @@ |
843 | -// rhythmboxfeature.h |
844 | +// itunesfeaturefeature.h |
845 | // Created 8/23/2009 by RJ Ryan (rryan@mit.edu) |
846 | |
847 | #ifndef ITUNESFEATURE_H |
848 | -#define ITUNESBOXFEATURE_H |
849 | +#define ITUNESFEATURE_H |
850 | |
851 | #include <QStringListModel> |
852 | #include <QtSql> |
853 | +#include <QFuture> |
854 | +#include <QtConcurrentRun> |
855 | +#include <QFutureWatcher> |
856 | |
857 | #include "library/libraryfeature.h" |
858 | #include "library/trackcollection.h" |
859 | -#include "treeitemmodel.h" |
860 | -#include "treeitem.h" |
861 | +#include "library/treeitemmodel.h" |
862 | +#include "library/treeitem.h" |
863 | |
864 | //class ITunesPlaylistModel; |
865 | class ITunesTrackModel; |
866 | @@ -36,18 +39,20 @@ |
867 | |
868 | public slots: |
869 | void activate(); |
870 | - void activate(bool forceReload, bool askToLoad = true); |
871 | + void activate(bool forceReload); |
872 | void activateChild(const QModelIndex& index); |
873 | void onRightClick(const QPoint& globalPos); |
874 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
875 | + void onTrackCollectionLoaded(); |
876 | |
877 | private: |
878 | static QString getiTunesMusicPath(); |
879 | - bool importLibrary(QString file); |
880 | + //returns the invisible rootItem for the sidebar model |
881 | + TreeItem* importLibrary(); |
882 | void parseTracks(QXmlStreamReader &xml); |
883 | void parseTrack(QXmlStreamReader &xml, QSqlQuery &query); |
884 | - void parsePlaylists(QXmlStreamReader &xml); |
885 | - void parsePlaylist(QXmlStreamReader &xml, QSqlQuery &query1, QSqlQuery &query2); |
886 | + TreeItem* parsePlaylists(QXmlStreamReader &xml); |
887 | + void parsePlaylist(QXmlStreamReader &xml, QSqlQuery &query1, QSqlQuery &query2, TreeItem*); |
888 | void clearTable(QString table_name); |
889 | bool readNextStartElement(QXmlStreamReader& xml); |
890 | |
891 | @@ -56,10 +61,14 @@ |
892 | TreeItemModel m_childModel; |
893 | QStringList m_playlists; |
894 | TrackCollection* m_pTrackCollection; |
895 | - QSqlDatabase &m_database; |
896 | + //a new DB connection for the worker thread |
897 | + QSqlDatabase m_database; |
898 | bool m_isActivated; |
899 | - //The root of the childmodel |
900 | - TreeItem *m_rootItem; |
901 | + QString m_dbfile; |
902 | + |
903 | + QFutureWatcher<TreeItem*> m_future_watcher; |
904 | + QFuture<TreeItem*> m_future; |
905 | + QString m_title; |
906 | |
907 | static const QString ITDB_PATH_KEY; |
908 | }; |
909 | |
910 | === renamed file 'mixxx/src/library/itunesplaylistmodel.cpp' => 'mixxx/src/library/itunes/itunesplaylistmodel.cpp' |
911 | --- mixxx/src/library/itunesplaylistmodel.cpp 2010-12-03 19:07:40 +0000 |
912 | +++ mixxx/src/library/itunes/itunesplaylistmodel.cpp 2011-02-21 23:55:30 +0000 |
913 | @@ -2,7 +2,7 @@ |
914 | #include <QtGui> |
915 | #include <QtSql> |
916 | #include "library/trackcollection.h" |
917 | -#include "library/itunesplaylistmodel.h" |
918 | +#include "library/itunes/itunesplaylistmodel.h" |
919 | |
920 | #include "mixxxutils.cpp" |
921 | |
922 | @@ -15,6 +15,7 @@ |
923 | m_database(m_pTrackCollection->getDatabase()) |
924 | { |
925 | connect(this, SIGNAL(doSearch(const QString&)), this, SLOT(slotSearch(const QString&))); |
926 | + setCaching(false); |
927 | } |
928 | |
929 | ITunesPlaylistModel::~ITunesPlaylistModel() { |
930 | @@ -131,7 +132,7 @@ |
931 | } |
932 | |
933 | TrackModel::CapabilitiesFlags ITunesPlaylistModel::getCapabilities() const { |
934 | - return NULL; |
935 | + return TRACKMODELCAPS_NONE; |
936 | } |
937 | |
938 | Qt::ItemFlags ITunesPlaylistModel::flags(const QModelIndex &index) const { |
939 | |
940 | === renamed file 'mixxx/src/library/itunesplaylistmodel.h' => 'mixxx/src/library/itunes/itunesplaylistmodel.h' |
941 | --- mixxx/src/library/itunesplaylistmodel.h 2010-11-21 05:05:14 +0000 |
942 | +++ mixxx/src/library/itunes/itunesplaylistmodel.h 2011-02-21 23:55:30 +0000 |
943 | @@ -4,7 +4,7 @@ |
944 | #include <QtSql> |
945 | #include <QItemDelegate> |
946 | #include <QtCore> |
947 | -#include "trackmodel.h" |
948 | +#include "library/trackmodel.h" |
949 | #include "library/basesqltablemodel.h" |
950 | #include "library/librarytablemodel.h" |
951 | #include "library/dao/playlistdao.h" |
952 | |
953 | === renamed file 'mixxx/src/library/itunestrackmodel.cpp' => 'mixxx/src/library/itunes/itunestrackmodel.cpp' |
954 | --- mixxx/src/library/itunestrackmodel.cpp 2010-12-03 19:07:40 +0000 |
955 | +++ mixxx/src/library/itunes/itunestrackmodel.cpp 2011-02-21 23:55:30 +0000 |
956 | @@ -2,7 +2,7 @@ |
957 | #include <QtGui> |
958 | #include <QtSql> |
959 | #include "library/trackcollection.h" |
960 | -#include "library/itunestrackmodel.h" |
961 | +#include "library/itunes/itunestrackmodel.h" |
962 | |
963 | #include "mixxxutils.cpp" |
964 | |
965 | @@ -15,6 +15,7 @@ |
966 | m_database(m_pTrackCollection->getDatabase()) { |
967 | connect(this, SIGNAL(doSearch(const QString&)), this, SLOT(slotSearch(const QString&))); |
968 | setTable("itunes_library"); |
969 | + setCaching(false); |
970 | initHeaderData(); |
971 | } |
972 | |
973 | @@ -128,7 +129,7 @@ |
974 | } |
975 | |
976 | TrackModel::CapabilitiesFlags ITunesTrackModel::getCapabilities() const { |
977 | - return NULL; |
978 | + return TRACKMODELCAPS_NONE; |
979 | } |
980 | |
981 | Qt::ItemFlags ITunesTrackModel::flags(const QModelIndex &index) const { |
982 | |
983 | === renamed file 'mixxx/src/library/itunestrackmodel.h' => 'mixxx/src/library/itunes/itunestrackmodel.h' |
984 | --- mixxx/src/library/itunestrackmodel.h 2010-11-21 05:05:14 +0000 |
985 | +++ mixxx/src/library/itunes/itunestrackmodel.h 2011-02-21 23:55:30 +0000 |
986 | @@ -4,7 +4,7 @@ |
987 | #include <QtSql> |
988 | #include <QItemDelegate> |
989 | #include <QtCore> |
990 | -#include "trackmodel.h" |
991 | +#include "library/trackmodel.h" |
992 | #include "library/basesqltablemodel.h" |
993 | #include "library/librarytablemodel.h" |
994 | #include "library/dao/playlistdao.h" |
995 | |
996 | === modified file 'mixxx/src/library/library.cpp' |
997 | --- mixxx/src/library/library.cpp 2011-02-12 19:25:05 +0000 |
998 | +++ mixxx/src/library/library.cpp 2011-02-21 23:55:30 +0000 |
999 | @@ -11,14 +11,14 @@ |
1000 | #include "library/trackmodel.h" |
1001 | #include "library/browsefeature.h" |
1002 | #include "library/cratefeature.h" |
1003 | -#include "library/rhythmboxfeature.h" |
1004 | -#include "library/itunesfeature.h" |
1005 | +#include "library/rhythmbox/rhythmboxfeature.h" |
1006 | +#include "library/itunes/itunesfeature.h" |
1007 | #include "library/mixxxlibraryfeature.h" |
1008 | #include "library/autodjfeature.h" |
1009 | #include "library/playlistfeature.h" |
1010 | #include "library/preparefeature.h" |
1011 | #include "library/promotracksfeature.h" |
1012 | -#include "library/traktorfeature.h" |
1013 | +#include "library/traktor/traktorfeature.h" |
1014 | |
1015 | #include "widget/wtracktableview.h" |
1016 | #include "widget/wlibrary.h" |
1017 | @@ -62,7 +62,7 @@ |
1018 | //messagebox popup when you select them. (This forces you to reach for your |
1019 | //mouse or keyboard if you're using MIDI control and you scroll through them...) |
1020 | if (RhythmboxFeature::isSupported()) |
1021 | - addFeature(new RhythmboxFeature(this)); |
1022 | + addFeature(new RhythmboxFeature(this, m_pTrackCollection)); |
1023 | if (ITunesFeature::isSupported()) |
1024 | addFeature(new ITunesFeature(this, m_pTrackCollection)); |
1025 | if (TraktorFeature::isSupported()) |
1026 | @@ -116,6 +116,8 @@ |
1027 | |
1028 | // Setup the sources view |
1029 | pSidebarWidget->setModel(m_pSidebarModel); |
1030 | + connect(m_pSidebarModel, SIGNAL(selectIndex(const QModelIndex&)), |
1031 | + pSidebarWidget, SLOT(selectIndex(const QModelIndex&))); |
1032 | connect(pSidebarWidget, SIGNAL(pressed(const QModelIndex&)), |
1033 | m_pSidebarModel, SLOT(clicked(const QModelIndex&))); |
1034 | // Lazy model: Let triange symbol increment the model |
1035 | |
1036 | === modified file 'mixxx/src/library/libraryfeature.cpp' |
1037 | --- mixxx/src/library/libraryfeature.cpp 2009-10-24 09:07:58 +0000 |
1038 | +++ mixxx/src/library/libraryfeature.cpp 2011-02-21 23:55:30 +0000 |
1039 | @@ -8,3 +8,4 @@ |
1040 | LibraryFeature::LibraryFeature(QObject* parent) |
1041 | : QObject(parent) { |
1042 | } |
1043 | + |
1044 | |
1045 | === modified file 'mixxx/src/library/libraryfeature.h' |
1046 | --- mixxx/src/library/libraryfeature.h 2010-12-23 18:55:54 +0000 |
1047 | +++ mixxx/src/library/libraryfeature.h 2011-02-21 23:55:30 +0000 |
1048 | @@ -53,6 +53,10 @@ |
1049 | void loadTrack(TrackPointer pTrack); |
1050 | void loadTrackToPlayer(TrackPointer pTrack, QString group); |
1051 | void restoreSearch(const QString&); |
1052 | + void featureIsLoading(LibraryFeature*); |
1053 | + void featureLoadingFinished(LibraryFeature*s); |
1054 | + |
1055 | + |
1056 | }; |
1057 | |
1058 | #endif /* LIBRARYFEATURE_H */ |
1059 | |
1060 | === modified file 'mixxx/src/library/mixxxlibraryfeature.cpp' |
1061 | --- mixxx/src/library/mixxxlibraryfeature.cpp 2011-01-13 18:40:56 +0000 |
1062 | +++ mixxx/src/library/mixxxlibraryfeature.cpp 2011-02-21 23:55:30 +0000 |
1063 | @@ -7,7 +7,7 @@ |
1064 | |
1065 | #include "library/librarytablemodel.h" |
1066 | #include "library/missingtablemodel.h" |
1067 | -#include "library/proxytrackmodel.h" |
1068 | + |
1069 | #include "library/trackcollection.h" |
1070 | #include "treeitem.h" |
1071 | |
1072 | |
1073 | === modified file 'mixxx/src/library/promotracksfeature.cpp' |
1074 | --- mixxx/src/library/promotracksfeature.cpp 2010-12-23 18:55:54 +0000 |
1075 | +++ mixxx/src/library/promotracksfeature.cpp 2011-02-21 23:55:30 +0000 |
1076 | @@ -21,7 +21,6 @@ |
1077 | #include "library/promotracksfeature.h" |
1078 | #include "library/bundledsongswebview.h" |
1079 | #include "library/featuredartistswebview.h" |
1080 | -#include "library/proxytrackmodel.h" |
1081 | #include "library/trackcollection.h" |
1082 | #include "library/dao/cratedao.h" |
1083 | #include "trackinfoobject.h" |
1084 | |
1085 | === added file 'mixxx/src/library/proxytrackmodel.cpp' |
1086 | --- mixxx/src/library/proxytrackmodel.cpp 1970-01-01 00:00:00 +0000 |
1087 | +++ mixxx/src/library/proxytrackmodel.cpp 2011-02-21 23:55:30 +0000 |
1088 | @@ -0,0 +1,130 @@ |
1089 | +// proxytrackmodel.cpp |
1090 | +// Created 10/22/2009 by RJ Ryan (rryan@mit.edu) |
1091 | + |
1092 | +#include <QtCore> |
1093 | +#include <QVariant> |
1094 | + |
1095 | +#include "library/proxytrackmodel.h" |
1096 | + |
1097 | +ProxyTrackModel::ProxyTrackModel(QAbstractItemModel* pTrackModel, |
1098 | + bool bHandleSearches) |
1099 | + // ProxyTrackModel proxies settings requests to the composed TrackModel, |
1100 | + // don't initialize its TrackModel with valid parameters. |
1101 | + : TrackModel(QSqlDatabase(), ""), |
1102 | + m_bHandleSearches(bHandleSearches) { |
1103 | + m_pTrackModel = dynamic_cast<TrackModel*>(pTrackModel); |
1104 | + Q_ASSERT(m_pTrackModel && pTrackModel); |
1105 | + setSourceModel(pTrackModel); |
1106 | +} |
1107 | + |
1108 | +ProxyTrackModel::~ProxyTrackModel() { |
1109 | +} |
1110 | + |
1111 | +TrackPointer ProxyTrackModel::getTrack(const QModelIndex& index) const { |
1112 | + QModelIndex indexSource = mapToSource(index); |
1113 | + return m_pTrackModel->getTrack(indexSource); |
1114 | +} |
1115 | + |
1116 | +QString ProxyTrackModel::getTrackLocation(const QModelIndex& index) const { |
1117 | + QModelIndex indexSource = mapToSource(index); |
1118 | + return m_pTrackModel->getTrackLocation(indexSource); |
1119 | +} |
1120 | + |
1121 | +void ProxyTrackModel::search(const QString& searchText) { |
1122 | + if (m_bHandleSearches) { |
1123 | + m_currentSearch = searchText; |
1124 | + setFilterFixedString(searchText); |
1125 | + } else { |
1126 | + m_pTrackModel->search(searchText); |
1127 | + } |
1128 | +} |
1129 | + |
1130 | +const QString ProxyTrackModel::currentSearch() { |
1131 | + if (m_bHandleSearches) { |
1132 | + return m_currentSearch; |
1133 | + } |
1134 | + return m_pTrackModel->currentSearch(); |
1135 | +} |
1136 | + |
1137 | +bool ProxyTrackModel::isColumnInternal(int column) { |
1138 | + return m_pTrackModel->isColumnInternal(column); |
1139 | +} |
1140 | + |
1141 | +bool ProxyTrackModel::isColumnHiddenByDefault(int column) { |
1142 | + return m_pTrackModel->isColumnHiddenByDefault(column); |
1143 | +} |
1144 | + |
1145 | + |
1146 | +void ProxyTrackModel::removeTrack(const QModelIndex& index) { |
1147 | + QModelIndex indexSource = mapToSource(index); |
1148 | + m_pTrackModel->removeTrack(indexSource); |
1149 | +} |
1150 | + |
1151 | +void ProxyTrackModel::removeTracks(const QModelIndexList& indices) { |
1152 | + QModelIndexList translatedList; |
1153 | + foreach (QModelIndex index, indices) { |
1154 | + QModelIndex indexSource = mapToSource(index); |
1155 | + translatedList.append(indexSource); |
1156 | + } |
1157 | + m_pTrackModel->removeTracks(translatedList); |
1158 | +} |
1159 | + |
1160 | +bool ProxyTrackModel::addTrack(const QModelIndex& index, QString location) { |
1161 | + QModelIndex indexSource = mapToSource(index); |
1162 | + return m_pTrackModel->addTrack(indexSource, location); |
1163 | +} |
1164 | + |
1165 | +void ProxyTrackModel::moveTrack(const QModelIndex& sourceIndex, |
1166 | + const QModelIndex& destIndex) { |
1167 | + QModelIndex sourceIndexSource = mapToSource(sourceIndex); |
1168 | + QModelIndex destIndexSource = mapToSource(destIndex); |
1169 | + m_pTrackModel->moveTrack(sourceIndexSource, destIndexSource); |
1170 | +} |
1171 | + |
1172 | +QItemDelegate* ProxyTrackModel::delegateForColumn(const int i) { |
1173 | + return m_pTrackModel->delegateForColumn(i); |
1174 | +} |
1175 | + |
1176 | +TrackModel::CapabilitiesFlags ProxyTrackModel::getCapabilities() const { |
1177 | + return m_pTrackModel->getCapabilities(); |
1178 | +} |
1179 | + |
1180 | +bool ProxyTrackModel::filterAcceptsRow(int sourceRow, |
1181 | + const QModelIndex& sourceParent) const { |
1182 | + |
1183 | + if (!m_bHandleSearches) |
1184 | + return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); |
1185 | + |
1186 | + const QList<int>& filterColumns = m_pTrackModel->searchColumns(); |
1187 | + QAbstractItemModel* itemModel = |
1188 | + dynamic_cast<QAbstractItemModel*>(m_pTrackModel); |
1189 | + bool rowMatches = false; |
1190 | + |
1191 | + QRegExp filter = filterRegExp(); |
1192 | + QListIterator<int> iter(filterColumns); |
1193 | + |
1194 | + while (!rowMatches && iter.hasNext()) { |
1195 | + int i = iter.next(); |
1196 | + QModelIndex index = itemModel->index(sourceRow, i, sourceParent); |
1197 | + QVariant data = itemModel->data(index); |
1198 | + if (qVariantCanConvert<QString>(data)) { |
1199 | + QString strData = qVariantValue<QString>(data); |
1200 | + if (strData.contains(filter)) |
1201 | + rowMatches = true; |
1202 | + } |
1203 | + } |
1204 | + |
1205 | + return rowMatches; |
1206 | +} |
1207 | + |
1208 | +QString ProxyTrackModel::getModelSetting(QString name) { |
1209 | + if (!m_pTrackModel) |
1210 | + return QString(); |
1211 | + return m_pTrackModel->getModelSetting(name); |
1212 | +} |
1213 | + |
1214 | +bool ProxyTrackModel::setModelSetting(QString name, QVariant value) { |
1215 | + if (!m_pTrackModel) |
1216 | + return false; |
1217 | + return m_pTrackModel->setModelSetting(name, value); |
1218 | +} |
1219 | |
1220 | === removed file 'mixxx/src/library/proxytrackmodel.cpp' |
1221 | --- mixxx/src/library/proxytrackmodel.cpp 2010-11-16 21:01:45 +0000 |
1222 | +++ mixxx/src/library/proxytrackmodel.cpp 1970-01-01 00:00:00 +0000 |
1223 | @@ -1,130 +0,0 @@ |
1224 | -// proxytrackmodel.cpp |
1225 | -// Created 10/22/2009 by RJ Ryan (rryan@mit.edu) |
1226 | - |
1227 | -#include <QtCore> |
1228 | -#include <QVariant> |
1229 | - |
1230 | -#include "library/proxytrackmodel.h" |
1231 | - |
1232 | -ProxyTrackModel::ProxyTrackModel(QAbstractItemModel* pTrackModel, |
1233 | - bool bHandleSearches) |
1234 | - // ProxyTrackModel proxies settings requests to the composed TrackModel, |
1235 | - // don't initialize its TrackModel with valid parameters. |
1236 | - : TrackModel(QSqlDatabase(), ""), |
1237 | - m_bHandleSearches(bHandleSearches) { |
1238 | - m_pTrackModel = dynamic_cast<TrackModel*>(pTrackModel); |
1239 | - Q_ASSERT(m_pTrackModel && pTrackModel); |
1240 | - setSourceModel(pTrackModel); |
1241 | -} |
1242 | - |
1243 | -ProxyTrackModel::~ProxyTrackModel() { |
1244 | -} |
1245 | - |
1246 | -TrackPointer ProxyTrackModel::getTrack(const QModelIndex& index) const { |
1247 | - QModelIndex indexSource = mapToSource(index); |
1248 | - return m_pTrackModel->getTrack(indexSource); |
1249 | -} |
1250 | - |
1251 | -QString ProxyTrackModel::getTrackLocation(const QModelIndex& index) const { |
1252 | - QModelIndex indexSource = mapToSource(index); |
1253 | - return m_pTrackModel->getTrackLocation(indexSource); |
1254 | -} |
1255 | - |
1256 | -void ProxyTrackModel::search(const QString& searchText) { |
1257 | - if (m_bHandleSearches) { |
1258 | - m_currentSearch = searchText; |
1259 | - setFilterFixedString(searchText); |
1260 | - } else { |
1261 | - m_pTrackModel->search(searchText); |
1262 | - } |
1263 | -} |
1264 | - |
1265 | -const QString ProxyTrackModel::currentSearch() { |
1266 | - if (m_bHandleSearches) { |
1267 | - return m_currentSearch; |
1268 | - } |
1269 | - return m_pTrackModel->currentSearch(); |
1270 | -} |
1271 | - |
1272 | -bool ProxyTrackModel::isColumnInternal(int column) { |
1273 | - return m_pTrackModel->isColumnInternal(column); |
1274 | -} |
1275 | - |
1276 | -bool ProxyTrackModel::isColumnHiddenByDefault(int column) { |
1277 | - return m_pTrackModel->isColumnHiddenByDefault(column); |
1278 | -} |
1279 | - |
1280 | - |
1281 | -void ProxyTrackModel::removeTrack(const QModelIndex& index) { |
1282 | - QModelIndex indexSource = mapToSource(index); |
1283 | - m_pTrackModel->removeTrack(indexSource); |
1284 | -} |
1285 | - |
1286 | -void ProxyTrackModel::removeTracks(const QModelIndexList& indices) { |
1287 | - QModelIndexList translatedList; |
1288 | - foreach (QModelIndex index, indices) { |
1289 | - QModelIndex indexSource = mapToSource(index); |
1290 | - translatedList.append(indexSource); |
1291 | - } |
1292 | - m_pTrackModel->removeTracks(translatedList); |
1293 | -} |
1294 | - |
1295 | -bool ProxyTrackModel::addTrack(const QModelIndex& index, QString location) { |
1296 | - QModelIndex indexSource = mapToSource(index); |
1297 | - return m_pTrackModel->addTrack(indexSource, location); |
1298 | -} |
1299 | - |
1300 | -void ProxyTrackModel::moveTrack(const QModelIndex& sourceIndex, |
1301 | - const QModelIndex& destIndex) { |
1302 | - QModelIndex sourceIndexSource = mapToSource(sourceIndex); |
1303 | - QModelIndex destIndexSource = mapToSource(destIndex); |
1304 | - m_pTrackModel->moveTrack(sourceIndexSource, destIndexSource); |
1305 | -} |
1306 | - |
1307 | -QItemDelegate* ProxyTrackModel::delegateForColumn(const int i) { |
1308 | - return m_pTrackModel->delegateForColumn(i); |
1309 | -} |
1310 | - |
1311 | -TrackModel::CapabilitiesFlags ProxyTrackModel::getCapabilities() const { |
1312 | - return m_pTrackModel->getCapabilities(); |
1313 | -} |
1314 | - |
1315 | -bool ProxyTrackModel::filterAcceptsRow(int sourceRow, |
1316 | - const QModelIndex& sourceParent) const { |
1317 | - |
1318 | - if (!m_bHandleSearches) |
1319 | - return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); |
1320 | - |
1321 | - const QList<int>& filterColumns = m_pTrackModel->searchColumns(); |
1322 | - QAbstractItemModel* itemModel = |
1323 | - dynamic_cast<QAbstractItemModel*>(m_pTrackModel); |
1324 | - bool rowMatches = false; |
1325 | - |
1326 | - QRegExp filter = filterRegExp(); |
1327 | - QListIterator<int> iter(filterColumns); |
1328 | - |
1329 | - while (!rowMatches && iter.hasNext()) { |
1330 | - int i = iter.next(); |
1331 | - QModelIndex index = itemModel->index(sourceRow, i, sourceParent); |
1332 | - QVariant data = itemModel->data(index); |
1333 | - if (qVariantCanConvert<QString>(data)) { |
1334 | - QString strData = qVariantValue<QString>(data); |
1335 | - if (strData.contains(filter)) |
1336 | - rowMatches = true; |
1337 | - } |
1338 | - } |
1339 | - |
1340 | - return rowMatches; |
1341 | -} |
1342 | - |
1343 | -QString ProxyTrackModel::getModelSetting(QString name) { |
1344 | - if (!m_pTrackModel) |
1345 | - return QString(); |
1346 | - return m_pTrackModel->getModelSetting(name); |
1347 | -} |
1348 | - |
1349 | -bool ProxyTrackModel::setModelSetting(QString name, QVariant value) { |
1350 | - if (!m_pTrackModel) |
1351 | - return false; |
1352 | - return m_pTrackModel->setModelSetting(name, value); |
1353 | -} |
1354 | |
1355 | === added file 'mixxx/src/library/proxytrackmodel.h' |
1356 | --- mixxx/src/library/proxytrackmodel.h 1970-01-01 00:00:00 +0000 |
1357 | +++ mixxx/src/library/proxytrackmodel.h 2011-02-21 23:55:30 +0000 |
1358 | @@ -0,0 +1,53 @@ |
1359 | +// proxytrackmodel.h |
1360 | +// Created 10/22/2009 by RJ Ryan (rryan@mit.edu) |
1361 | + |
1362 | +#ifndef PROXYTRACKMODEL_H |
1363 | +#define PROXYTRACKMODEL_H |
1364 | + |
1365 | +#include <QSortFilterProxyModel> |
1366 | +#include <QAbstractItemModel> |
1367 | + |
1368 | +#include "library/trackmodel.h" |
1369 | + |
1370 | +// ProxyTrackModel composes a TrackModel inside of a QSortFilterProxyModel so |
1371 | +// that the sorting and filtering of the QSortFilterProxyModel can be completely |
1372 | +// transparent to the user of the TrackModel. The ProxyTrackModel will |
1373 | +// automatically translate any QModelIndex's to their source index before |
1374 | +// calling the composed TrackModel. If the bHandleSearches flag is set, the |
1375 | +// TrackModel search calls will not be delivered to the composed TrackModel |
1376 | +// because filtering is handled by the QSortFilterProxyModel. |
1377 | +class ProxyTrackModel : public QSortFilterProxyModel, public virtual TrackModel { |
1378 | + public: |
1379 | + // Construct a new ProxyTrackModel with pTrackModel as the TrackModel it |
1380 | + // composes. If bHandleSearches is true, then search signals will not be |
1381 | + // delivered to pTrackModel -- instead the ProxyTrackModel will do its own |
1382 | + // filtering. |
1383 | + ProxyTrackModel(QAbstractItemModel* pTrackModel, bool bHandleSearches=true); |
1384 | + virtual ~ProxyTrackModel(); |
1385 | + |
1386 | + virtual TrackPointer getTrack(const QModelIndex& index) const; |
1387 | + virtual QString getTrackLocation(const QModelIndex& index) const; |
1388 | + virtual void search(const QString& searchText); |
1389 | + virtual const QString currentSearch(); |
1390 | + virtual bool isColumnInternal(int column); |
1391 | + virtual bool isColumnHiddenByDefault(int column); |
1392 | + virtual void removeTrack(const QModelIndex& index); |
1393 | + virtual void removeTracks(const QModelIndexList& indices); |
1394 | + virtual bool addTrack(const QModelIndex& index, QString location); |
1395 | + virtual void moveTrack(const QModelIndex& sourceIndex, |
1396 | + const QModelIndex& destIndex); |
1397 | + virtual QItemDelegate* delegateForColumn(const int i); |
1398 | + virtual TrackModel::CapabilitiesFlags getCapabilities() const; |
1399 | + |
1400 | + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const; |
1401 | + |
1402 | + virtual QString getModelSetting(QString name); |
1403 | + virtual bool setModelSetting(QString name, QVariant value); |
1404 | + |
1405 | + private: |
1406 | + TrackModel* m_pTrackModel; |
1407 | + QString m_currentSearch; |
1408 | + bool m_bHandleSearches; |
1409 | +}; |
1410 | + |
1411 | +#endif /* PROXYTRACKMODEL_H */ |
1412 | |
1413 | === removed file 'mixxx/src/library/proxytrackmodel.h' |
1414 | --- mixxx/src/library/proxytrackmodel.h 2010-11-16 21:01:45 +0000 |
1415 | +++ mixxx/src/library/proxytrackmodel.h 1970-01-01 00:00:00 +0000 |
1416 | @@ -1,53 +0,0 @@ |
1417 | -// proxytrackmodel.h |
1418 | -// Created 10/22/2009 by RJ Ryan (rryan@mit.edu) |
1419 | - |
1420 | -#ifndef PROXYTRACKMODEL_H |
1421 | -#define PROXYTRACKMODEL_H |
1422 | - |
1423 | -#include <QSortFilterProxyModel> |
1424 | -#include <QAbstractItemModel> |
1425 | - |
1426 | -#include "library/trackmodel.h" |
1427 | - |
1428 | -// ProxyTrackModel composes a TrackModel inside of a QSortFilterProxyModel so |
1429 | -// that the sorting and filtering of the QSortFilterProxyModel can be completely |
1430 | -// transparent to the user of the TrackModel. The ProxyTrackModel will |
1431 | -// automatically translate any QModelIndex's to their source index before |
1432 | -// calling the composed TrackModel. If the bHandleSearches flag is set, the |
1433 | -// TrackModel search calls will not be delivered to the composed TrackModel |
1434 | -// because filtering is handled by the QSortFilterProxyModel. |
1435 | -class ProxyTrackModel : public QSortFilterProxyModel, public virtual TrackModel { |
1436 | - public: |
1437 | - // Construct a new ProxyTrackModel with pTrackModel as the TrackModel it |
1438 | - // composes. If bHandleSearches is true, then search signals will not be |
1439 | - // delivered to pTrackModel -- instead the ProxyTrackModel will do its own |
1440 | - // filtering. |
1441 | - ProxyTrackModel(QAbstractItemModel* pTrackModel, bool bHandleSearches=true); |
1442 | - virtual ~ProxyTrackModel(); |
1443 | - |
1444 | - virtual TrackPointer getTrack(const QModelIndex& index) const; |
1445 | - virtual QString getTrackLocation(const QModelIndex& index) const; |
1446 | - virtual void search(const QString& searchText); |
1447 | - virtual const QString currentSearch(); |
1448 | - virtual bool isColumnInternal(int column); |
1449 | - virtual bool isColumnHiddenByDefault(int column); |
1450 | - virtual void removeTrack(const QModelIndex& index); |
1451 | - virtual void removeTracks(const QModelIndexList& indices); |
1452 | - virtual bool addTrack(const QModelIndex& index, QString location); |
1453 | - virtual void moveTrack(const QModelIndex& sourceIndex, |
1454 | - const QModelIndex& destIndex); |
1455 | - virtual QItemDelegate* delegateForColumn(const int i); |
1456 | - virtual TrackModel::CapabilitiesFlags getCapabilities() const; |
1457 | - |
1458 | - bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const; |
1459 | - |
1460 | - virtual QString getModelSetting(QString name); |
1461 | - virtual bool setModelSetting(QString name, QVariant value); |
1462 | - |
1463 | - private: |
1464 | - TrackModel* m_pTrackModel; |
1465 | - QString m_currentSearch; |
1466 | - bool m_bHandleSearches; |
1467 | -}; |
1468 | - |
1469 | -#endif /* PROXYTRACKMODEL_H */ |
1470 | |
1471 | === added directory 'mixxx/src/library/rhythmbox' |
1472 | === renamed file 'mixxx/src/library/rhythmboxfeature.cpp' => 'mixxx/src/library/rhythmbox/rhythmboxfeature.cpp' |
1473 | --- mixxx/src/library/rhythmboxfeature.cpp 2011-01-13 18:40:56 +0000 |
1474 | +++ mixxx/src/library/rhythmbox/rhythmboxfeature.cpp 2011-02-21 23:55:30 +0000 |
1475 | @@ -2,33 +2,48 @@ |
1476 | #include <QtDebug> |
1477 | #include <QStringList> |
1478 | |
1479 | -#include "library/proxytrackmodel.h" |
1480 | -#include "library/rhythmboxtrackmodel.h" |
1481 | -#include "library/rhythmboxplaylistmodel.h" |
1482 | -#include "library/rhythmboxfeature.h" |
1483 | -#include "treeitem.h" |
1484 | - |
1485 | -RhythmboxFeature::RhythmboxFeature(QObject* parent) |
1486 | - : LibraryFeature(parent) { |
1487 | - |
1488 | - m_pRhythmboxTrackModel = NULL; |
1489 | - m_pTrackModelProxy = NULL; |
1490 | - m_pRhythmboxPlaylistModel = NULL; |
1491 | - m_pPlaylistModelProxy = NULL; |
1492 | +#include "library/rhythmbox/rhythmboxtrackmodel.h" |
1493 | +#include "library/rhythmbox/rhythmboxplaylistmodel.h" |
1494 | +#include "library/rhythmbox/rhythmboxfeature.h" |
1495 | +#include "library/treeitem.h" |
1496 | + |
1497 | +RhythmboxFeature::RhythmboxFeature(QObject* parent, TrackCollection* pTrackCollection) |
1498 | + : LibraryFeature(parent), |
1499 | + m_pTrackCollection(pTrackCollection) { |
1500 | + |
1501 | + m_pRhythmboxTrackModel = new RhythmboxTrackModel(this, m_pTrackCollection); |
1502 | + m_pRhythmboxPlaylistModel = new RhythmboxPlaylistModel(this, m_pTrackCollection); |
1503 | + m_isActivated = false; |
1504 | + m_title = tr("Rhythmbox"); |
1505 | + |
1506 | + if (!m_database.isOpen()) { |
1507 | + m_database = QSqlDatabase::addDatabase("QSQLITE", "RHYTHMBOX_SCANNER"); |
1508 | + m_database.setHostName("localhost"); |
1509 | + m_database.setDatabaseName(MIXXX_DB_PATH); |
1510 | + m_database.setUserName("mixxx"); |
1511 | + m_database.setPassword("mixxx"); |
1512 | + |
1513 | + //Open the database connection in this thread. |
1514 | + if (!m_database.open()) { |
1515 | + qDebug() << "Failed to open database for Rhythmbox scanner." << m_database.lastError(); |
1516 | + } |
1517 | + } |
1518 | + connect(&m_track_watcher, SIGNAL(finished()), this, SLOT(onTrackCollectionLoaded()), Qt::QueuedConnection); |
1519 | |
1520 | } |
1521 | |
1522 | RhythmboxFeature::~RhythmboxFeature() { |
1523 | - |
1524 | + delete m_pRhythmboxTrackModel; |
1525 | + delete m_pRhythmboxPlaylistModel; |
1526 | } |
1527 | |
1528 | bool RhythmboxFeature::isSupported() { |
1529 | - return (QFile::exists(MIXXX_RHYTHMBOX_DB_LOCATION) || |
1530 | - QFile::exists(MIXXX_RHYTHMBOX_DB_LOCATION_ALT)); |
1531 | + return (QFile::exists(QDir::homePath() + "/.gnome2/rhythmbox/rhythmdb.xml") || |
1532 | + QFile::exists(QDir::homePath() + "/.local/share/rhythmbox/rhythmdb.xml")); |
1533 | } |
1534 | |
1535 | QVariant RhythmboxFeature::title() { |
1536 | - return tr("Rhythmbox"); |
1537 | + return m_title; |
1538 | } |
1539 | |
1540 | QIcon RhythmboxFeature::getIcon() { |
1541 | @@ -40,39 +55,29 @@ |
1542 | } |
1543 | |
1544 | void RhythmboxFeature::activate() { |
1545 | - //qDebug("RhythmboxFeature::activate()"); |
1546 | - |
1547 | - if (!m_pRhythmboxTrackModel) { |
1548 | - if (QMessageBox::question( |
1549 | - NULL, |
1550 | - tr("Load Rhythmbox Library?"), |
1551 | - tr("Would you like to load your Rhythmbox library?"), |
1552 | - QMessageBox::Ok, |
1553 | - QMessageBox::Cancel) |
1554 | - == QMessageBox::Cancel) { |
1555 | - return; |
1556 | - } |
1557 | - |
1558 | - m_pRhythmboxTrackModel = new RhythmboxTrackModel(); |
1559 | - m_pTrackModelProxy = new ProxyTrackModel(m_pRhythmboxTrackModel); |
1560 | - m_pTrackModelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); |
1561 | - m_pTrackModelProxy->setSortCaseSensitivity(Qt::CaseInsensitive); |
1562 | - |
1563 | - m_pRhythmboxPlaylistModel = new RhythmboxPlaylistModel(m_pRhythmboxTrackModel); |
1564 | - m_pPlaylistModelProxy = new ProxyTrackModel(m_pRhythmboxPlaylistModel); |
1565 | - m_pPlaylistModelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); |
1566 | - m_pPlaylistModelProxy->setSortCaseSensitivity(Qt::CaseInsensitive); |
1567 | - |
1568 | - TreeItem *rootItem = new TreeItem(); |
1569 | - for (int i = 0; i < m_pRhythmboxPlaylistModel->numPlaylists(); ++i) { |
1570 | - QString playlist_name = m_pRhythmboxPlaylistModel->playlistTitle(i); |
1571 | - TreeItem *item = new TreeItem(playlist_name, playlist_name, this,rootItem); |
1572 | - |
1573 | - rootItem->appendChild(item); |
1574 | - } |
1575 | - m_childModel.setRootItem(rootItem); |
1576 | + qDebug() << "RhythmboxFeature::activate()"; |
1577 | + |
1578 | + if(!m_isActivated){ |
1579 | + m_isActivated = true; |
1580 | + /* Ususally the maximum number of threads |
1581 | + * is > 2 depending on the CPU cores |
1582 | + * Unfortunately, within VirtualBox |
1583 | + * the maximum number of allowed threads |
1584 | + * is 1 at all times We'll need to increase |
1585 | + * the number to > 1, otherwise importing the music collection |
1586 | + * takes place when the GUI threads terminates, i.e., on |
1587 | + * Mixxx shutdown. |
1588 | + */ |
1589 | + QThreadPool::globalInstance()->setMaxThreadCount(4); //Tobias decided to use 4 |
1590 | + m_track_future = QtConcurrent::run(this, &RhythmboxFeature::importMusicCollection); |
1591 | + m_track_watcher.setFuture(m_track_future); |
1592 | + m_title = "Rhythmbox (loading)"; |
1593 | + //calls a slot in the sidebar model such that 'Rhythmbox (isLoading)' is displayed. |
1594 | + emit (featureIsLoading(this)); |
1595 | } |
1596 | - emit(showTrackModel(m_pTrackModelProxy)); |
1597 | + else |
1598 | + emit(showTrackModel(m_pRhythmboxTrackModel)); |
1599 | + |
1600 | } |
1601 | |
1602 | void RhythmboxFeature::activateChild(const QModelIndex& index) { |
1603 | @@ -80,7 +85,7 @@ |
1604 | QString playlist = index.data().toString(); |
1605 | qDebug() << "Activating " << playlist; |
1606 | m_pRhythmboxPlaylistModel->setPlaylist(playlist); |
1607 | - emit(showTrackModel(m_pPlaylistModelProxy)); |
1608 | + emit(showTrackModel(m_pRhythmboxPlaylistModel)); |
1609 | } |
1610 | |
1611 | void RhythmboxFeature::onRightClick(const QPoint& globalPos) { |
1612 | @@ -105,4 +110,314 @@ |
1613 | return false; |
1614 | } |
1615 | |
1616 | +TreeItem* RhythmboxFeature::importMusicCollection() |
1617 | +{ |
1618 | + qDebug() << "importMusicCollection Thread Id: " << QThread::currentThread(); |
1619 | + /* |
1620 | + * Try and open the Rhythmbox DB. An API call which tells us where |
1621 | + * the file is would be nice. |
1622 | + */ |
1623 | + QFile db(QDir::homePath() + "/.gnome2/rhythmbox/rhythmdb.xml"); |
1624 | + if ( ! db.exists()) { |
1625 | + db.setFileName(QDir::homePath() + "/.local/share/rhythmbox/rhythmdb.xml"); |
1626 | + if ( ! db.exists()) |
1627 | + return false; |
1628 | + } |
1629 | + |
1630 | + if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) |
1631 | + return false; |
1632 | + |
1633 | + //Delete all table entries of Traktor feature |
1634 | + m_database.transaction(); |
1635 | + clearTable("rhythmbox_playlist_tracks"); |
1636 | + clearTable("rhythmbox_library"); |
1637 | + clearTable("rhythmbox_playlists"); |
1638 | + m_database.commit(); |
1639 | + |
1640 | + m_database.transaction(); |
1641 | + QSqlQuery query(m_database); |
1642 | + query.prepare("INSERT INTO rhythmbox_library (artist, title, album, year, genre, comment, tracknumber," |
1643 | + "bpm, bitrate," |
1644 | + "duration, location," |
1645 | + "rating ) " |
1646 | + "VALUES (:artist, :title, :album, :year, :genre, :comment, :tracknumber," |
1647 | + ":bpm, :bitrate," |
1648 | + ":duration, :location, :rating )"); |
1649 | + |
1650 | + |
1651 | + QXmlStreamReader xml(&db); |
1652 | + while (!xml.atEnd()) { |
1653 | + xml.readNext(); |
1654 | + if (xml.isStartElement() && xml.name() == "entry") { |
1655 | + QXmlStreamAttributes attr = xml.attributes(); |
1656 | + //Check if we really parse a track and not album art information |
1657 | + if(attr.value("type").toString() == "song"){ |
1658 | + importTrack(xml, query); |
1659 | + |
1660 | + } |
1661 | + } |
1662 | + } |
1663 | + m_database.commit(); |
1664 | + |
1665 | + |
1666 | + if (xml.hasError()) { |
1667 | + // do error handling |
1668 | + qDebug() << "Cannot process Rhythmbox music collection"; |
1669 | + qDebug() << "XML ERROR: " << xml.errorString(); |
1670 | + return false; |
1671 | + } |
1672 | + |
1673 | + |
1674 | + db.close(); |
1675 | + return importPlaylists(); |
1676 | + |
1677 | + |
1678 | +} |
1679 | + |
1680 | +TreeItem* RhythmboxFeature::importPlaylists() |
1681 | +{ |
1682 | + QFile db(QDir::homePath() + "/.gnome2/rhythmbox/playlists.xml"); |
1683 | + if ( ! db.exists()) { |
1684 | + db.setFileName(QDir::homePath() + "/.local/share/rhythmbox/playlists.xml"); |
1685 | + if (!db.exists()) |
1686 | + return NULL; |
1687 | + } |
1688 | + //Open file |
1689 | + if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) |
1690 | + return NULL; |
1691 | + |
1692 | + QSqlQuery query_insert_to_playlists(m_database); |
1693 | + query_insert_to_playlists.prepare("INSERT INTO rhythmbox_playlists (id, name) " |
1694 | + "VALUES (:id, :name)"); |
1695 | + |
1696 | + QSqlQuery query_insert_to_playlist_tracks(m_database); |
1697 | + query_insert_to_playlist_tracks.prepare("INSERT INTO rhythmbox_playlist_tracks (playlist_id, track_id) " |
1698 | + "VALUES (:playlist_id, :track_id)"); |
1699 | + //The tree structure holding the playlists |
1700 | + TreeItem* rootItem = new TreeItem(); |
1701 | + |
1702 | + QXmlStreamReader xml(&db); |
1703 | + while (!xml.atEnd()) { |
1704 | + xml.readNext(); |
1705 | + if (xml.isStartElement() && xml.name() == "playlist") { |
1706 | + QXmlStreamAttributes attr = xml.attributes(); |
1707 | + |
1708 | + //Only parse non build-in playlists |
1709 | + if(attr.value("type").toString() == "static"){ |
1710 | + QString playlist_name = attr.value("name").toString(); |
1711 | + |
1712 | + //Construct the childmodel |
1713 | + TreeItem * item = new TreeItem(playlist_name,playlist_name, this, rootItem); |
1714 | + rootItem->appendChild(item); |
1715 | + |
1716 | + //Execute SQL statement |
1717 | + query_insert_to_playlists.bindValue(":name", playlist_name); |
1718 | + |
1719 | + bool success = query_insert_to_playlists.exec(); |
1720 | + if(!success){ |
1721 | + qDebug() << "SQL Error in RhythmboxFeature.cpp: line" << __LINE__ << " " |
1722 | + << query_insert_to_playlists.lastError(); |
1723 | + return NULL; |
1724 | + } |
1725 | + //get playlist_id |
1726 | + int playlist_id = query_insert_to_playlists.lastInsertId().toInt(); |
1727 | + |
1728 | + //Process playlist entries |
1729 | + importPlaylist(xml, query_insert_to_playlist_tracks, playlist_id); |
1730 | + |
1731 | + } |
1732 | + } |
1733 | + } |
1734 | + |
1735 | + m_database.commit(); |
1736 | + |
1737 | + |
1738 | + if (xml.hasError()) { |
1739 | + // do error handling |
1740 | + qDebug() << "Cannot process Rhythmbox music collection"; |
1741 | + qDebug() << "XML ERROR: " << xml.errorString(); |
1742 | + return NULL; |
1743 | + } |
1744 | + db.close(); |
1745 | + |
1746 | + return rootItem; |
1747 | + |
1748 | +} |
1749 | + |
1750 | +void RhythmboxFeature::importTrack(QXmlStreamReader &xml, QSqlQuery &query) |
1751 | +{ |
1752 | + QString title; |
1753 | + QString artist; |
1754 | + QString album; |
1755 | + QString year; |
1756 | + QString genre; |
1757 | + QString location; |
1758 | + |
1759 | + int bpm = 0; |
1760 | + int bitrate = 0; |
1761 | + |
1762 | + //duration of a track |
1763 | + int playtime = 0; |
1764 | + int rating = 0; |
1765 | + QString comment; |
1766 | + QString tracknumber; |
1767 | + |
1768 | + while (!xml.atEnd()) { |
1769 | + xml.readNext(); |
1770 | + if (xml.isStartElement()) { |
1771 | + if(xml.name() == "title"){ |
1772 | + title = xml.readElementText(); |
1773 | + continue; |
1774 | + } |
1775 | + if(xml.name() == "artist"){ |
1776 | + artist = xml.readElementText(); |
1777 | + continue; |
1778 | + } |
1779 | + if(xml.name() == "genre"){ |
1780 | + genre = xml.readElementText(); |
1781 | + continue; |
1782 | + } |
1783 | + if(xml.name() == "album"){ |
1784 | + album = xml.readElementText(); |
1785 | + continue; |
1786 | + } |
1787 | + if(xml.name() == "track-number"){ |
1788 | + tracknumber = xml.readElementText(); |
1789 | + continue; |
1790 | + } |
1791 | + if(xml.name() == "duration"){ |
1792 | + playtime = xml.readElementText().toInt();; |
1793 | + continue; |
1794 | + } |
1795 | + if(xml.name() == "bitrate"){ |
1796 | + bitrate = xml.readElementText().toInt(); |
1797 | + continue; |
1798 | + } |
1799 | + if(xml.name() == "beats-per-minute"){ |
1800 | + bpm = xml.readElementText().toInt(); |
1801 | + continue; |
1802 | + } |
1803 | + if(xml.name() == "comment"){ |
1804 | + comment = xml.readElementText(); |
1805 | + continue; |
1806 | + } |
1807 | + if(xml.name() == "location"){ |
1808 | + location = xml.readElementText(); |
1809 | + location.remove("file://"); |
1810 | + QByteArray strlocbytes = location.toUtf8(); |
1811 | + QUrl locationUrl = QUrl::fromEncoded(strlocbytes); |
1812 | + location = locationUrl.toLocalFile(); |
1813 | + continue; |
1814 | + } |
1815 | + } |
1816 | + //exit the loop if we reach the closing <entry> tag |
1817 | + if (xml.isEndElement() && xml.name() == "entry") { |
1818 | + break; |
1819 | + } |
1820 | + |
1821 | + } |
1822 | + query.bindValue(":artist", artist); |
1823 | + query.bindValue(":title", title); |
1824 | + query.bindValue(":album", album); |
1825 | + query.bindValue(":genre", genre); |
1826 | + query.bindValue(":year", year); |
1827 | + query.bindValue(":duration", playtime); |
1828 | + query.bindValue(":location", location); |
1829 | + query.bindValue(":rating", rating); |
1830 | + query.bindValue(":comment", comment); |
1831 | + query.bindValue(":tracknumber", tracknumber); |
1832 | + query.bindValue(":bpm", bpm); |
1833 | + query.bindValue(":bitrate", bitrate); |
1834 | + |
1835 | + bool success = query.exec(); |
1836 | + |
1837 | + if (!success) { |
1838 | + qDebug() << "SQL Error in rhythmboxfeature.cpp: line" << __LINE__ << " " << query.lastError(); |
1839 | + return; |
1840 | + } |
1841 | + |
1842 | +} |
1843 | + |
1844 | +/** reads all playlist entries and executes a SQL statement **/ |
1845 | +void RhythmboxFeature::importPlaylist(QXmlStreamReader &xml, QSqlQuery &query_insert_to_playlist_tracks, int playlist_id) |
1846 | +{ |
1847 | + while(!xml.atEnd()) |
1848 | + { |
1849 | + //read next XML element |
1850 | + xml.readNext(); |
1851 | + if(xml.isStartElement() && xml.name() == "location") |
1852 | + { |
1853 | + QString location = xml.readElementText(); |
1854 | + location.remove("file://"); |
1855 | + QByteArray strlocbytes = location.toUtf8(); |
1856 | + QUrl locationUrl = QUrl::fromEncoded(strlocbytes); |
1857 | + location = locationUrl.toLocalFile(); |
1858 | + |
1859 | + //get the ID of the file in the rhythmbox_library table |
1860 | + int track_id = -1; |
1861 | + QSqlQuery finder_query(m_database); |
1862 | + finder_query.prepare("select id from rhythmbox_library where location=:path"); |
1863 | + finder_query.bindValue(":path", location); |
1864 | + bool success = finder_query.exec(); |
1865 | + |
1866 | + |
1867 | + if(success){ |
1868 | + while (finder_query.next()) { |
1869 | + track_id = finder_query.value(finder_query.record().indexOf("id")).toInt(); |
1870 | + } |
1871 | + } |
1872 | + else |
1873 | + qDebug() << "SQL Error in RhythmboxFeature.cpp: line" << __LINE__ << " " << finder_query.lastError(); |
1874 | + |
1875 | + query_insert_to_playlist_tracks.bindValue(":playlist_id", playlist_id); |
1876 | + query_insert_to_playlist_tracks.bindValue(":track_id", track_id); |
1877 | + |
1878 | + success = query_insert_to_playlist_tracks.exec(); |
1879 | + |
1880 | + if(!success){ |
1881 | + qDebug() << "SQL Error in RhythmboxFeature.cpp: line" << __LINE__ << " " |
1882 | + << query_insert_to_playlist_tracks.lastError(); |
1883 | + qDebug() << "trackid" << track_id; |
1884 | + qDebug() << "playlis ID " << playlist_id; |
1885 | + qDebug() << "-----------------"; |
1886 | + |
1887 | + } |
1888 | + } |
1889 | + //Exit the the loop if we reach the closing <playlist> tag |
1890 | + if(xml.isEndElement() && xml.name() == "playlist") |
1891 | + { |
1892 | + break; |
1893 | + } |
1894 | + } |
1895 | + |
1896 | +} |
1897 | + |
1898 | +void RhythmboxFeature::clearTable(QString table_name) |
1899 | +{ |
1900 | + qDebug() << "clearTable Thread Id: " << QThread::currentThread(); |
1901 | + QSqlQuery query(m_database); |
1902 | + query.prepare("delete from "+table_name); |
1903 | + bool success = query.exec(); |
1904 | + |
1905 | + if(!success) |
1906 | + qDebug() << "Could not delete remove old entries from table " << table_name << " : " << query.lastError(); |
1907 | + else |
1908 | + qDebug() << "Rhythmbox table entries of '" << table_name <<"' have been cleared."; |
1909 | +} |
1910 | + |
1911 | +void RhythmboxFeature::onTrackCollectionLoaded() { |
1912 | + TreeItem* root = m_track_future.result(); |
1913 | + if (root) { |
1914 | + m_childModel.setRootItem(root); |
1915 | + m_pRhythmboxTrackModel->select(); |
1916 | + } |
1917 | + else { |
1918 | + qDebug() << "Rhythmbox Playlists loaded: false"; |
1919 | + } |
1920 | + //calls a slot in the sidebarmodel such that 'isLoading' is removed from the feature title. |
1921 | + m_title = tr("Rhythmbox"); |
1922 | + emit(featureLoadingFinished(this)); |
1923 | + activate(); |
1924 | +} |
1925 | + |
1926 | |
1927 | |
1928 | === renamed file 'mixxx/src/library/rhythmboxfeature.h' => 'mixxx/src/library/rhythmbox/rhythmboxfeature.h' |
1929 | --- mixxx/src/library/rhythmboxfeature.h 2010-12-23 18:55:54 +0000 |
1930 | +++ mixxx/src/library/rhythmbox/rhythmboxfeature.h 2011-02-21 23:55:30 +0000 |
1931 | @@ -5,18 +5,24 @@ |
1932 | #define RHYTHMBOXFEATURE_H |
1933 | |
1934 | #include <QStringListModel> |
1935 | +#include <QtSql> |
1936 | +#include <QXmlStreamReader> |
1937 | +#include <QFuture> |
1938 | +#include <QtConcurrentRun> |
1939 | +#include <QFutureWatcher> |
1940 | |
1941 | #include "library/libraryfeature.h" |
1942 | -#include "treeitemmodel.h" |
1943 | - |
1944 | -class RhythmboxPlaylistModel; |
1945 | -class RhythmboxTrackModel; |
1946 | -class ProxyTrackModel; |
1947 | +#include "library/treeitemmodel.h" |
1948 | +#include "library/rhythmbox/rhythmboxtrackmodel.h" |
1949 | +#include "library/rhythmbox/rhythmboxplaylistmodel.h" |
1950 | +#include "library/trackcollection.h" |
1951 | + |
1952 | + |
1953 | |
1954 | class RhythmboxFeature : public LibraryFeature { |
1955 | Q_OBJECT |
1956 | public: |
1957 | - RhythmboxFeature(QObject* parent = NULL); |
1958 | + RhythmboxFeature(QObject* parent, TrackCollection*); |
1959 | virtual ~RhythmboxFeature(); |
1960 | static bool isSupported(); |
1961 | |
1962 | @@ -29,19 +35,39 @@ |
1963 | bool dragMoveAcceptChild(const QModelIndex& index, QUrl url); |
1964 | |
1965 | TreeItemModel* getChildModel(); |
1966 | + /** processes the music collection **/ |
1967 | + TreeItem* importMusicCollection(); |
1968 | + /** processes the playlist entries **/ |
1969 | + TreeItem* importPlaylists(); |
1970 | +private: |
1971 | + RhythmboxTrackModel* m_pRhythmboxTrackModel; |
1972 | + RhythmboxPlaylistModel* m_pRhythmboxPlaylistModel; |
1973 | + TrackCollection* m_pTrackCollection; |
1974 | + //new DB object because of threads |
1975 | + QSqlDatabase m_database; |
1976 | + bool m_isActivated; |
1977 | + QString m_title; |
1978 | + |
1979 | + QFutureWatcher<TreeItem*> m_track_watcher; |
1980 | + QFuture<TreeItem*> m_track_future; |
1981 | + |
1982 | + |
1983 | + TreeItemModel m_childModel; |
1984 | + |
1985 | + /**Removes all rows from a given table **/ |
1986 | + void clearTable(QString table_name); |
1987 | + /** reads the properties of a track and executes a SQL statement **/ |
1988 | + void importTrack(QXmlStreamReader &xml, QSqlQuery &query); |
1989 | + /** reads all playlist entries and executes a SQL statement **/ |
1990 | + void importPlaylist(QXmlStreamReader &xml, QSqlQuery &query, int playlist_id); |
1991 | |
1992 | public slots: |
1993 | void activate(); |
1994 | void activateChild(const QModelIndex& index); |
1995 | void onRightClick(const QPoint& globalPos); |
1996 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
1997 | + void onTrackCollectionLoaded(); |
1998 | |
1999 | -private: |
2000 | - RhythmboxTrackModel* m_pRhythmboxTrackModel; |
2001 | - RhythmboxPlaylistModel* m_pRhythmboxPlaylistModel; |
2002 | - ProxyTrackModel* m_pTrackModelProxy; |
2003 | - ProxyTrackModel* m_pPlaylistModelProxy; |
2004 | - TreeItemModel m_childModel; |
2005 | }; |
2006 | |
2007 | #endif /* RHYTHMBOXFEATURE_H */ |
2008 | |
2009 | === renamed file 'mixxx/src/library/rhythmboxplaylistmodel.cpp' => 'mixxx/src/library/rhythmbox/rhythmboxplaylistmodel.cpp' |
2010 | --- mixxx/src/library/rhythmboxplaylistmodel.cpp 2010-11-16 21:01:45 +0000 |
2011 | +++ mixxx/src/library/rhythmbox/rhythmboxplaylistmodel.cpp 2011-02-21 23:55:30 +0000 |
2012 | @@ -1,99 +1,104 @@ |
2013 | -/*************************************************************************** |
2014 | - rhythmboxPlaylistsource.cpp |
2015 | - ------------------- |
2016 | - begin : 8/17/2009 |
2017 | - copyright : (C) 2009 Phillip Whelan |
2018 | - email : pwhelan@mixxx.org |
2019 | -***************************************************************************/ |
2020 | - |
2021 | -/*************************************************************************** |
2022 | - * * |
2023 | - * This program is free software; you can redistribute it and/or modify * |
2024 | - * it under the terms of the GNU General Public License as published by * |
2025 | - * the Free Software Foundation; either version 2 of the License, or * |
2026 | - * (at your option) any later version. * |
2027 | - * * |
2028 | - ***************************************************************************/ |
2029 | - |
2030 | #include <QtCore> |
2031 | #include <QtGui> |
2032 | #include <QtSql> |
2033 | -#include <QtDebug> |
2034 | -#include <QtXmlPatterns/QXmlQuery> |
2035 | - |
2036 | -#include "library/rhythmboxtrackmodel.h" |
2037 | -#include "library/rhythmboxplaylistmodel.h" |
2038 | -#include "xmlparse.h" |
2039 | -#include "trackinfoobject.h" |
2040 | -#include "defs.h" |
2041 | +#include "library/trackcollection.h" |
2042 | +#include "library/rhythmbox/rhythmboxplaylistmodel.h" |
2043 | |
2044 | #include "mixxxutils.cpp" |
2045 | |
2046 | -RhythmboxPlaylistModel::RhythmboxPlaylistModel(RhythmboxTrackModel *Rhythhmbox) : |
2047 | - TrackModel(QSqlDatabase::database("QSQLITE"), |
2048 | - "mixxx.db.model.rhythmbox_playlist"), |
2049 | - m_pRhythmbox(Rhythhmbox), |
2050 | - m_sCurrentPlaylist("") { |
2051 | - int idx = 0; |
2052 | - QDomDocument rhythmplaylistdb; |
2053 | - QXmlQuery query; |
2054 | - QString res; |
2055 | - |
2056 | - QFile db(MIXXX_RHYTHMBOX_DB_LOCATION); |
2057 | - if ( ! db.exists()) { |
2058 | - db.setFileName(MIXXX_RHYTHMBOX_DB_LOCATION_ALT); |
2059 | - if ( ! db.exists()) |
2060 | - return; |
2061 | - } |
2062 | - |
2063 | - if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) |
2064 | - return; |
2065 | - |
2066 | - /* |
2067 | - * Use QXmlQuery to execute an XPath query. We add the version to |
2068 | - * the XPath query to make sure it is the schema we expect. |
2069 | - */ |
2070 | - query.setFocus(&db); |
2071 | - query.setQuery("rhythmdb-playlists/playlist[@type='static']"); |
2072 | - if ( ! query.isValid()) |
2073 | - return; |
2074 | - |
2075 | - query.evaluateTo(&res); |
2076 | - db.close(); |
2077 | - |
2078 | - rhythmplaylistdb.setContent("<rhythmdb-playlists>" + res + "</rhythmdb-playlists>"); |
2079 | - m_playlistNodes = rhythmplaylistdb.elementsByTagName("playlist"); |
2080 | - |
2081 | - |
2082 | - /* Add Playlists to the internal playlist map */ |
2083 | - while (( idx < m_playlistNodes.count())) { |
2084 | - QDomNode n = m_playlistNodes.item(idx); |
2085 | - QDomElement e = n.toElement(); |
2086 | - |
2087 | - qDebug() << "Adding Rhythmbox Playlist" << e.attribute("name"); |
2088 | - QString playlist = e.attribute("name"); |
2089 | - |
2090 | - m_mPlaylists[playlist] = n.childNodes(); |
2091 | - m_sCurrentPlaylist = playlist; |
2092 | - |
2093 | - idx++; |
2094 | - } |
2095 | -} |
2096 | - |
2097 | -RhythmboxPlaylistModel::~RhythmboxPlaylistModel() |
2098 | -{ |
2099 | -} |
2100 | - |
2101 | -Qt::ItemFlags RhythmboxPlaylistModel::flags ( const QModelIndex & index ) const |
2102 | -{ |
2103 | - Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); |
2104 | - |
2105 | - if (!index.isValid()) |
2106 | - return Qt::ItemIsEnabled; |
2107 | - |
2108 | - defaultFlags |= Qt::ItemIsDragEnabled; |
2109 | - |
2110 | - return defaultFlags; |
2111 | +RhythmboxPlaylistModel::RhythmboxPlaylistModel(QObject* parent, |
2112 | + TrackCollection* pTrackCollection) |
2113 | + : TrackModel(pTrackCollection->getDatabase(), |
2114 | + "mixxx.db.model.rhythmbox_playlist"), |
2115 | + BaseSqlTableModel(parent, pTrackCollection, pTrackCollection->getDatabase()), |
2116 | + m_pTrackCollection(pTrackCollection), |
2117 | + m_database(m_pTrackCollection->getDatabase()) |
2118 | +{ |
2119 | + connect(this, SIGNAL(doSearch(const QString&)), this, SLOT(slotSearch(const QString&))); |
2120 | + setCaching(false); |
2121 | +} |
2122 | + |
2123 | +RhythmboxPlaylistModel::~RhythmboxPlaylistModel() { |
2124 | +} |
2125 | + |
2126 | +bool RhythmboxPlaylistModel::addTrack(const QModelIndex& index, QString location) |
2127 | +{ |
2128 | + |
2129 | + return false; |
2130 | +} |
2131 | + |
2132 | +TrackPointer RhythmboxPlaylistModel::getTrack(const QModelIndex& index) const |
2133 | +{ |
2134 | + QString artist = index.sibling(index.row(), fieldIndex("artist")).data().toString(); |
2135 | + QString title = index.sibling(index.row(), fieldIndex("title")).data().toString(); |
2136 | + QString album = index.sibling(index.row(), fieldIndex("album")).data().toString(); |
2137 | + QString year = index.sibling(index.row(), fieldIndex("year")).data().toString(); |
2138 | + QString genre = index.sibling(index.row(), fieldIndex("genre")).data().toString(); |
2139 | + float bpm = index.sibling(index.row(), fieldIndex("bpm")).data().toString().toFloat(); |
2140 | + |
2141 | + QString location = index.sibling(index.row(), fieldIndex("location")).data().toString(); |
2142 | + |
2143 | + TrackInfoObject* pTrack = new TrackInfoObject(location); |
2144 | + pTrack->setArtist(artist); |
2145 | + pTrack->setTitle(title); |
2146 | + pTrack->setAlbum(album); |
2147 | + pTrack->setYear(year); |
2148 | + pTrack->setGenre(genre); |
2149 | + pTrack->setBpm(bpm); |
2150 | + |
2151 | + return TrackPointer(pTrack, &QObject::deleteLater); |
2152 | +} |
2153 | + |
2154 | +QString RhythmboxPlaylistModel::getTrackLocation(const QModelIndex& index) const { |
2155 | + QString location = index.sibling(index.row(), fieldIndex("location")).data().toString(); |
2156 | + return location; |
2157 | +} |
2158 | + |
2159 | +void RhythmboxPlaylistModel::removeTrack(const QModelIndex& index) { |
2160 | + |
2161 | +} |
2162 | + |
2163 | +void RhythmboxPlaylistModel::removeTracks(const QModelIndexList& indices) { |
2164 | + |
2165 | +} |
2166 | + |
2167 | +void RhythmboxPlaylistModel::moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex) { |
2168 | + |
2169 | +} |
2170 | + |
2171 | +void RhythmboxPlaylistModel::search(const QString& searchText) { |
2172 | + // qDebug() << "RhythmboxPlaylistModel::search()" << searchText |
2173 | + // << QThread::currentThread(); |
2174 | + emit(doSearch(searchText)); |
2175 | +} |
2176 | + |
2177 | +void RhythmboxPlaylistModel::slotSearch(const QString& searchText) { |
2178 | + if (!m_currentSearch.isNull() && m_currentSearch == searchText) |
2179 | + return; |
2180 | + m_currentSearch = searchText; |
2181 | + |
2182 | + QString filter; |
2183 | + QSqlField search("search", QVariant::String); |
2184 | + search.setValue("%" + searchText + "%"); |
2185 | + QString escapedText = database().driver()->formatValue(search); |
2186 | + filter = "(artist LIKE " + escapedText + " OR " + |
2187 | + "album LIKE " + escapedText + " OR " + |
2188 | + "title LIKE " + escapedText + ")"; |
2189 | + setFilter(filter); |
2190 | +} |
2191 | + |
2192 | +const QString RhythmboxPlaylistModel::currentSearch() { |
2193 | + return m_currentSearch; |
2194 | +} |
2195 | + |
2196 | +bool RhythmboxPlaylistModel::isColumnInternal(int column) { |
2197 | + if (column == fieldIndex(LIBRARYTABLE_ID) || |
2198 | + column == fieldIndex(LIBRARYTABLE_MIXXXDELETED) || |
2199 | + column == fieldIndex(TRACKLOCATIONSTABLE_FSDELETED) || |
2200 | + column == fieldIndex("name") || |
2201 | + column == fieldIndex("track_id")) |
2202 | + return true; |
2203 | + return false; |
2204 | } |
2205 | |
2206 | QMimeData* RhythmboxPlaylistModel::mimeData(const QModelIndexList &indexes) const { |
2207 | @@ -122,173 +127,83 @@ |
2208 | } |
2209 | |
2210 | |
2211 | -QVariant RhythmboxPlaylistModel::data ( const QModelIndex & index, int role ) const |
2212 | -{ |
2213 | - if ( m_sCurrentPlaylist == "" ) |
2214 | - return QVariant(); |
2215 | - |
2216 | - if (!index.isValid()) |
2217 | - return QVariant(); |
2218 | - |
2219 | - // OwenB - attempting to make this more efficient, don't create a new |
2220 | - // TIO for every row |
2221 | - if (role == Qt::DisplayRole || role == Qt::ToolTipRole) { |
2222 | - // get location string from playlist |
2223 | - QString location = getTrackLocation(index); |
2224 | - |
2225 | - // use this to get DOM node from RhythmboxTrackModel |
2226 | - QDomNode songNode = m_pRhythmbox->getTrackNodeByLocation(location); |
2227 | - |
2228 | - // return the node's data item that was asked for. |
2229 | - return m_pRhythmbox->getTrackColumnData(songNode, index); |
2230 | - } |
2231 | - |
2232 | - return QVariant(); |
2233 | -} |
2234 | - |
2235 | -bool RhythmboxPlaylistModel::isColumnInternal(int column) { |
2236 | - return false; |
2237 | -} |
2238 | -bool RhythmboxPlaylistModel::isColumnHiddenByDefault(int column) { |
2239 | - return false; |
2240 | -} |
2241 | - |
2242 | - |
2243 | -QVariant RhythmboxPlaylistModel::headerData ( int section, Qt::Orientation orientation, int role ) const |
2244 | -{ |
2245 | - /* Only respond to requests for column header display names */ |
2246 | - if ( role != Qt::DisplayRole ) |
2247 | - return QVariant(); |
2248 | - |
2249 | - if (orientation == Qt::Horizontal) |
2250 | - { |
2251 | - switch (section) |
2252 | - { |
2253 | - case RhythmboxPlaylistModel::COLUMN_ARTIST: |
2254 | - return QString(tr("Artist")); |
2255 | - |
2256 | - case RhythmboxPlaylistModel::COLUMN_TITLE: |
2257 | - return QString(tr("Title")); |
2258 | - |
2259 | - case RhythmboxPlaylistModel::COLUMN_ALBUM: |
2260 | - return QString(tr("Album")); |
2261 | - |
2262 | - case RhythmboxPlaylistModel::COLUMN_DATE: |
2263 | - return QString(tr("Year")); |
2264 | - |
2265 | - case RhythmboxPlaylistModel::COLUMN_GENRE: |
2266 | - return QString(tr("Genre")); |
2267 | - |
2268 | - case RhythmboxPlaylistModel::COLUMN_LOCATION: |
2269 | - return QString(tr("Location")); |
2270 | - |
2271 | - case RhythmboxPlaylistModel::COLUMN_DURATION: |
2272 | - return QString(tr("Duration")); |
2273 | - |
2274 | - default: |
2275 | - return QString(tr("Unknown")); |
2276 | - } |
2277 | - } |
2278 | - |
2279 | - return QVariant(); |
2280 | -} |
2281 | - |
2282 | -int RhythmboxPlaylistModel::rowCount ( const QModelIndex & parent ) const |
2283 | -{ |
2284 | - // FIXME |
2285 | - //if ( !m_mPlaylists.containts(m_sCurrentPlaylist)) |
2286 | - // return 0; |
2287 | - |
2288 | - if ( m_sCurrentPlaylist == "" ) |
2289 | - return 0; |
2290 | - |
2291 | - return m_mPlaylists[m_sCurrentPlaylist].size(); |
2292 | -} |
2293 | - |
2294 | -int RhythmboxPlaylistModel::columnCount(const QModelIndex& parent) const |
2295 | -{ |
2296 | - if (parent != QModelIndex()) //Some weird thing for table-based models. |
2297 | - return 0; |
2298 | - return RhythmboxPlaylistModel::NUM_COLUMNS; |
2299 | -} |
2300 | - |
2301 | -bool RhythmboxPlaylistModel::addTrack(const QModelIndex& index, QString location) |
2302 | -{ |
2303 | - //Should do nothing... hmmm |
2304 | - return false; |
2305 | -} |
2306 | - |
2307 | -/** Removes a track from the library track collection. */ |
2308 | -void RhythmboxPlaylistModel::removeTrack(const QModelIndex& index) |
2309 | -{ |
2310 | - //Should do nothing... hmmm |
2311 | -} |
2312 | - |
2313 | -void RhythmboxPlaylistModel::removeTracks(const QModelIndexList& indices) |
2314 | -{ |
2315 | - //Should do nothing... hmmm |
2316 | -} |
2317 | - |
2318 | -void RhythmboxPlaylistModel::moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex) |
2319 | -{ |
2320 | - //Should do nothing... hmmm |
2321 | -} |
2322 | - |
2323 | - |
2324 | - |
2325 | -QString RhythmboxPlaylistModel::getTrackLocation(const QModelIndex& index) const |
2326 | -{ |
2327 | - QDomNodeList playlistTrackList = m_mPlaylists[m_sCurrentPlaylist]; |
2328 | - QDomNode pnode = playlistTrackList.at(index.row()); |
2329 | - QString location = pnode.toElement().text(); |
2330 | - |
2331 | - return location; |
2332 | -} |
2333 | - |
2334 | -TrackPointer RhythmboxPlaylistModel::getTrack(const QModelIndex& index) const |
2335 | -{ |
2336 | - QString location = getTrackLocation(index); |
2337 | - |
2338 | - return m_pRhythmbox->getTrackByLocation(location); |
2339 | -} |
2340 | - |
2341 | QItemDelegate* RhythmboxPlaylistModel::delegateForColumn(const int i) { |
2342 | return NULL; |
2343 | } |
2344 | |
2345 | -QList<QString> RhythmboxPlaylistModel::getPlaylists() |
2346 | -{ |
2347 | - return m_mPlaylists.keys(); |
2348 | -} |
2349 | - |
2350 | -int RhythmboxPlaylistModel::numPlaylists() { |
2351 | - return m_mPlaylists.size(); |
2352 | -} |
2353 | - |
2354 | -QString RhythmboxPlaylistModel::playlistTitle(int n) { |
2355 | - return m_mPlaylists.keys().at(n); |
2356 | -} |
2357 | - |
2358 | -void RhythmboxPlaylistModel::setPlaylist(QString playlist) |
2359 | -{ |
2360 | - if ( m_mPlaylists.contains(playlist)) |
2361 | - m_sCurrentPlaylist = playlist; |
2362 | +TrackModel::CapabilitiesFlags RhythmboxPlaylistModel::getCapabilities() const { |
2363 | + return TRACKMODELCAPS_NONE; |
2364 | +} |
2365 | + |
2366 | +Qt::ItemFlags RhythmboxPlaylistModel::flags(const QModelIndex &index) const { |
2367 | + return readOnlyFlags(index); |
2368 | +} |
2369 | + |
2370 | +void RhythmboxPlaylistModel::setPlaylist(QString playlist_path) { |
2371 | + int playlistId = -1; |
2372 | + QSqlQuery finder_query(m_database); |
2373 | + finder_query.prepare("SELECT id from rhythmbox_playlists where name='"+playlist_path+"'"); |
2374 | + |
2375 | + if(finder_query.exec()){ |
2376 | + while (finder_query.next()) { |
2377 | + playlistId = finder_query.value(finder_query.record().indexOf("id")).toInt(); |
2378 | + } |
2379 | + } |
2380 | else |
2381 | - m_sCurrentPlaylist = ""; |
2382 | - |
2383 | - // force the layout to update |
2384 | - emit(layoutChanged()); |
2385 | -} |
2386 | - |
2387 | -void RhythmboxPlaylistModel::search(const QString& searchText) |
2388 | -{ |
2389 | - m_currentSearch = searchText; |
2390 | -} |
2391 | - |
2392 | -const QString RhythmboxPlaylistModel::currentSearch() { |
2393 | - return m_currentSearch; |
2394 | -} |
2395 | - |
2396 | -const QList<int>& RhythmboxPlaylistModel::searchColumns() const { |
2397 | - return m_pRhythmbox->searchColumns(); |
2398 | + qDebug() << "SQL Error in RhythmboxPlaylistModel.cpp: line" << __LINE__ << " " << finder_query.lastError(); |
2399 | + |
2400 | + |
2401 | + QString playlistID = "Rhythmboxplaylist_" + QString("%1").arg(playlistId); |
2402 | + //Escape the playlist name |
2403 | + QSqlDriver* driver = m_pTrackCollection->getDatabase().driver(); |
2404 | + QSqlField playlistNameField("name", QVariant::String); |
2405 | + playlistNameField.setValue(playlistID); |
2406 | + |
2407 | + QSqlQuery query(m_database); |
2408 | + query.prepare("CREATE TEMPORARY VIEW IF NOT EXISTS "+ driver->formatValue(playlistNameField) + " AS " |
2409 | + "SELECT " |
2410 | + "rhythmbox_library.id," |
2411 | + "rhythmbox_library.artist," |
2412 | + "rhythmbox_library.title," |
2413 | + "rhythmbox_library.album," |
2414 | + "rhythmbox_library.year," |
2415 | + "rhythmbox_library.genre," |
2416 | + "rhythmbox_library.tracknumber," |
2417 | + "rhythmbox_library.location," |
2418 | + "rhythmbox_library.comment," |
2419 | + "rhythmbox_library.rating," |
2420 | + "rhythmbox_library.duration," |
2421 | + "rhythmbox_library.bitrate," |
2422 | + "rhythmbox_library.bpm," |
2423 | + "rhythmbox_playlist_tracks.track_id, " |
2424 | + "rhythmbox_playlists.name " |
2425 | + "FROM rhythmbox_library " |
2426 | + "INNER JOIN rhythmbox_playlist_tracks " |
2427 | + "ON rhythmbox_playlist_tracks.track_id = rhythmbox_library.id " |
2428 | + "INNER JOIN rhythmbox_playlists " |
2429 | + "ON rhythmbox_playlist_tracks.playlist_id = rhythmbox_playlists.id " |
2430 | + "where rhythmbox_playlists.name='"+playlist_path+"'" |
2431 | + ); |
2432 | + |
2433 | + |
2434 | + if (!query.exec()) { |
2435 | + |
2436 | + qDebug() << "Error creating temporary view for rhythmbox playlists. RhythmboxPlaylistModel --> line: " << __LINE__ << " " << query.lastError(); |
2437 | + qDebug() << "Executed Query: " << query.executedQuery(); |
2438 | + return; |
2439 | + } |
2440 | + setTable(playlistID); |
2441 | + |
2442 | + //removeColumn(fieldIndex("track_id")); |
2443 | + //removeColumn(fieldIndex("name")); |
2444 | + //removeColumn(fieldIndex("id")); |
2445 | + |
2446 | + slotSearch(""); |
2447 | + |
2448 | + select(); //Populate the data model. |
2449 | + initHeaderData(); |
2450 | +} |
2451 | + |
2452 | +bool RhythmboxPlaylistModel::isColumnHiddenByDefault(int column) { |
2453 | + return false; |
2454 | } |
2455 | |
2456 | === renamed file 'mixxx/src/library/rhythmboxplaylistmodel.h' => 'mixxx/src/library/rhythmbox/rhythmboxplaylistmodel.h' |
2457 | --- mixxx/src/library/rhythmboxplaylistmodel.h 2010-11-16 21:01:45 +0000 |
2458 | +++ mixxx/src/library/rhythmbox/rhythmboxplaylistmodel.h 2011-02-21 23:55:30 +0000 |
2459 | @@ -1,9 +1,9 @@ |
2460 | /*************************************************************************** |
2461 | - rhythmboxPlaylistsource.h |
2462 | + rhythmboxplaylistmodel.h |
2463 | ------------------- |
2464 | - begin : 8/17/2009 |
2465 | - copyright : (C) 2009 Phillip Whelan |
2466 | - email : pwhelan@mixxx.org |
2467 | + begin : 01/09/2011 |
2468 | + copyright : (C) 2011 Tobias Rafreider |
2469 | + |
2470 | ***************************************************************************/ |
2471 | |
2472 | /*************************************************************************** |
2473 | @@ -19,93 +19,51 @@ |
2474 | #define RHYTHMBOXPLAYLISTMODEL_H |
2475 | |
2476 | #include <QtSql> |
2477 | -#include <QtXml> |
2478 | -#include <QList> |
2479 | - |
2480 | -#include "trackmodel.h" |
2481 | -#include "rhythmboxtrackmodel.h" |
2482 | - |
2483 | - |
2484 | -#define MIXXX_RHYTHMBOX_DB_LOCATION QDir::homePath() + "/.gnome2/rhythmbox/playlists.xml" |
2485 | -#define MIXXX_RHYTHMBOX_DB_LOCATION_ALT QDir::homePath() + "/.local/share/rhythmbox/playlists.xml" |
2486 | - |
2487 | -/** |
2488 | - @author Phillip Whelan |
2489 | - Code copied originally from RhythmBoxTrackModel |
2490 | -*/ |
2491 | -class RhythmboxPlaylistModel : public QAbstractTableModel, public TrackModel |
2492 | +#include <QItemDelegate> |
2493 | +#include <QtCore> |
2494 | +#include "library/trackmodel.h" |
2495 | +#include "library/basesqltablemodel.h" |
2496 | +#include "library/librarytablemodel.h" |
2497 | +#include "library/dao/playlistdao.h" |
2498 | +#include "library/dao/trackdao.h" |
2499 | + |
2500 | +class TrackCollection; |
2501 | + |
2502 | +class RhythmboxPlaylistModel : public BaseSqlTableModel, public virtual TrackModel |
2503 | { |
2504 | - enum Columns { |
2505 | - COLUMN_ARTIST = 0, |
2506 | - COLUMN_TITLE, |
2507 | - COLUMN_ALBUM, |
2508 | - COLUMN_DATE, |
2509 | - COLUMN_GENRE, |
2510 | - COLUMN_LOCATION, |
2511 | - COLUMN_DURATION, |
2512 | - NUM_COLUMNS |
2513 | - }; |
2514 | - |
2515 | Q_OBJECT |
2516 | - public: |
2517 | - RhythmboxPlaylistModel(RhythmboxTrackModel*); |
2518 | + public: |
2519 | + RhythmboxPlaylistModel(QObject* parent, TrackCollection* pTrackCollection); |
2520 | virtual ~RhythmboxPlaylistModel(); |
2521 | |
2522 | - //QAbstractTableModel stuff |
2523 | - virtual Qt::ItemFlags flags(const QModelIndex& index) const; |
2524 | - QMimeData* mimeData(const QModelIndexList &indexes) const; |
2525 | - virtual QVariant data(const QModelIndex & index, |
2526 | - int role = Qt::DisplayRole) const; |
2527 | - virtual QVariant headerData(int section, |
2528 | - Qt::Orientation orientation, |
2529 | - int role = Qt::DisplayRole) const; |
2530 | - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; |
2531 | - virtual int columnCount(const QModelIndex& parent) const; |
2532 | - |
2533 | - //Playlist Model stuff |
2534 | virtual TrackPointer getTrack(const QModelIndex& index) const; |
2535 | virtual QString getTrackLocation(const QModelIndex& index) const; |
2536 | virtual void search(const QString& searchText); |
2537 | virtual const QString currentSearch(); |
2538 | - virtual const QList<int>& searchColumns() const; |
2539 | virtual bool isColumnInternal(int column); |
2540 | virtual bool isColumnHiddenByDefault(int column); |
2541 | virtual void removeTrack(const QModelIndex& index); |
2542 | virtual void removeTracks(const QModelIndexList& indices); |
2543 | virtual bool addTrack(const QModelIndex& index, QString location); |
2544 | - virtual void moveTrack(const QModelIndex& sourceIndex, |
2545 | - const QModelIndex& destIndex); |
2546 | + virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex); |
2547 | + |
2548 | + virtual Qt::ItemFlags flags(const QModelIndex &index) const; |
2549 | + QMimeData* mimeData(const QModelIndexList &indexes) const; |
2550 | + |
2551 | QItemDelegate* delegateForColumn(const int i); |
2552 | - |
2553 | - virtual QList<QString> getPlaylists(); |
2554 | - virtual void setPlaylist(QString playlist); |
2555 | - |
2556 | - int numPlaylists(); |
2557 | - QString playlistTitle(int n); |
2558 | - |
2559 | - |
2560 | -/* |
2561 | - void scanPath(QString path); |
2562 | - bool trackExistsInDatabase(QString file_location); |
2563 | - |
2564 | - QList<TrackInfoObject*> dumpDB(); |
2565 | - |
2566 | - QSqlDatabase getDatabase(); |
2567 | - */ |
2568 | - public slots: |
2569 | - |
2570 | + TrackModel::CapabilitiesFlags getCapabilities() const; |
2571 | + /** sets the playlist **/ |
2572 | + void setPlaylist(QString path_name); |
2573 | + |
2574 | + private slots: |
2575 | + void slotSearch(const QString& searchText); |
2576 | |
2577 | signals: |
2578 | - void startedLoading(); |
2579 | - void progressLoading(QString path); |
2580 | - void finishedLoading(); |
2581 | + void doSearch(const QString& searchText); |
2582 | |
2583 | -private: |
2584 | - QDomNodeList m_playlistNodes; |
2585 | - RhythmboxTrackModel *m_pRhythmbox; |
2586 | - QMap<QString, QDomNodeList> m_mPlaylists; |
2587 | - QString m_sCurrentPlaylist; |
2588 | + private: |
2589 | + TrackCollection* m_pTrackCollection; |
2590 | + QSqlDatabase &m_database; |
2591 | QString m_currentSearch; |
2592 | }; |
2593 | - |
2594 | #endif |
2595 | |
2596 | === renamed file 'mixxx/src/library/rhythmboxtrackmodel.cpp' => 'mixxx/src/library/rhythmbox/rhythmboxtrackmodel.cpp' |
2597 | --- mixxx/src/library/rhythmboxtrackmodel.cpp 2010-11-16 21:01:45 +0000 |
2598 | +++ mixxx/src/library/rhythmbox/rhythmboxtrackmodel.cpp 2011-02-21 23:55:30 +0000 |
2599 | @@ -1,181 +1,141 @@ |
2600 | #include <QtCore> |
2601 | #include <QtGui> |
2602 | #include <QtSql> |
2603 | -#include <QtDebug> |
2604 | -#include <QtXmlPatterns/QXmlQuery> |
2605 | - |
2606 | -#include "rhythmboxtrackmodel.h" |
2607 | -#include "xmlparse.h" |
2608 | -#include "trackinfoobject.h" |
2609 | -#include "defs.h" |
2610 | +#include "library/trackcollection.h" |
2611 | +#include "library/rhythmbox/rhythmboxtrackmodel.h" |
2612 | |
2613 | #include "mixxxutils.cpp" |
2614 | |
2615 | -RhythmboxTrackModel::RhythmboxTrackModel() |
2616 | - : AbstractXmlTrackModel("mixxx.db.model.rhythmbox") { |
2617 | - |
2618 | - QXmlQuery query; |
2619 | - QString res; |
2620 | - QDomDocument rhythmdb; |
2621 | - |
2622 | - |
2623 | - /* |
2624 | - * Try and open the Rhythmbox DB. An API call which tells us where |
2625 | - * the file is would be nice. |
2626 | - */ |
2627 | - QFile db(QDir::homePath() + "/.gnome2/rhythmbox/rhythmdb.xml"); |
2628 | - if ( ! db.exists()) { |
2629 | - db.setFileName(QDir::homePath() + "/.local/share/rhythmbox/rhythmdb.xml"); |
2630 | - if ( ! db.exists()) |
2631 | - return; |
2632 | - } |
2633 | - |
2634 | - if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) |
2635 | - return; |
2636 | - |
2637 | - /* |
2638 | - * Use QXmlQuery to execute an XPath query. We add the version to |
2639 | - * the XPath query to make sure it is the schema we expect. |
2640 | - */ |
2641 | - query.setFocus(&db); |
2642 | - query.setQuery("rhythmdb[@version='1.4' or @version='1.6' or @version='1.7']/entry[@type='song']"); |
2643 | - if ( ! query.isValid()) |
2644 | - return; |
2645 | - |
2646 | - query.evaluateTo(&res); |
2647 | - db.close(); |
2648 | - |
2649 | - |
2650 | - /* |
2651 | - * Parse the result as an XML file. These shennanigans actually |
2652 | - * reduce the load time from a minute to a matter of seconds. |
2653 | - */ |
2654 | - rhythmdb.setContent("<rhythmdb version='1.6'>" + res + "</rhythmdb>"); |
2655 | - m_trackNodes = rhythmdb.elementsByTagName("entry"); |
2656 | - |
2657 | - |
2658 | - for (int i = 0; i < m_trackNodes.count(); i++) { |
2659 | - QDomNode n = m_trackNodes.at(i); |
2660 | - QString location = n.firstChildElement("location").text(); |
2661 | - |
2662 | - m_mTracksByLocation[location] = n; |
2663 | - } |
2664 | - qDebug() << "RhythmboxTrackModel: m_entryNodes size is" << m_trackNodes.size(); |
2665 | - |
2666 | - |
2667 | - addColumnName(RhythmboxTrackModel::COLUMN_ARTIST, tr("Artist")); |
2668 | - addColumnName(RhythmboxTrackModel::COLUMN_TITLE, tr("Title")); |
2669 | - addColumnName(RhythmboxTrackModel::COLUMN_ALBUM, tr("Album")); |
2670 | - addColumnName(RhythmboxTrackModel::COLUMN_DATE, tr("Date")); |
2671 | - addColumnName(RhythmboxTrackModel::COLUMN_GENRE, tr("Genre")); |
2672 | - addColumnName(RhythmboxTrackModel::COLUMN_LOCATION, tr("Location")); |
2673 | - addColumnName(RhythmboxTrackModel::COLUMN_DURATION, tr("Duration")); |
2674 | - |
2675 | - addSearchColumn(RhythmboxTrackModel::COLUMN_ARTIST); |
2676 | - addSearchColumn(RhythmboxTrackModel::COLUMN_TITLE); |
2677 | - addSearchColumn(RhythmboxTrackModel::COLUMN_ALBUM); |
2678 | - addSearchColumn(RhythmboxTrackModel::COLUMN_GENRE); |
2679 | - addSearchColumn(RhythmboxTrackModel::COLUMN_LOCATION); |
2680 | -} |
2681 | - |
2682 | -RhythmboxTrackModel::~RhythmboxTrackModel() |
2683 | -{ |
2684 | - |
2685 | -} |
2686 | +RhythmboxTrackModel::RhythmboxTrackModel(QObject* parent, |
2687 | + TrackCollection* pTrackCollection) |
2688 | + : TrackModel(pTrackCollection->getDatabase(), |
2689 | + "mixxx.db.model.rhythmbox"), |
2690 | + BaseSqlTableModel(parent, pTrackCollection, pTrackCollection->getDatabase()), |
2691 | + m_pTrackCollection(pTrackCollection), |
2692 | + m_database(m_pTrackCollection->getDatabase()) { |
2693 | + connect(this, SIGNAL(doSearch(const QString&)), this, SLOT(slotSearch(const QString&))); |
2694 | + setTable("rhythmbox_library"); |
2695 | + initHeaderData(); |
2696 | + setCaching(false); |
2697 | +} |
2698 | + |
2699 | +RhythmboxTrackModel::~RhythmboxTrackModel() { |
2700 | +} |
2701 | + |
2702 | +bool RhythmboxTrackModel::addTrack(const QModelIndex& index, QString location) { |
2703 | + return false; |
2704 | +} |
2705 | + |
2706 | +TrackPointer RhythmboxTrackModel::getTrack(const QModelIndex& index) const { |
2707 | + QString artist = index.sibling(index.row(), fieldIndex("artist")).data().toString(); |
2708 | + QString title = index.sibling(index.row(), fieldIndex("title")).data().toString(); |
2709 | + QString album = index.sibling(index.row(), fieldIndex("album")).data().toString(); |
2710 | + QString year = index.sibling(index.row(), fieldIndex("year")).data().toString(); |
2711 | + QString genre = index.sibling(index.row(), fieldIndex("genre")).data().toString(); |
2712 | + float bpm = index.sibling(index.row(), fieldIndex("bpm")).data().toString().toFloat(); |
2713 | + |
2714 | + QString location = index.sibling(index.row(), fieldIndex("location")).data().toString(); |
2715 | + |
2716 | + TrackInfoObject* pTrack = new TrackInfoObject(location); |
2717 | + pTrack->setArtist(artist); |
2718 | + pTrack->setTitle(title); |
2719 | + pTrack->setAlbum(album); |
2720 | + pTrack->setYear(year); |
2721 | + pTrack->setGenre(genre); |
2722 | + pTrack->setBpm(bpm); |
2723 | + |
2724 | + return TrackPointer(pTrack, &QObject::deleteLater); |
2725 | +} |
2726 | + |
2727 | +QString RhythmboxTrackModel::getTrackLocation(const QModelIndex& index) const { |
2728 | + QString location = index.sibling(index.row(), fieldIndex("location")).data().toString(); |
2729 | + return location; |
2730 | +} |
2731 | + |
2732 | +void RhythmboxTrackModel::removeTrack(const QModelIndex& index) { |
2733 | + |
2734 | +} |
2735 | + |
2736 | +void RhythmboxTrackModel::removeTracks(const QModelIndexList& indices) { |
2737 | + |
2738 | +} |
2739 | + |
2740 | +void RhythmboxTrackModel::moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex) { |
2741 | + |
2742 | +} |
2743 | + |
2744 | +void RhythmboxTrackModel::search(const QString& searchText) { |
2745 | + // qDebug() << "RhythmboxTrackModel::search()" << searchText |
2746 | + // << QThread::currentThread(); |
2747 | + emit(doSearch(searchText)); |
2748 | +} |
2749 | + |
2750 | +void RhythmboxTrackModel::slotSearch(const QString& searchText) { |
2751 | + if (!m_currentSearch.isNull() && m_currentSearch == searchText) |
2752 | + return; |
2753 | + m_currentSearch = searchText; |
2754 | + |
2755 | + QString filter; |
2756 | + QSqlField search("search", QVariant::String); |
2757 | + search.setValue("%" + searchText + "%"); |
2758 | + QString escapedText = database().driver()->formatValue(search); |
2759 | + filter = "(artist LIKE " + escapedText + " OR " + |
2760 | + "album LIKE " + escapedText + " OR " + |
2761 | + "title LIKE " + escapedText + ")"; |
2762 | + setFilter(filter); |
2763 | + |
2764 | +} |
2765 | + |
2766 | +const QString RhythmboxTrackModel::currentSearch() { |
2767 | + return m_currentSearch; |
2768 | +} |
2769 | + |
2770 | +bool RhythmboxTrackModel::isColumnInternal(int column) { |
2771 | + if (column == fieldIndex(LIBRARYTABLE_ID) || |
2772 | + column == fieldIndex(LIBRARYTABLE_MIXXXDELETED) || |
2773 | + column == fieldIndex(TRACKLOCATIONSTABLE_FSDELETED)) |
2774 | + return true; |
2775 | + return false; |
2776 | +} |
2777 | + |
2778 | +QMimeData* RhythmboxTrackModel::mimeData(const QModelIndexList &indexes) const { |
2779 | + QMimeData *mimeData = new QMimeData(); |
2780 | + QList<QUrl> urls; |
2781 | + |
2782 | + //Ok, so the list of indexes we're given contains separates indexes for |
2783 | + //each column, so even if only one row is selected, we'll have like 7 indexes. |
2784 | + //We need to only count each row once: |
2785 | + QList<int> rows; |
2786 | + |
2787 | + foreach (QModelIndex index, indexes) { |
2788 | + if (index.isValid()) { |
2789 | + if (!rows.contains(index.row())) { |
2790 | + rows.push_back(index.row()); |
2791 | + QUrl url = QUrl::fromLocalFile(getTrackLocation(index)); |
2792 | + if (!url.isValid()) |
2793 | + qDebug() << "ERROR invalid url\n"; |
2794 | + else |
2795 | + urls.append(url); |
2796 | + } |
2797 | + } |
2798 | + } |
2799 | + mimeData->setUrls(urls); |
2800 | + return mimeData; |
2801 | +} |
2802 | + |
2803 | |
2804 | QItemDelegate* RhythmboxTrackModel::delegateForColumn(const int i) { |
2805 | return NULL; |
2806 | } |
2807 | |
2808 | -QVariant RhythmboxTrackModel::data(const QModelIndex& item, int role) const { |
2809 | - if (!item.isValid()) |
2810 | - return QVariant(); |
2811 | - |
2812 | - QVariant value = AbstractXmlTrackModel::data(item, role); |
2813 | - |
2814 | - if ((role == Qt::DisplayRole || role == Qt::ToolTipRole) && |
2815 | - item.column() == COLUMN_DURATION) { |
2816 | - |
2817 | - if (qVariantCanConvert<int>(value)) { |
2818 | - value = MixxxUtils::secondsToMinutes(qVariantValue<int>(value)); |
2819 | - } |
2820 | - } |
2821 | - return value; |
2822 | -} |
2823 | - |
2824 | -bool RhythmboxTrackModel::isColumnInternal(int column) { |
2825 | - return false; |
2826 | -} |
2827 | +TrackModel::CapabilitiesFlags RhythmboxTrackModel::getCapabilities() const { |
2828 | + return TRACKMODELCAPS_NONE; |
2829 | +} |
2830 | + |
2831 | +Qt::ItemFlags RhythmboxTrackModel::flags(const QModelIndex &index) const { |
2832 | + return readOnlyFlags(index); |
2833 | +} |
2834 | + |
2835 | bool RhythmboxTrackModel::isColumnHiddenByDefault(int column) { |
2836 | return false; |
2837 | } |
2838 | - |
2839 | -QVariant RhythmboxTrackModel::getTrackColumnData(QDomNode songNode, const QModelIndex& index) const |
2840 | -{ |
2841 | - int date; |
2842 | - switch (index.column()) { |
2843 | - case RhythmboxTrackModel::COLUMN_ARTIST: |
2844 | - return songNode.firstChildElement("artist").text(); |
2845 | - case RhythmboxTrackModel::COLUMN_TITLE: |
2846 | - return songNode.firstChildElement("title").text(); |
2847 | - case RhythmboxTrackModel::COLUMN_ALBUM: |
2848 | - return songNode.firstChildElement("album").text(); |
2849 | - case RhythmboxTrackModel::COLUMN_DATE: |
2850 | - date = songNode.firstChildElement("date").text().toInt(); |
2851 | - // Rhythmbox uses the Rata Die Julian Day system while Qt uses true |
2852 | - // Julian Days. It's just a difference in epoch start dates. |
2853 | - if (date == 0) |
2854 | - return ""; |
2855 | - return QDate::fromJulianDay(ceil(float(date) + 1721424.5)).year(); |
2856 | - case RhythmboxTrackModel::COLUMN_GENRE: |
2857 | - return songNode.firstChildElement("genre").text(); |
2858 | - case RhythmboxTrackModel::COLUMN_LOCATION: |
2859 | - return songNode.firstChildElement("location").text(); |
2860 | - case RhythmboxTrackModel::COLUMN_DURATION: |
2861 | - return songNode.firstChildElement("duration").text(); |
2862 | - |
2863 | - default: |
2864 | - return QVariant(); |
2865 | - } |
2866 | -} |
2867 | - |
2868 | -TrackPointer RhythmboxTrackModel::parseTrackNode(QDomNode songNode) const |
2869 | -{ |
2870 | - QString strloc = songNode.firstChildElement("location").text(); |
2871 | - QByteArray strlocbytes = strloc.toUtf8(); |
2872 | - QUrl location = QUrl::fromEncoded(strlocbytes); |
2873 | - QString trackLocation = location.toLocalFile(); |
2874 | - |
2875 | - TrackInfoObject *pTrack = new TrackInfoObject(trackLocation); |
2876 | - |
2877 | - pTrack->setArtist(songNode.firstChildElement("artist").text()); |
2878 | - pTrack->setTitle(songNode.firstChildElement("title").text()); |
2879 | - pTrack->setAlbum(songNode.firstChildElement("album").text()); |
2880 | - int date = songNode.firstChildElement("date").text().toInt(); |
2881 | - // Rhythmbox uses the Rata Die Julian Day system while Qt uses true |
2882 | - // Julian Days. It's just a difference in epoch start dates. |
2883 | - if (date == 0) { |
2884 | - pTrack->setYear(""); |
2885 | - } else { |
2886 | - date = QDate::fromJulianDay(ceil(float(date) + 1721424.5)).year(); |
2887 | - pTrack->setYear(QString("%1").arg(date)); |
2888 | - } |
2889 | - pTrack->setGenre(songNode.firstChildElement("genre").text()); |
2890 | - pTrack->setDuration(songNode.firstChildElement("duration").text().toUInt()); |
2891 | - |
2892 | - // TODO(ywwg) why was this added? constructor above does the same -- rryan |
2893 | - pTrack->setLocation(trackLocation); |
2894 | - |
2895 | - // Have QObject handle deleting this track |
2896 | - return TrackPointer(pTrack, &QObject::deleteLater); |
2897 | -} |
2898 | - |
2899 | -//OwenB - for use by the playlistmodel |
2900 | -QDomNode RhythmboxTrackModel::getTrackNodeByLocation(const QString& location) const |
2901 | -{ |
2902 | - if ( !m_mTracksByLocation.contains(location)) |
2903 | - return QDomNode(); |
2904 | - |
2905 | - QDomNode songNode = m_mTracksByLocation[location]; |
2906 | - return songNode; |
2907 | -} |
2908 | |
2909 | === renamed file 'mixxx/src/library/rhythmboxtrackmodel.h' => 'mixxx/src/library/rhythmbox/rhythmboxtrackmodel.h' |
2910 | --- mixxx/src/library/rhythmboxtrackmodel.h 2010-11-16 21:01:45 +0000 |
2911 | +++ mixxx/src/library/rhythmbox/rhythmboxtrackmodel.h 2011-02-21 23:55:30 +0000 |
2912 | @@ -2,7 +2,7 @@ |
2913 | rhythmboxtracksource.h |
2914 | ------------------- |
2915 | begin : 8/15/2009 |
2916 | - copyright : (C) 2009 Albert Santoni |
2917 | + copyright : (C) 2011 Tobias Rafreider |
2918 | email : alberts@mixxx.org |
2919 | ***************************************************************************/ |
2920 | |
2921 | @@ -18,55 +18,52 @@ |
2922 | #ifndef RHYTHMBOXTRACKMODEL_H |
2923 | #define RHYTHMBOXTRACKMODEL_H |
2924 | |
2925 | + |
2926 | #include <QtSql> |
2927 | -#include <QtXml> |
2928 | -#include "trackmodel.h" |
2929 | -#include "abstractxmltrackmodel.h" |
2930 | - |
2931 | - |
2932 | -class QSqlDatabase; |
2933 | - |
2934 | - |
2935 | -/** |
2936 | - @author Albert Santoni |
2937 | -*/ |
2938 | -class RhythmboxTrackModel : public AbstractXmlTrackModel |
2939 | +#include <QItemDelegate> |
2940 | +#include <QtCore> |
2941 | +#include "library/trackmodel.h" |
2942 | +#include "library/basesqltablemodel.h" |
2943 | +#include "library/librarytablemodel.h" |
2944 | +#include "library/dao/playlistdao.h" |
2945 | +#include "library/dao/trackdao.h" |
2946 | + |
2947 | +class TrackCollection; |
2948 | + |
2949 | +class RhythmboxTrackModel : public BaseSqlTableModel, public virtual TrackModel |
2950 | { |
2951 | - enum Columns { |
2952 | - COLUMN_ARTIST = 0, |
2953 | - COLUMN_TITLE, |
2954 | - COLUMN_ALBUM, |
2955 | - COLUMN_DATE, |
2956 | - COLUMN_GENRE, |
2957 | - COLUMN_LOCATION, |
2958 | - COLUMN_DURATION, |
2959 | - NUM_COLUMNS |
2960 | - }; |
2961 | - |
2962 | Q_OBJECT |
2963 | -public: |
2964 | - RhythmboxTrackModel(); |
2965 | + public: |
2966 | + RhythmboxTrackModel(QObject* parent, TrackCollection* pTrackCollection); |
2967 | virtual ~RhythmboxTrackModel(); |
2968 | - virtual QItemDelegate* delegateForColumn(const int i); |
2969 | + |
2970 | + virtual TrackPointer getTrack(const QModelIndex& index) const; |
2971 | + virtual QString getTrackLocation(const QModelIndex& index) const; |
2972 | + virtual void search(const QString& searchText); |
2973 | + virtual const QString currentSearch(); |
2974 | virtual bool isColumnInternal(int column); |
2975 | virtual bool isColumnHiddenByDefault(int column); |
2976 | - virtual QVariant data(const QModelIndex& item, int role) const; |
2977 | - QDomNode getTrackNodeByLocation(const QString& ) const; |
2978 | - |
2979 | -protected: |
2980 | - virtual TrackPointer parseTrackNode(QDomNode node) const; |
2981 | - /* Implemented by AbstractXmlTrackModel implementations to return the data for song columns */ |
2982 | - virtual QVariant getTrackColumnData(QDomNode node, const QModelIndex& index) const; |
2983 | - /* Called by AbstractXmlTrackModel implementations to enumerate their columns */ |
2984 | - |
2985 | -public slots: |
2986 | - |
2987 | -signals: |
2988 | - void startedLoading(); |
2989 | - void progressLoading(QString path); |
2990 | - void finishedLoading(); |
2991 | - |
2992 | - friend class RhythmboxPlaylistModel; |
2993 | + virtual void removeTrack(const QModelIndex& index); |
2994 | + virtual void removeTracks(const QModelIndexList& indices); |
2995 | + virtual bool addTrack(const QModelIndex& index, QString location); |
2996 | + virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex); |
2997 | + |
2998 | + virtual Qt::ItemFlags flags(const QModelIndex &index) const; |
2999 | + QMimeData* mimeData(const QModelIndexList &indexes) const; |
3000 | + |
3001 | + QItemDelegate* delegateForColumn(const int i); |
3002 | + TrackModel::CapabilitiesFlags getCapabilities() const; |
3003 | + |
3004 | + private slots: |
3005 | + void slotSearch(const QString& searchText); |
3006 | + |
3007 | + signals: |
3008 | + void doSearch(const QString& searchText); |
3009 | + |
3010 | + private: |
3011 | + TrackCollection* m_pTrackCollection; |
3012 | + QSqlDatabase &m_database; |
3013 | + QString m_currentSearch; |
3014 | }; |
3015 | |
3016 | #endif |
3017 | |
3018 | === modified file 'mixxx/src/library/sidebarmodel.cpp' |
3019 | --- mixxx/src/library/sidebarmodel.cpp 2011-02-20 20:47:37 +0000 |
3020 | +++ mixxx/src/library/sidebarmodel.cpp 2011-02-21 23:55:30 +0000 |
3021 | @@ -19,13 +19,18 @@ |
3022 | void SidebarModel::addLibraryFeature(LibraryFeature* feature) { |
3023 | m_sFeatures.push_back(feature); |
3024 | connect(feature, SIGNAL(featureUpdated()), this, SLOT(refreshData())); |
3025 | + connect(feature, SIGNAL(featureIsLoading(LibraryFeature*)), |
3026 | + this, SLOT(slotFeatureIsLoading(LibraryFeature*))); |
3027 | + connect(feature, SIGNAL(featureLoadingFinished(LibraryFeature*)), |
3028 | + this,SLOT(slotFeatureLoadingFinished(LibraryFeature*))); |
3029 | + |
3030 | QAbstractItemModel* model = feature->getChildModel(); |
3031 | |
3032 | connect(model, SIGNAL(modelReset()), |
3033 | this, SLOT(slotModelReset())); |
3034 | connect(model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)), |
3035 | this, SLOT(slotDataChanged(const QModelIndex&,const QModelIndex&))); |
3036 | - |
3037 | + |
3038 | connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)), |
3039 | this, SLOT(slotRowsAboutToBeInserted(const QModelIndex&, int, int))); |
3040 | connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)), |
3041 | @@ -34,7 +39,7 @@ |
3042 | this, SLOT(slotRowsInserted(const QModelIndex&, int, int))); |
3043 | connect(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), |
3044 | this, SLOT(slotRowsRemoved(const QModelIndex&, int, int))); |
3045 | - |
3046 | + |
3047 | } |
3048 | |
3049 | QModelIndex SidebarModel::getDefaultSelection() { |
3050 | @@ -77,19 +82,12 @@ |
3051 | const QAbstractItemModel* childModel = m_sFeatures[parent.row()]->getChildModel(); |
3052 | QModelIndex childIndex = childModel->index(row, column); |
3053 | TreeItem* tree_item = (TreeItem*)childIndex.internalPointer(); |
3054 | - if (childIndex.isValid()) |
3055 | - { |
3056 | - |
3057 | + if (tree_item && childIndex.isValid()) { |
3058 | return createIndex(childIndex.row(), childIndex.column(), (void*)tree_item); |
3059 | - } |
3060 | - else |
3061 | - { |
3062 | - |
3063 | + } else { |
3064 | return QModelIndex(); |
3065 | } |
3066 | - } |
3067 | - else |
3068 | - { |
3069 | + } else { |
3070 | // We have selected an item within the childmodel |
3071 | // This item has always an internal pointer of (sub)type TreeItem |
3072 | TreeItem* tree_item = (TreeItem*)parent.internalPointer(); |
3073 | @@ -104,29 +102,25 @@ |
3074 | if (index.isValid()) { |
3075 | /* If we have selected the root of a library feature |
3076 | * its internal pointer is the current sidebar object model |
3077 | - * A root library feature has no parent and thus we return |
3078 | + * A root library feature has no parent and thus we return |
3079 | * an invalid QModelIndex |
3080 | */ |
3081 | if (index.internalPointer() == this) { |
3082 | - |
3083 | return QModelIndex(); |
3084 | - } |
3085 | - else |
3086 | - { |
3087 | + } else { |
3088 | TreeItem* tree_item = (TreeItem*)index.internalPointer(); |
3089 | - //if we have selected an item at the first level of a childnode |
3090 | - if(tree_item->parent()->data() == "$root"){ |
3091 | - //qDebug() << "Parent== root"; |
3092 | + // if we have selected an item at the first level of a childnode |
3093 | + if (tree_item->parent()->data() == "$root"){ |
3094 | LibraryFeature* feature = tree_item->getFeature(); |
3095 | - for (int i = 0; i < m_sFeatures.size(); ++i) |
3096 | - { |
3097 | + for (int i = 0; i < m_sFeatures.size(); ++i) { |
3098 | if (feature == m_sFeatures[i]) { |
3099 | - //create a ModelIndex for parent 'this' having a library feature at position 'i' |
3100 | + // create a ModelIndex for parent 'this' having a |
3101 | + // library feature at position 'i' |
3102 | return createIndex(i, 0, (void*)this); |
3103 | } |
3104 | } |
3105 | } |
3106 | - //if we have selected an item at some deeper level of a childnode |
3107 | + // if we have selected an item at some deeper level of a childnode |
3108 | return createIndex(tree_item->parent()->row(), 0 , tree_item->parent()); |
3109 | } |
3110 | } |
3111 | @@ -137,14 +131,14 @@ |
3112 | //qDebug() << "SidebarModel::rowCount parent=" << parent.data(); |
3113 | if (parent.isValid()) { |
3114 | if (parent.internalPointer() == this) { |
3115 | - |
3116 | return m_sFeatures[parent.row()]->getChildModel()->rowCount(); |
3117 | - } |
3118 | - else { |
3119 | - //We support tree models deeper than 1 level |
3120 | + } else { |
3121 | + // We support tree models deeper than 1 level |
3122 | TreeItem* tree_item = (TreeItem*)parent.internalPointer(); |
3123 | - |
3124 | - return tree_item->childCount(); |
3125 | + if (tree_item) { |
3126 | + return tree_item->childCount(); |
3127 | + } |
3128 | + return 0; |
3129 | } |
3130 | } |
3131 | return m_sFeatures.size(); |
3132 | @@ -155,21 +149,19 @@ |
3133 | // TODO(rryan) will we ever have columns? I don't think so. |
3134 | return 1; |
3135 | } |
3136 | + |
3137 | bool SidebarModel::hasChildren(const QModelIndex& parent) const { |
3138 | - |
3139 | - if (parent.isValid()) { |
3140 | + if (parent.isValid()) { |
3141 | if (parent.internalPointer() == this) { |
3142 | return QAbstractItemModel::hasChildren(parent); |
3143 | - } |
3144 | - else |
3145 | - { |
3146 | + } else { |
3147 | TreeItem* tree_item = (TreeItem*)parent.internalPointer(); |
3148 | - LibraryFeature* feature = tree_item->getFeature(); |
3149 | - return feature->getChildModel()->hasChildren(parent); |
3150 | + if (tree_item) { |
3151 | + LibraryFeature* feature = tree_item->getFeature(); |
3152 | + return feature->getChildModel()->hasChildren(parent); |
3153 | + } |
3154 | } |
3155 | - } |
3156 | - else |
3157 | - { |
3158 | + } else { |
3159 | return QAbstractItemModel::hasChildren(parent); |
3160 | } |
3161 | } |
3162 | @@ -178,25 +170,22 @@ |
3163 | // qDebug("SidebarModel::data row=%d column=%d pointer=%8x, role=%d", |
3164 | // index.row(), index.column(), index.internalPointer(), role); |
3165 | if (index.isValid()) { |
3166 | - if (index.internalPointer() == this) |
3167 | - { |
3168 | + if (index.internalPointer() == this) { |
3169 | if (role == Qt::DisplayRole) { |
3170 | return m_sFeatures[index.row()]->title(); |
3171 | - } |
3172 | - else if (role == Qt::DecorationRole) { |
3173 | + } else if (role == Qt::DecorationRole) { |
3174 | return m_sFeatures[index.row()]->getIcon(); |
3175 | } |
3176 | - } |
3177 | - else { |
3178 | + } else { |
3179 | TreeItem* tree_item = (TreeItem*)index.internalPointer(); |
3180 | - |
3181 | - if (role == Qt::DisplayRole) { |
3182 | - return tree_item->data(); |
3183 | - } |
3184 | - else if (role == Qt::DecorationRole) { |
3185 | - return tree_item->getIcon(); |
3186 | - } |
3187 | - |
3188 | + |
3189 | + if (tree_item) { |
3190 | + if (role == Qt::DisplayRole) { |
3191 | + return tree_item->data(); |
3192 | + } else if (role == Qt::DecorationRole) { |
3193 | + return tree_item->getIcon(); |
3194 | + } |
3195 | + } |
3196 | } |
3197 | } |
3198 | return QVariant(); |
3199 | @@ -205,9 +194,8 @@ |
3200 | void SidebarModel::clicked(const QModelIndex& index) { |
3201 | //qDebug() << "SidebarModel::clicked() index=" << index; |
3202 | |
3203 | - |
3204 | - //We use clicked() for keyboard and mouse control, and the |
3205 | - //following code breaks that for us: |
3206 | + // We use clicked() for keyboard and mouse control, and the |
3207 | + // following code breaks that for us: |
3208 | /*if (QApplication::mouseButtons() != Qt::LeftButton) { |
3209 | return; |
3210 | }*/ |
3211 | @@ -215,50 +203,48 @@ |
3212 | if (index.isValid()) { |
3213 | if (index.internalPointer() == this) { |
3214 | m_sFeatures[index.row()]->activate(); |
3215 | - } |
3216 | - else |
3217 | - { |
3218 | + } else { |
3219 | TreeItem* tree_item = (TreeItem*)index.internalPointer(); |
3220 | - LibraryFeature* feature = tree_item->getFeature(); |
3221 | - feature->activateChild(index); |
3222 | + if (tree_item) { |
3223 | + LibraryFeature* feature = tree_item->getFeature(); |
3224 | + feature->activateChild(index); |
3225 | + } |
3226 | } |
3227 | } |
3228 | } |
3229 | |
3230 | void SidebarModel::rightClicked(const QPoint& globalPos, const QModelIndex& index) { |
3231 | //qDebug() << "SidebarModel::rightClicked() index=" << index; |
3232 | - if (index.isValid()) |
3233 | + if (index.isValid()) |
3234 | { |
3235 | - if (index.internalPointer() == this) |
3236 | + if (index.internalPointer() == this) |
3237 | { |
3238 | m_sFeatures[index.row()]->activate(); |
3239 | m_sFeatures[index.row()]->onRightClick(globalPos); |
3240 | } |
3241 | - else |
3242 | + else |
3243 | { |
3244 | TreeItem* tree_item = (TreeItem*)index.internalPointer(); |
3245 | - LibraryFeature* feature = tree_item->getFeature(); |
3246 | - feature->activateChild(index); |
3247 | - feature->onRightClickChild(globalPos, index); |
3248 | - |
3249 | + if (tree_item) { |
3250 | + LibraryFeature* feature = tree_item->getFeature(); |
3251 | + feature->activateChild(index); |
3252 | + feature->onRightClickChild(globalPos, index); |
3253 | + } |
3254 | } |
3255 | } |
3256 | } |
3257 | |
3258 | -bool SidebarModel::dropAccept(const QModelIndex& index, QUrl url) |
3259 | -{ |
3260 | +bool SidebarModel::dropAccept(const QModelIndex& index, QUrl url) { |
3261 | //qDebug() << "SidebarModel::dropAccept() index=" << index << url; |
3262 | if (index.isValid()) { |
3263 | - if (index.internalPointer() == this) |
3264 | - { |
3265 | + if (index.internalPointer() == this) { |
3266 | return m_sFeatures[index.row()]->dropAccept(url); |
3267 | - } |
3268 | - else |
3269 | - { |
3270 | + } else { |
3271 | TreeItem* tree_item = (TreeItem*)index.internalPointer(); |
3272 | - LibraryFeature* feature = tree_item->getFeature(); |
3273 | - return feature->dropAcceptChild(index, url); |
3274 | - |
3275 | + if (tree_item) { |
3276 | + LibraryFeature* feature = tree_item->getFeature(); |
3277 | + return feature->dropAcceptChild(index, url); |
3278 | + } |
3279 | } |
3280 | } |
3281 | |
3282 | @@ -269,43 +255,41 @@ |
3283 | { |
3284 | //qDebug() << "SidebarModel::dragMoveAccept() index=" << index << url; |
3285 | if (index.isValid()) { |
3286 | - if (index.internalPointer() == this) |
3287 | - { |
3288 | + if (index.internalPointer() == this) { |
3289 | return m_sFeatures[index.row()]->dragMoveAccept(url); |
3290 | - } |
3291 | - else |
3292 | - { |
3293 | - |
3294 | + } else { |
3295 | TreeItem* tree_item = (TreeItem*)index.internalPointer(); |
3296 | - LibraryFeature* feature = tree_item->getFeature(); |
3297 | - return feature->dragMoveAcceptChild(index, url); |
3298 | + if (tree_item) { |
3299 | + LibraryFeature* feature = tree_item->getFeature(); |
3300 | + return feature->dragMoveAcceptChild(index, url); |
3301 | + } |
3302 | } |
3303 | } |
3304 | return false; |
3305 | } |
3306 | -//Translates an index from the child models to an index of the sidebar models |
3307 | + |
3308 | +// Translates an index from the child models to an index of the sidebar models |
3309 | QModelIndex SidebarModel::translateSourceIndex(const QModelIndex& index) { |
3310 | QModelIndex translatedIndex; |
3311 | - |
3312 | + |
3313 | /* These method is called from the slot functions below. |
3314 | * QObject::sender() return the object which emitted the signal |
3315 | * handled by the slot functions. |
3316 | - |
3317 | + |
3318 | * For child models, this always the child models itself |
3319 | */ |
3320 | - |
3321 | - const QAbstractItemModel* model = (QAbstractItemModel*)sender(); |
3322 | - |
3323 | + |
3324 | + const QAbstractItemModel* model = dynamic_cast<QAbstractItemModel*>(sender()); |
3325 | + |
3326 | Q_ASSERT(model); |
3327 | if (index.isValid()) { |
3328 | TreeItem* item = (TreeItem*)index.internalPointer(); |
3329 | translatedIndex = createIndex(index.row(), index.column(), item); |
3330 | - |
3331 | - } |
3332 | - else |
3333 | + } |
3334 | + else |
3335 | { |
3336 | //Comment from Tobias Rafreider --> Dead Code???? |
3337 | - |
3338 | + |
3339 | for (int i = 0; i < m_sFeatures.size(); ++i) { |
3340 | if (m_sFeatures[i]->getChildModel() == model) { |
3341 | translatedIndex = createIndex(i, 0, (void*)this); |
3342 | @@ -314,20 +298,21 @@ |
3343 | } |
3344 | return translatedIndex; |
3345 | } |
3346 | + |
3347 | void SidebarModel::slotDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) { |
3348 | //qDebug() << "slotDataChanged topLeft:" << topLeft << "bottomRight:" << bottomRight; |
3349 | } |
3350 | |
3351 | void SidebarModel::slotRowsAboutToBeInserted(const QModelIndex& parent, int start, int end) { |
3352 | //qDebug() << "slotRowsABoutToBeInserted" << parent << start << end; |
3353 | - |
3354 | - QModelIndex newParent = translateSourceIndex(parent); |
3355 | + |
3356 | + QModelIndex newParent = translateSourceIndex(parent); |
3357 | beginInsertRows(newParent, start, end); |
3358 | } |
3359 | |
3360 | void SidebarModel::slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { |
3361 | //qDebug() << "slotRowsABoutToBeRemoved" << parent << start << end; |
3362 | - |
3363 | + |
3364 | QModelIndex newParent = translateSourceIndex(parent); |
3365 | beginRemoveRows(newParent, start, end); |
3366 | } |
3367 | @@ -349,3 +334,41 @@ |
3368 | // will close any open items. |
3369 | reset(); |
3370 | } |
3371 | + |
3372 | +/* |
3373 | + * Call this slot whenever the title of the feature has changed. |
3374 | + * See RhythmboxFeature for an example. |
3375 | + * While the rhythmbox music collection is parsed |
3376 | + * the title becomes 'Rhythmbox (loading)' |
3377 | + */ |
3378 | +void SidebarModel::slotFeatureIsLoading(LibraryFeature * feature) |
3379 | +{ |
3380 | + featureRenamed(feature); |
3381 | + selectFeature(feature); |
3382 | +} |
3383 | + |
3384 | +/* Tobias: This slot is somewhat redundant but I decided |
3385 | + * to leave it for code readability reasons |
3386 | + */ |
3387 | +void SidebarModel::slotFeatureLoadingFinished(LibraryFeature * feature){ |
3388 | + featureRenamed(feature); |
3389 | + selectFeature(feature); |
3390 | +} |
3391 | + |
3392 | +void SidebarModel::featureRenamed(LibraryFeature* pFeature){ |
3393 | + for (int i=0; i < m_sFeatures.size(); ++i) { |
3394 | + if (m_sFeatures[i] == pFeature) { |
3395 | + QModelIndex ind = index(i, 0); |
3396 | + emit(dataChanged(ind, ind)); |
3397 | + } |
3398 | + } |
3399 | +} |
3400 | + |
3401 | +void SidebarModel::selectFeature(LibraryFeature* pFeature) { |
3402 | + for (int i=0; i < m_sFeatures.size(); ++i) { |
3403 | + if (m_sFeatures[i] == pFeature) { |
3404 | + QModelIndex ind = index(i, 0); |
3405 | + emit(selectIndex(ind)); |
3406 | + } |
3407 | + } |
3408 | +} |
3409 | |
3410 | === modified file 'mixxx/src/library/sidebarmodel.h' |
3411 | --- mixxx/src/library/sidebarmodel.h 2011-02-12 19:25:05 +0000 |
3412 | +++ mixxx/src/library/sidebarmodel.h 2011-02-21 23:55:30 +0000 |
3413 | @@ -33,12 +33,13 @@ |
3414 | bool dropAccept(const QModelIndex& index, QUrl url); |
3415 | bool dragMoveAccept(const QModelIndex& index, QUrl url); |
3416 | virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const; |
3417 | + |
3418 | public slots: |
3419 | void clicked(const QModelIndex& index); |
3420 | void rightClicked(const QPoint& globalPos, const QModelIndex& index); |
3421 | void refreshData(); |
3422 | + void selectFeature(LibraryFeature* pFeature); |
3423 | |
3424 | - public slots: |
3425 | // Slots for every single QAbstractItemModel signal |
3426 | // void slotColumnsAboutToBeInserted(const QModelIndex& parent, int start, int end); |
3427 | // void slotColumnsAboutToBeRemoved(const QModelIndex & parent, int start, int end); |
3428 | @@ -55,9 +56,15 @@ |
3429 | void slotRowsInserted(const QModelIndex& parent, int start, int end); |
3430 | void slotRowsRemoved(const QModelIndex& parent, int start, int end); |
3431 | void slotModelReset(); |
3432 | + void slotFeatureIsLoading(LibraryFeature*); |
3433 | + void slotFeatureLoadingFinished(LibraryFeature*); |
3434 | + |
3435 | + signals: |
3436 | + void selectIndex(const QModelIndex& index); |
3437 | |
3438 | private: |
3439 | QModelIndex translateSourceIndex(const QModelIndex& parent); |
3440 | + void featureRenamed(LibraryFeature*); |
3441 | QList<LibraryFeature*> m_sFeatures; |
3442 | unsigned int m_iDefaultSelectedIndex; /** Index of the item in the sidebar model to select at startup. */ |
3443 | }; |
3444 | |
3445 | === modified file 'mixxx/src/library/trackcollection.cpp' |
3446 | --- mixxx/src/library/trackcollection.cpp 2011-01-20 21:52:58 +0000 |
3447 | +++ mixxx/src/library/trackcollection.cpp 2011-02-21 23:55:30 +0000 |
3448 | @@ -68,7 +68,7 @@ |
3449 | return false; |
3450 | } |
3451 | |
3452 | - int requiredSchemaVersion = 10; |
3453 | + int requiredSchemaVersion = 11; |
3454 | if (!SchemaManager::upgradeToSchemaVersion(m_pConfig, m_db, |
3455 | requiredSchemaVersion)) { |
3456 | QMessageBox::warning(0, tr("Cannot upgrade database schema"), |
3457 | |
3458 | === added directory 'mixxx/src/library/traktor' |
3459 | === renamed file 'mixxx/src/library/traktorfeature.cpp' => 'mixxx/src/library/traktor/traktorfeature.cpp' |
3460 | --- mixxx/src/library/traktorfeature.cpp 2011-01-13 18:40:56 +0000 |
3461 | +++ mixxx/src/library/traktor/traktorfeature.cpp 2011-02-21 23:55:30 +0000 |
3462 | @@ -7,24 +7,33 @@ |
3463 | #include <QMap> |
3464 | #include <QSettings> |
3465 | |
3466 | -#include "library/traktorfeature.h" |
3467 | +#include "library/traktor/traktorfeature.h" |
3468 | |
3469 | #include "library/librarytablemodel.h" |
3470 | #include "library/missingtablemodel.h" |
3471 | -#include "library/proxytrackmodel.h" |
3472 | #include "library/trackcollection.h" |
3473 | -#include "treeitem.h" |
3474 | +#include "library/treeitem.h" |
3475 | |
3476 | |
3477 | TraktorFeature::TraktorFeature(QObject* parent, TrackCollection* pTrackCollection): |
3478 | - m_pTrackCollection(pTrackCollection), |
3479 | - m_database(m_pTrackCollection->getDatabase()) |
3480 | -{ |
3481 | - |
3482 | - m_isActivated = false; |
3483 | + m_pTrackCollection(pTrackCollection) { |
3484 | + m_isActivated = false; |
3485 | m_pTraktorTableModel = new TraktorTableModel(this, m_pTrackCollection); |
3486 | m_pTraktorPlaylistModel = new TraktorPlaylistModel(this, m_pTrackCollection); |
3487 | - |
3488 | + m_title = tr("Traktor"); |
3489 | + if (!m_database.isOpen()) { |
3490 | + m_database = QSqlDatabase::addDatabase("QSQLITE", "TRAKTOR_SCANNER"); |
3491 | + m_database.setHostName("localhost"); |
3492 | + m_database.setDatabaseName(MIXXX_DB_PATH); |
3493 | + m_database.setUserName("mixxx"); |
3494 | + m_database.setPassword("mixxx"); |
3495 | + |
3496 | + //Open the database connection in this thread. |
3497 | + if (!m_database.open()) { |
3498 | + qDebug() << "Failed to open database for iTunes scanner." << m_database.lastError(); |
3499 | + } |
3500 | + } |
3501 | + connect(&m_future_watcher, SIGNAL(finished()), this, SLOT(onTrackCollectionLoaded())); |
3502 | } |
3503 | |
3504 | TraktorFeature::~TraktorFeature() { |
3505 | @@ -35,7 +44,7 @@ |
3506 | } |
3507 | |
3508 | QVariant TraktorFeature::title() { |
3509 | - return tr("Traktor"); |
3510 | + return m_title; |
3511 | } |
3512 | |
3513 | QIcon TraktorFeature::getIcon() { |
3514 | @@ -43,7 +52,7 @@ |
3515 | } |
3516 | bool TraktorFeature::isSupported() { |
3517 | return (QFile::exists(getTraktorMusicDatabase())); |
3518 | - |
3519 | + |
3520 | } |
3521 | TreeItemModel* TraktorFeature::getChildModel() { |
3522 | return &m_childModel; |
3523 | @@ -51,42 +60,51 @@ |
3524 | |
3525 | void TraktorFeature::refreshLibraryModels() |
3526 | { |
3527 | - |
3528 | + |
3529 | } |
3530 | |
3531 | void TraktorFeature::activate() { |
3532 | qDebug() << "TraktorFeature::activate()"; |
3533 | - |
3534 | + |
3535 | if(!m_isActivated){ |
3536 | - if (QMessageBox::question( |
3537 | - NULL, |
3538 | - tr("Load Traktor Library?"), |
3539 | - tr("Would you like to load your Traktor library?"), |
3540 | - QMessageBox::Ok, |
3541 | - QMessageBox::Cancel) |
3542 | - == QMessageBox::Cancel) { |
3543 | - return; |
3544 | - } |
3545 | - if(importLibrary(getTraktorMusicDatabase())) |
3546 | - m_isActivated = true; |
3547 | - } |
3548 | - emit(showTrackModel(m_pTraktorTableModel)); |
3549 | - |
3550 | + |
3551 | + m_isActivated = true; |
3552 | + /* Ususally the maximum number of threads |
3553 | + * is > 2 depending on the CPU cores |
3554 | + * Unfortunately, within VirtualBox |
3555 | + * the maximum number of allowed threads |
3556 | + * is 1 at all times We'll need to increase |
3557 | + * the number to > 1, otherwise importing the music collection |
3558 | + * takes place when the GUI threads terminates, i.e., on |
3559 | + * Mixxx shutdown. |
3560 | + */ |
3561 | + QThreadPool::globalInstance()->setMaxThreadCount(4); //Tobias decided to use 4 |
3562 | + // Let a worker thread do the XML parsing |
3563 | + m_future = QtConcurrent::run(this, &TraktorFeature::importLibrary, getTraktorMusicDatabase()); |
3564 | + m_future_watcher.setFuture(m_future); |
3565 | + m_title = tr("Traktor (loading)"); |
3566 | + //calls a slot in the sidebar model such that 'iTunes (isLoading)' is displayed. |
3567 | + emit (featureIsLoading(this)); |
3568 | + } |
3569 | + else{ |
3570 | + emit(showTrackModel(m_pTraktorTableModel)); |
3571 | + } |
3572 | + |
3573 | + |
3574 | } |
3575 | |
3576 | void TraktorFeature::activateChild(const QModelIndex& index) { |
3577 | - |
3578 | + |
3579 | if(!index.isValid()) return; |
3580 | - |
3581 | + |
3582 | //access underlying TreeItem object |
3583 | TreeItem *item = static_cast<TreeItem*>(index.internalPointer()); |
3584 | - |
3585 | + |
3586 | if(item->isPlaylist()){ |
3587 | qDebug() << "Activate Traktor Playlist: " << item->dataPath().toString(); |
3588 | m_pTraktorPlaylistModel->setPlaylist(item->dataPath().toString()); |
3589 | emit(showTrackModel(m_pTraktorPlaylistModel)); |
3590 | - |
3591 | - } |
3592 | + } |
3593 | } |
3594 | |
3595 | void TraktorFeature::onRightClick(const QPoint& globalPos) { |
3596 | @@ -112,8 +130,12 @@ |
3597 | QUrl url) { |
3598 | return false; |
3599 | } |
3600 | -bool TraktorFeature::importLibrary(QString file){ |
3601 | - |
3602 | +TreeItem* TraktorFeature::importLibrary(QString file){ |
3603 | + //Give thread a low priority |
3604 | + QThread* thisThread = QThread::currentThread(); |
3605 | + thisThread->setPriority(QThread::LowestPriority); |
3606 | + //Invisible root item of Traktor's child model |
3607 | + TreeItem* root = NULL; |
3608 | //Delete all table entries of Traktor feature |
3609 | m_database.transaction(); |
3610 | clearTable("traktor_playlist_tracks"); |
3611 | @@ -123,7 +145,7 @@ |
3612 | |
3613 | m_database.transaction(); |
3614 | QSqlQuery query(m_database); |
3615 | - query.prepare("INSERT INTO traktor_library (artist, title, album, year, genre,comment," |
3616 | + query.prepare("INSERT INTO traktor_library (artist, title, album, year, genre,comment," |
3617 | "tracknumber," |
3618 | "bpm, bitrate," |
3619 | "duration, location," |
3620 | @@ -134,7 +156,7 @@ |
3621 | ":duration, :location," |
3622 | ":rating," |
3623 | ":key)"); |
3624 | - |
3625 | + |
3626 | //Parse Trakor XML file using SAX (for performance) |
3627 | QFile traktor_file(file); |
3628 | if (!traktor_file.open(QIODevice::ReadOnly | QIODevice::Text)) { |
3629 | @@ -147,8 +169,8 @@ |
3630 | bool inPlaylistsTag = false; |
3631 | bool isRootFolderParsed = false; |
3632 | int nAudioFiles = 0; |
3633 | - |
3634 | - while (!xml.atEnd()) |
3635 | + |
3636 | + while (!xml.atEnd()) |
3637 | { |
3638 | xml.readNext(); |
3639 | if(xml.isStartElement()) |
3640 | @@ -156,11 +178,11 @@ |
3641 | if(xml.name() == "COLLECTION") |
3642 | { |
3643 | inCollectionTag = true; |
3644 | - |
3645 | + |
3646 | } |
3647 | /* |
3648 | * Each "ENTRY" tag in <COLLECTION> represents a track |
3649 | - */ |
3650 | + */ |
3651 | if(inCollectionTag && xml.name() == "ENTRY" ) |
3652 | { |
3653 | inEntryTag = true; |
3654 | @@ -168,22 +190,22 @@ |
3655 | parseTrack(xml, query); |
3656 | |
3657 | ++nAudioFiles; //increment number of files in the music collection |
3658 | - } |
3659 | + } |
3660 | if(xml.name() == "PLAYLISTS") |
3661 | { |
3662 | inPlaylistsTag = true; |
3663 | - |
3664 | + |
3665 | } |
3666 | if(inPlaylistsTag && !isRootFolderParsed && xml.name() == "NODE"){ |
3667 | QXmlStreamAttributes attr = xml.attributes(); |
3668 | QString nodetype = attr.value("TYPE").toString(); |
3669 | QString name = attr.value("NAME").toString(); |
3670 | - |
3671 | - if(nodetype == "FOLDER" && name == "$ROOT"){ |
3672 | + |
3673 | + if(nodetype == "FOLDER" && name == "$ROOT"){ |
3674 | //process all playlists |
3675 | - parsePlaylists(xml); |
3676 | + root = parsePlaylists(xml); |
3677 | isRootFolderParsed = true; |
3678 | - } |
3679 | + } |
3680 | } |
3681 | |
3682 | } |
3683 | @@ -192,37 +214,39 @@ |
3684 | if(xml.name() == "COLLECTION") |
3685 | { |
3686 | inCollectionTag = false; |
3687 | - |
3688 | + |
3689 | } |
3690 | if(xml.name() == "ENTRY" && inCollectionTag) |
3691 | { |
3692 | inEntryTag = false; |
3693 | - |
3694 | + |
3695 | } |
3696 | if(xml.name() == "PLAYLISTS" && inPlaylistsTag) |
3697 | { |
3698 | inPlaylistsTag = false; |
3699 | } |
3700 | - |
3701 | + |
3702 | } |
3703 | } |
3704 | if (xml.hasError()) { |
3705 | // do error handling |
3706 | qDebug() << "Cannot process Traktor music collection"; |
3707 | + if(root) |
3708 | + delete root; |
3709 | return false; |
3710 | } |
3711 | - |
3712 | + |
3713 | qDebug() << "Found: " << nAudioFiles << " audio files in Traktor"; |
3714 | //initialize TraktorTableModel |
3715 | m_database.commit(); |
3716 | - m_pTraktorTableModel->select(); |
3717 | - return true; |
3718 | + |
3719 | + return root; |
3720 | |
3721 | } |
3722 | void TraktorFeature::parseTrack(QXmlStreamReader &xml, QSqlQuery &query){ |
3723 | QString title; |
3724 | - QString artist; |
3725 | - QString album; |
3726 | + QString artist; |
3727 | + QString album; |
3728 | QString year; |
3729 | QString genre; |
3730 | //drive letter |
3731 | @@ -247,14 +271,14 @@ |
3732 | //read all sub tags of ENTRY until we reach the closing ENTRY tag |
3733 | while(!xml.atEnd()) |
3734 | { |
3735 | - xml.readNext(); |
3736 | + xml.readNext(); |
3737 | if(xml.isStartElement()){ |
3738 | if(xml.name() == "ALBUM") |
3739 | { |
3740 | QXmlStreamAttributes attr = xml.attributes (); |
3741 | album = attr.value("TITLE").toString(); |
3742 | tracknumber = attr.value("TRACK").toString(); |
3743 | - |
3744 | + |
3745 | continue; |
3746 | } |
3747 | if(xml.name() == "LOCATION") |
3748 | @@ -307,10 +331,10 @@ |
3749 | } |
3750 | //We leave the infinte loop, if twe have the closing tag "ENTRY" |
3751 | if(xml.name() == "ENTRY" && xml.isEndElement()){ |
3752 | - break; |
3753 | + break; |
3754 | } |
3755 | } |
3756 | - |
3757 | + |
3758 | /* If we reach the end of ENTRY within the COLLECTION tag |
3759 | * Save parsed track to database |
3760 | */ |
3761 | @@ -327,8 +351,8 @@ |
3762 | query.bindValue(":key", key); |
3763 | query.bindValue(":bpm", bpm); |
3764 | query.bindValue(":bitrate", bitrate); |
3765 | - |
3766 | - |
3767 | + |
3768 | + |
3769 | bool success = query.exec(); |
3770 | if(!success){ |
3771 | qDebug() << "SQL Error in TraktorTableModel.cpp: line" << __LINE__ << " " << query.lastError(); |
3772 | @@ -343,7 +367,7 @@ |
3773 | * In other words, Traktor uses a tree structure to organize music. Inner nodes represent folders while |
3774 | * leaves are playlists. |
3775 | */ |
3776 | -void TraktorFeature::parsePlaylists(QXmlStreamReader &xml){ |
3777 | +TreeItem* TraktorFeature::parsePlaylists(QXmlStreamReader &xml){ |
3778 | |
3779 | qDebug() << "Process RootFolder"; |
3780 | //Each playlist is unique and can be identified by a path in the tree structure. |
3781 | @@ -364,11 +388,11 @@ |
3782 | QSqlQuery query_insert_to_playlist_tracks(m_database); |
3783 | query_insert_to_playlist_tracks.prepare("INSERT INTO traktor_playlist_tracks (playlist_id, track_id) " |
3784 | "VALUES (:playlist_id, :track_id)"); |
3785 | - |
3786 | + |
3787 | while(!xml.atEnd()) |
3788 | { |
3789 | //read next XML element |
3790 | - xml.readNext(); |
3791 | + xml.readNext(); |
3792 | |
3793 | if(xml.isStartElement()) |
3794 | { |
3795 | @@ -376,49 +400,49 @@ |
3796 | QXmlStreamAttributes attr = xml.attributes(); |
3797 | QString name = attr.value("NAME").toString(); |
3798 | QString type = attr.value("TYPE").toString(); |
3799 | - |
3800 | + |
3801 | //TODO: What happens if the folder node is a leaf (empty folder) |
3802 | // Idea: Hide empty folders :-) |
3803 | if(type == "FOLDER") |
3804 | { |
3805 | - |
3806 | - current_path += delimiter; |
3807 | + |
3808 | + current_path += delimiter; |
3809 | current_path += name; |
3810 | //qDebug() << "Folder: " +current_path << " has parent " << parent->data().toString(); |
3811 | map.insert(current_path, "FOLDER"); |
3812 | - |
3813 | + |
3814 | TreeItem * item = new TreeItem(name,current_path, this, parent); |
3815 | parent->appendChild(item); |
3816 | parent = item; |
3817 | } |
3818 | if(type == "PLAYLIST") |
3819 | { |
3820 | - current_path += delimiter; |
3821 | + current_path += delimiter; |
3822 | current_path += name; |
3823 | //qDebug() << "Playlist: " +current_path << " has parent " << parent->data().toString(); |
3824 | map.insert(current_path, "PLAYLIST"); |
3825 | - |
3826 | + |
3827 | TreeItem * item = new TreeItem(name,current_path, this, parent); |
3828 | parent->appendChild(item); |
3829 | // process all the entries within the playlist 'name' having path 'current_path' |
3830 | parsePlaylistEntries(xml,current_path, query_insert_to_playlists, query_insert_to_playlist_tracks); |
3831 | - |
3832 | + |
3833 | } |
3834 | |
3835 | } |
3836 | if(xml.name() == "ENTRY" && inPlaylistTag){ |
3837 | - |
3838 | + |
3839 | |
3840 | } |
3841 | |
3842 | } |
3843 | - |
3844 | - if(xml.isEndElement()) |
3845 | + |
3846 | + if(xml.isEndElement()) |
3847 | { |
3848 | if(xml.name() == "NODE") |
3849 | { |
3850 | if(map.value(current_path) == "FOLDER"){ |
3851 | - parent = parent->parent(); |
3852 | + parent = parent->parent(); |
3853 | } |
3854 | |
3855 | //Whenever we find a closing NODE, remove the last component of the path |
3856 | @@ -427,14 +451,14 @@ |
3857 | |
3858 | current_path.remove(lastSlash, path_length - lastSlash); |
3859 | |
3860 | - |
3861 | - |
3862 | + |
3863 | + |
3864 | } |
3865 | if(xml.name() == "PLAYLIST") |
3866 | { |
3867 | inPlaylistTag = false; |
3868 | } |
3869 | - //We leave the infinte loop, if twe have the closing "PLAYLIST" tag |
3870 | + //We leave the infinte loop, if twe have the closing "PLAYLIST" tag |
3871 | if(xml.name() == "PLAYLISTS") |
3872 | { |
3873 | break; |
3874 | @@ -442,15 +466,11 @@ |
3875 | } |
3876 | |
3877 | } |
3878 | - |
3879 | - |
3880 | - m_childModel.setRootItem(rootItem); |
3881 | - |
3882 | - |
3883 | + return rootItem; |
3884 | } |
3885 | void TraktorFeature::parsePlaylistEntries(QXmlStreamReader &xml,QString playlist_path, QSqlQuery query_insert_into_playlist, QSqlQuery query_insert_into_playlisttracks) |
3886 | { |
3887 | - // In the database, the name of a playlist is specified by the unique path, e.g., /someFolderA/someFolderB/playlistA" |
3888 | + // In the database, the name of a playlist is specified by the unique path, e.g., /someFolderA/someFolderB/playlistA" |
3889 | query_insert_into_playlist.bindValue(":name", playlist_path); |
3890 | bool success = query_insert_into_playlist.exec(); |
3891 | if(!success){ |
3892 | @@ -459,10 +479,10 @@ |
3893 | } |
3894 | //Get playlist id |
3895 | QSqlQuery id_query(m_database); |
3896 | - id_query.prepare("select id from traktor_playlists where name=:path"); |
3897 | + id_query.prepare("select id from traktor_playlists where name=:path"); |
3898 | id_query.bindValue(":path", playlist_path); |
3899 | success = id_query.exec(); |
3900 | - |
3901 | + |
3902 | int playlist_id = -1; |
3903 | if(success){ |
3904 | //playlist_id = id_query.lastInsertId().toInt(); |
3905 | @@ -472,13 +492,13 @@ |
3906 | } |
3907 | else |
3908 | qDebug() << "SQL Error in TraktorTableModel.cpp: line" << __LINE__ << " " << id_query.lastError(); |
3909 | - |
3910 | - |
3911 | - |
3912 | + |
3913 | + |
3914 | + |
3915 | while(!xml.atEnd()) |
3916 | { |
3917 | //read next XML element |
3918 | - xml.readNext(); |
3919 | + xml.readNext(); |
3920 | if(xml.isStartElement()) |
3921 | { |
3922 | if(xml.name() == "PRIMARYKEY"){ |
3923 | @@ -489,7 +509,7 @@ |
3924 | { |
3925 | key.replace(QString(":"), QString("")); |
3926 | //TODO: IFDEF |
3927 | - #if defined(__WINDOWS__) |
3928 | + #if defined(__WINDOWS__) |
3929 | key.insert(1,":"); |
3930 | #else |
3931 | key.prepend("/Volumes/"); |
3932 | @@ -498,24 +518,24 @@ |
3933 | //insert to database |
3934 | int track_id = -1; |
3935 | QSqlQuery finder_query(m_database); |
3936 | - finder_query.prepare("select id from traktor_library where location=:path"); |
3937 | + finder_query.prepare("select id from traktor_library where location=:path"); |
3938 | finder_query.bindValue(":path", key); |
3939 | success = finder_query.exec(); |
3940 | - |
3941 | - |
3942 | + |
3943 | + |
3944 | if(success){ |
3945 | while (finder_query.next()) { |
3946 | track_id = finder_query.value(finder_query.record().indexOf("id")).toInt(); |
3947 | } |
3948 | - } |
3949 | + } |
3950 | else |
3951 | qDebug() << "SQL Error in TraktorTableModel.cpp: line" << __LINE__ << " " << finder_query.lastError(); |
3952 | |
3953 | query_insert_into_playlisttracks.bindValue(":playlist_id", playlist_id); |
3954 | query_insert_into_playlisttracks.bindValue(":track_id", track_id); |
3955 | success = query_insert_into_playlisttracks.exec(); |
3956 | - |
3957 | - |
3958 | + |
3959 | + |
3960 | if(!success){ |
3961 | qDebug() << "SQL Error in TraktorFeature.cpp: line" << __LINE__ << " " << query_insert_into_playlisttracks.lastError(); |
3962 | qDebug() << "trackid" << track_id << " with path " << key; |
3963 | @@ -523,27 +543,27 @@ |
3964 | qDebug() << "-----------------"; |
3965 | |
3966 | } |
3967 | - |
3968 | + |
3969 | } |
3970 | - |
3971 | + |
3972 | } |
3973 | } |
3974 | if(xml.isEndElement()){ |
3975 | - //We leave the infinte loop, if twe have the closing "PLAYLIST" tag |
3976 | + //We leave the infinte loop, if twe have the closing "PLAYLIST" tag |
3977 | if(xml.name() == "PLAYLIST") |
3978 | { |
3979 | break; |
3980 | } |
3981 | } |
3982 | } |
3983 | - |
3984 | + |
3985 | } |
3986 | void TraktorFeature::clearTable(QString table_name) |
3987 | { |
3988 | QSqlQuery query(m_database); |
3989 | query.prepare("delete from "+table_name); |
3990 | bool success = query.exec(); |
3991 | - |
3992 | + |
3993 | if(!success) |
3994 | qDebug() << "Could not delete remove old entries from table " << table_name << " : " << query.lastError(); |
3995 | else |
3996 | @@ -569,3 +589,23 @@ |
3997 | |
3998 | |
3999 | } |
4000 | +void TraktorFeature::onTrackCollectionLoaded() |
4001 | +{ |
4002 | + TreeItem* root = m_future.result(); |
4003 | + if (root) { |
4004 | + m_childModel.setRootItem(root); |
4005 | + m_pTraktorTableModel->select(); |
4006 | + emit(showTrackModel(m_pTraktorTableModel)); |
4007 | + qDebug() << "Traktor library loaded successfully"; |
4008 | + } else { |
4009 | + QMessageBox::warning( |
4010 | + NULL, |
4011 | + tr("Error Loading Traktor Library"), |
4012 | + tr("There was an error loading your Traktor library. Some of " |
4013 | + "your Traktor tracks or playlists may not have loaded.")); |
4014 | + } |
4015 | + //calls a slot in the sidebarmodel such that 'isLoading' is removed from the feature title. |
4016 | + m_title = tr("Traktor"); |
4017 | + emit(featureLoadingFinished(this)); |
4018 | + activate(); |
4019 | +} |
4020 | |
4021 | === renamed file 'mixxx/src/library/traktorfeature.h' => 'mixxx/src/library/traktor/traktorfeature.h' |
4022 | --- mixxx/src/library/traktorfeature.h 2011-01-03 13:54:29 +0000 |
4023 | +++ mixxx/src/library/traktor/traktorfeature.h 2011-02-21 23:55:30 +0000 |
4024 | @@ -7,11 +7,14 @@ |
4025 | #include <QStringListModel> |
4026 | #include <QtSql> |
4027 | #include <QXmlStreamReader> |
4028 | +#include <QFuture> |
4029 | +#include <QtConcurrentRun> |
4030 | +#include <QFutureWatcher> |
4031 | |
4032 | #include "library/libraryfeature.h" |
4033 | -#include "library/traktortablemodel.h" |
4034 | -#include "library/traktorplaylistmodel.h" |
4035 | -#include "treeitemmodel.h" |
4036 | +#include "library/traktor/traktortablemodel.h" |
4037 | +#include "library/traktor/traktorplaylistmodel.h" |
4038 | +#include "library/treeitemmodel.h" |
4039 | |
4040 | class LibraryTableModel; |
4041 | class MissingTableModel; |
4042 | @@ -40,27 +43,32 @@ |
4043 | void onRightClick(const QPoint& globalPos); |
4044 | void onRightClickChild(const QPoint& globalPos, QModelIndex index); |
4045 | void refreshLibraryModels(); |
4046 | + void onTrackCollectionLoaded(); |
4047 | private: |
4048 | - bool importLibrary(QString file); |
4049 | + TreeItem* importLibrary(QString file); |
4050 | /** parses a track in the music collection **/ |
4051 | void parseTrack(QXmlStreamReader &xml, QSqlQuery &query); |
4052 | - /** Iterates over all playliost and folders and constructs the childmodel **/ |
4053 | - void parsePlaylists(QXmlStreamReader &xml); |
4054 | + /** Iterates over all playliost and folders and constructs the childmodel **/ |
4055 | + TreeItem* parsePlaylists(QXmlStreamReader &xml); |
4056 | /** processes a particular playlist **/ |
4057 | - void parsePlaylistEntries(QXmlStreamReader &xml, QString playlist_path, |
4058 | + void parsePlaylistEntries(QXmlStreamReader &xml, QString playlist_path, |
4059 | QSqlQuery query_insert_into_playlist, QSqlQuery query_insert_into_playlisttracks); |
4060 | void clearTable(QString table_name); |
4061 | static QString getTraktorMusicDatabase(); |
4062 | //private fields |
4063 | TreeItemModel m_childModel; |
4064 | TrackCollection* m_pTrackCollection; |
4065 | - QSqlDatabase &m_database; |
4066 | + //A separate db connection for the worker parsing thread |
4067 | + QSqlDatabase m_database; |
4068 | TraktorTableModel* m_pTraktorTableModel; |
4069 | TraktorPlaylistModel* m_pTraktorPlaylistModel; |
4070 | |
4071 | bool m_isActivated; |
4072 | - |
4073 | - |
4074 | + QFutureWatcher<TreeItem*> m_future_watcher; |
4075 | + QFuture<TreeItem*> m_future; |
4076 | + QString m_title; |
4077 | + |
4078 | + |
4079 | }; |
4080 | |
4081 | |
4082 | |
4083 | === renamed file 'mixxx/src/library/traktorplaylistmodel.cpp' => 'mixxx/src/library/traktor/traktorplaylistmodel.cpp' |
4084 | --- mixxx/src/library/traktorplaylistmodel.cpp 2011-01-05 20:46:21 +0000 |
4085 | +++ mixxx/src/library/traktor/traktorplaylistmodel.cpp 2011-02-21 23:55:30 +0000 |
4086 | @@ -2,7 +2,7 @@ |
4087 | #include <QtGui> |
4088 | #include <QtSql> |
4089 | #include "library/trackcollection.h" |
4090 | -#include "library/traktorplaylistmodel.h" |
4091 | +#include "library/traktor/traktorplaylistmodel.h" |
4092 | |
4093 | #include "mixxxutils.cpp" |
4094 | |
4095 | @@ -13,13 +13,10 @@ |
4096 | BaseSqlTableModel(parent, pTrackCollection, pTrackCollection->getDatabase()), |
4097 | m_pTrackCollection(pTrackCollection), |
4098 | m_database(m_pTrackCollection->getDatabase()) |
4099 | - |
4100 | + |
4101 | { |
4102 | connect(this, SIGNAL(doSearch(const QString&)), this, SLOT(slotSearch(const QString&))); |
4103 | - |
4104 | - |
4105 | - |
4106 | - |
4107 | + setCaching(false); |
4108 | } |
4109 | |
4110 | TraktorPlaylistModel::~TraktorPlaylistModel() { |
4111 | @@ -34,16 +31,16 @@ |
4112 | TrackPointer TraktorPlaylistModel::getTrack(const QModelIndex& index) const |
4113 | { |
4114 | //qDebug() << "getTraktorTrack"; |
4115 | - |
4116 | + |
4117 | QString artist = index.sibling(index.row(), fieldIndex("artist")).data().toString(); |
4118 | QString title = index.sibling(index.row(), fieldIndex("title")).data().toString(); |
4119 | QString album = index.sibling(index.row(), fieldIndex("album")).data().toString(); |
4120 | QString year = index.sibling(index.row(), fieldIndex("year")).data().toString(); |
4121 | QString genre = index.sibling(index.row(), fieldIndex("genre")).data().toString(); |
4122 | float bpm = index.sibling(index.row(), fieldIndex("bpm")).data().toString().toFloat(); |
4123 | - |
4124 | + |
4125 | QString location = index.sibling(index.row(), fieldIndex("location")).data().toString(); |
4126 | - |
4127 | + |
4128 | TrackInfoObject* pTrack = new TrackInfoObject(location); |
4129 | pTrack->setArtist(artist); |
4130 | pTrack->setTitle(title); |
4131 | @@ -51,7 +48,7 @@ |
4132 | pTrack->setYear(year); |
4133 | pTrack->setGenre(genre); |
4134 | pTrack->setBpm(bpm); |
4135 | - |
4136 | + |
4137 | |
4138 | return TrackPointer(pTrack, &QObject::deleteLater); |
4139 | } |
4140 | @@ -64,7 +61,7 @@ |
4141 | |
4142 | void TraktorPlaylistModel::removeTrack(const QModelIndex& index) |
4143 | { |
4144 | - |
4145 | + |
4146 | } |
4147 | |
4148 | void TraktorPlaylistModel::removeTracks(const QModelIndexList& indices) { |
4149 | @@ -72,7 +69,7 @@ |
4150 | } |
4151 | void TraktorPlaylistModel::moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex) |
4152 | { |
4153 | - |
4154 | + |
4155 | } |
4156 | |
4157 | void TraktorPlaylistModel::search(const QString& searchText) { |
4158 | @@ -112,7 +109,7 @@ |
4159 | } |
4160 | |
4161 | QMimeData* TraktorPlaylistModel::mimeData(const QModelIndexList &indexes) const { |
4162 | - |
4163 | + |
4164 | QMimeData *mimeData = new QMimeData(); |
4165 | QList<QUrl> urls; |
4166 | |
4167 | @@ -145,8 +142,8 @@ |
4168 | |
4169 | TrackModel::CapabilitiesFlags TraktorPlaylistModel::getCapabilities() const |
4170 | { |
4171 | - |
4172 | - return NULL; |
4173 | + |
4174 | + return TRACKMODELCAPS_NONE; |
4175 | } |
4176 | |
4177 | Qt::ItemFlags TraktorPlaylistModel::flags(const QModelIndex &index) const |
4178 | @@ -158,15 +155,15 @@ |
4179 | int playlistId = -1; |
4180 | QSqlQuery finder_query(m_database); |
4181 | finder_query.prepare("SELECT id from traktor_playlists where name='"+playlist_path+"'"); |
4182 | - |
4183 | + |
4184 | if(finder_query.exec()){ |
4185 | while (finder_query.next()) { |
4186 | playlistId = finder_query.value(finder_query.record().indexOf("id")).toInt(); |
4187 | } |
4188 | - } |
4189 | + } |
4190 | else |
4191 | - qDebug() << "SQL Error in TraktorPlaylistModel.cpp: line" << __LINE__ << " " << finder_query.lastError(); |
4192 | - |
4193 | + qDebug() << "SQL Error in TraktorPlaylistModel.cpp: line" << __LINE__ << " " << finder_query.lastError(); |
4194 | + |
4195 | |
4196 | QString playlistID = "TraktorPlaylist_" + QString("%1").arg(playlistId); |
4197 | //Escape the playlist name |
4198 | @@ -176,9 +173,9 @@ |
4199 | |
4200 | QSqlQuery query(m_database); |
4201 | query.prepare("CREATE TEMPORARY VIEW IF NOT EXISTS "+ driver->formatValue(playlistNameField) + " AS " |
4202 | - "SELECT " |
4203 | + "SELECT " |
4204 | "traktor_library.id," |
4205 | - "traktor_library.artist," |
4206 | + "traktor_library.artist," |
4207 | "traktor_library.title," |
4208 | "traktor_library.album," |
4209 | "traktor_library.year," |
4210 | @@ -200,16 +197,16 @@ |
4211 | "ON traktor_playlist_tracks.playlist_id = traktor_playlists.id " |
4212 | "where traktor_playlists.name='"+playlist_path+"'" |
4213 | ); |
4214 | - |
4215 | - |
4216 | + |
4217 | + |
4218 | if (!query.exec()) { |
4219 | - |
4220 | + |
4221 | qDebug() << "Error creating temporary view for traktor playlists. TraktorPlaylistModel --> line: " << __LINE__ << " " << query.lastError(); |
4222 | qDebug() << "Executed Query: " << query.executedQuery(); |
4223 | return; |
4224 | } |
4225 | setTable(playlistID); |
4226 | - |
4227 | + |
4228 | //removeColumn(fieldIndex("track_id")); |
4229 | //removeColumn(fieldIndex("name")); |
4230 | //removeColumn(fieldIndex("id")); |
4231 | @@ -220,10 +217,10 @@ |
4232 | initHeaderData(); |
4233 | } |
4234 | bool TraktorPlaylistModel::isColumnHiddenByDefault(int column) { |
4235 | - if (column == fieldIndex(LIBRARYTABLE_KEY)) |
4236 | + if (column == fieldIndex(LIBRARYTABLE_KEY)) |
4237 | return true; |
4238 | if(column == fieldIndex(LIBRARYTABLE_BITRATE)) |
4239 | return true; |
4240 | - |
4241 | + |
4242 | return false; |
4243 | -} |
4244 | \ No newline at end of file |
4245 | +} |
4246 | |
4247 | === renamed file 'mixxx/src/library/traktorplaylistmodel.h' => 'mixxx/src/library/traktor/traktorplaylistmodel.h' |
4248 | --- mixxx/src/library/traktorplaylistmodel.h 2010-12-22 18:57:15 +0000 |
4249 | +++ mixxx/src/library/traktor/traktorplaylistmodel.h 2011-02-21 23:55:30 +0000 |
4250 | @@ -4,7 +4,7 @@ |
4251 | #include <QtSql> |
4252 | #include <QItemDelegate> |
4253 | #include <QtCore> |
4254 | -#include "trackmodel.h" |
4255 | +#include "library/trackmodel.h" |
4256 | #include "library/basesqltablemodel.h" |
4257 | #include "library/librarytablemodel.h" |
4258 | #include "library/dao/playlistdao.h" |
4259 | @@ -18,7 +18,7 @@ |
4260 | public: |
4261 | TraktorPlaylistModel(QObject* parent, TrackCollection* pTrackCollection); |
4262 | virtual ~TraktorPlaylistModel(); |
4263 | - |
4264 | + |
4265 | virtual TrackPointer getTrack(const QModelIndex& index) const; |
4266 | virtual QString getTrackLocation(const QModelIndex& index) const; |
4267 | virtual void search(const QString& searchText); |
4268 | @@ -29,13 +29,13 @@ |
4269 | virtual void removeTracks(const QModelIndexList& indices); |
4270 | virtual bool addTrack(const QModelIndex& index, QString location); |
4271 | virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex); |
4272 | - |
4273 | + |
4274 | virtual Qt::ItemFlags flags(const QModelIndex &index) const; |
4275 | QMimeData* mimeData(const QModelIndexList &indexes) const; |
4276 | - |
4277 | + |
4278 | QItemDelegate* delegateForColumn(const int i); |
4279 | TrackModel::CapabilitiesFlags getCapabilities() const; |
4280 | - /** sets the playlist **/ |
4281 | + /** sets the playlist **/ |
4282 | void setPlaylist(QString path_name); |
4283 | |
4284 | private slots: |
4285 | @@ -47,8 +47,8 @@ |
4286 | private: |
4287 | TrackCollection* m_pTrackCollection; |
4288 | QSqlDatabase &m_database; |
4289 | - |
4290 | - |
4291 | + |
4292 | + |
4293 | QString m_currentSearch; |
4294 | }; |
4295 | |
4296 | |
4297 | === renamed file 'mixxx/src/library/traktortablemodel.cpp' => 'mixxx/src/library/traktor/traktortablemodel.cpp' |
4298 | --- mixxx/src/library/traktortablemodel.cpp 2011-01-05 20:46:21 +0000 |
4299 | +++ mixxx/src/library/traktor/traktortablemodel.cpp 2011-02-21 23:55:30 +0000 |
4300 | @@ -2,7 +2,7 @@ |
4301 | #include <QtGui> |
4302 | #include <QtSql> |
4303 | #include "library/trackcollection.h" |
4304 | -#include "library/traktortablemodel.h" |
4305 | +#include "library/traktor/traktortablemodel.h" |
4306 | |
4307 | #include "mixxxutils.cpp" |
4308 | |
4309 | @@ -13,11 +13,12 @@ |
4310 | BaseSqlTableModel(parent, pTrackCollection, pTrackCollection->getDatabase()), |
4311 | m_pTrackCollection(pTrackCollection), |
4312 | m_database(m_pTrackCollection->getDatabase()) |
4313 | - |
4314 | + |
4315 | { |
4316 | connect(this, SIGNAL(doSearch(const QString&)), this, SLOT(slotSearch(const QString&))); |
4317 | setTable("traktor_library"); |
4318 | initHeaderData(); |
4319 | + setCaching(false); |
4320 | } |
4321 | |
4322 | TraktorTableModel::~TraktorTableModel() { |
4323 | @@ -32,16 +33,16 @@ |
4324 | TrackPointer TraktorTableModel::getTrack(const QModelIndex& index) const |
4325 | { |
4326 | //qDebug() << "getTraktorTrack"; |
4327 | - |
4328 | + |
4329 | QString artist = index.sibling(index.row(), fieldIndex("artist")).data().toString(); |
4330 | QString title = index.sibling(index.row(), fieldIndex("title")).data().toString(); |
4331 | QString album = index.sibling(index.row(), fieldIndex("album")).data().toString(); |
4332 | QString year = index.sibling(index.row(), fieldIndex("year")).data().toString(); |
4333 | QString genre = index.sibling(index.row(), fieldIndex("genre")).data().toString(); |
4334 | float bpm = index.sibling(index.row(), fieldIndex("bpm")).data().toString().toFloat(); |
4335 | - |
4336 | + |
4337 | QString location = index.sibling(index.row(), fieldIndex("location")).data().toString(); |
4338 | - |
4339 | + |
4340 | TrackInfoObject* pTrack = new TrackInfoObject(location); |
4341 | pTrack->setArtist(artist); |
4342 | pTrack->setTitle(title); |
4343 | @@ -49,7 +50,7 @@ |
4344 | pTrack->setYear(year); |
4345 | pTrack->setGenre(genre); |
4346 | pTrack->setBpm(bpm); |
4347 | - |
4348 | + |
4349 | |
4350 | return TrackPointer(pTrack, &QObject::deleteLater); |
4351 | } |
4352 | @@ -62,7 +63,7 @@ |
4353 | |
4354 | void TraktorTableModel::removeTrack(const QModelIndex& index) |
4355 | { |
4356 | - |
4357 | + |
4358 | } |
4359 | |
4360 | void TraktorTableModel::removeTracks(const QModelIndexList& indices) { |
4361 | @@ -70,7 +71,7 @@ |
4362 | } |
4363 | void TraktorTableModel::moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex) |
4364 | { |
4365 | - |
4366 | + |
4367 | } |
4368 | |
4369 | void TraktorTableModel::search(const QString& searchText) { |
4370 | @@ -93,7 +94,7 @@ |
4371 | "album LIKE " + escapedText + " OR " + |
4372 | "title LIKE " + escapedText + ")"; |
4373 | setFilter(filter); |
4374 | - |
4375 | + |
4376 | } |
4377 | |
4378 | const QString TraktorTableModel::currentSearch() { |
4379 | @@ -109,7 +110,7 @@ |
4380 | } |
4381 | |
4382 | QMimeData* TraktorTableModel::mimeData(const QModelIndexList &indexes) const { |
4383 | - |
4384 | + |
4385 | QMimeData *mimeData = new QMimeData(); |
4386 | QList<QUrl> urls; |
4387 | |
4388 | @@ -142,8 +143,8 @@ |
4389 | |
4390 | TrackModel::CapabilitiesFlags TraktorTableModel::getCapabilities() const |
4391 | { |
4392 | - |
4393 | - return NULL; |
4394 | + |
4395 | + return TRACKMODELCAPS_NONE; |
4396 | } |
4397 | |
4398 | Qt::ItemFlags TraktorTableModel::flags(const QModelIndex &index) const |
4399 | @@ -151,10 +152,10 @@ |
4400 | return readOnlyFlags(index); |
4401 | } |
4402 | bool TraktorTableModel::isColumnHiddenByDefault(int column) { |
4403 | - if (column == fieldIndex(LIBRARYTABLE_KEY)) |
4404 | + if (column == fieldIndex(LIBRARYTABLE_KEY)) |
4405 | return true; |
4406 | if(column == fieldIndex(LIBRARYTABLE_BITRATE)) |
4407 | return true; |
4408 | - |
4409 | - return false; |
4410 | -} |
4411 | \ No newline at end of file |
4412 | + |
4413 | + return false; |
4414 | +} |
4415 | |
4416 | === renamed file 'mixxx/src/library/traktortablemodel.h' => 'mixxx/src/library/traktor/traktortablemodel.h' |
4417 | --- mixxx/src/library/traktortablemodel.h 2010-12-22 18:57:15 +0000 |
4418 | +++ mixxx/src/library/traktor/traktortablemodel.h 2011-02-21 23:55:30 +0000 |
4419 | @@ -4,7 +4,8 @@ |
4420 | #include <QtSql> |
4421 | #include <QItemDelegate> |
4422 | #include <QtCore> |
4423 | -#include "trackmodel.h" |
4424 | + |
4425 | +#include "library/trackmodel.h" |
4426 | #include "library/basesqltablemodel.h" |
4427 | #include "library/librarytablemodel.h" |
4428 | #include "library/dao/playlistdao.h" |
4429 | @@ -18,7 +19,7 @@ |
4430 | public: |
4431 | TraktorTableModel(QObject* parent, TrackCollection* pTrackCollection); |
4432 | virtual ~TraktorTableModel(); |
4433 | - |
4434 | + |
4435 | virtual TrackPointer getTrack(const QModelIndex& index) const; |
4436 | virtual QString getTrackLocation(const QModelIndex& index) const; |
4437 | virtual void search(const QString& searchText); |
4438 | @@ -29,10 +30,10 @@ |
4439 | virtual void removeTracks(const QModelIndexList& indices); |
4440 | virtual bool addTrack(const QModelIndex& index, QString location); |
4441 | virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex); |
4442 | - |
4443 | + |
4444 | virtual Qt::ItemFlags flags(const QModelIndex &index) const; |
4445 | QMimeData* mimeData(const QModelIndexList &indexes) const; |
4446 | - |
4447 | + |
4448 | QItemDelegate* delegateForColumn(const int i); |
4449 | TrackModel::CapabilitiesFlags getCapabilities() const; |
4450 | |
4451 | @@ -45,8 +46,8 @@ |
4452 | private: |
4453 | TrackCollection* m_pTrackCollection; |
4454 | QSqlDatabase &m_database; |
4455 | - |
4456 | - |
4457 | + |
4458 | + |
4459 | QString m_currentSearch; |
4460 | }; |
4461 | |
4462 | |
4463 | === modified file 'mixxx/src/widget/wlibrarysidebar.cpp' |
4464 | --- mixxx/src/widget/wlibrarysidebar.cpp 2010-04-09 17:10:43 +0000 |
4465 | +++ mixxx/src/widget/wlibrarysidebar.cpp 2011-02-21 23:55:30 +0000 |
4466 | @@ -1,5 +1,6 @@ |
4467 | #include <QtCore> |
4468 | #include <QtGui> |
4469 | + |
4470 | #include "library/sidebarmodel.h" |
4471 | #include "widget/wlibrarysidebar.h" |
4472 | |
4473 | @@ -161,3 +162,9 @@ |
4474 | else |
4475 | QTreeView::keyPressEvent(event); |
4476 | } |
4477 | + |
4478 | +void WLibrarySidebar::selectIndex(const QModelIndex& index) { |
4479 | + QItemSelectionModel* pModel = new QItemSelectionModel(model()); |
4480 | + pModel->select(index, QItemSelectionModel::Select); |
4481 | + setSelectionModel(pModel); |
4482 | +} |
4483 | |
4484 | === modified file 'mixxx/src/widget/wlibrarysidebar.h' |
4485 | --- mixxx/src/widget/wlibrarysidebar.h 2009-12-11 06:10:55 +0000 |
4486 | +++ mixxx/src/widget/wlibrarysidebar.h 2011-02-21 23:55:30 +0000 |
4487 | @@ -15,6 +15,8 @@ |
4488 | void dragEnterEvent(QDragEnterEvent * event); |
4489 | void dropEvent(QDropEvent * event); |
4490 | void keyPressEvent(QKeyEvent* event); |
4491 | + public slots: |
4492 | + void selectIndex(const QModelIndex&); |
4493 | |
4494 | signals: |
4495 | void rightClicked(const QPoint&, const QModelIndex&); |
Hey Tobias,
Everything looks good except for this one issue:
The Mixxx library and Rhythmbox both share BaseSqlTableModel now.
In BaseSqlTableMod el::select( ), every returned result is added to a map from the row to a track ID and vice-versa. This combined with Mixxx-library trackChanged updates from the TrackDAO allow the BaseSqlTableModel to automatically update results in the table that are changed, and to return cached results on new queries if the mixxx-library tracks haven't been written to the database yet.
The problem with Rhythmbox now using BaseSqlTableModel is that if Rhythmbox tracks and Mixxx tracks share the same ID, then an updated Mixxx track will cause the Rhythmbox track of the same ID to be changed to show the Mixxx track. You can easily reproduce this by deleting your library, rescanning and loading your RB library. In the Mixxx Library, right-click and edit the #1 track in the list to haev a different name. Now switch to the RB view and you'll see the Row 1 result is now showing all the data from row 1 in your Mixxx library.
I think this could be fixed by one of two methods:
1) adding a protected method setCaching(bool) to BaseSqlTableModel which is default on. The RB trackmodels will set this to false. All the caching logic in BaseSqlTableModel will check this before executing their logic.
2) Split BaseSqlTableModel into 2 pieces, BaseSqlTableModel and CachingBaseSqlT ableModel, where CachingBSTM inherits from BSTM. The RB models will only inherit from plain BSTM while the reset inherit from CachingBSTM.
Apart from that, there are a couple minor fixes:
1) Please return TRACKMODELCAPS_NONE instead of NULL in RhythmboxPlayli stModel: :getCapabilitie s, same goes for the TrackModel
2) In RhythmboxPlayli stModel: :setPlaylist, it looks like you've got an "ITunes" mention in the playlist ID string. Probably should have a rhythmbox-ish name.
This is awesome work -- RB is really fast now. Thanks Tobias!
Also, sorry for taking so long to get back to you on these code reviews.