diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp index 36d45b676..6cb89ab18 100644 --- a/src/effects/nyquist/Nyquist.cpp +++ b/src/effects/nyquist/Nyquist.cpp @@ -329,7 +329,7 @@ bool NyquistEffect::DefineParams( ShuttleParams & S ) } else if (ctrl.type == NYQ_CTRL_CHOICE) { - wxArrayString choices = ParseChoice(ctrl); + const wxArrayString &choices = ctrl.choices; int x=d; //parms.WriteEnum(ctrl.var, (int) d, choices); S.DefineEnum( x, static_cast( ctrl.var.c_str() ), 0, choices ); @@ -378,7 +378,7 @@ bool NyquistEffect::GetAutomationParameters(CommandParameters & parms) } else if (ctrl.type == NYQ_CTRL_CHOICE) { - wxArrayString choices = ParseChoice(ctrl); + const wxArrayString &choices = ctrl.choices; parms.WriteEnum(ctrl.var, (int) d, choices); } else if (ctrl.type == NYQ_CTRL_STRING) @@ -428,7 +428,7 @@ bool NyquistEffect::SetAutomationParameters(CommandParameters & parms) else if (ctrl.type == NYQ_CTRL_CHOICE) { int val; - wxArrayString choices = ParseChoice(ctrl); + const wxArrayString &choices = ctrl.choices; good = parms.ReadEnum(ctrl.var, &val, choices) && val != wxNOT_FOUND; } @@ -468,7 +468,7 @@ bool NyquistEffect::SetAutomationParameters(CommandParameters & parms) else if (ctrl.type == NYQ_CTRL_CHOICE) { int val {0}; - wxArrayString choices = ParseChoice(ctrl); + const wxArrayString &choices = ctrl.choices; parms.ReadEnum(ctrl.var, &val, choices); ctrl.val = (double) val; } @@ -1469,13 +1469,25 @@ wxString NyquistEffect::EscapeString(const wxString & inStr) return str; } -wxArrayString NyquistEffect::ParseChoice(const NyqControl & ctrl) +wxArrayString NyquistEffect::ParseChoice(const wxString & text) { - wxArrayString choices = wxStringTokenize(ctrl.label, wxT(",")); - - for (size_t i = 0, cnt = choices.GetCount();i < cnt; i++) - { - choices[i] = choices[i].Trim(true).Trim(false); + wxArrayString choices; + if (text[0] == wxT('(')) { + // New style: expecting a Lisp-like list of strings + Tokenize(text, choices, 1, 1); + for (auto &choice : choices) + choice = UnQuote(choice); + } + else { + // Old style: expecting a comma-separated list of + // un-internationalized names, ignoring leading and trailing spaces + // on each; and the whole may be quoted + choices = wxStringTokenize( + text[0] == wxT('"') ? text.Mid(1, text.Length() - 2) : text, + wxT(",") + ); + for (auto &choice : choices) + choice = choice.Trim(true).Trim(false); } return choices; @@ -1508,16 +1520,28 @@ void NyquistEffect::Stop() mStop = true; } -wxString NyquistEffect::UnQuote(const wxString &s) +wxString NyquistEffect::UnQuote( + const wxString &s, bool allowParens, bool translate) { - wxString out; int len = s.Length(); - if (len >= 2 && s[0] == wxT('\"') && s[len - 1] == wxT('\"')) { - return s.Mid(1, len - 2); + auto unquoted = s.Mid(1, len - 2); + return translate ? GetCustomTranslation( unquoted ) : unquoted; } - - return s; + else if (allowParens && + len >= 2 && s[0] == wxT('(') && s[len - 1] == wxT(')')) { + wxArrayString tokens; + Tokenize(s, tokens, 1, 1); + if (tokens.size() > 1) + // Assume the first token was _ -- we don't check that + // And the second is the string, which is internationalized + return UnQuote( tokens[1], false, true ); + else + return {}; + } + else + // If string was not quoted, assume no translation exists + return s; } double NyquistEffect::GetCtrlValue(const wxString &s) @@ -1540,51 +1564,83 @@ double NyquistEffect::GetCtrlValue(const wxString &s) return Internat::CompatibleToDouble(s); } -void NyquistEffect::Parse(const wxString &line) +void NyquistEffect::Tokenize( + const wxString &line, wxArrayString &tokens, + size_t trimStart, size_t trimEnd) { - wxArrayString tokens; - - int i; - int len = line.Length(); bool sl = false; bool q = false; wxString tok = wxT(""); + int paren = 0; - for (i = 1; i < len; i++) { - wxChar c = line[i]; + auto endToken = [&]{ + if (!tok.empty()) { + tokens.push_back(tok); + tok = wxT(""); + } + }; - if (c == wxT('\\')) { + for (auto c : + make_iterator_range(line.begin() + trimStart, line.end() - trimEnd)) { + if (q && c == wxT('\\')) { + // begin escaped character, only within quotes sl = true; + continue; } - else if (c == wxT('"')) { - q = !q; + else if (sl && c == wxT('n')) + c = wxT('\n'); + + if (!sl && !paren && c == wxT('"')) { + if (!q) + // finish previous token; begin token, including the delimiter + endToken(), q = true, tok += c; + else + // end token, including the delimiter + tok += c, q = false, endToken(); } - else { - if ((!q && !sl && c == wxT(' ')) || c == wxT('\t')) { - tokens.Add(tok); - tok = wxT(""); - } - else if (sl && c == wxT('n')) { - tok += wxT('\n'); - } - else { + else if (!q && !paren && (c == wxT(' ') || c == wxT('\t'))) + // Separate tokens; don't accumulate this character + endToken(); + else if (!q && c == wxT('(')) { + if (++paren == 1) + // finish previous token; begin list, including the delimiter + endToken(), tok += c; + else + // defer tokenizing of nested list to a later pass over the token tok += c; - } - - sl = false; } + else if (!q && c == wxT(')')) { + if (--paren == 0) + // finish list, including the delimiter + tok += c, endToken(); + else if (paren < 0) + // forgive unbalanced right paren + paren = 0, endToken(); + else + // nested list; deferred tokenizing + tok += c; + } + else + tok += c; + + sl = false; } - if (tok != wxT("")) { - tokens.Add(tok); - } + // Finish -- this just forgives unbalanced open quotes or left parens + endToken(); +} - len = tokens.GetCount(); +void NyquistEffect::Parse(const wxString &line) +{ + wxArrayString tokens; + Tokenize(line, tokens, 1, 0); + + int len = tokens.size(); if (len < 1) { return; } - // Consistency decission is for "plug-in" as the correct spelling + // Consistency decision is for "plug-in" as the correct spelling // "plugin" (deprecated) is allowed as an undocumented convenience. if (len == 2 && tokens[0] == wxT("nyquist") && (tokens[1] == wxT("plug-in") || tokens[1] == wxT("plugin"))) { @@ -1665,9 +1721,7 @@ void NyquistEffect::Parse(const wxString &line) if (len >= 2 && tokens[0] == wxT("name")) { mName = UnQuote(tokens[1]); if (mName.EndsWith(wxT("..."))) - { mName = mName.RemoveLast(3); - } return; } @@ -1739,14 +1793,16 @@ void NyquistEffect::Parse(const wxString &line) // TODO: Document. // Page name in Audacity development manual if (len >= 2 && tokens[0] == wxT("manpage")) { - mManPage = UnQuote(tokens[1]); + // do not translate + mManPage = UnQuote(tokens[1], false, false); return; } // TODO: Document. // Local Help file if (len >= 2 && tokens[0] == wxT("helpfile")) { - mHelpFile = UnQuote(tokens[1]); + // do not translate + mHelpFile = UnQuote(tokens[1], false, false); return; } @@ -1763,18 +1819,31 @@ void NyquistEffect::Parse(const wxString &line) NyqControl ctrl; ctrl.var = tokens[1]; - ctrl.name = tokens[2]; + ctrl.name = UnQuote( tokens[2] ); + // 3 is type, below ctrl.label = tokens[4]; + + // valStr may or may not be a quoted string ctrl.valStr = tokens[5]; ctrl.val = GetCtrlValue(ctrl.valStr); + if (ctrl.valStr[0] == wxT('(') || ctrl.valStr[0] == wxT('"')) + ctrl.valStr = UnQuote( ctrl.valStr ); + + // 6 is minimum, below + // 7 is maximum, below if (tokens[3] == wxT("string")) { ctrl.type = NYQ_CTRL_STRING; + ctrl.label = UnQuote( ctrl.label ); } else if (tokens[3] == wxT("choice")) { ctrl.type = NYQ_CTRL_CHOICE; + ctrl.choices = ParseChoice(ctrl.label); + ctrl.label = wxT(""); } else { + ctrl.label = UnQuote( ctrl.label ); + if (len < 8) { return; } @@ -2124,7 +2193,7 @@ bool NyquistEffect::TransferDataToEffectWindow() if (ctrl.type == NYQ_CTRL_CHOICE) { - wxArrayString choices = ParseChoice(ctrl); + const wxArrayString &choices = ctrl.choices; int val = (int)ctrl.val; if (val < 0 || val >= (int)choices.GetCount()) @@ -2288,7 +2357,7 @@ void NyquistEffect::BuildEffectWindow(ShuttleGui & S) { S.AddSpace(10, 10); - wxArrayString choices = wxStringTokenize(ctrl.label, wxT(",")); + const wxArrayString &choices = ctrl.choices; S.Id(ID_Choice + i).AddChoice( {}, wxT(""), &choices); } else diff --git a/src/effects/nyquist/Nyquist.h b/src/effects/nyquist/Nyquist.h index f86bbab2e..71a419814 100644 --- a/src/effects/nyquist/Nyquist.h +++ b/src/effects/nyquist/Nyquist.h @@ -56,6 +56,7 @@ public: wxString var; wxString name; wxString label; + wxArrayString choices; wxString valStr; wxString lowStr; wxString highStr; @@ -141,7 +142,7 @@ private: static wxString NyquistToWxString(const char *nyqString); wxString EscapeString(const wxString & inStr); - wxArrayString ParseChoice(const NyqControl & ctrl); + static wxArrayString ParseChoice(const wxString & text); static int StaticGetCallback(float *buffer, int channel, long start, long len, long totlen, @@ -162,9 +163,13 @@ private: void ParseFile(); bool ParseCommand(const wxString & cmd); bool ParseProgram(wxInputStream & stream); + static void Tokenize( + const wxString &line, wxArrayString &tokens, + size_t trimStart, size_t trimEnd); void Parse(const wxString &line); - wxString UnQuote(const wxString &s); + static wxString UnQuote(const wxString &s, + bool allowParens = true, bool translate = true); double GetCtrlValue(const wxString &s); void OnLoad(wxCommandEvent & evt);