mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-01 16:19:43 +02:00
Some progress using wrappers of wxString to make type distinctions
This commit is contained in:
commit
a7469ad674
@ -46,11 +46,9 @@
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <wx/debug.h> // for wxASSERT
|
||||
#include <wx/string.h> // type used in inline function
|
||||
#include <wx/string.h> // type used in inline function and member variable
|
||||
#include <wx/version.h> // for wxCHECK_VERSION
|
||||
|
||||
class wxString;
|
||||
|
||||
#if !wxCHECK_VERSION(3, 1, 0)
|
||||
// For using std::unordered_map on wxString
|
||||
namespace std
|
||||
@ -73,6 +71,199 @@ namespace std
|
||||
// until proper public headers are created for the stuff in here.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// An explicitly nonlocalized string, not meant for the user to see.
|
||||
// String manipulations are discouraged, other than splitting and joining on
|
||||
// separator characters.
|
||||
// Wherever GET is used to fetch the underlying wxString, there should be a
|
||||
// comment explaining the need for it.
|
||||
class Identifier
|
||||
{
|
||||
public:
|
||||
|
||||
Identifier() = default;
|
||||
|
||||
// Allow implicit conversion to this class, but not from
|
||||
Identifier(const wxString &str) : value{ str } {}
|
||||
|
||||
// Allow implicit conversion to this class, but not from
|
||||
Identifier(const wxChar *str) : value{ str } {}
|
||||
|
||||
// Allow implicit conversion to this class, but not from
|
||||
Identifier(const char *str) : value{ str } {}
|
||||
|
||||
// Copy construction and assignment
|
||||
Identifier( const Identifier & ) = default;
|
||||
Identifier &operator = ( const Identifier& ) = default;
|
||||
|
||||
// Move construction and assignment
|
||||
Identifier( wxString && str )
|
||||
{ value.swap( str ); }
|
||||
Identifier( Identifier && id )
|
||||
{ swap( id ); }
|
||||
Identifier &operator= ( Identifier&& id )
|
||||
{
|
||||
if ( this != &id )
|
||||
value.clear(), swap( id );
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Implements moves
|
||||
void swap( Identifier &id ) { value.swap( id.value ); }
|
||||
|
||||
// Convenience for building concatenated identifiers.
|
||||
// The list must have at least two members
|
||||
// (so you don't easily circumvent the restrictions on interconversions
|
||||
// intended in TaggedIdentifier below)
|
||||
explicit
|
||||
Identifier(std::initializer_list<Identifier> components, wxChar separator);
|
||||
|
||||
bool empty() const { return value.empty(); }
|
||||
size_t size() const { return value.size(); }
|
||||
size_t length() const { return value.length(); }
|
||||
|
||||
// Explicit conversion to wxString, meant to be ugly-looking and
|
||||
// demanding of a comment why it's correct
|
||||
const wxString &GET() const { return value; }
|
||||
|
||||
std::vector< Identifier > split( wxChar separator ) const;
|
||||
|
||||
private:
|
||||
wxString value;
|
||||
};
|
||||
|
||||
// Comparisons of Identifiers are case-sensitive
|
||||
inline bool operator == ( const Identifier &x, const Identifier &y )
|
||||
{ return x.GET() == y.GET(); }
|
||||
|
||||
inline bool operator != ( const Identifier &x, const Identifier &y )
|
||||
{ return !(x == y); }
|
||||
|
||||
inline bool operator < ( const Identifier &x, const Identifier &y )
|
||||
{ return x.GET() < y.GET(); }
|
||||
|
||||
inline bool operator > ( const Identifier &x, const Identifier &y )
|
||||
{ return y < x; }
|
||||
|
||||
inline bool operator <= ( const Identifier &x, const Identifier &y )
|
||||
{ return !(y < x); }
|
||||
|
||||
inline bool operator >= ( const Identifier &x, const Identifier &y )
|
||||
{ return !(x < y); }
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash< Identifier > {
|
||||
size_t operator () ( const Identifier &id ) const // noexcept
|
||||
{ return hash<wxString>{}( id.GET() ); }
|
||||
};
|
||||
}
|
||||
|
||||
// This lets you pass Identifier into wxFileConfig::Read
|
||||
inline bool wxFromString(const wxString& str, Identifier *id)
|
||||
{ if (id) { *id = str; return true; } else return false; }
|
||||
|
||||
// This lets you pass Identifier into wxFileConfig::Write
|
||||
inline wxString wxToString( const Identifier& str ) { return str.GET(); }
|
||||
|
||||
// Template parameter allows generation of different TaggedIdentifier classes
|
||||
// that don't interconvert implicitly
|
||||
// The second template parameter determines whether comparisons are case
|
||||
// sensitive; default is case sensitive
|
||||
template<typename Tag, bool CaseSensitive = true >
|
||||
class TaggedIdentifier : public Identifier
|
||||
{
|
||||
public:
|
||||
|
||||
using TagType = Tag;
|
||||
|
||||
using Identifier::Identifier;
|
||||
TaggedIdentifier() = default;
|
||||
|
||||
// Allowed for the same Tag class and case sensitivity
|
||||
TaggedIdentifier( const TaggedIdentifier& ) = default;
|
||||
TaggedIdentifier( TaggedIdentifier&& ) = default;
|
||||
TaggedIdentifier& operator= ( const TaggedIdentifier& ) = default;
|
||||
TaggedIdentifier& operator= ( TaggedIdentifier&& ) = default;
|
||||
|
||||
// Prohibited for other Tag classes or case sensitivity
|
||||
template< typename Tag2, bool b >
|
||||
TaggedIdentifier( const TaggedIdentifier<Tag2, b>& ) = delete;
|
||||
template< typename Tag2, bool b >
|
||||
TaggedIdentifier( TaggedIdentifier<Tag2, b>&& ) = delete;
|
||||
template< typename Tag2, bool b >
|
||||
TaggedIdentifier& operator= ( const TaggedIdentifier<Tag2, b>& ) = delete;
|
||||
template< typename Tag2, bool b >
|
||||
TaggedIdentifier& operator= ( TaggedIdentifier<Tag2, b>&& ) = delete;
|
||||
|
||||
// Allow implicit conversion to this class from un-tagged Identifier,
|
||||
// but not from; resolution will use other overloads above if argument
|
||||
// has a tag
|
||||
TaggedIdentifier(const Identifier &str) : Identifier{ str } {}
|
||||
|
||||
// Conversion to another kind of TaggedIdentifier
|
||||
template<typename String, typename = typename String::TagType>
|
||||
String CONVERT() const
|
||||
{ return String{ this->GET() }; }
|
||||
};
|
||||
|
||||
// Comparison of a TaggedIdentifier with an Identifier is allowed, resolving
|
||||
// to one of the operators on Identifiers defined above, but always case
|
||||
// sensitive.
|
||||
|
||||
// Comparison operators for two TaggedIdentifers, below, require the same tags
|
||||
// and case sensitivity.
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator == (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{
|
||||
static_assert( std::is_same< Tag1, Tag2 >::value && b1 == b2,
|
||||
"TaggedIdentifiers with different tags or sensitivity are not comparable" );
|
||||
// This test should be eliminated at compile time:
|
||||
if ( b1 )
|
||||
return x.GET(). Cmp ( y.GET() ) == 0;
|
||||
else
|
||||
return x.GET(). CmpNoCase ( y.GET() ) == 0;
|
||||
}
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator != (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{ return !(x == y); }
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator < (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{
|
||||
static_assert( std::is_same< Tag1, Tag2 >::value && b1 == b2,
|
||||
"TaggedIdentifiers with different tags or sensitivity are not comparable" );
|
||||
// This test should be eliminated at compile time:
|
||||
if ( b1 )
|
||||
return x.GET(). Cmp ( y.GET() ) < 0;
|
||||
else
|
||||
return x.GET(). CmpNoCase ( y.GET() ) < 0;
|
||||
}
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator > (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{ return y < x; }
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator <= (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{ return !(y < x); }
|
||||
|
||||
template< typename Tag1, typename Tag2, bool b1, bool b2 >
|
||||
inline bool operator >= (
|
||||
const TaggedIdentifier< Tag1, b1 > &x, const TaggedIdentifier< Tag2, b2 > &y )
|
||||
{ return !(x < y); }
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<typename Tag, bool b > struct hash< TaggedIdentifier<Tag, b> >
|
||||
: hash< Identifier > {};
|
||||
}
|
||||
|
||||
/**************************************************************************//**
|
||||
|
||||
\brief type alias for identifying a Plugin supplied by a module, each module
|
||||
@ -95,8 +286,11 @@ using FileExtensions = wxArrayStringEx;
|
||||
using FilePath = wxString;
|
||||
using FilePaths = wxArrayStringEx;
|
||||
|
||||
using CommandID = wxString;
|
||||
using CommandIDs = std::vector< CommandID >;
|
||||
// Identifies a menu command or macro.
|
||||
// Case-insensitive comparison
|
||||
struct CommandIdTag;
|
||||
using CommandID = TaggedIdentifier< CommandIdTag, false >;
|
||||
using CommandIDs = std::vector<CommandID>;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// A native 64-bit integer...used when referring to any number of samples
|
||||
|
@ -154,7 +154,11 @@ void MacroCommandDialog::OnChoice(wxCommandEvent & WXUNUSED(event))
|
||||
|
||||
void MacroCommandDialog::OnOk(wxCommandEvent & WXUNUSED(event))
|
||||
{
|
||||
mSelectedCommand = mInternalCommandName.Strip(wxString::both);
|
||||
mSelectedCommand = mInternalCommandName
|
||||
// .Strip(wxString::both) // PRL: used to do this, here only,
|
||||
// but ultimately mSelectedCommand is looked up in the catalog without
|
||||
// similar adjustment of whitespace in the comparison
|
||||
;
|
||||
mSelectedParameters = mParameters->GetValue().Strip(wxString::trailing);
|
||||
EndModal(true);
|
||||
}
|
||||
@ -195,9 +199,12 @@ void MacroCommandDialog::OnItemSelected(wxListEvent &event)
|
||||
params = em.GetDefaultPreset(ID);
|
||||
}
|
||||
|
||||
// using GET to expose a CommandID to the user!
|
||||
// Cryptic command and category.
|
||||
// Later we can put help information there, perhaps.
|
||||
mDetails->SetValue( mInternalCommandName + "\r\n" + command.category );
|
||||
// Macro command details are one place that we do expose Identifier
|
||||
// to (more sophisticated) users
|
||||
mDetails->SetValue( mInternalCommandName.GET() + "\r\n" + command.category );
|
||||
mParameters->SetValue(params);
|
||||
}
|
||||
|
||||
@ -231,12 +238,15 @@ void MacroCommandDialog::SetCommandAndParams(const CommandID &Command, const wxS
|
||||
|
||||
mInternalCommandName = Command;
|
||||
if (iter == mCatalog.end())
|
||||
// Expose an internal name to the user in default of any friendly name
|
||||
// -- AVOID THIS!
|
||||
mCommand->SetValue( Command );
|
||||
// uh oh, using GET to expose an internal name to the user!
|
||||
// in default of any better friendly name
|
||||
mCommand->SetValue( Command.GET() );
|
||||
else {
|
||||
mCommand->SetValue( iter->name.Translated() );
|
||||
mDetails->SetValue( iter->name.Internal() + "\r\n" + iter->category );
|
||||
// using GET to expose a CommandID to the user!
|
||||
// Macro command details are one place that we do expose Identifier
|
||||
// to (more sophisticated) users
|
||||
mDetails->SetValue( iter->name.Internal().GET() + "\r\n" + iter->category );
|
||||
mChoices->SetItemState(iter - mCatalog.begin(),
|
||||
wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
||||
|
||||
|
@ -235,7 +235,8 @@ bool MacroCommands::WriteMacro(const wxString & macro)
|
||||
// Copy over the commands
|
||||
int lines = mCommandMacro.size();
|
||||
for (int i = 0; i < lines; i++) {
|
||||
tf.AddLine(mCommandMacro[i] + wxT(":") + mParamsMacro[ i ]);
|
||||
// using GET to serialize macro definition to a text file
|
||||
tf.AddLine(mCommandMacro[i].GET() + wxT(":") + mParamsMacro[ i ]);
|
||||
}
|
||||
|
||||
// Write the macro
|
||||
@ -346,11 +347,17 @@ MacroCommandsCatalog::MacroCommandsCatalog( const AudacityProject *project )
|
||||
wxString squashed = label;
|
||||
squashed.Replace( " ", "" );
|
||||
|
||||
suffix = squashed.length() < wxMin( 18, mNames[i].length());
|
||||
// uh oh, using GET for dubious comparison of (lengths of)
|
||||
// user-visible name and internal CommandID!
|
||||
// and doing this only for English locale!
|
||||
suffix = squashed.length() < wxMin( 18, mNames[i].GET().length());
|
||||
}
|
||||
|
||||
if( suffix )
|
||||
label = label + " (" + mNames[i] + ")";
|
||||
// uh oh, using GET to expose CommandID to the user, as a
|
||||
// disambiguating suffix on a name, but this is only ever done if
|
||||
// the locale is English!
|
||||
label = label + " (" + mNames[i].GET() + ")";
|
||||
|
||||
commands.push_back(
|
||||
{
|
||||
@ -759,11 +766,10 @@ bool MacroCommands::ApplyCommand( const wxString &friendlyCommand,
|
||||
CommandContext const * pContext)
|
||||
{
|
||||
|
||||
unsigned int i;
|
||||
// Test for a special command.
|
||||
// CLEANSPEECH remnant
|
||||
for( i = 0; i < sizeof(SpecialCommands)/sizeof(*SpecialCommands); ++i ) {
|
||||
if( command.IsSameAs( SpecialCommands[i].second, false) )
|
||||
for( size_t i = 0; i < WXSIZEOF( SpecialCommands ); ++i ) {
|
||||
if( command == SpecialCommands[i].second )
|
||||
return ApplySpecialCommand( i, friendlyCommand, command, params );
|
||||
}
|
||||
// end CLEANSPEECH remnant
|
||||
@ -860,7 +866,10 @@ bool MacroCommands::ApplyMacro(
|
||||
const auto &command = mCommandMacro[i];
|
||||
auto iter = catalog.ByCommandId(command);
|
||||
auto friendly = (iter == catalog.end())
|
||||
? command // Expose internal name to user, in default of a better one!
|
||||
?
|
||||
// uh oh, using GET to expose an internal name to the user!
|
||||
// in default of any better friendly name
|
||||
command.GET()
|
||||
: iter->name.Translated();
|
||||
if (!ApplyCommandInBatchMode(friendly, command, mParamsMacro[i]) || mAbort)
|
||||
break;
|
||||
|
@ -739,9 +739,9 @@ void MacrosWindow::AddItem(const CommandID &Action, const wxString &Params)
|
||||
auto friendlyName = entry != mCatalog.end()
|
||||
? entry->name.Translated()
|
||||
:
|
||||
// Expose an internal name to the user in default of any friendly name
|
||||
// -- AVOID THIS!
|
||||
Action;
|
||||
// uh oh, using GET to expose an internal name to the user!
|
||||
// in default of any better friendly name
|
||||
Action.GET();
|
||||
|
||||
int i = mList->GetItemCount();
|
||||
|
||||
|
@ -310,3 +310,25 @@ wxArrayStringEx LocalizedStrings(
|
||||
std::mem_fn( &EnumValueSymbol::Translation )
|
||||
);
|
||||
}
|
||||
|
||||
// Find a better place for this?
|
||||
#include "audacity/Types.h"
|
||||
Identifier::Identifier(
|
||||
std::initializer_list<Identifier> components, wxChar separator )
|
||||
{
|
||||
if( components.size() < 2 )
|
||||
{
|
||||
wxASSERT( false );
|
||||
return;
|
||||
}
|
||||
auto iter = components.begin(), end = components.end();
|
||||
value = (*iter++).value;
|
||||
while (iter != end)
|
||||
value += separator + (*iter++).value;
|
||||
}
|
||||
|
||||
std::vector< Identifier > Identifier::split( wxChar separator ) const
|
||||
{
|
||||
auto strings = ::wxSplit( value, separator );
|
||||
return { strings.begin(), strings.end() };
|
||||
}
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
class wxArrayString;
|
||||
class wxArrayStringEx;
|
||||
class wxString;
|
||||
|
||||
extern AUDACITY_DLL_API const wxString& GetCustomTranslation(const wxString& str1 );
|
||||
extern AUDACITY_DLL_API const wxString& GetCustomSubstitution(const wxString& str1 );
|
||||
|
@ -535,7 +535,8 @@ const std::vector<NormalizedKeyString> &CommandManager::ExcludedList()
|
||||
};
|
||||
|
||||
std::vector<NormalizedKeyString> result(
|
||||
strings, strings + sizeof(strings)/sizeof(*strings) );
|
||||
std::begin(strings), std::end(strings)
|
||||
);
|
||||
std::sort( result.begin(), result.end() );
|
||||
return result;
|
||||
}();
|
||||
@ -576,7 +577,7 @@ void CommandManager::PurgeData()
|
||||
|
||||
mCommandNameHash.clear();
|
||||
mCommandKeyHash.clear();
|
||||
mCommandIDHash.clear();
|
||||
mCommandNumericIDHash.clear();
|
||||
|
||||
mCurrentMenuName = COMMAND;
|
||||
mCurrentID = 17000;
|
||||
@ -995,7 +996,7 @@ CommandListEntry *CommandManager::NewIdentifier(const CommandID & nameIn,
|
||||
// the name in prefs is the category name plus the effect name.
|
||||
// This feature is not used for built-in effects.
|
||||
if (multi)
|
||||
name = wxString::Format(wxT("%s_%s"), name, nameSuffix);
|
||||
name = CommandID{ { name, nameSuffix }, wxT('_') };
|
||||
|
||||
// wxMac 2.5 and higher will do special things with the
|
||||
// Preferences, Exit (Quit), and About menu items,
|
||||
@ -1051,9 +1052,11 @@ CommandListEntry *CommandManager::NewIdentifier(const CommandID & nameIn,
|
||||
|
||||
// Key from preferences overrides the default key given
|
||||
gPrefs->SetPath(wxT("/NewKeys"));
|
||||
if (gPrefs->HasEntry(entry->name)) {
|
||||
// using GET to interpret CommandID as a config path component
|
||||
const auto &path = entry->name.GET();
|
||||
if (gPrefs->HasEntry(path)) {
|
||||
entry->key =
|
||||
NormalizedKeyString{ gPrefs->Read(entry->name, entry->key.Raw()) };
|
||||
NormalizedKeyString{ gPrefs->ReadObject(path, entry->key) };
|
||||
}
|
||||
gPrefs->SetPath(wxT("/"));
|
||||
|
||||
@ -1063,7 +1066,7 @@ CommandListEntry *CommandManager::NewIdentifier(const CommandID & nameIn,
|
||||
|
||||
// New variable
|
||||
CommandListEntry *entry = &*mCommandList.back();
|
||||
mCommandIDHash[entry->id] = entry;
|
||||
mCommandNumericIDHash[entry->id] = entry;
|
||||
|
||||
#if defined(__WXDEBUG__)
|
||||
prev = mCommandNameHash[entry->name];
|
||||
@ -1074,11 +1077,14 @@ CommandListEntry *CommandManager::NewIdentifier(const CommandID & nameIn,
|
||||
if( prev->label != entry->label )
|
||||
{
|
||||
wxLogDebug(wxT("Command '%s' defined by '%s' and '%s'"),
|
||||
entry->name,
|
||||
// using GET in a log message for devs' eyes only
|
||||
entry->name.GET(),
|
||||
prev->label.BeforeFirst(wxT('\t')),
|
||||
entry->label.BeforeFirst(wxT('\t')));
|
||||
wxFAIL_MSG(wxString::Format(wxT("Command '%s' defined by '%s' and '%s'"),
|
||||
entry->name,
|
||||
// using GET in an assertion violation message for devs'
|
||||
// eyes only
|
||||
entry->name.GET(),
|
||||
prev->label.BeforeFirst(wxT('\t')),
|
||||
entry->label.BeforeFirst(wxT('\t'))));
|
||||
}
|
||||
@ -1098,7 +1104,8 @@ wxString CommandManager::GetLabel(const CommandListEntry *entry) const
|
||||
wxString label = entry->label;
|
||||
if (!entry->key.empty())
|
||||
{
|
||||
label += wxT("\t") + entry->key.Raw();
|
||||
// using GET to compose menu item name for wxWidgets
|
||||
label += wxT("\t") + entry->key.GET();
|
||||
}
|
||||
|
||||
return label;
|
||||
@ -1120,7 +1127,8 @@ wxString CommandManager::GetLabelWithDisabledAccel(const CommandListEntry *entry
|
||||
// Dummy accelerator that looks Ok in menus but is non functional.
|
||||
// Note the space before the key.
|
||||
#ifdef __WXMSW__
|
||||
auto key = entry->key.Raw();
|
||||
// using GET to compose menu item name for wxWidgets
|
||||
auto key = entry->key.GET();
|
||||
Accel = wxString("\t ") + key;
|
||||
if( key.StartsWith("Left" )) break;
|
||||
if( key.StartsWith("Right")) break;
|
||||
@ -1149,7 +1157,8 @@ wxString CommandManager::GetLabelWithDisabledAccel(const CommandListEntry *entry
|
||||
#endif
|
||||
//wxLogDebug("Added Accel:[%s][%s]", entry->label, entry->key );
|
||||
// Normal accelerator.
|
||||
Accel = wxString("\t") + entry->key.Raw();
|
||||
// using GET to compose menu item name for wxWidgets
|
||||
Accel = wxString("\t") + entry->key.GET();
|
||||
}
|
||||
} while (false );
|
||||
label += Accel;
|
||||
@ -1188,15 +1197,16 @@ void CommandManager::Enable(CommandListEntry *entry, bool enabled)
|
||||
|
||||
// This menu item is not necessarily in the same menu, because
|
||||
// multi-items can be spread across multiple sub menus
|
||||
CommandListEntry *multiEntry = mCommandIDHash[ID];
|
||||
CommandListEntry *multiEntry = mCommandNumericIDHash[ID];
|
||||
if (multiEntry) {
|
||||
wxMenuItem *item = multiEntry->menu->FindItem(ID);
|
||||
|
||||
if (item) {
|
||||
item->Enable(enabled);
|
||||
} else {
|
||||
// using GET in a log message for devs' eyes only
|
||||
wxLogDebug(wxT("Warning: Menu entry with id %i in %s not found"),
|
||||
ID, (const wxChar*)entry->name);
|
||||
ID, entry->name.GET());
|
||||
}
|
||||
} else {
|
||||
wxLogDebug(wxT("Warning: Menu entry with id %i not in hash"), ID);
|
||||
@ -1238,8 +1248,9 @@ bool CommandManager::GetEnabled(const CommandID &name)
|
||||
{
|
||||
CommandListEntry *entry = mCommandNameHash[name];
|
||||
if (!entry || !entry->menu) {
|
||||
// using GET in a log message for devs' eyes only
|
||||
wxLogDebug(wxT("Warning: command doesn't exist: '%s'"),
|
||||
(const wxChar*)name);
|
||||
name.GET());
|
||||
return false;
|
||||
}
|
||||
return entry->enabled;
|
||||
@ -1550,7 +1561,7 @@ bool CommandManager::HandleCommandEntry(const CommandListEntry * entry,
|
||||
#include "../prefs/KeyConfigPrefs.h"
|
||||
bool CommandManager::HandleMenuID(int id, CommandFlag flags, CommandMask mask)
|
||||
{
|
||||
CommandListEntry *entry = mCommandIDHash[id];
|
||||
CommandListEntry *entry = mCommandNumericIDHash[id];
|
||||
|
||||
#ifdef EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
|
||||
if (::wxGetMouseState().ShiftDown()) {
|
||||
@ -1580,7 +1591,11 @@ bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandCo
|
||||
if (!entry->multi)
|
||||
{
|
||||
// Testing against labelPrefix too allows us to call Nyquist functions by name.
|
||||
if( Str.IsSameAs( entry->name, false ) || Str.IsSameAs( entry->labelPrefix, false ))
|
||||
if( Str == entry->name ||
|
||||
// PRL: uh oh, mixing internal string (Str) with user-visible
|
||||
// (labelPrefix, which was initialized from a user-visible
|
||||
// sub-menu name)
|
||||
Str == entry->labelPrefix )
|
||||
{
|
||||
return HandleCommandEntry( entry.get(), flags, mask);
|
||||
}
|
||||
@ -1588,7 +1603,7 @@ bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandCo
|
||||
else
|
||||
{
|
||||
// Handle multis too...
|
||||
if( Str.IsSameAs( entry->name, false ) )
|
||||
if( Str == entry->name )
|
||||
{
|
||||
return HandleCommandEntry( entry.get(), flags, mask);
|
||||
}
|
||||
@ -1608,7 +1623,7 @@ bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandCo
|
||||
const PluginDescriptor *plug = pm.GetFirstPlugin(PluginTypeEffect);
|
||||
while (plug)
|
||||
{
|
||||
if (em.GetCommandIdentifier(plug->GetID()).IsSameAs(Str, false))
|
||||
if (em.GetCommandIdentifier(plug->GetID()) == Str)
|
||||
{
|
||||
return PluginActions::DoEffect(
|
||||
plug->GetID(), context,
|
||||
@ -1724,9 +1739,9 @@ void CommandManager::GetAllCommandData(
|
||||
}
|
||||
}
|
||||
|
||||
CommandID CommandManager::GetNameFromID(int id)
|
||||
CommandID CommandManager::GetNameFromNumericID(int id)
|
||||
{
|
||||
CommandListEntry *entry = mCommandIDHash[id];
|
||||
CommandListEntry *entry = mCommandNumericIDHash[id];
|
||||
if (!entry)
|
||||
return {};
|
||||
return entry->name;
|
||||
@ -1847,7 +1862,7 @@ void CommandManager::WriteXML(XMLWriter &xmlFile) const
|
||||
xmlFile.StartTag(wxT("command"));
|
||||
xmlFile.WriteAttr(wxT("name"), entry->name);
|
||||
xmlFile.WriteAttr(wxT("label"), label);
|
||||
xmlFile.WriteAttr(wxT("key"), entry->key.Raw());
|
||||
xmlFile.WriteAttr(wxT("key"), entry->key);
|
||||
xmlFile.EndTag(wxT("command"));
|
||||
}
|
||||
|
||||
@ -1905,7 +1920,8 @@ void CommandManager::CheckDups()
|
||||
if (mCommandList[i]->key == mCommandList[j]->key) {
|
||||
wxString msg;
|
||||
msg.Printf(wxT("key combo '%s' assigned to '%s' and '%s'"),
|
||||
mCommandList[i]->key.Raw(),
|
||||
// using GET to form debug message
|
||||
mCommandList[i]->key.GET(),
|
||||
mCommandList[i]->label.BeforeFirst(wxT('\t')),
|
||||
mCommandList[j]->label.BeforeFirst(wxT('\t')));
|
||||
wxASSERT_MSG(mCommandList[i]->key != mCommandList[j]->key, msg);
|
||||
|
@ -93,21 +93,9 @@ using SubMenuList = std::vector < std::unique_ptr<SubMenuListEntry> >;
|
||||
// so we don't want the structures to relocate with vector operations.
|
||||
using CommandList = std::vector<std::unique_ptr<CommandListEntry>>;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash< NormalizedKeyString > {
|
||||
size_t operator () (const NormalizedKeyString &str) const // noexcept
|
||||
{
|
||||
auto &stdstr = str.Raw(); // no allocations, a cheap fetch
|
||||
using Hasher = std::hash< wxString >;
|
||||
return Hasher{}( stdstr );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
using CommandKeyHash = std::unordered_map<NormalizedKeyString, CommandListEntry*>;
|
||||
using CommandNameHash = std::unordered_map<wxString, CommandListEntry*>;
|
||||
using CommandIDHash = std::unordered_map<int, CommandListEntry*>;
|
||||
using CommandNameHash = std::unordered_map<CommandID, CommandListEntry*>;
|
||||
using CommandNumericIDHash = std::unordered_map<int, CommandListEntry*>;
|
||||
|
||||
class AudacityProject;
|
||||
class CommandContext;
|
||||
@ -265,7 +253,9 @@ class AUDACITY_DLL_API CommandManager final : public XMLTagHandler
|
||||
#endif
|
||||
bool includeMultis);
|
||||
|
||||
CommandID GetNameFromID( int id );
|
||||
// Each command is assigned a numerical ID for use in wxMenu and wxEvent,
|
||||
// which need not be the same across platforms or sessions
|
||||
CommandID GetNameFromNumericID( int id );
|
||||
|
||||
wxString GetLabelFromName(const CommandID &name);
|
||||
wxString GetPrefixedLabelFromName(const CommandID &name);
|
||||
@ -384,7 +374,7 @@ private:
|
||||
CommandList mCommandList;
|
||||
CommandNameHash mCommandNameHash;
|
||||
CommandKeyHash mCommandKeyHash;
|
||||
CommandIDHash mCommandIDHash;
|
||||
CommandNumericIDHash mCommandNumericIDHash;
|
||||
int mCurrentID;
|
||||
int mXMLKeysRead;
|
||||
|
||||
|
@ -653,7 +653,7 @@ void GetInfoCommand::ExploreMenu( const CommandContext &context, wxMenu * pMenu,
|
||||
for (size_t lndx = 0; lndx < lcnt; lndx++) {
|
||||
item = list.Item(lndx)->GetData();
|
||||
Label = item->GetItemLabelText();
|
||||
Name = pMan->GetNameFromID( item->GetId() );
|
||||
Name = pMan->GetNameFromNumericID( item->GetId() );
|
||||
Accel = item->GetItemLabel();
|
||||
if( Accel.Contains("\t") )
|
||||
Accel = Accel.AfterLast('\t');
|
||||
@ -673,7 +673,9 @@ void GetInfoCommand::ExploreMenu( const CommandContext &context, wxMenu * pMenu,
|
||||
context.AddItem( Label, "label" );
|
||||
context.AddItem( Accel, "accel" );
|
||||
if( !Name.empty() )
|
||||
context.AddItem( Name, "id" );// It is called Scripting ID outside Audacity.
|
||||
// using GET to expose CommandID in results of GetInfoCommand...
|
||||
// PRL asks, is that all right?
|
||||
context.AddItem( Name.GET(), "id" );// It is called Scripting ID outside Audacity.
|
||||
context.EndStruct();
|
||||
|
||||
if (item->IsSubMenu()) {
|
||||
|
@ -14,7 +14,8 @@
|
||||
|
||||
#include <wx/event.h>
|
||||
|
||||
NormalizedKeyString::NormalizedKeyString(const wxString & key)
|
||||
NormalizedKeyString::NormalizedKeyString( const wxString & key )
|
||||
: NormalizedKeyStringBase( key )
|
||||
{
|
||||
#if defined(__WXMAC__)
|
||||
wxString newkey;
|
||||
@ -45,16 +46,19 @@ NormalizedKeyString::NormalizedKeyString(const wxString & key)
|
||||
newkey += wxT("Ctrl+");
|
||||
}
|
||||
|
||||
(wxString&)*this = newkey + temp.AfterLast(wxT('+'));
|
||||
(NormalizedKeyStringBase&)*this =
|
||||
newkey + temp.AfterLast(wxT('+'));
|
||||
#else
|
||||
(wxString&)*this = key;
|
||||
(NormalizedKeyStringBase&)*this = key;
|
||||
#endif
|
||||
}
|
||||
|
||||
wxString NormalizedKeyString::Display(bool usesSpecialChars) const
|
||||
{
|
||||
(void)usesSpecialChars;//compiler food
|
||||
wxString newkey = *this;
|
||||
// using GET to manipulate key string as needed for macOS differences
|
||||
// in displaying of it
|
||||
auto newkey = this->GET();
|
||||
#if defined(__WXMAC__)
|
||||
|
||||
if (!usesSpecialChars) {
|
||||
|
@ -12,38 +12,28 @@
|
||||
#ifndef __AUDACITY_KEYBOARD__
|
||||
#define __AUDACITY_KEYBOARD__
|
||||
|
||||
#include <audacity/Types.h>
|
||||
#include <wx/defs.h>
|
||||
#include <wx/string.h> // to inherit
|
||||
|
||||
class wxKeyEvent;
|
||||
|
||||
struct NormalizedKeyString : private wxString
|
||||
struct NormalizedKeyStringTag;
|
||||
// Case insensitive comparisons
|
||||
using NormalizedKeyStringBase = TaggedIdentifier<NormalizedKeyStringTag, false>;
|
||||
|
||||
struct NormalizedKeyString : NormalizedKeyStringBase
|
||||
{
|
||||
NormalizedKeyString() = default;
|
||||
|
||||
explicit NormalizedKeyString( const wxString &str );
|
||||
explicit NormalizedKeyString( const wxString &key );
|
||||
|
||||
wxString Display(bool usesSpecialChars = false) const;
|
||||
|
||||
const wxString &Raw() const { return *this; }
|
||||
|
||||
bool NoCaseEqual( const NormalizedKeyString &other ) const
|
||||
{ return 0 == this->Raw() .CmpNoCase( other.Raw() ); }
|
||||
|
||||
using wxString::empty;
|
||||
};
|
||||
|
||||
inline bool operator ==
|
||||
( const NormalizedKeyString &a, const NormalizedKeyString &b)
|
||||
{ return a.Raw () == b.Raw(); }
|
||||
|
||||
inline bool operator !=
|
||||
( const NormalizedKeyString &a, const NormalizedKeyString &b)
|
||||
{ return a.Raw () != b.Raw(); }
|
||||
|
||||
inline bool operator <
|
||||
( const NormalizedKeyString &a, const NormalizedKeyString &b)
|
||||
{ return a.Raw () < b.Raw(); }
|
||||
namespace std
|
||||
{
|
||||
template<> struct hash< NormalizedKeyString >
|
||||
: hash< NormalizedKeyStringBase > {};
|
||||
}
|
||||
|
||||
NormalizedKeyString KeyEventToKeyString(const wxKeyEvent & keyEvent);
|
||||
|
||||
|
@ -441,7 +441,8 @@ void ScreenshotCommand::CapturePreferences(
|
||||
const CommandContext projectContext( *pProject );
|
||||
if( !pMan->HandleTextualCommand( Command, projectContext, AlwaysEnabledFlag, AlwaysEnabledFlag ) )
|
||||
{
|
||||
wxLogDebug("Command %s not found", Command );
|
||||
// using GET in a log message for devs' eyes only
|
||||
wxLogDebug("Command %s not found", Command.GET() );
|
||||
}
|
||||
// This sleep is not needed, but gives user a chance to see the
|
||||
// dialogs as they whizz by.
|
||||
|
@ -250,7 +250,10 @@ void EffectManager::GetCommandDefinition(const PluginID & ID, const CommandConte
|
||||
// This is capturing the output context into the shuttle.
|
||||
ShuttleGetDefinition S( *context.pOutput.get()->mStatusTarget.get() );
|
||||
S.StartStruct();
|
||||
S.AddItem( GetCommandIdentifier( ID ), "id" );
|
||||
// using GET to expose a CommandID to the user!
|
||||
// Macro command details are one place that we do expose Identifier
|
||||
// to (more sophisticated) users
|
||||
S.AddItem( GetCommandIdentifier( ID ).GET(), "id" );
|
||||
S.AddItem( GetCommandName( ID ), "name" );
|
||||
if( bHasParams ){
|
||||
S.StartField( "params" );
|
||||
@ -260,6 +263,7 @@ void EffectManager::GetCommandDefinition(const PluginID & ID, const CommandConte
|
||||
S.EndField();
|
||||
}
|
||||
S.AddItem( GetCommandUrl( ID ), "url" );
|
||||
// The tip is a translated string!
|
||||
S.AddItem( GetCommandTip( ID ), "tip" );
|
||||
S.EndStruct();
|
||||
}
|
||||
@ -960,7 +964,7 @@ const PluginID & EffectManager::GetEffectByIdentifier(const CommandID & strTarge
|
||||
const PluginDescriptor *plug = pm.GetFirstPlugin(PluginTypeEffect | PluginTypeAudacityCommand);
|
||||
while (plug)
|
||||
{
|
||||
if (GetCommandIdentifier(plug->GetID()).IsSameAs(strTarget, false))
|
||||
if (GetCommandIdentifier(plug->GetID()) == strTarget)
|
||||
{
|
||||
return plug->GetID();
|
||||
}
|
||||
|
@ -559,7 +559,8 @@ void OnManageGenerators(const CommandContext &context)
|
||||
|
||||
void OnEffect(const CommandContext &context)
|
||||
{
|
||||
DoEffect(context.parameter, context, 0);
|
||||
// using GET to interpret parameter as a PluginID
|
||||
DoEffect(context.parameter.GET(), context, 0);
|
||||
}
|
||||
|
||||
void OnManageEffects(const CommandContext &context)
|
||||
@ -680,7 +681,8 @@ void OnApplyMacroDirectly(const CommandContext &context )
|
||||
|
||||
void OnAudacityCommand(const CommandContext & ctx)
|
||||
{
|
||||
wxLogDebug( "Command was: %s", ctx.parameter);
|
||||
// using GET in a log message for devs' eyes only
|
||||
wxLogDebug( "Command was: %s", ctx.parameter.GET());
|
||||
// Not configured, so prompt user.
|
||||
DoAudacityCommand(EffectManager::Get().GetEffectByIdentifier(ctx.parameter),
|
||||
ctx, kNone);
|
||||
|
@ -657,12 +657,13 @@ bool KeyConfigPrefs::Commit()
|
||||
bool bFull = gPrefs->ReadBool(wxT("/GUI/Shortcuts/FullDefaults"), false);
|
||||
for (size_t i = 0; i < mNames.size(); i++) {
|
||||
const auto &dkey = bFull ? mDefaultKeys[i] : mStandardDefaultKeys[i];
|
||||
wxString name = wxT("/NewKeys/") + mNames[i];
|
||||
// using GET to interpret CommandID as a config path component
|
||||
auto name = wxT("/NewKeys/") + mNames[i].GET();
|
||||
const auto &key = mNewKeys[i];
|
||||
|
||||
if (gPrefs->HasEntry(name)) {
|
||||
if (key != NormalizedKeyString{ gPrefs->Read(name, key.Raw()) } ) {
|
||||
gPrefs->Write(name, key.Raw());
|
||||
if (key != NormalizedKeyString{ gPrefs->ReadObject(name, key) } ) {
|
||||
gPrefs->Write(name, key);
|
||||
}
|
||||
if (key == dkey) {
|
||||
gPrefs->DeleteEntry(name);
|
||||
@ -670,7 +671,7 @@ bool KeyConfigPrefs::Commit()
|
||||
}
|
||||
else {
|
||||
if (key != dkey) {
|
||||
gPrefs->Write(name, key.Raw());
|
||||
gPrefs->Write(name, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,25 +142,29 @@ BEGIN_POPUP_MENU(TrackMenuTable)
|
||||
OnMoveUpID,
|
||||
_("Move Track &Up") + wxT("\t") +
|
||||
(GetActiveProject()->GetCommandManager()->
|
||||
GetKeyFromName(wxT("TrackMoveUp")).Raw()),
|
||||
// using GET to compose menu item name for wxWidgets
|
||||
GetKeyFromName(wxT("TrackMoveUp")).GET()),
|
||||
OnMoveTrack)
|
||||
POPUP_MENU_ITEM(
|
||||
OnMoveDownID,
|
||||
_("Move Track &Down") + wxT("\t") +
|
||||
(GetActiveProject()->GetCommandManager()->
|
||||
GetKeyFromName(wxT("TrackMoveDown")).Raw()),
|
||||
// using GET to compose menu item name for wxWidgets
|
||||
GetKeyFromName(wxT("TrackMoveDown")).GET()),
|
||||
OnMoveTrack)
|
||||
POPUP_MENU_ITEM(
|
||||
OnMoveTopID,
|
||||
_("Move Track to &Top") + wxT("\t") +
|
||||
(GetActiveProject()->GetCommandManager()->
|
||||
GetKeyFromName(wxT("TrackMoveTop")).Raw()),
|
||||
// using GET to compose menu item name for wxWidgets
|
||||
GetKeyFromName(wxT("TrackMoveTop")).GET()),
|
||||
OnMoveTrack)
|
||||
POPUP_MENU_ITEM(
|
||||
OnMoveBottomID,
|
||||
_("Move Track to &Bottom") + wxT("\t") +
|
||||
(GetActiveProject()->GetCommandManager()->
|
||||
GetKeyFromName(wxT("TrackMoveBottom")).Raw()),
|
||||
// using GET to compose menu item name for wxWidgets
|
||||
GetKeyFromName(wxT("TrackMoveBottom")).GET()),
|
||||
OnMoveTrack)
|
||||
END_POPUP_MENU()
|
||||
|
||||
|
@ -252,7 +252,7 @@ KeyView::GetIndexByName(const CommandID & name) const
|
||||
// Search the nodes for the key
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
if (name.CmpNoCase(mNodes[i].name) == 0)
|
||||
if (name == mNodes[i].name)
|
||||
{
|
||||
return mNodes[i].index;
|
||||
}
|
||||
@ -288,7 +288,7 @@ KeyView::GetNameByKey(const NormalizedKeyString & key) const
|
||||
// Search the nodes for the key
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
if (key.NoCaseEqual( mNodes[i].key))
|
||||
if ( key == mNodes[i].key )
|
||||
{
|
||||
return mNodes[i].name;
|
||||
}
|
||||
@ -308,7 +308,7 @@ KeyView::GetIndexByKey(const NormalizedKeyString & key) const
|
||||
// Search the nodes for the key
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
if (key.NoCaseEqual( mNodes[i].key))
|
||||
if ( key == mNodes[i].key )
|
||||
{
|
||||
return mNodes[i].index;
|
||||
}
|
||||
|
@ -30,6 +30,12 @@ class AUDACITY_DLL_API XMLWriter /* not final */ {
|
||||
virtual void StartTag(const wxString &name);
|
||||
virtual void EndTag(const wxString &name);
|
||||
|
||||
// nonvirtual pass-through
|
||||
void WriteAttr(const wxString &name, const Identifier &value)
|
||||
// using GET once here, permitting Identifiers in XML,
|
||||
// so no need for it at each WriteAttr call
|
||||
{ WriteAttr( name, value.GET() ); }
|
||||
|
||||
virtual void WriteAttr(const wxString &name, const wxString &value);
|
||||
virtual void WriteAttr(const wxString &name, const wxChar *value);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user