Merge lp:~mhaulo/mixxx/allow-playlist-and-crate-renaming into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Mika Haulo
Status: Merged
Merge reported by: RAFFI TEA
Merged at revision: not available
Proposed branch: lp:~mhaulo/mixxx/allow-playlist-and-crate-renaming
Merge into: lp:~mixxxdevelopers/mixxx/trunk
Diff against target: 274 lines (+150/-0)
8 files modified
mixxx/src/library/cratefeature.cpp (+52/-0)
mixxx/src/library/cratefeature.h (+2/-0)
mixxx/src/library/dao/cratedao.cpp (+19/-0)
mixxx/src/library/dao/cratedao.h (+1/-0)
mixxx/src/library/dao/playlistdao.cpp (+21/-0)
mixxx/src/library/dao/playlistdao.h (+2/-0)
mixxx/src/library/playlistfeature.cpp (+51/-0)
mixxx/src/library/playlistfeature.h (+2/-0)
To merge this branch: bzr merge lp:~mhaulo/mixxx/allow-playlist-and-crate-renaming
Reviewer Review Type Date Requested Status
William Good Abstain
RAFFI TEA Approve
Review via email: mp+44722@code.launchpad.net

Description of the change

This is a fix proposal for wishlist bug #661461: Allow playlist and crate renaming (https://bugs.launchpad.net/mixxx/+bug/661461).

To post a comment you must log in.
Revision history for this message
RAFFI TEA (raffitea) wrote :

Hey Mika,

thank you very much for your contribution. I have reviewed your branch and found a bug: Assume you have two playlists "A" and "B". Renaming "B" to "A" will not fail and results in two playlists "A" and "A". Therefore, you must check if the renamed playlist name exists or not. Furthermore, I would like see an UNIQUE constraint on attribute <name> in the <playlist table> (if other developers agree).

@Albert & Developers
I've done a lot of library improvements in my Traktor branch, e.g., n-level tree-structures for child models. If we gonna merge his branch to trunk first, we end up in numerous conflicts when merging Traktor feature in. I recommend to merge his breach into the Traktor feature branch.

review: Needs Fixing
2618. By Mika Haulo

Added better input validation for playlist and crate renaming.

2619. By Mika Haulo

Getting rid of one extra debug print

Revision history for this message
Mika Haulo (mhaulo) wrote :

>
> thank you very much for your contribution. I have reviewed your branch and
> found a bug: Assume you have two playlists "A" and "B". Renaming "B" to "A"
> will not fail and results in two playlists "A" and "A". Therefore, you must
> check if the renamed playlist name exists or not.

Ah, how on earth did I forget that... anyway, a fix for this should be available in my branch now.

I also changed the behaviour a little bit. First of all, the user input is trimmed to eliminate extra whitespace. This effectively prevents whitespace-only names. If the name didn't change from the old name, the function simply returns doing nothing more. And the whole thing is in a loop that keeps popping up an input dialog until the user either gives a valid name or cancels the action.

I'd suggest this kind of behaviour also to be added to the new crate/playlist feature. What do you think?

> Furthermore, I would like
> see an UNIQUE constraint on attribute <name> in the <playlist table> (if other
> developers agree).

I agree with this.

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

Looks all good to me now :-) Your contribution is really helpful. In my opinion we can approve the proposal.

Preventing whitespace-only names is a problem. If do not mind go ahead and commit some fixes.

review: Approve
Revision history for this message
William Good (bkgood) wrote :

Why is it not allowable to have two playlists with the same name? If we use the name as a primary key, shouldn't it be UNIQUE anyway?

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

> Why is it not allowable to have two playlists with the same name?
I think this questions depends on how you consider the library domain. I guess most users do not want to have playlists with same name unless we have n-level tree structures.

>If we use the name as a primary key, shouldn't it be UNIQUE anyway?
Unfortunately, the name is not a primary key. The playlist table is made of an artificial primary key followed by the name attribute and so on.

