Merge lp:~mixxxcontributors/mixxx/features_setlog into lp:~mixxxdevelopers/mixxx/trunk

Proposed by RJ Skerry-Ryan
Status: Merged
Merged at revision: 2990
Proposed branch: lp:~mixxxcontributors/mixxx/features_setlog
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 3293 lines (+1546/-628)
37 files modified
mixxx/build/depends.py (+5/-1)
mixxx/res/html/setlogs.html (+10/-0)
mixxx/res/mixxx.qrc (+3/-0)
mixxx/src/basetrackplayer.cpp (+1/-1)
mixxx/src/dlgautodj.cpp (+1/-1)
mixxx/src/library/baseplaylistfeature.cpp (+411/-0)
mixxx/src/library/baseplaylistfeature.h (+81/-0)
mixxx/src/library/basesqltablemodel.cpp (+1/-1)
mixxx/src/library/browse/browsetablemodel.cpp (+2/-5)
mixxx/src/library/cratefeature.cpp (+104/-80)
mixxx/src/library/cratefeature.h (+4/-2)
mixxx/src/library/dao/cratedao.cpp (+5/-4)
mixxx/src/library/dao/cratedao.h (+3/-2)
mixxx/src/library/dao/playlistdao.cpp (+128/-62)
mixxx/src/library/dao/playlistdao.h (+30/-9)
mixxx/src/library/legacylibraryimporter.cpp (+1/-2)
mixxx/src/library/library.cpp (+2/-1)
mixxx/src/library/libraryfeature.h (+2/-1)
mixxx/src/library/parser.cpp (+1/-1)
mixxx/src/library/parsercsv.cpp (+233/-0)
mixxx/src/library/parsercsv.h (+41/-0)
mixxx/src/library/playlistfeature.cpp (+24/-341)
mixxx/src/library/playlistfeature.h (+9/-54)
mixxx/src/library/playlisttablemodel.cpp (+13/-1)
mixxx/src/library/playlisttablemodel.h (+1/-0)
mixxx/src/library/preparefeature.cpp (+11/-0)
mixxx/src/library/setlogfeature.cpp (+263/-0)
mixxx/src/library/setlogfeature.h (+48/-0)
mixxx/src/library/sidebarmodel.cpp (+42/-19)
mixxx/src/library/sidebarmodel.h (+1/-1)
mixxx/src/library/trackcollection.cpp (+1/-1)
mixxx/src/mixxx.cpp (+3/-8)
mixxx/src/playerinfo.cpp (+29/-23)
mixxx/src/playerinfo.h (+10/-1)
mixxx/src/trackinfoobject.cpp (+13/-4)
mixxx/src/trackinfoobject.h (+4/-2)
mixxx/src/widget/wlibrarysidebar.cpp (+5/-0)
To merge this branch: bzr merge lp:~mixxxcontributors/mixxx/features_setlog
Reviewer Review Type Date Requested Status
RJ Skerry-Ryan Approve
Review via email: mp+96964@code.launchpad.net

Description of the change

This branch brings Daniel Schürmann's session history feature to Mixxx. I have reviewed the branch and it looks good to me from a stability and product/feature point of view.

