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