Merge lp:~mixxxdevelopers/mixxx/features_rhythmbox into lp:~mixxxdevelopers/mixxx/trunk

Proposed by RAFFI TEA
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
Reviewer Review Type Date Requested Status
RJ Skerry-Ryan Approve
Review via email: mp+45645@code.launchpad.net

Description of the change

This is a rewrite of the Rhythmbox feature which is sql-powered.

I have removed the files "abstractxmltrackmodel.*" and "proxytablemodel.*". They are not longer used and were introduced previously to support sorting iTunes and rhythmbox XML models.

To post a comment you must log in.
Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Hey Tobias,

Everything looks good except for this one issue:

The Mixxx library and Rhythmbox both share BaseSqlTableModel now.

In BaseSqlTableModel::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 CachingBaseSqlTableModel, 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 RhythmboxPlaylistModel::getCapabilities, same goes for the TrackModel

2) In RhythmboxPlaylistModel::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.

review: Needs Fixing
Revision history for this message
RAFFI TEA (raffitea) wrote :

Thanks RJ fort your review.

I have anticipated the issue with BaseSqlTableModel::select(). The sql-based iTunesFeature in 1.8.2 will have same issues, but also the upcoming Traktor feature. Though, good catch. Your ideas of solving the issue cover my ideas.

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.

Revision history for this message
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

Revision history for this message
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.

review: Needs Fixing
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.

Revision history for this message
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.

Revision history for this message
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::run(). I used a while(1) loop in that. A ThreadPool of size 1 will never provide free slots because BrowseThread will only exit on shutdown. You've already merged the revised treeitem branch where I used QThreads rather than QtConcurrent::run(). I think we can remove the lines.

>
> 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(showTrackModel) in onTrackCollectionLoaded()

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 onTrackCollectionLoaded() you'll need to iterate over the TreeItem* root object and do m_childModel.insertRows(QList<TreeItem*>, ...) operations. This will populate the model incrementally with no reset.

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Everything looks good -- merge away Tobias!

review: Approve
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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&);

Subscribers

People subscribed via source and target branches