Token (const String& t, const Font& f, const Colour& c, const bool isWhitespace_) : text (t), font (f), colour (c), area (font.getStringWidth (t), roundToInt (f.getHeight())), isWhitespace (isWhitespace_), isNewLine (t.containsChar ('\n') || t.containsChar ('\r')) { }
Token (const String& t, const Font& f, const Colour& c, const bool isWhitespace_) : text (t), font (f), colour (c), x(0), y(0), isWhitespace (isWhitespace_) { w = font.getStringWidth (t); h = roundToInt (f.getHeight()); isNewLine = t.containsChar ('\n') || t.containsChar ('\r'); }
int JUCEApplication::main (int argc, const char* argv[]) { JUCE_AUTORELEASEPOOL #if JUCE_MAC initialiseNSApplication(); #endif #if ! JUCE_WINDOWS jassert (createInstance != nullptr); juce_Argv0 = argv[0]; #endif #if JUCE_IOS return juce_iOSMain (argc, argv); #else String cmd; for (int i = 1; i < argc; ++i) { String arg (argv[i]); if (arg.containsChar (' ') && ! arg.isQuotedString()) arg = arg.quoted ('"'); cmd << arg << ' '; } return JUCEApplication::main (cmd); #endif }
void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override { if (rowIsSelected) g.fillAll (Colours::deepskyblue); if (JuceDemoTypeBase* type = JuceDemoTypeBase::getDemoTypeList() [rowNumber]) { String name (type->name.trimCharactersAtStart ("0123456789").trimStart()); AttributedString a; a.setJustification (Justification::centredLeft); String category; if (name.containsChar (':')) { category = name.upToFirstOccurrenceOf (":", true, false); name = name.fromFirstOccurrenceOf (":", false, false).trim(); if (height > 20) category << "\n"; else category << " "; } if (category.isNotEmpty()) a.append (category, Font (10.0f), Colour::greyLevel (0.5f)); a.append (name, Font (13.0f), Colours::white.withAlpha (0.9f)); a.draw (g, Rectangle<int> (width + 10, height).reduced (6, 0).toFloat()); } }
void UnityProjectBuilder::updateBuildDirectories() { if (buildDir.isEmpty()) return; ValueTree exportsTree (project.getChildWithName (Ids::exportFormats)); if (! exportsTree.isValid()) return; const int numExports = exportsTree.getNumChildren(); for (int i = 0; i < numExports; ++i) { ValueTree exporter (exportsTree.getChild (i)); if (exporter.hasProperty (Ids::targetFolderProp)) { logOutput ("Updating exporter " + exporter.getType().toString()); const String oldTarget (exporter.getProperty (Ids::targetFolderProp).toString()); String newTarget (buildDir); if (oldTarget.containsChar ('/')) newTarget << oldTarget.fromLastOccurrenceOf ("/", true, false); exporter.setProperty (Ids::targetFolderProp, newTarget, nullptr); } } }
//============================================================================== bool URL::isProbablyAWebsiteURL (const String& possibleURL) { static const char* validProtocols[] = { "http:", "ftp:", "https:" }; for (auto* protocol : validProtocols) if (possibleURL.startsWithIgnoreCase (protocol)) return true; if (possibleURL.containsChar ('@') || possibleURL.containsChar (' ')) return false; const String topLevelDomain (possibleURL.upToFirstOccurrenceOf ("/", false, false) .fromLastOccurrenceOf (".", false, false)); return topLevelDomain.isNotEmpty() && topLevelDomain.length() <= 3; }
//============================================================================== const String valueToFloat (const double v) { String s ((double) (float) v, 4); if (s.containsChar ('.')) s << 'f'; else s << ".0f"; return s; }
String floatLiteral (double value, int numDecPlaces) { String s (value, numDecPlaces); if (s.containsChar ('.')) s << 'f'; else s << ".0f"; return s; }
//============================================================================== KeyPress KeyPress::createFromDescription (const String& desc) { int modifiers = 0; for (int i = 0; i < numElementsInArray (KeyPressHelpers::modifierNames); ++i) if (desc.containsWholeWordIgnoreCase (KeyPressHelpers::modifierNames[i].name)) modifiers |= KeyPressHelpers::modifierNames[i].flag; int key = 0; for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i) { if (desc.containsWholeWordIgnoreCase (String (KeyPressHelpers::translations[i].name))) { key = KeyPressHelpers::translations[i].code; break; } } if (key == 0) key = KeyPressHelpers::getNumpadKeyCode (desc); if (key == 0) { // see if it's a function key.. if (! desc.containsChar ('#')) // avoid mistaking hex-codes like "#f1" { for (int i = 1; i <= 35; ++i) { if (desc.containsWholeWordIgnoreCase ("f" + String (i))) { if (i <= 16) key = F1Key + i - 1; else if (i <= 24) key = F17Key + i - 17; else if (i <= 35) key = F25Key + i - 25; } } } if (key == 0) { // give up and use the hex code.. auto hexCode = desc.fromFirstOccurrenceOf ("#", false, false) .retainCharacters ("0123456789abcdefABCDEF") .getHexValue32(); if (hexCode > 0) key = hexCode; else key = (int) CharacterFunctions::toUpperCase (desc.getLastCharacter()); } } return KeyPress (key, ModifierKeys (modifiers), 0); }
bool Database::executeQuery (var& results, const String& sql, const QueryArgs& args) { if (nullptr == db) return false; char* err = nullptr; int res = SQLITE_ERROR; if (! sql.containsChar ('?')) { res = sqlite3_exec (db, sql.toRawUTF8(), ksp1_query_var, &results, &err); } else { String query; int start = 0; int end = 0; for (const var& val : args) { end = sql.indexOf (start, "?"); query << sql.substring (start, end); if (val.isString()) query << "'" << val.toString() << "'"; else query << val.toString(); start = end + 1; } query << sql.substring (start); res = sqlite3_exec (db, query.toRawUTF8(), ksp1_query_var, &results, &err); } const bool result = (res == SQLITE_OK); if (res == SQLITE_OK) { } else { } if (err) { DBG ("SQL Error: " << err); sqlite3_free (err); err = nullptr; } return result; }
RSAKey::RSAKey (const String& s) { if (s.containsChar (',')) { part1.parseString (s.upToFirstOccurrenceOf (",", false, false), 16); part2.parseString (s.fromFirstOccurrenceOf (",", false, false), 16); } else { // the string needs to be two hex numbers, comma-separated.. jassertfalse; } }
const String castToFloat (const String& expression) { if (expression.containsOnly ("0123456789.f")) { String s (expression.getFloatValue()); if (s.containsChar (T('.'))) return s + "f"; return s + ".0f"; } return "(float) (" + expression + ")"; }
String JUCEApplicationBase::getCommandLineParameters() { String argString; for (int i = 1; i < juce_argc; ++i) { String arg (juce_argv[i]); if (arg.containsChar (' ') && ! arg.isQuotedString()) arg = arg.quoted ('"'); argString << arg << ' '; } return argString.trim(); }
const String doubleLiteral (double v) { String s (v, 7); if (s.containsChar ('.')) { s = s.trimCharactersAtEnd ("0"); if (s.endsWithChar ('.')) s << '0'; } else { s << ".0"; } return s; }
void Database::executeUpdate (const String& sql, const QueryArgs& args) { if (nullptr == db) return; char* err = nullptr; int res = SQLITE_ERROR; if (! sql.containsChar ('?')) { res = sqlite3_exec (db, sql.toRawUTF8(), ksp1_query_var, this, &err); } else { String query; int start = 0; int end = 0; for (const var& val : args) { const bool isString = val.isString(); end = sql.indexOf (start, "?"); query << sql.substring (start, end); if (isString) query << "'"; query << val.toString(); if (isString) query << "'"; start = end + 1; } query << sql.substring (start); res = sqlite3_exec (db, query.toRawUTF8(), ksp1_query_var, this, &err); } if (err) { DBG ("SQL Error: " << err); sqlite3_free (err); err = nullptr; } }
const String floatLiteral (float v) { String s ((double) v, 4); if (s.containsChar ('.')) { s = s.trimCharactersAtEnd ("0"); if (s.endsWithChar ('.')) s << '0'; s << 'f'; } else { s << ".0f"; } return s; }
void Project::getJuceConfigFlags (OwnedArray <JuceConfigFlag>& flags) { ValueTree configNode (getJuceConfigNode()); File juceConfigH (getLocalJuceFolder().getChildFile ("juce_Config.h")); StringArray lines; lines.addLines (juceConfigH.loadFileAsString()); for (int i = 0; i < lines.size(); ++i) { String line (lines[i].trim()); if (line.startsWith ("/** ") && line.containsChar (':')) { ScopedPointer <JuceConfigFlag> config (new JuceConfigFlag()); config->symbol = line.substring (4).upToFirstOccurrenceOf (":", false, false).trim(); if (config->symbol.length() > 4) { config->description = line.fromFirstOccurrenceOf (":", false, false).trimStart(); ++i; while (! (lines[i].contains ("*/") || lines[i].contains ("@see"))) { if (lines[i].trim().isNotEmpty()) config->description = config->description.trim() + " " + lines[i].trim(); ++i; } config->description = config->description.upToFirstOccurrenceOf ("*/", false, false); config->value.referTo (getJuceConfigFlag (config->symbol)); flags.add (config.release()); } } } }
String File::parseAbsolutePath (const String& p) { if (p.isEmpty()) return String(); #if JUCE_WINDOWS // Windows.. String path (p.replaceCharacter ('/', '\\')); if (path.contains ("\\..\\")) path = removeEllipsis (path); if (path.startsWithChar (separator)) { if (path[1] != separator) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path; } } else if (! path.containsChar (':')) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ // #ZEN(Changed 2016/04/03): Removed jassert, put in DBG call instead due to live constant ed. //jassertfalse; DBG("Passed relative path to juce_File line 130! Jassert Bypassed"); return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } #else // Mac or Linux.. // Yes, I know it's legal for a unix pathname to contain a backslash, but this assertion is here // to catch anyone who's trying to run code that was written on Windows with hard-coded path names. // If that's why you've ended up here, use File::getChildFile() to build your paths instead. jassert ((! p.containsChar ('\\')) || (p.indexOfChar ('/') >= 0 && p.indexOfChar ('/') < p.indexOfChar ('\\'))); String path (p); if (path.contains ("/../")) path = removeEllipsis (path); if (path.startsWithChar ('~')) { if (path[1] == separator || path[1] == 0) { // expand a name of the form "~/abc" path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName() + path.substring (1); } else { // expand a name of type "~dave/abc" const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false)); if (struct passwd* const pw = getpwnam (userName.toUTF8())) path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false); } } else if (! path.startsWithChar (separator)) { #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS if (! (path.startsWith ("./") || path.startsWith ("../"))) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; #if JUCE_LOG_ASSERTIONS Logger::writeToLog ("Illegal absolute path: " + path); #endif } #endif return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } #endif while (path.endsWithChar (separator) && path != separatorString) // careful not to turn a single "/" into an empty string. path = path.dropLastCharacters (1); return path; }
//============================================================================== static bool parseFile (const File& rootFolder, const File& newTargetFile, OutputStream& dest, const File& file, StringArray& alreadyIncludedFiles, const StringArray& includesToIgnore, const StringArray& wildcards, bool isOuterFile, bool stripCommentBlocks) { if (! file.exists()) { std::cout << "!! ERROR - file doesn't exist!"; return false; } StringArray lines; lines.addLines (file.loadFileAsString()); if (lines.size() == 0) { std::cout << "!! ERROR - input file was empty: " << file.getFullPathName(); return false; } bool lastLineWasBlank = true; for (int i = 0; i < lines.size(); ++i) { String line (lines[i]); String trimmed (line.trimStart()); if ((! isOuterFile) && trimmed.startsWith ("//================================================================")) line = String::empty; if (trimmed.startsWithChar ('#') && trimmed.removeCharacters (" \t").startsWithIgnoreCase ("#include\"")) { const int endOfInclude = line.indexOfChar (line.indexOfChar ('\"') + 1, '\"') + 1; const String lineUpToEndOfInclude (line.substring (0, endOfInclude)); const String lineAfterInclude (line.substring (endOfInclude)); const String filename (line.fromFirstOccurrenceOf ("\"", false, false) .upToLastOccurrenceOf ("\"", false, false)); const File targetFile (file.getSiblingFile (filename)); if (targetFile.exists() && targetFile.isAChildOf (rootFolder)) { if (matchesWildcard (filename.replaceCharacter ('\\', '/'), wildcards) && ! includesToIgnore.contains (targetFile.getFileName())) { if (line.containsIgnoreCase ("FORCE_AMALGAMATOR_INCLUDE") || ! alreadyIncludedFiles.contains (targetFile.getFullPathName())) { if (! canFileBeReincluded (targetFile)) alreadyIncludedFiles.add (targetFile.getFullPathName()); dest << newLine << "/*** Start of inlined file: " << targetFile.getFileName() << " ***/" << newLine; if (! parseFile (rootFolder, newTargetFile, dest, targetFile, alreadyIncludedFiles, includesToIgnore, wildcards, false, stripCommentBlocks)) { return false; } dest << "/*** End of inlined file: " << targetFile.getFileName() << " ***/" << newLine << newLine; line = lineAfterInclude; } else { line = String::empty; } } else { line = lineUpToEndOfInclude.upToFirstOccurrenceOf ("\"", true, false) + targetFile.getRelativePathFrom (newTargetFile.getParentDirectory()) .replaceCharacter ('\\', '/') + "\"" + lineAfterInclude; } } } if ((stripCommentBlocks || i == 0) && trimmed.startsWith ("/*") && (i > 10 || ! isOuterFile)) { int originalI = i; String originalLine = line; for (;;) { int end = line.indexOf ("*/"); if (end >= 0) { line = line.substring (end + 2); // If our comment appeared just before an assertion, leave it in, as it // might be useful.. if (lines [i + 1].contains ("assert") || lines [i + 2].contains ("assert")) { i = originalI; line = originalLine; } break; } line = lines [++i]; if (i >= lines.size()) break; } line = line.trimEnd(); if (line.isEmpty()) continue; } line = line.trimEnd(); { // Turn initial spaces into tabs.. int numIntialSpaces = 0; int len = line.length(); while (numIntialSpaces < len && line [numIntialSpaces] == ' ') ++numIntialSpaces; if (numIntialSpaces > 0) { int tabSize = 4; int numTabs = numIntialSpaces / tabSize; line = String::repeatedString ("\t", numTabs) + line.substring (numTabs * tabSize); } if (! line.containsChar ('"')) { // turn large areas of spaces into tabs - this will mess up alignment a bit, but // it's only the amalgamated file, so doesn't matter... line = line.replace (" ", "\t", false); line = line.replace (" ", "\t", false); } } if (line.isNotEmpty() || ! lastLineWasBlank) dest << line << newLine; lastLineWasBlank = line.isEmpty(); } return true; }
//============================================================================== String File::parseAbsolutePath (const String& p) { if (p.isEmpty()) return String::empty; #if JUCE_WINDOWS // Windows.. String path (p.replaceCharacter ('/', '\\')); if (path.startsWithChar (File::separator)) { if (path[1] != File::separator) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; path = File::getCurrentWorkingDirectory().getFullPathName().substring (0, 2) + path; } } else if (! path.containsChar (':')) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassertfalse; return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } #else // Mac or Linux.. // Yes, I know it's legal for a unix pathname to contain a backslash, but this assertion is here // to catch anyone who's trying to run code that was written on Windows with hard-coded path names. // If that's why you've ended up here, use File::getChildFile() to build your paths instead. jassert ((! p.containsChar ('\\')) || (p.indexOfChar ('/') >= 0 && p.indexOfChar ('/') < p.indexOfChar ('\\'))); String path (p); if (path.startsWithChar ('~')) { if (path[1] == File::separator || path[1] == 0) { // expand a name of the form "~/abc" path = File::getSpecialLocation (File::userHomeDirectory).getFullPathName() + path.substring (1); } else { // expand a name of type "~dave/abc" const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false)); struct passwd* const pw = getpwnam (userName.toUTF8()); if (pw != nullptr) path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false); } } else if (! path.startsWithChar (File::separator)) { /* When you supply a raw string to the File object constructor, it must be an absolute path. If you're trying to parse a string that may be either a relative path or an absolute path, you MUST provide a context against which the partial path can be evaluated - you can do this by simply using File::getChildFile() instead of the File constructor. E.g. saying "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute path if that's what was supplied, or would evaluate a partial path relative to the CWD. */ jassert (path.startsWith ("./") || path.startsWith ("../")); // (assume that a path "./xyz" is deliberately intended to be relative to the CWD) return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } #endif while (path.endsWithChar (separator) && path != separatorString) // careful not to turn a single "/" into an empty string. path = path.dropLastCharacters (1); return path; }
bool parseFile (const File& rootFolder, const File& newTargetFile, OutputStream& dest, const File& file, StringArray& alreadyIncludedFiles, const StringArray& includesToIgnore, const StringArray& wildcards, int level, bool stripCommentBlocks) { // Make sure the file exists if (! file.exists()) { std::cout << " !! ERROR - file doesn't exist!"; return false; } // Load the entire file as an array of individual lines. StringArray lines; lines.addLines (file.loadFileAsString()); // Make sure there is something in the file. if (lines.size() == 0) { std::cout << " !! ERROR - input file was empty: " << file.getFullPathName(); return false; } // Produce some descriptive progress. if (m_verbose) { if (level == 0) std::cout << " Processing \"" << file.getFileName () << "\"\n"; else std::cout << " Inlining " << String::repeatedString (" ", level - 1) << "\"" << file.getFileName () << "\"\n"; } bool lastLineWasBlank = true; for (int i = 0; i < lines.size(); ++i) { String line (lines[i]); String trimmed (line.trimStart()); if ((level != 0) && trimmed.startsWith ("//================================================================")) line = String::empty; std::string s (line.toUTF8 ()); if (m_remapTable.processLine (s)) { line = String::empty; } else { ParsedInclude parsedInclude = parseInclude (line, trimmed); if (parsedInclude.isIncludeLine) { const File targetFile = findInclude (file, parsedInclude.filename); if (targetFile.exists()) { if (matchesWildcard (parsedInclude.filename.replaceCharacter ('\\', '/')) && ! includesToIgnore.contains (targetFile.getFileName())) { String fileNamePart = File (parsedInclude.filename).getFileName (); RemapTable::iterator remapTo = m_remapTable.find (std::string (fileNamePart.toUTF8 ())); if (level != 0 && remapTo != m_remapTable.end ()) { line = "#include \""; line << String(remapTo->second.c_str ()) << "\"" << newLine; findAllFilesIncludedIn (targetFile, alreadyIncludedFiles); } else if (line.containsIgnoreCase("SKIP_AMALGAMATOR_INCLUDE")) { } else if (line.containsIgnoreCase ("FORCE_AMALGAMATOR_INCLUDE") || ! alreadyIncludedFiles.contains (targetFile.getFullPathName())) { if (parsedInclude.preventReinclude) { alreadyIncludedFiles.add (targetFile.getFullPathName()); } else if (parsedInclude.forceReinclude) { } else if (! canFileBeReincluded (targetFile)) { alreadyIncludedFiles.add (targetFile.getFullPathName()); } dest << newLine << "/*** Start of inlined file: " << targetFile.getFileName() << " ***/" << newLine; if (! parseFile (rootFolder, newTargetFile, dest, targetFile, alreadyIncludedFiles, includesToIgnore, wildcards, level + 1, stripCommentBlocks)) { return false; } dest << "/*** End of inlined file: " << targetFile.getFileName() << " ***/" << newLine << newLine; line = parsedInclude.lineAfterInclude; } else { line = String::empty; } } else { line = parsedInclude.lineUpToEndOfInclude.upToFirstOccurrenceOf ("\"", true, false) + targetFile.getRelativePathFrom (newTargetFile.getParentDirectory()) .replaceCharacter ('\\', '/') + "\"" + parsedInclude.lineAfterInclude; } } } } if ((stripCommentBlocks || i == 0) && trimmed.startsWith ("/*") && (i > 10 || level != 0)) { int originalI = i; String originalLine = line; for (;;) { int end = line.indexOf ("*/"); if (end >= 0) { line = line.substring (end + 2); // If our comment appeared just before an assertion, leave it in, as it // might be useful.. if (lines [i + 1].contains ("assert") || lines [i + 2].contains ("assert")) { i = originalI; line = originalLine; } break; } line = lines [++i]; if (i >= lines.size()) break; } line = line.trimEnd(); if (line.isEmpty()) continue; } line = line.trimEnd(); { // Turn initial spaces into tabs.. int numIntialSpaces = 0; int len = line.length(); while (numIntialSpaces < len && line [numIntialSpaces] == ' ') ++numIntialSpaces; if (numIntialSpaces > 0) { int tabSize = 4; int numTabs = numIntialSpaces / tabSize; line = String::repeatedString ("\t", numTabs) + line.substring (numTabs * tabSize); } #if 0 if (! line.containsChar ('"')) { // turn large areas of spaces into tabs - this will mess up alignment a bit, but // it's only the amalgamated file, so doesn't matter... line = line.replace (" ", "\t", false); line = line.replace (" ", "\t", false); } #endif } if (line.isNotEmpty() || ! lastLineWasBlank) dest << line << newLine; lastLineWasBlank = line.isEmpty(); } return true; }
String addQuotesIfContainsSpaces (const String& text) { return (text.containsChar (' ') && ! text.isQuotedString()) ? text.quoted() : text; }
// TODO what is this doing in Processor? Should be in MLScale. void MLPluginProcessor::loadScale(const File& f) { MLScale* pScale = mEngine.getScale(); if (!pScale) return; String scaleName = f.getFileNameWithoutExtension(); String scaleDir = f.getParentDirectory().getFileNameWithoutExtension(); String scaleStr = f.loadFileAsString(); const String delim = "\n\r"; int a, b; int contentLines = 0; int ratios = 0; // debug() << "MLPluginProcessor: loading scale " << scaleDir << "/" << scaleName << "\n"; a = b = 0; while(b >= 0) { b = scaleStr.indexOfAnyOf(delim, a, false); // debug() << "[" << a << ", " << b << "] > " << scaleStr.substring(a, b) << "\n"; String inputLine = scaleStr.substring(a, b); if (inputLine[0] != '!') { contentLines++; switch(contentLines) { case 1: { const char* descStr = inputLine.toUTF8(); pScale->setDescription(descStr); const char* nameStr = scaleName.toUTF8(); pScale->setName(nameStr); } break; case 2: { // int notes = inputLine.getIntValue(); // unused pScale->clear(); } break; default: // after 2nd line, add ratios. { // if (inputLine.contains(".")) { double ratio = inputLine.getDoubleValue(); ratios++; pScale->addRatio(ratio); } else { if (inputLine.containsChar('/')) { int s = inputLine.indexOfChar('/'); String numStr = inputLine.substring(0, s); String denomStr = inputLine.substring(s + 1, inputLine.length()); int num = numStr.getIntValue(); int denom = denomStr.getIntValue(); // debug() << "n:" << num << " denom: " << denom << "\n"; if ((num > 0) && (denom > 0)) { ratios++; pScale->addRatio(num, denom); } } else { int num = inputLine.getIntValue(); if (num > 0) { ratios++; pScale->addRatio(num, 1); } } } } break; } } a = b + 2; } if (ratios > 0) { // TODO load .kbm mapping file if one exists pScale->setDefaultMapping(); pScale->recalcRatios(); } broadcastScale(pScale); }