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

Proposed by RAFFI TEA
Status: Merged
Merged at revision: 2618
Proposed branch: lp:~mixxxdevelopers/mixxx/traktor_library
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 4128 lines (+2617/-604)
51 files modified
mixxx/build/depends.py (+9/-3)
mixxx/res/mixxx.qrc (+1/-0)
mixxx/res/schema.xml (+29/-0)
mixxx/src/library/autodjfeature.cpp (+2/-2)
mixxx/src/library/autodjfeature.h (+3/-2)
mixxx/src/library/browsefeature.cpp (+1/-1)
mixxx/src/library/browsefeature.h (+3/-2)
mixxx/src/library/cratefeature.cpp (+195/-21)
mixxx/src/library/cratefeature.h (+10/-1)
mixxx/src/library/dao/cratedao.cpp (+19/-0)
mixxx/src/library/dao/cratedao.h (+1/-0)
mixxx/src/library/dao/playlistdao.cpp (+21/-0)
mixxx/src/library/dao/playlistdao.h (+2/-0)
mixxx/src/library/itunesfeature.cpp (+8/-8)
mixxx/src/library/itunesfeature.h (+6/-2)
mixxx/src/library/library.cpp (+4/-0)
mixxx/src/library/libraryfeature.h (+2/-1)
mixxx/src/library/mixxxlibraryfeature.cpp (+11/-5)
mixxx/src/library/mixxxlibraryfeature.h (+3/-2)
mixxx/src/library/parser.cpp (+76/-0)
mixxx/src/library/parser.h (+53/-0)
mixxx/src/library/parserm3u.cpp (+122/-0)
mixxx/src/library/parserm3u.h (+35/-0)
mixxx/src/library/parserpls.cpp (+143/-0)
mixxx/src/library/parserpls.h (+38/-0)
mixxx/src/library/playlistfeature.cpp (+195/-36)
mixxx/src/library/playlistfeature.h (+12/-2)
mixxx/src/library/preparefeature.cpp (+1/-1)
mixxx/src/library/preparefeature.h (+3/-2)
mixxx/src/library/promotracksfeature.cpp (+1/-1)
mixxx/src/library/promotracksfeature.h (+4/-2)
mixxx/src/library/rhythmboxfeature.cpp (+8/-4)
mixxx/src/library/rhythmboxfeature.h (+3/-2)
mixxx/src/library/sidebarmodel.cpp (+112/-71)
mixxx/src/library/trackcollection.cpp (+1/-1)
mixxx/src/library/traktorfeature.cpp (+571/-0)
mixxx/src/library/traktorfeature.h (+68/-0)
mixxx/src/library/traktorplaylistmodel.cpp (+229/-0)
mixxx/src/library/traktorplaylistmodel.h (+55/-0)
mixxx/src/library/traktortablemodel.cpp (+160/-0)
mixxx/src/library/traktortablemodel.h (+53/-0)
mixxx/src/library/treeitem.cpp (+97/-0)
mixxx/src/library/treeitem.h (+55/-0)
mixxx/src/library/treeitemmodel.cpp (+151/-0)
mixxx/src/library/treeitemmodel.h (+41/-0)
mixxx/src/parser.cpp (+0/-76)
mixxx/src/parser.h (+0/-53)
mixxx/src/parserm3u.cpp (+0/-105)
mixxx/src/parserm3u.h (+0/-35)
mixxx/src/parserpls.cpp (+0/-125)
mixxx/src/parserpls.h (+0/-38)
To merge this branch: bzr merge lp:~mixxxdevelopers/mixxx/traktor_library
Reviewer Review Type Date Requested Status
Albert Santoni Approve
Review via email: mp+44610@code.launchpad.net

Description of the change

This a rework of my former Traktor branch which resolves merge conflicts. I have implemented a Traktor library feature. It has been successfully tested on Windows and OS X 10.6.

Here is a detailed enumeration of what I did:
* Implemented a Traktor library parser
* Massive changes in SidebarModel to support n-level tree structures
  (needed for Traktor and prospective Serator feature)
  - Implemented TreeItemModel.cpp/h that operates on tree structures
  - From now on, all child models of a library feature must return an
    TreeItemModel
* Auto-detection of Traktor library file on Windows via registry
* Implemented search

Based on this branch, we can improve the library further. For instance, it would be possible
to show drive letters within the Browse feature. We could also allow users to have sub playlists and crates.

To post a comment you must log in.
2621. By Raffitea

Implemented drag 'n' drop from Traktor playlists * Hide key column and bitrate by default

2622. By Raffitea

* Merging from lp:~mhaulo/mixxx/validate_playlist_and_crate_creation

2623. By Raffitea

Merging from lp:~mhaulo/mixxx/allow-playlist-and-crate-renaming
* Crates and Playlists ca now be renamed.

2624. By Raffitea

Merging from trunk (1.10)

2625. By Raffitea

Moved M3U and PLS parser to library folder.
Fix: Don't show Traktor feature in sidebar if Traktor is not installed.

2626. By Raffitea

M3U playlists can now be imported partially.

2627. By Raffitea

Fix: M3U playlists from iTunes can now be imported

2628. By Raffitea

Improved PLS parser such it works on all platforms.

2629. By Raffitea

* Added playlist import for crates, too.
* Added some delete statements for QActions in destructor.
* Removed debugs

Revision history for this message
Albert Santoni (gamegod) wrote :

- Fix indentation in CrateFeature

- in CrateFeature::slotRenameCrate, we're probably resetting the selection model because we call m_crateListTableModel.select(). Can we instead tell the model that that particular row has changed so it tells the view to update it? Otherwise, I think when you rename a crate, you will lose your position in the sidebar. (This could be really annoying at some point if you're trying to rename subcrates.)

- I see you did some magic with CrateFeature::constructChildModel() to deal with this problem in the case of inserting or deleting playlists though, which is awesome! :)

- It's also awesome that you can import playlists into both crates and playlists.

- I have not tested the Traktor feature, but based on the code:
   - In TraktorFeature::getTraktorMusicDatabase(), should we look for registry entries for older versions of Traktor if we don't find the Traktor Pro entry? Do you know if the XML schema changed from Traktor 3 to Traktor Pro?
   - There's some more tabs in traktorfeature.h for indents instead of spaces!
   - Change the #ifndef at the top of treeitemmodel.h? (you'll see what I mean)

Great work overall though Tobias! There are some cool, polished features in here and the code is high quality.

review: Needs Fixing
Revision history for this message
Albert Santoni (gamegod) wrote :

- Fix indentation in CrateFeature

- in CrateFeature::slotRenameCrate, we're probably resetting the selection model because we call m_crateListTableModel.select(). Can we instead tell the model that that particular row has changed so it tells the view to update it? Otherwise, I think when you rename a crate, you will lose your position in the sidebar. (This could be really annoying at some point if you're trying to rename subcrates.)

- I see you did some magic with CrateFeature::constructChildModel() to deal with this problem in the case of inserting or deleting playlists though, which is awesome! :)

- It's also awesome that you can import playlists into both crates and playlists.

- I have not tested the Traktor feature, but based on the code:
   - In TraktorFeature::getTraktorMusicDatabase(), should we look for registry entries for older versions of Traktor if we don't find the Traktor Pro entry? Do you know if the XML schema changed from Traktor 3 to Traktor Pro?
   - There's some more tabs in traktorfeature.h for indents instead of spaces!
   - Change the #ifndef at the top of treeitemmodel.h? (you'll see what I mean)

Great work overall though Tobias! There are some cool, polished features in here and the code is high quality.

review: Needs Fixing
2630. By Raffitea

Hopfully fixed identation in CrateFeature

2631. By Raffitea

fixed identation in traktorfeature.h

2632. By Raffitea

Again fixed identation in traktorfeature.h

2633. By Raffitea

Finally fixed identation in traktorfeature.h

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

Thanks for your review Albert.

>
> - Fix indentation in CrateFeature

Done! Don't know why I always have indentation problems. My editor is set up correctly. Whenever I press <tab> it should create 4 spaces.

> - in CrateFeature::slotRenameCrate, we're probably resetting the selection
> model because we call m_crateListTableModel.select(). Can we instead tell the
> model that that particular row has changed so it tells the view to update it?
> Otherwise, I think when you rename a crate, you will lose your position in the
> sidebar. (This could be really annoying at some point if you're trying to
> rename subcrates.)

I've addressed this issue with some magic code as you noticed in your comment below. There's no reset in the sidebar model (the tree won't collapse) but you'll loose focus. I'll try to improve that behavior.

> - I see you did some magic with CrateFeature::constructChildModel() to deal
> with this problem in the case of inserting or deleting playlists though, which
> is awesome! :)
>
> - It's also awesome that you can import playlists into both crates and
> playlists.

The code was already there, I added only some improvements.

> - I have not tested the Traktor feature, but based on the code:
> - In TraktorFeature::getTraktorMusicDatabase(), should we look for registry
> entries for older versions of Traktor if we don't find the Traktor Pro entry?
> Do you know if the XML schema changed from Traktor 3 to Traktor Pro?
> - There's some more tabs in traktorfeature.h for indents instead of spaces!

Done.

> - Change the #ifndef at the top of treeitemmodel.h? (you'll see what I
> mean)

Done.

I've only tested with the latest 1.2.x series. I don't know where to get an older Traktor 3. But I've never seen Traktor 3 in German clubs. All DJs I know use either Serato or a one of the current Traktor 1.2.x releases.

The registry key to look up the library path should be the same for all Traktor 1.2.x products, i.e., Traktor LE, Traktor (Scratch) Duo, Traktor (Scratch) Pro.

> Great work overall though Tobias! There are some cool, polished features in
> here and the code is high quality.

Revision history for this message
Albert Santoni (gamegod) wrote :

Thanks for your thoroughness Tobias!

review: Approve
2634. By Raffitea

Fixed idents in depends.py

2635. By Raffitea

Fixed various idents and typos

2636. By Raffitea

Fixed various idents

2637. By Raffitea

Fixed various idents in TrakorPlaylistModel

2638. By Raffitea

Again fixed various idents in TrakorPlaylistModel

2639. By Raffitea

Again fixed various idents in TrakorTableModel

2640. By Raffitea

Fixed a bug in TreeItemModel preventing unsorted playlists and crates after renaming and insertions.

2641. By Raffitea