I think we need to decide from an artistic point of view what the root 'Set Log' page should look like (e.g. wording and the inclusion of a picture a-la the crates view). Also, should we call it "Set Logs" or just "History" or maybe "Set History"?

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'mixxx/build/depends.py'
2--- mixxx/build/depends.py 2011-12-25 05:45:11 +0000
3+++ mixxx/build/depends.py 2012-03-12 04:25:21 +0000
4@@ -236,7 +236,8 @@
5 # SoundTouch CPU optimizations are only for x86
6 # architectures. SoundTouch automatically ignores these files when it is
7 # not being built for an architecture that supports them.
8- cpu_detection = '#lib/%s/cpu_detect_x86_win.cpp' if build.toolchain_is_msvs else '#lib/%s/cpu_detect_x86_gcc.cpp'
9+ cpu_detection = '#lib/%s/cpu_detect_x86_win.cpp' if build.toolchain_is_msvs else \
10+ '#lib/%s/cpu_detect_x86_gcc.cpp'
11 sources.append(cpu_detection % self.SOUNDTOUCH_PATH)
12 return sources
13
14@@ -415,7 +416,9 @@
15 "library/preparefeature.cpp",
16 "library/autodjfeature.cpp",
17 "library/mixxxlibraryfeature.cpp",
18+ "library/baseplaylistfeature.cpp",
19 "library/playlistfeature.cpp",
20+ "library/setlogfeature.cpp",
21
22 "library/browse/browsetablemodel.cpp",
23 "library/browse/browsethread.cpp",
24@@ -473,6 +476,7 @@
25 "library/parser.cpp",
26 "library/parserpls.cpp",
27 "library/parserm3u.cpp",
28+ "library/parsercsv.cpp",
29
30 "bpm/bpmscheme.cpp",
31
32
33=== added file 'mixxx/res/html/setlogs.html'
34--- mixxx/res/html/setlogs.html 1970-01-01 00:00:00 +0000
35+++ mixxx/res/html/setlogs.html 2012-03-12 04:25:21 +0000
36@@ -0,0 +1,10 @@
37+<h2>Set Logs</h2>
38+<table border="0" cellpadding="5">
39+ <tr>
40+ <td>
41+ <p>Set Logs are automatic generated lists of your last DJ sets.</p>
42+ <p>Every time you start Mixxx, a new Set Log is generated. You can export it as m3u playlist or play it again with Auto DJ.</p>
43+ <p>When you restart mixxx you can join the current Set Log with the previous one. The track played flags are restored.</p>
44+ </td>
45+ </tr>
46+</table>
47
48=== added file 'mixxx/res/images/library/ic_library_setlog.png'
49Binary files mixxx/res/images/library/ic_library_setlog.png 1970-01-01 00:00:00 +0000 and mixxx/res/images/library/ic_library_setlog.png 2012-03-12 04:25:21 +0000 differ
50=== added file 'mixxx/res/images/library/ic_library_setlog_current.png'
51Binary files mixxx/res/images/library/ic_library_setlog_current.png 1970-01-01 00:00:00 +0000 and mixxx/res/images/library/ic_library_setlog_current.png 2012-03-12 04:25:21 +0000 differ
52=== modified file 'mixxx/res/mixxx.qrc'
53--- mixxx/res/mixxx.qrc 2011-12-22 18:43:09 +0000
54+++ mixxx/res/mixxx.qrc 2012-03-12 04:25:21 +0000
55@@ -2,6 +2,7 @@
56 <qresource prefix="/">
57 <file>html/crates.html</file>
58 <file>html/playlists.html</file>
59+ <file>html/setlogs.html</file>
60 <file>images/mixxx-icon.png</file>
61 <file>images/ic_mixxx_window.png</file>
62 <file>images/templates/logo_mixxx.png</file>
63@@ -32,6 +33,8 @@
64 <file>images/library/ic_library_rhythmbox.png</file>
65 <file>images/library/ic_library_traktor.png</file>
66 <file>images/library/ic_library_recordings.png</file>
67+ <file>images/library/ic_library_setlog.png</file>
68+ <file>images/library/ic_library_setlog_current.png</file>
69 <file>translations/mixxx_ar.qm</file>
70 <file>translations/mixxx_bg.qm</file>
71 <file>translations/mixxx_br.qm</file>
72
73=== modified file 'mixxx/src/basetrackplayer.cpp'
74--- mixxx/src/basetrackplayer.cpp 2012-01-09 00:43:24 +0000
75+++ mixxx/src/basetrackplayer.cpp 2012-03-12 04:25:21 +0000
76@@ -199,7 +199,7 @@
77 if(!m_pLoadedTrack->getHeaderParsed())
78 SoundSourceProxy::ParseHeader(m_pLoadedTrack.data());
79
80- m_pLoadedTrack->setPlayed(true);
81+ // m_pLoadedTrack->setPlayedAndUpdatePlaycount(true); // Actually the song is loaded but not played
82
83 // Generate waveform summary
84 //TODO: Consider reworking this visual resample stuff... need to ask rryan about this -- Albert.
85
86=== modified file 'mixxx/src/dlgautodj.cpp'
87--- mixxx/src/dlgautodj.cpp 2011-11-30 06:19:47 +0000
88+++ mixxx/src/dlgautodj.cpp 2012-03-12 04:25:21 +0000
89@@ -38,7 +38,7 @@
90 "mixxx.db.model.autodj");
91 int playlistId = m_playlistDao.getPlaylistIdFromName(AUTODJ_TABLE);
92 if (playlistId < 0) {
93- m_playlistDao.createPlaylist(AUTODJ_TABLE, true);
94+ m_playlistDao.createPlaylist(AUTODJ_TABLE, PlaylistDAO::PLHT_AUTO_DJ);
95 playlistId = m_playlistDao.getPlaylistIdFromName(AUTODJ_TABLE);
96 }
97 m_pAutoDJTableModel->setPlaylist(playlistId);
98
99=== added file 'mixxx/src/library/baseplaylistfeature.cpp'
100--- mixxx/src/library/baseplaylistfeature.cpp 1970-01-01 00:00:00 +0000
101+++ mixxx/src/library/baseplaylistfeature.cpp 2012-03-12 04:25:21 +0000
102@@ -0,0 +1,411 @@
103+#include "library/baseplaylistfeature.h"
104+
105+#include "library/parser.h"
106+#include "library/parserm3u.h"
107+#include "library/parserpls.h"
108+#include "library/parsercsv.h"
109+#include "library/playlisttablemodel.h"
110+#include "library/trackcollection.h"
111+#include "mixxxkeyboard.h"
112+#include "widget/wlibrary.h"
113+#include "widget/wlibrarysidebar.h"
114+#include "widget/wlibrarytextbrowser.h"
115+
116+BasePlaylistFeature::BasePlaylistFeature(
117+ QObject* parent, ConfigObject<ConfigValue>* pConfig,
118+ TrackCollection* pTrackCollection,
119+ QString rootViewName, QString rootViewUrl)
120+ : LibraryFeature(parent),
121+ m_pConfig(pConfig),
122+ m_pTrackCollection(pTrackCollection),
123+ m_playlistDao(pTrackCollection->getPlaylistDAO()),
124+ m_trackDao(pTrackCollection->getTrackDAO()),
125+ m_pPlaylistTableModel(NULL),
126+ m_playlistTableModel(this, pTrackCollection->getDatabase()),
127+ m_rootViewName(rootViewName),
128+ m_rootViewUrl(rootViewUrl) {
129+ m_pCreatePlaylistAction = new QAction(tr("New Playlist"),this);
130+ connect(m_pCreatePlaylistAction, SIGNAL(triggered()),
131+ this, SLOT(slotCreatePlaylist()));
132+
133+ m_pAddToAutoDJAction = new QAction(tr("Add to Auto DJ Queue (bottom)"), this);
134+ connect(m_pAddToAutoDJAction, SIGNAL(triggered()),
135+ this, SLOT(slotAddToAutoDJ()));
136+
137+ m_pAddToAutoDJTopAction = new QAction(tr("Add to Auto DJ Queue (top)"), this);
138+ connect(m_pAddToAutoDJTopAction, SIGNAL(triggered()),
139+ this, SLOT(slotAddToAutoDJTop()));
140+
141+ m_pDeletePlaylistAction = new QAction(tr("Remove"),this);
142+ connect(m_pDeletePlaylistAction, SIGNAL(triggered()),
143+ this, SLOT(slotDeletePlaylist()));
144+
145+ m_pRenamePlaylistAction = new QAction(tr("Rename"),this);
146+ connect(m_pRenamePlaylistAction, SIGNAL(triggered()),
147+ this, SLOT(slotRenamePlaylist()));
148+
149+ m_pLockPlaylistAction = new QAction(tr("Lock"),this);
150+ connect(m_pLockPlaylistAction, SIGNAL(triggered()),
151+ this, SLOT(slotTogglePlaylistLock()));
152+
153+ m_pImportPlaylistAction = new QAction(tr("Import Playlist"),this);
154+ connect(m_pImportPlaylistAction, SIGNAL(triggered()),
155+ this, SLOT(slotImportPlaylist()));
156+
157+ m_pExportPlaylistAction = new QAction(tr("Export Playlist"), this);
158+ connect(m_pExportPlaylistAction, SIGNAL(triggered()),
159+ this, SLOT(slotExportPlaylist()));
160+
161+ connect(&m_playlistDao, SIGNAL(added(int)),
162+ this, SLOT(slotPlaylistTableChanged(int)));
163+
164+ connect(&m_playlistDao, SIGNAL(deleted(int)),
165+ this, SLOT(slotPlaylistTableChanged(int)));
166+
167+ connect(&m_playlistDao, SIGNAL(renamed(int)),
168+ this, SLOT(slotPlaylistTableChanged(int)));
169+
170+ connect(&m_playlistDao, SIGNAL(lockChanged(int)),
171+ this, SLOT(slotPlaylistTableChanged(int)));
172+}
173+
174+BasePlaylistFeature::~BasePlaylistFeature() {
175+ delete m_pPlaylistTableModel;
176+ delete m_pCreatePlaylistAction;
177+ delete m_pDeletePlaylistAction;
178+ delete m_pImportPlaylistAction;
179+ delete m_pExportPlaylistAction;
180+ delete m_pAddToAutoDJAction;
181+ delete m_pAddToAutoDJTopAction;
182+ delete m_pRenamePlaylistAction;
183+ delete m_pLockPlaylistAction;
184+}
185+
186+void BasePlaylistFeature::activate() {
187+ emit(showPage(QUrl(m_rootViewUrl)));
188+ emit(switchToView(m_rootViewName));
189+}
190+
191+void BasePlaylistFeature::activateChild(const QModelIndex& index) {
192+ //qDebug() << "BasePlaylistFeature::activateChild()" << index;
193+
194+ // Switch the playlist table model's playlist.
195+ QString playlistName = index.data().toString();
196+ int playlistId = m_playlistDao.getPlaylistIdFromName(playlistName);
197+ if (m_pPlaylistTableModel) {
198+ m_pPlaylistTableModel->setPlaylist(playlistId);
199+ emit(showTrackModel(m_pPlaylistTableModel));
200+ }
201+}
202+
203+void BasePlaylistFeature::slotRenamePlaylist() {
204+ QString oldName = m_lastRightClickedIndex.data().toString();
205+ int playlistId = m_playlistDao.getPlaylistIdFromName(oldName);
206+ bool locked = m_playlistDao.isPlaylistLocked(playlistId);
207+
208+ if (locked) {
209+ qDebug() << "Skipping playlist rename because playlist" << playlistId
210+ << "is locked.";
211+ return;
212+ }
213+
214+ QString newName;
215+ bool validNameGiven = false;
216+
217+ do {
218+ bool ok = false;
219+ newName = QInputDialog::getText(NULL,
220+ tr("Rename Playlist"),
221+ tr("New playlist name:"),
222+ QLineEdit::Normal,
223+ oldName,
224+ &ok).trimmed();
225+
226+ if (!ok || oldName == newName) {
227+ return;
228+ }
229+
230+ int existingId = m_playlistDao.getPlaylistIdFromName(newName);
231+
232+ if (existingId != -1) {
233+ QMessageBox::warning(NULL,
234+ tr("Renaming Playlist Failed"),
235+ tr("A playlist by that name already exists."));
236+ }
237+ else if (newName.isEmpty()) {
238+ QMessageBox::warning(NULL,
239+ tr("Renaming Playlist Failed"),
240+ tr("A playlist cannot have a blank name."));
241+ }
242+ else {
243+ validNameGiven = true;
244+ }
245+ } while (!validNameGiven);
246+
247+ m_playlistDao.renamePlaylist(playlistId, newName);
248+ emit(featureUpdated());
249+}
250+
251+void BasePlaylistFeature::slotTogglePlaylistLock() {
252+ QString playlistName = m_lastRightClickedIndex.data().toString();
253+ int playlistId = m_playlistDao.getPlaylistIdFromName(playlistName);
254+ bool locked = !m_playlistDao.isPlaylistLocked(playlistId);
255+
256+ if (!m_playlistDao.setPlaylistLocked(playlistId, locked)) {
257+ qDebug() << "Failed to toggle lock of playlistId " << playlistId;
258+ }
259+}
260+
261+void BasePlaylistFeature::slotCreatePlaylist() {
262+ if (!m_pPlaylistTableModel) {
263+ return;
264+ }
265+
266+ QString name;
267+ bool validNameGiven = false;
268+
269+ do {
270+ bool ok = false;
271+ name = QInputDialog::getText(NULL,
272+ tr("New Playlist"),
273+ tr("Playlist name:"),
274+ QLineEdit::Normal,
275+ tr("New Playlist"),
276+ &ok).trimmed();
277+
278+ if (!ok)
279+ return;
280+
281+ int existingId = m_playlistDao.getPlaylistIdFromName(name);
282+
283+ if (existingId != -1) {
284+ QMessageBox::warning(NULL,
285+ tr("Playlist Creation Failed"),
286+ tr("A playlist by that name already exists."));
287+ } else if (name.isEmpty()) {
288+ QMessageBox::warning(NULL,
289+ tr("Playlist Creation Failed"),
290+ tr("A playlist cannot have a blank name."));
291+ } else {
292+ validNameGiven = true;
293+ }
294+
295+ } while (!validNameGiven);
296+
297+ int playlistId = m_playlistDao.createPlaylist(name);
298+
299+ if (playlistId != -1) {
300+ emit(featureUpdated());
301+ emit(showTrackModel(m_pPlaylistTableModel));
302+ }
303+ else {
304+ QMessageBox::warning(NULL,
305+ tr("Playlist Creation Failed"),
306+ tr("An unknown error occurred while creating playlist: ")
307+ + name);
308+ }
309+}
310+
311+void BasePlaylistFeature::slotDeletePlaylist() {
312+ //qDebug() << "slotDeletePlaylist() row:" << m_lastRightClickedIndex.data();
313+ int playlistId = m_playlistDao.getPlaylistIdFromName(m_lastRightClickedIndex.data().toString());
314+ bool locked = m_playlistDao.isPlaylistLocked(playlistId);
315+
316+ if (locked) {
317+ qDebug() << "Skipping playlist deletion because playlist" << playlistId << "is locked.";
318+ return;
319+ }
320+
321+ if (m_lastRightClickedIndex.isValid()) {
322+ Q_ASSERT(playlistId >= 0);
323+
324+ m_playlistDao.deletePlaylist(playlistId);
325+ emit(featureUpdated());
326+ activate();
327+ }
328+}
329+
330+bool BasePlaylistFeature::dropAccept(QUrl url) {
331+ Q_UNUSED(url);
332+ return false;
333+}
334+
335+bool BasePlaylistFeature::dragMoveAccept(QUrl url) {
336+ Q_UNUSED(url);
337+ return false;
338+}
339+
340+void BasePlaylistFeature::slotImportPlaylist() {
341+ qDebug() << "slotImportPlaylist() row:" ; //<< m_lastRightClickedIndex.data();
342+
343+ if (!m_pPlaylistTableModel) {
344+ return;
345+ }
346+
347+ QString playlist_file = QFileDialog::getOpenFileName(
348+ NULL,
349+ tr("Import Playlist"),
350+ QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
351+ tr("Playlist Files (*.m3u *.m3u8 *.pls *.csv)"));
352+ // Exit method if user cancelled the open dialog.
353+ if (playlist_file.isNull() || playlist_file.isEmpty()) {
354+ return;
355+ }
356+
357+ Parser* playlist_parser = NULL;
358+
359+ if (playlist_file.endsWith(".m3u", Qt::CaseInsensitive) ||
360+ playlist_file.endsWith(".m3u8", Qt::CaseInsensitive)) {
361+ playlist_parser = new ParserM3u();
362+ } else if (playlist_file.endsWith(".pls", Qt::CaseInsensitive)) {
363+ playlist_parser = new ParserPls();
364+ } else if (playlist_file.endsWith(".csv", Qt::CaseInsensitive)) {
365+ playlist_parser = new ParserCsv();
366+ } else {
367+ return;
368+ }
369+ QList<QString> entries = playlist_parser->parse(playlist_file);
370+
371+ // Iterate over the List that holds URLs of playlist entires
372+ for (int i = 0; i < entries.size(); ++i) {
373+ m_pPlaylistTableModel->addTrack(QModelIndex(), entries[i]);
374+ }
375+
376+ // delete the parser object
377+ if (playlist_parser) {
378+ delete playlist_parser;
379+ }
380+}
381+
382+void BasePlaylistFeature::slotExportPlaylist() {
383+ if (!m_pPlaylistTableModel) {
384+ return;
385+ }
386+
387+ qDebug() << "Export playlist" << m_lastRightClickedIndex.data();
388+ QString file_location = QFileDialog::getSaveFileName(
389+ NULL,
390+ tr("Export Playlist"),
391+ QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
392+ tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;"
393+ "PLS Playlist (*.pls);;Text CSV (*.csv);;Readable Text (*.txt)"));
394+ // Exit method if user cancelled the open dialog.
395+ if (file_location.isNull() || file_location.isEmpty()) {
396+ return;
397+ }
398+
399+ // Create a new table model since the main one might have an active search.
400+ QScopedPointer<PlaylistTableModel> pPlaylistTableModel(
401+ new PlaylistTableModel(this, m_pTrackCollection,
402+ "mixxx.db.model.playlist_export"));
403+
404+ pPlaylistTableModel->setPlaylist(m_pPlaylistTableModel->getPlaylist());
405+ pPlaylistTableModel->setSort(0, Qt::AscendingOrder);
406+ pPlaylistTableModel->select();
407+
408+ // check config if relative paths are desired
409+ bool useRelativePath = static_cast<bool>(m_pConfig->getValueString(
410+ ConfigKey("[Library]", "UseRelativePathOnExport")).toInt());
411+
412+ if (file_location.endsWith(".csv", Qt::CaseInsensitive)) {
413+ ParserCsv::writeCSVFile(file_location, pPlaylistTableModel.data(), useRelativePath);
414+ } else if (file_location.endsWith(".txt", Qt::CaseInsensitive)) {
415+ ParserCsv::writeReadableTextFile(file_location, pPlaylistTableModel.data());
416+ } else {
417+ // Create and populate a list of files of the playlist
418+ QList<QString> playlist_items;
419+ int rows = pPlaylistTableModel->rowCount();
420+ for (int i = 0; i < rows; ++i) {
421+ QModelIndex index = pPlaylistTableModel->index(i, 0);
422+ playlist_items << pPlaylistTableModel->getTrackLocation(index);
423+ }
424+
425+ if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {
426+ ParserPls::writePLSFile(file_location, playlist_items, useRelativePath);
427+ } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {
428+ ParserM3u::writeM3U8File(file_location, playlist_items, useRelativePath);
429+ } else {
430+ //default export to M3U if file extension is missing
431+ if(!file_location.endsWith(".m3u", Qt::CaseInsensitive))
432+ {
433+ qDebug() << "Crate export: No valid file extension specified. Appending .m3u "
434+ << "and exporting to M3U.";
435+ file_location.append(".m3u");
436+ }
437+ ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);
438+ }
439+ }
440+}
441+
442+void BasePlaylistFeature::slotAddToAutoDJ() {
443+ //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
444+ addToAutoDJ(false); // Top = True
445+}
446+
447+void BasePlaylistFeature::slotAddToAutoDJTop() {
448+ //qDebug() << "slotAddToAutoDJTop() row:" << m_lastRightClickedIndex.data();
449+ addToAutoDJ(true); // bTop = True
450+}
451+
452+void BasePlaylistFeature::addToAutoDJ(bool bTop) {
453+ //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
454+
455+ if (m_lastRightClickedIndex.isValid()) {
456+ int playlistId = m_playlistDao.getPlaylistIdFromName(
457+ m_lastRightClickedIndex.data().toString());
458+ if (playlistId >= 0) {
459+ // Insert this playlist
460+ m_playlistDao.addToAutoDJQueue(playlistId, bTop);
461+ }
462+ }
463+ emit(featureUpdated());
464+}
465+
466+void BasePlaylistFeature::onLazyChildExpandation(const QModelIndex &index){
467+ Q_UNUSED(index);
468+ //Nothing to do because the childmodel is not of lazy nature.
469+}
470+
471+TreeItemModel* BasePlaylistFeature::getChildModel() {
472+ return &m_childModel;
473+}
474+
475+void BasePlaylistFeature::bindWidget(WLibrarySidebar* sidebarWidget,
476+ WLibrary* libraryWidget,
477+ MixxxKeyboard* keyboard) {
478+ Q_UNUSED(sidebarWidget);
479+ Q_UNUSED(keyboard);
480+ WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget);
481+ connect(this, SIGNAL(showPage(const QUrl&)),
482+ edit, SLOT(setSource(const QUrl&)));
483+ libraryWidget->registerView(m_rootViewName, edit);
484+}
485+
486+/**
487+ * Clears the child model dynamically, but the invisible root item remains
488+ */
489+void BasePlaylistFeature::clearChildModel() {
490+ m_childModel.removeRows(0,m_playlistTableModel.rowCount());
491+}
492+
493+void BasePlaylistFeature::slotPlaylistTableChanged(int playlistId) {
494+ if (!m_pPlaylistTableModel) {
495+ return;
496+ }
497+
498+ //qDebug() << "slotPlaylistTableChanged() playlistId:" << playlistId;
499+ PlaylistDAO::HiddenType type = m_playlistDao.getHiddenType(playlistId);
500+ if (type == PlaylistDAO::PLHT_SET_LOG ||
501+ type == PlaylistDAO::PLHT_UNKNOWN) { // In case of a deleted Playlist
502+ clearChildModel();
503+ m_playlistTableModel.select();
504+ m_lastRightClickedIndex = constructChildModel(playlistId);
505+
506+ if (type != PlaylistDAO::PLHT_UNKNOWN) {
507+ // Switch the view to the playlist.
508+ m_pPlaylistTableModel->setPlaylist(playlistId);
509+ // Update selection
510+ emit(featureSelect(this, m_lastRightClickedIndex));
511+ }
512+ }
513+}
514
515=== added file 'mixxx/src/library/baseplaylistfeature.h'
516--- mixxx/src/library/baseplaylistfeature.h 1970-01-01 00:00:00 +0000
517+++ mixxx/src/library/baseplaylistfeature.h 2012-03-12 04:25:21 +0000
518@@ -0,0 +1,81 @@
519+#ifndef BASEPLAYLISTFEATURE_H
520+#define BASEPLAYLISTFEATURE_H
521+
522+#include <QSqlTableModel>
523+#include <QAction>
524+
525+#include "library/libraryfeature.h"
526+#include "library/dao/playlistdao.h"
527+#include "library/dao/trackdao.h"
528+
529+class WLibrarySidebar;
530+class WLibrary;
531+class MixxxKeyboard;
532+class PlaylistTableModel;
533+class TrackCollection;
534+
535+class BasePlaylistFeature : public LibraryFeature {
536+ Q_OBJECT
537+ public:
538+ BasePlaylistFeature(QObject* parent,
539+ ConfigObject<ConfigValue>* pConfig,
540+ TrackCollection* pTrackCollection,
541+ QString rootViewName, QString rootViewUrl);
542+ virtual ~BasePlaylistFeature();
543+
544+ TreeItemModel* getChildModel();
545+
546+ void bindWidget(WLibrarySidebar* sidebarWidget,
547+ WLibrary* libraryWidget,
548+ MixxxKeyboard* keyboard);
549+
550+ signals:
551+ void showPage(const QUrl& page);
552+
553+ public slots:
554+ virtual void activate();
555+ virtual void activateChild(const QModelIndex& index);
556+ virtual bool dropAccept(QUrl url);
557+ virtual bool dragMoveAccept(QUrl url);
558+ virtual void onLazyChildExpandation(const QModelIndex& index);
559+
560+ void slotCreatePlaylist();
561+
562+ protected slots:
563+ void slotDeletePlaylist();
564+ void slotAddToAutoDJ();
565+ void slotAddToAutoDJTop();
566+ void slotRenamePlaylist();
567+ void slotTogglePlaylistLock();
568+ void slotImportPlaylist();
569+ void slotExportPlaylist();
570+ void slotPlaylistTableChanged(int playlistId);
571+
572+ protected:
573+ virtual QModelIndex constructChildModel(int selected_id) = 0;
574+ virtual void clearChildModel();
575+ virtual void addToAutoDJ(bool bTop);
576+
577+ ConfigObject<ConfigValue>* m_pConfig;
578+ TrackCollection* m_pTrackCollection;
579+ PlaylistDAO &m_playlistDao;
580+ TrackDAO &m_trackDao;
581+ PlaylistTableModel* m_pPlaylistTableModel;
582+ QAction *m_pCreatePlaylistAction;
583+ QAction *m_pDeletePlaylistAction;
584+ QAction *m_pAddToAutoDJAction;
585+ QAction *m_pAddToAutoDJTopAction;
586+ QAction *m_pRenamePlaylistAction;
587+ QAction *m_pLockPlaylistAction;
588+ QAction *m_pImportPlaylistAction;
589+ QAction *m_pExportPlaylistAction;
590+ QSqlTableModel m_playlistTableModel;
591+ QModelIndex m_lastRightClickedIndex;
592+ TreeItemModel m_childModel;
593+
594+ private:
595+ QString m_rootViewName;
596+ QString m_rootViewUrl;
597+};
598+
599+#endif /* BASEPLAYLISTFEATURE_H */
600
601=== modified file 'mixxx/src/library/basesqltablemodel.cpp'
602--- mixxx/src/library/basesqltablemodel.cpp 2011-12-28 20:30:15 +0000
603+++ mixxx/src/library/basesqltablemodel.cpp 2012-03-12 04:25:21 +0000
604@@ -628,7 +628,7 @@
605 // QVariant::toFloat needs >= QT 4.6.x
606 pTrack->setBpm(static_cast<float>(value.toDouble()));
607 } else if (fieldIndex(LIBRARYTABLE_PLAYED) == column) {
608- pTrack->setPlayed(value.toBool());
609+ pTrack->setPlayedAndUpdatePlaycount(value.toBool());
610 } else if (fieldIndex(LIBRARYTABLE_TIMESPLAYED) == column) {
611 pTrack->setTimesPlayed(value.toInt());
612 } else if (fieldIndex(LIBRARYTABLE_RATING) == column) {
613
614=== modified file 'mixxx/src/library/browse/browsetablemodel.cpp'
615--- mixxx/src/library/browse/browsetablemodel.cpp 2011-12-28 20:30:15 +0000
616+++ mixxx/src/library/browse/browsetablemodel.cpp 2012-03-12 04:25:21 +0000
617@@ -22,7 +22,7 @@
618 "mixxx.db.model.browse"),
619 QStandardItemModel(parent),
620 m_pTrackCollection(pTrackCollection),
621- m_pRecordingManager(pRecordingManager) {
622+ m_pRecordingManager(pRecordingManager) {
623 QStringList header_data;
624 header_data.insert(COLUMN_FILENAME, tr("Filename"));
625 header_data.insert(COLUMN_ARTIST, tr("Artist"));
626@@ -61,12 +61,9 @@
627 connect(BrowseThread::getInstance(), SIGNAL(rowsAppended(const QList< QList<QStandardItem*> >&, BrowseTableModel*)),
628 this, SLOT(slotInsert(const QList< QList<QStandardItem*> >&, BrowseTableModel*)),
629 Qt::QueuedConnection);
630-
631 }
632
633-BrowseTableModel::~BrowseTableModel()
634-{
635-
636+BrowseTableModel::~BrowseTableModel() {
637 }
638
639 const QList<int>& BrowseTableModel::searchColumns() const {
640
641=== modified file 'mixxx/src/library/cratefeature.cpp'
642--- mixxx/src/library/cratefeature.cpp 2011-12-09 02:49:02 +0000
643+++ mixxx/src/library/cratefeature.cpp 2012-03-12 04:25:21 +0000
644@@ -9,6 +9,7 @@
645 #include "library/parser.h"
646 #include "library/parserm3u.h"
647 #include "library/parserpls.h"
648+#include "library/parsercsv.h"
649
650 #include "library/cratetablemodel.h"
651 #include "library/trackcollection.h"
652@@ -22,9 +23,11 @@
653 CrateFeature::CrateFeature(QObject* parent,
654 TrackCollection* pTrackCollection, ConfigObject<ConfigValue>* pConfig)
655 : m_pTrackCollection(pTrackCollection),
656+ m_crateDao(pTrackCollection->getCrateDAO()),
657 m_crateListTableModel(this, pTrackCollection->getDatabase()),
658- m_pConfig(pConfig),
659- m_crateTableModel(this, pTrackCollection) {
660+ m_crateTableModel(this, pTrackCollection),
661+ m_pConfig(pConfig) {
662+ Q_UNUSED(parent);
663 m_pCreateCrateAction = new QAction(tr("New Crate"),this);
664 connect(m_pCreateCrateAction, SIGNAL(triggered()),
665 this, SLOT(slotCreateCrate()));
666@@ -48,6 +51,19 @@
667 connect(m_pExportPlaylistAction, SIGNAL(triggered()),
668 this, SLOT(slotExportPlaylist()));
669
670+ connect(&m_crateDao, SIGNAL(added(int)),
671+ this, SLOT(slotCrateTableChanged(int)));
672+
673+ connect(&m_crateDao, SIGNAL(deleted(int)),
674+ this, SLOT(slotCrateTableChanged(int)));
675+
676+ connect(&m_crateDao, SIGNAL(renamed(int)),
677+ this, SLOT(slotCrateTableChanged(int)));
678+
679+ connect(&m_crateDao, SIGNAL(lockChanged(int)),
680+ this, SLOT(slotCrateTableChanged(int)));
681+
682+
683 m_crateListTableModel.setTable("crates");
684 m_crateListTableModel.setSort(m_crateListTableModel.fieldIndex("name"),
685 Qt::AscendingOrder);
686@@ -57,7 +73,7 @@
687 // construct child model
688 TreeItem *rootItem = new TreeItem();
689 m_childModel.setRootItem(rootItem);
690- constructChildModel();
691+ constructChildModel(-1);
692 }
693
694 CrateFeature::~CrateFeature() {
695@@ -78,12 +94,13 @@
696 }
697
698 bool CrateFeature::dropAccept(QUrl url) {
699+ Q_UNUSED(url)
700 return false;
701 }
702
703 bool CrateFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {
704 QString crateName = index.data().toString();
705- int crateId = m_pTrackCollection->getCrateDAO().getCrateIdByName(crateName);
706+ int crateId = m_crateDao.getCrateIdByName(crateName);
707
708 //XXX: See the comment in PlaylistFeature::dropAcceptChild() about
709 // QUrl::toLocalFile() vs. QUrl::toString() usage.
710@@ -95,10 +112,8 @@
711 qDebug() << "CrateFeature::dropAcceptChild adding track"
712 << trackId << "to crate" << crateId;
713
714- CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();
715-
716 if (trackId >= 0)
717- return crateDao.addTrackToCrate(trackId, crateId);
718+ return m_crateDao.addTrackToCrate(trackId, crateId);
719 return false;
720 }
721
722@@ -110,9 +125,8 @@
723 bool CrateFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) {
724 //TODO: Filter by supported formats regex and reject anything that doesn't match.
725 QString crateName = index.data().toString();
726- CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();
727- int crateId = crateDao.getCrateIdByName(crateName);
728- bool locked = crateDao.isCrateLocked(crateId);
729+ int crateId = m_crateDao.getCrateIdByName(crateName);
730+ bool locked = m_crateDao.isCrateLocked(crateId);
731
732 QFileInfo file(url.toLocalFile());
733 bool formatSupported = SoundSourceProxy::isFilenameSupported(file.fileName());
734@@ -143,7 +157,7 @@
735 if (!index.isValid())
736 return;
737 QString crateName = index.data().toString();
738- int crateId = m_pTrackCollection->getCrateDAO().getCrateIdByName(crateName);
739+ int crateId = m_crateDao.getCrateIdByName(crateName);
740 m_crateTableModel.setCrate(crateId);
741 emit(showTrackModel(&m_crateTableModel));
742 }
743@@ -160,10 +174,9 @@
744 m_lastRightClickedIndex = index;
745
746 QString crateName = index.data().toString();
747- CrateDAO& crateDAO = m_pTrackCollection->getCrateDAO();
748- int crateId = crateDAO.getCrateIdByName(crateName);
749+ int crateId = m_crateDao.getCrateIdByName(crateName);
750
751- bool locked = crateDAO.isCrateLocked(crateId);
752+ bool locked = m_crateDao.isCrateLocked(crateId);
753
754 m_pDeleteCrateAction->setEnabled(!locked);
755 m_pRenameCrateAction->setEnabled(!locked);
756@@ -186,7 +199,6 @@
757
758 QString name;
759 bool validNameGiven = false;
760- CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();
761
762 do {
763 bool ok = false;
764@@ -199,7 +211,7 @@
765 if (!ok)
766 return;
767
768- int existingId = crateDao.getCrateIdByName(name);
769+ int existingId = m_crateDao.getCrateIdByName(name);
770
771 if (existingId != -1) {
772 QMessageBox::warning(NULL,
773@@ -217,17 +229,10 @@
774
775 } while (!validNameGiven);
776
777- bool crateCreated = crateDao.createCrate(name);
778+ int crate_id = m_crateDao.createCrate(name);
779
780- if (crateCreated) {
781- clearChildModel();
782- m_crateListTableModel.select();
783- constructChildModel();
784- // Switch to the new crate.
785- int crate_id = crateDao.getCrateIdByName(name);
786- m_crateTableModel.setCrate(crate_id);
787+ if (crate_id != -1) {
788 emit(showTrackModel(&m_crateTableModel));
789- // TODO(XXX) set sidebar selection
790 emit(featureUpdated());
791 } else {
792 qDebug() << "Error creating crate with name " << name;
793@@ -241,22 +246,19 @@
794
795 void CrateFeature::slotDeleteCrate() {
796 QString crateName = m_lastRightClickedIndex.data().toString();
797- CrateDAO &crateDao = m_pTrackCollection->getCrateDAO();
798- int crateId = crateDao.getCrateIdByName(crateName);
799- bool locked = crateDao.isCrateLocked(crateId);
800+ int crateId = m_crateDao.getCrateIdByName(crateName);
801+ bool locked = m_crateDao.isCrateLocked(crateId);
802
803 if (locked) {
804 qDebug() << "Skipping crate deletion because crate" << crateId << "is locked.";
805 return;
806 }
807
808- bool deleted = crateDao.deleteCrate(crateId);
809+ bool deleted = m_crateDao.deleteCrate(crateId);
810
811 if (deleted) {
812- clearChildModel();
813- m_crateListTableModel.select();
814- constructChildModel();
815 emit(featureUpdated());
816+ activate();
817 } else {
818 qDebug() << "Failed to delete crateId" << crateId;
819 }
820@@ -264,9 +266,8 @@
821
822 void CrateFeature::slotRenameCrate() {
823 QString oldName = m_lastRightClickedIndex.data().toString();
824- CrateDAO &crateDao = m_pTrackCollection->getCrateDAO();
825- int crateId = crateDao.getCrateIdByName(oldName);
826- bool locked = crateDao.isCrateLocked(crateId);
827+ int crateId = m_crateDao.getCrateIdByName(oldName);
828+ bool locked = m_crateDao.isCrateLocked(crateId);
829
830 if (locked) {
831 qDebug() << "Skipping crate rename because crate" << crateId << "is locked.";
832@@ -289,7 +290,7 @@
833 return;
834 }
835
836- int existingId = m_pTrackCollection->getCrateDAO().getCrateIdByName(newName);
837+ int existingId = m_crateDao.getCrateIdByName(newName);
838
839 if (existingId != -1) {
840 QMessageBox::warning(NULL,
841@@ -307,12 +308,8 @@
842 } while (!validNameGiven);
843
844
845- if (m_pTrackCollection->getCrateDAO().renameCrate(crateId, newName)) {
846- clearChildModel();
847- m_crateListTableModel.select();
848- constructChildModel();
849+ if (m_crateDao.renameCrate(crateId, newName)) {
850 emit(featureUpdated());
851- m_crateTableModel.setCrate(crateId);
852 } else {
853 qDebug() << "Failed to rename crateId" << crateId;
854 }
855@@ -321,17 +318,12 @@
856 void CrateFeature::slotToggleCrateLock()
857 {
858 QString crateName = m_lastRightClickedIndex.data().toString();
859- CrateDAO& crateDAO = m_pTrackCollection->getCrateDAO();
860- int crateId = crateDAO.getCrateIdByName(crateName);
861- bool locked = !crateDAO.isCrateLocked(crateId);
862+ int crateId = m_crateDao.getCrateIdByName(crateName);
863+ bool locked = !m_crateDao.isCrateLocked(crateId);
864
865- if (!crateDAO.setCrateLocked(crateId, locked)) {
866+ if (!m_crateDao.setCrateLocked(crateId, locked)) {
867 qDebug() << "Failed to toggle lock of crateId " << crateId;
868 }
869-
870- TreeItem* crateItem = m_childModel.getItem(m_lastRightClickedIndex);
871- crateItem->setIcon(
872- locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
873 }
874
875
876@@ -340,14 +332,14 @@
877 * we require the sidebar model not to reset.
878 * This method queries the database and does dynamic insertion
879 */
880-void CrateFeature::constructChildModel()
881+QModelIndex CrateFeature::constructChildModel(int selected_id)
882 {
883 QList<TreeItem*> data_list;
884 int nameColumn = m_crateListTableModel.record().indexOf("name");
885 int idColumn = m_crateListTableModel.record().indexOf("id");
886- //Access the invisible root item
887+ int selected_row = -1;
888+ // Access the invisible root item
889 TreeItem* root = m_childModel.getItem(QModelIndex());
890- CrateDAO &crateDao = m_pTrackCollection->getCrateDAO();
891
892 for (int row = 0; row < m_crateListTableModel.rowCount(); ++row) {
893 QModelIndex ind = m_crateListTableModel.index(row, nameColumn);
894@@ -355,14 +347,23 @@
895 ind = m_crateListTableModel.index(row, idColumn);
896 int crate_id = m_crateListTableModel.data(ind).toInt();
897
898- //Create the TreeItem whose parent is the invisible root item
899+ if (selected_id == crate_id) {
900+ // save index for selection
901+ selected_row = row; m_childModel.index(selected_row, 0);
902+ }
903+
904+ // Create the TreeItem whose parent is the invisible root item
905 TreeItem* item = new TreeItem(crate_name, crate_name, this, root);
906- bool locked = crateDao.isCrateLocked(crate_id);
907+ bool locked = m_crateDao.isCrateLocked(crate_id);
908 item->setIcon(locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
909 data_list.append(item);
910 }
911- //Append all the newly created TreeItems in a dynamic way to the childmodel
912+ // Append all the newly created TreeItems in a dynamic way to the childmodel
913 m_childModel.insertRows(data_list, 0, m_crateListTableModel.rowCount());
914+ if (selected_row == -1) {
915+ return QModelIndex();
916+ }
917+ return m_childModel.index(selected_row, 0);
918 }
919
920 /**
921@@ -382,7 +383,7 @@
922 NULL,
923 tr("Import Playlist"),
924 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
925- tr("Playlist Files (*.m3u *.m3u8 *.pls)"));
926+ tr("Playlist Files (*.m3u *.m3u8 *.pls *.csv)"));
927 // Exit method if user cancelled the open dialog.
928 if (playlist_file.isNull() || playlist_file.isEmpty() ) return;
929
930@@ -394,6 +395,8 @@
931 playlist_parser = new ParserM3u();
932 } else if (playlist_file.endsWith(".pls", Qt::CaseInsensitive)) {
933 playlist_parser = new ParserPls();
934+ } else if (playlist_file.endsWith(".csv", Qt::CaseInsensitive)) {
935+ playlist_parser = new ParserCsv();
936 } else {
937 return;
938 }
939@@ -423,16 +426,17 @@
940 NULL,
941 tr("Export Crate"),
942 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
943- tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;PLS Playlist (*.pls)"));
944+ tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;PLS Playlist (*.pls);;Text CSV (*.csv);;Readable Text (*.txt)"));
945 // Exit method if user cancelled the open dialog.
946- if (file_location.isNull() || file_location.isEmpty())
947+ if (file_location.isNull() || file_location.isEmpty()) {
948 return;
949+ }
950 // check config if relative paths are desired
951 bool useRelativePath = static_cast<bool>(
952 m_pConfig->getValueString(
953 ConfigKey("[Library]", "UseRelativePathOnExport")).toInt());
954
955- // Create and populate a list of files of the crate
956+ // Create list of files of the crate
957 QList<QString> playlist_items;
958 // Create a new table model since the main one might have an active search.
959 QScopedPointer<CrateTableModel> pCrateTableModel(
960@@ -440,24 +444,44 @@
961 pCrateTableModel->setCrate(m_crateTableModel.getCrate());
962 pCrateTableModel->select();
963 int rows = pCrateTableModel->rowCount();
964- for (int i = 0; i < rows; ++i) {
965- QModelIndex index = pCrateTableModel->index(i, 0);
966- playlist_items << pCrateTableModel->getTrackLocation(index);
967- }
968-
969- if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {
970- ParserPls::writePLSFile(file_location, playlist_items, useRelativePath);
971- } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {
972- ParserM3u::writeM3U8File(file_location, playlist_items,
973- useRelativePath);
974- } else {
975- // Default export to M3U if file extension is missing
976- if (!file_location.endsWith(".m3u", Qt::CaseInsensitive)) {
977- qDebug() << "Crate export: No valid file extension specified. Appending .m3u "
978- << "and exporting to M3U.";
979- file_location.append(".m3u");
980- }
981- ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);
982- }
983-}
984-
985+
986+ if (file_location.endsWith(".csv", Qt::CaseInsensitive)) {
987+ ParserCsv::writeCSVFile(file_location, pCrateTableModel.data(), useRelativePath);
988+ } else if (file_location.endsWith(".txt", Qt::CaseInsensitive)) {
989+ ParserCsv::writeReadableTextFile(file_location, pCrateTableModel.data());
990+ } else{
991+ // populate a list of files of the crate
992+ QList<QString> playlist_items;
993+ int rows = pCrateTableModel->rowCount();
994+ for (int i = 0; i < rows; ++i) {
995+ QModelIndex index = m_crateTableModel.index(i, 0);
996+ playlist_items << m_crateTableModel.getTrackLocation(index);
997+ }
998+
999+ if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {
1000+ ParserPls::writePLSFile(file_location, playlist_items, useRelativePath);
1001+ } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {
1002+ ParserM3u::writeM3U8File(file_location, playlist_items, useRelativePath);
1003+ } else {
1004+ //default export to M3U if file extension is missing
1005+ if(!file_location.endsWith(".m3u", Qt::CaseInsensitive))
1006+ {
1007+ qDebug() << "Crate export: No valid file extension specified. Appending .m3u "
1008+ << "and exporting to M3U.";
1009+ file_location.append(".m3u");
1010+ }
1011+ ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);
1012+ }
1013+ }
1014+}
1015+
1016+void CrateFeature::slotCrateTableChanged(int crateId) {
1017+ //qDebug() << "slotPlaylistTableChanged() playlistId:" << playlistId;
1018+ clearChildModel();
1019+ m_crateListTableModel.select();
1020+ m_lastRightClickedIndex = constructChildModel(crateId);
1021+ // Switch the view to the crate.
1022+ m_crateTableModel.setCrate(crateId);
1023+ // Update selection
1024+ emit(featureSelect(this, m_lastRightClickedIndex));
1025+}
1026
1027=== modified file 'mixxx/src/library/cratefeature.h'
1028--- mixxx/src/library/cratefeature.h 2011-03-12 13:10:25 +0000
1029+++ mixxx/src/library/cratefeature.h 2012-03-12 04:25:21 +0000
1030@@ -48,12 +48,14 @@
1031 void slotToggleCrateLock();
1032 void slotImportPlaylist();
1033 void slotExportPlaylist();
1034+ void slotCrateTableChanged(int playlistId);
1035
1036 private:
1037- void constructChildModel();
1038+ QModelIndex constructChildModel(int selected_id);
1039 void clearChildModel();
1040-
1041+
1042 TrackCollection* m_pTrackCollection;
1043+ CrateDAO& m_crateDao;
1044 QAction *m_pCreateCrateAction;
1045 QAction *m_pDeleteCrateAction;
1046 QAction *m_pRenameCrateAction;
1047
1048=== modified file 'mixxx/src/library/dao/cratedao.cpp'
1049--- mixxx/src/library/dao/cratedao.cpp 2011-12-18 20:23:14 +0000
1050+++ mixxx/src/library/dao/cratedao.cpp 2012-03-12 04:25:21 +0000
1051@@ -31,19 +31,19 @@
1052 return query.value(0).toInt();
1053 }
1054
1055-bool CrateDAO::createCrate(const QString& name) {
1056+int CrateDAO::createCrate(const QString& name) {
1057 QSqlQuery query(m_database);
1058 query.prepare("INSERT INTO " CRATE_TABLE " (name) VALUES (:name)");
1059 query.bindValue(":name", name);
1060
1061 if (!query.exec()) {
1062 LOG_FAILED_QUERY(query);
1063- return false;
1064+ return -1;
1065 }
1066
1067 int crateId = query.lastInsertId().toInt();
1068 emit(added(crateId));
1069- return true;
1070+ return crateId;
1071 }
1072
1073 bool CrateDAO::renameCrate(int crateId, const QString& newName) {
1074@@ -56,6 +56,7 @@
1075 LOG_FAILED_QUERY(query);
1076 return false;
1077 }
1078+ emit(renamed(crateId));
1079 return true;
1080 }
1081
1082@@ -71,7 +72,7 @@
1083 LOG_FAILED_QUERY(query);
1084 return false;
1085 }
1086-
1087+ emit(lockChanged(crateId));
1088 return true;
1089 }
1090
1091
1092=== modified file 'mixxx/src/library/dao/cratedao.h'
1093--- mixxx/src/library/dao/cratedao.h 2011-10-21 00:42:23 +0000
1094+++ mixxx/src/library/dao/cratedao.h 2012-03-12 04:25:21 +0000
1095@@ -28,7 +28,7 @@
1096 void initialize();
1097
1098 unsigned int crateCount();
1099- bool createCrate(const QString& name);
1100+ int createCrate(const QString& name);
1101 bool deleteCrate(int crateId);
1102 bool renameCrate(int crateId, const QString& newName);
1103 bool setCrateLocked(int crateId, bool locked);
1104@@ -47,6 +47,8 @@
1105 void changed(int crateId);
1106 void trackAdded(int crateId, int trackId);
1107 void trackRemoved(int crateId, int trackId);
1108+ void renamed(int crateId);
1109+ void lockChanged(int crateId);
1110
1111 private:
1112 QSqlDatabase& m_database;
1113@@ -54,4 +56,3 @@
1114 };
1115
1116 #endif /* CRATEDAO_H */
1117-
1118
1119=== modified file 'mixxx/src/library/dao/playlistdao.cpp'
1120--- mixxx/src/library/dao/playlistdao.cpp 2011-12-18 20:23:14 +0000
1121+++ mixxx/src/library/dao/playlistdao.cpp 2012-03-12 04:25:21 +0000
1122@@ -1,4 +1,3 @@
1123-
1124 #include <QtDebug>
1125 #include <QtCore>
1126 #include <QtSql>
1127@@ -19,10 +18,7 @@
1128 {
1129 }
1130
1131-/** Create a playlist with the given name.
1132- @param name The name of the playlist to be created.
1133-*/
1134-bool PlaylistDAO::createPlaylist(QString name, bool hidden)
1135+int PlaylistDAO::createPlaylist(QString name, HiddenType hidden)
1136 {
1137 // qDebug() << "PlaylistDAO::createPlaylist"
1138 // << QThread::currentThread()
1139@@ -38,7 +34,7 @@
1140 if (!query.exec()) {
1141 LOG_FAILED_QUERY(query);
1142 m_database.rollback();
1143- return false;
1144+ return -1;
1145 }
1146
1147 //Get the id of the last playlist.
1148@@ -50,34 +46,33 @@
1149
1150 //qDebug() << "Inserting playlist" << name << "at position" << position;
1151
1152- query.prepare("INSERT INTO Playlists (name, position, hidden) "
1153- "VALUES (:name, :position, :hidden)");
1154+ query.prepare("INSERT INTO Playlists (name, position, hidden, date_created, date_modified) "
1155+ "VALUES (:name, :position, :hidden, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)");
1156 query.bindValue(":name", name);
1157 query.bindValue(":position", position);
1158- query.bindValue(":hidden", hidden ? 1 : 0);
1159+ query.bindValue(":hidden", static_cast<int>(hidden));
1160
1161 if (!query.exec()) {
1162 LOG_FAILED_QUERY(query);
1163 m_database.rollback();
1164- return false;
1165+ return -1;
1166 }
1167
1168 int playlistId = query.lastInsertId().toInt();
1169 //Commit the transaction
1170 m_database.commit();
1171 emit(added(playlistId));
1172- return true;
1173+ return playlistId;
1174 }
1175
1176-/** Find out the name of the playlist at the given position */
1177-QString PlaylistDAO::getPlaylistName(unsigned int position)
1178+QString PlaylistDAO::getPlaylistName(int playlistId)
1179 {
1180 // qDebug() << "PlaylistDAO::getPlaylistName" << QThread::currentThread() << m_database.connectionName();
1181
1182 QSqlQuery query(m_database);
1183 query.prepare("SELECT name FROM Playlists "
1184- "WHERE position = :position");
1185- query.bindValue(":position", position);
1186+ "WHERE id= :id");
1187+ query.bindValue(":id", playlistId);
1188
1189 if (!query.exec()) {
1190 LOG_FAILED_QUERY(query);
1191@@ -108,8 +103,6 @@
1192 return -1;
1193 }
1194
1195-
1196-/** Delete a playlist */
1197 void PlaylistDAO::deletePlaylist(int playlistId)
1198 {
1199 // qDebug() << "PlaylistDAO::deletePlaylist" << QThread::currentThread() << m_database.connectionName();
1200@@ -144,7 +137,6 @@
1201 emit(deleted(playlistId));
1202 }
1203
1204-
1205 void PlaylistDAO::renamePlaylist(int playlistId, const QString& newName) {
1206 QSqlQuery query(m_database);
1207 query.prepare("UPDATE Playlists SET name = :name WHERE id = :id");
1208@@ -152,23 +144,23 @@
1209 query.bindValue(":id", playlistId);
1210 if (!query.exec()) {
1211 LOG_FAILED_QUERY(query);
1212+ return;
1213 }
1214+ emit(renamed(playlistId));
1215 }
1216
1217-
1218 bool PlaylistDAO::setPlaylistLocked(int playlistId, bool locked) {
1219+ QSqlQuery query(m_database);
1220+ query.prepare("UPDATE Playlists SET locked = :lock WHERE id = :id");
1221 // SQLite3 doesn't support boolean value. Using integer instead.
1222- int lock = locked ? 1 : 0;
1223-
1224- QSqlQuery query(m_database);
1225- query.prepare("UPDATE Playlists SET locked = :lock WHERE id = :id");
1226- query.bindValue(":lock", lock);
1227+ query.bindValue(":lock", static_cast<int>(locked));
1228 query.bindValue(":id", playlistId);
1229
1230 if (!query.exec()) {
1231 LOG_FAILED_QUERY(query);
1232 return false;
1233 }
1234+ emit(lockChanged(playlistId));
1235 return true;
1236 }
1237
1238@@ -188,10 +180,8 @@
1239 return false;
1240 }
1241
1242-/** Append a track to a playlist */
1243-void PlaylistDAO::appendTrackToPlaylist(int trackId, int playlistId)
1244-{
1245- // qDebug() << "PlaylistDAO::appendTrackToPlaylist"
1246+void PlaylistDAO::appendTracksToPlaylist(QList<int> trackIds, int playlistId) {
1247+ // qDebug() << "PlaylistDAO::appendTracksToPlaylist"
1248 // << QThread::currentThread() << m_database.connectionName();
1249
1250 // Start the transaction
1251@@ -207,32 +197,44 @@
1252 LOG_FAILED_QUERY(query);
1253 }
1254
1255- // Get the position of the highest playlist...
1256+ // Get the position of the highest track in the playlist.
1257 int position = 0;
1258 if (query.next()) {
1259 position = query.value(query.record().indexOf("position")).toInt();
1260 }
1261- position++; //Append after the last song.
1262+ // Append after the last song. If no songs or a failed query then 0 becomes
1263+ // 1.
1264+ position++;
1265
1266 //Insert the song into the PlaylistTracks table
1267 query.prepare("INSERT INTO PlaylistTracks (playlist_id, track_id, position)"
1268 "VALUES (:playlist_id, :track_id, :position)");
1269 query.bindValue(":playlist_id", playlistId);
1270- query.bindValue(":track_id", trackId);
1271- query.bindValue(":position", position);
1272-
1273- if (!query.exec()) {
1274- LOG_FAILED_QUERY(query);
1275+
1276+
1277+ foreach (int trackId, trackIds) {
1278+ query.bindValue(":track_id", trackId);
1279+ query.bindValue(":position", position++);
1280+ if (!query.exec()) {
1281+ LOG_FAILED_QUERY(query);
1282+ }
1283 }
1284
1285 // Commit the transaction
1286 m_database.commit();
1287
1288- emit(trackAdded(playlistId, trackId, position));
1289+ foreach (int trackId, trackIds) {
1290+ emit(trackAdded(playlistId, trackId, position));
1291+ }
1292 emit(changed(playlistId));
1293 }
1294
1295-/** Find out how many playlists exist. */
1296+void PlaylistDAO::appendTrackToPlaylist(int trackId, int playlistId) {
1297+ QList<int> tracks;
1298+ tracks.append(trackId);
1299+ appendTracksToPlaylist(tracks, playlistId);
1300+}
1301+
1302 unsigned int PlaylistDAO::playlistCount()
1303 {
1304 // qDebug() << "PlaylistDAO::playlistCount" << QThread::currentThread() << m_database.connectionName();
1305@@ -249,7 +251,7 @@
1306 return numRecords;
1307 }
1308
1309-int PlaylistDAO::getPlaylistId(int position)
1310+int PlaylistDAO::getPlaylistId(int index)
1311 {
1312 // qDebug() << "PlaylistDAO::getPlaylistId"
1313 // << QThread::currentThread() << m_database.connectionName();
1314@@ -257,37 +259,50 @@
1315 QSqlQuery query(m_database);
1316 query.prepare("SELECT id FROM Playlists");
1317
1318+ if (!query.exec()) {
1319+ LOG_FAILED_QUERY(query);
1320+ return -1;
1321+ }
1322+
1323+ int currentRow = 0;
1324+ while(query.next()) {
1325+ if (currentRow++ == index) {
1326+ int id = query.value(0).toInt();
1327+ return id;
1328+ }
1329+ }
1330+ return -1;
1331+}
1332+
1333+PlaylistDAO::HiddenType PlaylistDAO::getHiddenType(int playlistId) {
1334+ // qDebug() << "PlaylistDAO::getHiddenType"
1335+ // << QThread::currentThread() << m_database.connectionName();
1336+
1337+ QSqlQuery query(m_database);
1338+ query.prepare("SELECT hidden FROM Playlists WHERE id = :id");
1339+ query.bindValue(":id", playlistId);
1340+
1341 if (query.exec()) {
1342- int currentRow = 0;
1343- while(query.next()) {
1344- if (currentRow++ == position) {
1345- int id = query.value(0).toInt();
1346- return id;
1347- }
1348+ if (query.next()) {
1349+ return static_cast<HiddenType>(query.value(0).toInt());
1350 }
1351 } else {
1352 LOG_FAILED_QUERY(query);
1353 }
1354-
1355- return -1;
1356+ qDebug() << "PlaylistDAO::getHiddenType returns PLHT_UNKNOWN for playlistId "
1357+ << playlistId;
1358+ return PLHT_UNKNOWN;
1359 }
1360
1361 bool PlaylistDAO::isHidden(int playlistId) {
1362 // qDebug() << "PlaylistDAO::isHidden"
1363 // << QThread::currentThread() << m_database.connectionName();
1364
1365- QSqlQuery query(m_database);
1366- query.prepare("SELECT hidden FROM Playlists WHERE id = :id");
1367- query.bindValue(":id", playlistId);
1368-
1369- if (query.exec()) {
1370- if (query.next()) {
1371- return query.value(0).toBool();
1372- }
1373- } else {
1374- LOG_FAILED_QUERY(query);
1375+ HiddenType ht = getHiddenType(playlistId);
1376+ if (ht == PLHT_NOT_HIDDEN) {
1377+ return false;
1378 }
1379- return false;
1380+ return true;
1381 }
1382
1383 void PlaylistDAO::removeTrackFromPlaylists(int trackId) {
1384@@ -398,7 +413,7 @@
1385 emit(changed(playlistId));
1386 }
1387
1388-void PlaylistDAO::addToAutoDJQueue(int playlistId) {
1389+void PlaylistDAO::addToAutoDJQueue(int playlistId, bool bTop) {
1390 //qDebug() << "Adding tracks from playlist " << playlistId << " to the Auto-DJ Queue";
1391
1392 // Query the PlaylistTracks database to locate tracks in the selected playlist
1393@@ -408,12 +423,63 @@
1394 query.bindValue(":plid", playlistId);
1395 if (!query.exec()) {
1396 LOG_FAILED_QUERY(query);
1397+ return;
1398 }
1399
1400 // Get the ID of the Auto-DJ playlist
1401 int autoDJId = getPlaylistIdFromName(AUTODJ_TABLE);
1402- // Loop through the tracks, adding them to the Auto-DJ Queue
1403- while(query.next()) {
1404- appendTrackToPlaylist(query.value(0).toInt(), autoDJId);
1405- }
1406+
1407+ // Loop through the tracks, adding them to the Auto-DJ Queue. Start at
1408+ // position 2 because position 1 was already loaded to the deck
1409+ int i = 2;
1410+
1411+ while (query.next()) {
1412+ if (bTop) {
1413+ insertTrackIntoPlaylist(query.value(0).toInt(), autoDJId, i++);
1414+ }
1415+ else {
1416+ appendTrackToPlaylist(query.value(0).toInt(), autoDJId);
1417+ }
1418+ }
1419+}
1420+
1421+int PlaylistDAO::getPreviousPlaylist(int currentPlaylistId, HiddenType hidden) {
1422+ // Find out the highest position existing in the playlist so we know what
1423+ // position this track should have.
1424+ QSqlQuery query(m_database);
1425+ query.prepare("SELECT max(id) as id FROM Playlists "
1426+ "WHERE id < :id AND hidden = :hidden");
1427+ query.bindValue(":id", currentPlaylistId);
1428+ query.bindValue(":hidden", hidden);
1429+
1430+ if (!query.exec()) {
1431+ LOG_FAILED_QUERY(query);
1432+ return -1;
1433+ }
1434+
1435+ // Get the id of the highest playlist
1436+ int previousPlaylistId = -1;
1437+ if (query.next()) {
1438+ previousPlaylistId = query.value(query.record().indexOf("id")).toInt();
1439+ }
1440+ return previousPlaylistId;
1441+}
1442+
1443+void PlaylistDAO::copyPlaylistTracks(int sourcePlaylistID, int targetPlaylistId) {
1444+ // Query Tracks from the source Playlist
1445+ QSqlQuery query(m_database);
1446+ query.prepare("SELECT track_id FROM PlaylistTracks "
1447+ "WHERE playlist_id = :plid");
1448+ query.bindValue(":plid", sourcePlaylistID);
1449+
1450+ if (!query.exec()) {
1451+ LOG_FAILED_QUERY(query);
1452+ return;
1453+ }
1454+
1455+ QList<int> trackIds;
1456+ while (query.next()) {
1457+ trackIds.append(query.value(0).toInt());
1458+ }
1459+ appendTracksToPlaylist(trackIds, targetPlaylistId);
1460 }
1461
1462=== modified file 'mixxx/src/library/dao/playlistdao.h'
1463--- mixxx/src/library/dao/playlistdao.h 2011-10-21 00:42:23 +0000
1464+++ mixxx/src/library/dao/playlistdao.h 2012-03-12 04:25:21 +0000
1465@@ -13,17 +13,27 @@
1466 const QString PLAYLISTTRACKSTABLE_TRACKID = "track_id";
1467 const QString PLAYLISTTRACKSTABLE_POSITION = "position";
1468 const QString PLAYLISTTRACKSTABLE_PLAYLISTID = "playlist_id";
1469+const QString PLAYLISTTRACKSTABLE_LOCATION = "location";
1470+const QString PLAYLISTTRACKSTABLE_ARTIST = "artist";
1471+const QString PLAYLISTTRACKSTABLE_TITLE = "title";
1472
1473 class PlaylistDAO : public QObject, public virtual DAO {
1474 Q_OBJECT
1475 public:
1476+ enum HiddenType {
1477+ PLHT_NOT_HIDDEN = 0,
1478+ PLHT_AUTO_DJ = 1,
1479+ PLHT_SET_LOG = 2,
1480+ PLHT_UNKNOWN = -1
1481+ };
1482+
1483 PlaylistDAO(QSqlDatabase& database);
1484 virtual ~PlaylistDAO();
1485
1486 void initialize();
1487 void setDatabase(QSqlDatabase& database) { m_database = database; };
1488 /** Create a playlist */
1489- bool createPlaylist(QString name, bool hidden = false);
1490+ int createPlaylist(QString name, HiddenType type = PLHT_NOT_HIDDEN);
1491 /** Delete a playlist */
1492 void deletePlaylist(int playlistId);
1493 /** Rename a playlist */
1494@@ -32,20 +42,24 @@
1495 bool setPlaylistLocked(int playlistId, bool locked);
1496 /** Find out the state of a playlist lock */
1497 bool isPlaylistLocked(int playlistId);
1498- /** Append a track to a playlist */
1499+ /** Append a list of tracks to a playlist */
1500+ void appendTracksToPlaylist(QList<int> trackIds, int playlistId);
1501+ /** Append a track to a playlist */
1502 void appendTrackToPlaylist(int trackId, int playlistId);
1503 /** Find out how many playlists exist. */
1504 unsigned int playlistCount();
1505- /** Get the name of the playlist at the given position */
1506- QString getPlaylistName(unsigned int position);
1507+ /** Find out the name of the playlist at the given Id */
1508+ QString getPlaylistName(int playlistId);
1509 // Get the playlist id by its name
1510 int getPlaylistIdFromName(QString name);
1511- /** Get the id of the playlist at position. Note that the position is the
1512- * natural position in the database table, not the display order position
1513- * column stored in the database. */
1514- int getPlaylistId(int position);
1515+ // Get the id of the playlist at index. Note that the index is the natural
1516+ // position in the database table, not the display order position column
1517+ // stored in the database.
1518+ int getPlaylistId(int index);
1519 // Returns true if the playlist with playlistId is hidden
1520 bool isHidden(int playlistId);
1521+ // Returns the HiddenType of playlistId
1522+ HiddenType getHiddenType(int playlistId);
1523 /** Remove a track from all playlists */
1524 void removeTrackFromPlaylists(int trackId);
1525 /** Remove a track from a playlist */
1526@@ -53,13 +67,20 @@
1527 /** Insert a track into a specific position in a playlist */
1528 void insertTrackIntoPlaylist(int trackId, int playlistId, int position);
1529 /** Add a playlist to the Auto-DJ Queue */
1530- void addToAutoDJQueue(int playlistId);
1531+ void addToAutoDJQueue(int playlistId, bool bTop);
1532+ // Get the preceding playlist of currentPlaylistId with the HiddenType
1533+ // hidden. Returns -1 if no such playlist exists.
1534+ int getPreviousPlaylist(int currentPlaylistId, HiddenType hidden);
1535+ // Append all the tracks in the source playlist to the target playlist.
1536+ void copyPlaylistTracks(int sourcePlaylistID, int targetPlaylistId);
1537 signals:
1538 void added(int playlistId);
1539 void deleted(int playlistId);
1540 void changed(int playlistId);
1541 void trackAdded(int playlistId, int trackId, int position);
1542 void trackRemoved(int playlistId, int trackId, int position);
1543+ void renamed(int playlistId);
1544+ void lockChanged(int playlistId);
1545 private:
1546 QSqlDatabase& m_database;
1547 DISALLOW_COPY_AND_ASSIGN(PlaylistDAO);
1548
1549=== modified file 'mixxx/src/library/legacylibraryimporter.cpp'
1550--- mixxx/src/library/legacylibraryimporter.cpp 2010-11-19 01:54:28 +0000
1551+++ mixxx/src/library/legacylibraryimporter.cpp 2012-03-12 04:25:21 +0000
1552@@ -148,8 +148,7 @@
1553
1554 //Create the playlist with the imported name.
1555 //qDebug() << "Importing playlist:" << current.name;
1556- m_playlistDao.createPlaylist(current.name, false);
1557- int playlistId = m_playlistDao.getPlaylistIdFromName(current.name);
1558+ int playlistId = m_playlistDao.createPlaylist(current.name);
1559
1560 //For each track ID in the XML...
1561 QList<int> trackIDs = current.indexes;
1562
1563=== modified file 'mixxx/src/library/library.cpp'
1564--- mixxx/src/library/library.cpp 2011-09-25 06:39:39 +0000
1565+++ mixxx/src/library/library.cpp 2012-03-12 04:25:21 +0000
1566@@ -21,6 +21,7 @@
1567 #include "library/promotracksfeature.h"
1568 #include "library/traktor/traktorfeature.h"
1569 #include "library/librarycontrol.h"
1570+#include "library/setlogfeature.h"
1571
1572 #include "widget/wtracktableview.h"
1573 #include "widget/wlibrary.h"
1574@@ -60,6 +61,7 @@
1575 addFeature(m_pCrateFeature);
1576 addFeature(new BrowseFeature(this, pConfig, m_pTrackCollection, m_pRecordingManager));
1577 addFeature(new RecordingFeature(this, pConfig, m_pTrackCollection, m_pRecordingManager));
1578+ addFeature(new SetlogFeature(this, pConfig, m_pTrackCollection));
1579 addFeature(new PrepareFeature(this, pConfig, m_pTrackCollection));
1580 //iTunes and Rhythmbox should be last until we no longer have an obnoxious
1581 //messagebox popup when you select them. (This forces you to reach for your
1582@@ -141,7 +143,6 @@
1583 ->select(m_pSidebarModel->getDefaultSelection(),
1584 QItemSelectionModel::SelectCurrent);
1585 m_pSidebarModel->activateDefaultSelection();
1586-
1587 }
1588
1589 void Library::addFeature(LibraryFeature* feature) {
1590
1591=== modified file 'mixxx/src/library/libraryfeature.h'
1592--- mixxx/src/library/libraryfeature.h 2011-03-10 14:06:26 +0000
1593+++ mixxx/src/library/libraryfeature.h 2012-03-12 04:25:21 +0000
1594@@ -66,7 +66,8 @@
1595 void featureIsLoading(LibraryFeature*);
1596 /** emit this signal if the foreign music collection has been imported/parsed. **/
1597 void featureLoadingFinished(LibraryFeature*s);
1598-
1599+ /** emit this signal to select pFeature **/
1600+ void featureSelect(LibraryFeature* pFeature, const QModelIndex& index);
1601
1602 };
1603
1604
1605=== modified file 'mixxx/src/library/parser.cpp'
1606--- mixxx/src/library/parser.cpp 2011-11-27 04:30:58 +0000
1607+++ mixxx/src/library/parser.cpp 2012-03-12 04:25:21 +0000
1608@@ -87,7 +87,7 @@
1609 }
1610
1611 const unsigned char* bytes = (const unsigned char *)string;
1612- while(*bytes) {
1613+ while (*bytes) {
1614 if( (// ASCII
1615 bytes[0] == 0x09 ||
1616 bytes[0] == 0x0A ||
1617
1618=== added file 'mixxx/src/library/parsercsv.cpp'
1619--- mixxx/src/library/parsercsv.cpp 1970-01-01 00:00:00 +0000
1620+++ mixxx/src/library/parsercsv.cpp 2012-03-12 04:25:21 +0000
1621@@ -0,0 +1,233 @@
1622+//
1623+// C++ Implementation: parsercsv
1624+//
1625+// Description: module to parse Comma-Separated Values (CSV) formated playlists (rfc4180)
1626+//
1627+//
1628+// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
1629+// Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011
1630+// Author: Daniel Schürmann daschuer@gmx.de, (C) 2011
1631+//
1632+// Copyright: See COPYING file that comes with this distribution
1633+//
1634+//
1635+#include <QTextStream>
1636+#include <QDebug>
1637+#include <QDir>
1638+#include <QMessageBox>
1639+#include <QUrl>
1640+
1641+#include "library/parsercsv.h"
1642+
1643+ParserCsv::ParserCsv() : Parser() {
1644+}
1645+
1646+ParserCsv::~ParserCsv() {
1647+}
1648+
1649+QList<QString> ParserCsv::parse(QString sFilename) {
1650+ QFile file(sFilename);
1651+ QString basepath = sFilename.section('/', 0, -2);
1652+
1653+ clearLocations();
1654+ //qDebug() << "ParserCsv: Starting to parse.";
1655+ if (file.open(QIODevice::ReadOnly) && !isBinary(sFilename)) {
1656+ QByteArray ba = file.readAll();
1657+
1658+ QList<QList<QString> > tokens = tokenize( ba, ',');
1659+
1660+ // detect Location column
1661+ int loc_coll = 0x7fffffff;
1662+ if (tokens.size()) {
1663+ for (int i = 0; i < tokens[0].size(); ++i) {
1664+ if (tokens[0][i] == tr("Location")) {
1665+ loc_coll = i;
1666+ break;
1667+ }
1668+ }
1669+ for (int i = 1; i < tokens.size(); ++i) {
1670+ if (loc_coll < tokens[i].size()) {
1671+ // Todo: check if path is relative
1672+ QFileInfo fi = tokens[i][loc_coll];
1673+ if (fi.isRelative()){
1674+ // add base path
1675+ qDebug() << "is relative" << basepath << fi.filePath();
1676+ fi.setFile(basepath,fi.filePath());
1677+ }
1678+ m_sLocations.append(fi.filePath());
1679+ }
1680+ }
1681+ }
1682+
1683+ file.close();
1684+
1685+ if(m_sLocations.count() != 0)
1686+ return m_sLocations;
1687+ else
1688+ return QList<QString>(); // NULL pointer returned when no locations were found
1689+
1690+ }
1691+
1692+ file.close();
1693+ return QList<QString>(); //if we get here something went wrong
1694+}
1695+
1696+// Code was posted at http://www.qtcentre.org/threads/35511-Parsing-CSV-data
1697+// by "adzajac" and adapted to use QT Classes
1698+QList<QList<QString> > ParserCsv::tokenize(const QByteArray& str, char delimiter) {
1699+ QList<QList<QString> > tokens;
1700+
1701+ unsigned int row = 0;
1702+ bool quotes = false;
1703+ QByteArray field = "";
1704+
1705+ tokens.append(QList<QString>());
1706+
1707+ for (int pos = 0; pos < str.length(); ++pos) {
1708+ char c = str[pos];
1709+ if (!quotes && c == '"' ){
1710+ quotes = true;
1711+ } else if (quotes && c== '"' ){
1712+ if (pos + 1 < str.length() && str[pos+1]== '"') {
1713+ field.append(c);
1714+ pos++;
1715+ } else {
1716+ quotes = false;
1717+ }
1718+ } else if (!quotes && c == delimiter) {
1719+ if (isUtf8(field.data())) {
1720+ tokens[row].append(QString::fromUtf8(field));
1721+ } else {
1722+ tokens[row].append(QString::fromLatin1(field));
1723+ }
1724+ field.clear();
1725+ } else if (!quotes && (c == '\r' || c == '\n')) {
1726+ if (isUtf8(field.data())) {
1727+ tokens[row].append(QString::fromUtf8(field));
1728+ } else {
1729+ tokens[row].append(QString::fromLatin1(field));
1730+ }
1731+ field.clear();
1732+ tokens.append(QList<QString>());
1733+ row++;
1734+ } else {
1735+ field.push_back(c);
1736+ }
1737+ }
1738+ return tokens;
1739+}
1740+
1741+bool ParserCsv::writeCSVFile(const QString &file_str, BaseSqlTableModel* pPlaylistTableModel, bool useRelativePath)
1742+{
1743+ /*
1744+ * Important note:
1745+ * On Windows \n will produce a <CR><CL> (=\r\n)
1746+ * On Linux and OS X \n is <CR> (which remains \n)
1747+ */
1748+
1749+ QFile file(file_str);
1750+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)){
1751+ QMessageBox::warning(NULL,tr("Playlist Export Failed"),
1752+ tr("Could not create file")+" "+file_str);
1753+ return false;
1754+ }
1755+ //Base folder of file
1756+ QString base = file_str.section('/', 0, -2);
1757+ QDir base_dir(base);
1758+
1759+ qDebug() << "Basepath: " << base;
1760+ QTextStream out(&file);
1761+ out.setCodec("UTF-8"); // rfc4180: Common usage of CSV is US-ASCII ...
1762+ // Using UTF-8 to get around codepage issues
1763+ // and it's the default encoding in Ooo Calc
1764+
1765+ // writing header section
1766+ bool first = true;
1767+ int columns = pPlaylistTableModel->columnCount();
1768+ for (int i = 0; i < columns; ++i) {
1769+ if (pPlaylistTableModel->isColumnInternal(i)) {
1770+ continue;
1771+ }
1772+ if (!first){
1773+ out << ",";
1774+ } else {
1775+ first = false;
1776+ }
1777+ out << "\"";
1778+ QString field = pPlaylistTableModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
1779+ out << field.replace('\"', "\"\""); // escape "
1780+ out << "\"";
1781+ }
1782+ out << "\r\n"; // CRLF according to rfc4180
1783+
1784+
1785+ int rows = pPlaylistTableModel->rowCount();
1786+ for (int j = 0; j < rows; j++) {
1787+ // writing fields section
1788+ first = true;
1789+ for(int i = 0; i < columns; ++i){
1790+ if (pPlaylistTableModel->isColumnInternal(i)) {
1791+ continue;
1792+ }
1793+ if (!first){
1794+ out << ",";
1795+ } else {
1796+ first = false;
1797+ }
1798+ out << "\"";
1799+ QString field = pPlaylistTableModel->data(pPlaylistTableModel->index(j,i)).toString();
1800+ if (useRelativePath && i == pPlaylistTableModel->fieldIndex(PLAYLISTTRACKSTABLE_LOCATION)) {
1801+ field = base_dir.relativeFilePath(field);
1802+ }
1803+ out << field.replace('\"', "\"\""); // escape "
1804+ out << "\"";
1805+ }
1806+ out << "\r\n"; // CRLF according to rfc4180
1807+ }
1808+ return true;
1809+}
1810+
1811+bool ParserCsv::writeReadableTextFile(const QString &file_str, BaseSqlTableModel* pPlaylistTableModel)
1812+{
1813+ /*
1814+ * Important note:
1815+ * On Windows \n will produce a <CR><CL> (=\r\n)
1816+ * On Linux and OS X \n is <CR> (which remains \n)
1817+ */
1818+
1819+ QFile file(file_str);
1820+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)){
1821+ QMessageBox::warning(NULL,tr("Readable text Export Failed"),
1822+ tr("Could not create file")+" "+file_str);
1823+ return false;
1824+ }
1825+
1826+ QTextStream out(&file);
1827+
1828+ // export each row as "01. 00:00 Artist - Title"
1829+
1830+ int i; // fieldIndex
1831+ int rows = pPlaylistTableModel->rowCount();
1832+ for (int j = 0; j < rows; j++) {
1833+ // writing fields section
1834+ i = pPlaylistTableModel->fieldIndex(PLAYLISTTRACKSTABLE_POSITION);
1835+ if (i >= 0){
1836+ int nr = pPlaylistTableModel->data(pPlaylistTableModel->index(j,i)).toInt();
1837+ out << QString("%1.").arg(nr,2,10,QLatin1Char('0'));
1838+ }
1839+ // TODO(DSC) // Fill in Cue point
1840+
1841+ i = pPlaylistTableModel->fieldIndex(PLAYLISTTRACKSTABLE_ARTIST);
1842+ if (i >= 0){
1843+ out << " ";
1844+ out << pPlaylistTableModel->data(pPlaylistTableModel->index(j,i)).toString();
1845+ }
1846+ i = pPlaylistTableModel->fieldIndex(PLAYLISTTRACKSTABLE_TITLE);
1847+ if (i >= 0){
1848+ out << " - ";
1849+ out << pPlaylistTableModel->data(pPlaylistTableModel->index(j,i)).toString();;
1850+ }
1851+ out << "\n";
1852+ }
1853+ return true;
1854+}
1855
1856=== added file 'mixxx/src/library/parsercsv.h'
1857--- mixxx/src/library/parsercsv.h 1970-01-01 00:00:00 +0000
1858+++ mixxx/src/library/parsercsv.h 2012-03-12 04:25:21 +0000
1859@@ -0,0 +1,41 @@
1860+//
1861+// C++ Interface: parserm3u
1862+//
1863+// Description: Interface header parse Comma-Separated Values (CSV) formated playlists (rfc4180)
1864+//
1865+//
1866+// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
1867+// Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011
1868+// Author: Daniel Schürmann daschuer@gmx.de, (C) 2011
1869+//
1870+// Copyright: See COPYING file that comes with this distribution
1871+//
1872+//
1873+#include "library/parser.h"
1874+#include "library/basesqltablemodel.h"
1875+
1876+#ifndef PARSERCSV_H
1877+#define PARSERCSV_H
1878+
1879+class QTextStream;
1880+
1881+class ParserCsv : public Parser
1882+{
1883+ Q_OBJECT
1884+public:
1885+ ParserCsv();
1886+ virtual ~ParserCsv();
1887+ /**Overwriting function parse in class Parser**/
1888+ QList<QString> parse(QString);
1889+ // Playlist Export
1890+ static bool writeCSVFile(const QString &file, BaseSqlTableModel* pPlaylistTableModel, bool useRelativePath);
1891+ // Readable Text export
1892+ static bool writeReadableTextFile(const QString &file, BaseSqlTableModel* pPlaylistTableModel);
1893+private:
1894+ /**Reads a line from the file and returns filepath if a valid file**/
1895+ QList<QList<QString> > tokenize(const QByteArray& str, char delimiter);
1896+
1897+
1898+};
1899+
1900+#endif
1901
1902=== modified file 'mixxx/src/library/playlistfeature.cpp'
1903--- mixxx/src/library/playlistfeature.cpp 2011-11-30 05:27:44 +0000
1904+++ mixxx/src/library/playlistfeature.cpp 2012-03-12 04:25:21 +0000
1905@@ -8,7 +8,7 @@
1906 #include "library/parser.h"
1907 #include "library/parserm3u.h"
1908 #include "library/parserpls.h"
1909-
1910+#include "library/parsercsv.h"
1911
1912 #include "widget/wlibrary.h"
1913 #include "widget/wlibrarysidebar.h"
1914@@ -22,42 +22,10 @@
1915 PlaylistFeature::PlaylistFeature(QObject* parent,
1916 TrackCollection* pTrackCollection,
1917 ConfigObject<ConfigValue>* pConfig)
1918- : LibraryFeature(parent),
1919- m_pTrackCollection(pTrackCollection),
1920- m_playlistDao(pTrackCollection->getPlaylistDAO()),
1921- m_trackDao(pTrackCollection->getTrackDAO()),
1922- m_pConfig(pConfig),
1923- m_playlistTableModel(this, pTrackCollection->getDatabase()) {
1924+ : BasePlaylistFeature(parent, pConfig, pTrackCollection,
1925+ "PLAYLISTHOME", "qrc:/html/playlists.html") {
1926 m_pPlaylistTableModel = new PlaylistTableModel(this, pTrackCollection,
1927 "mixxx.db.model.playlist");
1928-
1929- m_pCreatePlaylistAction = new QAction(tr("New Playlist"),this);
1930- connect(m_pCreatePlaylistAction, SIGNAL(triggered()),
1931- this, SLOT(slotCreatePlaylist()));
1932-
1933- m_pAddToAutoDJAction = new QAction(tr("Add to Auto-DJ Queue"),this);
1934- connect(m_pAddToAutoDJAction, SIGNAL(triggered()),
1935- this, SLOT(slotAddToAutoDJ()));
1936-
1937- m_pDeletePlaylistAction = new QAction(tr("Remove"),this);
1938- connect(m_pDeletePlaylistAction, SIGNAL(triggered()),
1939- this, SLOT(slotDeletePlaylist()));
1940-
1941- m_pRenamePlaylistAction = new QAction(tr("Rename"),this);
1942- connect(m_pRenamePlaylistAction, SIGNAL(triggered()),
1943- this, SLOT(slotRenamePlaylist()));
1944-
1945- m_pLockPlaylistAction = new QAction(tr("Lock"),this);
1946- connect(m_pLockPlaylistAction, SIGNAL(triggered()),
1947- this, SLOT(slotTogglePlaylistLock()));
1948-
1949- m_pImportPlaylistAction = new QAction(tr("Import Playlist"),this);
1950- connect(m_pImportPlaylistAction, SIGNAL(triggered()),
1951- this, SLOT(slotImportPlaylist()));
1952- m_pExportPlaylistAction = new QAction(tr("Export Playlist"), this);
1953- connect(m_pExportPlaylistAction, SIGNAL(triggered()),
1954- this, SLOT(slotExportPlaylist()));
1955-
1956 // Setup the sidebar playlist model
1957 m_playlistTableModel.setTable("Playlists");
1958 m_playlistTableModel.setFilter("hidden=0");
1959@@ -68,17 +36,10 @@
1960 //construct child model
1961 TreeItem *rootItem = new TreeItem();
1962 m_childModel.setRootItem(rootItem);
1963- constructChildModel();
1964+ constructChildModel(-1);
1965 }
1966
1967 PlaylistFeature::~PlaylistFeature() {
1968- delete m_pPlaylistTableModel;
1969- delete m_pCreatePlaylistAction;
1970- delete m_pDeletePlaylistAction;
1971- delete m_pImportPlaylistAction;
1972- delete m_pAddToAutoDJAction;
1973- delete m_pRenamePlaylistAction;
1974- delete m_pLockPlaylistAction;
1975 }
1976
1977 QVariant PlaylistFeature::title() {
1978@@ -89,31 +50,6 @@
1979 return QIcon(":/images/library/ic_library_playlist.png");
1980 }
1981
1982-
1983-void PlaylistFeature::bindWidget(WLibrarySidebar* sidebarWidget,
1984- WLibrary* libraryWidget,
1985- MixxxKeyboard* keyboard) {
1986- WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget);
1987- connect(this, SIGNAL(showPage(const QUrl&)),
1988- edit, SLOT(setSource(const QUrl&)));
1989- libraryWidget->registerView("PLAYLISTHOME", edit);
1990-}
1991-
1992-void PlaylistFeature::activate() {
1993- emit(showPage(QUrl("qrc:/html/playlists.html")));
1994- emit(switchToView("PLAYLISTHOME"));
1995-}
1996-
1997-void PlaylistFeature::activateChild(const QModelIndex& index) {
1998- //qDebug() << "PlaylistFeature::activateChild()" << index;
1999-
2000- //Switch the playlist table model's playlist.
2001- QString playlistName = index.data().toString();
2002- int playlistId = m_playlistDao.getPlaylistIdFromName(playlistName);
2003- m_pPlaylistTableModel->setPlaylist(playlistId);
2004- emit(showTrackModel(m_pPlaylistTableModel));
2005-}
2006-
2007 void PlaylistFeature::onRightClick(const QPoint& globalPos) {
2008 m_lastRightClickedIndex = QModelIndex();
2009
2010@@ -142,6 +78,7 @@
2011 menu.addAction(m_pCreatePlaylistAction);
2012 menu.addSeparator();
2013 menu.addAction(m_pAddToAutoDJAction);
2014+ menu.addAction(m_pAddToAutoDJTopAction);
2015 menu.addAction(m_pRenamePlaylistAction);
2016 menu.addAction(m_pDeletePlaylistAction);
2017 menu.addAction(m_pLockPlaylistAction);
2018@@ -151,156 +88,6 @@
2019 menu.exec(globalPos);
2020 }
2021
2022-void PlaylistFeature::slotCreatePlaylist() {
2023- QString name;
2024- bool validNameGiven = false;
2025-
2026- do {
2027- bool ok = false;
2028- name = QInputDialog::getText(NULL,
2029- tr("New Playlist"),
2030- tr("Playlist name:"),
2031- QLineEdit::Normal,
2032- tr("New Playlist"),
2033- &ok).trimmed();
2034-
2035- if (!ok)
2036- return;
2037-
2038- int existingId = m_playlistDao.getPlaylistIdFromName(name);
2039-
2040- if (existingId != -1) {
2041- QMessageBox::warning(NULL,
2042- tr("Playlist Creation Failed"),
2043- tr("A playlist by that name already exists."));
2044- } else if (name.isEmpty()) {
2045- QMessageBox::warning(NULL,
2046- tr("Playlist Creation Failed"),
2047- tr("A playlist cannot have a blank name."));
2048- } else {
2049- validNameGiven = true;
2050- }
2051-
2052- } while (!validNameGiven);
2053-
2054- bool playlistCreated = m_playlistDao.createPlaylist(name);
2055-
2056- if (playlistCreated) {
2057- clearChildModel();
2058- m_playlistTableModel.select();
2059- constructChildModel();
2060- emit(featureUpdated());
2061- //Switch the view to the new playlist.
2062- int playlistId = m_playlistDao.getPlaylistIdFromName(name);
2063- m_pPlaylistTableModel->setPlaylist(playlistId);
2064- // TODO(XXX) set sidebar selection
2065- emit(showTrackModel(m_pPlaylistTableModel));
2066- }
2067- else {
2068- QMessageBox::warning(NULL,
2069- tr("Playlist Creation Failed"),
2070- tr("An unknown error occurred while creating playlist: ")
2071- + name);
2072- }
2073-}
2074-
2075-void PlaylistFeature::slotRenamePlaylist()
2076-{
2077- qDebug() << "slotRenamePlaylist()";
2078-
2079- QString oldName = m_lastRightClickedIndex.data().toString();
2080- int playlistId = m_playlistDao.getPlaylistIdFromName(oldName);
2081- bool locked = m_playlistDao.isPlaylistLocked(playlistId);
2082-
2083- if (locked) {
2084- qDebug() << "Skipping playlist rename because playlist" << playlistId << "is locked.";
2085- return;
2086- }
2087-
2088- QString newName;
2089- bool validNameGiven = false;
2090-
2091- do {
2092- bool ok = false;
2093- newName = QInputDialog::getText(NULL,
2094- tr("Rename Playlist"),
2095- tr("New playlist name:"),
2096- QLineEdit::Normal,
2097- oldName,
2098- &ok).trimmed();
2099-
2100- if (!ok || oldName == newName) {
2101- return;
2102- }
2103-
2104- int existingId = m_playlistDao.getPlaylistIdFromName(newName);
2105-
2106- if (existingId != -1) {
2107- QMessageBox::warning(NULL,
2108- tr("Renaming Playlist Failed"),
2109- tr("A playlist by that name already exists."));
2110- }
2111- else if (newName.isEmpty()) {
2112- QMessageBox::warning(NULL,
2113- tr("Renaming Playlist Failed"),
2114- tr("A playlist cannot have a blank name."));
2115- }
2116- else {
2117- validNameGiven = true;
2118- }
2119- } while (!validNameGiven);
2120-
2121- m_playlistDao.renamePlaylist(playlistId, newName);
2122- clearChildModel();
2123- m_playlistTableModel.select();
2124- constructChildModel();
2125- emit(featureUpdated());
2126- m_pPlaylistTableModel->setPlaylist(playlistId);
2127-}
2128-
2129-
2130-void PlaylistFeature::slotTogglePlaylistLock()
2131-{
2132- QString playlistName = m_lastRightClickedIndex.data().toString();
2133- int playlistId = m_playlistDao.getPlaylistIdFromName(playlistName);
2134- bool locked = !m_playlistDao.isPlaylistLocked(playlistId);
2135-
2136- if (!m_playlistDao.setPlaylistLocked(playlistId, locked)) {
2137- qDebug() << "Failed to toggle lock of playlistId " << playlistId;
2138- }
2139-
2140- TreeItem* playlistItem = m_childModel.getItem(m_lastRightClickedIndex);
2141- playlistItem->setIcon(locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
2142-}
2143-
2144-void PlaylistFeature::slotDeletePlaylist()
2145-{
2146- //qDebug() << "slotDeletePlaylist() row:" << m_lastRightClickedIndex.data();
2147- int playlistId = m_playlistDao.getPlaylistIdFromName(m_lastRightClickedIndex.data().toString());
2148- bool locked = m_playlistDao.isPlaylistLocked(playlistId);
2149-
2150- if (locked) {
2151- qDebug() << "Skipping playlist deletion because playlist" << playlistId << "is locked.";
2152- return;
2153- }
2154-
2155- if (m_lastRightClickedIndex.isValid() &&
2156- !m_playlistDao.isPlaylistLocked(playlistId)) {
2157- Q_ASSERT(playlistId >= 0);
2158-
2159- clearChildModel();
2160- m_playlistDao.deletePlaylist(playlistId);
2161- m_playlistTableModel.select();
2162- constructChildModel();
2163- emit(featureUpdated());
2164- }
2165-
2166-}
2167-
2168-bool PlaylistFeature::dropAccept(QUrl url) {
2169- return false;
2170-}
2171-
2172 bool PlaylistFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {
2173 //TODO: Filter by supported formats regex and reject anything that doesn't match.
2174 QString playlistName = index.data().toString();
2175@@ -333,10 +120,6 @@
2176 return true;
2177 }
2178
2179-bool PlaylistFeature::dragMoveAccept(QUrl url) {
2180- return false;
2181-}
2182-
2183 bool PlaylistFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) {
2184 //TODO: Filter by supported formats regex and reject anything that doesn't match.
2185
2186@@ -349,23 +132,21 @@
2187 return !locked && formatSupported;
2188 }
2189
2190-TreeItemModel* PlaylistFeature::getChildModel() {
2191- return &m_childModel;
2192-}
2193+
2194 /**
2195 * Purpose: When inserting or removing playlists,
2196 * we require the sidebar model not to reset.
2197 * This method queries the database and does dynamic insertion
2198 */
2199-void PlaylistFeature::constructChildModel()
2200+QModelIndex PlaylistFeature::constructChildModel(int selected_id)
2201 {
2202 QList<TreeItem*> data_list;
2203 int nameColumn = m_playlistTableModel.record().indexOf("name");
2204 int idColumn = m_playlistTableModel.record().indexOf("id");
2205-
2206- //Access the invisible root item
2207+ int selected_row = -1;
2208+ // Access the invisible root item
2209 TreeItem* root = m_childModel.getItem(QModelIndex());
2210- //Create new TreeItems for the playlists in the database
2211+ // Create new TreeItems for the playlists in the database
2212 for (int row = 0; row < m_playlistTableModel.rowCount(); ++row) {
2213 QModelIndex ind = m_playlistTableModel.index(row, nameColumn);
2214 QString playlist_name = m_playlistTableModel.data(ind).toString();
2215@@ -373,121 +154,23 @@
2216 int playlist_id = m_playlistTableModel.data(ind).toInt();
2217 bool locked = m_playlistDao.isPlaylistLocked(playlist_id);
2218
2219- //Create the TreeItem whose parent is the invisible root item
2220+ if (selected_id == playlist_id) {
2221+ // save index for selection
2222+ selected_row = row;
2223+ }
2224+
2225+ // Create the TreeItem whose parent is the invisible root item
2226 TreeItem* item = new TreeItem(playlist_name, playlist_name, this, root);
2227 item->setIcon(locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
2228 data_list.append(item);
2229 }
2230
2231- //Append all the newly created TreeItems in a dynamic way to the childmodel
2232+ // Append all the newly created TreeItems in a dynamic way to the childmodel
2233 m_childModel.insertRows(data_list, 0, m_playlistTableModel.rowCount());
2234-}
2235-
2236-/**
2237- * Clears the child model dynamically, but the invisible root item remains
2238- */
2239-void PlaylistFeature::clearChildModel()
2240-{
2241- m_childModel.removeRows(0,m_playlistTableModel.rowCount());
2242-}
2243-void PlaylistFeature::slotImportPlaylist()
2244-{
2245- qDebug() << "slotImportPlaylist() row:" ; //<< m_lastRightClickedIndex.data();
2246-
2247- QString playlist_file = QFileDialog::getOpenFileName(
2248- NULL,
2249- tr("Import Playlist"),
2250- QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
2251- tr("Playlist Files (*.m3u *.m3u8 *.pls)"));
2252- // Exit method if user cancelled the open dialog.
2253- if (playlist_file.isNull() || playlist_file.isEmpty()) {
2254- return;
2255- }
2256-
2257- Parser* playlist_parser = NULL;
2258-
2259- if (playlist_file.endsWith(".m3u", Qt::CaseInsensitive) ||
2260- playlist_file.endsWith(".m3u8", Qt::CaseInsensitive)) {
2261- playlist_parser = new ParserM3u();
2262- } else if (playlist_file.endsWith(".pls", Qt::CaseInsensitive)) {
2263- playlist_parser = new ParserPls();
2264- } else {
2265- return;
2266- }
2267- QList<QString> entries = playlist_parser->parse(playlist_file);
2268-
2269- // Iterate over the List that holds URLs of playlist entires
2270- for (int i = 0; i < entries.size(); ++i) {
2271- m_pPlaylistTableModel->addTrack(QModelIndex(), entries[i]);
2272- }
2273-
2274- // delete the parser object
2275- if (playlist_parser) {
2276- delete playlist_parser;
2277- }
2278-}
2279-void PlaylistFeature::onLazyChildExpandation(const QModelIndex &index){
2280- //Nothing to do because the childmodel is not of lazy nature.
2281-}
2282-
2283-void PlaylistFeature::slotExportPlaylist(){
2284- qDebug() << "Export playlist" << m_lastRightClickedIndex.data();
2285- QString file_location = QFileDialog::getSaveFileName(
2286- NULL,
2287- tr("Export Playlist"),
2288- QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
2289- tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;PLS Playlist (*.pls)"));
2290- // Exit method if user cancelled the open dialog.
2291- if (file_location.isNull() || file_location.isEmpty()) {
2292- return;
2293- }
2294- // Create and populate a list of files of the playlist
2295- QList<QString> playlist_items;
2296- // Create a new table model since the main one might have an active search.
2297- QScopedPointer<PlaylistTableModel> pPlaylistTableModel(
2298- new PlaylistTableModel(this, m_pTrackCollection,
2299- "mixxx.db.model.playlist_export"));
2300-
2301- pPlaylistTableModel->setPlaylist(m_pPlaylistTableModel->getPlaylist());
2302- pPlaylistTableModel->setSort(0, Qt::AscendingOrder);
2303- pPlaylistTableModel->select();
2304- int rows = pPlaylistTableModel->rowCount();
2305- for (int i = 0; i < rows; ++i) {
2306- QModelIndex index = pPlaylistTableModel->index(i, 0);
2307- playlist_items << pPlaylistTableModel->getTrackLocation(index);
2308- }
2309-
2310- // check config if relative paths are desired
2311- bool useRelativePath = static_cast<bool>(m_pConfig->getValueString(
2312- ConfigKey("[Library]", "UseRelativePathOnExport")).toInt());
2313-
2314- if (file_location.endsWith(".m3u", Qt::CaseInsensitive)) {
2315- ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);
2316- } else if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {
2317- ParserPls::writePLSFile(file_location,playlist_items,
2318- useRelativePath);
2319- } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {
2320- ParserM3u::writeM3U8File(file_location, playlist_items,
2321- useRelativePath);
2322- } else {
2323- //default export to M3U if file extension is missing
2324-
2325- qDebug() << "Playlist export: No file extension specified. Appending .m3u "
2326- << "and exporting to M3U.";
2327- file_location.append(".m3u");
2328- ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);
2329- }
2330-}
2331-
2332-void PlaylistFeature::slotAddToAutoDJ() {
2333- //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
2334-
2335- if (m_lastRightClickedIndex.isValid()) {
2336- int playlistId = m_playlistDao.getPlaylistIdFromName(
2337- m_lastRightClickedIndex.data().toString());
2338- if (playlistId >= 0) {
2339- m_playlistDao.addToAutoDJQueue(playlistId);
2340- }
2341- }
2342- emit(featureUpdated());
2343-}
2344+ if (selected_row == -1) {
2345+ return QModelIndex();
2346+ }
2347+ return m_childModel.index(selected_row, 0);
2348+}
2349+
2350+
2351
2352=== modified file 'mixxx/src/library/playlistfeature.h'
2353--- mixxx/src/library/playlistfeature.h 2011-11-27 06:59:02 +0000
2354+++ mixxx/src/library/playlistfeature.h 2012-03-12 04:25:21 +0000
2355@@ -5,76 +5,31 @@
2356 #define PLAYLISTFEATURE_H
2357
2358 #include <QSqlTableModel>
2359-#include <QAction>
2360-#include <QList>
2361
2362-#include "library/libraryfeature.h"
2363-#include "library/dao/playlistdao.h"
2364-#include "library/dao/trackdao.h"
2365-#include "treeitemmodel.h"
2366+#include "library/baseplaylistfeature.h"
2367 #include "configobject.h"
2368
2369-
2370-class PlaylistTableModel;
2371 class TrackCollection;
2372
2373-class PlaylistFeature : public LibraryFeature {
2374+class PlaylistFeature : public BasePlaylistFeature {
2375 Q_OBJECT
2376-public:
2377- PlaylistFeature(QObject* parent, TrackCollection* pTrackCollection, ConfigObject<ConfigValue>* pConfig);
2378+ public:
2379+ PlaylistFeature(QObject* parent, TrackCollection* pTrackCollection,
2380+ ConfigObject<ConfigValue>* pConfig);
2381 virtual ~PlaylistFeature();
2382
2383 QVariant title();
2384 QIcon getIcon();
2385
2386- bool dropAccept(QUrl url);
2387 bool dropAcceptChild(const QModelIndex& index, QUrl url);
2388- bool dragMoveAccept(QUrl url);
2389 bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
2390
2391- TreeItemModel* getChildModel();
2392-
2393- void bindWidget(WLibrarySidebar* sidebarWidget,
2394- WLibrary* libraryWidget,
2395- MixxxKeyboard* keyboard);
2396- signals:
2397- void showPage(const QUrl& page);
2398-
2399-public slots:
2400- void activate();
2401- void activateChild(const QModelIndex& index);
2402+ public slots:
2403 void onRightClick(const QPoint& globalPos);
2404 void onRightClickChild(const QPoint& globalPos, QModelIndex index);
2405- void onLazyChildExpandation(const QModelIndex& index);
2406-
2407- void slotCreatePlaylist();
2408- void slotDeletePlaylist();
2409- void slotAddToAutoDJ();
2410- void slotRenamePlaylist();
2411- void slotTogglePlaylistLock();
2412- void slotImportPlaylist();
2413- void slotExportPlaylist();
2414-
2415-
2416- private:
2417- void constructChildModel();
2418- void clearChildModel();
2419-
2420- TrackCollection* m_pTrackCollection;
2421- PlaylistTableModel* m_pPlaylistTableModel;
2422- PlaylistDAO &m_playlistDao;
2423- TrackDAO &m_trackDao;
2424- QAction *m_pCreatePlaylistAction;
2425- QAction *m_pDeletePlaylistAction;
2426- QAction *m_pAddToAutoDJAction;
2427- QAction *m_pRenamePlaylistAction;
2428- QAction *m_pLockPlaylistAction;
2429- QAction *m_pImportPlaylistAction;
2430- QAction *m_pExportPlaylistAction;
2431- QSqlTableModel m_playlistTableModel;
2432- QModelIndex m_lastRightClickedIndex;
2433- TreeItemModel m_childModel;
2434- ConfigObject<ConfigValue>* m_pConfig;
2435+
2436+ protected:
2437+ QModelIndex constructChildModel(int selected_id);
2438 };
2439
2440 #endif /* PLAYLISTFEATURE_H */
2441
2442=== modified file 'mixxx/src/library/playlisttablemodel.cpp'
2443--- mixxx/src/library/playlisttablemodel.cpp 2011-12-18 20:23:14 +0000
2444+++ mixxx/src/library/playlisttablemodel.cpp 2012-03-12 04:25:21 +0000
2445@@ -94,6 +94,17 @@
2446 return true;
2447 }
2448
2449+bool PlaylistTableModel::appendTrack(int trackId) {
2450+ if (trackId < 0) {
2451+ return false;
2452+ }
2453+
2454+ m_playlistDao.appendTrackToPlaylist(trackId, m_iPlaylistId);
2455+
2456+ select(); //Repopulate the data model.
2457+ return true;
2458+}
2459+
2460 TrackPointer PlaylistTableModel::getTrack(const QModelIndex& index) const {
2461 //FIXME: use position instead of location for playlist tracks?
2462
2463@@ -256,7 +267,7 @@
2464
2465 //Print out any SQL error, if there was one.
2466 if (query.lastError().isValid()) {
2467- qDebug() << query.lastError();
2468+ qDebug() << query.lastError();
2469 }
2470
2471 select();
2472@@ -326,6 +337,7 @@
2473 }
2474
2475 QItemDelegate* PlaylistTableModel::delegateForColumn(const int i) {
2476+ Q_UNUSED(i);
2477 return NULL;
2478 }
2479
2480
2481=== modified file 'mixxx/src/library/playlisttablemodel.h'
2482--- mixxx/src/library/playlisttablemodel.h 2011-11-30 05:27:44 +0000
2483+++ mixxx/src/library/playlisttablemodel.h 2012-03-12 04:25:21 +0000
2484@@ -30,6 +30,7 @@
2485 virtual void removeTrack(const QModelIndex& index);
2486 virtual void removeTracks(const QModelIndexList& indices);
2487 virtual bool addTrack(const QModelIndex& index, QString location);
2488+ virtual bool appendTrack(int trackId);
2489 virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex);
2490 virtual void shuffleTracks(const QModelIndex& currentIndex);
2491
2492
2493=== modified file 'mixxx/src/library/preparefeature.cpp'
2494--- mixxx/src/library/preparefeature.cpp 2011-03-26 12:29:21 +0000
2495+++ mixxx/src/library/preparefeature.cpp 2012-03-12 04:25:21 +0000
2496@@ -78,34 +78,45 @@
2497 }
2498
2499 void PrepareFeature::activateChild(const QModelIndex& index) {
2500+ Q_UNUSED(index);
2501 }
2502
2503 void PrepareFeature::onRightClick(const QPoint& globalPos) {
2504+ Q_UNUSED(globalPos);
2505 }
2506
2507 void PrepareFeature::onRightClickChild(const QPoint& globalPos,
2508 QModelIndex index) {
2509+ Q_UNUSED(globalPos);
2510+ Q_UNUSED(index);
2511 }
2512
2513 bool PrepareFeature::dropAccept(QUrl url) {
2514+ Q_UNUSED(url);
2515 return false;
2516 }
2517
2518 bool PrepareFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {
2519+ Q_UNUSED(index);
2520+ Q_UNUSED(url);
2521 return false;
2522 }
2523
2524 bool PrepareFeature::dragMoveAccept(QUrl url) {
2525+ Q_UNUSED(url);
2526 return false;
2527 }
2528
2529 bool PrepareFeature::dragMoveAcceptChild(const QModelIndex& index,
2530 QUrl url) {
2531+ Q_UNUSED(index);
2532+ Q_UNUSED(url);
2533 return false;
2534 }
2535
2536 void PrepareFeature::onLazyChildExpandation(const QModelIndex &index){
2537 //Nothing to do because the childmodel is not of lazy nature.
2538+ Q_UNUSED(index);
2539 }
2540
2541 void PrepareFeature::analyzeTracks(QList<int> trackIds) {
2542
2543=== added file 'mixxx/src/library/setlogfeature.cpp'
2544--- mixxx/src/library/setlogfeature.cpp 1970-01-01 00:00:00 +0000
2545+++ mixxx/src/library/setlogfeature.cpp 2012-03-12 04:25:21 +0000
2546@@ -0,0 +1,263 @@
2547+#include <QtDebug>
2548+#include <QMenu>
2549+#include <QInputDialog>
2550+#include <QFileDialog>
2551+#include <QDesktopServices>
2552+#include <QDateTime>
2553+
2554+#include "library/setlogfeature.h"
2555+
2556+#include "controlobject.h"
2557+#include "library/playlisttablemodel.h"
2558+#include "library/trackcollection.h"
2559+#include "playerinfo.h"
2560+#include "treeitem.h"
2561+
2562+SetlogFeature::SetlogFeature(QObject* parent,
2563+ ConfigObject<ConfigValue>* pConfig,
2564+ TrackCollection* pTrackCollection)
2565+ : BasePlaylistFeature(parent, pConfig, pTrackCollection,
2566+ "SETLOGHOME", "qrc:/html/setlogs.html") {
2567+ m_pPlaylistTableModel = new PlaylistTableModel(this, pTrackCollection,
2568+ "mixxx.db.model.setlog");
2569+ m_pJoinWithPreviousAction = new QAction(tr("Join with previous"), this);
2570+ connect(m_pJoinWithPreviousAction, SIGNAL(triggered()),
2571+ this, SLOT(slotJoinWithPrevious()));
2572+
2573+ //create a new playlist for today
2574+ QString set_log_name_format;
2575+ QString set_log_name;
2576+
2577+ set_log_name = QDate::currentDate().toString(Qt::ISODate);
2578+ set_log_name_format = set_log_name + " (%1)";
2579+ int i = 1;
2580+
2581+ // calculate name of the todays setlog
2582+ while (m_playlistDao.getPlaylistIdFromName(set_log_name) != -1) {
2583+ set_log_name = set_log_name_format.arg(++i);
2584+ }
2585+
2586+ m_playlistId = m_playlistDao.createPlaylist(set_log_name,
2587+ PlaylistDAO::PLHT_SET_LOG);
2588+
2589+ if (m_playlistId == -1) {
2590+ qDebug() << "Playlist Creation Failed";
2591+ qDebug() << "An unknown error occurred while creating playlist: " << set_log_name;
2592+ }
2593+
2594+ // Setup the sidebar playlist model
2595+ m_playlistTableModel.setTable("Playlists");
2596+ m_playlistTableModel.setFilter("hidden=2"); // PLHT_SET_LOG
2597+ m_playlistTableModel.setSort(m_playlistTableModel.fieldIndex("id"),
2598+ Qt::AscendingOrder);
2599+ m_playlistTableModel.select();
2600+
2601+ //construct child model
2602+ TreeItem *rootItem = new TreeItem();
2603+ m_childModel.setRootItem(rootItem);
2604+ constructChildModel(-1);
2605+}
2606+
2607+SetlogFeature::~SetlogFeature() {
2608+}
2609+
2610+QVariant SetlogFeature::title() {
2611+ return tr("Set Logs");
2612+}
2613+
2614+QIcon SetlogFeature::getIcon() {
2615+ return QIcon(":/images/library/ic_library_setlog.png");
2616+}
2617+
2618+void SetlogFeature::bindWidget(WLibrarySidebar* sidebarWidget,
2619+ WLibrary* libraryWidget,
2620+ MixxxKeyboard* keyboard) {
2621+ BasePlaylistFeature::bindWidget(sidebarWidget,
2622+ libraryWidget,
2623+ keyboard);
2624+ connect(&PlayerInfo::Instance(), SIGNAL(currentPlayingDeckChanged(int)),
2625+ this, SLOT(slotPlayingDeckChanged(int)));
2626+}
2627+
2628+void SetlogFeature::onRightClick(const QPoint& globalPos) {
2629+ Q_UNUSED(globalPos);
2630+ m_lastRightClickedIndex = QModelIndex();
2631+
2632+ // Create the right-click menu
2633+ // QMenu menu(NULL);
2634+ // menu.addAction(m_pCreatePlaylistAction);
2635+ // TODO(DASCHUER) add something like disable logging
2636+ // menu.exec(globalPos);
2637+}
2638+
2639+void SetlogFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) {
2640+ //Save the model index so we can get it in the action slots...
2641+ m_lastRightClickedIndex = index;
2642+ QString playlistName = index.data().toString();
2643+ int playlistId = m_playlistDao.getPlaylistIdFromName(playlistName);
2644+
2645+
2646+ bool locked = m_playlistDao.isPlaylistLocked(playlistId);
2647+ m_pDeletePlaylistAction->setEnabled(!locked);
2648+ m_pRenamePlaylistAction->setEnabled(!locked);
2649+
2650+ m_pLockPlaylistAction->setText(locked ? tr("Unlock") : tr("Lock"));
2651+
2652+
2653+ //Create the right-click menu
2654+ QMenu menu(NULL);
2655+ //menu.addAction(m_pCreatePlaylistAction);
2656+ //menu.addSeparator();
2657+ menu.addAction(m_pAddToAutoDJAction);
2658+ menu.addAction(m_pAddToAutoDJTopAction);
2659+ menu.addAction(m_pRenamePlaylistAction);
2660+ if (playlistId != m_playlistId) {
2661+ // Todays playlist should not be locked or deleted
2662+ menu.addAction(m_pDeletePlaylistAction);
2663+ menu.addAction(m_pLockPlaylistAction);
2664+ }
2665+ if (index.row() > 0) {
2666+ // The very first setlog cannot be joint
2667+ menu.addAction(m_pJoinWithPreviousAction);
2668+ }
2669+ menu.addSeparator();
2670+ menu.addAction(m_pExportPlaylistAction);
2671+ menu.exec(globalPos);
2672+}
2673+
2674+bool SetlogFeature::dropAcceptChild(const QModelIndex& index, QUrl url){
2675+ Q_UNUSED(url);
2676+ Q_UNUSED(index);
2677+ return false;
2678+}
2679+
2680+bool SetlogFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) {
2681+ Q_UNUSED(url);
2682+ Q_UNUSED(index);
2683+ return false;
2684+}
2685+
2686+/**
2687+ * Purpose: When inserting or removing playlists,
2688+ * we require the sidebar model not to reset.
2689+ * This method queries the database and does dynamic insertion
2690+*/
2691+QModelIndex SetlogFeature::constructChildModel(int selected_id)
2692+{
2693+ QList<TreeItem*> data_list;
2694+ int nameColumn = m_playlistTableModel.record().indexOf("name");
2695+ int idColumn = m_playlistTableModel.record().indexOf("id");
2696+ int selected_row = -1;
2697+ // Access the invisible root item
2698+ TreeItem* root = m_childModel.getItem(QModelIndex());
2699+
2700+ // Create new TreeItems for the playlists in the database
2701+ for (int row = 0; row < m_playlistTableModel.rowCount(); ++row) {
2702+ QModelIndex ind = m_playlistTableModel.index(row, nameColumn);
2703+ QString playlist_name = m_playlistTableModel.data(ind).toString();
2704+ ind = m_playlistTableModel.index(row, idColumn);
2705+ int playlist_id = m_playlistTableModel.data(ind).toInt();
2706+
2707+ if ( selected_id == playlist_id) {
2708+ // save index for selection
2709+ selected_row = row;
2710+ }
2711+
2712+ // Create the TreeItem whose parent is the invisible root item
2713+ TreeItem* item = new TreeItem(playlist_name, playlist_name, this, root);
2714+ if (playlist_id == m_playlistId) {
2715+ item->setIcon(QIcon(":/images/library/ic_library_setlog_current.png"));
2716+ } else if (m_playlistDao.isPlaylistLocked(playlist_id)) {
2717+ item->setIcon(QIcon(":/images/library/ic_library_locked.png"));
2718+ } else {
2719+ item->setIcon(QIcon());
2720+ }
2721+ data_list.append(item);
2722+ }
2723+
2724+ // Append all the newly created TreeItems in a dynamic way to the childmodel
2725+ m_childModel.insertRows(data_list, 0, m_playlistTableModel.rowCount());
2726+ if (selected_row == -1) {
2727+ return QModelIndex();
2728+ }
2729+ return m_childModel.index(selected_row, 0);
2730+}
2731+
2732+void SetlogFeature::slotJoinWithPrevious() {
2733+ //qDebug() << "slotJoinWithPrevious() row:" << m_lastRightClickedIndex.data();
2734+
2735+ if (m_lastRightClickedIndex.isValid()) {
2736+ int currentPlaylistId = m_playlistDao.getPlaylistIdFromName(
2737+ m_lastRightClickedIndex.data().toString());
2738+
2739+ if (currentPlaylistId >= 0) {
2740+
2741+ bool locked = m_playlistDao.isPlaylistLocked(currentPlaylistId);
2742+
2743+ if (locked) {
2744+ qDebug() << "Skipping playlist deletion because playlist" << currentPlaylistId << "is locked.";
2745+ return;
2746+ }
2747+
2748+ // Add every track from right klicked playlist to that with the next smaller ID
2749+ int previousPlaylistId = m_playlistDao.getPreviousPlaylist(currentPlaylistId, PlaylistDAO::PLHT_SET_LOG);
2750+ if (previousPlaylistId >= 0) {
2751+
2752+ m_pPlaylistTableModel->setPlaylist(previousPlaylistId);
2753+
2754+ if (currentPlaylistId == m_playlistId) {
2755+ // mark all the Tracks in the previous Playlist as played
2756+
2757+ m_pPlaylistTableModel->select();
2758+ int rows = m_pPlaylistTableModel->rowCount();
2759+ for(int i = 0; i < rows; ++i){
2760+ QModelIndex index = m_pPlaylistTableModel->index(i,0);
2761+ if (index.isValid()) {
2762+ TrackPointer track = m_pPlaylistTableModel->getTrack(index);
2763+ // Do not update the playcount, just set played
2764+ // status.
2765+ track->setPlayed(true);
2766+ }
2767+ }
2768+
2769+ // Change current setlog
2770+ m_playlistId = previousPlaylistId;
2771+ }
2772+ qDebug() << "slotJoinWithPrevious() current:" << currentPlaylistId << " previous:" << previousPlaylistId;
2773+ m_playlistDao.copyPlaylistTracks(currentPlaylistId, previousPlaylistId);
2774+ m_playlistDao.deletePlaylist(currentPlaylistId);
2775+ slotPlaylistTableChanged(previousPlaylistId); // For moving selection
2776+ emit(showTrackModel(m_pPlaylistTableModel));
2777+ emit(featureUpdated());
2778+ }
2779+ }
2780+ }
2781+}
2782+
2783+void SetlogFeature::slotPlayingDeckChanged(int deck) {
2784+ if (deck > 0) {
2785+ QString chan = QString("[Channel%1]").arg(deck);
2786+ TrackPointer currentPlayingTrack =
2787+ PlayerInfo::Instance().getTrackInfo(chan);
2788+ if (!currentPlayingTrack) {
2789+ return;
2790+ }
2791+ int currentPlayingTrackId = currentPlayingTrack->getId();
2792+ // We can only add tracks that are Mixxx library tracks, not external
2793+ // sources.
2794+ if (currentPlayingTrackId < 0) {
2795+ return;
2796+ }
2797+
2798+ // Here the song is realy played, not only loaded.
2799+ currentPlayingTrack->setPlayedAndUpdatePlaycount(true);
2800+
2801+ if (m_pPlaylistTableModel->getPlaylist() == m_playlistId) {
2802+ // View needs a refresh
2803+ m_pPlaylistTableModel->appendTrack(currentPlayingTrackId);
2804+ } else {
2805+ m_playlistDao.appendTrackToPlaylist(currentPlayingTrackId,
2806+ m_playlistId);
2807+ }
2808+ }
2809+}
2810
2811=== added file 'mixxx/src/library/setlogfeature.h'
2812--- mixxx/src/library/setlogfeature.h 1970-01-01 00:00:00 +0000
2813+++ mixxx/src/library/setlogfeature.h 2012-03-12 04:25:21 +0000
2814@@ -0,0 +1,48 @@
2815+// setlogfeature.h
2816+
2817+#ifndef SETLOGFEATURE_H
2818+#define SETLOGFEATURE_H
2819+
2820+#include <QSqlTableModel>
2821+#include <QAction>
2822+
2823+#include "library/baseplaylistfeature.h"
2824+#include "configobject.h"
2825+#include "controlobjectthreadmain.h"
2826+
2827+class TrackCollection;
2828+
2829+class SetlogFeature : public BasePlaylistFeature {
2830+ Q_OBJECT
2831+public:
2832+ SetlogFeature(QObject* parent, ConfigObject<ConfigValue>* pConfig,
2833+ TrackCollection* pTrackCollection);
2834+ virtual ~SetlogFeature();
2835+
2836+ QVariant title();
2837+ QIcon getIcon();
2838+
2839+ bool dropAcceptChild(const QModelIndex& index, QUrl url);
2840+ bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
2841+
2842+ virtual void bindWidget(WLibrarySidebar* sidebarWidget,
2843+ WLibrary* libraryWidget,
2844+ MixxxKeyboard* keyboard);
2845+
2846+ public slots:
2847+ void onRightClick(const QPoint& globalPos);
2848+ void onRightClickChild(const QPoint& globalPos, QModelIndex index);
2849+ void slotJoinWithPrevious();
2850+
2851+ protected:
2852+ QModelIndex constructChildModel(int selected_id);
2853+
2854+ private slots:
2855+ void slotPlayingDeckChanged(int deck);
2856+
2857+ private:
2858+ QAction *m_pJoinWithPreviousAction;
2859+ int m_playlistId;
2860+};
2861+
2862+#endif /* SETLOGFEATURE_H */
2863
2864=== modified file 'mixxx/src/library/sidebarmodel.cpp'
2865--- mixxx/src/library/sidebarmodel.cpp 2011-10-21 02:26:31 +0000
2866+++ mixxx/src/library/sidebarmodel.cpp 2012-03-12 04:25:21 +0000
2867@@ -17,11 +17,14 @@
2868
2869 void SidebarModel::addLibraryFeature(LibraryFeature* feature) {
2870 m_sFeatures.push_back(feature);
2871- connect(feature, SIGNAL(featureUpdated()), this, SLOT(refreshData()));
2872+ connect(feature, SIGNAL(featureUpdated()),
2873+ this, SLOT(refreshData()));
2874 connect(feature, SIGNAL(featureIsLoading(LibraryFeature*)),
2875 this, SLOT(slotFeatureIsLoading(LibraryFeature*)));
2876 connect(feature, SIGNAL(featureLoadingFinished(LibraryFeature*)),
2877- this,SLOT(slotFeatureLoadingFinished(LibraryFeature*)));
2878+ this, SLOT(slotFeatureLoadingFinished(LibraryFeature*)));
2879+ connect(feature, SIGNAL(featureSelect(LibraryFeature*, const QModelIndex&)),
2880+ this, SLOT(slotFeatureSelect(LibraryFeature*, const QModelIndex&)));
2881
2882 QAbstractItemModel* model = feature->getChildModel();
2883
2884@@ -108,19 +111,23 @@
2885 return QModelIndex();
2886 } else {
2887 TreeItem* tree_item = (TreeItem*)index.internalPointer();
2888+ TreeItem* tree_item_parent = tree_item->parent();
2889 // if we have selected an item at the first level of a childnode
2890- if (tree_item->parent()->data() == "$root"){
2891- LibraryFeature* feature = tree_item->getFeature();
2892- for (int i = 0; i < m_sFeatures.size(); ++i) {
2893- if (feature == m_sFeatures[i]) {
2894- // create a ModelIndex for parent 'this' having a
2895- // library feature at position 'i'
2896- return createIndex(i, 0, (void*)this);
2897+
2898+ if (tree_item_parent) {
2899+ if (tree_item_parent->data() == "$root"){
2900+ LibraryFeature* feature = tree_item->getFeature();
2901+ for (int i = 0; i < m_sFeatures.size(); ++i) {
2902+ if (feature == m_sFeatures[i]) {
2903+ // create a ModelIndex for parent 'this' having a
2904+ // library feature at position 'i'
2905+ return createIndex(i, 0, (void*)this);
2906+ }
2907 }
2908 }
2909+ // if we have selected an item at some deeper level of a childnode
2910+ return createIndex(tree_item_parent->row(), 0 , tree_item_parent);
2911 }
2912- // if we have selected an item at some deeper level of a childnode
2913- return createIndex(tree_item->parent()->row(), 0 , tree_item->parent());
2914 }
2915 }
2916 return QModelIndex();
2917@@ -144,6 +151,7 @@
2918 }
2919
2920 int SidebarModel::columnCount(const QModelIndex& parent) const {
2921+ Q_UNUSED(parent);
2922 //qDebug() << "SidebarModel::columnCount parent=" << parent;
2923 // TODO(rryan) will we ever have columns? I don't think so.
2924 return 1;
2925@@ -312,6 +320,8 @@
2926 }
2927
2928 void SidebarModel::slotDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) {
2929+ Q_UNUSED(topLeft);
2930+ Q_UNUSED(bottomRight);
2931 //qDebug() << "slotDataChanged topLeft:" << topLeft << "bottomRight:" << bottomRight;
2932 }
2933
2934@@ -330,12 +340,18 @@
2935 }
2936
2937 void SidebarModel::slotRowsInserted(const QModelIndex& parent, int start, int end) {
2938- // qDebug() << "slotRowsInserted" << parent << start << end;
2939+ Q_UNUSED(parent);
2940+ Q_UNUSED(start);
2941+ Q_UNUSED(end);
2942+ // qDebug() << "slotRowsInserted" << parent << start << end;
2943 //QModelIndex newParent = translateSourceIndex(parent);
2944 endInsertRows();
2945 }
2946
2947 void SidebarModel::slotRowsRemoved(const QModelIndex& parent, int start, int end) {
2948+ Q_UNUSED(parent);
2949+ Q_UNUSED(start);
2950+ Q_UNUSED(end);
2951 //qDebug() << "slotRowsRemoved" << parent << start << end;
2952 //QModelIndex newParent = translateSourceIndex(parent);
2953 endRemoveRows();
2954@@ -356,7 +372,7 @@
2955 void SidebarModel::slotFeatureIsLoading(LibraryFeature * feature)
2956 {
2957 featureRenamed(feature);
2958- selectFeature(feature);
2959+ slotFeatureSelect(feature);
2960 }
2961
2962 /* Tobias: This slot is somewhat redundant but I decided
2963@@ -364,7 +380,7 @@
2964 */
2965 void SidebarModel::slotFeatureLoadingFinished(LibraryFeature * feature){
2966 featureRenamed(feature);
2967- selectFeature(feature);
2968+ slotFeatureSelect(feature);
2969 }
2970
2971 void SidebarModel::featureRenamed(LibraryFeature* pFeature){
2972@@ -376,11 +392,18 @@
2973 }
2974 }
2975
2976-void SidebarModel::selectFeature(LibraryFeature* pFeature) {
2977- for (int i=0; i < m_sFeatures.size(); ++i) {
2978- if (m_sFeatures[i] == pFeature) {
2979- QModelIndex ind = index(i, 0);
2980- emit(selectIndex(ind));
2981+void SidebarModel::slotFeatureSelect(LibraryFeature* pFeature, const QModelIndex& featureIndex) {
2982+ QModelIndex ind;
2983+ if (featureIndex.isValid()) {
2984+ TreeItem* item = (TreeItem*)featureIndex.internalPointer();
2985+ ind = createIndex(featureIndex.row(), featureIndex.column(), item);
2986+ } else {
2987+ for (int i=0; i < m_sFeatures.size(); ++i) {
2988+ if (m_sFeatures[i] == pFeature) {
2989+ ind = index(i, 0);
2990+ break;
2991+ }
2992 }
2993 }
2994+ emit(selectIndex(ind));
2995 }
2996
2997=== modified file 'mixxx/src/library/sidebarmodel.h'
2998--- mixxx/src/library/sidebarmodel.h 2011-03-27 20:12:33 +0000
2999+++ mixxx/src/library/sidebarmodel.h 2012-03-12 04:25:21 +0000
3000@@ -39,7 +39,7 @@
3001 void doubleClicked(const QModelIndex& index);
3002 void rightClicked(const QPoint& globalPos, const QModelIndex& index);
3003 void refreshData();
3004- void selectFeature(LibraryFeature* pFeature);
3005+ void slotFeatureSelect(LibraryFeature* pFeature, const QModelIndex& index = QModelIndex());
3006
3007 // Slots for every single QAbstractItemModel signal
3008 // void slotColumnsAboutToBeInserted(const QModelIndex& parent, int start, int end);
3009
3010=== modified file 'mixxx/src/library/trackcollection.cpp'
3011--- mixxx/src/library/trackcollection.cpp 2011-12-28 20:30:15 +0000
3012+++ mixxx/src/library/trackcollection.cpp 2012-03-12 04:25:21 +0000
3013@@ -15,9 +15,9 @@
3014 TrackCollection::TrackCollection(ConfigObject<ConfigValue>* pConfig)
3015 : m_pConfig(pConfig),
3016 m_db(QSqlDatabase::addDatabase("QSQLITE")), // defaultConnection
3017- m_cueDao(m_db),
3018 m_playlistDao(m_db),
3019 m_crateDao(m_db),
3020+ m_cueDao(m_db),
3021 m_trackDao(m_db, m_cueDao, m_playlistDao, m_crateDao, pConfig),
3022 m_supportedFileExtensionsRegex(
3023 SoundSourceProxy::supportedFileExtensionsRegex(),
3024
3025=== modified file 'mixxx/src/mixxx.cpp'
3026--- mixxx/src/mixxx.cpp 2012-01-16 03:04:52 +0000
3027+++ mixxx/src/mixxx.cpp 2012-03-12 04:25:21 +0000
3028@@ -263,8 +263,6 @@
3029 QDir().mkpath(QDir::homePath().append("/").append(SETTINGS_PATH));
3030 }
3031
3032-
3033-
3034 m_pLibrary = new Library(this, m_pConfig,
3035 bFirstRun || bUpgraded,
3036 m_pRecordingManager);
3037@@ -432,12 +430,9 @@
3038
3039 m_pWidgetParent = NULL;
3040 // Loads the skin as a child of m_pView
3041- // assignment itentional in next line
3042- if (!(m_pWidgetParent = m_pSkinLoader->loadDefaultSkin(m_pView,
3043- m_pKeyboard,
3044- m_pPlayerManager,
3045- m_pLibrary,
3046- m_pVCManager))) {
3047+ // assignment intentional in next line
3048+ if (!(m_pWidgetParent = m_pSkinLoader->loadDefaultSkin(
3049+ m_pView, m_pKeyboard, m_pPlayerManager, m_pLibrary, m_pVCManager))) {
3050 qDebug() << "Could not load default skin.";
3051 }
3052
3053
3054=== modified file 'mixxx/src/playerinfo.cpp'
3055--- mixxx/src/playerinfo.cpp 2011-03-25 05:31:57 +0000
3056+++ mixxx/src/playerinfo.cpp 2012-03-12 04:25:21 +0000
3057@@ -22,7 +22,7 @@
3058 #include "engine/enginexfader.h"
3059
3060 PlayerInfo::PlayerInfo()
3061-{
3062+ : m_currentlyPlayingDeck(0) {
3063 int i;
3064 m_iNumDecks = ControlObject::getControl(ConfigKey("[Master]","num_decks"))->get();
3065
3066@@ -36,13 +36,12 @@
3067 }
3068
3069 m_COxfader = new ControlObjectThread(ControlObject::getControl(ConfigKey("[Master]","crossfader")));
3070+
3071+ startTimer(2000);
3072 }
3073
3074-PlayerInfo::~PlayerInfo()
3075-{
3076+PlayerInfo::~PlayerInfo() {
3077 int i;
3078-
3079-
3080 m_loadedTrackMap.clear();
3081
3082 for (i = 1; i <= m_iNumDecks; i++) {
3083@@ -57,14 +56,12 @@
3084 delete m_COxfader;
3085 }
3086
3087-PlayerInfo &PlayerInfo::Instance()
3088-{
3089+PlayerInfo &PlayerInfo::Instance() {
3090 static PlayerInfo playerInfo;
3091 return playerInfo;
3092 }
3093
3094-TrackPointer PlayerInfo::getTrackInfo(QString group)
3095-{
3096+TrackPointer PlayerInfo::getTrackInfo(QString group) {
3097 QMutexLocker locker(&m_mutex);
3098
3099 if (m_loadedTrackMap.contains(group)) {
3100@@ -80,13 +77,15 @@
3101 m_loadedTrackMap[group] = track;
3102 }
3103
3104-int PlayerInfo::getCurrentPlayingDeck()
3105-{
3106+void PlayerInfo::timerEvent(QTimerEvent* pTimerEvent) {
3107+ updateCurrentPlayingDeck();
3108+}
3109+
3110+void PlayerInfo::updateCurrentPlayingDeck() {
3111 QMutexLocker locker(&m_mutex);
3112 int MaxVolume = 0;
3113 int MaxDeck = 0;
3114 int i;
3115-
3116
3117 for (i = 1; i <= m_iNumDecks; i++) {
3118 QString chan = QString("[Channel%1]").arg(i);
3119@@ -95,10 +94,10 @@
3120 float dvol;
3121 int orient;
3122
3123- if ( m_listCOPlay[chan]->get() == 0.0 )
3124+ if (m_listCOPlay[chan]->get() == 0.0 )
3125 continue;
3126
3127- if ( m_listCOpregain[chan]->get() <= 0.5 )
3128+ if (m_listCOpregain[chan]->get() <= 0.5 )
3129 continue;
3130
3131 if ((fvol = m_listCOVolume[chan]->get()) == 0.0 )
3132@@ -106,7 +105,7 @@
3133
3134 EngineXfader::getXfadeGains(xfl, xfr, m_COxfader->get(), 1.0, 0.0);
3135
3136- // Orientation goes: left is 0, center is 1, right is 2.
3137+ // Orientation goes: left is 0, center is 1, right is 2.
3138 // Leave math out of it...
3139 orient = m_listCOOrientation[chan]->get();
3140 if ( orient == 0 )
3141@@ -117,23 +116,30 @@
3142 xfvol = 1;
3143
3144 dvol = fvol * xfvol;
3145- if ( dvol > MaxVolume ) {
3146+ if (dvol > MaxVolume ) {
3147 MaxDeck = i;
3148 MaxVolume = dvol;
3149 }
3150 }
3151
3152- return MaxDeck;
3153-}
3154-
3155-TrackPointer PlayerInfo::getCurrentPlayingTrack()
3156-{
3157+ if (MaxDeck != m_currentlyPlayingDeck) {
3158+ m_currentlyPlayingDeck = MaxDeck;
3159+ m_mutex.unlock();
3160+ emit(currentPlayingDeckChanged(MaxDeck));
3161+ }
3162+}
3163+
3164+int PlayerInfo::getCurrentPlayingDeck() {
3165+ QMutexLocker locker(&m_mutex);
3166+ return m_currentlyPlayingDeck;
3167+}
3168+
3169+TrackPointer PlayerInfo::getCurrentPlayingTrack() {
3170 int deck = getCurrentPlayingDeck();
3171- if ( deck ) {
3172+ if (deck) {
3173 QString chan = QString("[Channel%1]").arg(deck);
3174 return getTrackInfo(chan);
3175 }
3176-
3177 return TrackPointer();
3178 }
3179
3180
3181=== modified file 'mixxx/src/playerinfo.h'
3182--- mixxx/src/playerinfo.h 2011-03-23 09:20:56 +0000
3183+++ mixxx/src/playerinfo.h 2012-03-12 04:25:21 +0000
3184@@ -20,7 +20,7 @@
3185 #include <QObject>
3186 #include <QMutex>
3187 #include <QMap>
3188-
3189+#include <QTimerEvent>
3190
3191 class ControlObjectThread;
3192
3193@@ -35,7 +35,14 @@
3194 void setTrackInfo(QString group, TrackPointer trackInfoObj);
3195 int getCurrentPlayingDeck();
3196 TrackPointer getCurrentPlayingTrack();
3197+
3198+ signals:
3199+ void currentPlayingDeckChanged(int deck);
3200+
3201 private:
3202+ void timerEvent(QTimerEvent* pTimerEvent);
3203+ void updateCurrentPlayingDeck();
3204+
3205 PlayerInfo();
3206 ~PlayerInfo();
3207 PlayerInfo(PlayerInfo const&);
3208@@ -48,6 +55,8 @@
3209 QMap<QString, ControlObjectThread*> m_listCOVolume;
3210 QMap<QString, ControlObjectThread*> m_listCOOrientation;
3211 QMap<QString, ControlObjectThread*> m_listCOpregain;
3212+
3213+ int m_currentlyPlayingDeck;
3214 };
3215
3216 #endif
3217
3218=== modified file 'mixxx/src/trackinfoobject.cpp'
3219--- mixxx/src/trackinfoobject.cpp 2011-12-28 20:30:15 +0000
3220+++ mixxx/src/trackinfoobject.cpp 2012-03-12 04:25:21 +0000
3221@@ -557,7 +557,7 @@
3222
3223 void TrackInfoObject::incTimesPlayed()
3224 {
3225- setPlayed(true); //setPlayed increases play count
3226+ setPlayedAndUpdatePlaycount(true);
3227 }
3228
3229 bool TrackInfoObject::getPlayed() const
3230@@ -567,20 +567,29 @@
3231 return bPlayed;
3232 }
3233
3234-void TrackInfoObject::setPlayed(bool bPlayed)
3235+void TrackInfoObject::setPlayedAndUpdatePlaycount(bool bPlayed)
3236 {
3237 QMutexLocker lock(&m_qMutex);
3238 if (bPlayed) {
3239 ++m_iTimesPlayed;
3240- setDirty(true);
3241+ setDirty(true);
3242 }
3243 else if (m_bPlayed && !bPlayed) {
3244 --m_iTimesPlayed;
3245- setDirty(true);
3246+ setDirty(true);
3247 }
3248 m_bPlayed = bPlayed;
3249 }
3250
3251+void TrackInfoObject::setPlayed(bool bPlayed)
3252+{
3253+ QMutexLocker lock(&m_qMutex);
3254+ if (bPlayed != m_bPlayed) {
3255+ m_bPlayed = bPlayed;
3256+ setDirty(true);
3257+ }
3258+}
3259+
3260 QString TrackInfoObject::getComment() const
3261 {
3262 QMutexLocker lock(&m_qMutex);
3263
3264=== modified file 'mixxx/src/trackinfoobject.h'
3265--- mixxx/src/trackinfoobject.h 2012-01-07 05:48:41 +0000
3266+++ mixxx/src/trackinfoobject.h 2012-03-12 04:25:21 +0000
3267@@ -189,8 +189,10 @@
3268 void incTimesPlayed();
3269 /** Returns true if track has been played this instance*/
3270 bool getPlayed() const;
3271- /** Set Played status*/
3272- void setPlayed(bool);
3273+ /** Set played status and increment or decrement playcount. */
3274+ void setPlayedAndUpdatePlaycount(bool);
3275+ /** Set played status without affecting the playcount */
3276+ void setPlayed(bool bPlayed);
3277
3278 int getId() const;
3279
3280
3281=== modified file 'mixxx/src/widget/wlibrarysidebar.cpp'
3282--- mixxx/src/widget/wlibrarysidebar.cpp 2011-03-27 21:15:15 +0000
3283+++ mixxx/src/widget/wlibrarysidebar.cpp 2012-03-12 04:25:21 +0000
3284@@ -199,4 +199,9 @@
3285 QItemSelectionModel* pModel = new QItemSelectionModel(model());
3286 pModel->select(index, QItemSelectionModel::Select);
3287 setSelectionModel(pModel);
3288+
3289+ if (index.parent().isValid()) {
3290+ expand(index.parent());
3291+ }
3292+ scrollTo(index);
3293 }