Revision history for this message
William Good (bkgood) wrote :

Okay, I thought this was due to some problem arising from design -- like the name was the pkey, and allowing the user to name two playlists the same thing would result in a database error. Using an auto-generated primary key is good database design as far as I've been taught.

As much as the power user in me thinks I should be able to name two playlists the same thing, the usability issues trump my desires.

In regards to merging, I vote for Tobias merging it into his branch. Tobias: if you think the code is good, I'd just merge it now and it'll be better scrutinized when the traktor branch is merged. Keeping branches merge-able with moving targets is a pita.

review: Abstain
Revision history for this message
Mika Haulo (mhaulo) wrote :

> Preventing whitespace-only names is a problem. If do not mind go ahead and
> commit some fixes.

I uploaded the changes to this branch:
https://code.launchpad.net/~mhaulo/mixxx/validate_playlist_and_crate_creation

> In regards to merging, I vote for Tobias merging it into his branch. Tobias:
> if you think the code is good, I'd just merge it now and it'll be better
> scrutinized when the traktor branch is merged. Keeping branches merge-able
> with moving targets is a pita.

I'm not completely aware of your future plans about this playlist&traktor thing, but if it helps, I can create a patch directly against the branch Bill mentioned above (or whatever branch you prefer, I just need a url), including the stuff in this merge request.

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

Thanks for your offer Mika. But I've just merged both branches ~mhaulo/mixxx/validate_playlist_and_crate_creation and lp:~mhaulo/mixxx/allow-playlist-and-crate-renaming into lp:~mixxxdevelopers/mixxx/traktor_library. There were some merge conflicts since Mika and I worked on same files :-)