Fix: Traktor playlists can have duplicate songs * Fix: Ratings can be 0

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 2010-12-05 09:59:50 +0000
3+++ mixxx/build/depends.py 2011-01-06 15:28:44 +0000
4@@ -480,11 +480,17 @@
5 "library/stardelegate.cpp",
6 "library/stareditor.cpp",
7 "audiotagger.cpp",
8+
9+ "library/treeitemmodel.cpp",
10+ "library/treeitem.cpp",
11+ "library/traktorfeature.cpp",
12+ "library/traktortablemodel.cpp",
13+ "library/traktorplaylistmodel.cpp",
14
15 "xmlparse.cpp",
16- "parser.cpp",
17- "parserpls.cpp",
18- "parserm3u.cpp",
19+ "library/parser.cpp",
20+ "library/parserpls.cpp",
21+ "library/parserm3u.cpp",
22
23 "bpm/bpmscheme.cpp",
24
25
26=== added file 'mixxx/res/images/library/ic_library_traktor.png'
27Binary files mixxx/res/images/library/ic_library_traktor.png 1970-01-01 00:00:00 +0000 and mixxx/res/images/library/ic_library_traktor.png 2011-01-06 15:28:44 +0000 differ
28=== modified file 'mixxx/res/mixxx.qrc'
29--- mixxx/res/mixxx.qrc 2010-11-22 15:24:51 +0000
30+++ mixxx/res/mixxx.qrc 2011-01-06 15:28:44 +0000
31@@ -28,5 +28,6 @@
32 <file>images/library/ic_library_prepare.png</file>
33 <file>images/library/ic_library_promotracks.png</file>
34 <file>images/library/ic_library_rhythmbox.png</file>
35+ <file>images/library/ic_library_traktor.png</file>
36 </qresource>
37 </RCC>
38
39=== modified file 'mixxx/res/schema.xml'
40--- mixxx/res/schema.xml 2010-12-03 19:32:01 +0000
41+++ mixxx/res/schema.xml 2011-01-06 15:28:44 +0000
42@@ -199,4 +199,33 @@
43 track_id INTEGER REFERENCES itunes_library(id));
44 </sql>
45 </revision>
46+ <revision version="9" min_compatible="3">
47+ <description>
48+ Tables for Traktor library feature
49+ </description>
50+ <sql>
51+ CREATE TABLE IF NOT EXISTS traktor_library (
52+ id INTEGER primary key AUTOINCREMENT,
53+ artist varchar(48), title varchar(48),
54+ album varchar(48), year varchar(16),
55+ genre varchar(32), tracknumber varchar(3),
56+ location varchar(512) UNIQUE,
57+ comment varchar(60),
58+ duration integer,
59+ bitrate integer,
60+ bpm float,
61+ key varchar(6),
62+ rating integer
63+ );
64+ CREATE TABLE IF NOT EXISTS traktor_playlists (
65+ id INTEGER primary key,
66+ name varchar(100) UNIQUE
67+ );
68+ CREATE TABLE IF NOT EXISTS traktor_playlist_tracks (
69+ id INTEGER primary key AUTOINCREMENT,
70+ playlist_id INTEGER REFERENCES traktor_playlist(id),
71+ track_id INTEGER REFERENCES traktor_library(id)
72+ );
73+ </sql>
74+ </revision>
75 </schema>
76
77=== modified file 'mixxx/src/library/autodjfeature.cpp'
78--- mixxx/src/library/autodjfeature.cpp 2010-10-24 09:50:11 +0000
79+++ mixxx/src/library/autodjfeature.cpp 2011-01-06 15:28:44 +0000
80@@ -50,8 +50,8 @@
81 this, SIGNAL(loadTrackToPlayer(TrackPointer, QString)));
82 }
83
84-QAbstractItemModel* AutoDJFeature::getChildModel() {
85- return &m_childModel;
86+TreeItemModel* AutoDJFeature::getChildModel() {
87+ return &m_childModel;
88 }
89
90 void AutoDJFeature::activate() {
91
92=== modified file 'mixxx/src/library/autodjfeature.h'
93--- mixxx/src/library/autodjfeature.h 2010-02-23 20:06:25 +0000
94+++ mixxx/src/library/autodjfeature.h 2011-01-06 15:28:44 +0000
95@@ -10,6 +10,7 @@
96 #include "library/libraryfeature.h"
97 #include "library/dao/playlistdao.h"
98 #include "configobject.h"
99+#include "treeitemmodel.h"
100
101 class PlaylistTableModel;
102 class TrackCollection;
103@@ -34,7 +35,7 @@
104 WLibrary* libraryWidget,
105 MixxxKeyboard* keyboard);
106
107- QAbstractItemModel* getChildModel();
108+ TreeItemModel* getChildModel();
109
110 public slots:
111 void activate();
112@@ -47,7 +48,7 @@
113 TrackCollection* m_pTrackCollection;
114 PlaylistDAO& m_playlistDao;
115 const static QString m_sAutoDJViewName;
116- QStringListModel m_childModel;
117+ TreeItemModel m_childModel;
118 };
119
120
121
122=== modified file 'mixxx/src/library/browsefeature.cpp'
123--- mixxx/src/library/browsefeature.cpp 2010-12-02 21:52:18 +0000
124+++ mixxx/src/library/browsefeature.cpp 2011-01-06 15:28:44 +0000
125@@ -42,7 +42,7 @@
126 return QIcon(":/images/library/ic_library_browse.png");
127 }
128
129-QAbstractItemModel* BrowseFeature::getChildModel() {
130+TreeItemModel* BrowseFeature::getChildModel() {
131 return &m_childModel;
132 }
133
134
135=== modified file 'mixxx/src/library/browsefeature.h'
136--- mixxx/src/library/browsefeature.h 2010-10-24 09:50:11 +0000
137+++ mixxx/src/library/browsefeature.h 2011-01-06 15:28:44 +0000
138@@ -11,6 +11,7 @@
139 #include "library/browsefilter.h"
140 #include "library/browsetablemodel.h"
141 #include "library/libraryfeature.h"
142+#include "treeitemmodel.h"
143
144 class TrackCollection;
145
146@@ -33,7 +34,7 @@
147 virtual void bindWidget(WLibrarySidebar* sidebarWidget,
148 WLibrary* libraryWidget,
149 MixxxKeyboard* keyboard);
150- QAbstractItemModel* getChildModel();
151+ TreeItemModel* getChildModel();
152
153 public slots:
154 void activate();
155@@ -55,7 +56,7 @@
156 BrowseTableModel m_browseModel;
157 BrowseFilter m_proxyModel;
158 TrackCollection* m_pTrackCollection;
159- QStringListModel m_childModel;
160+ TreeItemModel m_childModel;
161 QString m_currentSearch;
162 };
163
164
165=== modified file 'mixxx/src/library/cratefeature.cpp'
166--- mixxx/src/library/cratefeature.cpp 2010-11-25 01:03:57 +0000
167+++ mixxx/src/library/cratefeature.cpp 2011-01-06 15:28:44 +0000
168@@ -6,6 +6,9 @@
169 #include <QLineEdit>
170
171 #include "library/cratefeature.h"
172+#include "library/parser.h"
173+#include "library/parserm3u.h"
174+#include "library/parserpls.h"
175
176 #include "library/cratetablemodel.h"
177 #include "library/trackcollection.h"
178@@ -13,6 +16,7 @@
179 #include "widget/wlibrary.h"
180 #include "widget/wlibrarysidebar.h"
181 #include "mixxxkeyboard.h"
182+#include "treeitem.h"
183
184 CrateFeature::CrateFeature(QObject* parent,
185 TrackCollection* pTrackCollection)
186@@ -27,6 +31,14 @@
187 connect(m_pDeleteCrateAction, SIGNAL(triggered()),
188 this, SLOT(slotDeleteCrate()));
189
190+ m_pRenameCrateAction = new QAction(tr("Rename"),this);
191+ connect(m_pRenameCrateAction, SIGNAL(triggered()),
192+ this, SLOT(slotRenameCrate()));
193+
194+ m_pImportPlaylistAction = new QAction(tr("Import Playlist"),this);
195+ connect(m_pImportPlaylistAction, SIGNAL(triggered()),
196+ this, SLOT(slotImportPlaylist()));
197+
198 m_crateListTableModel.setTable("crates");
199 m_crateListTableModel.removeColumn(m_crateListTableModel.fieldIndex("id"));
200 m_crateListTableModel.removeColumn(m_crateListTableModel.fieldIndex("show"));
201@@ -34,9 +46,29 @@
202 Qt::AscendingOrder);
203 m_crateListTableModel.setFilter("show = 1");
204 m_crateListTableModel.select();
205+
206+ //construct child model
207+ TreeItem *rootItem = new TreeItem("$root","$root", this);
208+
209+ int idColumn = m_crateListTableModel.record().indexOf("name");
210+ for (int row = 0; row < m_crateListTableModel.rowCount(); ++row) {
211+ QModelIndex ind = m_crateListTableModel.index(row, idColumn);
212+ QString crate_name = m_crateListTableModel.data(ind).toString();
213+ TreeItem *playlist_item = new TreeItem(crate_name, crate_name, this, rootItem);
214+ rootItem->appendChild(playlist_item);
215+
216+ }
217+ m_childModel.setRootItem(rootItem);
218+
219 }
220
221 CrateFeature::~CrateFeature() {
222+ //delete QActions
223+ delete m_pCreateCrateAction;
224+ delete m_pDeleteCrateAction;
225+ delete m_pRenameCrateAction;
226+ delete m_pImportPlaylistAction;
227+
228 }
229
230 QVariant CrateFeature::title() {
231@@ -92,8 +124,8 @@
232 libraryWidget->registerView("CRATEHOME", edit);
233 }
234
235-QAbstractItemModel* CrateFeature::getChildModel() {
236- return &m_crateListTableModel;
237+TreeItemModel* CrateFeature::getChildModel() {
238+ return &m_childModel;
239 }
240
241 void CrateFeature::activate() {
242@@ -124,31 +156,54 @@
243 QMenu menu(NULL);
244 menu.addAction(m_pCreateCrateAction);
245 menu.addSeparator();
246+ menu.addAction(m_pRenameCrateAction);
247 menu.addAction(m_pDeleteCrateAction);
248+ menu.addSeparator();
249+ menu.addAction(m_pImportPlaylistAction);
250 menu.exec(globalPos);
251 }
252
253 void CrateFeature::slotCreateCrate() {
254
255- bool ok = false;
256- QString name = QInputDialog::getText(NULL,
257- tr("New Crate"),
258- tr("Crate name:"),
259- QLineEdit::Normal, tr("New Crate"),
260- &ok);
261-
262- if (!ok)
263- return;
264-
265+ QString name;
266+ bool validNameGiven = false;
267 CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();
268-
269- if (name == "") {
270- QMessageBox::warning(NULL,
271- tr("Crate Creation Failed"),
272- tr("A crate cannot have a blank name."));
273- return;
274- } else if (crateDao.createCrate(name)) {
275+
276+ do {
277+ bool ok = false;
278+ name = QInputDialog::getText(NULL,
279+ tr("New Crate"),
280+ tr("Crate name:"),
281+ QLineEdit::Normal, tr("New Crate"),
282+ &ok).trimmed();
283+
284+ if (!ok)
285+ return;
286+
287+ int existingId = crateDao.getCrateIdByName(name);
288+
289+ if (existingId != -1) {
290+ QMessageBox::warning(NULL,
291+ tr("Creating Crate Failed"),
292+ tr("A crate by that name already exists."));
293+ }
294+ else if (name.isEmpty()) {
295+ QMessageBox::warning(NULL,
296+ tr("Creating Crate Failed"),
297+ tr("A crate cannot have a blank name."));
298+ }
299+ else {
300+ validNameGiven = true;
301+ }
302+
303+ } while (!validNameGiven);
304+
305+ bool crateCreated = crateDao.createCrate(name);
306+
307+ if (crateCreated) {
308+ clearChildModel();
309 m_crateListTableModel.select();
310+ constructChildModel();
311 // Switch to the new crate.
312 int crate_id = crateDao.getCrateIdByName(name);
313 m_crateTableModel.setCrate(crate_id);
314@@ -156,10 +211,11 @@
315 // TODO(XXX) set sidebar selection
316 emit(featureUpdated());
317 } else {
318- qDebug() << "Error creating crate (may already exist) with name " << name;
319+ qDebug() << "Error creating crate with name " << name;
320 QMessageBox::warning(NULL,
321 tr("Creating Crate Failed"),
322- tr("A crate by that name already exists."));
323+ tr("An unknown error occurred while creating crate: ")
324+ + name);
325
326 }
327 }
328@@ -169,9 +225,127 @@
329 int crateId = m_pTrackCollection->getCrateDAO().getCrateIdByName(crateName);
330
331 if (m_pTrackCollection->getCrateDAO().deleteCrate(crateId)) {
332+ clearChildModel();
333 m_crateListTableModel.select();
334+ constructChildModel();
335 emit(featureUpdated());
336 } else {
337 qDebug() << "Failed to delete crateId" << crateId;
338 }
339 }
340+
341+void CrateFeature::slotRenameCrate() {
342+ QString oldName = m_lastRightClickedIndex.data().toString();
343+ int crateId = m_pTrackCollection->getCrateDAO().getCrateIdByName(oldName);
344+
345+ QString newName;
346+ bool validNameGiven = false;
347+
348+ do {
349+ bool ok = false;
350+ newName = QInputDialog::getText(NULL,
351+ tr("Rename Crate"),
352+ tr("New crate name:"),
353+ QLineEdit::Normal,
354+ oldName,
355+ &ok).trimmed();
356+
357+ if (!ok || newName == oldName) {
358+ return;
359+ }
360+
361+ int existingId = m_pTrackCollection->getCrateDAO().getCrateIdByName(newName);
362+
363+ if (existingId != -1) {
364+ QMessageBox::warning(NULL,
365+ tr("Renaming Crate Failed"),
366+ tr("A crate by that name already exists."));
367+ }
368+ else if (newName.isEmpty()) {
369+ QMessageBox::warning(NULL,
370+ tr("Renaming Crate Failed"),
371+ tr("A crate cannot have a blank name."));
372+ }
373+ else {
374+ validNameGiven = true;
375+ }
376+ } while (!validNameGiven);
377+
378+
379+ if (m_pTrackCollection->getCrateDAO().renameCrate(crateId, newName)) {
380+ clearChildModel();
381+ m_crateListTableModel.select();
382+ constructChildModel();
383+ emit(featureUpdated());
384+ m_crateTableModel.setCrate(crateId);
385+ } else {
386+ qDebug() << "Failed to rename crateId" << crateId;
387+ }
388+}
389+/**
390+ * Purpose: When inserting or removing playlists,
391+ * we require the sidebar model not to reset.
392+ * This method queries the database and does dynamic insertion
393+*/
394+void CrateFeature::constructChildModel()
395+{
396+ QList<QString> data_list;
397+ int idColumn = m_crateListTableModel.record().indexOf("name");
398+ for (int row = 0; row < m_crateListTableModel.rowCount(); ++row) {
399+ QModelIndex ind = m_crateListTableModel.index(row, idColumn);
400+ QString crate_name = m_crateListTableModel.data(ind).toString();
401+ data_list.append(crate_name);
402+ }
403+ m_childModel.insertRows(data_list, 0, m_crateListTableModel.rowCount());
404+}
405+/**
406+ * Clears the child model dynamically
407+ */
408+void CrateFeature::clearChildModel()
409+{
410+ m_childModel.removeRows(0,m_crateListTableModel.rowCount());
411+}
412+void CrateFeature::slotImportPlaylist()
413+{
414+ qDebug() << "slotImportPlaylist() row:" ; //<< m_lastRightClickedIndex.data();
415+
416+
417+ QString playlist_file = QFileDialog::getOpenFileName
418+ (
419+ NULL,
420+ tr("Import Playlist"),
421+ QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
422+ tr("Playlist Files (*.m3u *.pls)")
423+ );
424+ //Exit method if user cancelled the open dialog.
425+ if (playlist_file.isNull() || playlist_file.isEmpty() ) return;
426+
427+ Parser* playlist_parser = NULL;
428+
429+ if(playlist_file.endsWith(".m3u", Qt::CaseInsensitive))
430+ {
431+ playlist_parser = new ParserM3u();
432+ }
433+ else if(playlist_file.endsWith(".pls", Qt::CaseInsensitive))
434+ {
435+ playlist_parser = new ParserPls();
436+ }
437+ else
438+ {
439+ return;
440+ }
441+
442+ QList<QString> entries = playlist_parser->parse(playlist_file);
443+
444+ //Iterate over the List that holds URLs of playlist entires
445+ for (int i = 0; i < entries.size(); ++i) {
446+ m_crateTableModel.addTrack(QModelIndex(), entries[i]);
447+
448+ }
449+
450+ //delete the parser object
451+ if(playlist_parser) delete playlist_parser;
452+
453+
454+}
455+
456
457=== modified file 'mixxx/src/library/cratefeature.h'
458--- mixxx/src/library/cratefeature.h 2010-02-23 20:06:25 +0000
459+++ mixxx/src/library/cratefeature.h 2011-01-06 15:28:44 +0000
460@@ -8,6 +8,7 @@
461 #include "library/libraryfeature.h"
462 #include "library/cratetablemodel.h"
463 #include "library/proxytrackmodel.h"
464+#include "treeitemmodel.h"
465
466 class TrackCollection;
467
468@@ -29,7 +30,7 @@
469 WLibrary* libraryWidget,
470 MixxxKeyboard* keyboard);
471
472- QAbstractItemModel* getChildModel();
473+ TreeItemModel* getChildModel();
474 signals:
475 void showPage(const QUrl& page);
476
477@@ -41,14 +42,22 @@
478
479 void slotCreateCrate();
480 void slotDeleteCrate();
481+ void slotRenameCrate();
482+ void slotImportPlaylist();
483
484 private:
485+ void constructChildModel();
486+ void clearChildModel();
487+
488 TrackCollection* m_pTrackCollection;
489 QAction *m_pCreateCrateAction;
490 QAction *m_pDeleteCrateAction;
491+ QAction *m_pRenameCrateAction;
492+ QAction *m_pImportPlaylistAction;
493 QSqlTableModel m_crateListTableModel;
494 CrateTableModel m_crateTableModel;
495 QModelIndex m_lastRightClickedIndex;
496+ TreeItemModel m_childModel;
497 };
498
499 #endif /* CRATEFEATURE_H */
500
501=== modified file 'mixxx/src/library/dao/cratedao.cpp'
502--- mixxx/src/library/dao/cratedao.cpp 2010-10-19 01:35:26 +0000
503+++ mixxx/src/library/dao/cratedao.cpp 2011-01-06 15:28:44 +0000
504@@ -51,6 +51,25 @@
505 return false;
506 }
507
508+bool CrateDAO::renameCrate(int crateId, const QString& newName) {
509+ qDebug() << "renameCrate()";
510+
511+ Q_ASSERT(m_database.transaction());
512+ QSqlQuery query;
513+ query.prepare("UPDATE " CRATE_TABLE " SET name = :name WHERE id = :id");
514+ query.bindValue(":name", newName);
515+ query.bindValue(":id", crateId);
516+
517+ if (!query.exec()) {
518+ qDebug() << query.executedQuery() << query.lastError();
519+ Q_ASSERT(m_database.rollback());
520+ return false;
521+ }
522+
523+ Q_ASSERT(m_database.commit());
524+ return true;
525+}
526+
527 bool CrateDAO::deleteCrate(int crateId) {
528 Q_ASSERT(m_database.transaction());
529
530
531=== modified file 'mixxx/src/library/dao/cratedao.h'
532--- mixxx/src/library/dao/cratedao.h 2010-10-19 01:35:26 +0000
533+++ mixxx/src/library/dao/cratedao.h 2011-01-06 15:28:44 +0000
534@@ -23,6 +23,7 @@
535 unsigned int crateCount();
536 bool createCrate(const QString& name);
537 bool deleteCrate(int crateId);
538+ bool renameCrate(int crateId, const QString& newName);
539 int getCrateIdByName(const QString& name);
540 int getCrateId(int position);
541 QString crateName(int crateId);
542
543=== modified file 'mixxx/src/library/dao/playlistdao.cpp'
544--- mixxx/src/library/dao/playlistdao.cpp 2010-10-07 03:05:48 +0000
545+++ mixxx/src/library/dao/playlistdao.cpp 2011-01-06 15:28:44 +0000
546@@ -145,6 +145,27 @@
547 //TODO: Crap, we need to shuffle the positions of all the playlists?
548 }
549
550+
551+void PlaylistDAO::renamePlaylist(int playlistId, const QString& newName)
552+{
553+ qDebug() << "renamePlaylist()";
554+
555+ m_database.transaction();
556+ QSqlQuery query(m_database);
557+ query.prepare("UPDATE Playlists SET name = :name WHERE id = :id");
558+ query.bindValue(":name", newName);
559+ query.bindValue(":id", playlistId);
560+
561+ if (!query.exec()) {
562+ qDebug() << query.executedQuery() << query.lastError();
563+ m_database.rollback();
564+ }
565+ else {
566+ m_database.commit();
567+ }
568+}
569+
570+
571 /** Append a track to a playlist */
572 void PlaylistDAO::appendTrackToPlaylist(int trackId, int playlistId)
573 {
574
575=== modified file 'mixxx/src/library/dao/playlistdao.h'
576--- mixxx/src/library/dao/playlistdao.h 2010-09-12 20:00:07 +0000
577+++ mixxx/src/library/dao/playlistdao.h 2011-01-06 15:28:44 +0000
578@@ -17,6 +17,8 @@
579 bool createPlaylist(QString name, bool hidden = false);
580 /** Delete a playlist */
581 void deletePlaylist(int playlistId);
582+ /** Rename a playlist */
583+ void renamePlaylist(int playlistId, const QString& newName);
584 /** Append a track to a playlist */
585 void appendTrackToPlaylist(int trackId, int playlistId);
586 /** Find out how many playlists exist. */
587
588=== modified file 'mixxx/src/library/itunesfeature.cpp'
589--- mixxx/src/library/itunesfeature.cpp 2010-12-03 19:07:40 +0000
590+++ mixxx/src/library/itunesfeature.cpp 2011-01-06 15:28:44 +0000
591@@ -8,6 +8,7 @@
592 #include "library/itunesplaylistmodel.h"
593
594
595+
596 ITunesFeature::ITunesFeature(QObject* parent, TrackCollection* pTrackCollection)
597 : LibraryFeature(parent),
598 m_pTrackCollection(pTrackCollection),
599@@ -15,6 +16,7 @@
600 m_pITunesTrackModel = new ITunesTrackModel(this, m_pTrackCollection);
601 m_pITunesPlaylistModel = new ITunesPlaylistModel(this, m_pTrackCollection);
602 m_isActivated = false;
603+ m_rootItem = new TreeItem("$root","$root", this);
604 }
605
606 ITunesFeature::~ITunesFeature() {
607@@ -58,11 +60,8 @@
608 tr("There was an error loading your iTunes library. Some of "
609 "your iTunes tracks or playlists may not have loaded."));
610 }
611-
612- //Sort the playlists since in iTunes they are sorted, too.
613- //list.sort();
614-
615- m_childModel.setStringList(m_playlists);
616+ //set the root item for the childmodel.
617+ m_childModel.setRootItem(m_rootItem);
618 }
619
620 emit(showTrackModel(m_pITunesTrackModel));
621@@ -76,7 +75,7 @@
622 emit(showTrackModel(m_pITunesPlaylistModel));
623 }
624
625-QAbstractItemModel* ITunesFeature::getChildModel() {
626+TreeItemModel* ITunesFeature::getChildModel() {
627 return &m_childModel;
628 }
629
630@@ -423,8 +422,9 @@
631 << " " << query_insert_to_playlists.lastError();
632 return;
633 }
634- //for the child model
635- m_playlists << playlistname;
636+ //append the playlist to the child model
637+ TreeItem *item = new TreeItem(playlistname, playlistname, this, m_rootItem);
638+ m_rootItem->appendChild(item);
639
640 }
641 /*
642
643=== modified file 'mixxx/src/library/itunesfeature.h'
644--- mixxx/src/library/itunesfeature.h 2010-11-21 04:39:36 +0000
645+++ mixxx/src/library/itunesfeature.h 2011-01-06 15:28:44 +0000
646@@ -9,6 +9,8 @@
647
648 #include "library/libraryfeature.h"
649 #include "library/trackcollection.h"
650+#include "treeitemmodel.h"
651+#include "treeitem.h"
652
653 //class ITunesPlaylistModel;
654 class ITunesTrackModel;
655@@ -30,7 +32,7 @@
656 bool dragMoveAccept(QUrl url);
657 bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
658
659- QAbstractItemModel* getChildModel();
660+ TreeItemModel* getChildModel();
661
662 public slots:
663 void activate();
664@@ -49,11 +51,13 @@
665
666 ITunesTrackModel* m_pITunesTrackModel;
667 ITunesPlaylistModel* m_pITunesPlaylistModel;
668- QStringListModel m_childModel;
669+ TreeItemModel m_childModel;
670 QStringList m_playlists;
671 TrackCollection* m_pTrackCollection;
672 QSqlDatabase &m_database;
673 bool m_isActivated;
674+ //The root of the childmodel
675+ TreeItem *m_rootItem;
676 };
677
678 #endif /* ITUNESFEATURE_H */
679
680=== modified file 'mixxx/src/library/library.cpp'
681--- mixxx/src/library/library.cpp 2010-11-18 20:36:13 +0000
682+++ mixxx/src/library/library.cpp 2011-01-06 15:28:44 +0000
683@@ -18,6 +18,7 @@
684 #include "library/playlistfeature.h"
685 #include "library/preparefeature.h"
686 #include "library/promotracksfeature.h"
687+#include "library/traktorfeature.h"
688
689 #include "widget/wtracktableview.h"
690 #include "widget/wlibrary.h"
691@@ -26,6 +27,7 @@
692 #include "mixxxkeyboard.h"
693 #include "librarymidicontrol.h"
694
695+
696 // This is is the name which we use to register the WTrackTableView with the
697 // WLibrary
698 const QString Library::m_sTrackViewName = QString("WTrackTableView");
699@@ -63,6 +65,8 @@
700 addFeature(new RhythmboxFeature(this));
701 if (ITunesFeature::isSupported())
702 addFeature(new ITunesFeature(this, m_pTrackCollection));
703+ if (TraktorFeature::isSupported())
704+ addFeature(new TraktorFeature(this, m_pTrackCollection));
705
706 //Show the promo tracks view on first run, otherwise show the library
707 if (firstRun) {
708
709=== modified file 'mixxx/src/library/libraryfeature.h'
710--- mixxx/src/library/libraryfeature.h 2010-10-24 09:50:11 +0000
711+++ mixxx/src/library/libraryfeature.h 2011-01-06 15:28:44 +0000
712@@ -14,6 +14,7 @@
713 #include <QUrl>
714
715 #include "trackinfoobject.h"
716+#include "treeitemmodel.h"
717
718 class TrackModel;
719 class WLibrarySidebar;
720@@ -38,7 +39,7 @@
721 WLibrary* /* libraryWidget */,
722 MixxxKeyboard* /* keyboard */) {
723 }
724- virtual QAbstractItemModel* getChildModel() = 0;
725+ virtual TreeItemModel* getChildModel() = 0;
726
727 public slots:
728 virtual void activate() = 0;
729
730=== modified file 'mixxx/src/library/mixxxlibraryfeature.cpp'
731--- mixxx/src/library/mixxxlibraryfeature.cpp 2010-10-28 19:31:23 +0000
732+++ mixxx/src/library/mixxxlibraryfeature.cpp 2011-01-06 15:28:44 +0000
733@@ -9,6 +9,7 @@
734 #include "library/missingtablemodel.h"
735 #include "library/proxytrackmodel.h"
736 #include "library/trackcollection.h"
737+#include "treeitem.h"
738
739 #define CHILD_MISSING "Missing Songs"
740
741@@ -17,9 +18,11 @@
742 : LibraryFeature(parent),
743 m_pLibraryTableModel(new LibraryTableModel(this, pTrackCollection)),
744 m_pMissingTableModel(new MissingTableModel(this, pTrackCollection)) {
745- QStringList children;
746- children << tr("Missing Songs"); //Insert michael jackson joke here
747- m_childModel.setStringList(children);
748+
749+ TreeItem *rootItem = new TreeItem("$root","$root", this);
750+ TreeItem *childItem = new TreeItem(CHILD_MISSING,CHILD_MISSING, this,rootItem);
751+ rootItem->appendChild(childItem);
752+ m_childModel.setRootItem(rootItem);
753 }
754
755 MixxxLibraryFeature::~MixxxLibraryFeature() {
756@@ -35,7 +38,7 @@
757 return QIcon(":/images/library/ic_library_library.png");
758 }
759
760-QAbstractItemModel* MixxxLibraryFeature::getChildModel() {
761+TreeItemModel* MixxxLibraryFeature::getChildModel() {
762 return &m_childModel;
763 }
764
765@@ -53,7 +56,10 @@
766 void MixxxLibraryFeature::activateChild(const QModelIndex& index) {
767 QString itemName = index.data().toString();
768
769- if (itemName == m_childModel.stringList().at(0))
770+ /*if (itemName == m_childModel.stringList().at(0))
771+ emit(showTrackModel(m_pMissingTableModel));
772+ */
773+ if (itemName == CHILD_MISSING)
774 emit(showTrackModel(m_pMissingTableModel));
775 }
776
777
778=== modified file 'mixxx/src/library/mixxxlibraryfeature.h'
779--- mixxx/src/library/mixxxlibraryfeature.h 2010-01-29 19:38:32 +0000
780+++ mixxx/src/library/mixxxlibraryfeature.h 2011-01-06 15:28:44 +0000
781@@ -7,6 +7,7 @@
782 #include <QStringListModel>
783
784 #include "library/libraryfeature.h"
785+#include "treeitemmodel.h"
786
787 class LibraryTableModel;
788 class MissingTableModel;
789@@ -27,7 +28,7 @@
790 bool dragMoveAccept(QUrl url);
791 bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
792
793- QAbstractItemModel* getChildModel();
794+ TreeItemModel* getChildModel();
795
796 public slots:
797 void activate();
798@@ -38,7 +39,7 @@
799 private:
800 LibraryTableModel* m_pLibraryTableModel;
801 MissingTableModel* m_pMissingTableModel;
802- QStringListModel m_childModel;
803+ TreeItemModel m_childModel;
804 };
805
806
807
808=== added file 'mixxx/src/library/parser.cpp'
809--- mixxx/src/library/parser.cpp 1970-01-01 00:00:00 +0000
810+++ mixxx/src/library/parser.cpp 2011-01-06 15:28:44 +0000
811@@ -0,0 +1,76 @@
812+//
813+// C++ Implementation: parser
814+//
815+// Description: superclass for external formats parsers
816+//
817+//
818+// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
819+//
820+// Copyright: See COPYING file that comes with this distribution
821+//
822+//
823+
824+#include <QtDebug>
825+#include "parser.h"
826+
827+/**
828+ @author Ingo Kossyk (kossyki@cs.tu-berlin.de)
829+ **/
830+
831+
832+Parser::Parser() : QObject()
833+{
834+}
835+
836+Parser::~Parser()
837+{
838+
839+
840+}
841+
842+void Parser::clearLocations()
843+{
844+ while(!m_sLocations.isEmpty())
845+ m_sLocations.removeFirst();
846+}
847+
848+long Parser::countParsed()
849+{
850+ return (long)m_sLocations.count();
851+}
852+
853+bool Parser::isFilepath(QString sFilepath){
854+ QFile file(sFilepath);
855+ bool exists = file.exists();
856+ file.close();
857+ return exists;
858+}
859+
860+bool Parser::isBinary(QString filename){
861+ QFile file(filename);
862+
863+ if(file.open(QIODevice::ReadOnly)){
864+ char c;
865+ unsigned char uc;
866+
867+ if(!file.getChar(&c))
868+ {
869+ qDebug() << "Parser: Error reading stream on " << filename;
870+ return true; //should this raise an exception?
871+ }
872+
873+ uc = uchar(c);
874+
875+ if(!(33<=uc && uc<=127)) //Starting byte is no character
876+ {
877+ file.close();
878+ return true;
879+ }
880+
881+ } else{
882+ qDebug() << "Parser: Could not open file: " << filename;
883+ }
884+ //qDebug(QString("Parser: textstream starting character is: %1").arg(i));
885+ file.close();
886+ return false;
887+}
888
889=== added file 'mixxx/src/library/parser.h'
890--- mixxx/src/library/parser.h 1970-01-01 00:00:00 +0000
891+++ mixxx/src/library/parser.h 2011-01-06 15:28:44 +0000
892@@ -0,0 +1,53 @@
893+//
894+// C++ Interface: parser
895+//
896+// Description: Interface header for the parser Parser
897+//
898+//
899+// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
900+//
901+// Copyright: See COPYING file that comes with this distribution
902+//
903+//
904+
905+#ifndef PARSER_H
906+#define PARSER_H
907+
908+/**Developer Information:
909+This is the rootclass for all parser classes for the Importer class.
910+It can be used to write a new type-specific parser by deriving a new class
911+from it and overwrite the parse function and add class specific functions to
912+it afterwards fro proper functioning
913+**/
914+
915+#include <qobject.h>
916+#include <qstring.h>
917+#include <qfile.h>
918+
919+
920+class Parser : public QObject
921+{
922+public:
923+ Parser();
924+ ~Parser();
925+ /**Can be called to parse a pls file
926+ Note for developers:
927+ This function should return an empty PtrList
928+ or 0 in order for the trackimporter to function**/
929+ virtual QList<QString> parse(QString) = 0;
930+
931+
932+protected:
933+ /**Pointer to the parsed Filelocations**/
934+ QList<QString> m_sLocations;
935+ /**Returns the number of parsed locations**/
936+ long countParsed();
937+ /**Clears m_psLocations**/
938+ void clearLocations();
939+ /**Checks if the file does contain binary content**/
940+ bool isBinary(QString);
941+ /**Checks if the given string represents a local filepath**/
942+ bool isFilepath(QString );
943+};
944+
945+#endif
946
947=== added file 'mixxx/src/library/parserm3u.cpp'
948--- mixxx/src/library/parserm3u.cpp 1970-01-01 00:00:00 +0000
949+++ mixxx/src/library/parserm3u.cpp 2011-01-06 15:28:44 +0000
950@@ -0,0 +1,122 @@
951+//
952+// C++ Implementation: parserm3u
953+//
954+// Description: module to parse m3u(plaintext) formated playlists
955+//
956+//
957+// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
958+//
959+// Copyright: See COPYING file that comes with this distribution
960+//
961+//
962+#include <QTextStream>
963+#include <QDebug>
964+#include "parserm3u.h"
965+#include <QUrl>
966+
967+/**
968+ @author Ingo Kossyk (kossyki@cs.tu-berlin.de)
969+ **/
970+
971+/**
972+ ToDo:
973+ - parse ALL informations from the pls file if available ,
974+ not only the filepath;
975+
976+ Userinformation :
977+ The M3U format is just a headerless plaintext format
978+ where every line of text either represents
979+ a file location or a comment. comments are being
980+ preceeded by a '#'. This parser will try to parse all
981+ file information from the given file and add the filepaths
982+ to the locations ptrlist when the file is existing locally
983+ or on a mounted harddrive.
984+ **/
985+
986+ParserM3u::ParserM3u() : Parser()
987+{
988+}
989+
990+ParserM3u::~ParserM3u()
991+{
992+
993+}
994+
995+
996+QList<QString> ParserM3u::parse(QString sFilename)
997+{
998+ QFile file(sFilename);
999+ QString basepath = sFilename.section('/', 0, -2);
1000+
1001+ clearLocations();
1002+ //qDebug() << "ParserM3u: Starting to parse.";
1003+ if (file.open(QIODevice::ReadOnly) && !isBinary(sFilename)) {
1004+ /* Unfortunately, QT 4.7 does not handle <CR> line breaks.
1005+ * This is important on OS X where iTunes, e.g., exports M3U playlists using <CR>
1006+ *
1007+ * Using QFile::readAll() we obtain the complete content of the playlist as a ByteArray.
1008+ * We replace any '\r' with '\n' if applicaple
1009+ * This ensures that playlists from iTunes on OS X can be parsed
1010+ */
1011+ QByteArray ba = file.readAll();
1012+ ba.replace('\r',"\n");
1013+ QTextStream textstream(ba.data());
1014+
1015+ while(!textstream.atEnd()) {
1016+ QString sLine = getFilepath(&textstream, basepath);
1017+ if(sLine.isEmpty())
1018+ break;
1019+
1020+ //qDebug) << ("ParserM3u: parsed: " << (sLine);
1021+ m_sLocations.append(sLine);
1022+ }
1023+
1024+ file.close();
1025+
1026+ if(m_sLocations.count() != 0)
1027+ return m_sLocations;
1028+ else
1029+ return QList<QString>(); // NULL pointer returned when no locations were found
1030+
1031+ }
1032+
1033+ file.close();
1034+ return QList<QString>(); //if we get here something went wrong
1035+}
1036+
1037+
1038+QString ParserM3u::getFilepath(QTextStream *stream, QString basepath)
1039+{
1040+ QString textline,filename = "";
1041+
1042+ textline = stream->readLine();
1043+ qDebug() << textline;
1044+ while(!textline.isEmpty()){
1045+ if(textline.isNull())
1046+ break;
1047+
1048+ if(!textline.contains("#") && !textline.isEmpty()){
1049+ filename = textline;
1050+ filename.remove("file://");
1051+ QByteArray strlocbytes = filename.toUtf8();
1052+ QUrl location = QUrl::fromEncoded(strlocbytes);
1053+ QString trackLocation = location.toLocalFile();
1054+ //qDebug() << trackLocation;
1055+ if(isFilepath(trackLocation)) {
1056+ return trackLocation;
1057+ } else {
1058+ // Try relative to m3u dir
1059+ QString rel = basepath + "/" + trackLocation;
1060+ if (isFilepath(rel)) {
1061+ return rel;
1062+ }
1063+ // We couldn't match this to a real file so ignore it
1064+ }
1065+ }
1066+ textline = stream->readLine();
1067+ }
1068+
1069+ // Signal we reached the end
1070+ return 0;
1071+
1072+}
1073
1074=== added file 'mixxx/src/library/parserm3u.h'
1075--- mixxx/src/library/parserm3u.h 1970-01-01 00:00:00 +0000
1076+++ mixxx/src/library/parserm3u.h 2011-01-06 15:28:44 +0000
1077@@ -0,0 +1,35 @@
1078+//
1079+// C++ Interface: parserm3u
1080+//
1081+// Description: Interface header for the example parser ParserM3u
1082+//
1083+//
1084+// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
1085+//
1086+// Copyright: See COPYING file that comes with this distribution
1087+//
1088+//
1089+#include "parser.h"
1090+
1091+#ifndef PARSERM3U_H
1092+#define PARSERM3U_H
1093+
1094+class QTextStream;
1095+
1096+class ParserM3u : public Parser
1097+{
1098+ Q_OBJECT
1099+public:
1100+ ParserM3u();
1101+ ~ParserM3u();
1102+ /**Overwriting function parse in class Parser**/
1103+ QList<QString> parse(QString);
1104+
1105+private:
1106+ /**Reads a line from the file and returns filepath if a valid file**/
1107+ QString getFilepath(QTextStream *, QString);
1108+
1109+
1110+};
1111+
1112+#endif
1113
1114=== added file 'mixxx/src/library/parserpls.cpp'
1115--- mixxx/src/library/parserpls.cpp 1970-01-01 00:00:00 +0000
1116+++ mixxx/src/library/parserpls.cpp 2011-01-06 15:28:44 +0000
1117@@ -0,0 +1,143 @@
1118+//
1119+// C++ Implementation: parserpls
1120+//
1121+// Description: module to parse pls formated playlists
1122+//
1123+//
1124+// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
1125+//
1126+// Copyright: See COPYING file that comes with this distribution
1127+//
1128+//
1129+#include "parser.h"
1130+#include "parserpls.h"
1131+#include <QDebug>
1132+#include <QTextStream>
1133+#include <QFile>
1134+#include <QUrl>
1135+
1136+/**
1137+ @author Ingo Kossyk (kossyki@cs.tu-berlin.de)
1138+ **/
1139+
1140+/**
1141+ ToDo:
1142+ - parse ALL informations from the pls file if available ,
1143+ not only the filepath;
1144+ **/
1145+
1146+ParserPls::ParserPls() : Parser()
1147+{
1148+}
1149+
1150+ParserPls::~ParserPls()
1151+{
1152+}
1153+
1154+QList<QString> ParserPls::parse(QString sFilename)
1155+{
1156+ //long numEntries =0;
1157+ QFile file(sFilename);
1158+ QString basepath = sFilename.section('/', 0, -2);
1159+
1160+ clearLocations();
1161+
1162+ if (file.open(QIODevice::ReadOnly) && !isBinary(sFilename) ) {
1163+
1164+ /* Unfortunately, QT 4.7 does not handle <CR> line breaks.
1165+ * This is important on OS X where iTunes, e.g., exports M3U playlists using <CR>
1166+ *
1167+ * Using QFile::readAll() we obtain the complete content of the playlist as a ByteArray.
1168+ * We replace any '\r' with '\n' if applicaple
1169+ * This ensures that playlists from iTunes on OS X can be parsed
1170+ */
1171+ QByteArray ba = file.readAll();
1172+ ba.replace('\r',"\n");
1173+ QTextStream textstream(ba.data());
1174+
1175+ while(!textstream.atEnd()) {
1176+ QString psLine = getFilepath(&textstream, basepath);
1177+ if(psLine.isNull()) {
1178+ break;
1179+ } else {
1180+ m_sLocations.append(psLine);
1181+ }
1182+
1183+ //--numEntries;
1184+ }
1185+
1186+ file.close();
1187+
1188+ if(m_sLocations.count() != 0)
1189+ return m_sLocations;
1190+ else
1191+ return QList<QString>(); // NULL pointer returned when no locations were found
1192+ }
1193+
1194+ file.close();
1195+ return QList<QString>(); //if we get here something went wrong :D
1196+}
1197+
1198+long ParserPls::getNumEntries(QTextStream *stream)
1199+{
1200+
1201+ QString textline;
1202+ textline = stream->readLine();
1203+
1204+ if(textline.contains("[playlist]")){
1205+
1206+ while(!textline.contains("NumberOfEntries"))
1207+ textline = stream->readLine();
1208+
1209+ QString temp = textline.section("=",-1,-1);
1210+
1211+ return temp.toLong();
1212+
1213+ } else{
1214+ qDebug() << "ParserPls: pls file is not a playlist! \n";
1215+ return 0;
1216+ }
1217+
1218+}
1219+
1220+
1221+QString ParserPls::getFilepath(QTextStream *stream, QString basepath)
1222+{
1223+ QString textline,filename = "";
1224+ textline = stream->readLine();
1225+ while(!textline.isEmpty()){
1226+ if(textline.isNull())
1227+ break;
1228+
1229+ if(textline.contains("File")) {
1230+ int iPos = textline.indexOf("=",0);
1231+ ++iPos;
1232+
1233+ filename = textline.right(textline.length()-iPos);
1234+
1235+ //Rythmbox playlists starts with file://<path>
1236+ //We remove the file protocol if found.
1237+ filename.remove("file://");
1238+ QByteArray strlocbytes = filename.toUtf8();
1239+ QUrl location = QUrl::fromEncoded(strlocbytes);
1240+ QString trackLocation = location.toLocalFile();
1241+ //qDebug() << trackLocation;
1242+
1243+ if(isFilepath(trackLocation)) {
1244+ return trackLocation;
1245+ } else {
1246+ // Try relative to m3u dir
1247+ QString rel = basepath + "/" + trackLocation;
1248+ if (isFilepath(rel)) {
1249+ return rel;
1250+ }
1251+ // We couldn't match this to a real file so ignore it
1252+ }
1253+ }
1254+ textline = stream->readLine();
1255+ }
1256+
1257+ // Signal we reached the end
1258+ return 0;
1259+
1260+}
1261
1262=== added file 'mixxx/src/library/parserpls.h'
1263--- mixxx/src/library/parserpls.h 1970-01-01 00:00:00 +0000
1264+++ mixxx/src/library/parserpls.h 2011-01-06 15:28:44 +0000
1265@@ -0,0 +1,38 @@
1266+//
1267+// C++ Interface: parserpls
1268+//
1269+// Description: Interface header for the example parser PlsParser
1270+//
1271+//
1272+// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
1273+//
1274+// Copyright: See COPYING file that comes with this distribution
1275+//
1276+//
1277+
1278+
1279+#ifndef PARSERPLS_H
1280+#define PARSERPLS_H
1281+
1282+#include "parser.h"
1283+
1284+class QTextStream;
1285+
1286+class ParserPls : public Parser
1287+{
1288+ Q_OBJECT
1289+public:
1290+ ParserPls();
1291+ ~ParserPls();
1292+ /**Can be called to parse a pls file**/
1293+ QList<QString> parse(QString);
1294+
1295+private:
1296+ /**Returns the Number of entries in the pls file**/
1297+ long getNumEntries(QTextStream * );
1298+ /**Reads a line from the file and returns filepath**/
1299+ QString getFilepath(QTextStream *, QString);
1300+
1301+};
1302+
1303+#endif
1304
1305=== modified file 'mixxx/src/library/playlistfeature.cpp'
1306--- mixxx/src/library/playlistfeature.cpp 2010-11-25 01:06:46 +0000
1307+++ mixxx/src/library/playlistfeature.cpp 2011-01-06 15:28:44 +0000
1308@@ -1,8 +1,14 @@
1309 #include <QtDebug>
1310 #include <QMenu>
1311 #include <QInputDialog>
1312+#include <QFileDialog>
1313+#include <QDesktopServices>
1314
1315 #include "library/playlistfeature.h"
1316+#include "library/parser.h"
1317+#include "library/parserm3u.h"
1318+#include "library/parserpls.h"
1319+
1320
1321 #include "widget/wlibrary.h"
1322 #include "widget/wlibrarysidebar.h"
1323@@ -10,6 +16,7 @@
1324 #include "library/trackcollection.h"
1325 #include "library/playlisttablemodel.h"
1326 #include "mixxxkeyboard.h"
1327+#include "treeitem.h"
1328
1329 PlaylistFeature::PlaylistFeature(QObject* parent, TrackCollection* pTrackCollection)
1330 : LibraryFeature(parent),
1331@@ -27,6 +34,14 @@
1332 connect(m_pDeletePlaylistAction, SIGNAL(triggered()),
1333 this, SLOT(slotDeletePlaylist()));
1334
1335+ m_pRenamePlaylistAction = new QAction(tr("Rename"),this);
1336+ connect(m_pRenamePlaylistAction, SIGNAL(triggered()),
1337+ this, SLOT(slotRenamePlaylist()));
1338+
1339+ m_pImportPlaylistAction = new QAction(tr("Import Playlist"),this);
1340+ connect(m_pImportPlaylistAction, SIGNAL(triggered()),
1341+ this, SLOT(slotImportPlaylist()));
1342+
1343 // Setup the sidebar playlist model
1344 m_playlistTableModel.setTable("Playlists");
1345 m_playlistTableModel.setFilter("hidden=0");
1346@@ -34,15 +49,30 @@
1347 m_playlistTableModel.removeColumn(m_playlistTableModel.fieldIndex("position"));
1348 m_playlistTableModel.removeColumn(m_playlistTableModel.fieldIndex("date_created"));
1349 m_playlistTableModel.removeColumn(m_playlistTableModel.fieldIndex("date_modified"));
1350- m_playlistTableModel.setSort(m_playlistTableModel.fieldIndex("position"),
1351+ m_playlistTableModel.setSort(m_playlistTableModel.fieldIndex("name"),
1352 Qt::AscendingOrder);
1353 m_playlistTableModel.select();
1354+
1355+ //construct child model
1356+ TreeItem *rootItem = new TreeItem("$root","$root", this);
1357+
1358+ int idColumn = m_playlistTableModel.record().indexOf("name");
1359+ for (int row = 0; row < m_playlistTableModel.rowCount(); ++row) {
1360+ QModelIndex ind = m_playlistTableModel.index(row, idColumn);
1361+ QString playlist_name = m_playlistTableModel.data(ind).toString();
1362+ TreeItem *playlist_item = new TreeItem(playlist_name, playlist_name, this, rootItem);
1363+ rootItem->appendChild(playlist_item);
1364+
1365+ }
1366+ m_childModel.setRootItem(rootItem);
1367 }
1368
1369 PlaylistFeature::~PlaylistFeature() {
1370 delete m_pPlaylistTableModel;
1371 delete m_pCreatePlaylistAction;
1372 delete m_pDeletePlaylistAction;
1373+ delete m_pImportPlaylistAction;
1374+ delete m_pRenamePlaylistAction;
1375 }
1376
1377 QVariant PlaylistFeature::title() {
1378@@ -95,52 +125,114 @@
1379 QMenu menu(NULL);
1380 menu.addAction(m_pCreatePlaylistAction);
1381 menu.addSeparator();
1382+ menu.addAction(m_pRenamePlaylistAction);
1383 menu.addAction(m_pDeletePlaylistAction);
1384+ menu.addSeparator();
1385+ menu.addAction(m_pImportPlaylistAction);
1386 menu.exec(globalPos);
1387 }
1388
1389 void PlaylistFeature::slotCreatePlaylist() {
1390- int err = 0;
1391- bool ok = false;
1392- QString name = QInputDialog::getText(
1393- NULL, tr("New Playlist"), tr("Playlist name:"),
1394- QLineEdit::Normal, tr("New Playlist"), &ok);
1395-
1396- if (!ok)
1397- return;
1398-
1399- //Ensure the name isn't blank
1400- if (name == "") {
1401- QMessageBox::warning(NULL,
1402- tr("Playlist Creation Failed"),
1403- tr("A playlist cannot have a blank name."));
1404- return;
1405- }
1406- //Ensure that a playlist with this name doesn't exist already
1407- if (m_playlistDao.getPlaylistIdFromName(name) == -1) {
1408- //Ensure the creation works at the DAO level...
1409- if (!m_playlistDao.createPlaylist(name))
1410- {
1411- QMessageBox::warning(NULL,
1412- tr("Playlist Creation Failed"),
1413- tr("An unknown error occurred while creating playlist: ")
1414- + name);
1415+ QString name;
1416+ bool validNameGiven = false;
1417+
1418+ do {
1419+ bool ok = false;
1420+ name = QInputDialog::getText(NULL,
1421+ tr("New Playlist"),
1422+ tr("Playlist name:"),
1423+ QLineEdit::Normal,
1424+ tr("New Playlist"),
1425+ &ok).trimmed();
1426+
1427+ if (!ok)
1428 return;
1429- }
1430-
1431+
1432+ int existingId = m_playlistDao.getPlaylistIdFromName(name);
1433+
1434+ if (existingId != -1) {
1435+ QMessageBox::warning(NULL,
1436+ tr("Playlist Creation Failed"),
1437+ tr("A playlist by that name already exists."));
1438+ }
1439+ else if (name.isEmpty()) {
1440+ QMessageBox::warning(NULL,
1441+ tr("Playlist Creation Failed"),
1442+ tr("A playlist cannot have a blank name."));
1443+ }
1444+ else {
1445+ validNameGiven = true;
1446+ }
1447+
1448+ } while (!validNameGiven);
1449+
1450+ bool playlistCreated = m_playlistDao.createPlaylist(name);
1451+
1452+ if (playlistCreated) {
1453+ clearChildModel();
1454 m_playlistTableModel.select();
1455+ constructChildModel();
1456 emit(featureUpdated());
1457-
1458 //Switch the view to the new playlist.
1459 int playlistId = m_playlistDao.getPlaylistIdFromName(name);
1460 m_pPlaylistTableModel->setPlaylist(playlistId);
1461 // TODO(XXX) set sidebar selection
1462 emit(showTrackModel(m_pPlaylistTableModel));
1463- } else {
1464- QMessageBox::warning(NULL,
1465- tr("Playlist Creation Failed"),
1466- tr("A playlist by that name already exists."));
1467- }
1468+ }
1469+ else {
1470+ QMessageBox::warning(NULL,
1471+ tr("Playlist Creation Failed"),
1472+ tr("An unknown error occurred while creating playlist: ")
1473+ + name);
1474+ }
1475+}
1476+
1477+void PlaylistFeature::slotRenamePlaylist()
1478+{
1479+ qDebug() << "slotRenamePlaylist()";
1480+
1481+ QString oldName = m_lastRightClickedIndex.data().toString();
1482+ int playlistId = m_playlistDao.getPlaylistIdFromName(oldName);
1483+
1484+ QString newName;
1485+ bool validNameGiven = false;
1486+
1487+ do {
1488+ bool ok = false;
1489+ newName = QInputDialog::getText(NULL,
1490+ tr("Rename Playlist"),
1491+ tr("New playlist name:"),
1492+ QLineEdit::Normal,
1493+ oldName,
1494+ &ok).trimmed();
1495+
1496+ if (!ok || oldName == newName) {
1497+ return;
1498+ }
1499+
1500+ int existingId = m_playlistDao.getPlaylistIdFromName(newName);
1501+
1502+ if (existingId != -1) {
1503+ QMessageBox::warning(NULL,
1504+ tr("Renaming Playlist Failed"),
1505+ tr("A playlist by that name already exists."));
1506+ }
1507+ else if (newName.isEmpty()) {
1508+ QMessageBox::warning(NULL,
1509+ tr("Renaming Playlist Failed"),
1510+ tr("A playlist cannot have a blank name."));
1511+ }
1512+ else {
1513+ validNameGiven = true;
1514+ }
1515+ } while (!validNameGiven);
1516+
1517+ m_playlistDao.renamePlaylist(playlistId, newName);
1518+ clearChildModel();
1519+ m_playlistTableModel.select();
1520+ constructChildModel();
1521+ emit(featureUpdated());
1522+ m_pPlaylistTableModel->setPlaylist(playlistId);
1523 }
1524
1525 void PlaylistFeature::slotDeletePlaylist()
1526@@ -149,8 +241,11 @@
1527 if (m_lastRightClickedIndex.isValid()) {
1528 int playlistId = m_playlistDao.getPlaylistIdFromName(m_lastRightClickedIndex.data().toString());
1529 Q_ASSERT(playlistId >= 0);
1530+
1531+ clearChildModel();
1532 m_playlistDao.deletePlaylist(playlistId);
1533 m_playlistTableModel.select();
1534+ constructChildModel();
1535 }
1536
1537 emit(featureUpdated());
1538@@ -203,6 +298,70 @@
1539 return true;
1540 }
1541
1542-QAbstractItemModel* PlaylistFeature::getChildModel() {
1543- return &m_playlistTableModel;
1544+TreeItemModel* PlaylistFeature::getChildModel() {
1545+ return &m_childModel;
1546+}
1547+/**
1548+ * Purpose: When inserting or removing playlists,
1549+ * we require the sidebar model not to reset.
1550+ * This method queries the database and does dynamic insertion
1551+*/
1552+void PlaylistFeature::constructChildModel()
1553+{
1554+ QList<QString> data_list;
1555+ int idColumn = m_playlistTableModel.record().indexOf("name");
1556+ for (int row = 0; row < m_playlistTableModel.rowCount(); ++row) {
1557+ QModelIndex ind = m_playlistTableModel.index(row, idColumn);
1558+ QString playlist_name = m_playlistTableModel.data(ind).toString();
1559+ data_list.insert(row,playlist_name);
1560+ }
1561+
1562+ m_childModel.insertRows(data_list, 0, m_playlistTableModel.rowCount());
1563+}
1564+/**
1565+ * Clears the child model dynamically
1566+ */
1567+void PlaylistFeature::clearChildModel()
1568+{
1569+ m_childModel.removeRows(0,m_playlistTableModel.rowCount());
1570+}
1571+void PlaylistFeature::slotImportPlaylist()
1572+{
1573+ qDebug() << "slotImportPlaylist() row:" ; //<< m_lastRightClickedIndex.data();
1574+
1575+
1576+ QString playlist_file = QFileDialog::getOpenFileName
1577+ (
1578+ NULL,
1579+ tr("Import Playlist"),
1580+ QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
1581+ tr("Playlist Files (*.m3u *.pls)")
1582+ );
1583+ //Exit method if user cancelled the open dialog.
1584+ if (playlist_file.isNull() || playlist_file.isEmpty() ) return;
1585+
1586+ Parser* playlist_parser = NULL;
1587+
1588+ if(playlist_file.endsWith(".m3u", Qt::CaseInsensitive))
1589+ {
1590+ playlist_parser = new ParserM3u();
1591+ }
1592+ else if(playlist_file.endsWith(".pls", Qt::CaseInsensitive))
1593+ {
1594+ playlist_parser = new ParserPls();
1595+ }
1596+ else
1597+ {
1598+ return;
1599+ }
1600+ QList<QString> entries = playlist_parser->parse(playlist_file);
1601+
1602+ //Iterate over the List that holds URLs of playlist entires
1603+ for (int i = 0; i < entries.size(); ++i) {
1604+ m_pPlaylistTableModel->addTrack(QModelIndex(), entries[i]);
1605+
1606+ }
1607+
1608+ //delete the parser object
1609+ if(playlist_parser) delete playlist_parser;
1610 }
1611
1612=== modified file 'mixxx/src/library/playlistfeature.h'
1613--- mixxx/src/library/playlistfeature.h 2010-02-23 20:06:25 +0000
1614+++ mixxx/src/library/playlistfeature.h 2011-01-06 15:28:44 +0000
1615@@ -11,6 +11,8 @@
1616 #include "library/libraryfeature.h"
1617 #include "library/dao/playlistdao.h"
1618 #include "library/dao/trackdao.h"
1619+#include "treeitemmodel.h"
1620+
1621
1622 class PlaylistTableModel;
1623 class TrackCollection;
1624@@ -29,8 +31,8 @@
1625 bool dragMoveAccept(QUrl url);
1626 bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
1627
1628- QAbstractItemModel* getChildModel();
1629-
1630+ TreeItemModel* getChildModel();
1631+
1632 void bindWidget(WLibrarySidebar* sidebarWidget,
1633 WLibrary* libraryWidget,
1634 MixxxKeyboard* keyboard);
1635@@ -45,15 +47,23 @@
1636
1637 void slotCreatePlaylist();
1638 void slotDeletePlaylist();
1639+ void slotRenamePlaylist();
1640+ void slotImportPlaylist();
1641
1642 private:
1643+ void constructChildModel();
1644+ void clearChildModel();
1645+
1646 PlaylistTableModel* m_pPlaylistTableModel;
1647 PlaylistDAO &m_playlistDao;
1648 TrackDAO &m_trackDao;
1649 QAction *m_pCreatePlaylistAction;
1650 QAction *m_pDeletePlaylistAction;
1651+ QAction *m_pRenamePlaylistAction;
1652+ QAction *m_pImportPlaylistAction;
1653 QSqlTableModel m_playlistTableModel;
1654 QModelIndex m_lastRightClickedIndex;
1655+ TreeItemModel m_childModel;
1656 };
1657
1658 #endif /* PLAYLISTFEATURE_H */
1659
1660=== modified file 'mixxx/src/library/preparefeature.cpp'
1661--- mixxx/src/library/preparefeature.cpp 2010-10-24 09:50:11 +0000
1662+++ mixxx/src/library/preparefeature.cpp 2011-01-06 15:28:44 +0000
1663@@ -50,7 +50,7 @@
1664 libraryWidget->registerView(m_sPrepareViewName, pPrepareView);
1665 }
1666
1667-QAbstractItemModel* PrepareFeature::getChildModel() {
1668+TreeItemModel* PrepareFeature::getChildModel() {
1669 return &m_childModel;
1670 }
1671
1672
1673=== modified file 'mixxx/src/library/preparefeature.h'
1674--- mixxx/src/library/preparefeature.h 2010-02-23 20:06:25 +0000
1675+++ mixxx/src/library/preparefeature.h 2011-01-06 15:28:44 +0000
1676@@ -8,6 +8,7 @@
1677 #include <QStringListModel>
1678 #include "library/libraryfeature.h"
1679 #include "configobject.h"
1680+#include "treeitemmodel.h"
1681
1682 class LibraryTableModel;
1683 class TrackCollection;
1684@@ -32,7 +33,7 @@
1685 WLibrary* libraryWidget,
1686 MixxxKeyboard* keyboard);
1687
1688- QAbstractItemModel* getChildModel();
1689+ TreeItemModel* getChildModel();
1690
1691 public slots:
1692 void activate();
1693@@ -43,7 +44,7 @@
1694 private:
1695 ConfigObject<ConfigValue>* m_pConfig;
1696 TrackCollection* m_pTrackCollection;
1697- QStringListModel m_childModel;
1698+ TreeItemModel m_childModel;
1699 const static QString m_sPrepareViewName;
1700 };
1701
1702
1703=== modified file 'mixxx/src/library/promotracksfeature.cpp'
1704--- mixxx/src/library/promotracksfeature.cpp 2010-10-27 06:51:37 +0000
1705+++ mixxx/src/library/promotracksfeature.cpp 2011-01-06 15:28:44 +0000
1706@@ -161,7 +161,7 @@
1707 */
1708 }
1709
1710-QAbstractItemModel* PromoTracksFeature::getChildModel() {
1711+TreeItemModel* PromoTracksFeature::getChildModel() {
1712 //XXX Promo 3.0:
1713 //return NULL;
1714 return &m_childModel;
1715
1716=== modified file 'mixxx/src/library/promotracksfeature.h'
1717--- mixxx/src/library/promotracksfeature.h 2010-10-26 08:02:18 +0000
1718+++ mixxx/src/library/promotracksfeature.h 2011-01-06 15:28:44 +0000
1719@@ -20,12 +20,14 @@
1720
1721 #include <QStringListModel>
1722
1723+#include "treeitemmodel.h"
1724 #include "trackinfoobject.h"
1725 #include "library/libraryfeature.h"
1726 #include "library/dao/playlistdao.h"
1727 #include "library/cratetablemodel.h"
1728 #include "configobject.h"
1729
1730+
1731 class PlaylistTableModel;
1732 class ProxyTrackModel;
1733 class TrackCollection;
1734@@ -57,7 +59,7 @@
1735 WLibrary* libraryWidget,
1736 MixxxKeyboard* keyboard);
1737
1738- QAbstractItemModel* getChildModel();
1739+ TreeItemModel* getChildModel();
1740
1741 public slots:
1742 void activate();
1743@@ -76,7 +78,7 @@
1744 const static QString m_sFeaturedArtistsViewName;
1745 const static QString m_sBundledSongsViewName;
1746 const static QString m_sMyDownloadsViewName;
1747- QStringListModel m_childModel;
1748+ TreeItemModel m_childModel;
1749 CrateTableModel m_downloadsTableModel;
1750 SongDownloader* m_pSongDownloader;
1751 bool m_bFirstRun;
1752
1753=== modified file 'mixxx/src/library/rhythmboxfeature.cpp'
1754--- mixxx/src/library/rhythmboxfeature.cpp 2010-08-08 05:08:23 +0000
1755+++ mixxx/src/library/rhythmboxfeature.cpp 2011-01-06 15:28:44 +0000
1756@@ -6,6 +6,7 @@
1757 #include "library/rhythmboxtrackmodel.h"
1758 #include "library/rhythmboxplaylistmodel.h"
1759 #include "library/rhythmboxfeature.h"
1760+#include "treeitem.h"
1761
1762 RhythmboxFeature::RhythmboxFeature(QObject* parent)
1763 : LibraryFeature(parent) {
1764@@ -34,7 +35,7 @@
1765 return QIcon(":/images/library/ic_library_rhythmbox.png");
1766 }
1767
1768-QAbstractItemModel* RhythmboxFeature::getChildModel() {
1769+TreeItemModel* RhythmboxFeature::getChildModel() {
1770 return &m_childModel;
1771 }
1772
1773@@ -62,11 +63,14 @@
1774 m_pPlaylistModelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
1775 m_pPlaylistModelProxy->setSortCaseSensitivity(Qt::CaseInsensitive);
1776
1777- QStringList list;
1778+ TreeItem *rootItem = new TreeItem("$root","$root", this);
1779 for (int i = 0; i < m_pRhythmboxPlaylistModel->numPlaylists(); ++i) {
1780- list << m_pRhythmboxPlaylistModel->playlistTitle(i);
1781+ QString playlist_name = m_pRhythmboxPlaylistModel->playlistTitle(i);
1782+ TreeItem *item = new TreeItem(playlist_name, playlist_name, this,rootItem);
1783+
1784+ rootItem->appendChild(item);
1785 }
1786- m_childModel.setStringList(list);
1787+ m_childModel.setRootItem(rootItem);
1788 }
1789 emit(showTrackModel(m_pTrackModelProxy));
1790 }
1791
1792=== modified file 'mixxx/src/library/rhythmboxfeature.h'
1793--- mixxx/src/library/rhythmboxfeature.h 2009-12-02 19:24:04 +0000
1794+++ mixxx/src/library/rhythmboxfeature.h 2011-01-06 15:28:44 +0000
1795@@ -7,6 +7,7 @@
1796 #include <QStringListModel>
1797
1798 #include "library/libraryfeature.h"
1799+#include "treeitemmodel.h"
1800
1801 class RhythmboxPlaylistModel;
1802 class RhythmboxTrackModel;
1803@@ -27,7 +28,7 @@
1804 bool dragMoveAccept(QUrl url);
1805 bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
1806
1807- QAbstractItemModel* getChildModel();
1808+ TreeItemModel* getChildModel();
1809
1810 public slots:
1811 void activate();
1812@@ -40,7 +41,7 @@
1813 RhythmboxPlaylistModel* m_pRhythmboxPlaylistModel;
1814 ProxyTrackModel* m_pTrackModelProxy;
1815 ProxyTrackModel* m_pPlaylistModelProxy;
1816- QStringListModel m_childModel;
1817+ TreeItemModel m_childModel;
1818 };
1819
1820 #endif /* RHYTHMBOXFEATURE_H */
1821
1822=== modified file 'mixxx/src/library/sidebarmodel.cpp'
1823--- mixxx/src/library/sidebarmodel.cpp 2010-09-14 19:05:04 +0000
1824+++ mixxx/src/library/sidebarmodel.cpp 2011-01-06 15:28:44 +0000
1825@@ -4,6 +4,7 @@
1826
1827 #include "library/libraryfeature.h"
1828 #include "library/sidebarmodel.h"
1829+#include "library/treeitem.h"
1830
1831 SidebarModel::SidebarModel(QObject* parent)
1832 : QAbstractItemModel(parent),
1833@@ -23,6 +24,7 @@
1834 this, SLOT(slotModelReset()));
1835 connect(model, SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)),
1836 this, SLOT(slotDataChanged(const QModelIndex&,const QModelIndex&)));
1837+
1838 connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)),
1839 this, SLOT(slotRowsAboutToBeInserted(const QModelIndex&, int, int)));
1840 connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),
1841@@ -31,6 +33,7 @@
1842 this, SLOT(slotRowsInserted(const QModelIndex&, int, int)));
1843 connect(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int)),
1844 this, SLOT(slotRowsRemoved(const QModelIndex&, int, int)));
1845+
1846 }
1847
1848 QModelIndex SidebarModel::getDefaultSelection() {
1849@@ -63,53 +66,84 @@
1850 QModelIndex SidebarModel::index(int row, int column,
1851 const QModelIndex& parent) const {
1852 // qDebug() << "SidebarModel::index row=" << row
1853- // << "column=" << column << "parent=" << parent;
1854+ // << "column=" << column << "parent=" << parent.data();
1855 if (parent.isValid()) {
1856- const QAbstractItemModel* childModel;
1857+ /* If we have selected the root of a library feature at position 'row'
1858+ * its internal pointer is the current sidebar object model
1859+ * we return its associated childmodel
1860+ */
1861 if (parent.internalPointer() == this) {
1862- childModel = m_sFeatures[parent.row()]->getChildModel();
1863- } else {
1864- childModel = (QAbstractItemModel*)parent.internalPointer();
1865- }
1866- QModelIndex childIndex = childModel->index(row, column);
1867-
1868- if (childIndex.isValid()) {
1869- return createIndex(childIndex.row(), childIndex.column(),
1870- (void*)childModel);
1871- } else {
1872- return QModelIndex();
1873+ const QAbstractItemModel* childModel = m_sFeatures[parent.row()]->getChildModel();
1874+ QModelIndex childIndex = childModel->index(row, column);
1875+ TreeItem* tree_item = (TreeItem*)childIndex.internalPointer();
1876+ if (childIndex.isValid())
1877+ {
1878+
1879+ return createIndex(childIndex.row(), childIndex.column(), (void*)tree_item);
1880+ }
1881+ else
1882+ {
1883+
1884+ return QModelIndex();
1885+ }
1886+ }
1887+ else
1888+ {
1889+ // We have selected an item within the childmodel
1890+ // This item has always an internal pointer of (sub)type TreeItem
1891+ TreeItem* tree_item = (TreeItem*)parent.internalPointer();
1892+ return createIndex(row, column, (void*) tree_item->child(row));
1893 }
1894 }
1895 return createIndex(row, column, (void*)this);
1896 }
1897
1898 QModelIndex SidebarModel::parent(const QModelIndex& index) const {
1899- //qDebug() << "SidebarModel::parent index=" << index;
1900+ //qDebug() << "SidebarModel::parent index=" << index.data();
1901 if (index.isValid()) {
1902+ /* If we have selected the root of a library feature
1903+ * its internal pointer is the current sidebar object model
1904+ * A root library feature has no parent and thus we return
1905+ * an invalid QModelIndex
1906+ */
1907 if (index.internalPointer() == this) {
1908- // Top level, no parent
1909+
1910 return QModelIndex();
1911- } else {
1912- const QAbstractItemModel* childModel = (QAbstractItemModel*)index.internalPointer();
1913-
1914- for (int i = 0; i < m_sFeatures.size(); ++i) {
1915- if (childModel == m_sFeatures[i]->getChildModel()) {
1916- return createIndex(i, 0, (void*)this);
1917+ }
1918+ else
1919+ {
1920+ TreeItem* tree_item = (TreeItem*)index.internalPointer();
1921+ //if we have selected an item at the first level of a childnode
1922+ if(tree_item->parent()->data() == "$root"){
1923+ //qDebug() << "Parent== root";
1924+ LibraryFeature* feature = tree_item->getFeature();
1925+ for (int i = 0; i < m_sFeatures.size(); ++i)
1926+ {
1927+ if (feature == m_sFeatures[i]) {
1928+ //create a ModelIndex for parent 'this' having a library feature at position 'i'
1929+ return createIndex(i, 0, (void*)this);
1930+ }
1931 }
1932 }
1933+ //if we have selected an item at some deeper level of a childnode
1934+ return createIndex(tree_item->parent()->row(), 0 , tree_item->parent());
1935 }
1936 }
1937 return QModelIndex();
1938 }
1939
1940 int SidebarModel::rowCount(const QModelIndex& parent) const {
1941- //qDebug() << "SidebarModel::rowCount parent=" << parent;
1942+ //qDebug() << "SidebarModel::rowCount parent=" << parent.data();
1943 if (parent.isValid()) {
1944 if (parent.internalPointer() == this) {
1945+
1946 return m_sFeatures[parent.row()]->getChildModel()->rowCount();
1947- } else {
1948- // We don't support feature models any deeper than 1 level.
1949- return 0;
1950+ }
1951+ else {
1952+ //We support tree models deeper than 1 level
1953+ TreeItem* tree_item = (TreeItem*)parent.internalPointer();
1954+
1955+ return tree_item->childCount();
1956 }
1957 }
1958 return m_sFeatures.size();
1959@@ -125,22 +159,28 @@
1960 // qDebug("SidebarModel::data row=%d column=%d pointer=%8x, role=%d",
1961 // index.row(), index.column(), index.internalPointer(), role);
1962 if (index.isValid()) {
1963- if (this == index.internalPointer()) {
1964+ if (index.internalPointer() == this)
1965+ {
1966 if (role == Qt::DisplayRole) {
1967 return m_sFeatures[index.row()]->title();
1968- } else if (role == Qt::DecorationRole) {
1969+ }
1970+ else if (role == Qt::DecorationRole) {
1971 return m_sFeatures[index.row()]->getIcon();
1972 }
1973- } else {
1974- QAbstractItemModel* childModel = (QAbstractItemModel*)index.internalPointer();
1975- return childModel->data(childModel->index(index.row(), index.column()), role);
1976+ }
1977+ else {
1978+ if (role == Qt::DisplayRole) {
1979+ TreeItem* tree_item = (TreeItem*)index.internalPointer();
1980+ return tree_item->data();
1981+ }
1982+
1983 }
1984 }
1985 return QVariant();
1986 }
1987
1988 void SidebarModel::clicked(const QModelIndex& index) {
1989- qDebug() << "SidebarModel::clicked() index=" << index;
1990+ //qDebug() << "SidebarModel::clicked() index=" << index;
1991
1992
1993 //We use clicked() for keyboard and mouse control, and the
1994@@ -152,33 +192,32 @@
1995 if (index.isValid()) {
1996 if (index.internalPointer() == this) {
1997 m_sFeatures[index.row()]->activate();
1998- } else {
1999- QAbstractItemModel* childModel = (QAbstractItemModel*)index.internalPointer();
2000- QModelIndex childIndex = childModel->index(index.row(), index.column());
2001- for (int i = 0; i < m_sFeatures.size(); ++i) {
2002- if (m_sFeatures[i]->getChildModel() == childModel) {
2003- m_sFeatures[i]->activateChild(childIndex);
2004- }
2005- }
2006+ }
2007+ else
2008+ {
2009+ TreeItem* tree_item = (TreeItem*)index.internalPointer();
2010+ LibraryFeature* feature = tree_item->getFeature();
2011+ feature->activateChild(index);
2012 }
2013 }
2014 }
2015
2016 void SidebarModel::rightClicked(const QPoint& globalPos, const QModelIndex& index) {
2017- qDebug() << "SidebarModel::rightClicked() index=" << index;
2018- if (index.isValid()) {
2019- if (index.internalPointer() == this) {
2020+ //qDebug() << "SidebarModel::rightClicked() index=" << index;
2021+ if (index.isValid())
2022+ {
2023+ if (index.internalPointer() == this)
2024+ {
2025 m_sFeatures[index.row()]->activate();
2026 m_sFeatures[index.row()]->onRightClick(globalPos);
2027- } else {
2028- QAbstractItemModel* childModel = (QAbstractItemModel*)index.internalPointer();
2029- QModelIndex childIndex = childModel->index(index.row(), index.column());
2030- for (int i = 0; i < m_sFeatures.size(); ++i) {
2031- if (m_sFeatures[i]->getChildModel() == childModel) {
2032- m_sFeatures[i]->activateChild(childIndex);
2033- m_sFeatures[i]->onRightClickChild(globalPos, childIndex);
2034- }
2035- }
2036+ }
2037+ else
2038+ {
2039+ TreeItem* tree_item = (TreeItem*)index.internalPointer();
2040+ LibraryFeature* feature = tree_item->getFeature();
2041+ feature->activateChild(index);
2042+ feature->onRightClickChild(globalPos, index);
2043+
2044 }
2045 }
2046 }
2047@@ -187,16 +226,16 @@
2048 {
2049 //qDebug() << "SidebarModel::dropAccept() index=" << index << url;
2050 if (index.isValid()) {
2051- if (index.internalPointer() == this) {
2052+ if (index.internalPointer() == this)
2053+ {
2054 return m_sFeatures[index.row()]->dropAccept(url);
2055- } else {
2056- QAbstractItemModel* childModel = (QAbstractItemModel*)index.internalPointer();
2057- QModelIndex childIndex = childModel->index(index.row(), index.column());
2058- for (int i = 0; i < m_sFeatures.size(); ++i) {
2059- if (m_sFeatures[i]->getChildModel() == childModel) {
2060- return m_sFeatures[i]->dropAcceptChild(childIndex, url);
2061- }
2062- }
2063+ }
2064+ else
2065+ {
2066+ TreeItem* tree_item = (TreeItem*)index.internalPointer();
2067+ LibraryFeature* feature = tree_item->getFeature();
2068+ return feature->dropAcceptChild(index, url);
2069+
2070 }
2071 }
2072
2073@@ -207,16 +246,16 @@
2074 {
2075 //qDebug() << "SidebarModel::dragMoveAccept() index=" << index << url;
2076 if (index.isValid()) {
2077- if (index.internalPointer() == this) {
2078+ if (index.internalPointer() == this)
2079+ {
2080 return m_sFeatures[index.row()]->dragMoveAccept(url);
2081- } else {
2082- QAbstractItemModel* childModel = (QAbstractItemModel*)index.internalPointer();
2083- QModelIndex childIndex = childModel->index(index.row(), index.column());
2084- for (int i = 0; i < m_sFeatures.size(); ++i) {
2085- if (m_sFeatures[i]->getChildModel() == childModel) {
2086- return m_sFeatures[i]->dragMoveAcceptChild(childIndex, url);
2087- }
2088- }
2089+ }
2090+ else
2091+ {
2092+
2093+ TreeItem* tree_item = (TreeItem*)index.internalPointer();
2094+ LibraryFeature* feature = tree_item->getFeature();
2095+ return feature->dragMoveAcceptChild(index, url);
2096 }
2097 }
2098 return false;
2099@@ -245,18 +284,20 @@
2100
2101 void SidebarModel::slotRowsAboutToBeInserted(const QModelIndex& parent, int start, int end) {
2102 //qDebug() << "slotRowsABoutToBeInserted" << parent << start << end;
2103- QModelIndex newParent = translateSourceIndex(parent);
2104+
2105+ QModelIndex newParent = translateSourceIndex(parent);
2106 beginInsertRows(newParent, start, end);
2107 }
2108
2109 void SidebarModel::slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) {
2110 //qDebug() << "slotRowsABoutToBeRemoved" << parent << start << end;
2111+
2112 QModelIndex newParent = translateSourceIndex(parent);
2113 beginRemoveRows(newParent, start, end);
2114 }
2115
2116 void SidebarModel::slotRowsInserted(const QModelIndex& parent, int start, int end) {
2117- //qDebug() << "slotRowsInserted" << parent << start << end;
2118+ // qDebug() << "slotRowsInserted" << parent << start << end;
2119 //QModelIndex newParent = translateSourceIndex(parent);
2120 endInsertRows();
2121 }
2122
2123=== modified file 'mixxx/src/library/trackcollection.cpp'
2124--- mixxx/src/library/trackcollection.cpp 2010-11-21 04:39:36 +0000
2125+++ mixxx/src/library/trackcollection.cpp 2011-01-06 15:28:44 +0000
2126@@ -68,7 +68,7 @@
2127 return false;
2128 }
2129
2130- int requiredSchemaVersion = 8;
2131+ int requiredSchemaVersion = 9;
2132 if (!SchemaManager::upgradeToSchemaVersion(m_pConfig, m_db,
2133 requiredSchemaVersion)) {
2134 QMessageBox::warning(0, tr("Cannot upgrade database schema"),
2135
2136=== added file 'mixxx/src/library/traktorfeature.cpp'
2137--- mixxx/src/library/traktorfeature.cpp 1970-01-01 00:00:00 +0000
2138+++ mixxx/src/library/traktorfeature.cpp 2011-01-06 15:28:44 +0000
2139@@ -0,0 +1,571 @@
2140+// traktorfeature.cpp
2141+// Created 9/26/2010 by Tobias Rafreider
2142+
2143+#include <QtDebug>
2144+#include <QMessageBox>
2145+#include <QXmlStreamReader>
2146+#include <QMap>
2147+#include <QSettings>
2148+
2149+#include "library/traktorfeature.h"
2150+
2151+#include "library/librarytablemodel.h"
2152+#include "library/missingtablemodel.h"
2153+#include "library/proxytrackmodel.h"
2154+#include "library/trackcollection.h"
2155+#include "treeitem.h"
2156+
2157+
2158+TraktorFeature::TraktorFeature(QObject* parent, TrackCollection* pTrackCollection):
2159+ m_pTrackCollection(pTrackCollection),
2160+ m_database(m_pTrackCollection->getDatabase())
2161+{
2162+
2163+ m_isActivated = false;
2164+ m_pTraktorTableModel = new TraktorTableModel(this, m_pTrackCollection);
2165+ m_pTraktorPlaylistModel = new TraktorPlaylistModel(this, m_pTrackCollection);
2166+
2167+}
2168+
2169+TraktorFeature::~TraktorFeature() {
2170+ if(m_pTraktorTableModel)
2171+ delete m_pTraktorTableModel;
2172+ if(m_pTraktorPlaylistModel)
2173+ delete m_pTraktorPlaylistModel;
2174+}
2175+
2176+QVariant TraktorFeature::title() {
2177+ return tr("Traktor");
2178+}
2179+
2180+QIcon TraktorFeature::getIcon() {
2181+ return QIcon(":/images/library/ic_library_traktor.png");
2182+}
2183+bool TraktorFeature::isSupported() {
2184+ return (QFile::exists(getTraktorMusicDatabase()));
2185+
2186+}
2187+TreeItemModel* TraktorFeature::getChildModel() {
2188+ return &m_childModel;
2189+}
2190+
2191+void TraktorFeature::refreshLibraryModels()
2192+{
2193+
2194+}
2195+
2196+void TraktorFeature::activate() {
2197+ qDebug() << "TraktorFeature::activate()";
2198+
2199+ if(!m_isActivated){
2200+ if (QMessageBox::question(
2201+ NULL,
2202+ tr("Load Traktor Library?"),
2203+ tr("Would you like to load your Traktor library?"),
2204+ QMessageBox::Ok,
2205+ QMessageBox::Cancel)
2206+ == QMessageBox::Cancel) {
2207+ return;
2208+ }
2209+ if(importLibrary(getTraktorMusicDatabase()))
2210+ m_isActivated = true;
2211+ }
2212+ emit(showTrackModel(m_pTraktorTableModel));
2213+
2214+}
2215+
2216+void TraktorFeature::activateChild(const QModelIndex& index) {
2217+
2218+ if(!index.isValid()) return;
2219+
2220+ //access underlying TreeItem object
2221+ TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
2222+
2223+ if(item->isPlaylist()){
2224+ qDebug() << "Activate Traktor Playlist: " << item->dataPath().toString();
2225+ m_pTraktorPlaylistModel->setPlaylist(item->dataPath().toString());
2226+ emit(showTrackModel(m_pTraktorPlaylistModel));
2227+
2228+ }
2229+}
2230+
2231+void TraktorFeature::onRightClick(const QPoint& globalPos) {
2232+}
2233+
2234+void TraktorFeature::onRightClickChild(const QPoint& globalPos,
2235+ QModelIndex index) {
2236+}
2237+
2238+bool TraktorFeature::dropAccept(QUrl url) {
2239+ return false;
2240+}
2241+
2242+bool TraktorFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {
2243+ return false;
2244+}
2245+
2246+bool TraktorFeature::dragMoveAccept(QUrl url) {
2247+ return false;
2248+}
2249+
2250+bool TraktorFeature::dragMoveAcceptChild(const QModelIndex& index,
2251+ QUrl url) {
2252+ return false;
2253+}
2254+bool TraktorFeature::importLibrary(QString file){
2255+
2256+ //Delete all table entries of Traktor feature
2257+ m_database.transaction();
2258+ clearTable("traktor_playlist_tracks");
2259+ clearTable("traktor_library");
2260+ clearTable("traktor_playlists");
2261+ m_database.commit();
2262+
2263+ m_database.transaction();
2264+ QSqlQuery query(m_database);
2265+ query.prepare("INSERT INTO traktor_library (artist, title, album, year, genre,comment,"
2266+ "tracknumber,"
2267+ "bpm, bitrate,"
2268+ "duration, location,"
2269+ "rating,"
2270+ "key) "
2271+ "VALUES (:artist, :title, :album, :year, :genre,:comment, :tracknumber,"
2272+ ":bpm, :bitrate,"
2273+ ":duration, :location,"
2274+ ":rating,"
2275+ ":key)");
2276+
2277+ //Parse Trakor XML file using SAX (for performance)
2278+ QFile traktor_file(file);
2279+ if (!traktor_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
2280+ qDebug() << "Cannot open Traktor music collection";
2281+ return false;
2282+ }
2283+ QXmlStreamReader xml(&traktor_file);
2284+ bool inCollectionTag = false;
2285+ bool inEntryTag = false;
2286+ bool inPlaylistsTag = false;
2287+ bool isRootFolderParsed = false;
2288+ int nAudioFiles = 0;
2289+
2290+ while (!xml.atEnd())
2291+ {
2292+ xml.readNext();
2293+ if(xml.isStartElement())
2294+ {
2295+ if(xml.name() == "COLLECTION")
2296+ {
2297+ inCollectionTag = true;
2298+
2299+ }
2300+ /*
2301+ * Each "ENTRY" tag in <COLLECTION> represents a track
2302+ */
2303+ if(inCollectionTag && xml.name() == "ENTRY" )
2304+ {
2305+ inEntryTag = true;
2306+ //parse track
2307+ parseTrack(xml, query);
2308+
2309+ ++nAudioFiles; //increment number of files in the music collection
2310+ }
2311+ if(xml.name() == "PLAYLISTS")
2312+ {
2313+ inPlaylistsTag = true;
2314+
2315+ }
2316+ if(inPlaylistsTag && !isRootFolderParsed && xml.name() == "NODE"){
2317+ QXmlStreamAttributes attr = xml.attributes();
2318+ QString nodetype = attr.value("TYPE").toString();
2319+ QString name = attr.value("NAME").toString();
2320+
2321+ if(nodetype == "FOLDER" && name == "$ROOT"){
2322+ //process all playlists
2323+ parsePlaylists(xml);
2324+ isRootFolderParsed = true;
2325+ }
2326+ }
2327+
2328+ }
2329+ if(xml.isEndElement())
2330+ {
2331+ if(xml.name() == "COLLECTION")
2332+ {
2333+ inCollectionTag = false;
2334+
2335+ }
2336+ if(xml.name() == "ENTRY" && inCollectionTag)
2337+ {
2338+ inEntryTag = false;
2339+
2340+ }
2341+ if(xml.name() == "PLAYLISTS" && inPlaylistsTag)
2342+ {
2343+ inPlaylistsTag = false;
2344+ }
2345+
2346+ }
2347+ }
2348+ if (xml.hasError()) {
2349+ // do error handling
2350+ qDebug() << "Cannot process Traktor music collection";
2351+ return false;
2352+ }
2353+
2354+ qDebug() << "Found: " << nAudioFiles << " audio files in Traktor";
2355+ //initialize TraktorTableModel
2356+ m_database.commit();
2357+ m_pTraktorTableModel->select();
2358+ return true;
2359+
2360+}
2361+void TraktorFeature::parseTrack(QXmlStreamReader &xml, QSqlQuery &query){
2362+ QString title;
2363+ QString artist;
2364+ QString album;
2365+ QString year;
2366+ QString genre;
2367+ //drive letter
2368+ QString volume;
2369+ QString path;
2370+ QString filename;
2371+ QString location;
2372+ float bpm = 0.0;
2373+ int bitrate = 0;
2374+ QString key;
2375+ //duration of a track
2376+ int playtime = 0;
2377+ int rating = 0;
2378+ QString comment;
2379+ QString tracknumber;
2380+
2381+ //get XML attributes of starting ENTRY tag
2382+ QXmlStreamAttributes attr = xml.attributes ();
2383+ title = attr.value("TITLE").toString();
2384+ artist = attr.value("ARTIST").toString();
2385+
2386+ //read all sub tags of ENTRY until we reach the closing ENTRY tag
2387+ while(!xml.atEnd())
2388+ {
2389+ xml.readNext();
2390+ if(xml.isStartElement()){
2391+ if(xml.name() == "ALBUM")
2392+ {
2393+ QXmlStreamAttributes attr = xml.attributes ();
2394+ album = attr.value("TITLE").toString();
2395+ tracknumber = attr.value("TRACK").toString();
2396+
2397+ continue;
2398+ }
2399+ if(xml.name() == "LOCATION")
2400+ {
2401+ QXmlStreamAttributes attr = xml.attributes ();
2402+ volume = attr.value("VOLUME").toString();
2403+ path = attr.value("DIR").toString();
2404+ filename = attr.value("FILE").toString();
2405+ /*compute the location, i.e, combining all the values
2406+ * On Windows the volume holds the drive letter e.g., d:
2407+ * On OS X, the volume is supposed to be "Macintosh HD" at all times,
2408+ * which is a folder in /Volumes/
2409+ */
2410+ #if defined(__APPLE__)
2411+ location = "/Volumes/"+volume;
2412+ #else
2413+ location = volume;
2414+ #endif
2415+ location += path.replace(QString(":"), QString(""));
2416+ location += filename;
2417+ continue;
2418+ }
2419+ if(xml.name() == "INFO")
2420+ {
2421+ QXmlStreamAttributes attr = xml.attributes();
2422+ key = attr.value("KEY").toString();
2423+ bitrate = attr.value("BITRATE").toString().toInt() / 1000;
2424+ playtime = attr.value("PLAYTIME").toString().toInt();
2425+ genre = attr.value("GENRE").toString();
2426+ year = attr.value("RELEASE_DATE").toString();
2427+ comment = attr.value("COMMENT").toString();
2428+ QString ranking_str = attr.value("RANKING").toString();
2429+ /* A ranking in Traktor has ranges between 0 and 255 internally.
2430+ * This is same as the POPULARIMETER tag in IDv2, see http://help.mp3tag.de/main_tags.html
2431+ *
2432+ * Our rating values range from 1 to 5. The mapping is defined as follow
2433+ * ourRatingValue = TraktorRating / 51
2434+ */
2435+ if(ranking_str != "" && qVariantCanConvert<int>(ranking_str)){
2436+ rating = ranking_str.toInt()/51;
2437+ }
2438+ continue;
2439+ }
2440+ if(xml.name() == "TEMPO")
2441+ {
2442+ QXmlStreamAttributes attr = xml.attributes ();
2443+ bpm = attr.value("BPM").toString().toFloat();
2444+ continue;
2445+ }
2446+ }
2447+ //We leave the infinte loop, if twe have the closing tag "ENTRY"
2448+ if(xml.name() == "ENTRY" && xml.isEndElement()){
2449+ break;
2450+ }
2451+ }
2452+
2453+ /* If we reach the end of ENTRY within the COLLECTION tag
2454+ * Save parsed track to database
2455+ */
2456+ query.bindValue(":artist", artist);
2457+ query.bindValue(":title", title);
2458+ query.bindValue(":album", album);
2459+ query.bindValue(":genre", genre);
2460+ query.bindValue(":year", year);
2461+ query.bindValue(":duration", playtime);
2462+ query.bindValue(":location", location);
2463+ query.bindValue(":rating", rating);
2464+ query.bindValue(":comment", comment);
2465+ query.bindValue(":tracknumber", tracknumber);
2466+ query.bindValue(":key", key);
2467+ query.bindValue(":bpm", bpm);
2468+ query.bindValue(":bitrate", bitrate);
2469+
2470+
2471+ bool success = query.exec();
2472+ if(!success){
2473+ qDebug() << "SQL Error in TraktorTableModel.cpp: line" << __LINE__ << " " << query.lastError();
2474+ return;
2475+ }
2476+
2477+}
2478+/*
2479+ * Purpose: Parsing all the folder and playlists of Traktor
2480+ * This is a complex operation since Traktor uses the concept of folders and playlist.
2481+ * A folder can contain folders and playlists. A playlist contains entries but no folders.
2482+ * In other words, Traktor uses a tree structure to organize music. Inner nodes represent folders while
2483+ * leaves are playlists.
2484+ */
2485+void TraktorFeature::parsePlaylists(QXmlStreamReader &xml){
2486+
2487+ qDebug() << "Process RootFolder";
2488+ //Each playlist is unique and can be identified by a path in the tree structure.
2489+ QString current_path = "";
2490+ QMap<QString,QString> map;
2491+
2492+ QString delimiter = "-->";
2493+
2494+ TreeItem *rootItem = new TreeItem("$root","$root", this);
2495+ TreeItem * parent = rootItem;
2496+
2497+ bool inPlaylistTag = false;
2498+
2499+ QSqlQuery query_insert_to_playlists(m_database);
2500+ query_insert_to_playlists.prepare("INSERT INTO traktor_playlists (name) "
2501+ "VALUES (:name)");
2502+
2503+ QSqlQuery query_insert_to_playlist_tracks(m_database);
2504+ query_insert_to_playlist_tracks.prepare("INSERT INTO traktor_playlist_tracks (playlist_id, track_id) "
2505+ "VALUES (:playlist_id, :track_id)");
2506+
2507+ while(!xml.atEnd())
2508+ {
2509+ //read next XML element
2510+ xml.readNext();
2511+
2512+ if(xml.isStartElement())
2513+ {
2514+ if(xml.name() == "NODE"){
2515+ QXmlStreamAttributes attr = xml.attributes();
2516+ QString name = attr.value("NAME").toString();
2517+ QString type = attr.value("TYPE").toString();
2518+
2519+ //TODO: What happens if the folder node is a leaf (empty folder)
2520+ // Idea: Hide empty folders :-)
2521+ if(type == "FOLDER")
2522+ {
2523+
2524+ current_path += delimiter;
2525+ current_path += name;
2526+ //qDebug() << "Folder: " +current_path << " has parent " << parent->data().toString();
2527+ map.insert(current_path, "FOLDER");
2528+
2529+ TreeItem * item = new TreeItem(name,current_path, this, parent);
2530+ parent->appendChild(item);
2531+ parent = item;
2532+ }
2533+ if(type == "PLAYLIST")
2534+ {
2535+ current_path += delimiter;
2536+ current_path += name;
2537+ //qDebug() << "Playlist: " +current_path << " has parent " << parent->data().toString();
2538+ map.insert(current_path, "PLAYLIST");
2539+
2540+ TreeItem * item = new TreeItem(name,current_path, this, parent);
2541+ parent->appendChild(item);
2542+ // process all the entries within the playlist 'name' having path 'current_path'
2543+ parsePlaylistEntries(xml,current_path, query_insert_to_playlists, query_insert_to_playlist_tracks);
2544+
2545+ }
2546+
2547+ }
2548+ if(xml.name() == "ENTRY" && inPlaylistTag){
2549+
2550+
2551+ }
2552+
2553+ }
2554+
2555+ if(xml.isEndElement())
2556+ {
2557+ if(xml.name() == "NODE")
2558+ {
2559+ if(map.value(current_path) == "FOLDER"){
2560+ parent = parent->parent();
2561+ }
2562+
2563+ //Whenever we find a closing NODE, remove the last component of the path
2564+ int lastSlash = current_path.lastIndexOf(delimiter);
2565+ int path_length = current_path.size();
2566+
2567+ current_path.remove(lastSlash, path_length - lastSlash);
2568+
2569+
2570+
2571+ }
2572+ if(xml.name() == "PLAYLIST")
2573+ {
2574+ inPlaylistTag = false;
2575+ }
2576+ //We leave the infinte loop, if twe have the closing "PLAYLIST" tag
2577+ if(xml.name() == "PLAYLISTS")
2578+ {
2579+ break;
2580+ }
2581+ }
2582+
2583+ }
2584+
2585+
2586+ m_childModel.setRootItem(rootItem);
2587+
2588+
2589+}
2590+void TraktorFeature::parsePlaylistEntries(QXmlStreamReader &xml,QString playlist_path, QSqlQuery query_insert_into_playlist, QSqlQuery query_insert_into_playlisttracks)
2591+{
2592+ // In the database, the name of a playlist is specified by the unique path, e.g., /someFolderA/someFolderB/playlistA"
2593+ query_insert_into_playlist.bindValue(":name", playlist_path);
2594+ bool success = query_insert_into_playlist.exec();
2595+ if(!success){
2596+ qDebug() << "SQL Error in TraktorTableModel.cpp: line" << __LINE__ << " " << query_insert_into_playlist.lastError();
2597+ return;
2598+ }
2599+ //Get playlist id
2600+ QSqlQuery id_query(m_database);
2601+ id_query.prepare("select id from traktor_playlists where name=:path");
2602+ id_query.bindValue(":path", playlist_path);
2603+ success = id_query.exec();
2604+
2605+ int playlist_id = -1;
2606+ if(success){
2607+ //playlist_id = id_query.lastInsertId().toInt();
2608+ while (id_query.next()) {
2609+ playlist_id = id_query.value(id_query.record().indexOf("id")).toInt();
2610+ }
2611+ }
2612+ else
2613+ qDebug() << "SQL Error in TraktorTableModel.cpp: line" << __LINE__ << " " << id_query.lastError();
2614+
2615+
2616+
2617+ while(!xml.atEnd())
2618+ {
2619+ //read next XML element
2620+ xml.readNext();
2621+ if(xml.isStartElement())
2622+ {
2623+ if(xml.name() == "PRIMARYKEY"){
2624+ QXmlStreamAttributes attr = xml.attributes();
2625+ QString key = attr.value("KEY").toString();
2626+ QString type = attr.value("TYPE").toString();
2627+ if(type == "TRACK")
2628+ {
2629+ key.replace(QString(":"), QString(""));
2630+ //TODO: IFDEF
2631+ #if defined(__WINDOWS__)
2632+ key.insert(1,":");
2633+ #else
2634+ key.prepend("/Volumes/");
2635+ #endif
2636+
2637+ //insert to database
2638+ int track_id = -1;
2639+ QSqlQuery finder_query(m_database);
2640+ finder_query.prepare("select id from traktor_library where location=:path");
2641+ finder_query.bindValue(":path", key);
2642+ success = finder_query.exec();
2643+
2644+
2645+ if(success){
2646+ while (finder_query.next()) {
2647+ track_id = finder_query.value(finder_query.record().indexOf("id")).toInt();
2648+ }
2649+ }
2650+ else
2651+ qDebug() << "SQL Error in TraktorTableModel.cpp: line" << __LINE__ << " " << finder_query.lastError();
2652+
2653+ query_insert_into_playlisttracks.bindValue(":playlist_id", playlist_id);
2654+ query_insert_into_playlisttracks.bindValue(":track_id", track_id);
2655+ success = query_insert_into_playlisttracks.exec();
2656+
2657+
2658+ if(!success){
2659+ qDebug() << "SQL Error in TraktorFeature.cpp: line" << __LINE__ << " " << query_insert_into_playlisttracks.lastError();
2660+ qDebug() << "trackid" << track_id << " with path " << key;
2661+ qDebug() << "playlistname; " << playlist_path <<" with ID " << playlist_id;
2662+ qDebug() << "-----------------";
2663+
2664+ }
2665+
2666+ }
2667+
2668+ }
2669+ }
2670+ if(xml.isEndElement()){
2671+ //We leave the infinte loop, if twe have the closing "PLAYLIST" tag
2672+ if(xml.name() == "PLAYLIST")
2673+ {
2674+ break;
2675+ }
2676+ }
2677+ }
2678+
2679+}
2680+void TraktorFeature::clearTable(QString table_name)
2681+{
2682+ QSqlQuery query(m_database);
2683+ query.prepare("delete from "+table_name);
2684+ bool success = query.exec();
2685+
2686+ if(!success)
2687+ qDebug() << "Could not delete remove old entries from table " << table_name << " : " << query.lastError();
2688+ else
2689+ qDebug() << "Traktor table entries of '" << table_name <<"' have been cleared.";
2690+}
2691+QString TraktorFeature::getTraktorMusicDatabase()
2692+{
2693+ QString musicFolder;
2694+#if defined(__APPLE__)
2695+ musicFolder = QDir::homePath() +"/Documents/Native Instruments/Traktor/collection.nml";
2696+#elif defined(__WINDOWS__)
2697+ QSettings settings("HKEY_CURRENT_USER\\Software\\Native Instruments\\Traktor Pro", QSettings::NativeFormat);
2698+ // if the value method fails it returns QTDir::homePath
2699+ musicFolder = settings.value("RootDirectory", QDir::homePath()).toString();
2700+ musicFolder += "collection.nml";
2701+#elif defined(__LINUX__)
2702+ musicFolder = QDir::homePath() + "/collection.nml";
2703+#else
2704+ musicFolder = "";
2705+#endif
2706+ qDebug() << "Traktor Library Location=[" << musicFolder << "]";
2707+ return musicFolder;
2708+
2709+
2710+}
2711
2712=== added file 'mixxx/src/library/traktorfeature.h'
2713--- mixxx/src/library/traktorfeature.h 1970-01-01 00:00:00 +0000
2714+++ mixxx/src/library/traktorfeature.h 2011-01-06 15:28:44 +0000
2715@@ -0,0 +1,68 @@
2716+// traktorfeature.h
2717+// Created 9/26/2010 by Tobias Rafreider
2718+
2719+#ifndef TRAKTOR_FEATURE_H
2720+#define TRAKTOR_FEATURE_H
2721+
2722+#include <QStringListModel>
2723+#include <QtSql>
2724+#include <QXmlStreamReader>
2725+
2726+#include "library/libraryfeature.h"
2727+#include "library/traktortablemodel.h"
2728+#include "library/traktorplaylistmodel.h"
2729+#include "treeitemmodel.h"
2730+
2731+class LibraryTableModel;
2732+class MissingTableModel;
2733+class TrackCollection;
2734+
2735+class TraktorFeature : public LibraryFeature {
2736+ Q_OBJECT
2737+ public:
2738+ TraktorFeature(QObject* parent, TrackCollection*);
2739+ virtual ~TraktorFeature();
2740+
2741+ QVariant title();
2742+ QIcon getIcon();
2743+ static bool isSupported();
2744+ bool dropAccept(QUrl url);
2745+ bool dropAcceptChild(const QModelIndex& index, QUrl url);
2746+ bool dragMoveAccept(QUrl url);
2747+ bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
2748+
2749+
2750+ TreeItemModel* getChildModel();
2751+
2752+public slots:
2753+ void activate();
2754+ void activateChild(const QModelIndex& index);
2755+ void onRightClick(const QPoint& globalPos);
2756+ void onRightClickChild(const QPoint& globalPos, QModelIndex index);
2757+ void refreshLibraryModels();
2758+private:
2759+ bool importLibrary(QString file);
2760+ /** parses a track in the music collection **/
2761+ void parseTrack(QXmlStreamReader &xml, QSqlQuery &query);
2762+ /** Iterates over all playliost and folders and constructs the childmodel **/
2763+ void parsePlaylists(QXmlStreamReader &xml);
2764+ /** processes a particular playlist **/
2765+ void parsePlaylistEntries(QXmlStreamReader &xml, QString playlist_path,
2766+ QSqlQuery query_insert_into_playlist, QSqlQuery query_insert_into_playlisttracks);
2767+ void clearTable(QString table_name);
2768+ static QString getTraktorMusicDatabase();
2769+ //private fields
2770+ TreeItemModel m_childModel;
2771+ TrackCollection* m_pTrackCollection;
2772+ QSqlDatabase &m_database;
2773+ TraktorTableModel* m_pTraktorTableModel;
2774+ TraktorPlaylistModel* m_pTraktorPlaylistModel;
2775+
2776+ bool m_isActivated;
2777+
2778+
2779+};
2780+
2781+
2782+
2783+#endif /* TRAKTOR_FEATURE_H */
2784
2785=== added file 'mixxx/src/library/traktorplaylistmodel.cpp'
2786--- mixxx/src/library/traktorplaylistmodel.cpp 1970-01-01 00:00:00 +0000
2787+++ mixxx/src/library/traktorplaylistmodel.cpp 2011-01-06 15:28:44 +0000
2788@@ -0,0 +1,229 @@
2789+#include <QtCore>
2790+#include <QtGui>
2791+#include <QtSql>
2792+#include "library/trackcollection.h"
2793+#include "library/traktorplaylistmodel.h"
2794+
2795+#include "mixxxutils.cpp"
2796+
2797+TraktorPlaylistModel::TraktorPlaylistModel(QObject* parent,
2798+ TrackCollection* pTrackCollection)
2799+ : TrackModel(pTrackCollection->getDatabase(),
2800+ "mixxx.db.model.traktor.playlistmodel"),
2801+ BaseSqlTableModel(parent, pTrackCollection, pTrackCollection->getDatabase()),
2802+ m_pTrackCollection(pTrackCollection),
2803+ m_database(m_pTrackCollection->getDatabase())
2804+
2805+{
2806+ connect(this, SIGNAL(doSearch(const QString&)), this, SLOT(slotSearch(const QString&)));
2807+
2808+
2809+
2810+
2811+}
2812+
2813+TraktorPlaylistModel::~TraktorPlaylistModel() {
2814+}
2815+
2816+bool TraktorPlaylistModel::addTrack(const QModelIndex& index, QString location)
2817+{
2818+
2819+ return false;
2820+}
2821+
2822+TrackPointer TraktorPlaylistModel::getTrack(const QModelIndex& index) const
2823+{
2824+ //qDebug() << "getTraktorTrack";
2825+
2826+ QString artist = index.sibling(index.row(), fieldIndex("artist")).data().toString();
2827+ QString title = index.sibling(index.row(), fieldIndex("title")).data().toString();
2828+ QString album = index.sibling(index.row(), fieldIndex("album")).data().toString();
2829+ QString year = index.sibling(index.row(), fieldIndex("year")).data().toString();
2830+ QString genre = index.sibling(index.row(), fieldIndex("genre")).data().toString();
2831+ float bpm = index.sibling(index.row(), fieldIndex("bpm")).data().toString().toFloat();
2832+
2833+ QString location = index.sibling(index.row(), fieldIndex("location")).data().toString();
2834+
2835+ TrackInfoObject* pTrack = new TrackInfoObject(location);
2836+ pTrack->setArtist(artist);
2837+ pTrack->setTitle(title);
2838+ pTrack->setAlbum(album);
2839+ pTrack->setYear(year);
2840+ pTrack->setGenre(genre);
2841+ pTrack->setBpm(bpm);
2842+
2843+
2844+ return TrackPointer(pTrack, &QObject::deleteLater);
2845+}
2846+
2847+QString TraktorPlaylistModel::getTrackLocation(const QModelIndex& index) const
2848+{
2849+ QString location = index.sibling(index.row(), fieldIndex("location")).data().toString();
2850+ return location;
2851+}
2852+
2853+void TraktorPlaylistModel::removeTrack(const QModelIndex& index)
2854+{
2855+
2856+}
2857+
2858+void TraktorPlaylistModel::removeTracks(const QModelIndexList& indices) {
2859+
2860+}
2861+void TraktorPlaylistModel::moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex)
2862+{
2863+
2864+}
2865+
2866+void TraktorPlaylistModel::search(const QString& searchText) {
2867+ // qDebug() << "TraktorPlaylistModel::search()" << searchText
2868+ // << QThread::currentThread();
2869+ emit(doSearch(searchText));
2870+}
2871+
2872+void TraktorPlaylistModel::slotSearch(const QString& searchText)
2873+{
2874+ if (!m_currentSearch.isNull() && m_currentSearch == searchText)
2875+ return;
2876+ m_currentSearch = searchText;
2877+
2878+ QString filter;
2879+ QSqlField search("search", QVariant::String);
2880+ search.setValue("%" + searchText + "%");
2881+ QString escapedText = database().driver()->formatValue(search);
2882+ filter = "(artist LIKE " + escapedText + " OR " +
2883+ "album LIKE " + escapedText + " OR " +
2884+ "title LIKE " + escapedText + ")";
2885+ setFilter(filter);
2886+}
2887+
2888+const QString TraktorPlaylistModel::currentSearch() {
2889+ return m_currentSearch;
2890+}
2891+
2892+bool TraktorPlaylistModel::isColumnInternal(int column) {
2893+ if (column == fieldIndex(LIBRARYTABLE_ID) ||
2894+ column == fieldIndex(LIBRARYTABLE_MIXXXDELETED) ||
2895+ column == fieldIndex(TRACKLOCATIONSTABLE_FSDELETED) ||
2896+ column == fieldIndex("name") ||
2897+ column == fieldIndex("track_id"))
2898+ return true;
2899+ return false;
2900+}
2901+
2902+QMimeData* TraktorPlaylistModel::mimeData(const QModelIndexList &indexes) const {
2903+
2904+ QMimeData *mimeData = new QMimeData();
2905+ QList<QUrl> urls;
2906+
2907+ //Ok, so the list of indexes we're given contains separates indexes for
2908+ //each column, so even if only one row is selected, we'll have like 7 indexes.
2909+ //We need to only count each row once:
2910+ QList<int> rows;
2911+
2912+ foreach (QModelIndex index, indexes) {
2913+ if (index.isValid()) {
2914+ if (!rows.contains(index.row())) {
2915+ rows.push_back(index.row());
2916+ QUrl url = QUrl::fromLocalFile(getTrackLocation(index));
2917+ if (!url.isValid())
2918+ qDebug() << "ERROR invalid url\n";
2919+ else
2920+ urls.append(url);
2921+ }
2922+ }
2923+ }
2924+ mimeData->setUrls(urls);
2925+ return mimeData;
2926+
2927+}
2928+
2929+
2930+QItemDelegate* TraktorPlaylistModel::delegateForColumn(const int i) {
2931+ return NULL;
2932+}
2933+
2934+TrackModel::CapabilitiesFlags TraktorPlaylistModel::getCapabilities() const
2935+{
2936+
2937+ return NULL;
2938+}
2939+
2940+Qt::ItemFlags TraktorPlaylistModel::flags(const QModelIndex &index) const
2941+{
2942+ return readOnlyFlags(index);
2943+}
2944+void TraktorPlaylistModel::setPlaylist(QString playlist_path)
2945+{
2946+ int playlistId = -1;
2947+ QSqlQuery finder_query(m_database);
2948+ finder_query.prepare("SELECT id from traktor_playlists where name='"+playlist_path+"'");
2949+
2950+ if(finder_query.exec()){
2951+ while (finder_query.next()) {
2952+ playlistId = finder_query.value(finder_query.record().indexOf("id")).toInt();
2953+ }
2954+ }
2955+ else
2956+ qDebug() << "SQL Error in TraktorPlaylistModel.cpp: line" << __LINE__ << " " << finder_query.lastError();
2957+
2958+
2959+ QString playlistID = "TraktorPlaylist_" + QString("%1").arg(playlistId);
2960+ //Escape the playlist name
2961+ QSqlDriver* driver = m_pTrackCollection->getDatabase().driver();
2962+ QSqlField playlistNameField("name", QVariant::String);
2963+ playlistNameField.setValue(playlistID);
2964+
2965+ QSqlQuery query(m_database);
2966+ query.prepare("CREATE TEMPORARY VIEW IF NOT EXISTS "+ driver->formatValue(playlistNameField) + " AS "
2967+ "SELECT "
2968+ "traktor_library.id,"
2969+ "traktor_library.artist,"
2970+ "traktor_library.title,"
2971+ "traktor_library.album,"
2972+ "traktor_library.year,"
2973+ "traktor_library.genre,"
2974+ "traktor_library.tracknumber,"
2975+ "traktor_library.location,"
2976+ "traktor_library.comment,"
2977+ "traktor_library.rating,"
2978+ "traktor_library.duration,"
2979+ "traktor_library.bitrate,"
2980+ "traktor_library.bpm,"
2981+ "traktor_library.key,"
2982+ "traktor_playlist_tracks.track_id, "
2983+ "traktor_playlists.name "
2984+ "FROM traktor_library "
2985+ "INNER JOIN traktor_playlist_tracks "
2986+ "ON traktor_playlist_tracks.track_id = traktor_library.id "
2987+ "INNER JOIN traktor_playlists "
2988+ "ON traktor_playlist_tracks.playlist_id = traktor_playlists.id "
2989+ "where traktor_playlists.name='"+playlist_path+"'"
2990+ );
2991+
2992+
2993+ if (!query.exec()) {
2994+
2995+ qDebug() << "Error creating temporary view for traktor playlists. TraktorPlaylistModel --> line: " << __LINE__ << " " << query.lastError();
2996+ qDebug() << "Executed Query: " << query.executedQuery();
2997+ return;
2998+ }
2999+ setTable(playlistID);
3000+
3001+ //removeColumn(fieldIndex("track_id"));
3002+ //removeColumn(fieldIndex("name"));
3003+ //removeColumn(fieldIndex("id"));
3004+
3005+ slotSearch("");
3006+
3007+ select(); //Populate the data model.
3008+ initHeaderData();
3009+}
3010+bool TraktorPlaylistModel::isColumnHiddenByDefault(int column) {
3011+ if (column == fieldIndex(LIBRARYTABLE_KEY))
3012+ return true;
3013+ if(column == fieldIndex(LIBRARYTABLE_BITRATE))
3014+ return true;
3015+
3016+ return false;
3017+}
3018\ No newline at end of file
3019
3020=== added file 'mixxx/src/library/traktorplaylistmodel.h'
3021--- mixxx/src/library/traktorplaylistmodel.h 1970-01-01 00:00:00 +0000
3022+++ mixxx/src/library/traktorplaylistmodel.h 2011-01-06 15:28:44 +0000
3023@@ -0,0 +1,55 @@
3024+#ifndef TRAKTOR_PLAYLIST_MODEL_H
3025+#define TRAKTOR_PLAYLIST_MODEL_H
3026+
3027+#include <QtSql>
3028+#include <QItemDelegate>
3029+#include <QtCore>
3030+#include "trackmodel.h"
3031+#include "library/basesqltablemodel.h"
3032+#include "library/librarytablemodel.h"
3033+#include "library/dao/playlistdao.h"
3034+#include "library/dao/trackdao.h"
3035+
3036+class TrackCollection;
3037+
3038+class TraktorPlaylistModel : public BaseSqlTableModel, public virtual TrackModel
3039+{
3040+ Q_OBJECT
3041+ public:
3042+ TraktorPlaylistModel(QObject* parent, TrackCollection* pTrackCollection);
3043+ virtual ~TraktorPlaylistModel();
3044+
3045+ virtual TrackPointer getTrack(const QModelIndex& index) const;
3046+ virtual QString getTrackLocation(const QModelIndex& index) const;
3047+ virtual void search(const QString& searchText);
3048+ virtual const QString currentSearch();
3049+ virtual bool isColumnInternal(int column);
3050+ virtual bool isColumnHiddenByDefault(int column);
3051+ virtual void removeTrack(const QModelIndex& index);
3052+ virtual void removeTracks(const QModelIndexList& indices);
3053+ virtual bool addTrack(const QModelIndex& index, QString location);
3054+ virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex);
3055+
3056+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
3057+ QMimeData* mimeData(const QModelIndexList &indexes) const;
3058+
3059+ QItemDelegate* delegateForColumn(const int i);
3060+ TrackModel::CapabilitiesFlags getCapabilities() const;
3061+ /** sets the playlist **/
3062+ void setPlaylist(QString path_name);
3063+
3064+ private slots:
3065+ void slotSearch(const QString& searchText);
3066+
3067+ signals:
3068+ void doSearch(const QString& searchText);
3069+
3070+ private:
3071+ TrackCollection* m_pTrackCollection;
3072+ QSqlDatabase &m_database;
3073+
3074+
3075+ QString m_currentSearch;
3076+};
3077+
3078+#endif /* TRAKTOR_TABLE_MODEL_H */
3079
3080=== added file 'mixxx/src/library/traktortablemodel.cpp'
3081--- mixxx/src/library/traktortablemodel.cpp 1970-01-01 00:00:00 +0000
3082+++ mixxx/src/library/traktortablemodel.cpp 2011-01-06 15:28:44 +0000
3083@@ -0,0 +1,160 @@
3084+#include <QtCore>
3085+#include <QtGui>
3086+#include <QtSql>
3087+#include "library/trackcollection.h"
3088+#include "library/traktortablemodel.h"
3089+
3090+#include "mixxxutils.cpp"
3091+
3092+TraktorTableModel::TraktorTableModel(QObject* parent,
3093+ TrackCollection* pTrackCollection)
3094+ : TrackModel(pTrackCollection->getDatabase(),
3095+ "mixxx.db.model.traktor_tablemodel"),
3096+ BaseSqlTableModel(parent, pTrackCollection, pTrackCollection->getDatabase()),
3097+ m_pTrackCollection(pTrackCollection),
3098+ m_database(m_pTrackCollection->getDatabase())
3099+
3100+{
3101+ connect(this, SIGNAL(doSearch(const QString&)), this, SLOT(slotSearch(const QString&)));
3102+ setTable("traktor_library");
3103+ initHeaderData();
3104+}
3105+
3106+TraktorTableModel::~TraktorTableModel() {
3107+}
3108+
3109+bool TraktorTableModel::addTrack(const QModelIndex& index, QString location)
3110+{
3111+
3112+ return false;
3113+}
3114+
3115+TrackPointer TraktorTableModel::getTrack(const QModelIndex& index) const
3116+{
3117+ //qDebug() << "getTraktorTrack";
3118+
3119+ QString artist = index.sibling(index.row(), fieldIndex("artist")).data().toString();
3120+ QString title = index.sibling(index.row(), fieldIndex("title")).data().toString();
3121+ QString album = index.sibling(index.row(), fieldIndex("album")).data().toString();
3122+ QString year = index.sibling(index.row(), fieldIndex("year")).data().toString();
3123+ QString genre = index.sibling(index.row(), fieldIndex("genre")).data().toString();
3124+ float bpm = index.sibling(index.row(), fieldIndex("bpm")).data().toString().toFloat();
3125+
3126+ QString location = index.sibling(index.row(), fieldIndex("location")).data().toString();
3127+
3128+ TrackInfoObject* pTrack = new TrackInfoObject(location);
3129+ pTrack->setArtist(artist);
3130+ pTrack->setTitle(title);
3131+ pTrack->setAlbum(album);
3132+ pTrack->setYear(year);
3133+ pTrack->setGenre(genre);
3134+ pTrack->setBpm(bpm);
3135+
3136+
3137+ return TrackPointer(pTrack, &QObject::deleteLater);
3138+}
3139+
3140+QString TraktorTableModel::getTrackLocation(const QModelIndex& index) const
3141+{
3142+ QString location = index.sibling(index.row(), fieldIndex("location")).data().toString();
3143+ return location;
3144+}
3145+
3146+void TraktorTableModel::removeTrack(const QModelIndex& index)
3147+{
3148+
3149+}
3150+
3151+void TraktorTableModel::removeTracks(const QModelIndexList& indices) {
3152+
3153+}
3154+void TraktorTableModel::moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex)
3155+{
3156+
3157+}
3158+
3159+void TraktorTableModel::search(const QString& searchText) {
3160+ // qDebug() << "TraktorTableModel::search()" << searchText
3161+ // << QThread::currentThread();
3162+ emit(doSearch(searchText));
3163+}
3164+
3165+void TraktorTableModel::slotSearch(const QString& searchText)
3166+{
3167+ if (!m_currentSearch.isNull() && m_currentSearch == searchText)
3168+ return;
3169+ m_currentSearch = searchText;
3170+
3171+ QString filter;
3172+ QSqlField search("search", QVariant::String);
3173+ search.setValue("%" + searchText + "%");
3174+ QString escapedText = database().driver()->formatValue(search);
3175+ filter = "(artist LIKE " + escapedText + " OR " +
3176+ "album LIKE " + escapedText + " OR " +
3177+ "title LIKE " + escapedText + ")";
3178+ setFilter(filter);
3179+
3180+}
3181+
3182+const QString TraktorTableModel::currentSearch() {
3183+ return m_currentSearch;
3184+}
3185+
3186+bool TraktorTableModel::isColumnInternal(int column) {
3187+ if (column == fieldIndex(LIBRARYTABLE_ID) ||
3188+ column == fieldIndex(LIBRARYTABLE_MIXXXDELETED) ||
3189+ column == fieldIndex(TRACKLOCATIONSTABLE_FSDELETED))
3190+ return true;
3191+ return false;
3192+}
3193+
3194+QMimeData* TraktorTableModel::mimeData(const QModelIndexList &indexes) const {
3195+
3196+ QMimeData *mimeData = new QMimeData();
3197+ QList<QUrl> urls;
3198+
3199+ //Ok, so the list of indexes we're given contains separates indexes for
3200+ //each column, so even if only one row is selected, we'll have like 7 indexes.
3201+ //We need to only count each row once:
3202+ QList<int> rows;
3203+
3204+ foreach (QModelIndex index, indexes) {
3205+ if (index.isValid()) {
3206+ if (!rows.contains(index.row())) {
3207+ rows.push_back(index.row());
3208+ QUrl url = QUrl::fromLocalFile(getTrackLocation(index));
3209+ if (!url.isValid())
3210+ qDebug() << "ERROR invalid url\n";
3211+ else
3212+ urls.append(url);
3213+ }
3214+ }
3215+ }
3216+ mimeData->setUrls(urls);
3217+ return mimeData;
3218+
3219+}
3220+
3221+
3222+QItemDelegate* TraktorTableModel::delegateForColumn(const int i) {
3223+ return NULL;
3224+}
3225+
3226+TrackModel::CapabilitiesFlags TraktorTableModel::getCapabilities() const
3227+{
3228+
3229+ return NULL;
3230+}
3231+
3232+Qt::ItemFlags TraktorTableModel::flags(const QModelIndex &index) const
3233+{
3234+ return readOnlyFlags(index);
3235+}
3236+bool TraktorTableModel::isColumnHiddenByDefault(int column) {
3237+ if (column == fieldIndex(LIBRARYTABLE_KEY))
3238+ return true;
3239+ if(column == fieldIndex(LIBRARYTABLE_BITRATE))
3240+ return true;
3241+
3242+ return false;
3243+}
3244\ No newline at end of file
3245
3246=== added file 'mixxx/src/library/traktortablemodel.h'
3247--- mixxx/src/library/traktortablemodel.h 1970-01-01 00:00:00 +0000
3248+++ mixxx/src/library/traktortablemodel.h 2011-01-06 15:28:44 +0000
3249@@ -0,0 +1,53 @@
3250+#ifndef TRAKTOR_TABLE_MODEL_H
3251+#define TRAKTOR_TABLE_MODEL_H
3252+
3253+#include <QtSql>
3254+#include <QItemDelegate>
3255+#include <QtCore>
3256+#include "trackmodel.h"
3257+#include "library/basesqltablemodel.h"
3258+#include "library/librarytablemodel.h"
3259+#include "library/dao/playlistdao.h"
3260+#include "library/dao/trackdao.h"
3261+
3262+class TrackCollection;
3263+
3264+class TraktorTableModel : public BaseSqlTableModel, public virtual TrackModel
3265+{
3266+ Q_OBJECT
3267+ public:
3268+ TraktorTableModel(QObject* parent, TrackCollection* pTrackCollection);
3269+ virtual ~TraktorTableModel();
3270+
3271+ virtual TrackPointer getTrack(const QModelIndex& index) const;
3272+ virtual QString getTrackLocation(const QModelIndex& index) const;
3273+ virtual void search(const QString& searchText);
3274+ virtual const QString currentSearch();
3275+ virtual bool isColumnInternal(int column);
3276+ virtual bool isColumnHiddenByDefault(int column);
3277+ virtual void removeTrack(const QModelIndex& index);
3278+ virtual void removeTracks(const QModelIndexList& indices);
3279+ virtual bool addTrack(const QModelIndex& index, QString location);
3280+ virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex);
3281+
3282+ virtual Qt::ItemFlags flags(const QModelIndex &index) const;
3283+ QMimeData* mimeData(const QModelIndexList &indexes) const;
3284+
3285+ QItemDelegate* delegateForColumn(const int i);
3286+ TrackModel::CapabilitiesFlags getCapabilities() const;
3287+
3288+ private slots:
3289+ void slotSearch(const QString& searchText);
3290+
3291+ signals:
3292+ void doSearch(const QString& searchText);
3293+
3294+ private:
3295+ TrackCollection* m_pTrackCollection;
3296+ QSqlDatabase &m_database;
3297+
3298+
3299+ QString m_currentSearch;
3300+};
3301+
3302+#endif /* TRAKTOR_TABLE_MODEL_H */
3303
3304=== added file 'mixxx/src/library/treeitem.cpp'
3305--- mixxx/src/library/treeitem.cpp 1970-01-01 00:00:00 +0000
3306+++ mixxx/src/library/treeitem.cpp 2011-01-06 15:28:44 +0000
3307@@ -0,0 +1,97 @@
3308+// TreeItem.cpp
3309+// Created 10/02/2010 by Tobias Rafreider
3310+
3311+#include <QStringList>
3312+#include "treeitem.h"
3313+
3314+TreeItem::TreeItem(const QString &data, const QString &data_path, LibraryFeature* feature, TreeItem* parent)
3315+ {
3316+ m_data = data;
3317+ m_dataPath = data_path;
3318+ m_parentItem = parent;
3319+ m_feature = feature;
3320+ }
3321+
3322+ TreeItem::~TreeItem()
3323+ {
3324+ qDeleteAll(m_childItems);
3325+ }
3326+
3327+ void TreeItem::appendChild(TreeItem *item)
3328+ {
3329+ m_childItems.append(item);
3330+ }
3331+
3332+ TreeItem *TreeItem::child(int row)
3333+ {
3334+ return m_childItems.value(row);
3335+ }
3336+
3337+ int TreeItem::childCount() const
3338+ {
3339+ return m_childItems.count();
3340+ }
3341+
3342+
3343+ QVariant TreeItem::data() const
3344+ {
3345+ return m_data;
3346+ }
3347+ QVariant TreeItem::dataPath() const
3348+ {
3349+ return m_dataPath;
3350+ }
3351+ bool TreeItem::isPlaylist() const
3352+ {
3353+ return (m_childItems.count() == 0);
3354+ }
3355+bool TreeItem::isFolder() const
3356+ {
3357+ return (m_childItems.count() != 0);
3358+ }
3359+
3360+ TreeItem *TreeItem::parent()
3361+ {
3362+ return m_parentItem;
3363+ }
3364+
3365+ int TreeItem::row() const
3366+ {
3367+ if (m_parentItem)
3368+ return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));
3369+
3370+ return 0;
3371+ }
3372+ LibraryFeature* TreeItem::getFeature()
3373+ {
3374+ return m_feature;
3375+ }
3376+bool TreeItem::insertChildren(QList<QString>& data, int position, int count)
3377+ {
3378+ if (position < 0 || position > m_childItems.size())
3379+ return false;
3380+
3381+ for (int row = 0; row < count; ++row) {
3382+ TreeItem *item = new TreeItem(data.at(row), data.at(row), m_feature, this);
3383+ m_childItems.insert(row, item);
3384+ }
3385+
3386+ return true;
3387+ }
3388+bool TreeItem::removeChildren(int position, int count)
3389+ {
3390+ if (position < 0 || position + count > m_childItems.size())
3391+ return false;
3392+
3393+ for (int row = 0; row < count; ++row)
3394+ delete m_childItems.takeAt(position);
3395+
3396+ return true;
3397+ }
3398+
3399+bool TreeItem::setData(const QVariant &data, const QVariant &data_path)
3400+ {
3401+ m_data = data.toString();
3402+ m_dataPath = data_path.toString();
3403+ return true;
3404+ }
3405
3406=== added file 'mixxx/src/library/treeitem.h'
3407--- mixxx/src/library/treeitem.h 1970-01-01 00:00:00 +0000
3408+++ mixxx/src/library/treeitem.h 2011-01-06 15:28:44 +0000
3409@@ -0,0 +1,55 @@
3410+// TreeItem.h
3411+// Created 10/02/2010 by Tobias Rafreider
3412+
3413+#ifndef TREEITEM_H
3414+#define TREEITEM_H
3415+
3416+#include <QList>
3417+#include <QString>
3418+#include <QVariant>
3419+#include "libraryfeature.h"
3420+
3421+ class TreeItem
3422+ {
3423+ public:
3424+ TreeItem(const QString &data, const QString &data_path, LibraryFeature* feature = 0, TreeItem* parent = 0);
3425+ ~TreeItem();
3426+ /** appends a child item to this object **/
3427+ void appendChild(TreeItem *child);
3428+ /** returns the tree item at position 'row' in the childlist **/
3429+ TreeItem *child(int row);
3430+ /** returns the number of childs of this tree item **/
3431+ int childCount() const;
3432+ /** Returns the position of this object within its parent **/
3433+ int row() const;
3434+ /** returns the parent **/
3435+ TreeItem *parent();
3436+
3437+ /** for dynamic resizing models **/
3438+ bool insertChildren(QList<QString>& data, int position, int count);
3439+ bool removeChildren(int position, int count);
3440+
3441+ /** sets data **/
3442+ bool setData(const QVariant &data, const QVariant &data_path);
3443+ /** simple name of the playlist **/
3444+ QVariant data() const;
3445+ /** Full path of the playlist **/
3446+ QVariant dataPath() const;
3447+ /** Returns true if we have a leaf node **/
3448+ bool isPlaylist() const;
3449+ /** returns true if we have an inner node **/
3450+ bool isFolder() const;
3451+
3452+ /* Returns the Library feature object to which an item belongs to */
3453+ LibraryFeature* getFeature();
3454+
3455+ private:
3456+ QList<TreeItem*> m_childItems;
3457+ QString m_dataPath;
3458+ QString m_data;
3459+ LibraryFeature* m_feature;
3460+
3461+ TreeItem *m_parentItem;
3462+ };
3463+
3464+ #endif
3465
3466=== added file 'mixxx/src/library/treeitemmodel.cpp'
3467--- mixxx/src/library/treeitemmodel.cpp 1970-01-01 00:00:00 +0000
3468+++ mixxx/src/library/treeitemmodel.cpp 2011-01-06 15:28:44 +0000
3469@@ -0,0 +1,151 @@
3470+#include <QtGui>
3471+
3472+ #include "treeitem.h"
3473+ #include "treeitemmodel.h"
3474+
3475+ TreeItemModel::TreeItemModel(QObject *parent)
3476+ : QAbstractItemModel(parent)
3477+ {
3478+
3479+ m_rootItem = new TreeItem("$root","$root");
3480+
3481+ }
3482+
3483+ TreeItemModel::~TreeItemModel()
3484+ {
3485+ delete m_rootItem;
3486+ }
3487+//Our Treeview Model supports exactly a single column
3488+ int TreeItemModel::columnCount(const QModelIndex &parent) const
3489+ {
3490+ return 1;
3491+ }
3492+
3493+ QVariant TreeItemModel::data(const QModelIndex &index, int role) const
3494+ {
3495+ if (!index.isValid())
3496+ return QVariant();
3497+
3498+ if (role != Qt::DisplayRole)
3499+ return QVariant();
3500+
3501+ TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
3502+
3503+ return item->data();
3504+ }
3505+
3506+ Qt::ItemFlags TreeItemModel::flags(const QModelIndex &index) const
3507+ {
3508+ if (!index.isValid())
3509+ return 0;
3510+
3511+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
3512+ }
3513+
3514+ QVariant TreeItemModel::headerData(int section, Qt::Orientation orientation, int role) const
3515+ {
3516+ return QVariant();
3517+ }
3518+
3519+ QModelIndex TreeItemModel::index(int row, int column, const QModelIndex &parent)
3520+ const
3521+ {
3522+ if (!hasIndex(row, column, parent))
3523+ return QModelIndex();
3524+
3525+ TreeItem *parentItem = NULL;
3526+
3527+ if (!parent.isValid())
3528+ parentItem = m_rootItem;
3529+ else
3530+ parentItem = static_cast<TreeItem*>(parent.internalPointer());
3531+
3532+ TreeItem *childItem = parentItem->child(row);
3533+ if (childItem)
3534+ return createIndex(row, column, childItem);
3535+ else
3536+ return QModelIndex();
3537+ }
3538+
3539+ QModelIndex TreeItemModel::parent(const QModelIndex &index) const
3540+ {
3541+ if (!index.isValid())
3542+ return QModelIndex();
3543+
3544+ TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
3545+ TreeItem *parentItem = childItem->parent();
3546+
3547+ if (parentItem == m_rootItem)
3548+ return QModelIndex();
3549+
3550+ return createIndex(parentItem->row(), 0, parentItem);
3551+ }
3552+
3553+ int TreeItemModel::rowCount(const QModelIndex &parent) const
3554+ {
3555+ if (parent.column() > 0)
3556+ return 0;
3557+
3558+
3559+ TreeItem *parentItem = NULL;
3560+ //qDebug() << "parent data: " << parent.data();
3561+ if (!parent.isValid()){
3562+ parentItem = m_rootItem;
3563+ }
3564+ else{
3565+ parentItem = static_cast<TreeItem*>(parent.internalPointer());
3566+
3567+ }
3568+
3569+ //qDebug() << "TreeItem data: " << parent.internalPointer();
3570+
3571+ return parentItem->childCount();
3572+ }
3573+/**
3574+ * Populates the model and notifies the view.
3575+ * Call this method first, before you do call any other methods.
3576+*/
3577+ void TreeItemModel::setRootItem(TreeItem *item)
3578+ {
3579+ if(m_rootItem) delete m_rootItem;
3580+
3581+ m_rootItem = item;
3582+ reset();
3583+ }
3584+/**
3585+ * Before you can resize the data model dynamically by using 'insertRows' and 'removeRows'
3586+ * make sure you have initialized
3587+ */
3588+bool TreeItemModel::insertRows(QList<QString>& data, int position, int rows, const QModelIndex &parent)
3589+ {
3590+ TreeItem *parentItem = getItem(parent);
3591+ bool success;
3592+
3593+ beginInsertRows(parent, position, position + rows - 1);
3594+ success = parentItem->insertChildren(data, position, rows);
3595+ endInsertRows();
3596+
3597+ return success;
3598+ }
3599+ bool TreeItemModel::removeRows(int position, int rows, const QModelIndex &parent)
3600+ {
3601+ TreeItem *parentItem = getItem(parent);
3602+ bool success = true;
3603+
3604+ beginRemoveRows(parent, position, position + rows - 1);
3605+ success = parentItem->removeChildren(position, rows);
3606+ endRemoveRows();
3607+
3608+ return success;
3609+ }
3610+TreeItem* TreeItemModel::getItem(const QModelIndex &index) const
3611+ {
3612+ if (index.isValid()) {
3613+ TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
3614+ if (item) return item;
3615+ }
3616+ return m_rootItem;
3617+ }
3618+
3619+
3620+
3621
3622=== added file 'mixxx/src/library/treeitemmodel.h'
3623--- mixxx/src/library/treeitemmodel.h 1970-01-01 00:00:00 +0000
3624+++ mixxx/src/library/treeitemmodel.h 2011-01-06 15:28:44 +0000
3625@@ -0,0 +1,41 @@
3626+#ifndef TREE_ITEM_MODEL_H
3627+ #define TREE_ITEM_MODEL_H
3628+
3629+ #include <QAbstractItemModel>
3630+ #include <QModelIndex>
3631+ #include <QVariant>
3632+ #include <QList>
3633+
3634+
3635+
3636+ class TreeItem;
3637+
3638+ class TreeItemModel : public QAbstractItemModel
3639+ {
3640+ Q_OBJECT
3641+
3642+ public:
3643+ TreeItemModel(QObject *parent = 0);
3644+ ~TreeItemModel();
3645+
3646+ QVariant data(const QModelIndex &index, int role) const;
3647+ Qt::ItemFlags flags(const QModelIndex &index) const;
3648+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
3649+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
3650+ QModelIndex parent(const QModelIndex &index) const;
3651+
3652+ bool insertRows(QList<QString>& data, int position, int rows, const QModelIndex &parent = QModelIndex());
3653+ bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex());
3654+
3655+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
3656+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
3657+ void setRootItem(TreeItem *item);
3658+ TreeItem* getItem(const QModelIndex &index) const;
3659+
3660+
3661+ private:
3662+ TreeItem *m_rootItem;
3663+
3664+ };
3665+
3666+ #endif
3667
3668=== removed file 'mixxx/src/parser.cpp'
3669--- mixxx/src/parser.cpp 2010-06-22 18:39:04 +0000
3670+++ mixxx/src/parser.cpp 1970-01-01 00:00:00 +0000
3671@@ -1,76 +0,0 @@
3672-//
3673-// C++ Implementation: parser
3674-//
3675-// Description: superclass for external formats parsers
3676-//
3677-//
3678-// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
3679-//
3680-// Copyright: See COPYING file that comes with this distribution
3681-//
3682-//
3683-
3684-#include <QtDebug>
3685-#include "parser.h"
3686-
3687-/**
3688- @author Ingo Kossyk (kossyki@cs.tu-berlin.de)
3689- **/
3690-
3691-
3692-Parser::Parser() : QObject()
3693-{
3694-}
3695-
3696-Parser::~Parser()
3697-{
3698-
3699-
3700-}
3701-
3702-void Parser::clearLocations()
3703-{
3704- while(!m_sLocations.isEmpty())
3705- m_sLocations.removeFirst();
3706-}
3707-
3708-long Parser::countParsed()
3709-{
3710- return (long)m_sLocations.count();
3711-}
3712-
3713-bool Parser::isFilepath(QString sFilepath){
3714- QFile file(sFilepath);
3715- bool exists = file.exists();
3716- file.close();
3717- return exists;
3718-}
3719-
3720-bool Parser::isBinary(QString filename){
3721- QFile file(filename);
3722-
3723- if(file.open(QIODevice::ReadOnly)){
3724- char c;
3725- unsigned char uc;
3726-
3727- if(!file.getChar(&c))
3728- {
3729- qDebug() << "Parser: Error reading stream on " << filename;
3730- return true; //should this raise an exception?
3731- }
3732-
3733- uc = uchar(c);
3734-
3735- if(!(33<=uc && uc<=127)) //Starting byte is no character
3736- {
3737- file.close();
3738- return true;
3739- }
3740-
3741- } else{
3742- qDebug() << "Parser: Could not open file: " << filename;
3743- }
3744- //qDebug(QString("Parser: textstream starting character is: %1").arg(i));
3745- file.close();
3746- return false;
3747-}
3748
3749=== removed file 'mixxx/src/parser.h'
3750--- mixxx/src/parser.h 2010-06-22 18:39:04 +0000
3751+++ mixxx/src/parser.h 1970-01-01 00:00:00 +0000
3752@@ -1,53 +0,0 @@
3753-//
3754-// C++ Interface: parser
3755-//
3756-// Description: Interface header for the parser Parser
3757-//
3758-//
3759-// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
3760-//
3761-// Copyright: See COPYING file that comes with this distribution
3762-//
3763-//
3764-
3765-#ifndef PARSER_H
3766-#define PARSER_H
3767-
3768-/**Developer Information:
3769-This is the rootclass for all parser classes for the Importer class.
3770-It can be used to write a new type-specific parser by deriving a new class
3771-from it and overwrite the parse function and add class specific functions to
3772-it afterwards fro proper functioning
3773-**/
3774-
3775-#include <qobject.h>
3776-#include <qstring.h>
3777-#include <qfile.h>
3778-
3779-
3780-class Parser : public QObject
3781-{
3782-public:
3783- Parser();
3784- ~Parser();
3785- /**Can be called to parse a pls file
3786- Note for developers:
3787- This function should return an empty PtrList
3788- or 0 in order for the trackimporter to function**/
3789- virtual QList<QString> parse(QString) = 0;
3790-
3791-
3792-protected:
3793- /**Pointer to the parsed Filelocations**/
3794- QList<QString> m_sLocations;
3795- /**Returns the number of parsed locations**/
3796- long countParsed();
3797- /**Clears m_psLocations**/
3798- void clearLocations();
3799- /**Checks if the file does contain binary content**/
3800- bool isBinary(QString);
3801- /**Checks if the given string represents a local filepath**/
3802- bool isFilepath(QString );
3803-};
3804-
3805-#endif
3806
3807=== removed file 'mixxx/src/parserm3u.cpp'
3808--- mixxx/src/parserm3u.cpp 2010-06-22 18:39:04 +0000
3809+++ mixxx/src/parserm3u.cpp 1970-01-01 00:00:00 +0000
3810@@ -1,105 +0,0 @@
3811-//
3812-// C++ Implementation: parserm3u
3813-//
3814-// Description: module to parse m3u(plaintext) formated playlists
3815-//
3816-//
3817-// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
3818-//
3819-// Copyright: See COPYING file that comes with this distribution
3820-//
3821-//
3822-#include <QTextStream>
3823-#include "parserm3u.h"
3824-
3825-/**
3826- @author Ingo Kossyk (kossyki@cs.tu-berlin.de)
3827- **/
3828-
3829-/**
3830- ToDo:
3831- - parse ALL informations from the pls file if available ,
3832- not only the filepath;
3833-
3834- Userinformation :
3835- The M3U format is just a headerless plaintext format
3836- where every line of text either represents
3837- a file location or a comment. comments are being
3838- preceeded by a '#'. This parser will try to parse all
3839- file information from the given file and add the filepaths
3840- to the locations ptrlist when the file is existing locally
3841- or on a mounted harddrive.
3842- **/
3843-
3844-ParserM3u::ParserM3u() : Parser()
3845-{
3846-}
3847-
3848-ParserM3u::~ParserM3u()
3849-{
3850-
3851-}
3852-
3853-
3854-QList<QString> ParserM3u::parse(QString sFilename)
3855-{
3856- QFile file(sFilename);
3857- QString basepath = sFilename.section('/', 0, -2);
3858-
3859- clearLocations();
3860- //qDebug() << "ParserM3u: Starting to parse.";
3861- if (file.open(QIODevice::ReadOnly) && !isBinary(sFilename)) {
3862- QTextStream textstream(&file);
3863-
3864- while(!textstream.atEnd()) {
3865- QString sLine = getFilepath(&textstream, basepath);
3866- if(sLine.isEmpty())
3867- break;
3868-
3869- //qDebug) << ("ParserM3u: parsed: " << (sLine);
3870- m_sLocations.append(sLine);
3871- }
3872-
3873- file.close();
3874-
3875- if(m_sLocations.count() != 0)
3876- return m_sLocations;
3877- else
3878- return QList<QString>(); // NULL pointer returned when no locations were found
3879-
3880- }
3881-
3882- file.close();
3883- return QList<QString>(); //if we get here something went wrong
3884-}
3885-
3886-
3887-QString ParserM3u::getFilepath(QTextStream *stream, QString basepath)
3888-{
3889- QString textline,filename = "";
3890-
3891- textline = stream->readLine();
3892- while(!textline.isEmpty()){
3893- if(textline.isNull())
3894- break;
3895-
3896- if(!textline.contains("#") && !textline.isEmpty()){
3897- filename = textline;
3898- if(isFilepath(filename)) {
3899- return filename;
3900- } else {
3901- // Try relative to m3u dir
3902- QString rel = basepath + "/" + filename;
3903- if (isFilepath(rel)) {
3904- return rel;
3905- }
3906- // We couldn't match this to a real file so ignore it
3907- }
3908- }
3909- textline = stream->readLine();
3910- }
3911-
3912- // Signal we reached the end
3913- return 0;
3914-
3915-}
3916
3917=== removed file 'mixxx/src/parserm3u.h'
3918--- mixxx/src/parserm3u.h 2010-06-22 18:39:04 +0000
3919+++ mixxx/src/parserm3u.h 1970-01-01 00:00:00 +0000
3920@@ -1,35 +0,0 @@
3921-//
3922-// C++ Interface: parserm3u
3923-//
3924-// Description: Interface header for the example parser ParserM3u
3925-//
3926-//
3927-// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
3928-//
3929-// Copyright: See COPYING file that comes with this distribution
3930-//
3931-//
3932-#include "parser.h"
3933-
3934-#ifndef PARSERM3U_H
3935-#define PARSERM3U_H
3936-
3937-class QTextStream;
3938-
3939-class ParserM3u : public Parser
3940-{
3941- Q_OBJECT
3942-public:
3943- ParserM3u();
3944- ~ParserM3u();
3945- /**Overwriting function parse in class Parser**/
3946- QList<QString> parse(QString);
3947-
3948-private:
3949- /**Reads a line from the file and returns filepath if a valid file**/
3950- QString getFilepath(QTextStream *, QString);
3951-
3952-
3953-};
3954-
3955-#endif
3956
3957=== removed file 'mixxx/src/parserpls.cpp'
3958--- mixxx/src/parserpls.cpp 2010-06-22 18:39:04 +0000
3959+++ mixxx/src/parserpls.cpp 1970-01-01 00:00:00 +0000
3960@@ -1,125 +0,0 @@
3961-//
3962-// C++ Implementation: parserpls
3963-//
3964-// Description: module to parse pls formated playlists
3965-//
3966-//
3967-// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
3968-//
3969-// Copyright: See COPYING file that comes with this distribution
3970-//
3971-//
3972-#include "parser.h"
3973-#include "parserpls.h"
3974-#include <QDebug>
3975-#include <QTextStream>
3976-#include <QFile>
3977-
3978-/**
3979- @author Ingo Kossyk (kossyki@cs.tu-berlin.de)
3980- **/
3981-
3982-/**
3983- ToDo:
3984- - parse ALL informations from the pls file if available ,
3985- not only the filepath;
3986- **/
3987-
3988-ParserPls::ParserPls() : Parser()
3989-{
3990-}
3991-
3992-ParserPls::~ParserPls()
3993-{
3994-}
3995-
3996-QList<QString> ParserPls::parse(QString sFilename)
3997-{
3998- //long numEntries =0;
3999- QFile file(sFilename);
4000- QString basepath = sFilename.section('/', 0, -2);
4001-
4002- clearLocations();
4003-
4004- if (file.open(QIODevice::ReadOnly) && !isBinary(sFilename) ) {
4005-
4006- QTextStream textstream(&file);
4007-
4008- while(!textstream.atEnd()) {
4009- QString psLine = getFilepath(&textstream, basepath);
4010- if(psLine.isNull()) {
4011- break;
4012- } else {
4013- m_sLocations.append(psLine);
4014- }
4015-
4016- //--numEntries;
4017- }
4018-
4019- file.close();
4020-
4021- if(m_sLocations.count() != 0)
4022- return m_sLocations;
4023- else
4024- return QList<QString>(); // NULL pointer returned when no locations were found
4025- }
4026-
4027- file.close();
4028- return QList<QString>(); //if we get here something went wrong :D
4029-}
4030-
4031-long ParserPls::getNumEntries(QTextStream *stream)
4032-{
4033-
4034- QString textline;
4035- textline = stream->readLine();
4036-
4037- if(textline.contains("[playlist]")){
4038-
4039- while(!textline.contains("NumberOfEntries"))
4040- textline = stream->readLine();
4041-
4042- QString temp = textline.section("=",-1,-1);
4043-
4044- return temp.toLong();
4045-
4046- } else{
4047- qDebug() << "ParserPls: pls file is not a playlist! \n";
4048- return 0;
4049- }
4050-
4051-}
4052-
4053-
4054-QString ParserPls::getFilepath(QTextStream *stream, QString basepath)
4055-{
4056- QString textline,filename = "";
4057- textline = stream->readLine();
4058- while(!textline.isEmpty()){
4059- if(textline.isNull())
4060- break;
4061-
4062- if(textline.contains("File")) {
4063- int iPos = textline.indexOf("=",0);
4064- ++iPos;
4065-
4066- filename = textline.right(textline.length()-iPos);
4067-
4068- if(isFilepath(filename)) {
4069- return filename;
4070- } else {
4071- // Try relative to m3u dir
4072- QString rel = basepath + "/" + filename;
4073- if (isFilepath(rel)) {
4074- return rel;
4075- }
4076- // We couldn't match this to a real file so ignore it
4077- }
4078- }
4079- textline = stream->readLine();
4080- }
4081-
4082- // Signal we reached the end
4083- return 0;
4084-
4085-}
4086
4087=== removed file 'mixxx/src/parserpls.h'
4088--- mixxx/src/parserpls.h 2010-06-22 18:39:04 +0000
4089+++ mixxx/src/parserpls.h 1970-01-01 00:00:00 +0000
4090@@ -1,38 +0,0 @@
4091-//
4092-// C++ Interface: parserpls
4093-//
4094-// Description: Interface header for the example parser PlsParser
4095-//
4096-//
4097-// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
4098-//
4099-// Copyright: See COPYING file that comes with this distribution
4100-//
4101-//
4102-
4103-
4104-#ifndef PARSERPLS_H
4105-#define PARSERPLS_H
4106-
4107-#include "parser.h"
4108-
4109-class QTextStream;
4110-
4111-class ParserPls : public Parser
4112-{
4113- Q_OBJECT
4114-public:
4115- ParserPls();
4116- ~ParserPls();
4117- /**Can be called to parse a pls file**/
4118- QList<QString> parse(QString);
4119-
4120-private:
4121- /**Returns the Number of entries in the pls file**/
4122- long getNumEntries(QTextStream * );
4123- /**Reads a line from the file and returns filepath**/
4124- QString getFilepath(QTextStream *, QString);
4125-
4126-};
4127-
4128-#endif

Subscribers

People subscribed via source and target branches