| @@ -66,16 +66,10 @@ void SourceCodeDocument::reloadInternal() | |||
| auto fileContent = getFile().loadFileAsString(); | |||
| auto lineFeed = [&]() -> const char* | |||
| { | |||
| if (fileContent.contains ("\r\n")) | |||
| return "\r\n"; | |||
| if (fileContent.contains ("\n")) | |||
| return "\n"; | |||
| auto lineFeed = getLineFeedForFile (fileContent); | |||
| return project->getProjectLineFeed().toRawUTF8(); | |||
| }(); | |||
| if (lineFeed.isEmpty()) | |||
| lineFeed = project->getProjectLineFeed(); | |||
| codeDoc->setNewLineCharacters (lineFeed); | |||
| @@ -98,7 +98,7 @@ void JucerDocument::timerCallback() | |||
| stopTimer(); | |||
| beginTransaction(); | |||
| flushChangesToDocuments (nullptr); | |||
| flushChangesToDocuments (nullptr, false); | |||
| } | |||
| } | |||
| @@ -513,23 +513,7 @@ bool JucerDocument::findTemplateFiles (String& headerContent, String& cppContent | |||
| return true; | |||
| } | |||
| static String fixLineEndings (const String& s, const char* lineFeed) | |||
| { | |||
| StringArray lines; | |||
| lines.addLines (s); | |||
| for (int i = 0; i < lines.size(); ++i) | |||
| lines.set (i, lines[i].trimEnd()); | |||
| while (lines.size() > 0 && lines [lines.size() - 1].trim().isEmpty()) | |||
| lines.remove (lines.size() - 1); | |||
| lines.add (String()); | |||
| return lines.joinIntoString (lineFeed); | |||
| } | |||
| bool JucerDocument::flushChangesToDocuments (Project* project) | |||
| bool JucerDocument::flushChangesToDocuments (Project* project, bool isInitial) | |||
| { | |||
| String headerTemplate, cppTemplate; | |||
| if (! findTemplateFiles (headerTemplate, cppTemplate)) | |||
| @@ -554,10 +538,20 @@ bool JucerDocument::flushChangesToDocuments (Project* project) | |||
| generated.applyToCode (cppTemplate, headerFile.withFileExtension (".cpp"), | |||
| existingCpp, project); | |||
| auto* lineFeed = project->getProjectLineFeed().toRawUTF8(); | |||
| if (isInitial) | |||
| { | |||
| jassert (project != nullptr); | |||
| auto lineFeed = project->getProjectLineFeed(); | |||
| headerTemplate = fixLineEndings (headerTemplate, lineFeed); | |||
| cppTemplate = fixLineEndings (cppTemplate, lineFeed); | |||
| headerTemplate = replaceLineFeeds (headerTemplate, lineFeed); | |||
| cppTemplate = replaceLineFeeds (cppTemplate, lineFeed); | |||
| } | |||
| else | |||
| { | |||
| headerTemplate = replaceLineFeeds (headerTemplate, getLineFeedForFile (existingHeader)); | |||
| cppTemplate = replaceLineFeeds (cppTemplate, getLineFeedForFile (existingCpp)); | |||
| } | |||
| if (header->getCodeDocument().getAllContent() != headerTemplate) | |||
| header->getCodeDocument().replaceAllContent (headerTemplate); | |||
| @@ -780,7 +774,7 @@ struct NewGUIComponentWizard : public NewFileWizard::Type | |||
| { | |||
| jucerDoc->setClassName (newFile.getFileNameWithoutExtension()); | |||
| jucerDoc->flushChangesToDocuments (&project); | |||
| jucerDoc->flushChangesToDocuments (&project, true); | |||
| jucerDoc.reset(); | |||
| cpp->save(); | |||
| @@ -58,7 +58,7 @@ public: | |||
| File getCppFile() const { return cpp->getFile(); } | |||
| File getHeaderFile() const { return getCppFile().withFileExtension (".h"); } | |||
| bool flushChangesToDocuments (Project*); | |||
| bool flushChangesToDocuments (Project*, bool); | |||
| bool reloadFromDocument(); | |||
| //============================================================================== | |||
| @@ -253,6 +253,8 @@ public: | |||
| void writeFile (const File& gradleProjectFolder, const String& filePath, const String& fileContent) const | |||
| { | |||
| MemoryOutputStream outStream; | |||
| outStream.setNewLineString ("\n"); | |||
| outStream << fileContent; | |||
| overwriteFileIfDifferentOrThrow (gradleProjectFolder.getChildFile (filePath), outStream); | |||
| } | |||
| @@ -260,6 +262,8 @@ public: | |||
| void writeBinaryFile (const File& gradleProjectFolder, const String& filePath, const char* binaryData, const int binarySize) const | |||
| { | |||
| MemoryOutputStream outStream; | |||
| outStream.setNewLineString ("\n"); | |||
| outStream.write (binaryData, static_cast<size_t> (binarySize)); | |||
| overwriteFileIfDifferentOrThrow (gradleProjectFolder.getChildFile (filePath), outStream); | |||
| } | |||
| @@ -350,6 +354,7 @@ private: | |||
| void writeCmakeFile (const File& file) const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| mo << "# Automatically generated makefile, created by the Projucer" << newLine | |||
| << "# Don't edit this file! Your changes will be overwritten when you re-save the Projucer project!" << newLine | |||
| @@ -544,6 +549,7 @@ private: | |||
| String getProjectBuildGradleFileContent() const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| mo << "buildscript {" << newLine; | |||
| mo << " repositories {" << newLine; | |||
| @@ -581,6 +587,8 @@ private: | |||
| String getAppBuildGradleFileContent (const OwnedArray<LibraryModule>& modules) const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| mo << "apply plugin: 'com.android." << (isLibrary() ? "library" : "application") << "'" << newLine << newLine; | |||
| mo << "android {" << newLine; | |||
| @@ -610,6 +618,7 @@ private: | |||
| String getAndroidProductFlavours() const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| mo << " flavorDimensions \"default\"" << newLine; | |||
| mo << " productFlavors {" << newLine; | |||
| @@ -653,6 +662,7 @@ private: | |||
| String getAndroidSigningConfig() const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| auto keyStoreFilePath = androidKeyStore.get().toString().replace ("${user.home}", "${System.properties['user.home']}") | |||
| .replace ("/", "${File.separator}"); | |||
| @@ -680,6 +690,7 @@ private: | |||
| auto targetSdkVersion = static_cast<int> (androidTargetSDK.get()); | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| mo << " defaultConfig {" << newLine; | |||
| @@ -710,6 +721,7 @@ private: | |||
| String getAndroidBuildTypes() const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| mo << " buildTypes {" << newLine; | |||
| @@ -740,6 +752,7 @@ private: | |||
| String getAndroidVariantFilter() const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| mo << " variantFilter { variant ->" << newLine; | |||
| mo << " def names = variant.flavors*.name" << newLine; | |||
| @@ -762,6 +775,7 @@ private: | |||
| String getAndroidRepositories() const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| auto repositories = StringArray::fromLines (androidRepositories.get().toString()); | |||
| @@ -778,6 +792,8 @@ private: | |||
| String getAndroidDependencies() const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| mo << " dependencies {" << newLine; | |||
| for (auto& d : StringArray::fromLines (androidDependencies.get().toString())) | |||
| @@ -800,6 +816,7 @@ private: | |||
| String getApplyPlugins() const | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| if (androidEnableRemoteNotifications.get()) | |||
| mo << "apply plugin: 'com.google.gms.google-services'" << newLine; | |||
| @@ -834,6 +851,8 @@ private: | |||
| } | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| mo << " sourceSets {" << newLine; | |||
| mo << getSourceSetStringFor ("main.java.srcDirs", javaSourceSets); | |||
| mo << newLine; | |||
| @@ -888,7 +907,7 @@ private: | |||
| s << "]" << newLine; | |||
| return s; | |||
| return replaceLineFeeds (s, "\n"); | |||
| } | |||
| //============================================================================== | |||
| @@ -899,7 +918,7 @@ private: | |||
| props << "ndk.dir=" << sanitisePath (getAppSettings().getStoredPath (Ids::androidNDKPath, TargetOS::getThisOS()).get().toString()) << newLine | |||
| << "sdk.dir=" << sanitisePath (getAppSettings().getStoredPath (Ids::androidSDKPath, TargetOS::getThisOS()).get().toString()) << newLine; | |||
| return props; | |||
| return replaceLineFeeds (props, "\n"); | |||
| } | |||
| String getGradleWrapperPropertiesFileContent() const | |||
| @@ -1186,7 +1205,9 @@ private: | |||
| createDirectoryOrThrow (file.getParentDirectory()); | |||
| PNGImageFormat png; | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| if (! png.writeImageToStream (im, mo)) | |||
| throw SaveError ("Can't generate Android icon file"); | |||
| @@ -169,6 +169,7 @@ public: | |||
| void create (const OwnedArray<LibraryModule>&) const override | |||
| { | |||
| MemoryOutputStream out; | |||
| out.setNewLineString ("\n"); | |||
| out << "# Automatically generated CMakeLists, created by the Projucer" << newLine | |||
| << "# Don't edit this file! Your changes will be overwritten when you re-save the Projucer project!" << newLine | |||
| @@ -198,7 +199,9 @@ public: | |||
| MemoryBlock existingContent; | |||
| getTargetFolder().getChildFile ("CMakeLists.txt").loadFileAsData (existingContent); | |||
| MemoryOutputStream out (existingContent, true); | |||
| out.setNewLineString ("\n"); | |||
| out << "###############################################################################" << newLine | |||
| << "# " << exporter->getName() << newLine | |||
| @@ -150,7 +150,7 @@ public: | |||
| XmlElement xml ("CodeBlocks_project_file"); | |||
| addVersion (xml); | |||
| createProject (*xml.createNewChildElement ("Project")); | |||
| writeXmlOrThrow (xml, cbpFile, "UTF-8", 10); | |||
| writeXmlOrThrow (xml, cbpFile, "UTF-8", 10, true); | |||
| } | |||
| //============================================================================== | |||
| @@ -422,6 +422,8 @@ public: | |||
| void create (const OwnedArray<LibraryModule>&) const override | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString ("\n"); | |||
| writeMakefile (mo); | |||
| overwriteFileIfDifferentOrThrow (getTargetFolder().getChildFile ("Makefile"), mo); | |||
| @@ -443,18 +443,12 @@ protected: | |||
| static void writeXmlOrThrow (const XmlElement& xml, const File& file, const String& encoding, int maxCharsPerLine, bool useUnixNewLines = false) | |||
| { | |||
| MemoryOutputStream mo; | |||
| xml.writeToStream (mo, String(), false, true, encoding, maxCharsPerLine); | |||
| if (useUnixNewLines) | |||
| { | |||
| MemoryOutputStream mo2; | |||
| mo2 << mo.toString().replace ("\r\n", "\n"); | |||
| overwriteFileIfDifferentOrThrow (file, mo2); | |||
| } | |||
| else | |||
| { | |||
| overwriteFileIfDifferentOrThrow (file, mo); | |||
| } | |||
| mo.setNewLineString ("\n"); | |||
| xml.writeToStream (mo, String(), false, true, encoding, maxCharsPerLine); | |||
| overwriteFileIfDifferentOrThrow (file, mo); | |||
| } | |||
| static Image rescaleImageForIcon (Drawable&, int iconSize); | |||
| @@ -136,6 +136,7 @@ void ProjectSaver::writePluginCharacteristicsFile() | |||
| } | |||
| MemoryOutputStream mem; | |||
| mem.setNewLineString (projectLineFeed); | |||
| mem << "//==============================================================================" << newLine | |||
| << "// Audio plugin settings.." << newLine | |||
| @@ -76,6 +76,8 @@ public: | |||
| return thread.result; | |||
| } | |||
| projectLineFeed = project.getProjectLineFeed(); | |||
| auto appConfigUserContent = loadUserContentFromAppConfig(); | |||
| auto oldFile = project.getFile(); | |||
| @@ -269,6 +271,7 @@ private: | |||
| File appConfigFile; | |||
| bool hasBinaryData = false; | |||
| String projectLineFeed = "\r\n"; | |||
| // Recursively clears out any files in a folder that we didn't create, but avoids | |||
| // any folders containing hidden files that might be used by version-control systems. | |||
| @@ -325,6 +328,8 @@ private: | |||
| if (xml != nullptr) | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString (projectLineFeed); | |||
| xml->writeToStream (mo, String()); | |||
| replaceFileIfDifferent (projectFile, mo); | |||
| } | |||
| @@ -367,7 +372,7 @@ private: | |||
| userContent.add ({}); | |||
| } | |||
| return userContent.joinIntoString (newLine) + newLine; | |||
| return userContent.joinIntoString (projectLineFeed) + projectLineFeed; | |||
| } | |||
| void checkModuleValidity (OwnedArray<LibraryModule>& modules) | |||
| @@ -516,6 +521,8 @@ private: | |||
| appConfigFile = getAppConfigFile(); | |||
| MemoryOutputStream mem; | |||
| mem.setNewLineString (projectLineFeed); | |||
| writeAppConfig (mem, modules, userContent); | |||
| saveGeneratedFile (project.getAppConfigFilename(), mem); | |||
| } | |||
| @@ -570,6 +577,8 @@ private: | |||
| void writeAppHeader (const OwnedArray<LibraryModule>& modules) | |||
| { | |||
| MemoryOutputStream mem; | |||
| mem.setNewLineString (projectLineFeed); | |||
| writeAppHeader (mem, modules); | |||
| saveGeneratedFile (project.getJuceSourceHFilename(), mem); | |||
| } | |||
| @@ -581,6 +590,7 @@ private: | |||
| for (auto& cu : module->getAllCompileUnits()) | |||
| { | |||
| MemoryOutputStream mem; | |||
| mem.setNewLineString (projectLineFeed); | |||
| writeAutoGenWarningComment (mem); | |||
| @@ -650,6 +660,8 @@ private: | |||
| void writeReadmeFile() | |||
| { | |||
| MemoryOutputStream out; | |||
| out.setNewLineString (projectLineFeed); | |||
| out << newLine | |||
| << " Important Note!!" << newLine | |||
| << " ================" << newLine | |||
| @@ -676,7 +688,8 @@ private: | |||
| void writeUnityScriptFile() | |||
| { | |||
| String unityScriptContents (BinaryData::jucer_UnityPluginGUIScript_cs); | |||
| auto unityScriptContents = replaceLineFeeds (BinaryData::jucer_UnityPluginGUIScript_cs, | |||
| projectLineFeed); | |||
| auto projectName = Project::addUnityPluginPrefixIfNecessary (project.getProjectNameString()); | |||
| @@ -96,23 +96,20 @@ int64 ResourceFile::getTotalDataSize() const | |||
| return total; | |||
| } | |||
| static String getComment() | |||
| static void writeComment (MemoryOutputStream& mo) | |||
| { | |||
| String comment; | |||
| comment << newLine << newLine | |||
| << " This is an auto-generated file: Any edits you make may be overwritten!" << newLine | |||
| << newLine | |||
| << "*/" << newLine | |||
| << newLine; | |||
| return comment; | |||
| mo << newLine << newLine | |||
| << " This is an auto-generated file: Any edits you make may be overwritten!" << newLine | |||
| << newLine | |||
| << "*/" << newLine | |||
| << newLine; | |||
| } | |||
| Result ResourceFile::writeHeader (MemoryOutputStream& header) | |||
| { | |||
| header << "/* =========================================================================================" | |||
| << getComment() | |||
| << "#pragma once" << newLine | |||
| header << "/* ========================================================================================="; | |||
| writeComment (header); | |||
| header << "#pragma once" << newLine | |||
| << newLine | |||
| << "namespace " << className << newLine | |||
| << "{" << newLine; | |||
| @@ -167,9 +164,9 @@ Result ResourceFile::writeCpp (MemoryOutputStream& cpp, const File& headerFile, | |||
| { | |||
| bool isFirstFile = (i == 0); | |||
| cpp << "/* ==================================== " << resourceFileIdentifierString << " ====================================" | |||
| << getComment() | |||
| << "namespace " << className << newLine | |||
| cpp << "/* ==================================== " << resourceFileIdentifierString << " ===================================="; | |||
| writeComment (cpp); | |||
| cpp << "namespace " << className << newLine | |||
| << "{" << newLine; | |||
| bool containsAnyImages = false; | |||
| @@ -275,10 +272,14 @@ Result ResourceFile::writeCpp (MemoryOutputStream& cpp, const File& headerFile, | |||
| Result ResourceFile::write (Array<File>& filesCreated, const int maxFileSize) | |||
| { | |||
| auto projectLineFeed = project.getProjectLineFeed(); | |||
| auto headerFile = project.getBinaryDataHeaderFile(); | |||
| { | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString (projectLineFeed); | |||
| auto r = writeHeader (mo); | |||
| if (r.failed()) | |||
| @@ -298,6 +299,7 @@ Result ResourceFile::write (Array<File>& filesCreated, const int maxFileSize) | |||
| auto cpp = project.getBinaryDataCppFile (fileIndex); | |||
| MemoryOutputStream mo; | |||
| mo.setNewLineString (projectLineFeed); | |||
| auto r = writeCpp (mo, headerFile, i, maxFileSize); | |||
| @@ -39,6 +39,32 @@ String joinLinesIntoSourceFile (StringArray& lines) | |||
| return lines.joinIntoString (getPreferredLineFeed()) + getPreferredLineFeed(); | |||
| } | |||
| String replaceLineFeeds (const String& content, const String& lineFeed) | |||
| { | |||
| StringArray lines; | |||
| lines.addLines (content); | |||
| return lines.joinIntoString (lineFeed); | |||
| } | |||
| String getLineFeedForFile (const String& fileContent) | |||
| { | |||
| auto t = fileContent.getCharPointer(); | |||
| while (! t.isEmpty()) | |||
| { | |||
| switch (t.getAndAdvance()) | |||
| { | |||
| case 0: break; | |||
| case '\n': return "\n"; | |||
| case '\r': if (*t == '\n') return "\r\n"; | |||
| default: continue; | |||
| } | |||
| } | |||
| return {}; | |||
| } | |||
| String trimCommentCharsFromStartOfLine (const String& line) | |||
| { | |||
| return line.trimStart().trimCharactersAtStart ("*/").trimStart(); | |||
| @@ -30,6 +30,8 @@ | |||
| //============================================================================== | |||
| const char* getPreferredLineFeed(); | |||
| String joinLinesIntoSourceFile (StringArray& lines); | |||
| String replaceLineFeeds (const String& content, const String& lineFeed); | |||
| String getLineFeedForFile (const String& fileContent); | |||
| var parseJUCEHeaderMetadata (const File&); | |||
| @@ -32,22 +32,14 @@ NewFileWizard::Type* createGUIComponentWizard(); | |||
| //============================================================================== | |||
| namespace | |||
| { | |||
| String addPreferredProjectLineFeed (const String& content, const String& lineFeed) | |||
| { | |||
| if (lineFeed != "\r\n") | |||
| return content.replace ("\r\n", lineFeed); | |||
| return content; | |||
| } | |||
| static String fillInBasicTemplateFields (const File& file, const Project::Item& item, const char* templateName) | |||
| { | |||
| return addPreferredProjectLineFeed (item.project.getFileTemplate (templateName) | |||
| .replace ("%%filename%%", file.getFileName(), false) | |||
| .replace ("%%date%%", Time::getCurrentTime().toString (true, true, true), false) | |||
| .replace ("%%author%%", SystemStats::getFullUserName(), false) | |||
| .replace ("%%include_corresponding_header%%", CodeHelpers::createIncludeStatement (file.withFileExtension (".h"), file)), | |||
| item.project.getProjectLineFeed()); | |||
| return replaceLineFeeds (item.project.getFileTemplate (templateName) | |||
| .replace ("%%filename%%", file.getFileName(), false) | |||
| .replace ("%%date%%", Time::getCurrentTime().toString (true, true, true), false) | |||
| .replace ("%%author%%", SystemStats::getFullUserName(), false) | |||
| .replace ("%%include_corresponding_header%%", CodeHelpers::createIncludeStatement (file.withFileExtension (".h"), file)), | |||
| item.project.getProjectLineFeed()); | |||
| } | |||
| static bool fillInNewCppFileTemplate (const File& file, const Project::Item& item, const char* templateName) | |||
| @@ -180,7 +172,7 @@ public: | |||
| .replace ("%%component_class%%", className) | |||
| .replace ("%%include_juce%%", CodeHelpers::createIncludeStatement (parent.project.getAppIncludeFile(), newFile)); | |||
| addPreferredProjectLineFeed (content, parent.project.getProjectLineFeed()); | |||
| content = replaceLineFeeds (content, parent.project.getProjectLineFeed()); | |||
| if (FileHelpers::overwriteFileWithNewDataIfDifferent (newFile, content)) | |||
| { | |||