JFI: We plan to add a Traktor library to Mixxx as we did it with iTunes. Traktor is popular commercial DJ application.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'mixxx/src/library/cratefeature.cpp'
--- mixxx/src/library/cratefeature.cpp 2010-11-25 01:03:57 +0000
+++ mixxx/src/library/cratefeature.cpp 2010-12-28 15:47:10 +0000
@@ -26,6 +26,10 @@
26 m_pDeleteCrateAction = new QAction(tr("Remove"),this);26 m_pDeleteCrateAction = new QAction(tr("Remove"),this);
27 connect(m_pDeleteCrateAction, SIGNAL(triggered()),27 connect(m_pDeleteCrateAction, SIGNAL(triggered()),
28 this, SLOT(slotDeleteCrate()));28 this, SLOT(slotDeleteCrate()));
29
30 m_pRenameCrateAction = new QAction(tr("Rename"),this);
31 connect(m_pRenameCrateAction, SIGNAL(triggered()),
32 this, SLOT(slotRenameCrate()));
2933
30 m_crateListTableModel.setTable("crates");34 m_crateListTableModel.setTable("crates");
31 m_crateListTableModel.removeColumn(m_crateListTableModel.fieldIndex("id"));35 m_crateListTableModel.removeColumn(m_crateListTableModel.fieldIndex("id"));
@@ -124,6 +128,7 @@
124 QMenu menu(NULL);128 QMenu menu(NULL);
125 menu.addAction(m_pCreateCrateAction);129 menu.addAction(m_pCreateCrateAction);
126 menu.addSeparator();130 menu.addSeparator();
131 menu.addAction(m_pRenameCrateAction);
127 menu.addAction(m_pDeleteCrateAction);132 menu.addAction(m_pDeleteCrateAction);
128 menu.exec(globalPos);133 menu.exec(globalPos);
129}134}
@@ -175,3 +180,50 @@
175 qDebug() << "Failed to delete crateId" << crateId;180 qDebug() << "Failed to delete crateId" << crateId;
176 }181 }
177}182}
183
184void CrateFeature::slotRenameCrate() {
185 QString oldName = m_lastRightClickedIndex.data().toString();
186 int crateId = m_pTrackCollection->getCrateDAO().getCrateIdByName(oldName);
187
188 QString newName;
189 bool validNameGiven = false;
190
191 do {
192 bool ok = false;
193 newName = QInputDialog::getText(NULL,
194 tr("Rename Crate"),
195 tr("New crate name:"),
196 QLineEdit::Normal,
197 oldName,
198 &ok).trimmed();
199
200 if (!ok || newName == oldName) {
201 return;
202 }
203
204 int existingId = m_pTrackCollection->getCrateDAO().getCrateIdByName(newName);
205
206 if (existingId != -1) {
207 QMessageBox::warning(NULL,
208 tr("Renaming Crate Failed"),
209 tr("A crate by that name already exists."));
210 }
211 else if (newName.isEmpty()) {
212 QMessageBox::warning(NULL,
213 tr("Renaming Crate Failed"),
214 tr("A crate cannot have a blank name."));
215 }
216 else {
217 validNameGiven = true;
218 }
219 } while (!validNameGiven);
220
221
222 if (m_pTrackCollection->getCrateDAO().renameCrate(crateId, newName)) {
223 m_crateListTableModel.select();
224 emit(featureUpdated());
225 m_crateTableModel.setCrate(crateId);
226 } else {
227 qDebug() << "Failed to rename crateId" << crateId;
228 }
229}
178230
=== modified file 'mixxx/src/library/cratefeature.h'
--- mixxx/src/library/cratefeature.h 2010-02-23 20:06:25 +0000
+++ mixxx/src/library/cratefeature.h 2010-12-28 15:47:10 +0000
@@ -41,11 +41,13 @@
4141
42 void slotCreateCrate();42 void slotCreateCrate();
43 void slotDeleteCrate();43 void slotDeleteCrate();
44 void slotRenameCrate();
4445
45 private:46 private:
46 TrackCollection* m_pTrackCollection;47 TrackCollection* m_pTrackCollection;
47 QAction *m_pCreateCrateAction;48 QAction *m_pCreateCrateAction;
48 QAction *m_pDeleteCrateAction;49 QAction *m_pDeleteCrateAction;
50 QAction *m_pRenameCrateAction;
49 QSqlTableModel m_crateListTableModel;51 QSqlTableModel m_crateListTableModel;
50 CrateTableModel m_crateTableModel;52 CrateTableModel m_crateTableModel;
51 QModelIndex m_lastRightClickedIndex;53 QModelIndex m_lastRightClickedIndex;
5254
=== modified file 'mixxx/src/library/dao/cratedao.cpp'
--- mixxx/src/library/dao/cratedao.cpp 2010-10-19 01:35:26 +0000
+++ mixxx/src/library/dao/cratedao.cpp 2010-12-28 15:47:10 +0000
@@ -51,6 +51,25 @@
51 return false;51 return false;
52}52}
5353
54bool CrateDAO::renameCrate(int crateId, const QString& newName) {
55 qDebug() << "renameCrate()";
56
57 Q_ASSERT(m_database.transaction());
58 QSqlQuery query;
59 query.prepare("UPDATE " CRATE_TABLE " SET name = :name WHERE id = :id");
60 query.bindValue(":name", newName);
61 query.bindValue(":id", crateId);
62
63 if (!query.exec()) {
64 qDebug() << query.executedQuery() << query.lastError();
65 Q_ASSERT(m_database.rollback());
66 return false;
67 }
68
69 Q_ASSERT(m_database.commit());
70 return true;
71}
72
54bool CrateDAO::deleteCrate(int crateId) {73bool CrateDAO::deleteCrate(int crateId) {
55 Q_ASSERT(m_database.transaction());74 Q_ASSERT(m_database.transaction());
5675
5776
=== modified file 'mixxx/src/library/dao/cratedao.h'
--- mixxx/src/library/dao/cratedao.h 2010-10-19 01:35:26 +0000
+++ mixxx/src/library/dao/cratedao.h 2010-12-28 15:47:10 +0000
@@ -23,6 +23,7 @@
23 unsigned int crateCount();23 unsigned int crateCount();
24 bool createCrate(const QString& name);24 bool createCrate(const QString& name);
25 bool deleteCrate(int crateId);25 bool deleteCrate(int crateId);
26 bool renameCrate(int crateId, const QString& newName);
26 int getCrateIdByName(const QString& name);27 int getCrateIdByName(const QString& name);
27 int getCrateId(int position);28 int getCrateId(int position);
28 QString crateName(int crateId);29 QString crateName(int crateId);
2930
=== modified file 'mixxx/src/library/dao/playlistdao.cpp'
--- mixxx/src/library/dao/playlistdao.cpp 2010-10-07 03:05:48 +0000
+++ mixxx/src/library/dao/playlistdao.cpp 2010-12-28 15:47:10 +0000
@@ -145,6 +145,27 @@
145 //TODO: Crap, we need to shuffle the positions of all the playlists?145 //TODO: Crap, we need to shuffle the positions of all the playlists?
146}146}
147147
148
149void PlaylistDAO::renamePlaylist(int playlistId, const QString& newName)
150{
151 qDebug() << "renamePlaylist()";
152
153 m_database.transaction();
154 QSqlQuery query(m_database);
155 query.prepare("UPDATE Playlists SET name = :name WHERE id = :id");
156 query.bindValue(":name", newName);
157 query.bindValue(":id", playlistId);
158
159 if (!query.exec()) {
160 qDebug() << query.executedQuery() << query.lastError();
161 m_database.rollback();
162 }
163 else {
164 m_database.commit();
165 }
166}
167
168
148/** Append a track to a playlist */169/** Append a track to a playlist */
149void PlaylistDAO::appendTrackToPlaylist(int trackId, int playlistId)170void PlaylistDAO::appendTrackToPlaylist(int trackId, int playlistId)
150{171{
151172
=== modified file 'mixxx/src/library/dao/playlistdao.h'
--- mixxx/src/library/dao/playlistdao.h 2010-09-12 20:00:07 +0000
+++ mixxx/src/library/dao/playlistdao.h 2010-12-28 15:47:10 +0000
@@ -17,6 +17,8 @@
17 bool createPlaylist(QString name, bool hidden = false);17 bool createPlaylist(QString name, bool hidden = false);
18 /** Delete a playlist */18 /** Delete a playlist */
19 void deletePlaylist(int playlistId);19 void deletePlaylist(int playlistId);
20 /** Rename a playlist */
21 void renamePlaylist(int playlistId, const QString& newName);
20 /** Append a track to a playlist */22 /** Append a track to a playlist */
21 void appendTrackToPlaylist(int trackId, int playlistId);23 void appendTrackToPlaylist(int trackId, int playlistId);
22 /** Find out how many playlists exist. */24 /** Find out how many playlists exist. */
2325
=== modified file 'mixxx/src/library/playlistfeature.cpp'
--- mixxx/src/library/playlistfeature.cpp 2010-11-25 01:06:46 +0000
+++ mixxx/src/library/playlistfeature.cpp 2010-12-28 15:47:10 +0000
@@ -27,6 +27,10 @@
27 connect(m_pDeletePlaylistAction, SIGNAL(triggered()),27 connect(m_pDeletePlaylistAction, SIGNAL(triggered()),
28 this, SLOT(slotDeletePlaylist()));28 this, SLOT(slotDeletePlaylist()));
2929
30 m_pRenamePlaylistAction = new QAction(tr("Rename"),this);
31 connect(m_pRenamePlaylistAction, SIGNAL(triggered()),
32 this, SLOT(slotRenamePlaylist()));
33
30 // Setup the sidebar playlist model34 // Setup the sidebar playlist model
31 m_playlistTableModel.setTable("Playlists");35 m_playlistTableModel.setTable("Playlists");
32 m_playlistTableModel.setFilter("hidden=0");36 m_playlistTableModel.setFilter("hidden=0");
@@ -95,6 +99,7 @@
95 QMenu menu(NULL);99 QMenu menu(NULL);
96 menu.addAction(m_pCreatePlaylistAction);100 menu.addAction(m_pCreatePlaylistAction);
97 menu.addSeparator();101 menu.addSeparator();
102 menu.addAction(m_pRenamePlaylistAction);
98 menu.addAction(m_pDeletePlaylistAction);103 menu.addAction(m_pDeletePlaylistAction);
99 menu.exec(globalPos);104 menu.exec(globalPos);
100}105}
@@ -143,6 +148,52 @@
143 }148 }
144}149}
145150
151void PlaylistFeature::slotRenamePlaylist()
152{
153 qDebug() << "slotRenamePlaylist()";
154
155 QString oldName = m_lastRightClickedIndex.data().toString();
156 int playlistId = m_playlistDao.getPlaylistIdFromName(oldName);
157
158 QString newName;
159 bool validNameGiven = false;
160
161 do {
162 bool ok = false;
163 newName = QInputDialog::getText(NULL,
164 tr("Rename Playlist"),
165 tr("New playlist name:"),
166 QLineEdit::Normal,
167 oldName,
168 &ok).trimmed();
169
170 if (!ok || oldName == newName) {
171 return;
172 }
173
174 int existingId = m_playlistDao.getPlaylistIdFromName(newName);
175
176 if (existingId != -1) {
177 QMessageBox::warning(NULL,
178 tr("Renaming Playlist Failed"),
179 tr("A playlist by that name already exists."));
180 }
181 else if (newName.isEmpty()) {
182 QMessageBox::warning(NULL,
183 tr("Renaming Playlist Failed"),
184 tr("A playlist cannot have a blank name."));
185 }
186 else {
187 validNameGiven = true;
188 }
189 } while (!validNameGiven);
190
191 m_playlistDao.renamePlaylist(playlistId, newName);
192 m_playlistTableModel.select();
193 emit(featureUpdated());
194 m_pPlaylistTableModel->setPlaylist(playlistId);
195}
196
146void PlaylistFeature::slotDeletePlaylist()197void PlaylistFeature::slotDeletePlaylist()
147{198{
148 //qDebug() << "slotDeletePlaylist() row:" << m_lastRightClickedIndex.data();199 //qDebug() << "slotDeletePlaylist() row:" << m_lastRightClickedIndex.data();
149200
=== modified file 'mixxx/src/library/playlistfeature.h'
--- mixxx/src/library/playlistfeature.h 2010-02-23 20:06:25 +0000
+++ mixxx/src/library/playlistfeature.h 2010-12-28 15:47:10 +0000
@@ -45,6 +45,7 @@
4545
46 void slotCreatePlaylist();46 void slotCreatePlaylist();
47 void slotDeletePlaylist();47 void slotDeletePlaylist();
48 void slotRenamePlaylist();
4849
49 private:50 private:
50 PlaylistTableModel* m_pPlaylistTableModel;51 PlaylistTableModel* m_pPlaylistTableModel;
@@ -52,6 +53,7 @@
52 TrackDAO &m_trackDao;53 TrackDAO &m_trackDao;
53 QAction *m_pCreatePlaylistAction;54 QAction *m_pCreatePlaylistAction;
54 QAction *m_pDeletePlaylistAction;55 QAction *m_pDeletePlaylistAction;
56 QAction *m_pRenamePlaylistAction;
55 QSqlTableModel m_playlistTableModel;57 QSqlTableModel m_playlistTableModel;
56 QModelIndex m_lastRightClickedIndex;58 QModelIndex m_lastRightClickedIndex;
57};59};