mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-04 22:49:07 +02:00
Error messages for disallowed commands are table-driven too
This commit is contained in:
parent
b09a1af564
commit
1b329b0e36
186
src/Menus.cpp
186
src/Menus.cpp
@ -448,13 +448,62 @@ ReservedCommandFlag::ReservedCommandFlag(
|
|||||||
Options().emplace_back( options );
|
Options().emplace_back( options );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const CommandFlagOptions cutCopyOptions{
|
||||||
|
// In reporting the issue with cut or copy, we don't tell the user they could also select some text in a label.
|
||||||
|
[]( const wxString &Name ) {
|
||||||
|
// PRL: These strings have hard-coded mention of a certain shortcut key,
|
||||||
|
// thus assuming the default shortcuts. That is questionable.
|
||||||
|
wxString format;
|
||||||
|
#ifdef EXPERIMENTAL_DA
|
||||||
|
// i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade.
|
||||||
|
format = _("You must first select some audio for '%s' to act on.\n\nCtrl + A selects all audio.");
|
||||||
|
#else
|
||||||
|
#ifdef __WXMAC__
|
||||||
|
// i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade.
|
||||||
|
format = _("Select the audio for %s to use (for example, Cmd + A to Select All) then try again."
|
||||||
|
// No need to explain what a help button is for.
|
||||||
|
// "\n\nClick the Help button to learn more about selection methods."
|
||||||
|
);
|
||||||
|
|
||||||
|
#else
|
||||||
|
// i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade.
|
||||||
|
format = _("Select the audio for %s to use (for example, Ctrl + A to Select All) then try again."
|
||||||
|
// No need to explain what a help button is for.
|
||||||
|
// "\n\nClick the Help button to learn more about selection methods."
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
return wxString::Format( format, Name );
|
||||||
|
#endif
|
||||||
|
},
|
||||||
|
"Selecting_Audio_-_the_basics",
|
||||||
|
XO("No Audio Selected")
|
||||||
|
};
|
||||||
|
|
||||||
const ReservedCommandFlag
|
const ReservedCommandFlag
|
||||||
|
// This flag has higher priority than others for purposes of help messages.
|
||||||
|
// It was important to arrange that for reasons of breaking dependency
|
||||||
|
// cycles, giving more freedom in the choice of file in which to implement
|
||||||
|
// this flag.
|
||||||
AudioIONotBusyFlag{
|
AudioIONotBusyFlag{
|
||||||
[](const AudacityProject &project ){
|
[](const AudacityProject &project ){
|
||||||
return !AudioIOBusyPred( project );
|
return !AudioIOBusyPred( project );
|
||||||
},
|
},
|
||||||
CommandFlagOptions{}.QuickTest()
|
CommandFlagOptions{ []( const wxString& ) { return
|
||||||
}, //lll
|
// This reason will not be shown, because options that require it will be greyed out.
|
||||||
|
_("You can only do this when playing and recording are\nstopped. (Pausing is not sufficient.)");
|
||||||
|
} }
|
||||||
|
.QuickTest()
|
||||||
|
.Priority( 1 )
|
||||||
|
}; //lll
|
||||||
|
|
||||||
|
const ReservedCommandFlag
|
||||||
|
// The sequence of these definitions has a minor significance in determining
|
||||||
|
// which user error message has precedence if more than one might apply, so
|
||||||
|
// they should be kept in this sequence in one .cpp file if it is important
|
||||||
|
// to preserve that behavior. If they are dispersed to more than one file,
|
||||||
|
// then the precedence will be unspecified.
|
||||||
|
// The ordering of the flags that only disable the default message is not
|
||||||
|
// significant.
|
||||||
StereoRequiredFlag{
|
StereoRequiredFlag{
|
||||||
[](const AudacityProject &project){
|
[](const AudacityProject &project){
|
||||||
// True iff at least one stereo track is selected, i.e., at least
|
// True iff at least one stereo track is selected, i.e., at least
|
||||||
@ -463,10 +512,15 @@ const ReservedCommandFlag
|
|||||||
auto range = TrackList::Get( project ).Selected<const WaveTrack>()
|
auto range = TrackList::Get( project ).Selected<const WaveTrack>()
|
||||||
- &Track::IsLeader;
|
- &Track::IsLeader;
|
||||||
return !range.empty();
|
return !range.empty();
|
||||||
}
|
},
|
||||||
|
{ []( const wxString& ) { return
|
||||||
|
// This reason will not be shown, because the stereo-to-mono is greyed out if not allowed.
|
||||||
|
_("You must first select some stereo audio to perform this\naction. (You cannot use this with mono.)");
|
||||||
|
} }
|
||||||
}, //lda
|
}, //lda
|
||||||
TimeSelectedFlag{
|
TimeSelectedFlag{
|
||||||
TimeSelectedPred
|
TimeSelectedPred,
|
||||||
|
cutCopyOptions
|
||||||
},
|
},
|
||||||
CutCopyAvailableFlag{
|
CutCopyAvailableFlag{
|
||||||
[](const AudacityProject &project){
|
[](const AudacityProject &project){
|
||||||
@ -485,20 +539,30 @@ const ReservedCommandFlag
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
|
cutCopyOptions
|
||||||
},
|
},
|
||||||
WaveTracksSelectedFlag{
|
WaveTracksSelectedFlag{
|
||||||
[](const AudacityProject &project){
|
[](const AudacityProject &project){
|
||||||
return !TrackList::Get( project ).Selected<const WaveTrack>().empty();
|
return !TrackList::Get( project ).Selected<const WaveTrack>().empty();
|
||||||
}
|
},
|
||||||
|
{ []( const wxString& ) { return
|
||||||
|
_("You must first select some audio to perform this action.\n(Selecting other kinds of track won't work.)");
|
||||||
|
} }
|
||||||
},
|
},
|
||||||
TracksExistFlag{
|
TracksExistFlag{
|
||||||
[](const AudacityProject &project){
|
[](const AudacityProject &project){
|
||||||
return !TrackList::Get( project ).Any().empty();
|
return !TrackList::Get( project ).Any().empty();
|
||||||
}
|
},
|
||||||
|
CommandFlagOptions{}.DisableDefaultMessage()
|
||||||
},
|
},
|
||||||
TracksSelectedFlag{
|
TracksSelectedFlag{
|
||||||
TracksSelectedPred
|
TracksSelectedPred,
|
||||||
|
{ []( const wxString &Name ){ return wxString::Format(
|
||||||
|
// i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks".
|
||||||
|
_("\"%s\" requires one or more tracks to be selected."),
|
||||||
|
Name
|
||||||
|
); } }
|
||||||
},
|
},
|
||||||
TrackPanelHasFocus{
|
TrackPanelHasFocus{
|
||||||
[](const AudacityProject &project){
|
[](const AudacityProject &project){
|
||||||
@ -507,7 +571,8 @@ const ReservedCommandFlag
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
|
CommandFlagOptions{}.DisableDefaultMessage()
|
||||||
}; //lll
|
}; //lll
|
||||||
|
|
||||||
const ReservedCommandFlag
|
const ReservedCommandFlag
|
||||||
@ -987,60 +1052,69 @@ void MenuManager::TellUserWhyDisallowed(
|
|||||||
{
|
{
|
||||||
// The default string for 'reason' is a catch all. I hope it won't ever be seen
|
// The default string for 'reason' is a catch all. I hope it won't ever be seen
|
||||||
// and that we will get something more specific.
|
// and that we will get something more specific.
|
||||||
wxString reason = _("There was a problem with your last action. If you think\nthis is a bug, please tell us exactly where it occurred.");
|
auto reason = _("There was a problem with your last action. If you think\nthis is a bug, please tell us exactly where it occurred.");
|
||||||
// The default title string is 'Disallowed'.
|
// The default title string is 'Disallowed'.
|
||||||
wxString title = _("Disallowed");
|
auto untranslatedTitle = XO("Disallowed");
|
||||||
wxString helpPage;
|
wxString helpPage;
|
||||||
|
|
||||||
auto missingFlags = flagsRequired & ~flagsGot;
|
bool enableDefaultMessage = true;
|
||||||
if( (missingFlags & AudioIONotBusyFlag).any() )
|
bool defaultMessage = true;
|
||||||
// This reason will not be shown, because options that require it will be greyed our.
|
|
||||||
reason = _("You can only do this when playing and recording are\nstopped. (Pausing is not sufficient.)");
|
|
||||||
else if( (missingFlags & StereoRequiredFlag).any() )
|
|
||||||
// This reason will not be shown, because the stereo-to-mono is greyed out if not allowed.
|
|
||||||
reason = _("You must first select some stereo audio to perform this\naction. (You cannot use this with mono.)");
|
|
||||||
// In reporting the issue with cut or copy, we don't tell the user they could also select some text in a label.
|
|
||||||
else if( (
|
|
||||||
( missingFlags & TimeSelectedFlag ) |
|
|
||||||
( missingFlags & CutCopyAvailableFlag )
|
|
||||||
).any() ){
|
|
||||||
title = _("No Audio Selected");
|
|
||||||
#ifdef EXPERIMENTAL_DA
|
|
||||||
// i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade.
|
|
||||||
reason = wxString::Format( _("You must first select some audio for '%s' to act on.\n\nCtrl + A selects all audio."), Name );
|
|
||||||
#else
|
|
||||||
#ifdef __WXMAC__
|
|
||||||
// i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade.
|
|
||||||
reason = wxString::Format( _("Select the audio for %s to use (for example, Cmd + A to Select All) then try again."
|
|
||||||
// No need to explain what a help button is for.
|
|
||||||
// "\n\nClick the Help button to learn more about selection methods."
|
|
||||||
), Name );
|
|
||||||
|
|
||||||
#else
|
auto doOption = [&](const CommandFlagOptions &options) {
|
||||||
// i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade.
|
if ( options.message ) {
|
||||||
reason = wxString::Format( _("Select the audio for %s to use (for example, Ctrl + A to Select All) then try again."
|
reason = options.message( Name );
|
||||||
// No need to explain what a help button is for.
|
defaultMessage = false;
|
||||||
// "\n\nClick the Help button to learn more about selection methods."
|
if ( !options.title.empty() )
|
||||||
), Name );
|
untranslatedTitle = options.title;
|
||||||
#endif
|
helpPage = options.helpPage;
|
||||||
#endif
|
return true;
|
||||||
helpPage = "Selecting_Audio_-_the_basics";
|
}
|
||||||
|
else {
|
||||||
|
enableDefaultMessage =
|
||||||
|
enableDefaultMessage && options.enableDefaultMessage;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto &alloptions = Options();
|
||||||
|
auto missingFlags = flagsRequired & ~flagsGot;
|
||||||
|
|
||||||
|
// Find greatest priority
|
||||||
|
unsigned priority = 0;
|
||||||
|
for ( const auto &options : alloptions )
|
||||||
|
priority = std::max( priority, options.priority );
|
||||||
|
|
||||||
|
// Visit all unsatisfied conditions' options, by descending priority,
|
||||||
|
// stopping when we find a message
|
||||||
|
++priority;
|
||||||
|
while( priority-- ) {
|
||||||
|
size_t ii = 0;
|
||||||
|
for ( const auto &options : alloptions ) {
|
||||||
|
if (
|
||||||
|
priority == options.priority
|
||||||
|
&&
|
||||||
|
missingFlags[ii]
|
||||||
|
&&
|
||||||
|
doOption( options ) )
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
++ii;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if( (missingFlags & WaveTracksSelectedFlag).any() )
|
done:
|
||||||
reason = _("You must first select some audio to perform this action.\n(Selecting other kinds of track won't work.)");
|
|
||||||
else if ( (missingFlags & TracksSelectedFlag).any() )
|
if (
|
||||||
// i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks".
|
// didn't find a message
|
||||||
reason = wxString::Format(_("\"%s\" requires one or more tracks to be selected."), Name);
|
defaultMessage
|
||||||
// If the only thing wrong was no tracks, we do nothing and don't report a problem
|
&&
|
||||||
else if( missingFlags == TracksExistFlag )
|
// did find a condition that suppresses the default message
|
||||||
return;
|
!enableDefaultMessage
|
||||||
// Likewise return if it was just no tracks, and track panel did not have focus. (e.g. up-arrow to move track)
|
)
|
||||||
else if( missingFlags == (TracksExistFlag | TrackPanelHasFocus) )
|
|
||||||
return;
|
|
||||||
// Likewise as above too...
|
|
||||||
else if( missingFlags == TrackPanelHasFocus )
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Message is already translated but title is not yet
|
||||||
|
auto title = ::GetCustomTranslation( untranslatedTitle );
|
||||||
|
|
||||||
// Does not have the warning icon...
|
// Does not have the warning icon...
|
||||||
ShowErrorDialog(
|
ShowErrorDialog(
|
||||||
NULL,
|
NULL,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <wx/string.h>
|
||||||
|
|
||||||
class AudacityProject;
|
class AudacityProject;
|
||||||
|
|
||||||
@ -33,9 +34,44 @@ constexpr CommandFlag
|
|||||||
NoFlagsSpecified{ ~0ULL }; // all ones
|
NoFlagsSpecified{ ~0ULL }; // all ones
|
||||||
|
|
||||||
struct CommandFlagOptions{
|
struct CommandFlagOptions{
|
||||||
|
// Supplied the translated name of the command, returns a translated
|
||||||
|
// error message
|
||||||
|
using MessageFormatter = std::function< wxString( const wxString& ) >;
|
||||||
|
|
||||||
CommandFlagOptions() = default;
|
CommandFlagOptions() = default;
|
||||||
|
CommandFlagOptions(
|
||||||
|
const MessageFormatter &message_,
|
||||||
|
const wxString &helpPage_ = {},
|
||||||
|
const wxString &title_ = {}
|
||||||
|
) : message{ message_ }, helpPage{ helpPage_ }, title{ title_ }
|
||||||
|
{}
|
||||||
|
|
||||||
CommandFlagOptions && QuickTest() &&
|
CommandFlagOptions && QuickTest() &&
|
||||||
{ quickTest = true; return std::move( *this ); }
|
{ quickTest = true; return std::move( *this ); }
|
||||||
|
CommandFlagOptions && DisableDefaultMessage() &&
|
||||||
|
{ enableDefaultMessage = false; return std::move( *this ); }
|
||||||
|
CommandFlagOptions && Priority( unsigned priority_ ) &&
|
||||||
|
{ priority = priority_; return std::move( *this ); }
|
||||||
|
|
||||||
|
// null, or else computes non-default message for the dialog box when the
|
||||||
|
// condition is not satisfied for the selected command
|
||||||
|
MessageFormatter message;
|
||||||
|
|
||||||
|
// Title and help page are used only if a message function is given
|
||||||
|
wxString helpPage;
|
||||||
|
|
||||||
|
// Empty, or non-default title for the dialog box when the
|
||||||
|
// condition is not satisfied for the selected command
|
||||||
|
// This string must be given UN-translated.
|
||||||
|
wxString title;
|
||||||
|
|
||||||
|
// Conditions with higher "priority" are preferred over others in choosing
|
||||||
|
// the help message
|
||||||
|
unsigned priority = 0;
|
||||||
|
|
||||||
|
// If false, and no other condition with a message is unsatisfied, then
|
||||||
|
// display no dialog box at all when this condition is not satisfied
|
||||||
|
bool enableDefaultMessage = true;
|
||||||
|
|
||||||
// If true, assume this is a cheap test to be done always. If false, the
|
// If true, assume this is a cheap test to be done always. If false, the
|
||||||
// test may be skipped and the condition assumed to be unchanged since the
|
// test may be skipped and the condition assumed to be unchanged since the
|
||||||
|
Loading…
x
Reference in New Issue
Block a user