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

Proposed by Daniel Schürmann
Status: Merged
Merged at revision: 2990
Proposed branch: lp:~daschuer/mixxx/features_setlog
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 2721 lines (+1490/-224)
32 files modified
mixxx/build/depends.py (+26/-4)
mixxx/lib/soundtouch-1.6.0/STTypes.h (+1/-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/cratefeature.cpp (+106/-82)
mixxx/src/library/cratefeature.h (+4/-1)
mixxx/src/library/dao/cratedao.cpp (+5/-4)
mixxx/src/library/dao/cratedao.h (+3/-1)
mixxx/src/library/dao/playlistdao.cpp (+143/-29)
mixxx/src/library/dao/playlistdao.h (+23/-5)
mixxx/src/library/legacylibraryimporter.cpp (+1/-2)
mixxx/src/library/library.cpp (+2/-0)
mixxx/src/library/libraryfeature.h (+2/-1)
mixxx/src/library/parser.cpp (+1/-1)
mixxx/src/library/parsercsv.cpp (+238/-0)
mixxx/src/library/parsercsv.h (+41/-0)
mixxx/src/library/playlistfeature.cpp (+113/-57)
mixxx/src/library/playlistfeature.h (+5/-1)
mixxx/src/library/playlisttablemodel.cpp (+12/-0)
mixxx/src/library/playlisttablemodel.h (+1/-0)
mixxx/src/library/preparefeature.cpp (+13/-2)
mixxx/src/library/setlogfeature.cpp (+571/-0)
mixxx/src/library/setlogfeature.h (+93/-0)
mixxx/src/library/sidebarmodel.cpp (+50/-26)
mixxx/src/library/sidebarmodel.h (+1/-1)
mixxx/src/library/trackcollection.cpp (+1/-1)
mixxx/src/mixxx.cpp (+1/-1)
mixxx/src/trackinfoobject.cpp (+11/-2)
mixxx/src/trackinfoobject.h (+2/-0)
mixxx/src/widget/wlibrarysidebar.cpp (+5/-0)
To merge this branch: bzr merge lp:~daschuer/mixxx/features_setlog
Reviewer Review Type Date Requested Status
Mixxx Development Team Pending
Review via email: mp+78882@code.launchpad.net

Description of the change

This is an implementation of set log, a playlist filled with all the played songs from last DJ set.

Features:
* add a set log to Auto DJ
* export a setlog as m3u, m3u8, and pls playlist
* export a setlog as readable text and csv
* merge two set logs in case of mixxx restart

To post a comment you must log in.
lp:~daschuer/mixxx/features_setlog updated
2852. By Daniel Schürmann

merged with lp:mixxx

Revision history for this message
jus (jus) wrote :

Tested your branch and would like to few notes:
* In the treeview`s set log submenu sort from newest to oldest entry, currently it`s sorted from oldest to newest. The session that is currently running session should be on 1st position.
* The new icons (setlog & running session ) need some work, I can take care of it if you like.

* We had a small user survey about what format Mixxx users would like to generate session playlists in @ http://mixxx.org/forums/viewtopic.php?f=1&t=2463&p=9265 , txt/csv got the most votes. Is it possible with reasonable effort to add txt/csv export to your branch? That would increase setlog`s usability for professional application (think GEMA/ASCAP/BMI set lists).

Thanks for all your work,
jus

Revision history for this message
Daniel Schürmann (daschuer) wrote :

Hi Jus, Thank you for testing.

* I have decided to put the most recent setlog to the bottom because the most recent track is also at the bottom. I notice also that its a bit annoying to scroll to bottom. if you have a long list of set logs. If I find time, I will add a sorting option to context menu. Additional sub folder for archived set logs might be also a good idea.

* I will be happy, if you create improved icons.

* Are you able to provide me example GEMA/ASCAP/BMI set lists?

Kind regards,

Daniel

Revision history for this message
jus (jus) wrote :

There are various application options for Mixxx (playing live, broadcasting, making DJ tapes..) which may follow different rules for the report of royalties. They likely even differ from country to country.

The assistance we can give with the set log feature is to allow the setlist to be exported in a standard compliant format. I really think we are ok with txt/csv export which includes the important song metadata (like Title,Artist,Album,Composer) + the time/date that any song was played.

lp:~daschuer/mixxx/features_setlog updated
2853. By Daniel Schürmann

merged with lp:mixxx

Revision history for this message
Sean M. Pappalardo (pegasus-renegadetech) wrote :

Don't forget record label information as well, if present. (Required for US radio reporting.)

lp:~daschuer/mixxx/features_setlog updated
2854. By Daniel Schürmann

merged from lp:mixxx

2855. By Daniel Schürmann

merged with lp:mixxx

2856. By Daniel Schürmann

csv und m3u8 import and export

2857. By Daniel Schürmann

Readable text export

2858. By Daniel Schürmann

export position in 01. format

2859. By Daniel Schürmann

merged with lp:mixxx

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'mixxx/build/depends.py'
--- mixxx/build/depends.py 2011-11-08 20:10:32 +0000
+++ mixxx/build/depends.py 2011-12-10 16:03:24 +0000
@@ -233,23 +233,43 @@
233 '#lib/%s/FIFOSampleBuffer.cpp' % self.SOUNDTOUCH_PATH,233 '#lib/%s/FIFOSampleBuffer.cpp' % self.SOUNDTOUCH_PATH,
234 '#lib/%s/FIRFilter.cpp' % self.SOUNDTOUCH_PATH,234 '#lib/%s/FIRFilter.cpp' % self.SOUNDTOUCH_PATH,
235 '#lib/%s/PeakFinder.cpp' % self.SOUNDTOUCH_PATH,235 '#lib/%s/PeakFinder.cpp' % self.SOUNDTOUCH_PATH,
236 '#lib/%s/BPMDetect.cpp' % self.SOUNDTOUCH_PATH,236 '#lib/%s/BPMDetect.cpp' % self.SOUNDTOUCH_PATH]
237 '#lib/%s/mmx_optimized.cpp' % self.SOUNDTOUCH_PATH,
238 '#lib/%s/sse_optimized.cpp' % self.SOUNDTOUCH_PATH,]
239237
240 # SoundTouch CPU optimizations are only for x86238 # SoundTouch CPU optimizations are only for x86
241 # architectures. SoundTouch automatically ignores these files when it is239 # architectures. SoundTouch automatically ignores these files when it is
242 # not being built for an architecture that supports them.240 # not being built for an architecture that supports them.
243 cpu_detection = '#lib/%s/cpu_detect_x86_win.cpp' if build.toolchain_is_msvs else '#lib/%s/cpu_detect_x86_gcc.cpp'241 cpu_detection = '#lib/%s/cpu_detect_x86_win.cpp' if build.toolchain_is_msvs else \
242 '#lib/%s/cpu_detect_x86_gcc.cpp'
244 sources.append(cpu_detection % self.SOUNDTOUCH_PATH)243 sources.append(cpu_detection % self.SOUNDTOUCH_PATH)
244
245 # Check if the compiler has SSE extention enabled
246 # Allways the case on x64 (core instructions)
247 optimize = int(util.get_flags(build.env, 'optimize', 1))
248 if build.machine_is_64bit or \
249 (build.toolchain_is_msvs and optimize > 2) or \
250 (build.toolchain_is_gnu and optimize > 1):
251 sources.extend(
252 ['#lib/%s/mmx_optimized.cpp' % self.SOUNDTOUCH_PATH,
253 '#lib/%s/sse_optimized.cpp' % self.SOUNDTOUCH_PATH,
254 ])
255
245 return sources256 return sources
246257
258
247 def configure(self, build, conf):259 def configure(self, build, conf):
248 if build.platform_is_windows:260 if build.platform_is_windows:
249 # Regardless of the bitwidth, ST checks for WIN32261 # Regardless of the bitwidth, ST checks for WIN32
250 build.env.Append(CPPDEFINES = 'WIN32')262 build.env.Append(CPPDEFINES = 'WIN32')
251 build.env.Append(CPPPATH=['#lib/%s' % self.SOUNDTOUCH_PATH])263 build.env.Append(CPPPATH=['#lib/%s' % self.SOUNDTOUCH_PATH])
252264
265 # Check if the compiler has SSE extention enabled
266 # Allways the case on x64 (core instructions)
267 optimize = int(util.get_flags(build.env, 'optimize', 1))
268 if build.machine_is_64bit or \
269 (build.toolchain_is_msvs and optimize > 2) or \
270 (build.toolchain_is_gnu and optimize > 1):
271 build.env.Append(CPPDEFINES='SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS')
272
253class TagLib(Dependence):273class TagLib(Dependence):
254 def configure(self, build, conf):274 def configure(self, build, conf):
255 if not conf.CheckLib('tag'):275 if not conf.CheckLib('tag'):
@@ -420,6 +440,7 @@
420 "library/autodjfeature.cpp",440 "library/autodjfeature.cpp",
421 "library/mixxxlibraryfeature.cpp",441 "library/mixxxlibraryfeature.cpp",
422 "library/playlistfeature.cpp",442 "library/playlistfeature.cpp",
443 "library/setlogfeature.cpp",
423444
424 "library/browse/browsetablemodel.cpp",445 "library/browse/browsetablemodel.cpp",
425 "library/browse/browsethread.cpp",446 "library/browse/browsethread.cpp",
@@ -477,6 +498,7 @@
477 "library/parser.cpp",498 "library/parser.cpp",
478 "library/parserpls.cpp",499 "library/parserpls.cpp",
479 "library/parserm3u.cpp",500 "library/parserm3u.cpp",
501 "library/parsercsv.cpp",
480502
481 "bpm/bpmscheme.cpp",503 "bpm/bpmscheme.cpp",
482504
483505
=== modified file 'mixxx/lib/soundtouch-1.6.0/STTypes.h'
--- mixxx/lib/soundtouch-1.6.0/STTypes.h 2011-07-24 21:30:08 +0000
+++ mixxx/lib/soundtouch-1.6.0/STTypes.h 2011-12-10 16:03:24 +0000
@@ -95,7 +95,7 @@
95 /// routines compiled for whatever reason, you may disable these optimizations 95 /// routines compiled for whatever reason, you may disable these optimizations
96 /// to make the library compile.96 /// to make the library compile.
9797
98 #define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 198 // #define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1
9999
100 #endif100 #endif
101101
102102
=== added file 'mixxx/res/html/setlogs.html'
--- mixxx/res/html/setlogs.html 1970-01-01 00:00:00 +0000
+++ mixxx/res/html/setlogs.html 2011-12-10 16:03:24 +0000
@@ -0,0 +1,10 @@
1<h2>Set Logs</h2>
2<table border="0" cellpadding="5">
3 <tr>
4 <td>
5 <p>Set Logs are automatic generated lists of your last DJ sets.</p>
6 <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>
7 <p>When you restart mixxx you can join the current Set Log with the previous one. The track played flags are restored.</p>
8 </td>
9 </tr>
10</table>
011
=== added file 'mixxx/res/images/library/ic_library_setlog.png'
1Binary 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 2011-12-10 16:03:24 +0000 differ12Binary 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 2011-12-10 16:03:24 +0000 differ
=== added file 'mixxx/res/images/library/ic_library_setlog_current.png'
2Binary 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 2011-12-10 16:03:24 +0000 differ13Binary 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 2011-12-10 16:03:24 +0000 differ
=== modified file 'mixxx/res/mixxx.qrc'
--- mixxx/res/mixxx.qrc 2011-11-01 16:14:41 +0000
+++ mixxx/res/mixxx.qrc 2011-12-10 16:03:24 +0000
@@ -2,6 +2,7 @@
2 <qresource prefix="/">2 <qresource prefix="/">
3 <file>html/crates.html</file>3 <file>html/crates.html</file>
4 <file>html/playlists.html</file>4 <file>html/playlists.html</file>
5 <file>html/setlogs.html</file>
5 <file>images/mixxx-icon.png</file>6 <file>images/mixxx-icon.png</file>
6 <file>images/ic_mixxx_window.png</file>7 <file>images/ic_mixxx_window.png</file>
7 <file>images/templates/logo_mixxx.png</file>8 <file>images/templates/logo_mixxx.png</file>
@@ -32,6 +33,8 @@
32 <file>images/library/ic_library_rhythmbox.png</file>33 <file>images/library/ic_library_rhythmbox.png</file>
33 <file>images/library/ic_library_traktor.png</file>34 <file>images/library/ic_library_traktor.png</file>
34 <file>images/library/ic_library_recordings.png</file>35 <file>images/library/ic_library_recordings.png</file>
36 <file>images/library/ic_library_setlog.png</file>
37 <file>images/library/ic_library_setlog_current.png</file>
35 <file>translations/mixxx_ar.qm</file>38 <file>translations/mixxx_ar.qm</file>
36 <file>translations/mixxx_bg.qm</file>39 <file>translations/mixxx_bg.qm</file>
37 <file>translations/mixxx_br.qm</file>40 <file>translations/mixxx_br.qm</file>
3841
=== modified file 'mixxx/src/basetrackplayer.cpp'
--- mixxx/src/basetrackplayer.cpp 2011-11-26 06:41:02 +0000
+++ mixxx/src/basetrackplayer.cpp 2011-12-10 16:03:24 +0000
@@ -197,7 +197,7 @@
197 if(!m_pLoadedTrack->getHeaderParsed())197 if(!m_pLoadedTrack->getHeaderParsed())
198 SoundSourceProxy::ParseHeader(m_pLoadedTrack.data());198 SoundSourceProxy::ParseHeader(m_pLoadedTrack.data());
199199
200 m_pLoadedTrack->setPlayed(true);200 // m_pLoadedTrack->setPlayed(true); // Actually the song is loaded but not played
201201
202 // Generate waveform summary202 // Generate waveform summary
203 //TODO: Consider reworking this visual resample stuff... need to ask rryan about this -- Albert.203 //TODO: Consider reworking this visual resample stuff... need to ask rryan about this -- Albert.
204204
=== modified file 'mixxx/src/dlgautodj.cpp'
--- mixxx/src/dlgautodj.cpp 2011-11-30 06:19:47 +0000
+++ mixxx/src/dlgautodj.cpp 2011-12-10 16:03:24 +0000
@@ -38,7 +38,7 @@
38 "mixxx.db.model.autodj");38 "mixxx.db.model.autodj");
39 int playlistId = m_playlistDao.getPlaylistIdFromName(AUTODJ_TABLE);39 int playlistId = m_playlistDao.getPlaylistIdFromName(AUTODJ_TABLE);
40 if (playlistId < 0) {40 if (playlistId < 0) {
41 m_playlistDao.createPlaylist(AUTODJ_TABLE, true);41 m_playlistDao.createPlaylist(AUTODJ_TABLE, PlaylistDAO::PLHT_AUTO_DJ);
42 playlistId = m_playlistDao.getPlaylistIdFromName(AUTODJ_TABLE);42 playlistId = m_playlistDao.getPlaylistIdFromName(AUTODJ_TABLE);
43 }43 }
44 m_pAutoDJTableModel->setPlaylist(playlistId);44 m_pAutoDJTableModel->setPlaylist(playlistId);
4545
=== modified file 'mixxx/src/library/cratefeature.cpp'
--- mixxx/src/library/cratefeature.cpp 2011-11-27 06:59:02 +0000
+++ mixxx/src/library/cratefeature.cpp 2011-12-10 16:03:24 +0000
@@ -9,6 +9,7 @@
9#include "library/parser.h"9#include "library/parser.h"
10#include "library/parserm3u.h"10#include "library/parserm3u.h"
11#include "library/parserpls.h"11#include "library/parserpls.h"
12#include "library/parsercsv.h"
1213
13#include "library/cratetablemodel.h"14#include "library/cratetablemodel.h"
14#include "library/trackcollection.h"15#include "library/trackcollection.h"
@@ -22,10 +23,12 @@
22CrateFeature::CrateFeature(QObject* parent,23CrateFeature::CrateFeature(QObject* parent,
23 TrackCollection* pTrackCollection, ConfigObject<ConfigValue>* pConfig)24 TrackCollection* pTrackCollection, ConfigObject<ConfigValue>* pConfig)
24 : m_pTrackCollection(pTrackCollection),25 : m_pTrackCollection(pTrackCollection),
26 m_crateDao(pTrackCollection->getCrateDAO()),
25 m_crateListTableModel(this, pTrackCollection->getDatabase()),27 m_crateListTableModel(this, pTrackCollection->getDatabase()),
26 m_pConfig(pConfig),28 m_crateTableModel(this, pTrackCollection),
27 m_crateTableModel(this, pTrackCollection) {29 m_pConfig(pConfig) {
28 m_pCreateCrateAction = new QAction(tr("New Crate"),this);30 Q_UNUSED(parent);
31 m_pCreateCrateAction = new QAction(tr("New Crate"),this);
29 connect(m_pCreateCrateAction, SIGNAL(triggered()),32 connect(m_pCreateCrateAction, SIGNAL(triggered()),
30 this, SLOT(slotCreateCrate()));33 this, SLOT(slotCreateCrate()));
3134
@@ -48,6 +51,19 @@
48 connect(m_pExportPlaylistAction, SIGNAL(triggered()),51 connect(m_pExportPlaylistAction, SIGNAL(triggered()),
49 this, SLOT(slotExportPlaylist()));52 this, SLOT(slotExportPlaylist()));
5053
54 connect(&m_crateDao, SIGNAL(added(int)),
55 this, SLOT(slotCrateTableChanged(int)));
56
57 connect(&m_crateDao, SIGNAL(deleted(int)),
58 this, SLOT(slotCrateTableChanged(int)));
59
60 connect(&m_crateDao, SIGNAL(renamed(int)),
61 this, SLOT(slotCrateTableChanged(int)));
62
63 connect(&m_crateDao, SIGNAL(lockChanged(int)),
64 this, SLOT(slotCrateTableChanged(int)));
65
66
51 m_crateListTableModel.setTable("crates");67 m_crateListTableModel.setTable("crates");
52 m_crateListTableModel.setSort(m_crateListTableModel.fieldIndex("name"),68 m_crateListTableModel.setSort(m_crateListTableModel.fieldIndex("name"),
53 Qt::AscendingOrder);69 Qt::AscendingOrder);
@@ -57,7 +73,7 @@
57 // construct child model73 // construct child model
58 TreeItem *rootItem = new TreeItem();74 TreeItem *rootItem = new TreeItem();
59 m_childModel.setRootItem(rootItem);75 m_childModel.setRootItem(rootItem);
60 constructChildModel();76 constructChildModel(-1);
61}77}
6278
63CrateFeature::~CrateFeature() {79CrateFeature::~CrateFeature() {
@@ -78,12 +94,13 @@
78}94}
7995
80bool CrateFeature::dropAccept(QUrl url) {96bool CrateFeature::dropAccept(QUrl url) {
97 Q_UNUSED(url)
81 return false;98 return false;
82}99}
83100
84bool CrateFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {101bool CrateFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {
85 QString crateName = index.data().toString();102 QString crateName = index.data().toString();
86 int crateId = m_pTrackCollection->getCrateDAO().getCrateIdByName(crateName);103 int crateId = m_crateDao.getCrateIdByName(crateName);
87104
88 //XXX: See the comment in PlaylistFeature::dropAcceptChild() about105 //XXX: See the comment in PlaylistFeature::dropAcceptChild() about
89 // QUrl::toLocalFile() vs. QUrl::toString() usage.106 // QUrl::toLocalFile() vs. QUrl::toString() usage.
@@ -95,10 +112,8 @@
95 qDebug() << "CrateFeature::dropAcceptChild adding track"112 qDebug() << "CrateFeature::dropAcceptChild adding track"
96 << trackId << "to crate" << crateId;113 << trackId << "to crate" << crateId;
97114
98 CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();
99
100 if (trackId >= 0)115 if (trackId >= 0)
101 return crateDao.addTrackToCrate(trackId, crateId);116 return m_crateDao.addTrackToCrate(trackId, crateId);
102 return false;117 return false;
103}118}
104119
@@ -110,9 +125,8 @@
110bool CrateFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) {125bool CrateFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) {
111 //TODO: Filter by supported formats regex and reject anything that doesn't match.126 //TODO: Filter by supported formats regex and reject anything that doesn't match.
112 QString crateName = index.data().toString();127 QString crateName = index.data().toString();
113 CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();128 int crateId = m_crateDao.getCrateIdByName(crateName);
114 int crateId = crateDao.getCrateIdByName(crateName);129 bool locked = m_crateDao.isCrateLocked(crateId);
115 bool locked = crateDao.isCrateLocked(crateId);
116130
117 QFileInfo file(url.toLocalFile());131 QFileInfo file(url.toLocalFile());
118 bool formatSupported = SoundSourceProxy::isFilenameSupported(file.fileName());132 bool formatSupported = SoundSourceProxy::isFilenameSupported(file.fileName());
@@ -143,7 +157,7 @@
143 if (!index.isValid())157 if (!index.isValid())
144 return;158 return;
145 QString crateName = index.data().toString();159 QString crateName = index.data().toString();
146 int crateId = m_pTrackCollection->getCrateDAO().getCrateIdByName(crateName);160 int crateId = m_crateDao.getCrateIdByName(crateName);
147 m_crateTableModel.setCrate(crateId);161 m_crateTableModel.setCrate(crateId);
148 emit(showTrackModel(&m_crateTableModel));162 emit(showTrackModel(&m_crateTableModel));
149}163}
@@ -160,10 +174,9 @@
160 m_lastRightClickedIndex = index;174 m_lastRightClickedIndex = index;
161175
162 QString crateName = index.data().toString();176 QString crateName = index.data().toString();
163 CrateDAO& crateDAO = m_pTrackCollection->getCrateDAO();177 int crateId = m_crateDao.getCrateIdByName(crateName);
164 int crateId = crateDAO.getCrateIdByName(crateName);
165178
166 bool locked = crateDAO.isCrateLocked(crateId);179 bool locked = m_crateDao.isCrateLocked(crateId);
167180
168 m_pDeleteCrateAction->setEnabled(!locked);181 m_pDeleteCrateAction->setEnabled(!locked);
169 m_pRenameCrateAction->setEnabled(!locked);182 m_pRenameCrateAction->setEnabled(!locked);
@@ -186,7 +199,6 @@
186199
187 QString name;200 QString name;
188 bool validNameGiven = false;201 bool validNameGiven = false;
189 CrateDAO& crateDao = m_pTrackCollection->getCrateDAO();
190202
191 do {203 do {
192 bool ok = false;204 bool ok = false;
@@ -199,7 +211,7 @@
199 if (!ok)211 if (!ok)
200 return;212 return;
201213
202 int existingId = crateDao.getCrateIdByName(name);214 int existingId = m_crateDao.getCrateIdByName(name);
203215
204 if (existingId != -1) {216 if (existingId != -1) {
205 QMessageBox::warning(NULL,217 QMessageBox::warning(NULL,
@@ -217,17 +229,10 @@
217229
218 } while (!validNameGiven);230 } while (!validNameGiven);
219231
220 bool crateCreated = crateDao.createCrate(name);232 int crate_id = m_crateDao.createCrate(name);
221233
222 if (crateCreated) {234 if (crate_id != -1) {
223 clearChildModel();
224 m_crateListTableModel.select();
225 constructChildModel();
226 // Switch to the new crate.
227 int crate_id = crateDao.getCrateIdByName(name);
228 m_crateTableModel.setCrate(crate_id);
229 emit(showTrackModel(&m_crateTableModel));235 emit(showTrackModel(&m_crateTableModel));
230 // TODO(XXX) set sidebar selection
231 emit(featureUpdated());236 emit(featureUpdated());
232 } else {237 } else {
233 qDebug() << "Error creating crate with name " << name;238 qDebug() << "Error creating crate with name " << name;
@@ -241,22 +246,19 @@
241246
242void CrateFeature::slotDeleteCrate() {247void CrateFeature::slotDeleteCrate() {
243 QString crateName = m_lastRightClickedIndex.data().toString();248 QString crateName = m_lastRightClickedIndex.data().toString();
244 CrateDAO &crateDao = m_pTrackCollection->getCrateDAO();249 int crateId = m_crateDao.getCrateIdByName(crateName);
245 int crateId = crateDao.getCrateIdByName(crateName);250 bool locked = m_crateDao.isCrateLocked(crateId);
246 bool locked = crateDao.isCrateLocked(crateId);
247251
248 if (locked) {252 if (locked) {
249 qDebug() << "Skipping crate deletion because crate" << crateId << "is locked.";253 qDebug() << "Skipping crate deletion because crate" << crateId << "is locked.";
250 return;254 return;
251 }255 }
252256
253 bool deleted = crateDao.deleteCrate(crateId);257 bool deleted = m_crateDao.deleteCrate(crateId);
254258
255 if (deleted) {259 if (deleted) {
256 clearChildModel();
257 m_crateListTableModel.select();
258 constructChildModel();
259 emit(featureUpdated());260 emit(featureUpdated());
261 activate();
260 } else {262 } else {
261 qDebug() << "Failed to delete crateId" << crateId;263 qDebug() << "Failed to delete crateId" << crateId;
262 }264 }
@@ -264,9 +266,8 @@
264266
265void CrateFeature::slotRenameCrate() {267void CrateFeature::slotRenameCrate() {
266 QString oldName = m_lastRightClickedIndex.data().toString();268 QString oldName = m_lastRightClickedIndex.data().toString();
267 CrateDAO &crateDao = m_pTrackCollection->getCrateDAO();269 int crateId = m_crateDao.getCrateIdByName(oldName);
268 int crateId = crateDao.getCrateIdByName(oldName);270 bool locked = m_crateDao.isCrateLocked(crateId);
269 bool locked = crateDao.isCrateLocked(crateId);
270271
271 if (locked) {272 if (locked) {
272 qDebug() << "Skipping crate rename because crate" << crateId << "is locked.";273 qDebug() << "Skipping crate rename because crate" << crateId << "is locked.";
@@ -289,7 +290,7 @@
289 return;290 return;
290 }291 }
291292
292 int existingId = m_pTrackCollection->getCrateDAO().getCrateIdByName(newName);293 int existingId = m_crateDao.getCrateIdByName(newName);
293294
294 if (existingId != -1) {295 if (existingId != -1) {
295 QMessageBox::warning(NULL,296 QMessageBox::warning(NULL,
@@ -307,12 +308,8 @@
307 } while (!validNameGiven);308 } while (!validNameGiven);
308309
309310
310 if (m_pTrackCollection->getCrateDAO().renameCrate(crateId, newName)) {311 if (m_crateDao.renameCrate(crateId, newName)) {
311 clearChildModel();
312 m_crateListTableModel.select();
313 constructChildModel();
314 emit(featureUpdated());312 emit(featureUpdated());
315 m_crateTableModel.setCrate(crateId);
316 } else {313 } else {
317 qDebug() << "Failed to rename crateId" << crateId;314 qDebug() << "Failed to rename crateId" << crateId;
318 }315 }
@@ -321,17 +318,12 @@
321void CrateFeature::slotToggleCrateLock()318void CrateFeature::slotToggleCrateLock()
322{319{
323 QString crateName = m_lastRightClickedIndex.data().toString();320 QString crateName = m_lastRightClickedIndex.data().toString();
324 CrateDAO& crateDAO = m_pTrackCollection->getCrateDAO();321 int crateId = m_crateDao.getCrateIdByName(crateName);
325 int crateId = crateDAO.getCrateIdByName(crateName);322 bool locked = !m_crateDao.isCrateLocked(crateId);
326 bool locked = !crateDAO.isCrateLocked(crateId);
327323
328 if (!crateDAO.setCrateLocked(crateId, locked)) {324 if (!m_crateDao.setCrateLocked(crateId, locked)) {
329 qDebug() << "Failed to toggle lock of crateId " << crateId;325 qDebug() << "Failed to toggle lock of crateId " << crateId;
330 }326 }
331
332 TreeItem* crateItem = m_childModel.getItem(m_lastRightClickedIndex);
333 crateItem->setIcon(
334 locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
335}327}
336328
337329
@@ -340,14 +332,14 @@
340 * we require the sidebar model not to reset.332 * we require the sidebar model not to reset.
341 * This method queries the database and does dynamic insertion333 * This method queries the database and does dynamic insertion
342*/334*/
343void CrateFeature::constructChildModel()335QModelIndex CrateFeature::constructChildModel(int selected_id)
344{336{
345 QList<TreeItem*> data_list;337 QList<TreeItem*> data_list;
346 int nameColumn = m_crateListTableModel.record().indexOf("name");338 int nameColumn = m_crateListTableModel.record().indexOf("name");
347 int idColumn = m_crateListTableModel.record().indexOf("id");339 int idColumn = m_crateListTableModel.record().indexOf("id");
348 //Access the invisible root item340 int selected_row = -1;
341 // Access the invisible root item
349 TreeItem* root = m_childModel.getItem(QModelIndex());342 TreeItem* root = m_childModel.getItem(QModelIndex());
350 CrateDAO &crateDao = m_pTrackCollection->getCrateDAO();
351343
352 for (int row = 0; row < m_crateListTableModel.rowCount(); ++row) {344 for (int row = 0; row < m_crateListTableModel.rowCount(); ++row) {
353 QModelIndex ind = m_crateListTableModel.index(row, nameColumn);345 QModelIndex ind = m_crateListTableModel.index(row, nameColumn);
@@ -355,14 +347,23 @@
355 ind = m_crateListTableModel.index(row, idColumn);347 ind = m_crateListTableModel.index(row, idColumn);
356 int crate_id = m_crateListTableModel.data(ind).toInt();348 int crate_id = m_crateListTableModel.data(ind).toInt();
357349
358 //Create the TreeItem whose parent is the invisible root item350 if ( selected_id == crate_id) {
351 // save index for selection
352 selected_row = row; m_childModel.index(selected_row, 0);
353 }
354
355 // Create the TreeItem whose parent is the invisible root item
359 TreeItem* item = new TreeItem(crate_name, crate_name, this, root);356 TreeItem* item = new TreeItem(crate_name, crate_name, this, root);
360 bool locked = crateDao.isCrateLocked(crate_id);357 bool locked = m_crateDao.isCrateLocked(crate_id);
361 item->setIcon(locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());358 item->setIcon(locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
362 data_list.append(item);359 data_list.append(item);
363 }360 }
364 //Append all the newly created TreeItems in a dynamic way to the childmodel361 // Append all the newly created TreeItems in a dynamic way to the childmodel
365 m_childModel.insertRows(data_list, 0, m_crateListTableModel.rowCount());362 m_childModel.insertRows(data_list, 0, m_crateListTableModel.rowCount());
363 if (selected_row == -1) {
364 return QModelIndex();
365 }
366 return m_childModel.index(selected_row, 0);
366}367}
367368
368/**369/**
@@ -382,7 +383,7 @@
382 NULL,383 NULL,
383 tr("Import Playlist"),384 tr("Import Playlist"),
384 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),385 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
385 tr("Playlist Files (*.m3u *.m3u8 *.pls)"));386 tr("Playlist Files (*.m3u *.m3u8 *.pls *.csv)"));
386 // Exit method if user cancelled the open dialog.387 // Exit method if user cancelled the open dialog.
387 if (playlist_file.isNull() || playlist_file.isEmpty() ) return;388 if (playlist_file.isNull() || playlist_file.isEmpty() ) return;
388389
@@ -394,6 +395,8 @@
394 playlist_parser = new ParserM3u();395 playlist_parser = new ParserM3u();
395 } else if (playlist_file.endsWith(".pls", Qt::CaseInsensitive)) {396 } else if (playlist_file.endsWith(".pls", Qt::CaseInsensitive)) {
396 playlist_parser = new ParserPls();397 playlist_parser = new ParserPls();
398 } else if (playlist_file.endsWith(".csv", Qt::CaseInsensitive)) {
399 playlist_parser = new ParserCsv();
397 } else {400 } else {
398 return;401 return;
399 }402 }
@@ -423,40 +426,61 @@
423 NULL,426 NULL,
424 tr("Export Crate"),427 tr("Export Crate"),
425 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),428 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
426 tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;PLS Playlist (*.pls)"));429 tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;PLS Playlist (*.pls);;Text CSV (*.csv);;Readable Text (*.txt)"));
427 // Exit method if user cancelled the open dialog.430 // Exit method if user cancelled the open dialog.
428 if (file_location.isNull() || file_location.isEmpty())431 if (file_location.isNull() || file_location.isEmpty()) {
429 return;432 return;
433 }
430 // check config if relative paths are desired434 // check config if relative paths are desired
431 bool useRelativePath = static_cast<bool>(435 bool useRelativePath = static_cast<bool>(
432 m_pConfig->getValueString(436 m_pConfig->getValueString(
433 ConfigKey("[Library]", "UseRelativePathOnExport")).toInt());437 ConfigKey("[Library]", "UseRelativePathOnExport")).toInt());
434438
435 // Create and populate a list of files of the crate439 // Create list of files of the crate
436 QList<QString> playlist_items;440 QList<QString> playlist_items;
437 QScopedPointer<CrateTableModel> pCrateTableModel(441 QScopedPointer<CrateTableModel> pCrateTableModel(
438 new CrateTableModel(this, m_pTrackCollection));442 new CrateTableModel(this, m_pTrackCollection));
439 pCrateTableModel->setCrate(m_crateTableModel.getCrate());443 pCrateTableModel->setCrate(m_crateTableModel.getCrate());
440 pCrateTableModel->select();444 pCrateTableModel->select();
441 int rows = pCrateTableModel->rowCount();445 int rows = pCrateTableModel->rowCount();
442 for (int i = 0; i < rows; ++i) {446
443 QModelIndex index = pCrateTableModel->index(i, 0);447 if (file_location.endsWith(".csv", Qt::CaseInsensitive)) {
444 playlist_items << pCrateTableModel->getTrackLocation(index);448 ParserCsv::writeCSVFile(file_location, pCrateTableModel.data(), useRelativePath);
445 }449 } else if (file_location.endsWith(".txt", Qt::CaseInsensitive)) {
446450 ParserCsv::writeReadableTextFile(file_location, pCrateTableModel.data());
447 if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {451 } else{
448 ParserPls::writePLSFile(file_location, playlist_items, useRelativePath);452 // populate a list of files of the crate
449 } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {453 QList<QString> playlist_items;
450 ParserM3u::writeM3U8File(file_location, playlist_items,454 int rows = pCrateTableModel->rowCount();
451 useRelativePath);455 for (int i = 0; i < rows; ++i) {
452 } else {456 QModelIndex index = m_crateTableModel.index(i, 0);
453 // Default export to M3U if file extension is missing457 playlist_items << m_crateTableModel.getTrackLocation(index);
454 if (!file_location.endsWith(".m3u", Qt::CaseInsensitive)) {458 }
455 qDebug() << "Crate export: No valid file extension specified. Appending .m3u "459
456 << "and exporting to M3U.";460 if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {
457 file_location.append(".m3u");461 ParserPls::writePLSFile(file_location, playlist_items, useRelativePath);
458 }462 } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {
459 ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);463 ParserM3u::writeM3U8File(file_location, playlist_items, useRelativePath);
460 }464 } else {
461}465 //default export to M3U if file extension is missing
462466 if(!file_location.endsWith(".m3u", Qt::CaseInsensitive))
467 {
468 qDebug() << "Crate export: No valid file extension specified. Appending .m3u "
469 << "and exporting to M3U.";
470 file_location.append(".m3u");
471 }
472 ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);
473 }
474 }
475}
476
477void CrateFeature::slotCrateTableChanged(int crateId) {
478 //qDebug() << "slotPlaylistTableChanged() playlistId:" << playlistId;
479 clearChildModel();
480 m_crateListTableModel.select();
481 m_lastRightClickedIndex = constructChildModel(crateId);
482 // Switch the view to the crate.
483 m_crateTableModel.setCrate(crateId);
484 // Update selection
485 emit(featureSelect(this, m_lastRightClickedIndex));
486}
463487
=== modified file 'mixxx/src/library/cratefeature.h'
--- mixxx/src/library/cratefeature.h 2011-03-12 13:10:25 +0000
+++ mixxx/src/library/cratefeature.h 2011-12-10 16:03:24 +0000
@@ -49,11 +49,14 @@
49 void slotImportPlaylist();49 void slotImportPlaylist();
50 void slotExportPlaylist();50 void slotExportPlaylist();
5151
52 void slotCrateTableChanged(int playlistId);
53
52 private:54 private:
53 void constructChildModel();55 QModelIndex constructChildModel(int selected_id);
54 void clearChildModel();56 void clearChildModel();
55 57
56 TrackCollection* m_pTrackCollection;58 TrackCollection* m_pTrackCollection;
59 CrateDAO& m_crateDao;
57 QAction *m_pCreateCrateAction;60 QAction *m_pCreateCrateAction;
58 QAction *m_pDeleteCrateAction;61 QAction *m_pDeleteCrateAction;
59 QAction *m_pRenameCrateAction;62 QAction *m_pRenameCrateAction;
6063
=== modified file 'mixxx/src/library/dao/cratedao.cpp'
--- mixxx/src/library/dao/cratedao.cpp 2011-10-21 00:42:23 +0000
+++ mixxx/src/library/dao/cratedao.cpp 2011-12-10 16:03:24 +0000
@@ -31,19 +31,19 @@
31 return query.value(0).toInt();31 return query.value(0).toInt();
32}32}
3333
34bool CrateDAO::createCrate(const QString& name) {34int CrateDAO::createCrate(const QString& name) {
35 QSqlQuery query(m_database);35 QSqlQuery query(m_database);
36 query.prepare("INSERT INTO " CRATE_TABLE " (name) VALUES (:name)");36 query.prepare("INSERT INTO " CRATE_TABLE " (name) VALUES (:name)");
37 query.bindValue(":name", name);37 query.bindValue(":name", name);
3838
39 if (!query.exec()) {39 if (!query.exec()) {
40 LOG_FAILED_QUERY(query);40 LOG_FAILED_QUERY(query);
41 return false;41 return -1;
42 }42 }
4343
44 int crateId = query.lastInsertId().toInt();44 int crateId = query.lastInsertId().toInt();
45 emit(added(crateId));45 emit(added(crateId));
46 return true;46 return crateId;
47}47}
4848
49bool CrateDAO::renameCrate(int crateId, const QString& newName) {49bool CrateDAO::renameCrate(int crateId, const QString& newName) {
@@ -56,6 +56,7 @@
56 LOG_FAILED_QUERY(query);56 LOG_FAILED_QUERY(query);
57 return false;57 return false;
58 }58 }
59 emit(renamed(crateId));
59 return true;60 return true;
60}61}
6162
@@ -71,7 +72,7 @@
71 LOG_FAILED_QUERY(query);72 LOG_FAILED_QUERY(query);
72 return false;73 return false;
73 }74 }
7475 emit(lockChanged(crateId));
75 return true;76 return true;
76}77}
7778
7879
=== modified file 'mixxx/src/library/dao/cratedao.h'
--- mixxx/src/library/dao/cratedao.h 2011-10-21 00:42:23 +0000
+++ mixxx/src/library/dao/cratedao.h 2011-12-10 16:03:24 +0000
@@ -28,7 +28,7 @@
28 void initialize();28 void initialize();
2929
30 unsigned int crateCount();30 unsigned int crateCount();
31 bool createCrate(const QString& name);31 int createCrate(const QString& name);
32 bool deleteCrate(int crateId);32 bool deleteCrate(int crateId);
33 bool renameCrate(int crateId, const QString& newName);33 bool renameCrate(int crateId, const QString& newName);
34 bool setCrateLocked(int crateId, bool locked);34 bool setCrateLocked(int crateId, bool locked);
@@ -47,6 +47,8 @@
47 void changed(int crateId);47 void changed(int crateId);
48 void trackAdded(int crateId, int trackId);48 void trackAdded(int crateId, int trackId);
49 void trackRemoved(int crateId, int trackId);49 void trackRemoved(int crateId, int trackId);
50 void renamed(int crateId);
51 void lockChanged(int crateId);
5052
51 private:53 private:
52 QSqlDatabase& m_database;54 QSqlDatabase& m_database;
5355
=== modified file 'mixxx/src/library/dao/playlistdao.cpp'
--- mixxx/src/library/dao/playlistdao.cpp 2011-10-21 00:42:23 +0000
+++ mixxx/src/library/dao/playlistdao.cpp 2011-12-10 16:03:24 +0000
@@ -22,7 +22,7 @@
22/** Create a playlist with the given name.22/** Create a playlist with the given name.
23 @param name The name of the playlist to be created.23 @param name The name of the playlist to be created.
24*/24*/
25bool PlaylistDAO::createPlaylist(QString name, bool hidden)25int PlaylistDAO::createPlaylist(QString name, enum hidden_type hidden)
26{26{
27 // qDebug() << "PlaylistDAO::createPlaylist"27 // qDebug() << "PlaylistDAO::createPlaylist"
28 // << QThread::currentThread()28 // << QThread::currentThread()
@@ -38,7 +38,7 @@
38 if (!query.exec()) {38 if (!query.exec()) {
39 LOG_FAILED_QUERY(query);39 LOG_FAILED_QUERY(query);
40 m_database.rollback();40 m_database.rollback();
41 return false;41 return -1;
42 }42 }
4343
44 //Get the id of the last playlist.44 //Get the id of the last playlist.
@@ -50,34 +50,34 @@
5050
51 //qDebug() << "Inserting playlist" << name << "at position" << position;51 //qDebug() << "Inserting playlist" << name << "at position" << position;
5252
53 query.prepare("INSERT INTO Playlists (name, position, hidden) "53 query.prepare("INSERT INTO Playlists (name, position, hidden, date_created, date_modified) "
54 "VALUES (:name, :position, :hidden)");54 "VALUES (:name, :position, :hidden, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)");
55 query.bindValue(":name", name);55 query.bindValue(":name", name);
56 query.bindValue(":position", position);56 query.bindValue(":position", position);
57 query.bindValue(":hidden", hidden ? 1 : 0);57 query.bindValue(":hidden", (int)hidden);
5858
59 if (!query.exec()) {59 if (!query.exec()) {
60 LOG_FAILED_QUERY(query);60 LOG_FAILED_QUERY(query);
61 m_database.rollback();61 m_database.rollback();
62 return false;62 return -1;
63 }63 }
6464
65 int playlistId = query.lastInsertId().toInt();65 int playlistId = query.lastInsertId().toInt();
66 //Commit the transaction66 //Commit the transaction
67 m_database.commit();67 m_database.commit();
68 emit(added(playlistId));68 emit(added(playlistId));
69 return true;69 return playlistId;
70}70}
7171
72/** Find out the name of the playlist at the given position */72/** Find out the name of the playlist at the given Id */
73QString PlaylistDAO::getPlaylistName(unsigned int position)73QString PlaylistDAO::getPlaylistName(int playlistId)
74{74{
75 // qDebug() << "PlaylistDAO::getPlaylistName" << QThread::currentThread() << m_database.connectionName();75 // qDebug() << "PlaylistDAO::getPlaylistName" << QThread::currentThread() << m_database.connectionName();
7676
77 QSqlQuery query(m_database);77 QSqlQuery query(m_database);
78 query.prepare("SELECT name FROM Playlists "78 query.prepare("SELECT name FROM Playlists "
79 "WHERE position = :position");79 "WHERE id= :id");
80 query.bindValue(":position", position);80 query.bindValue(":id", playlistId);
8181
82 if (!query.exec()) {82 if (!query.exec()) {
83 LOG_FAILED_QUERY(query);83 LOG_FAILED_QUERY(query);
@@ -153,6 +153,9 @@
153 if (!query.exec()) {153 if (!query.exec()) {
154 LOG_FAILED_QUERY(query);154 LOG_FAILED_QUERY(query);
155 }155 }
156 else {
157 emit(renamed(playlistId));
158 }
156}159}
157160
158161
@@ -169,6 +172,7 @@
169 LOG_FAILED_QUERY(query);172 LOG_FAILED_QUERY(query);
170 return false;173 return false;
171 }174 }
175 emit(lockChanged(playlistId));
172 return true;176 return true;
173}177}
174178
@@ -249,7 +253,7 @@
249 return numRecords;253 return numRecords;
250}254}
251255
252int PlaylistDAO::getPlaylistId(int position)256int PlaylistDAO::getPlaylistId(int index)
253{257{
254 // qDebug() << "PlaylistDAO::getPlaylistId"258 // qDebug() << "PlaylistDAO::getPlaylistId"
255 // << QThread::currentThread() << m_database.connectionName();259 // << QThread::currentThread() << m_database.connectionName();
@@ -260,7 +264,7 @@
260 if (query.exec()) {264 if (query.exec()) {
261 int currentRow = 0;265 int currentRow = 0;
262 while(query.next()) {266 while(query.next()) {
263 if (currentRow++ == position) {267 if (currentRow++ == index) {
264 int id = query.value(0).toInt();268 int id = query.value(0).toInt();
265 return id;269 return id;
266 }270 }
@@ -272,22 +276,37 @@
272 return -1;276 return -1;
273}277}
274278
279enum PlaylistDAO::hidden_type PlaylistDAO::getHiddenType(int playlistId){
280 // qDebug() << "PlaylistDAO::getHiddenType"
281 // << QThread::currentThread() << m_database.connectionName();
282
283 QSqlQuery query(m_database);
284 query.prepare("SELECT hidden FROM Playlists WHERE id = :id");
285 query.bindValue(":id", playlistId);
286
287 if (query.exec()) {
288 if (query.next()) {
289 return (enum hidden_type)query.value(0).toInt();
290 }
291 } else {
292 LOG_FAILED_QUERY(query);
293 }
294 qDebug() << "PlaylistDAO::hidden_type returns PLHT_UNKNOWN for playlistId " << playlistId;
295 return PLHT_UNKNOWN;
296}
297
275bool PlaylistDAO::isHidden(int playlistId) {298bool PlaylistDAO::isHidden(int playlistId) {
276 // qDebug() << "PlaylistDAO::isHidden"299 // qDebug() << "PlaylistDAO::isHidden"
277 // << QThread::currentThread() << m_database.connectionName();300 // << QThread::currentThread() << m_database.connectionName();
278301
279 QSqlQuery query(m_database);302 enum hidden_type ht = getHiddenType(playlistId);
280 query.prepare("SELECT hidden FROM Playlists WHERE id = :id");
281 query.bindValue(":id", playlistId);
282303
283 if (query.exec()) {304 if(ht==PLHT_NOT_HIDDEN){
284 if (query.next()) {305 return false;
285 return query.value(0).toBool();306 }
286 }307 else{
287 } else {308 return true;
288 LOG_FAILED_QUERY(query);309 }
289 }
290 return false;
291}310}
292311
293void PlaylistDAO::removeTrackFromPlaylists(int trackId) {312void PlaylistDAO::removeTrackFromPlaylists(int trackId) {
@@ -396,7 +415,7 @@
396 emit(changed(playlistId));415 emit(changed(playlistId));
397}416}
398417
399void PlaylistDAO::addToAutoDJQueue(int playlistId) {418void PlaylistDAO::addToAutoDJQueue(int playlistId, bool bTop) {
400 //qDebug() << "Adding tracks from playlist " << playlistId << " to the Auto-DJ Queue";419 //qDebug() << "Adding tracks from playlist " << playlistId << " to the Auto-DJ Queue";
401420
402 // Query the PlaylistTracks database to locate tracks in the selected playlist421 // Query the PlaylistTracks database to locate tracks in the selected playlist
@@ -411,7 +430,102 @@
411 // Get the ID of the Auto-DJ playlist430 // Get the ID of the Auto-DJ playlist
412 int autoDJId = getPlaylistIdFromName(AUTODJ_TABLE);431 int autoDJId = getPlaylistIdFromName(AUTODJ_TABLE);
413 // Loop through the tracks, adding them to the Auto-DJ Queue432 // Loop through the tracks, adding them to the Auto-DJ Queue
414 while(query.next()) {433
415 appendTrackToPlaylist(query.value(0).toInt(), autoDJId);434 int i = 2; // Start at position 2 because position 1 was already loaded to the deck
416 }435
417}436 while (query.next()) {
437 if (bTop) {
438 insertTrackIntoPlaylist(query.value(0).toInt(), autoDJId, i++);
439 }
440 else {
441 appendTrackToPlaylist(query.value(0).toInt(), autoDJId);
442 }
443 }
444}
445
446int PlaylistDAO::getPreviousPlaylist(int currentPlaylistId, enum hidden_type hidden) {
447 //Start the transaction
448 m_database.transaction();
449
450 //Find out the highest position existing in the playlist so we know what
451 //position this track should have.
452 QSqlQuery query(m_database);
453 query.prepare("SELECT max(id) as id FROM Playlists "
454 "WHERE id < :id AND hidden = :hidden");
455 query.bindValue(":id", currentPlaylistId);
456 query.bindValue(":hidden", hidden);
457 query.exec();
458
459 //Print out any SQL error, if there was one.
460 if (query.lastError().isValid()) {
461 qDebug() << "appendTrackToPlaylist" << query.lastError();
462 // m_database.rollback();
463 // return;
464 }
465
466 // Get the position of the highest playlist...
467 int previousPlaylistId = -1;
468 if (query.next()) {
469 previousPlaylistId = query.value(query.record().indexOf("id")).toInt();
470 }
471 return previousPlaylistId;
472}
473
474
475void PlaylistDAO::copyPlaylistTracks(int sourcePlaylistID, int targetPlaylistId) {
476
477 //Start the transaction
478 m_database.transaction();
479
480 //Find out the highest position existing in the target playlist so we know what
481 //position this track should have.
482 QSqlQuery query(m_database);
483 query.prepare("SELECT max(position) as position FROM PlaylistTracks "
484 "WHERE playlist_id = :id");
485 query.bindValue(":id", targetPlaylistId);
486 query.exec();
487
488 //Print out any SQL error, if there was one.
489 if (query.lastError().isValid()) {
490 qDebug() << "appendTrackToPlaylist" << query.lastError();
491 // m_database.rollback();
492 // return;
493 }
494
495 // Get the position of the highest playlist...
496 int position = 0;
497 if (query.next()) {
498 position = query.value(query.record().indexOf("position")).toInt();
499 }
500
501
502 // Query Tracks from the source Playlist
503 query.prepare("SELECT track_id FROM PlaylistTracks "
504 "WHERE playlist_id = :plid");
505 query.bindValue(":plid", sourcePlaylistID);
506 query.exec();
507
508 //Print out any SQL error, if there was one.
509 if (query.lastError().isValid()) {
510 qDebug() << "addToAutoDJQueue" << query.lastError();
511 return;
512 }
513
514
515 QSqlQuery query2(m_database);
516 //Insert the Tracks into the PlaylistTracks table
517 query2.prepare("INSERT INTO PlaylistTracks (playlist_id, track_id, position)"
518 "VALUES (:playlist_id, :track_id, :position)");
519 query2.bindValue(":playlist_id", targetPlaylistId);
520
521 while (query.next()) {
522 query2.bindValue(":track_id", query.value(0));
523 query2.bindValue(":position", ++position);
524 query2.exec();
525 }
526
527 //Start the transaction
528 m_database.commit();
529 emit(changed(targetPlaylistId));
530}
531
418532
=== modified file 'mixxx/src/library/dao/playlistdao.h'
--- mixxx/src/library/dao/playlistdao.h 2011-10-21 00:42:23 +0000
+++ mixxx/src/library/dao/playlistdao.h 2011-12-10 16:03:24 +0000
@@ -13,17 +13,27 @@
13const QString PLAYLISTTRACKSTABLE_TRACKID = "track_id";13const QString PLAYLISTTRACKSTABLE_TRACKID = "track_id";
14const QString PLAYLISTTRACKSTABLE_POSITION = "position";14const QString PLAYLISTTRACKSTABLE_POSITION = "position";
15const QString PLAYLISTTRACKSTABLE_PLAYLISTID = "playlist_id";15const QString PLAYLISTTRACKSTABLE_PLAYLISTID = "playlist_id";
16const QString PLAYLISTTRACKSTABLE_LOCATION = "location";
17const QString PLAYLISTTRACKSTABLE_ARTIST = "artist";
18const QString PLAYLISTTRACKSTABLE_TITLE = "title";
19
1620
17class PlaylistDAO : public QObject, public virtual DAO {21class PlaylistDAO : public QObject, public virtual DAO {
18 Q_OBJECT22 Q_OBJECT
19 public:23 public:
24 enum hidden_type {
25 PLHT_NOT_HIDDEN = 0,
26 PLHT_AUTO_DJ = 1,
27 PLHT_SET_LOG = 2,
28 PLHT_UNKNOWN = -1
29 };
20 PlaylistDAO(QSqlDatabase& database);30 PlaylistDAO(QSqlDatabase& database);
21 virtual ~PlaylistDAO();31 virtual ~PlaylistDAO();
2232
23 void initialize();33 void initialize();
24 void setDatabase(QSqlDatabase& database) { m_database = database; };34 void setDatabase(QSqlDatabase& database) { m_database = database; };
25 /** Create a playlist */35 /** Create a playlist */
26 bool createPlaylist(QString name, bool hidden = false);36 int createPlaylist(QString name, enum hidden_type = PLHT_NOT_HIDDEN);
27 /** Delete a playlist */37 /** Delete a playlist */
28 void deletePlaylist(int playlistId);38 void deletePlaylist(int playlistId);
29 /** Rename a playlist */39 /** Rename a playlist */
@@ -37,15 +47,17 @@
37 /** Find out how many playlists exist. */47 /** Find out how many playlists exist. */
38 unsigned int playlistCount();48 unsigned int playlistCount();
39 /** Get the name of the playlist at the given position */49 /** Get the name of the playlist at the given position */
40 QString getPlaylistName(unsigned int position);50 QString getPlaylistName(int playlistId);
41 // Get the playlist id by its name51 // Get the playlist id by its name
42 int getPlaylistIdFromName(QString name);52 int getPlaylistIdFromName(QString name);
43 /** Get the id of the playlist at position. Note that the position is the53 /** Get the id of the playlist at index. Note that the index is the
44 * natural position in the database table, not the display order position54 * natural position in the database table, not the display order position
45 * column stored in the database. */55 * column stored in the database. */
46 int getPlaylistId(int position);56 int getPlaylistId(int index);
47 // Returns true if the playlist with playlistId is hidden57 // Returns true if the playlist with playlistId is hidden
48 bool isHidden(int playlistId);58 bool isHidden(int playlistId);
59 // Returns cause of playlistId is hidden
60 enum hidden_type getHiddenType(int playlistId);
49 /** Remove a track from all playlists */61 /** Remove a track from all playlists */
50 void removeTrackFromPlaylists(int trackId);62 void removeTrackFromPlaylists(int trackId);
51 /** Remove a track from a playlist */63 /** Remove a track from a playlist */
@@ -53,13 +65,19 @@
53 /** Insert a track into a specific position in a playlist */65 /** Insert a track into a specific position in a playlist */
54 void insertTrackIntoPlaylist(int trackId, int playlistId, int position);66 void insertTrackIntoPlaylist(int trackId, int playlistId, int position);
55 /** Add a playlist to the Auto-DJ Queue */67 /** Add a playlist to the Auto-DJ Queue */
56 void addToAutoDJQueue(int playlistId);68 void addToAutoDJQueue(int playlistId, bool bTop);
69 // Get a playlist from the same hidden type that was created just before
70 int getPreviousPlaylist(int currentPlaylistId, enum hidden_type hidden);
71 // Copy tracks from source to target playlist
72 void copyPlaylistTracks( int sourcePlaylistID, int targetPlaylistId);
57 signals:73 signals:
58 void added(int playlistId);74 void added(int playlistId);
59 void deleted(int playlistId);75 void deleted(int playlistId);
60 void changed(int playlistId);76 void changed(int playlistId);
61 void trackAdded(int playlistId, int trackId, int position);77 void trackAdded(int playlistId, int trackId, int position);
62 void trackRemoved(int playlistId, int trackId, int position);78 void trackRemoved(int playlistId, int trackId, int position);
79 void renamed(int playlistId);
80 void lockChanged(int playlistId);
63 private:81 private:
64 QSqlDatabase& m_database;82 QSqlDatabase& m_database;
65 DISALLOW_COPY_AND_ASSIGN(PlaylistDAO);83 DISALLOW_COPY_AND_ASSIGN(PlaylistDAO);
6684
=== modified file 'mixxx/src/library/legacylibraryimporter.cpp'
--- mixxx/src/library/legacylibraryimporter.cpp 2010-11-19 01:54:28 +0000
+++ mixxx/src/library/legacylibraryimporter.cpp 2011-12-10 16:03:24 +0000
@@ -148,8 +148,7 @@
148148
149 //Create the playlist with the imported name.149 //Create the playlist with the imported name.
150 //qDebug() << "Importing playlist:" << current.name;150 //qDebug() << "Importing playlist:" << current.name;
151 m_playlistDao.createPlaylist(current.name, false);151 int playlistId = m_playlistDao.createPlaylist(current.name);
152 int playlistId = m_playlistDao.getPlaylistIdFromName(current.name);
153152
154 //For each track ID in the XML...153 //For each track ID in the XML...
155 QList<int> trackIDs = current.indexes;154 QList<int> trackIDs = current.indexes;
156155
=== modified file 'mixxx/src/library/library.cpp'
--- mixxx/src/library/library.cpp 2011-09-25 06:39:39 +0000
+++ mixxx/src/library/library.cpp 2011-12-10 16:03:24 +0000
@@ -21,6 +21,7 @@
21#include "library/promotracksfeature.h"21#include "library/promotracksfeature.h"
22#include "library/traktor/traktorfeature.h"22#include "library/traktor/traktorfeature.h"
23#include "library/librarycontrol.h"23#include "library/librarycontrol.h"
24#include "library/setlogfeature.h"
2425
25#include "widget/wtracktableview.h"26#include "widget/wtracktableview.h"
26#include "widget/wlibrary.h"27#include "widget/wlibrary.h"
@@ -60,6 +61,7 @@
60 addFeature(m_pCrateFeature);61 addFeature(m_pCrateFeature);
61 addFeature(new BrowseFeature(this, pConfig, m_pTrackCollection, m_pRecordingManager));62 addFeature(new BrowseFeature(this, pConfig, m_pTrackCollection, m_pRecordingManager));
62 addFeature(new RecordingFeature(this, pConfig, m_pTrackCollection, m_pRecordingManager));63 addFeature(new RecordingFeature(this, pConfig, m_pTrackCollection, m_pRecordingManager));
64 addFeature(new SetlogFeature(this, pConfig, m_pTrackCollection));
63 addFeature(new PrepareFeature(this, pConfig, m_pTrackCollection));65 addFeature(new PrepareFeature(this, pConfig, m_pTrackCollection));
64 //iTunes and Rhythmbox should be last until we no longer have an obnoxious66 //iTunes and Rhythmbox should be last until we no longer have an obnoxious
65 //messagebox popup when you select them. (This forces you to reach for your67 //messagebox popup when you select them. (This forces you to reach for your
6668
=== modified file 'mixxx/src/library/libraryfeature.h'
--- mixxx/src/library/libraryfeature.h 2011-03-10 14:06:26 +0000
+++ mixxx/src/library/libraryfeature.h 2011-12-10 16:03:24 +0000
@@ -66,7 +66,8 @@
66 void featureIsLoading(LibraryFeature*);66 void featureIsLoading(LibraryFeature*);
67 /** emit this signal if the foreign music collection has been imported/parsed. **/67 /** emit this signal if the foreign music collection has been imported/parsed. **/
68 void featureLoadingFinished(LibraryFeature*s);68 void featureLoadingFinished(LibraryFeature*s);
6969 /** emit this signal to move the selection **/
70 void featureSelect(LibraryFeature* pFeature, const QModelIndex& index);
7071
71};72};
7273
7374
=== modified file 'mixxx/src/library/parser.cpp'
--- mixxx/src/library/parser.cpp 2011-11-27 04:30:58 +0000
+++ mixxx/src/library/parser.cpp 2011-12-10 16:03:24 +0000
@@ -87,7 +87,7 @@
87 }87 }
8888
89 const unsigned char* bytes = (const unsigned char *)string;89 const unsigned char* bytes = (const unsigned char *)string;
90 while(*bytes) {90 while (*bytes) {
91 if( (// ASCII91 if( (// ASCII
92 bytes[0] == 0x09 ||92 bytes[0] == 0x09 ||
93 bytes[0] == 0x0A ||93 bytes[0] == 0x0A ||
9494
=== added file 'mixxx/src/library/parsercsv.cpp'
--- mixxx/src/library/parsercsv.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/parsercsv.cpp 2011-12-10 16:03:24 +0000
@@ -0,0 +1,238 @@
1//
2// C++ Implementation: parsercsv
3//
4// Description: module to parse Comma-Separated Values (CSV) formated playlists (rfc4180)
5//
6//
7// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
8// Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011
9// Author: Daniel Schürmann daschuer@gmx.de, (C) 2011
10//
11// Copyright: See COPYING file that comes with this distribution
12//
13//
14#include <QTextStream>
15#include <QDebug>
16#include <QDir>
17#include <QMessageBox>
18#include "parsercsv.h"
19#include <QUrl>
20
21ParserCsv::ParserCsv() : Parser()
22{
23}
24
25ParserCsv::~ParserCsv()
26{
27
28}
29
30
31QList<QString> ParserCsv::parse(QString sFilename)
32{
33 QFile file(sFilename);
34 QString basepath = sFilename.section('/', 0, -2);
35
36 clearLocations();
37 //qDebug() << "ParserCsv: Starting to parse.";
38 if (file.open(QIODevice::ReadOnly) && !isBinary(sFilename)) {
39 QByteArray ba = file.readAll();
40
41 QList<QList<QString> > tokens = tokenize( ba, ',');
42
43 // detect Location column
44 int loc_coll = 0x7fffffff;
45 if (tokens.size()) {
46 for (int i = 0; i < tokens[0].size(); ++i) {
47 if (tokens[0][i] == tr("Location")) {
48 loc_coll = i;
49 break;
50 }
51 }
52 for (int i = 1; i < tokens.size(); ++i) {
53 if (loc_coll < tokens[i].size()) {
54 // Todo: check if path is relative
55 QFileInfo fi = tokens[i][loc_coll];
56 if (fi.isRelative()){
57 // add base path
58 qDebug() << "is relative" << basepath << fi.filePath();
59 fi.setFile(basepath,fi.filePath());
60 }
61 m_sLocations.append(fi.filePath());
62
63 }
64 }
65 }
66
67 file.close();
68
69 if(m_sLocations.count() != 0)
70 return m_sLocations;
71 else
72 return QList<QString>(); // NULL pointer returned when no locations were found
73
74 }
75
76 file.close();
77 return QList<QString>(); //if we get here something went wrong
78}
79
80// Code was posted at http://www.qtcentre.org/threads/35511-Parsing-CSV-data
81// by "adzajac" and adapted to use QT Classes
82QList<QList<QString> > ParserCsv::tokenize(const QByteArray& str, char delimiter) {
83 QList<QList<QString> > tokens;
84
85 unsigned int row = 0;
86 bool quotes = false;
87 QByteArray field = "";
88
89 tokens.append(QList<QString>());
90
91 for (int pos = 0; pos < str.length(); ++pos) {
92 char c = str[pos];
93 if (!quotes && c == '"' ){
94 quotes = true;
95 } else if (quotes && c== '"' ){
96 if (pos + 1 < str.length() && str[pos+1]== '"') {
97 field.append(c);
98 pos++;
99 } else {
100 quotes = false;
101 }
102 } else if (!quotes && c == delimiter) {
103 if (isUtf8(field.data())) {
104 tokens[row].append(QString::fromUtf8(field));
105 } else {
106 tokens[row].append(QString::fromLatin1(field));
107 }
108 field.clear();
109 } else if (!quotes && (c == '\r' || c == '\n')) {
110 if (isUtf8(field.data())) {
111 tokens[row].append(QString::fromUtf8(field));
112 } else {
113 tokens[row].append(QString::fromLatin1(field));
114 }
115 field.clear();
116 tokens.append(QList<QString>());
117 row++;
118 } else {
119 field.push_back(c);
120 }
121 }
122 return tokens;
123}
124
125bool ParserCsv::writeCSVFile(const QString &file_str, BaseSqlTableModel* pPlaylistTableModel, bool useRelativePath)
126{
127 /*
128 * Important note:
129 * On Windows \n will produce a <CR><CL> (=\r\n)
130 * On Linux and OS X \n is <CR> (which remains \n)
131 */
132
133 QFile file(file_str);
134 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)){
135 QMessageBox::warning(NULL,tr("Playlist Export Failed"),
136 tr("Could not create file")+" "+file_str);
137 return false;
138 }
139 //Base folder of file
140 QString base = file_str.section('/', 0, -2);
141 QDir base_dir(base);
142
143 qDebug() << "Basepath: " << base;
144 QTextStream out(&file);
145 out.setCodec("UTF-8"); // rfc4180: Common usage of CSV is US-ASCII ...
146 // Using UTF-8 to get around codepage issues
147 // and it's the default encoding in Ooo Calc
148
149 // writing header section
150 bool first = true;
151 int columns = pPlaylistTableModel->columnCount();
152 for (int i = 0; i < columns; ++i) {
153 if (pPlaylistTableModel->isColumnInternal(i)) {
154 continue;
155 }
156 if (!first){
157 out << ",";
158 } else {
159 first = false;
160 }
161 out << "\"";
162 QString field = pPlaylistTableModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
163 out << field.replace('\"', "\"\""); // escape "
164 out << "\"";
165 }
166 out << "\r\n"; // CRLF according to rfc4180
167
168
169 int rows = pPlaylistTableModel->rowCount();
170 for (int j = 0; j < rows; j++) {
171 // writing fields section
172 first = true;
173 for(int i = 0; i < columns; ++i){
174 if (pPlaylistTableModel->isColumnInternal(i)) {
175 continue;
176 }
177 if (!first){
178 out << ",";
179 } else {
180 first = false;
181 }
182 out << "\"";
183 QString field = pPlaylistTableModel->data(pPlaylistTableModel->index(j,i)).toString();
184 if (useRelativePath && i == pPlaylistTableModel->fieldIndex(PLAYLISTTRACKSTABLE_LOCATION)) {
185 field = base_dir.relativeFilePath(field);
186 }
187 out << field.replace('\"', "\"\""); // escape "
188 out << "\"";
189 }
190 out << "\r\n"; // CRLF according to rfc4180
191 }
192 return true;
193}
194
195bool ParserCsv::writeReadableTextFile(const QString &file_str, BaseSqlTableModel* pPlaylistTableModel)
196{
197 /*
198 * Important note:
199 * On Windows \n will produce a <CR><CL> (=\r\n)
200 * On Linux and OS X \n is <CR> (which remains \n)
201 */
202
203 QFile file(file_str);
204 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)){
205 QMessageBox::warning(NULL,tr("Readable text Export Failed"),
206 tr("Could not create file")+" "+file_str);
207 return false;
208 }
209
210 QTextStream out(&file);
211
212 // export each row as "01. 00:00 Artist - Title"
213
214 int i; // fieldIndex
215 int rows = pPlaylistTableModel->rowCount();
216 for (int j = 0; j < rows; j++) {
217 // writing fields section
218 i = pPlaylistTableModel->fieldIndex(PLAYLISTTRACKSTABLE_POSITION);
219 if (i >= 0){
220 int nr = pPlaylistTableModel->data(pPlaylistTableModel->index(j,i)).toInt();
221 out << QString("%1.").arg(nr,2,10,QLatin1Char('0'));
222 }
223 // TODO(DSC) // Fill in Cue point
224
225 i = pPlaylistTableModel->fieldIndex(PLAYLISTTRACKSTABLE_ARTIST);
226 if (i >= 0){
227 out << " ";
228 out << pPlaylistTableModel->data(pPlaylistTableModel->index(j,i)).toString();
229 }
230 i = pPlaylistTableModel->fieldIndex(PLAYLISTTRACKSTABLE_TITLE);
231 if (i >= 0){
232 out << " - ";
233 out << pPlaylistTableModel->data(pPlaylistTableModel->index(j,i)).toString();;
234 }
235 out << "\n";
236 }
237 return true;
238}
0239
=== added file 'mixxx/src/library/parsercsv.h'
--- mixxx/src/library/parsercsv.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/parsercsv.h 2011-12-10 16:03:24 +0000
@@ -0,0 +1,41 @@
1//
2// C++ Interface: parserm3u
3//
4// Description: Interface header parse Comma-Separated Values (CSV) formated playlists (rfc4180)
5//
6//
7// Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
8// Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011
9// Author: Daniel Schürmann daschuer@gmx.de, (C) 2011
10//
11// Copyright: See COPYING file that comes with this distribution
12//
13//
14#include "parser.h"
15#include "library/basesqltablemodel.h"
16
17#ifndef PARSERCSV_H
18#define PARSERCSV_H
19
20class QTextStream;
21
22class ParserCsv : public Parser
23{
24 Q_OBJECT
25public:
26 ParserCsv();
27 virtual ~ParserCsv();
28 /**Overwriting function parse in class Parser**/
29 QList<QString> parse(QString);
30 // Playlist Export
31 static bool writeCSVFile(const QString &file, BaseSqlTableModel* pPlaylistTableModel, bool useRelativePath);
32 // Readable Text export
33 static bool writeReadableTextFile(const QString &file, BaseSqlTableModel* pPlaylistTableModel);
34private:
35 /**Reads a line from the file and returns filepath if a valid file**/
36 QList<QList<QString> > tokenize(const QByteArray& str, char delimiter);
37
38
39};
40
41#endif
042
=== modified file 'mixxx/src/library/playlistfeature.cpp'
--- mixxx/src/library/playlistfeature.cpp 2011-11-30 05:27:44 +0000
+++ mixxx/src/library/playlistfeature.cpp 2011-12-10 16:03:24 +0000
@@ -8,6 +8,7 @@
8#include "library/parser.h"8#include "library/parser.h"
9#include "library/parserm3u.h"9#include "library/parserm3u.h"
10#include "library/parserpls.h"10#include "library/parserpls.h"
11#include "library/parsercsv.h"
1112
1213
13#include "widget/wlibrary.h"14#include "widget/wlibrary.h"
@@ -26,8 +27,8 @@
26 m_pTrackCollection(pTrackCollection),27 m_pTrackCollection(pTrackCollection),
27 m_playlistDao(pTrackCollection->getPlaylistDAO()),28 m_playlistDao(pTrackCollection->getPlaylistDAO()),
28 m_trackDao(pTrackCollection->getTrackDAO()),29 m_trackDao(pTrackCollection->getTrackDAO()),
29 m_pConfig(pConfig),30 m_playlistTableModel(this, pTrackCollection->getDatabase()),
30 m_playlistTableModel(this, pTrackCollection->getDatabase()) {31 m_pConfig(pConfig) {
31 m_pPlaylistTableModel = new PlaylistTableModel(this, pTrackCollection,32 m_pPlaylistTableModel = new PlaylistTableModel(this, pTrackCollection,
32 "mixxx.db.model.playlist");33 "mixxx.db.model.playlist");
3334
@@ -35,10 +36,14 @@
35 connect(m_pCreatePlaylistAction, SIGNAL(triggered()),36 connect(m_pCreatePlaylistAction, SIGNAL(triggered()),
36 this, SLOT(slotCreatePlaylist()));37 this, SLOT(slotCreatePlaylist()));
3738
38 m_pAddToAutoDJAction = new QAction(tr("Add to Auto-DJ Queue"),this);39 m_pAddToAutoDJAction = new QAction(tr("Add to Auto DJ bottom"),this);
39 connect(m_pAddToAutoDJAction, SIGNAL(triggered()),40 connect(m_pAddToAutoDJAction, SIGNAL(triggered()),
40 this, SLOT(slotAddToAutoDJ()));41 this, SLOT(slotAddToAutoDJ()));
4142
43 m_pAddToAutoDJTopAction = new QAction(tr("Add to Auto DJ top 2"),this);
44 connect(m_pAddToAutoDJTopAction, SIGNAL(triggered()),
45 this, SLOT(slotAddToAutoDJTop()));
46
42 m_pDeletePlaylistAction = new QAction(tr("Remove"),this);47 m_pDeletePlaylistAction = new QAction(tr("Remove"),this);
43 connect(m_pDeletePlaylistAction, SIGNAL(triggered()),48 connect(m_pDeletePlaylistAction, SIGNAL(triggered()),
44 this, SLOT(slotDeletePlaylist()));49 this, SLOT(slotDeletePlaylist()));
@@ -58,6 +63,18 @@
58 connect(m_pExportPlaylistAction, SIGNAL(triggered()),63 connect(m_pExportPlaylistAction, SIGNAL(triggered()),
59 this, SLOT(slotExportPlaylist()));64 this, SLOT(slotExportPlaylist()));
6065
66 connect(&m_playlistDao, SIGNAL(added(int)),
67 this, SLOT(slotPlaylistTableChanged(int)));
68
69 connect(&m_playlistDao, SIGNAL(deleted(int)),
70 this, SLOT(slotPlaylistTableChanged(int)));
71
72 connect(&m_playlistDao, SIGNAL(renamed(int)),
73 this, SLOT(slotPlaylistTableChanged(int)));
74
75 connect(&m_playlistDao, SIGNAL(lockChanged(int)),
76 this, SLOT(slotPlaylistTableChanged(int)));
77
61 // Setup the sidebar playlist model78 // Setup the sidebar playlist model
62 m_playlistTableModel.setTable("Playlists");79 m_playlistTableModel.setTable("Playlists");
63 m_playlistTableModel.setFilter("hidden=0");80 m_playlistTableModel.setFilter("hidden=0");
@@ -68,7 +85,7 @@
68 //construct child model85 //construct child model
69 TreeItem *rootItem = new TreeItem();86 TreeItem *rootItem = new TreeItem();
70 m_childModel.setRootItem(rootItem);87 m_childModel.setRootItem(rootItem);
71 constructChildModel();88 constructChildModel(-1);
72}89}
7390
74PlaylistFeature::~PlaylistFeature() {91PlaylistFeature::~PlaylistFeature() {
@@ -77,6 +94,7 @@
77 delete m_pDeletePlaylistAction;94 delete m_pDeletePlaylistAction;
78 delete m_pImportPlaylistAction;95 delete m_pImportPlaylistAction;
79 delete m_pAddToAutoDJAction;96 delete m_pAddToAutoDJAction;
97 delete m_pAddToAutoDJTopAction;
80 delete m_pRenamePlaylistAction;98 delete m_pRenamePlaylistAction;
81 delete m_pLockPlaylistAction;99 delete m_pLockPlaylistAction;
82}100}
@@ -93,6 +111,8 @@
93void PlaylistFeature::bindWidget(WLibrarySidebar* sidebarWidget,111void PlaylistFeature::bindWidget(WLibrarySidebar* sidebarWidget,
94 WLibrary* libraryWidget,112 WLibrary* libraryWidget,
95 MixxxKeyboard* keyboard) {113 MixxxKeyboard* keyboard) {
114 Q_UNUSED(sidebarWidget);
115 Q_UNUSED(keyboard);
96 WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget);116 WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget);
97 connect(this, SIGNAL(showPage(const QUrl&)),117 connect(this, SIGNAL(showPage(const QUrl&)),
98 edit, SLOT(setSource(const QUrl&)));118 edit, SLOT(setSource(const QUrl&)));
@@ -142,6 +162,7 @@
142 menu.addAction(m_pCreatePlaylistAction);162 menu.addAction(m_pCreatePlaylistAction);
143 menu.addSeparator();163 menu.addSeparator();
144 menu.addAction(m_pAddToAutoDJAction);164 menu.addAction(m_pAddToAutoDJAction);
165 menu.addAction(m_pAddToAutoDJTopAction);
145 menu.addAction(m_pRenamePlaylistAction);166 menu.addAction(m_pRenamePlaylistAction);
146 menu.addAction(m_pDeletePlaylistAction);167 menu.addAction(m_pDeletePlaylistAction);
147 menu.addAction(m_pLockPlaylistAction);168 menu.addAction(m_pLockPlaylistAction);
@@ -183,17 +204,10 @@
183204
184 } while (!validNameGiven);205 } while (!validNameGiven);
185206
186 bool playlistCreated = m_playlistDao.createPlaylist(name);207 int playlistId = m_playlistDao.createPlaylist(name);
187208
188 if (playlistCreated) {209 if (playlistId != -1) {
189 clearChildModel();
190 m_playlistTableModel.select();
191 constructChildModel();
192 emit(featureUpdated());210 emit(featureUpdated());
193 //Switch the view to the new playlist.
194 int playlistId = m_playlistDao.getPlaylistIdFromName(name);
195 m_pPlaylistTableModel->setPlaylist(playlistId);
196 // TODO(XXX) set sidebar selection
197 emit(showTrackModel(m_pPlaylistTableModel));211 emit(showTrackModel(m_pPlaylistTableModel));
198 }212 }
199 else {213 else {
@@ -251,11 +265,7 @@
251 } while (!validNameGiven);265 } while (!validNameGiven);
252266
253 m_playlistDao.renamePlaylist(playlistId, newName);267 m_playlistDao.renamePlaylist(playlistId, newName);
254 clearChildModel();268 emit(featureUpdated());
255 m_playlistTableModel.select();
256 constructChildModel();
257 emit(featureUpdated());
258 m_pPlaylistTableModel->setPlaylist(playlistId);
259}269}
260270
261271
@@ -268,9 +278,6 @@
268 if (!m_playlistDao.setPlaylistLocked(playlistId, locked)) {278 if (!m_playlistDao.setPlaylistLocked(playlistId, locked)) {
269 qDebug() << "Failed to toggle lock of playlistId " << playlistId;279 qDebug() << "Failed to toggle lock of playlistId " << playlistId;
270 }280 }
271
272 TreeItem* playlistItem = m_childModel.getItem(m_lastRightClickedIndex);
273 playlistItem->setIcon(locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
274}281}
275282
276void PlaylistFeature::slotDeletePlaylist()283void PlaylistFeature::slotDeletePlaylist()
@@ -284,20 +291,17 @@
284 return;291 return;
285 }292 }
286293
287 if (m_lastRightClickedIndex.isValid() &&294 if (m_lastRightClickedIndex.isValid()) {
288 !m_playlistDao.isPlaylistLocked(playlistId)) {
289 Q_ASSERT(playlistId >= 0);295 Q_ASSERT(playlistId >= 0);
290296
291 clearChildModel();
292 m_playlistDao.deletePlaylist(playlistId);297 m_playlistDao.deletePlaylist(playlistId);
293 m_playlistTableModel.select();
294 constructChildModel();
295 emit(featureUpdated());298 emit(featureUpdated());
299 activate();
296 }300 }
297
298}301}
299302
300bool PlaylistFeature::dropAccept(QUrl url) {303bool PlaylistFeature::dropAccept(QUrl url) {
304 Q_UNUSED(url);
301 return false;305 return false;
302}306}
303307
@@ -334,6 +338,7 @@
334}338}
335339
336bool PlaylistFeature::dragMoveAccept(QUrl url) {340bool PlaylistFeature::dragMoveAccept(QUrl url) {
341 Q_UNUSED(url);
337 return false;342 return false;
338}343}
339344
@@ -352,20 +357,21 @@
352TreeItemModel* PlaylistFeature::getChildModel() {357TreeItemModel* PlaylistFeature::getChildModel() {
353 return &m_childModel;358 return &m_childModel;
354}359}
360
355/**361/**
356 * Purpose: When inserting or removing playlists,362 * Purpose: When inserting or removing playlists,
357 * we require the sidebar model not to reset.363 * we require the sidebar model not to reset.
358 * This method queries the database and does dynamic insertion364 * This method queries the database and does dynamic insertion
359*/365*/
360void PlaylistFeature::constructChildModel()366QModelIndex PlaylistFeature::constructChildModel(int selected_id)
361{367{
362 QList<TreeItem*> data_list;368 QList<TreeItem*> data_list;
363 int nameColumn = m_playlistTableModel.record().indexOf("name");369 int nameColumn = m_playlistTableModel.record().indexOf("name");
364 int idColumn = m_playlistTableModel.record().indexOf("id");370 int idColumn = m_playlistTableModel.record().indexOf("id");
365371 int selected_row = -1;
366 //Access the invisible root item372 // Access the invisible root item
367 TreeItem* root = m_childModel.getItem(QModelIndex());373 TreeItem* root = m_childModel.getItem(QModelIndex());
368 //Create new TreeItems for the playlists in the database374 // Create new TreeItems for the playlists in the database
369 for (int row = 0; row < m_playlistTableModel.rowCount(); ++row) {375 for (int row = 0; row < m_playlistTableModel.rowCount(); ++row) {
370 QModelIndex ind = m_playlistTableModel.index(row, nameColumn);376 QModelIndex ind = m_playlistTableModel.index(row, nameColumn);
371 QString playlist_name = m_playlistTableModel.data(ind).toString();377 QString playlist_name = m_playlistTableModel.data(ind).toString();
@@ -373,14 +379,23 @@
373 int playlist_id = m_playlistTableModel.data(ind).toInt();379 int playlist_id = m_playlistTableModel.data(ind).toInt();
374 bool locked = m_playlistDao.isPlaylistLocked(playlist_id);380 bool locked = m_playlistDao.isPlaylistLocked(playlist_id);
375381
376 //Create the TreeItem whose parent is the invisible root item382 if ( selected_id == playlist_id) {
383 // save index for selection
384 selected_row = row;
385 }
386
387 // Create the TreeItem whose parent is the invisible root item
377 TreeItem* item = new TreeItem(playlist_name, playlist_name, this, root);388 TreeItem* item = new TreeItem(playlist_name, playlist_name, this, root);
378 item->setIcon(locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());389 item->setIcon(locked ? QIcon(":/images/library/ic_library_locked.png") : QIcon());
379 data_list.append(item);390 data_list.append(item);
380 }391 }
381392
382 //Append all the newly created TreeItems in a dynamic way to the childmodel393 // Append all the newly created TreeItems in a dynamic way to the childmodel
383 m_childModel.insertRows(data_list, 0, m_playlistTableModel.rowCount());394 m_childModel.insertRows(data_list, 0, m_playlistTableModel.rowCount());
395 if (selected_row == -1) {
396 return QModelIndex();
397 }
398 return m_childModel.index(selected_row, 0);
384}399}
385400
386/**401/**
@@ -398,8 +413,8 @@
398 NULL,413 NULL,
399 tr("Import Playlist"),414 tr("Import Playlist"),
400 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),415 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
401 tr("Playlist Files (*.m3u *.m3u8 *.pls)"));416 tr("Playlist Files (*.m3u *.m3u8 *.pls *.csv)"));
402 // Exit method if user cancelled the open dialog.417 //Exit method if user cancelled the open dialog.
403 if (playlist_file.isNull() || playlist_file.isEmpty()) {418 if (playlist_file.isNull() || playlist_file.isEmpty()) {
404 return;419 return;
405 }420 }
@@ -411,6 +426,8 @@
411 playlist_parser = new ParserM3u();426 playlist_parser = new ParserM3u();
412 } else if (playlist_file.endsWith(".pls", Qt::CaseInsensitive)) {427 } else if (playlist_file.endsWith(".pls", Qt::CaseInsensitive)) {
413 playlist_parser = new ParserPls();428 playlist_parser = new ParserPls();
429 } else if (playlist_file.endsWith(".csv", Qt::CaseInsensitive)) {
430 playlist_parser = new ParserCsv();
414 } else {431 } else {
415 return;432 return;
416 }433 }
@@ -426,7 +443,9 @@
426 delete playlist_parser;443 delete playlist_parser;
427 }444 }
428}445}
446
429void PlaylistFeature::onLazyChildExpandation(const QModelIndex &index){447void PlaylistFeature::onLazyChildExpandation(const QModelIndex &index){
448 Q_UNUSED(index);
430 //Nothing to do because the childmodel is not of lazy nature.449 //Nothing to do because the childmodel is not of lazy nature.
431}450}
432451
@@ -436,13 +455,13 @@
436 NULL,455 NULL,
437 tr("Export Playlist"),456 tr("Export Playlist"),
438 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),457 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
439 tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;PLS Playlist (*.pls)"));458 tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;"
459 "PLS Playlist (*.pls);;Text CSV (*.csv);;Readable Text (*.txt)"));
440 // Exit method if user cancelled the open dialog.460 // Exit method if user cancelled the open dialog.
441 if (file_location.isNull() || file_location.isEmpty()) {461 if (file_location.isNull() || file_location.isEmpty()) {
442 return;462 return;
443 }463 }
444 // Create and populate a list of files of the playlist464
445 QList<QString> playlist_items;
446 // Create a new table model since the main one might have an active search.465 // Create a new table model since the main one might have an active search.
447 QScopedPointer<PlaylistTableModel> pPlaylistTableModel(466 QScopedPointer<PlaylistTableModel> pPlaylistTableModel(
448 new PlaylistTableModel(this, m_pTrackCollection,467 new PlaylistTableModel(this, m_pTrackCollection,
@@ -451,43 +470,80 @@
451 pPlaylistTableModel->setPlaylist(m_pPlaylistTableModel->getPlaylist());470 pPlaylistTableModel->setPlaylist(m_pPlaylistTableModel->getPlaylist());
452 pPlaylistTableModel->setSort(0, Qt::AscendingOrder);471 pPlaylistTableModel->setSort(0, Qt::AscendingOrder);
453 pPlaylistTableModel->select();472 pPlaylistTableModel->select();
454 int rows = pPlaylistTableModel->rowCount();
455 for (int i = 0; i < rows; ++i) {
456 QModelIndex index = pPlaylistTableModel->index(i, 0);
457 playlist_items << pPlaylistTableModel->getTrackLocation(index);
458 }
459473
460 // check config if relative paths are desired474 // check config if relative paths are desired
461 bool useRelativePath = static_cast<bool>(m_pConfig->getValueString(475 bool useRelativePath = static_cast<bool>(m_pConfig->getValueString(
462 ConfigKey("[Library]", "UseRelativePathOnExport")).toInt());476 ConfigKey("[Library]", "UseRelativePathOnExport")).toInt());
463477
464 if (file_location.endsWith(".m3u", Qt::CaseInsensitive)) {478 if (file_location.endsWith(".csv", Qt::CaseInsensitive)) {
465 ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);479 ParserCsv::writeCSVFile(file_location, pPlaylistTableModel.data(), useRelativePath);
466 } else if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {480 } else if (file_location.endsWith(".txt", Qt::CaseInsensitive)) {
467 ParserPls::writePLSFile(file_location,playlist_items,481 ParserCsv::writeReadableTextFile(file_location, pPlaylistTableModel.data());
468 useRelativePath);
469 } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {
470 ParserM3u::writeM3U8File(file_location, playlist_items,
471 useRelativePath);
472 } else {482 } else {
473 //default export to M3U if file extension is missing483 // Create and populate a list of files of the playlist
484 QList<QString> playlist_items;
485 int rows = pPlaylistTableModel->rowCount();
486 for (int i = 0; i < rows; ++i) {
487 QModelIndex index = pPlaylistTableModel->index(i, 0);
488 playlist_items << pPlaylistTableModel->getTrackLocation(index);
489 }
474490
475 qDebug() << "Playlist export: No file extension specified. Appending .m3u "491 if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {
476 << "and exporting to M3U.";492 ParserPls::writePLSFile(file_location, playlist_items, useRelativePath);
477 file_location.append(".m3u");493 } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {
478 ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);494 ParserM3u::writeM3U8File(file_location, playlist_items, useRelativePath);
495 } else {
496 //default export to M3U if file extension is missing
497 if(!file_location.endsWith(".m3u", Qt::CaseInsensitive))
498 {
499 qDebug() << "Crate export: No valid file extension specified. Appending .m3u "
500 << "and exporting to M3U.";
501 file_location.append(".m3u");
502 }
503 ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);
504 }
479 }505 }
480}506}
481507
482void PlaylistFeature::slotAddToAutoDJ() {508void PlaylistFeature::slotAddToAutoDJ() {
483 //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();509 //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
510 addToAutoDJ(false); // Top = True
511}
512
513void PlaylistFeature::slotAddToAutoDJTop() {
514 //qDebug() << "slotAddToAutoDJTop() row:" << m_lastRightClickedIndex.data();
515 addToAutoDJ(true); // bTop = True
516}
517
518void PlaylistFeature::addToAutoDJ(bool bTop) {
519 //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
484520
485 if (m_lastRightClickedIndex.isValid()) {521 if (m_lastRightClickedIndex.isValid()) {
486 int playlistId = m_playlistDao.getPlaylistIdFromName(522 int playlistId = m_playlistDao.getPlaylistIdFromName(
487 m_lastRightClickedIndex.data().toString());523 m_lastRightClickedIndex.data().toString());
488 if (playlistId >= 0) {524 if (playlistId >= 0) {
489 m_playlistDao.addToAutoDJQueue(playlistId);525 // Insert this playlist
526 m_playlistDao.addToAutoDJQueue(playlistId, bTop);
490 }527 }
491 }528 }
492 emit(featureUpdated());529 emit(featureUpdated());
493}530}
531
532void PlaylistFeature::slotPlaylistTableChanged(int playlistId) {
533 //qDebug() << "slotPlaylistTableChanged() playlistId:" << playlistId;
534 enum PlaylistDAO::hidden_type type = m_playlistDao.getHiddenType(playlistId);
535 if ( type == PlaylistDAO::PLHT_NOT_HIDDEN
536 || type == PlaylistDAO::PLHT_UNKNOWN // In case of a deleted Playlist
537 ){
538 clearChildModel();
539 m_playlistTableModel.select();
540 m_lastRightClickedIndex = constructChildModel(playlistId);
541
542 if(type != PlaylistDAO::PLHT_UNKNOWN) {
543 // Switch the view to the playlist.
544 m_pPlaylistTableModel->setPlaylist(playlistId);
545 // Update selection
546 emit(featureSelect(this, m_lastRightClickedIndex));
547 }
548 }
549}
494550
=== modified file 'mixxx/src/library/playlistfeature.h'
--- mixxx/src/library/playlistfeature.h 2011-11-27 06:59:02 +0000
+++ mixxx/src/library/playlistfeature.h 2011-12-10 16:03:24 +0000
@@ -50,15 +50,18 @@
50 void slotCreatePlaylist();50 void slotCreatePlaylist();
51 void slotDeletePlaylist();51 void slotDeletePlaylist();
52 void slotAddToAutoDJ();52 void slotAddToAutoDJ();
53 void slotAddToAutoDJTop();
53 void slotRenamePlaylist();54 void slotRenamePlaylist();
54 void slotTogglePlaylistLock();55 void slotTogglePlaylistLock();
55 void slotImportPlaylist();56 void slotImportPlaylist();
56 void slotExportPlaylist();57 void slotExportPlaylist();
5758
59 void slotPlaylistTableChanged(int playlistId);
5860
59 private:61 private:
60 void constructChildModel();62 QModelIndex constructChildModel(int selected_id);
61 void clearChildModel();63 void clearChildModel();
64 void addToAutoDJ(bool bTop);
6265
63 TrackCollection* m_pTrackCollection;66 TrackCollection* m_pTrackCollection;
64 PlaylistTableModel* m_pPlaylistTableModel;67 PlaylistTableModel* m_pPlaylistTableModel;
@@ -67,6 +70,7 @@
67 QAction *m_pCreatePlaylistAction;70 QAction *m_pCreatePlaylistAction;
68 QAction *m_pDeletePlaylistAction;71 QAction *m_pDeletePlaylistAction;
69 QAction *m_pAddToAutoDJAction;72 QAction *m_pAddToAutoDJAction;
73 QAction *m_pAddToAutoDJTopAction;
70 QAction *m_pRenamePlaylistAction;74 QAction *m_pRenamePlaylistAction;
71 QAction *m_pLockPlaylistAction;75 QAction *m_pLockPlaylistAction;
72 QAction *m_pImportPlaylistAction;76 QAction *m_pImportPlaylistAction;
7377
=== modified file 'mixxx/src/library/playlisttablemodel.cpp'
--- mixxx/src/library/playlisttablemodel.cpp 2011-11-30 06:19:47 +0000
+++ mixxx/src/library/playlisttablemodel.cpp 2011-12-10 16:03:24 +0000
@@ -94,6 +94,17 @@
94 return true;94 return true;
95}95}
9696
97bool PlaylistTableModel::appendTrack(int trackId) {
98 if (trackId < 0) {
99 return false;
100 }
101
102 m_playlistDao.appendTrackToPlaylist(trackId, m_iPlaylistId);
103
104 select(); //Repopulate the data model.
105 return true;
106}
107
97TrackPointer PlaylistTableModel::getTrack(const QModelIndex& index) const {108TrackPointer PlaylistTableModel::getTrack(const QModelIndex& index) const {
98 //FIXME: use position instead of location for playlist tracks?109 //FIXME: use position instead of location for playlist tracks?
99110
@@ -306,6 +317,7 @@
306}317}
307318
308QItemDelegate* PlaylistTableModel::delegateForColumn(const int i) {319QItemDelegate* PlaylistTableModel::delegateForColumn(const int i) {
320 Q_UNUSED(i);
309 return NULL;321 return NULL;
310}322}
311323
312324
=== modified file 'mixxx/src/library/playlisttablemodel.h'
--- mixxx/src/library/playlisttablemodel.h 2011-11-30 05:27:44 +0000
+++ mixxx/src/library/playlisttablemodel.h 2011-12-10 16:03:24 +0000
@@ -30,6 +30,7 @@
30 virtual void removeTrack(const QModelIndex& index);30 virtual void removeTrack(const QModelIndex& index);
31 virtual void removeTracks(const QModelIndexList& indices);31 virtual void removeTracks(const QModelIndexList& indices);
32 virtual bool addTrack(const QModelIndex& index, QString location);32 virtual bool addTrack(const QModelIndex& index, QString location);
33 virtual bool appendTrack(int trackId);
33 virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex);34 virtual void moveTrack(const QModelIndex& sourceIndex, const QModelIndex& destIndex);
34 virtual void shuffleTracks(const QModelIndex& currentIndex);35 virtual void shuffleTracks(const QModelIndex& currentIndex);
3536
3637
=== modified file 'mixxx/src/library/preparefeature.cpp'
--- mixxx/src/library/preparefeature.cpp 2011-03-26 12:29:21 +0000
+++ mixxx/src/library/preparefeature.cpp 2011-12-10 16:03:24 +0000
@@ -78,34 +78,45 @@
78}78}
7979
80void PrepareFeature::activateChild(const QModelIndex& index) {80void PrepareFeature::activateChild(const QModelIndex& index) {
81 Q_UNUSED(index);
81}82}
8283
83void PrepareFeature::onRightClick(const QPoint& globalPos) {84void PrepareFeature::onRightClick(const QPoint& globalPos) {
85 Q_UNUSED(globalPos);
84}86}
8587
86void PrepareFeature::onRightClickChild(const QPoint& globalPos,88void PrepareFeature::onRightClickChild(const QPoint& globalPos,
87 QModelIndex index) {89 QModelIndex index) {
90 Q_UNUSED(globalPos);
91 Q_UNUSED(index);
88}92}
8993
90bool PrepareFeature::dropAccept(QUrl url) {94bool PrepareFeature::dropAccept(QUrl url) {
95 Q_UNUSED(url);
91 return false;96 return false;
92}97}
9398
94bool PrepareFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {99bool PrepareFeature::dropAcceptChild(const QModelIndex& index, QUrl url) {
95 return false;100 Q_UNUSED(index);
101 Q_UNUSED(url);
102 return false;
96}103}
97104
98bool PrepareFeature::dragMoveAccept(QUrl url) {105bool PrepareFeature::dragMoveAccept(QUrl url) {
99 return false;106 Q_UNUSED(url);
107 return false;
100}108}
101109
102bool PrepareFeature::dragMoveAcceptChild(const QModelIndex& index,110bool PrepareFeature::dragMoveAcceptChild(const QModelIndex& index,
103 QUrl url) {111 QUrl url) {
112 Q_UNUSED(index);
113 Q_UNUSED(url);
104 return false;114 return false;
105}115}
106116
107void PrepareFeature::onLazyChildExpandation(const QModelIndex &index){117void PrepareFeature::onLazyChildExpandation(const QModelIndex &index){
108 //Nothing to do because the childmodel is not of lazy nature.118 //Nothing to do because the childmodel is not of lazy nature.
119 Q_UNUSED(index);
109}120}
110121
111void PrepareFeature::analyzeTracks(QList<int> trackIds) {122void PrepareFeature::analyzeTracks(QList<int> trackIds) {
112123
=== added file 'mixxx/src/library/setlogfeature.cpp'
--- mixxx/src/library/setlogfeature.cpp 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/setlogfeature.cpp 2011-12-10 16:03:24 +0000
@@ -0,0 +1,571 @@
1#include <QtDebug>
2#include <QMenu>
3#include <QInputDialog>
4#include <QFileDialog>
5#include <QDesktopServices>
6#include <QDateTime>
7
8#include "library/setlogfeature.h"
9#include "library/parser.h"
10#include "library/parserm3u.h"
11#include "library/parserpls.h"
12#include "library/parsercsv.h"
13
14
15#include "widget/wlibrary.h"
16#include "widget/wlibrarysidebar.h"
17#include "widget/wlibrarytextbrowser.h"
18#include "library/trackcollection.h"
19#include "library/playlisttablemodel.h"
20#include "mixxxkeyboard.h"
21#include "treeitem.h"
22#include "soundsourceproxy.h"
23#include "playerinfo.h"
24
25const QString SetlogFeature::m_sSetlogViewName = QString("SETLOGHOME");
26
27SetlogFeature::SetlogFeature(QObject* parent, ConfigObject<ConfigValue>* pConfig, TrackCollection* pTrackCollection)
28 : LibraryFeature(parent),
29 m_pTrackCollection(pTrackCollection),
30 m_playlistDao(pTrackCollection->getPlaylistDAO()),
31 m_trackDao(pTrackCollection->getTrackDAO()),
32 m_pCOPlayPos1(NULL),
33 m_pCOPlayPos2(NULL),
34 m_pConfig(pConfig),
35 m_playlistTableModel(this, pTrackCollection->getDatabase())
36{
37 m_pPlaylistTableModel = new PlaylistTableModel(this, pTrackCollection,
38 "mixxx.db.model.setlog");
39
40 m_pAddToAutoDJAction = new QAction(tr("Add to Auto DJ bottom"),this);
41 connect(m_pAddToAutoDJAction, SIGNAL(triggered()),
42 this, SLOT(slotAddToAutoDJ()));
43
44 m_pAddToAutoDJTopAction = new QAction(tr("Add to Auto DJ top 2"),this);
45 connect(m_pAddToAutoDJTopAction, SIGNAL(triggered()),
46 this, SLOT(slotAddToAutoDJTop()));
47
48 m_pDeletePlaylistAction = new QAction(tr("Remove"),this);
49 connect(m_pDeletePlaylistAction, SIGNAL(triggered()),
50 this, SLOT(slotDeletePlaylist()));
51
52 m_pRenamePlaylistAction = new QAction(tr("Rename"),this);
53 connect(m_pRenamePlaylistAction, SIGNAL(triggered()),
54 this, SLOT(slotRenamePlaylist()));
55
56 m_pLockPlaylistAction = new QAction(tr("Lock"),this);
57 connect(m_pLockPlaylistAction, SIGNAL(triggered()),
58 this, SLOT(slotTogglePlaylistLock()));
59
60 m_pExportPlaylistAction = new QAction(tr("Export Playlist"), this);
61 connect(m_pExportPlaylistAction, SIGNAL(triggered()),
62 this, SLOT(slotExportPlaylist()));
63
64 m_pJoinWithPreviousAction = new QAction(tr("Join with previous"), this);
65 connect(m_pJoinWithPreviousAction, SIGNAL(triggered()),
66 this, SLOT(slotJoinWithPrevious()));
67
68 connect(&m_playlistDao, SIGNAL(added(int)),
69 this, SLOT(slotPlaylistTableChanged(int)));
70
71 connect(&m_playlistDao, SIGNAL(deleted(int)),
72 this, SLOT(slotPlaylistTableChanged(int)));
73
74 connect(&m_playlistDao, SIGNAL(renamed(int)),
75 this, SLOT(slotPlaylistTableChanged(int)));
76
77 connect(&m_playlistDao, SIGNAL(lockChanged(int)),
78 this, SLOT(slotPlaylistTableChanged(int)));
79
80
81 m_oldTrackIdPlayer[0] = 0;
82 m_oldTrackIdPlayer[1] = 0;
83
84 //create a new playlist for today
85 QString set_log_name_format;
86 QString set_log_name;
87
88 set_log_name = QDate::currentDate().toString(Qt::ISODate);
89 set_log_name_format = set_log_name + " (%1)";
90 int i = 1;
91
92 // calculate name of the todays setlog
93 while (m_playlistDao.getPlaylistIdFromName(set_log_name) != -1) {
94 set_log_name = set_log_name_format.arg(++i);
95 }
96
97 m_playlistId = m_playlistDao.createPlaylist(set_log_name, PlaylistDAO::PLHT_SET_LOG);
98
99 if (m_playlistId == -1) {
100 qDebug() << tr("Playlist Creation Failed");
101 qDebug() << tr("An unknown error occurred while creating playlist: ") << set_log_name;
102 }
103
104 // Setup the sidebar playlist model
105 m_playlistTableModel.setTable("Playlists");
106 m_playlistTableModel.setFilter("hidden=2"); // PLHT_SET_LOG
107 m_playlistTableModel.setSort(m_playlistTableModel.fieldIndex("id"),
108 Qt::AscendingOrder);
109 m_playlistTableModel.select();
110
111 //construct child model
112 TreeItem *rootItem = new TreeItem();
113 m_childModel.setRootItem(rootItem);
114 constructChildModel(-1);
115}
116
117SetlogFeature::~SetlogFeature() {
118 delete m_pPlaylistTableModel;
119 delete m_pDeletePlaylistAction;
120 delete m_pAddToAutoDJAction;
121 delete m_pAddToAutoDJTopAction;
122 delete m_pRenamePlaylistAction;
123 delete m_pLockPlaylistAction;
124 if (m_pCOPlayPos1) {
125 delete m_pCOPlayPos1;
126 }
127 if (m_pCOPlayPos2) {
128 delete m_pCOPlayPos2;
129 }
130}
131
132QVariant SetlogFeature::title() {
133 return tr("Set Logs");
134}
135
136QIcon SetlogFeature::getIcon() {
137 return QIcon(":/images/library/ic_library_setlog.png");
138}
139
140
141void SetlogFeature::bindWidget(WLibrarySidebar* sidebarWidget,
142 WLibrary* libraryWidget,
143 MixxxKeyboard* keyboard) {
144 Q_UNUSED(keyboard);
145 Q_UNUSED(sidebarWidget);
146 WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget);
147 connect(this, SIGNAL(showPage(const QUrl&)),
148 edit, SLOT(setSource(const QUrl&)));
149
150 if (!m_pCOPlayPos1) {
151 m_pCOPlayPos1 = new ControlObjectThreadMain(
152 ControlObject::getControl(ConfigKey("[Channel1]", "playposition")));
153 connect(m_pCOPlayPos1, SIGNAL(valueChanged(double)),
154 this, SLOT(slotPositionChanged(double)));
155 }
156 if (!m_pCOPlayPos2) {
157 m_pCOPlayPos2 = new ControlObjectThreadMain(
158 ControlObject::getControl(ConfigKey("[Channel2]", "playposition")));
159 connect(m_pCOPlayPos2, SIGNAL(valueChanged(double)),
160 this, SLOT(slotPositionChanged(double)));
161 }
162
163 libraryWidget->registerView(m_sSetlogViewName, edit);
164}
165
166void SetlogFeature::activate() {
167 emit(showPage(QUrl("qrc:/html/setlogs.html")));
168 emit(switchToView(m_sSetlogViewName));
169}
170
171void SetlogFeature::activateChild(const QModelIndex& index) {
172 //qDebug() << "SetlogFeature::activateChild()" << index;
173
174 //Switch the playlist table model's playlist.
175 QString playlistName = index.data().toString();
176 int playlistId = m_playlistDao.getPlaylistIdFromName(playlistName);
177 m_pPlaylistTableModel->setPlaylist(playlistId);
178 emit(showTrackModel(m_pPlaylistTableModel));
179}
180
181void SetlogFeature::onRightClick(const QPoint& globalPos) {
182 Q_UNUSED(globalPos);
183 m_lastRightClickedIndex = QModelIndex();
184
185 //Create the right-click menu
186 // QMenu menu(NULL);
187 // menu.addAction(m_pCreatePlaylistAction);
188 // TODO(DASCHUER) add something like disable logging
189 // menu.exec(globalPos);
190}
191
192void SetlogFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) {
193 //Save the model index so we can get it in the action slots...
194 m_lastRightClickedIndex = index;
195 QString playlistName = index.data().toString();
196 int playlistId = m_playlistDao.getPlaylistIdFromName(playlistName);
197
198
199 bool locked = m_playlistDao.isPlaylistLocked(playlistId);
200 m_pDeletePlaylistAction->setEnabled(!locked);
201 m_pRenamePlaylistAction->setEnabled(!locked);
202
203 m_pLockPlaylistAction->setText(locked ? tr("Unlock") : tr("Lock"));
204
205
206 //Create the right-click menu
207 QMenu menu(NULL);
208 //menu.addAction(m_pCreatePlaylistAction);
209 //menu.addSeparator();
210 menu.addAction(m_pAddToAutoDJAction);
211 menu.addAction(m_pAddToAutoDJTopAction);
212 menu.addAction(m_pRenamePlaylistAction);
213 if (playlistId != m_playlistId) {
214 // Todays playlist should not be locked or deleted
215 menu.addAction(m_pDeletePlaylistAction);
216 menu.addAction(m_pLockPlaylistAction);
217 }
218 if (index.row() > 0) {
219 // The very first setlog cannot be joint
220 menu.addAction(m_pJoinWithPreviousAction);
221 }
222 menu.addSeparator();
223 menu.addAction(m_pExportPlaylistAction);
224 menu.exec(globalPos);
225}
226
227
228void SetlogFeature::slotRenamePlaylist() {
229
230 qDebug() << "slotRenamePlaylist()";
231
232 QString oldName = m_lastRightClickedIndex.data().toString();
233 int playlistId = m_playlistDao.getPlaylistIdFromName(oldName);
234 bool locked = m_playlistDao.isPlaylistLocked(playlistId);
235
236 if (locked) {
237 qDebug() << "Skipping playlist rename because playlist" << playlistId << "is locked.";
238 return;
239 }
240
241 QString newName;
242 bool validNameGiven = false;
243
244 do {
245 bool ok = false;
246 newName = QInputDialog::getText(NULL,
247 tr("Rename Playlist"),
248 tr("New playlist name:"),
249 QLineEdit::Normal,
250 oldName,
251 &ok).trimmed();
252
253 if (!ok || oldName == newName) {
254 return;
255 }
256
257 int existingId = m_playlistDao.getPlaylistIdFromName(newName);
258
259 if (existingId != -1) {
260 QMessageBox::warning(NULL,
261 tr("Renaming Playlist Failed"),
262 tr("A playlist by that name already exists."));
263 }
264 else if (newName.isEmpty()) {
265 QMessageBox::warning(NULL,
266 tr("Renaming Playlist Failed"),
267 tr("A playlist cannot have a blank name."));
268 }
269 else {
270 validNameGiven = true;
271 }
272 } while (!validNameGiven);
273
274 m_playlistDao.renamePlaylist(playlistId, newName);
275 emit(featureUpdated());
276}
277
278
279void SetlogFeature::slotTogglePlaylistLock() {
280 QString playlistName = m_lastRightClickedIndex.data().toString();
281 int playlistId = m_playlistDao.getPlaylistIdFromName(playlistName);
282 bool locked = !m_playlistDao.isPlaylistLocked(playlistId);
283
284 if (!m_playlistDao.setPlaylistLocked(playlistId, locked)) {
285 qDebug() << "Failed to toggle lock of playlistId " << playlistId;
286 }
287}
288
289void SetlogFeature::slotDeletePlaylist() {
290 //qDebug() << "slotDeletePlaylist() row:" << m_lastRightClickedIndex.data();
291 int playlistId = m_playlistDao.getPlaylistIdFromName(m_lastRightClickedIndex.data().toString());
292 bool locked = m_playlistDao.isPlaylistLocked(playlistId);
293
294 if (locked) {
295 qDebug() << "Skipping playlist deletion because playlist" << playlistId << "is locked.";
296 return;
297 }
298
299 if (m_lastRightClickedIndex.isValid()) {
300 Q_ASSERT(playlistId >= 0);
301
302 m_playlistDao.deletePlaylist(playlistId);
303 emit(featureUpdated());
304 activate();
305 }
306}
307
308bool SetlogFeature::dropAccept(QUrl url) {
309 Q_UNUSED(url);
310 return false;
311}
312
313bool SetlogFeature::dropAcceptChild(const QModelIndex& index, QUrl url){
314 Q_UNUSED(url);
315 Q_UNUSED(index);
316 return false;
317}
318
319bool SetlogFeature::dragMoveAccept(QUrl url) {
320 Q_UNUSED(url);
321 return false;
322}
323
324bool SetlogFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) {
325 Q_UNUSED(url);
326 Q_UNUSED(index);
327 return false;
328}
329
330
331TreeItemModel* SetlogFeature::getChildModel() {
332 return &m_childModel;
333}
334
335/**lock
336 * Purpose: When inserting or removing playlists,
337 * we require the sidebar model not to reset.
338 * This method queries the database and does dynamic insertion
339*/
340QModelIndex SetlogFeature::constructChildModel(int selected_id)
341{
342 QList<TreeItem*> data_list;
343 int nameColumn = m_playlistTableModel.record().indexOf("name");
344 int idColumn = m_playlistTableModel.record().indexOf("id");
345 int selected_row = -1;
346 // Access the invisible root item
347 TreeItem* root = m_childModel.getItem(QModelIndex());
348
349 // Create new TreeItems for the playlists in the database
350 for (int row = 0; row < m_playlistTableModel.rowCount(); ++row) {
351 QModelIndex ind = m_playlistTableModel.index(row, nameColumn);
352 QString playlist_name = m_playlistTableModel.data(ind).toString();
353 ind = m_playlistTableModel.index(row, idColumn);
354 int playlist_id = m_playlistTableModel.data(ind).toInt();
355
356 if ( selected_id == playlist_id) {
357 // save index for selection
358 selected_row = row;
359 }
360
361 // Create the TreeItem whose parent is the invisible root item
362 TreeItem* item = new TreeItem(playlist_name, playlist_name, this, root);
363 if (playlist_id == m_playlistId) {
364 item->setIcon(QIcon(":/images/library/ic_library_setlog_current.png"));
365 } else if (m_playlistDao.isPlaylistLocked(playlist_id)) {
366 item->setIcon(QIcon(":/images/library/ic_library_locked.png"));
367 } else {
368 item->setIcon(QIcon());
369 }
370 data_list.append(item);
371 }
372
373 // Append all the newly created TreeItems in a dynamic way to the childmodel
374 m_childModel.insertRows(data_list, 0, m_playlistTableModel.rowCount());
375 if (selected_row == -1) {
376 return QModelIndex();
377 }
378 return m_childModel.index(selected_row, 0);
379}
380
381/**
382 * Clears the child model dynamically, but the invisible root item remains
383 */
384void SetlogFeature::clearChildModel()
385{
386 m_childModel.removeRows(0,m_playlistTableModel.rowCount());
387}
388
389void SetlogFeature::onLazyChildExpandation(const QModelIndex &index) {
390 Q_UNUSED(index);
391 //Nothing to do because the childmodel is not of lazy nature.
392}
393
394void SetlogFeature::slotExportPlaylist() {
395 qDebug() << "Export playlist" << m_lastRightClickedIndex.data();
396 QString file_location = QFileDialog::getSaveFileName(
397 NULL,
398 tr("Export Playlist"),
399 QDesktopServices::storageLocation(QDesktopServices::MusicLocation),
400 tr("M3U Playlist (*.m3u);;M3U8 Playlist (*.m3u8);;"
401 "PLS Playlist (*.pls);;Text CSV (*.csv);;Readable Text (*.txt)"));
402 // Exit method if user cancelled the open dialog.
403 if (file_location.isNull() || file_location.isEmpty()) {
404 return;
405 }
406
407
408 // Create a new table model since the main one might have an active search.
409 QScopedPointer<PlaylistTableModel> pPlaylistTableModel(
410 new PlaylistTableModel(this, m_pTrackCollection,
411 "mixxx.db.model.playlist_export"));
412
413 pPlaylistTableModel->setPlaylist(m_pPlaylistTableModel->getPlaylist());
414 pPlaylistTableModel->setSort(0, Qt::AscendingOrder);
415 pPlaylistTableModel->select();
416
417 // check config if relative paths are desired
418 bool useRelativePath = static_cast<bool>(m_pConfig->getValueString(
419 ConfigKey("[Library]", "UseRelativePathOnExport")).toInt());
420
421 if (file_location.endsWith(".csv", Qt::CaseInsensitive)) {
422 ParserCsv::writeCSVFile(file_location, pPlaylistTableModel.data(), useRelativePath);
423 } else if (file_location.endsWith(".txt", Qt::CaseInsensitive)) {
424 ParserCsv::writeReadableTextFile(file_location, pPlaylistTableModel.data());
425 } else {
426 // Create and populate a list of files of the playlist
427 QList<QString> playlist_items;
428 int rows = pPlaylistTableModel->rowCount();
429 for (int i = 0; i < rows; ++i) {
430 QModelIndex index = pPlaylistTableModel->index(i, 0);
431 playlist_items << pPlaylistTableModel->getTrackLocation(index);
432 }
433
434 if (file_location.endsWith(".pls", Qt::CaseInsensitive)) {
435 ParserPls::writePLSFile(file_location, playlist_items, useRelativePath);
436 } else if (file_location.endsWith(".m3u8", Qt::CaseInsensitive)) {
437 ParserM3u::writeM3U8File(file_location, playlist_items, useRelativePath);
438 } else {
439 //default export to M3U if file extension is missing
440 if(!file_location.endsWith(".m3u", Qt::CaseInsensitive))
441 {
442 qDebug() << "Crate export: No valid file extension specified. Appending .m3u "
443 << "and exporting to M3U.";
444 file_location.append(".m3u");
445 }
446 ParserM3u::writeM3UFile(file_location, playlist_items, useRelativePath);
447 }
448 }
449}
450
451void SetlogFeature::slotAddToAutoDJ() {
452 //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
453 addToAutoDJ(false); // Top = True
454}
455
456void SetlogFeature::slotAddToAutoDJTop() {
457 //qDebug() << "slotAddToAutoDJTop() row:" << m_lastRightClickedIndex.data();
458 addToAutoDJ(true); // bTop = True
459}
460
461void SetlogFeature::addToAutoDJ(bool bTop) {
462 //qDebug() << "slotAddToAutoDJ() row:" << m_lastRightClickedIndex.data();
463
464 if (m_lastRightClickedIndex.isValid()) {
465 int playlistId = m_playlistDao.getPlaylistIdFromName(
466 m_lastRightClickedIndex.data().toString());
467 if (playlistId >= 0) {
468 // Insert this playlist
469 m_playlistDao.addToAutoDJQueue(playlistId, bTop);
470 }
471 }
472 emit(featureUpdated());
473}
474
475void SetlogFeature::slotJoinWithPrevious() {
476 //qDebug() << "slotJoinWithPrevious() row:" << m_lastRightClickedIndex.data();
477
478 if (m_lastRightClickedIndex.isValid()) {
479 int currentPlaylistId = m_playlistDao.getPlaylistIdFromName(
480 m_lastRightClickedIndex.data().toString());
481
482 if (currentPlaylistId >= 0) {
483
484 bool locked = m_playlistDao.isPlaylistLocked(currentPlaylistId);
485
486 if (locked) {
487 qDebug() << "Skipping playlist deletion because playlist" << currentPlaylistId << "is locked.";
488 return;
489 }
490
491 // Add every track from right klicked playlist to that with the next smaller ID
492 int previousPlaylistId = m_playlistDao.getPreviousPlaylist(currentPlaylistId, PlaylistDAO::PLHT_SET_LOG);
493 if (previousPlaylistId >= 0) {
494
495 m_pPlaylistTableModel->setPlaylist(previousPlaylistId);
496
497 if (currentPlaylistId == m_playlistId) {
498 // mark all the Tracks in the previous Playlist as played
499
500 m_pPlaylistTableModel->select();
501 int rows = m_pPlaylistTableModel->rowCount();
502 for(int i = 0; i < rows; ++i){
503 QModelIndex index = m_pPlaylistTableModel->index(i,0);
504 if (index.isValid()) {
505 TrackPointer track = m_pPlaylistTableModel->getTrack(index);
506 track->restorePlayed(true);
507 }
508 }
509
510 // Change current setlog
511 m_playlistId = previousPlaylistId;
512 }
513 qDebug() << "slotJoinWithPrevious() current:" << currentPlaylistId << " previous:" << previousPlaylistId;
514 m_playlistDao.copyPlaylistTracks(currentPlaylistId, previousPlaylistId);
515 m_playlistDao.deletePlaylist(currentPlaylistId);
516 slotPlaylistTableChanged(previousPlaylistId); // For moving selection
517 emit(showTrackModel(m_pPlaylistTableModel));
518 emit(featureUpdated());
519 }
520 }
521 }
522}
523
524void SetlogFeature::slotPositionChanged(double value) {
525 Q_UNUSED(value);
526 TrackPointer currendPlayingTrack;
527 int currendPlayingTrackId = 0;
528
529 int deck = PlayerInfo::Instance().getCurrentPlayingDeck();
530 if ( deck && deck <= 2) {
531 QString chan = QString("[Channel%1]").arg(deck);
532 currendPlayingTrack = PlayerInfo::Instance().getTrackInfo(chan);
533 if (currendPlayingTrack) {
534 currendPlayingTrackId = currendPlayingTrack->getId();
535 }
536 if (m_oldTrackIdPlayer[deck-1] != currendPlayingTrackId) {
537 // The audience listens to a new track
538
539 qDebug() << "The audience listens to track " << currendPlayingTrackId;
540 currendPlayingTrack->setPlayed(true); // Here the song is realy played, not only loaded.
541
542 if (m_pPlaylistTableModel->getPlaylist() == m_playlistId) {
543 // View needs a refresh
544 m_pPlaylistTableModel->appendTrack(currendPlayingTrackId);
545 }
546 else {
547 m_playlistDao.appendTrackToPlaylist(currendPlayingTrackId, m_playlistId);
548 }
549 m_oldTrackIdPlayer[deck-1] = currendPlayingTrackId;
550 }
551 }
552}
553
554void SetlogFeature::slotPlaylistTableChanged(int playlistId) {
555 //qDebug() << "slotPlaylistTableChanged() playlistId:" << playlistId;
556 enum PlaylistDAO::hidden_type type = m_playlistDao.getHiddenType(playlistId);
557 if ( type == PlaylistDAO::PLHT_SET_LOG
558 || type == PlaylistDAO::PLHT_UNKNOWN // In case of a deleted Playlist
559 ){
560 clearChildModel();
561 m_playlistTableModel.select();
562 m_lastRightClickedIndex = constructChildModel(playlistId);
563
564 if(type != PlaylistDAO::PLHT_UNKNOWN) {
565 // Switch the view to the playlist.
566 m_pPlaylistTableModel->setPlaylist(playlistId);
567 // Update selection
568 emit(featureSelect(this, m_lastRightClickedIndex));
569 }
570 }
571}
0572
=== added file 'mixxx/src/library/setlogfeature.h'
--- mixxx/src/library/setlogfeature.h 1970-01-01 00:00:00 +0000
+++ mixxx/src/library/setlogfeature.h 2011-12-10 16:03:24 +0000
@@ -0,0 +1,93 @@
1// setlogfeature.h
2// Created 8/17/09 by RJ Ryan (rryan@mit.edu)
3
4#ifndef SETLOGFEATURE_H
5#define SETLOGFEATURE_H
6
7#include <QSqlTableModel>
8#include <QAction>
9#include <QList>
10
11#include "library/libraryfeature.h"
12#include "library/dao/playlistdao.h"
13#include "library/dao/trackdao.h"
14#include "treeitemmodel.h"
15#include "configobject.h"
16#include "controlobject.h"
17#include "controlobjectthreadmain.h"
18
19
20
21class PlaylistTableModel;
22class TrackCollection;
23
24class SetlogFeature : public LibraryFeature {
25 Q_OBJECT
26public:
27 SetlogFeature(QObject* parent, ConfigObject<ConfigValue>* pConfig, TrackCollection* pTrackCollection);
28 virtual ~SetlogFeature();
29
30 QVariant title();
31 QIcon getIcon();
32
33 bool dropAccept(QUrl url);
34 bool dropAcceptChild(const QModelIndex& index, QUrl url);
35 bool dragMoveAccept(QUrl url);
36 bool dragMoveAcceptChild(const QModelIndex& index, QUrl url);
37
38 TreeItemModel* getChildModel();
39
40 void bindWidget(WLibrarySidebar* sidebarWidget,
41 WLibrary* libraryWidget,
42 MixxxKeyboard* keyboard);
43 signals:
44 void showPage(const QUrl& page);
45
46public slots:
47 void activate();
48 void activateChild(const QModelIndex& index);
49 void onRightClick(const QPoint& globalPos);
50 void onRightClickChild(const QPoint& globalPos, QModelIndex index);
51 void onLazyChildExpandation(const QModelIndex& index);
52
53 void slotDeletePlaylist();
54 void slotAddToAutoDJ();
55 void slotAddToAutoDJTop();
56 void slotRenamePlaylist();
57 void slotTogglePlaylistLock();
58 void slotExportPlaylist();
59 void slotJoinWithPrevious();
60
61 void slotPositionChanged(double /*value*/);
62 void slotPlaylistTableChanged(int playlistId);
63
64 private:
65 QModelIndex constructChildModel(int selected_id);
66 void clearChildModel();
67 void addToAutoDJ(bool bTop);
68
69 TrackCollection* m_pTrackCollection;
70 PlaylistTableModel* m_pPlaylistTableModel;
71 PlaylistDAO &m_playlistDao;
72 TrackDAO &m_trackDao;
73 QAction *m_pDeletePlaylistAction;
74 QAction *m_pAddToAutoDJAction;
75 QAction *m_pAddToAutoDJTopAction;
76 QAction *m_pRenamePlaylistAction;
77 QAction *m_pLockPlaylistAction;
78 QAction *m_pExportPlaylistAction;
79 QAction *m_pJoinWithPreviousAction;
80 ControlObjectThreadMain* m_pCOPlayPos1;
81 ControlObjectThreadMain* m_pCOPlayPos2;
82 QModelIndex m_lastRightClickedIndex;
83 TreeItemModel m_childModel;
84 ConfigObject<ConfigValue>* m_pConfig;
85 QSqlTableModel m_playlistTableModel;
86 const static QString m_sSetlogViewName;
87 int m_playlistId;
88 int m_oldTrackIdPlayer[2];
89 //int m_oldTrackPlayer2;
90 //int m_oldCurrendPlayingTrack;
91};
92
93#endif /* SETLOGFEATURE_H */
094
=== modified file 'mixxx/src/library/sidebarmodel.cpp'
--- mixxx/src/library/sidebarmodel.cpp 2011-10-21 02:26:31 +0000
+++ mixxx/src/library/sidebarmodel.cpp 2011-12-10 16:03:24 +0000
@@ -17,11 +17,14 @@
1717
18void SidebarModel::addLibraryFeature(LibraryFeature* feature) {18void SidebarModel::addLibraryFeature(LibraryFeature* feature) {
19 m_sFeatures.push_back(feature);19 m_sFeatures.push_back(feature);
20 connect(feature, SIGNAL(featureUpdated()), this, SLOT(refreshData()));20 connect(feature, SIGNAL(featureUpdated()),
21 this, SLOT(refreshData()));
21 connect(feature, SIGNAL(featureIsLoading(LibraryFeature*)),22 connect(feature, SIGNAL(featureIsLoading(LibraryFeature*)),
22 this, SLOT(slotFeatureIsLoading(LibraryFeature*)));23 this, SLOT(slotFeatureIsLoading(LibraryFeature*)));
23 connect(feature, SIGNAL(featureLoadingFinished(LibraryFeature*)),24 connect(feature, SIGNAL(featureLoadingFinished(LibraryFeature*)),
24 this,SLOT(slotFeatureLoadingFinished(LibraryFeature*)));25 this, SLOT(slotFeatureLoadingFinished(LibraryFeature*)));
26 connect(feature, SIGNAL(featureSelect(LibraryFeature*, const QModelIndex&)),
27 this, SLOT(slotFeatureSelect(LibraryFeature*, const QModelIndex&)));
2528
26 QAbstractItemModel* model = feature->getChildModel();29 QAbstractItemModel* model = feature->getChildModel();
2730
@@ -108,19 +111,23 @@
108 return QModelIndex();111 return QModelIndex();
109 } else {112 } else {
110 TreeItem* tree_item = (TreeItem*)index.internalPointer();113 TreeItem* tree_item = (TreeItem*)index.internalPointer();
114 TreeItem* tree_item_parent = tree_item->parent();
111 // if we have selected an item at the first level of a childnode115 // if we have selected an item at the first level of a childnode
112 if (tree_item->parent()->data() == "$root"){116
113 LibraryFeature* feature = tree_item->getFeature();117 if (tree_item_parent) {
114 for (int i = 0; i < m_sFeatures.size(); ++i) {118 if (tree_item_parent->data() == "$root"){
115 if (feature == m_sFeatures[i]) {119 LibraryFeature* feature = tree_item->getFeature();
116 // create a ModelIndex for parent 'this' having a120 for (int i = 0; i < m_sFeatures.size(); ++i) {
117 // library feature at position 'i'121 if (feature == m_sFeatures[i]) {
118 return createIndex(i, 0, (void*)this);122 // create a ModelIndex for parent 'this' having a
119 }123 // library feature at position 'i'
120 }124 return createIndex(i, 0, (void*)this);
125 }
126 }
127 }
128 // if we have selected an item at some deeper level of a childnode
129 return createIndex(tree_item_parent->row(), 0 , tree_item_parent);
121 }130 }
122 // if we have selected an item at some deeper level of a childnode
123 return createIndex(tree_item->parent()->row(), 0 , tree_item->parent());
124 }131 }
125 }132 }
126 return QModelIndex();133 return QModelIndex();
@@ -144,7 +151,8 @@
144}151}
145152
146int SidebarModel::columnCount(const QModelIndex& parent) const {153int SidebarModel::columnCount(const QModelIndex& parent) const {
147 //qDebug() << "SidebarModel::columnCount parent=" << parent;154 Q_UNUSED(parent);
155 //qDebug() << "SidebarModel::columnCount parent=" << parent;
148 // TODO(rryan) will we ever have columns? I don't think so.156 // TODO(rryan) will we ever have columns? I don't think so.
149 return 1;157 return 1;
150}158}
@@ -312,6 +320,8 @@
312}320}
313321
314void SidebarModel::slotDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) {322void SidebarModel::slotDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) {
323 Q_UNUSED(topLeft);
324 Q_UNUSED(bottomRight);
315 //qDebug() << "slotDataChanged topLeft:" << topLeft << "bottomRight:" << bottomRight;325 //qDebug() << "slotDataChanged topLeft:" << topLeft << "bottomRight:" << bottomRight;
316}326}
317327
@@ -330,13 +340,19 @@
330}340}
331341
332void SidebarModel::slotRowsInserted(const QModelIndex& parent, int start, int end) {342void SidebarModel::slotRowsInserted(const QModelIndex& parent, int start, int end) {
333 // qDebug() << "slotRowsInserted" << parent << start << end;343 Q_UNUSED(parent);
344 Q_UNUSED(start);
345 Q_UNUSED(end);
346 // qDebug() << "slotRowsInserted" << parent << start << end;
334 //QModelIndex newParent = translateSourceIndex(parent);347 //QModelIndex newParent = translateSourceIndex(parent);
335 endInsertRows();348 endInsertRows();
336}349}
337350
338void SidebarModel::slotRowsRemoved(const QModelIndex& parent, int start, int end) {351void SidebarModel::slotRowsRemoved(const QModelIndex& parent, int start, int end) {
339 //qDebug() << "slotRowsRemoved" << parent << start << end;352 Q_UNUSED(parent);
353 Q_UNUSED(start);
354 Q_UNUSED(end);
355 //qDebug() << "slotRowsRemoved" << parent << start << end;
340 //QModelIndex newParent = translateSourceIndex(parent);356 //QModelIndex newParent = translateSourceIndex(parent);
341 endRemoveRows();357 endRemoveRows();
342}358}
@@ -355,16 +371,16 @@
355 */371 */
356void SidebarModel::slotFeatureIsLoading(LibraryFeature * feature)372void SidebarModel::slotFeatureIsLoading(LibraryFeature * feature)
357{373{
358 featureRenamed(feature);374 featureRenamed(feature);
359 selectFeature(feature);375 slotFeatureSelect(feature);
360}376}
361377
362/* Tobias: This slot is somewhat redundant but I decided378/* Tobias: This slot is somewhat redundant but I decided
363 * to leave it for code readability reasons379 * to leave it for code readability reasons
364 */380 */
365void SidebarModel::slotFeatureLoadingFinished(LibraryFeature * feature){381void SidebarModel::slotFeatureLoadingFinished(LibraryFeature * feature){
366 featureRenamed(feature);382 featureRenamed(feature);
367 selectFeature(feature);383 slotFeatureSelect(feature);
368}384}
369385
370void SidebarModel::featureRenamed(LibraryFeature* pFeature){386void SidebarModel::featureRenamed(LibraryFeature* pFeature){
@@ -376,11 +392,19 @@
376 }392 }
377}393}
378394
379void SidebarModel::selectFeature(LibraryFeature* pFeature) {395void SidebarModel::slotFeatureSelect(LibraryFeature* pFeature, const QModelIndex& featureIndex)
380 for (int i=0; i < m_sFeatures.size(); ++i) {396{
381 if (m_sFeatures[i] == pFeature) {397 QModelIndex ind;
382 QModelIndex ind = index(i, 0);398 if (featureIndex.isValid()) {
383 emit(selectIndex(ind));399 TreeItem* item = (TreeItem*)featureIndex.internalPointer();
384 }400 ind = createIndex(featureIndex.row(), featureIndex.column(), item);
401 } else {
402 for (int i=0; i < m_sFeatures.size(); ++i) {
403 if (m_sFeatures[i] == pFeature) {
404 ind = index(i, 0);
405 break;
406 }
407 }
385 }408 }
409 emit(selectIndex(ind));
386}410}
387411
=== modified file 'mixxx/src/library/sidebarmodel.h'
--- mixxx/src/library/sidebarmodel.h 2011-03-27 20:12:33 +0000
+++ mixxx/src/library/sidebarmodel.h 2011-12-10 16:03:24 +0000
@@ -39,7 +39,7 @@
39 void doubleClicked(const QModelIndex& index);39 void doubleClicked(const QModelIndex& index);
40 void rightClicked(const QPoint& globalPos, const QModelIndex& index);40 void rightClicked(const QPoint& globalPos, const QModelIndex& index);
41 void refreshData();41 void refreshData();
42 void selectFeature(LibraryFeature* pFeature);42 void slotFeatureSelect(LibraryFeature* pFeature, const QModelIndex& index = QModelIndex());
4343
44 // Slots for every single QAbstractItemModel signal44 // Slots for every single QAbstractItemModel signal
45 // void slotColumnsAboutToBeInserted(const QModelIndex& parent, int start, int end);45 // void slotColumnsAboutToBeInserted(const QModelIndex& parent, int start, int end);
4646
=== modified file 'mixxx/src/library/trackcollection.cpp'
--- mixxx/src/library/trackcollection.cpp 2011-10-21 02:09:53 +0000
+++ mixxx/src/library/trackcollection.cpp 2011-12-10 16:03:24 +0000
@@ -15,9 +15,9 @@
15TrackCollection::TrackCollection(ConfigObject<ConfigValue>* pConfig)15TrackCollection::TrackCollection(ConfigObject<ConfigValue>* pConfig)
16 : m_pConfig(pConfig),16 : m_pConfig(pConfig),
17 m_db(QSqlDatabase::addDatabase("QSQLITE")), // defaultConnection17 m_db(QSqlDatabase::addDatabase("QSQLITE")), // defaultConnection
18 m_cueDao(m_db),
19 m_playlistDao(m_db),18 m_playlistDao(m_db),
20 m_crateDao(m_db),19 m_crateDao(m_db),
20 m_cueDao(m_db),
21 m_trackDao(m_db, m_cueDao, m_playlistDao, m_crateDao, pConfig),21 m_trackDao(m_db, m_cueDao, m_playlistDao, m_crateDao, pConfig),
22 m_supportedFileExtensionsRegex(22 m_supportedFileExtensionsRegex(
23 SoundSourceProxy::supportedFileExtensionsRegex(),23 SoundSourceProxy::supportedFileExtensionsRegex(),
2424
=== modified file 'mixxx/src/mixxx.cpp'
--- mixxx/src/mixxx.cpp 2011-12-06 02:54:33 +0000
+++ mixxx/src/mixxx.cpp 2011-12-10 16:03:24 +0000
@@ -414,7 +414,7 @@
414414
415 m_pWidgetParent = NULL;415 m_pWidgetParent = NULL;
416 // Loads the skin as a child of m_pView416 // Loads the skin as a child of m_pView
417 // assignment itentional in next line417 // assignment intentional in next line
418 if (!(m_pWidgetParent = m_pSkinLoader->loadDefaultSkin(m_pView,418 if (!(m_pWidgetParent = m_pSkinLoader->loadDefaultSkin(m_pView,
419 m_pKeyboard,419 m_pKeyboard,
420 m_pPlayerManager,420 m_pPlayerManager,
421421
=== modified file 'mixxx/src/trackinfoobject.cpp'
--- mixxx/src/trackinfoobject.cpp 2011-10-03 22:37:35 +0000
+++ mixxx/src/trackinfoobject.cpp 2011-12-10 16:03:24 +0000
@@ -556,15 +556,24 @@
556 QMutexLocker lock(&m_qMutex);556 QMutexLocker lock(&m_qMutex);
557 if (bPlayed) {557 if (bPlayed) {
558 ++m_iTimesPlayed;558 ++m_iTimesPlayed;
559 setDirty(true);559 setDirty(true);
560 }560 }
561 else if (m_bPlayed && !bPlayed) {561 else if (m_bPlayed && !bPlayed) {
562 --m_iTimesPlayed;562 --m_iTimesPlayed;
563 setDirty(true);563 setDirty(true);
564 }564 }
565 m_bPlayed = bPlayed;565 m_bPlayed = bPlayed;
566}566}
567567
568void TrackInfoObject::restorePlayed(bool bPlayed)
569{
570 QMutexLocker lock(&m_qMutex);
571 if (bPlayed != m_bPlayed) {
572 m_bPlayed = bPlayed;
573 setDirty(true);
574 }
575}
576
568QString TrackInfoObject::getComment() const577QString TrackInfoObject::getComment() const
569{578{
570 QMutexLocker lock(&m_qMutex);579 QMutexLocker lock(&m_qMutex);
571580
=== modified file 'mixxx/src/trackinfoobject.h'
--- mixxx/src/trackinfoobject.h 2011-10-03 22:37:35 +0000
+++ mixxx/src/trackinfoobject.h 2011-12-10 16:03:24 +0000
@@ -186,6 +186,8 @@
186 bool getPlayed() const;186 bool getPlayed() const;
187 /** Set Played status*/187 /** Set Played status*/
188 void setPlayed(bool);188 void setPlayed(bool);
189 /** To restore the played flag without increment the played counter */
190 void restorePlayed(bool bPlayed);
189191
190 int getId() const;192 int getId() const;
191193
192194
=== modified file 'mixxx/src/widget/wlibrarysidebar.cpp'
--- mixxx/src/widget/wlibrarysidebar.cpp 2011-03-27 21:15:15 +0000
+++ mixxx/src/widget/wlibrarysidebar.cpp 2011-12-10 16:03:24 +0000
@@ -199,4 +199,9 @@
199 QItemSelectionModel* pModel = new QItemSelectionModel(model());199 QItemSelectionModel* pModel = new QItemSelectionModel(model());
200 pModel->select(index, QItemSelectionModel::Select);200 pModel->select(index, QItemSelectionModel::Select);
201 setSelectionModel(pModel);201 setSelectionModel(pModel);
202
203 if (index.parent().isValid()) {
204 expand(index.parent());
205 }
206 scrollTo(index);
202}207}