Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Hotcue Color Replace Dialog #2547

Merged
merged 66 commits into from
Apr 21, 2020
Merged

Conversation

Holzhaus
Copy link
Member

@Holzhaus Holzhaus commented Mar 13, 2020

This is an inital implementation of the Replace Color Dialog. I don't want to rebase on #2520 all the time, so this is based on master.

Until #2520 has been merged, there'll be some drawbacks:

  • No support for replacing hotcue colors yet, just track colors
  • Uses plain QColorDialog instead of WColorPicker, so no support for "no track color" yet
  • Can be found via the global library menu (will be moved to a button on the color settings preferences page after Make Hotcue RGB colors configurable #2530 has been merged)

Screenshot:
2020-03-13-125202_1018x809_scrot

@Holzhaus Holzhaus added this to the 2.3.0 milestone Mar 13, 2020
@Holzhaus
Copy link
Member Author

Any comments?

@ywwg
Copy link
Member

ywwg commented Mar 15, 2020

I would like to avoid this sort of wizard-style dialog box that does multiple different things at once and forces the user to make a bunch of choices that activate or deactivate different UI elements. Instead, I'd prefer to have one box for recoloring tracks, and another for recoloring hotcues. They can share code and components for efficiency, but they shouldn't be presented in the same UI. We can have two separate options to launch either one.

One reason for this is I expect the UIs for these to diverge, since hotcues and tracks are extremely different items and we may want to provide more complex options for deciding, for instance, how to recolor tracks. ("set red if genre == house"). There's going to be essentially no overlap between the kinds of mutations people will want to do, except that they both involve assigning colors to a bunch of items in a database. That's too weak and technical a reason to combine the UIs.

I can also imagine adding a button in the hotcue color setter so that users can discover the option more easily, so it would make sense to have a way to launch a UI that only works on hotcues. (For instance, a button in the cuecolor popup that says "bulk assign"). same for tracks.

Since this PR doesn't support hotcue recoloring anyway, please have this PR just address recoloring tracks, and make the UI only for that. Again, you can write the code with an eye toward also working with recoloring hotcues, but that should be done in a separate class.

@Holzhaus
Copy link
Member Author

Ok, this dialog only does track color changes now.

@Holzhaus Holzhaus changed the title Add Mass Color Replace Dialog Add Track Color Replace Dialog Mar 15, 2020
@Holzhaus
Copy link
Member Author

2020-03-15-164441_879x681_scrot

@ywwg
Copy link
Member

ywwg commented Mar 15, 2020

thanks!

Can you reduce the big blank area?

@Swiftb0y
Copy link
Member

Can we make it affect selected tracks only and put it the context menu of the tracks in the library or did we explicitly agree against this?

@Holzhaus
Copy link
Member Author

Holzhaus commented Mar 15, 2020

Can we make it affect selected tracks only and put it the context menu of the tracks in the library or did we explicitly agree against this?

That would be redundant, since you can already select multiple tracks, right click and do "Select Color -> Some color" in the library view. This dialog is for bulk changes that affect the whole DB.

@Holzhaus
Copy link
Member Author

thanks!

Can you reduce the big blank area?

Done.

@Swiftb0y
Copy link
Member

That would be redundant, since you can already select multiple tracks, right click and do "Select Color -> Some color" in the library view. This dialog is for bulk changes that affect the whole DB.

Ah right, but then someone could also just sort by colors, select the entire range of the tracks with the color they want, and recolor it that way.
We should still merge the PR in this state, but in the long run I suggest we either get rid of it again and allow people to find the tracks in the library using the search function, or we give the dialog some features the library option can't fulfill.

@Holzhaus
Copy link
Member Author

To be honest, I actually started this for bulk replacing hotcue colors but couldn't really implement that because I'm waiting for #2520, but then thought "Hey, we could also use this for track colors", and later the placeholders for hotcue colors were removed ;-)

You're right, I'm not even certain this dialog is actually helpful except for cases where you database is huge...

@Swiftb0y
Copy link
Member

It certainly makes sense for the hotcues ("we give the dialog some features the library option can't fulfill.") but not really for the track colors.

@Holzhaus Holzhaus changed the title Add Track Color Replace Dialog [WIP] Add Hotcue Color Replace Dialog Mar 16, 2020
@ywwg
Copy link
Member

ywwg commented Mar 16, 2020

in that case, are we sure we want this PR at all? The code might still be useful when we need to make the editor for hotcues, but if there isn't a strong need for it we shouldn't merge it.

@Holzhaus
Copy link
Member Author

Holzhaus commented Mar 16, 2020

No, that'why I changed the title and moved it back to "in progress" on the project board. I'll use this for the hotcue color replace dialog after #2520 has been merged.

@Holzhaus Holzhaus force-pushed the mass-color-replace branch 2 times, most recently from bab0707 to 7926008 Compare March 17, 2020 13:41
@Holzhaus
Copy link
Member Author

Here's how it looks right now:

2020-03-17-144433_1091x683_scrot

@Holzhaus
Copy link
Member Author

I still have problems with invalidating the track cache, but apart from that it's already working. If you don't see the changed colors, try restarting Mixxx.

Copy link
Member

@ywwg ywwg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is coming along nicely, thanks!

src/dialog/dlgreplacecuecolor.h Show resolved Hide resolved
src/dialog/dlgreplacecuecolor.cpp Outdated Show resolved Hide resolved
src/dialog/dlgreplacecuecolor.cpp Outdated Show resolved Hide resolved
src/dialog/dlgreplacecuecolor.cpp Show resolved Hide resolved
src/dialog/dlgreplacecuecolor.cpp Outdated Show resolved Hide resolved
src/widget/wmainmenubar.cpp Outdated Show resolved Hide resolved
src/dialog/dlgreplacecuecolor.h Outdated Show resolved Hide resolved
src/dialog/dlgreplacecuecolor.h Show resolved Hide resolved
@uklotzde
Copy link
Contributor

Please be aware that write access to the database from another thread may cause locks for the main thread if it tries to make any changes concurrently, e.g. when saving track objects or adding/removing tracks to/from playlist or crates. The corresponding queries will be aborted, the changes are lost and a warning appears only in the log, unnoticed by the user. We already have known issues while a library scan is running and updating the database.

Blocking the main thread like during many other actions is also bad. But at least it doesn't cause data loss.

@Holzhaus
Copy link
Member Author

Holzhaus commented Apr 14, 2020

Ok, I think this ready to merge now. The only remaining item of discussion is if we drop processEvents() or not.

@daschuer Can you elaborate what kind of inconsistencies you expect when using this? The only thing I can imagine that could happen is that the user manually edits the color of a cue that is going to be replaced after starting the DB update. In this case it's possible that the color is still being replaced. From the user perspective, this behaviour shouldn't be suprising. And changing a color would only be possible via a controller script because the dialog is application modal.

@daschuer
Copy link
Member

daschuer commented Apr 14, 2020

If we use processEvents() from a slot, it is a unprotected recursive call, because all slots are called from processEvents().

I am unsure if this is already an issue here, but such a call hidden in a dialog can be a trap, if we use this pattern elsewhere and its very unexpected.

Because of various issues we have removed all such processEvents() calls from the Mixxx source.

Analyze all edge cases in this case is some work and you cannot be sure if you concider all current and future cases.

From top of my head I think it is an issue to have interlaced sqlite transactions.
This happens for example if Auto DJ loads a new track during changing colors.

@uklotzde
Copy link
Contributor

If we use processEvents() from a slot, it is a unprotected recursive call, because all slots are called from processEvents().

I am unsure if this is already an issue here, but such a call hidden in a dialog can be a trap, if we use this pattern elsewhere and its very unexpected.

Because of various issues we have removed all such processEvents() calls from the Mixxx source.

Analyze all edge cases in this case is some work and you cannot be sure if you concider all current and future cases.

From top of my head I think it is an issue to have interlaced sqlite transactions.
This happens for example if Auto DJ loads a new track during changing colors.

I agree that the transaction argument is valid. Should we prevent that our local transaction gets hijacked by using a separate, cloned database connection? Then AutoDJ might fail to write its changes during the batch processing.

Sending any signal in Qt could have a similar, cascading effect. Is processEvents() really so much different? Maybe those processEvents() calls just revealed other shortcomings in the code and were not the actual cause of the issues.

@daschuer
Copy link
Member

Each thread has it's own main loop calling processEvents() in a loop checking if a signal arrives to calls the slot. If a slot emits a signal with a direct connected slot, it is processed almost like a direct callback. This cause a sort of predictable interlaceing, that's all.
No other slots are processed by this thread once in a slot.
This gurantees that we have no other interlaced calls than due to different threads.
A processEvent() call within a slot breaks this assumption, because any slot can be called during the first slot.

@uklotzde
Copy link
Contributor

If we can ensure that the code calling processEvents() is reentrant regarding this yield point it should not be an issue. By calling processEvents() you are responsible for ensuring that any other of your slots might be invoked (by the same thread) until execution continues at that point. Let's check that this is the case for DlgReplaceCueColor and document accordingly.

If transaction hijacking is considered a problem I would simply use a transaction for each track. It takes more time and we loose the rollback-all feature, but who cares. You could at least complete the operation simply by executing it again.

@daschuer
Copy link
Member

Is the GUI stalling issue really such a problem?
I don't think so, compared to other GUI stalled issues we have, and the case that the color excsnge can be stopped half way.

So let's go the save and easy route and remove the processEvent() call.

@uklotzde
Copy link
Contributor

Is the GUI stalling issue really such a problem?
I don't think so, compared to other GUI stalled issues we have, and the case that the color excsnge can be stopped half way.

So let's go the save and easy route and remove the processEvent() call.

This operation is supposed to be executed on a large number of tracks. Therefore we need a progress dialog. A modal progress dialog calls processEvents() behind the scenes.

@daschuer
Copy link
Member

daschuer commented Apr 15, 2020

Ups, never mind. I think it was an merge issue. Now I build the clean branch.

@daschuer
Copy link
Member

Ok, I did some tests. Interestingly unlike the normal preferences dialog Mixxx can be not controlled once the GUI dialog is open. So there is a low risk that the processEvents() does any harm.

There is an issue with the checkboxes that makes the "All warning visible" if checkbox is checked and it can also be hidden if no check-boxes is are checked.

The rest is OK.

@Holzhaus
Copy link
Member Author

There is an issue with the checkboxes that makes the "All warning visible" if checkbox is checked and it can also be hidden if no check-boxes is are checked.

Sorry, I don't understand. The warning should be shown if no checkbox is checked and hidden otherwise. Doesn't this work for you?

@daschuer
Copy link
Member

That dos not work reliable after you have play around with them for a while.
Maybe the update code is not executed on every change?

@Holzhaus
Copy link
Member Author

Holzhaus commented Apr 16, 2020

That dos not work reliable after you have play around with them for a while.
Maybe the update code is not executed on every change?

Oh right, I forgot to connect the stateChanged signal of the Hotcue Index checkbox. Should be fixed now. Thanks.

@Holzhaus Holzhaus requested a review from daschuer April 16, 2020 12:31
@Holzhaus
Copy link
Member Author

Merge?

@Be-ing
Copy link
Contributor

Be-ing commented Apr 20, 2020

ping

@ronso0
Copy link
Member

ronso0 commented Apr 20, 2020

I cannot reproduce the checkbox bug @daschuer encountered.
GUI-wise this is ready IMO!

Copy link
Member

@daschuer daschuer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, Than you.

@daschuer
Copy link
Member

@ywwg: Is everything fine from your review?

@Be-ing
Copy link
Contributor

Be-ing commented Apr 21, 2020

@ywwg has not raised any concerns about this in weeks, so I'm going to go ahead and merge it now.

@Be-ing Be-ing merged commit 961056e into mixxxdj:master Apr 21, 2020
@Be-ing
Copy link
Contributor

Be-ing commented Apr 22, 2020

Whoops, Windows builds are failing:

[CXX] src\dialog\dlgreplacecuecolor.cpp
dlgreplacecuecolor.cpp
src\dialog\dlgreplacecuecolor.cpp(339): error C2059: syntax error: '.'
src\dialog\dlgreplacecuecolor.cpp(342): error C2143: syntax error: missing ';' before '}'
src\dialog\dlgreplacecuecolor.cpp(343): error C2065: 'row': undeclared identifier
src\dialog\dlgreplacecuecolor.cpp(344): error C2065: 'row': undeclared identifier
src\dialog\dlgreplacecuecolor.cpp(348): error C3927: '->': trailing return type is not allowed after a non-function declarator
src\dialog\dlgreplacecuecolor.cpp(348): error C3484: syntax error: expected '->' before the return type
src\dialog\dlgreplacecuecolor.cpp(348): error C3613: missing return type after '->' ('int' assumed)
src\dialog\dlgreplacecuecolor.cpp(348): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(348): error C2146: syntax error: missing ';' before identifier 'reset'
src\dialog\dlgreplacecuecolor.cpp(350): error C2059: syntax error: 'if'
src\dialog\dlgreplacecuecolor.cpp(350): error C2143: syntax error: missing ';' before '{'
src\dialog\dlgreplacecuecolor.cpp(350): error C2447: '{': missing function header (old-style formal list?)
src\dialog\dlgreplacecuecolor.cpp(359): error C2059: syntax error: 'if'
src\dialog\dlgreplacecuecolor.cpp(365): error C2143: syntax error: missing ';' before '{'
src\dialog\dlgreplacecuecolor.cpp(365): error C2447: '{': missing function header (old-style formal list?)
src\dialog\dlgreplacecuecolor.cpp(372): error C3927: '->': trailing return type is not allowed after a non-function declarator
src\dialog\dlgreplacecuecolor.cpp(372): error C3484: syntax error: expected '->' before the return type
src\dialog\dlgreplacecuecolor.cpp(372): error C3613: missing return type after '->' ('int' assumed)
src\dialog\dlgreplacecuecolor.cpp(372): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(372): error C2086: 'int progress': redefinition
src\dialog\dlgreplacecuecolor.cpp(348): note: see declaration of 'progress'
src\dialog\dlgreplacecuecolor.cpp(372): error C2146: syntax error: missing ';' before identifier 'setLabelText'
src\dialog\dlgreplacecuecolor.cpp(373): error C3927: '->': trailing return type is not allowed after a non-function declarator
src\dialog\dlgreplacecuecolor.cpp(373): error C3484: syntax error: expected '->' before the return type
src\dialog\dlgreplacecuecolor.cpp(373): error C3613: missing return type after '->' ('int' assumed)
src\dialog\dlgreplacecuecolor.cpp(373): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(373): error C2086: 'int progress': redefinition
src\dialog\dlgreplacecuecolor.cpp(348): note: see declaration of 'progress'
src\dialog\dlgreplacecuecolor.cpp(373): error C2146: syntax error: missing ';' before identifier 'setValue'
src\dialog\dlgreplacecuecolor.cpp(375): error C2065: 'database': undeclared identifier
src\dialog\dlgreplacecuecolor.cpp(376): error C2065: 'database': undeclared identifier
src\dialog\dlgreplacecuecolor.cpp(377): error C3927: '->': trailing return type is not allowed after a non-function declarator
src\dialog\dlgreplacecuecolor.cpp(377): error C3484: syntax error: expected '->' before the return type
src\dialog\dlgreplacecuecolor.cpp(377): error C3613: missing return type after '->' ('int' assumed)
src\dialog\dlgreplacecuecolor.cpp(377): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(377): error C2371: 'query': redefinition; different basic types
src\dialog\dlgreplacecuecolor.cpp(376): note: see declaration of 'query'
src\dialog\dlgreplacecuecolor.cpp(377): error C2146: syntax error: missing ';' before identifier 'prepare'
src\dialog\dlgreplacecuecolor.cpp(380): error C3927: '->': trailing return type is not allowed after a non-function declarator
src\dialog\dlgreplacecuecolor.cpp(380): error C3484: syntax error: expected '->' before the return type
src\dialog\dlgreplacecuecolor.cpp(380): error C3613: missing return type after '->' ('int' assumed)
src\dialog\dlgreplacecuecolor.cpp(380): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(380): error C2371: 'query': redefinition; different basic types
src\dialog\dlgreplacecuecolor.cpp(376): note: see declaration of 'query'
src\dialog\dlgreplacecuecolor.cpp(380): error C2146: syntax error: missing ';' before identifier 'bindValue'
src\dialog\dlgreplacecuecolor.cpp(385): error C2059: syntax error: 'for'
src\dialog\dlgreplacecuecolor.cpp(385): error C2059: syntax error: ')'
src\dialog\dlgreplacecuecolor.cpp(385): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(386): error C2059: syntax error: ';'
src\dialog\dlgreplacecuecolor.cpp(387): error C2059: syntax error: 'if'
src\dialog\dlgreplacecuecolor.cpp(387): error C2143: syntax error: missing ';' before '{'
src\dialog\dlgreplacecuecolor.cpp(387): error C2447: '{': missing function header (old-style formal list?)
src\dialog\dlgreplacecuecolor.cpp(391): error C3927: '->': trailing return type is not allowed after a non-function declarator
src\dialog\dlgreplacecuecolor.cpp(391): error C3484: syntax error: expected '->' before the return type
src\dialog\dlgreplacecuecolor.cpp(391): error C3613: missing return type after '->' ('int' assumed)
src\dialog\dlgreplacecuecolor.cpp(391): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(391): error C2371: 'query': redefinition; different basic types
src\dialog\dlgreplacecuecolor.cpp(376): note: see declaration of 'query'
src\dialog\dlgreplacecuecolor.cpp(391): error C2146: syntax error: missing ';' before identifier 'bindValue'
src\dialog\dlgreplacecuecolor.cpp(392): error C3927: '->': trailing return type is not allowed after a non-function declarator
src\dialog\dlgreplacecuecolor.cpp(392): error C3484: syntax error: expected '->' before the return type
src\dialog\dlgreplacecuecolor.cpp(392): error C3613: missing return type after '->' ('int' assumed)
src\dialog\dlgreplacecuecolor.cpp(392): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(392): error C2371: 'query': redefinition; different basic types
src\dialog\dlgreplacecuecolor.cpp(376): note: see declaration of 'query'
src\dialog\dlgreplacecuecolor.cpp(392): error C2146: syntax error: missing ';' before identifier 'bindValue'
src\dialog\dlgreplacecuecolor.cpp(393): error C3927: '->': trailing return type is not allowed after a non-function declarator
src\dialog\dlgreplacecuecolor.cpp(393): error C3484: syntax error: expected '->' before the return type
src\dialog\dlgreplacecuecolor.cpp(393): error C3613: missing return type after '->' ('int' assumed)
src\dialog\dlgreplacecuecolor.cpp(393): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(393): error C2371: 'query': redefinition; different basic types
src\dialog\dlgreplacecuecolor.cpp(376): note: see declaration of 'query'
src\dialog\dlgreplacecuecolor.cpp(393): error C2146: syntax error: missing ';' before identifier 'bindValue'
src\dialog\dlgreplacecuecolor.cpp(394): error C2059: syntax error: 'if'
src\dialog\dlgreplacecuecolor.cpp(394): error C2143: syntax error: missing ';' before '{'
src\dialog\dlgreplacecuecolor.cpp(394): error C2447: '{': missing function header (old-style formal list?)
src\dialog\dlgreplacecuecolor.cpp(400): error C2059: syntax error: 'if'
src\dialog\dlgreplacecuecolor.cpp(400): error C2143: syntax error: missing ';' before '{'
src\dialog\dlgreplacecuecolor.cpp(400): error C2447: '{': missing function header (old-style formal list?)
src\dialog\dlgreplacecuecolor.cpp(409): error C2059: syntax error: '}'
src\dialog\dlgreplacecuecolor.cpp(409): error C2143: syntax error: missing ';' before '}'
src\dialog\dlgreplacecuecolor.cpp(411): error C2143: syntax error: missing ';' before '{'
src\dialog\dlgreplacecuecolor.cpp(411): error C2447: '{': missing function header (old-style formal list?)
src\dialog\dlgreplacecuecolor.cpp(413): error C2059: syntax error: 'else'
src\dialog\dlgreplacecuecolor.cpp(413): error C2143: syntax error: missing ';' before '{'
src\dialog\dlgreplacecuecolor.cpp(413): error C2447: '{': missing function header (old-style formal list?)
src\dialog\dlgreplacecuecolor.cpp(432): error C3927: '->': trailing return type is not allowed after a non-function declarator
src\dialog\dlgreplacecuecolor.cpp(432): error C3484: syntax error: expected '->' before the return type
src\dialog\dlgreplacecuecolor.cpp(432): error C3613: missing return type after '->' ('int' assumed)
src\dialog\dlgreplacecuecolor.cpp(432): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(432): error C2086: 'int progress': redefinition
src\dialog\dlgreplacecuecolor.cpp(348): note: see declaration of 'progress'
src\dialog\dlgreplacecuecolor.cpp(432): error C2146: syntax error: missing ';' before identifier 'reset'
src\dialog\dlgreplacecuecolor.cpp(433): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(434): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(435): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src\dialog\dlgreplacecuecolor.cpp(436): error C2059: syntax error: '}'
src\dialog\dlgreplacecuecolor.cpp(436): error C2143: syntax error: missing ';' before '}'
src\dialog\dlgreplacecuecolor.cpp(438): error C2143: syntax error: missing ';' before '{'
src\dialog\dlgreplacecuecolor.cpp(438): error C2447: '{': missing function header (old-style formal list?)
scons: *** [win64_build\src\dialog\dlgreplacecuecolor.obj] Error 2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants