Merge lp:~mixxxdevelopers/mixxx/traktor_library into lp:~mixxxdevelopers/mixxx/trunk
- traktor_library
- Merge into trunk
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 | ||||||||
Related bugs: |
|
||||||||
Related blueprints: |
Traktor Library Support
(Medium)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Albert Santoni | Approve | ||
Review via email: mp+44610@code.launchpad.net |
Commit message
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.
- 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
Albert Santoni (gamegod) wrote : | # |
- Fix indentation in CrateFeature
- in CrateFeature:
- I see you did some magic with CrateFeature:
- 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:
- 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.
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:
> model because we call m_crateListTabl
> 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:
> 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:
> 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.
Albert Santoni (gamegod) wrote : | # |
Thanks for your thoroughness Tobias!
- 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
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' |
27 | Binary 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 |
- Fix indentation in CrateFeature
- in CrateFeature: :slotRenameCrat e, we're probably resetting the selection model because we call m_crateListTabl eModel. 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: :constructChild Model() 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: :getTraktorMusi cDatabase( ), 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?
- In TraktorFeature:
- 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.