From 090a6d3e1efa1e44465e7290e47c02e3aec976ad Mon Sep 17 00:00:00 2001 From: "royqh1979@gmail.com" Date: Tue, 7 Sep 2021 10:28:40 +0800 Subject: [PATCH] work save --- RedPandaIDE/compiler/filecompiler.cpp | 2 +- RedPandaIDE/compiler/stdincompiler.cpp | 2 +- RedPandaIDE/editor.cpp | 2 +- RedPandaIDE/mainwindow.cpp | 4 +- RedPandaIDE/project.cpp | 312 ++++++++++++++++++++++++- RedPandaIDE/project.h | 87 +++---- RedPandaIDE/settings.cpp | 8 +- RedPandaIDE/systemconsts.h | 4 + RedPandaIDE/utils.cpp | 30 ++- RedPandaIDE/utils.h | 2 + 10 files changed, 398 insertions(+), 55 deletions(-) diff --git a/RedPandaIDE/compiler/filecompiler.cpp b/RedPandaIDE/compiler/filecompiler.cpp index ca9b723b..2d301280 100644 --- a/RedPandaIDE/compiler/filecompiler.cpp +++ b/RedPandaIDE/compiler/filecompiler.cpp @@ -63,7 +63,7 @@ bool FileCompiler::prepareForCompile() } mArguments += getLibraryArguments(fileType); - if (!QFile(mCompiler).exists()) { + if (!fileExists(mCompiler)) { throw CompileError(tr("The Compiler '%1' doesn't exists!").arg(mCompiler)); } diff --git a/RedPandaIDE/compiler/stdincompiler.cpp b/RedPandaIDE/compiler/stdincompiler.cpp index 18cea234..aef2a60a 100644 --- a/RedPandaIDE/compiler/stdincompiler.cpp +++ b/RedPandaIDE/compiler/stdincompiler.cpp @@ -52,7 +52,7 @@ bool StdinCompiler::prepareForCompile() } mArguments += getLibraryArguments(fileType); - if (!QFile(mCompiler).exists()) { + if (!fileExists(mCompiler)) { throw CompileError(tr("The Compiler '%1' doesn't exists!").arg(mCompiler)); } diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index f1710652..8c2d3b25 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -2100,7 +2100,7 @@ void Editor::cancelHint() QString Editor::getFileHint(const QString &s) { QString fileName = mParser->getHeaderFileName(mFilename, s); - if (QFileInfo(fileName).exists()) { + if (fileExists(fileName)) { return fileName + " - " + tr("Ctrl+click for more info"); } return ""; diff --git a/RedPandaIDE/mainwindow.cpp b/RedPandaIDE/mainwindow.cpp index 94df0d4b..793ccf5c 100644 --- a/RedPandaIDE/mainwindow.cpp +++ b/RedPandaIDE/mainwindow.cpp @@ -661,7 +661,7 @@ bool MainWindow::compile(bool rebuild) void MainWindow::runExecutable(const QString &exeName,const QString &filename) { // Check if it exists - if (!QFile(exeName).exists()) { + if (!fileExists(exeName)) { if (ui->actionCompile_Run->isEnabled()) { if (QMessageBox::question(this,tr("Confirm"), tr("Source file is not compiled.") @@ -1417,7 +1417,7 @@ void MainWindow::onFileChanged(const QString &path) { Editor *e = mEditorList->getOpenedEditorByFilename(path); if (e) { - if (QFile(path).exists()) { + if (fileExists(path)) { e->activate(); if (QMessageBox::question(this,tr("Compile"), tr("File '%1' was changed.").arg(path)+"

" + tr("Reload its content from disk?"), diff --git a/RedPandaIDE/project.cpp b/RedPandaIDE/project.cpp index 6b84b50d..99752b5b 100644 --- a/RedPandaIDE/project.cpp +++ b/RedPandaIDE/project.cpp @@ -131,7 +131,6 @@ void Project::open() } mIniFile->endGroup(); } - emit changed(); rebuildNodes(); } @@ -154,8 +153,305 @@ void Project::setModified(bool value) if (!file.exists() || (file.exists() && file.isWritable())) { mModified=value; + emit modifyChanged(mModified); + } +} + +void Project::addFolder(const QString &s) +{ + if (mFolders.indexOf(s)<0) { + mFolders.append(s); + rebuildNodes(); + //todo: MainForm.ProjectView.Select(FolderNodeFromName(s)); + //folderNodeFromName(s)->makeVisible(); + setModified(true); + } +} + +PProjectUnit Project::addUnit(const QString &inFileName, PFolderNode parentNode, bool rebuild) +{ + PProjectUnit newUnit; + // Don't add if it already exists + if (fileAlreadyExists(inFileName)) { + QMessageBox::critical(pMainWindow, + tr("File Exists"), + tr("File '%1' is already in the project"), + QMessageBox::Ok); + return newUnit; + } + newUnit = std::make_shared(this); + + // Set all properties + newUnit->setFileName(QDir(directory()).filePath(inFileName)); + newUnit->setNew(false); + newUnit->setEditor(nullptr); + newUnit->setFolder(getFolderPath(parentNode)); + newUnit->setNode(makeNewFileNode(baseFileName(newUnit->fileName()), false, parentNode); + newUnit->node()->unitIndex = mUnits.count(); + mUnits.append(newUnit); + + // Determine compilation flags + switch(getFileType(inFileName)) { + case FileType::CSource: + newUnit->setCompile(true); + newUnit->setCompileCpp(mOptions.useGPP); + newUnit->setLink(true); + break; + case FileType::CppSource: + newUnit->setCompile(true); + newUnit->setCompileCpp(true); + newUnit->setLink(true); + break; + case FileType::WindowsResourceSource: + newUnit->setCompile(true); + newUnit->setCompileCpp(mOptions.useGPP); + newUnit->setLink(false); + break; + default: + newUnit->setCompile(false); + newUnit->setCompileCpp(false); + newUnit->setLink(false); + } + newUnit->setPriority(1000); + newUnit->setOverrideBuildCmd(false); + newUnit->setBuildCmd(""); + if (rebuild) + rebuildNodes(); + setModified(true); + return newUnit; +} + +void Project::buildPrivateResource(bool forceSave) +{ + int comp = 0; + foreach (const PProjectUnit& unit,mUnits) { + if ( + (getFileType(unit->fileName()) == FileType::WindowsResourceSource) + && unit->compile() ) + comp++; } + // if project has no other resources included + // and does not have an icon + // and does not include the XP style manifest + // and does not include version info + // then do not create a private resource file + if ((comp == 0) && + (! mOptions.supportXPThemes) + && (! mOptions.includeVersionInfo) + && (mOptions.icon == "")) { + mOptions.privateResource=""; + return; + } + + // change private resource from .res + // to _private.res + // + // in many cases (like in importing a MSVC project) + // the project's resource file has already the + // .res filename. + QString res; + if (!mOptions.privateResource.isEmpty()) { + res = QDir(directory()).filePath(mOptions.privateResource); + if (changeFileExt(res, DEV_PROJECT_EXT) == mFilename) + res = changeFileExt(mFilename,QString("_private") + RC_EXT); + } else + res = changeFileExt(mFilename,QString("_private") + RC_EXT); + res = extractRelativePath(mFilename, res); + res.replace(' ','_'); + + // don't run the private resource file and header if not modified, + // unless ForceSave is true + if (!forceSave + && fileExists(res) + && fileExists(changeFileExt(res, H_EXT)) + && !mModified) + return; + + QStringList resFile; + resFile.append("/* THIS FILE WILL BE OVERWRITTEN BY DEV-C++ */"); + resFile.append("/* DO NOT EDIT! */"); + resFile.append(""); + + if (mOptions.includeVersionInfo) { + resFile.append("#include // include for version info constants"); + resFile.append(""); + end; + + foreach (const PProjectUnit& unit, mUnits) { + if ( + (getFileType(unit->fileName()) == FileType::WindowsResourceSource) + && unit->compile() ) + resFile.append("#include \"" + + genMakePath( + extractRelativePath(directory(), unit->fileName()), + false, + false) + "\""); + } + + if (!mOptions.icon.isEmpty()) { + resFile.append(""); + QString icon = QDir(directory()).absoluteFilePath(mOptions.icon); + if (fileExists(icon)) { + icon = extractRelativePath(mFilename, icon); + icon.replace('\\', '/'); + resFile.append("A ICON \"" + icon + '"'); + } else + mOptions.icon = ""; + } + + if (mOptions.supportXPThemes) { + resFile.append(""); + resFile.append("//"); + resFile.append("// SUPPORT FOR WINDOWS XP THEMES:"); + resFile.append("// THIS WILL MAKE THE PROGRAM USE THE COMMON CONTROLS"); + resFile.append("// LIBRARY VERSION 6.0 (IF IT IS AVAILABLE)"); + resFile.append("//"); + if (!mOptions.exeOutput.isEmpty()) + resFile.append( + "1 24 \"" + + genMakePath2( + includeTrailingPathDelimiter(mOptions.exeOutput) + + baseFileName(executable())) + + ".Manifest\""); + else + resFile.append("1 24 \"" + baseFileName(executable()) + ".Manifest\""); + } + + if Options.IncludeVersionInfo then begin + resFile.append("'); + resFile.append("//'); + resFile.append("// TO CHANGE VERSION INFORMATION, EDIT PROJECT OPTIONS...'); + resFile.append("//'); + resFile.append("1 VERSIONINFO'); + resFile.append("FILEVERSION ' + Format('%d,%d,%d,%d', [Options.VersionInfo.Major, Options.VersionInfo.Minor, + Options.VersionInfo.Release, Options.VersionInfo.Build])); + resFile.append("PRODUCTVERSION ' + Format('%d,%d,%d,%d', [Options.VersionInfo.Major, Options.VersionInfo.Minor, + Options.VersionInfo.Release, Options.VersionInfo.Build])); + case Options.typ of + dptGUI, + dptCon: resFile.append("FILETYPE VFT_APP'); + dptStat: resFile.append("FILETYPE VFT_STATIC_LIB'); + dptDyn: resFile.append("FILETYPE VFT_DLL'); + end; + resFile.append("{'); + resFile.append(" BLOCK "StringFileInfo"'); + resFile.append(" {'); + resFile.append(" BLOCK "' + Format('%4.4x%4.4x', [fOptions.VersionInfo.LanguageID, fOptions.VersionInfo.CharsetID]) + + + '"'); + resFile.append(" {'); + resFile.append(" VALUE "CompanyName", "' + fOptions.VersionInfo.CompanyName + '"'); + resFile.append(" VALUE "FileVersion", "' + fOptions.VersionInfo.FileVersion + '"'); + resFile.append(" VALUE "FileDescription", "' + fOptions.VersionInfo.FileDescription + '"'); + resFile.append(" VALUE "InternalName", "' + fOptions.VersionInfo.InternalName + '"'); + resFile.append(" VALUE "LegalCopyright", "' + fOptions.VersionInfo.LegalCopyright + '"'); + resFile.append(" VALUE "LegalTrademarks", "' + fOptions.VersionInfo.LegalTrademarks + '"'); + resFile.append(" VALUE "OriginalFilename", "' + fOptions.VersionInfo.OriginalFilename + '"'); + resFile.append(" VALUE "ProductName", "' + fOptions.VersionInfo.ProductName + '"'); + resFile.append(" VALUE "ProductVersion", "' + fOptions.VersionInfo.ProductVersion + '"'); + resFile.append(" }'); + resFile.append(" }'); + + // additional block for windows 95->NT + resFile.append(" BLOCK "VarFileInfo"'); + resFile.append(" {'); + resFile.append(" VALUE "Translation", ' + Format('0x%4.4x, %4.4d', [fOptions.VersionInfo.LanguageID, + fOptions.VersionInfo.CharsetID])); + resFile.append(" }'); + + resFile.append("}'); + end; + + Res := GetRealPath(Res, Directory); + if resFile.Count > 3 then begin + if FileExists(Res) and not ForceSave then begin + Original := TStringList.Create; + Original.LoadFromFile(Res); + if CompareStr(Original.Text, resFile.Text) <> 0 then begin + resFile.SaveToFile(Res); + end; + Original.Free; + end else begin + resFile.SaveToFile(Res); + end; + fOptions.PrivateResource := ExtractRelativePath(Directory, Res); + end else begin + if FileExists(Res) then + DeleteFile(PAnsiChar(Res)); + Res := ChangeFileExt(Res, RES_EXT); + if FileExists(Res) then + DeleteFile(PAnsiChar(Res)); + fOptions.PrivateResource := ''; + end; + if FileExists(Res) then + FileSetDate(Res, DateTimeToFileDate(Now)); // fix the "Clock skew detected" warning ;) + + // create XP manifest + if fOptions.SupportXPThemes then begin + resFile.Clear; + resFile.append("'); + resFile.append("'); + resFile.append("'); + resFile.append("' + Name + ''); + resFile.append("'); + resFile.append(" '); + resFile.append(" '); + resFile.append(" '); + resFile.append("'); + resFile.append("'); + resFile.SaveToFile(Executable + '.Manifest'); + FileSetDate(Executable + '.Manifest', DateTimeToFileDate(Now)); // fix the "Clock skew detected" warning ;) + end else if FileExists(Executable + '.Manifest') then + DeleteFile(PAnsiChar(Executable + '.Manifest')); + + // create private header file + Res := ChangeFileExt(Res, H_EXT); + resFile.Clear; + Def := StringReplace(ExtractFilename(UpperCase(Res)), '.', '_', [rfReplaceAll]); + resFile.append("/* THIS FILE WILL BE OVERWRITTEN BY DEV-C++ */'); + resFile.append("/* DO NOT EDIT ! */'); + resFile.append("'); + resFile.append("#ifndef ' + Def); + resFile.append("#define ' + Def); + resFile.append("'); + resFile.append("/* VERSION DEFINITIONS */'); + resFile.append("#define VER_STRING'#9 + Format('"%d.%d.%d.%d"', [fOptions.VersionInfo.Major, fOptions.VersionInfo.Minor, + fOptions.VersionInfo.Release, fOptions.VersionInfo.Build])); + resFile.append("#define VER_MAJOR'#9 + IntToStr(fOptions.VersionInfo.Major)); + resFile.append("#define VER_MINOR'#9 + IntToStr(fOptions.VersionInfo.Minor)); + resFile.append("#define VER_RELEASE'#9 + IntToStr(fOptions.VersionInfo.Release)); + resFile.append("#define VER_BUILD'#9 + IntToStr(fOptions.VersionInfo.Build)); + resFile.append("#define COMPANY_NAME'#9'"' + fOptions.VersionInfo.CompanyName + '"'); + resFile.append("#define FILE_VERSION'#9'"' + fOptions.VersionInfo.FileVersion + '"'); + resFile.append("#define FILE_DESCRIPTION'#9'"' + fOptions.VersionInfo.FileDescription + '"'); + resFile.append("#define INTERNAL_NAME'#9'"' + fOptions.VersionInfo.InternalName + '"'); + resFile.append("#define LEGAL_COPYRIGHT'#9'"' + fOptions.VersionInfo.LegalCopyright + '"'); + resFile.append("#define LEGAL_TRADEMARKS'#9'"' + fOptions.VersionInfo.LegalTrademarks + '"'); + resFile.append("#define ORIGINAL_FILENAME'#9'"' + fOptions.VersionInfo.OriginalFilename + '"'); + resFile.append("#define PRODUCT_NAME'#9'"' + fOptions.VersionInfo.ProductName + '"'); + resFile.append("#define PRODUCT_VERSION'#9'"' + fOptions.VersionInfo.ProductVersion + '"'); + resFile.append("'); + resFile.append("#endif /*' + Def + '*/'); + resFile.SaveToFile(Res); + + if FileExists(Res) then + FileSetDate(Res, DateTimeToFileDate(Now)); // fix the "Clock skew detected" warning ;) + + resFile.Free; } void Project::sortUnitsByPriority() @@ -320,7 +616,7 @@ void ProjectUnit::setModified(bool value) // If modified is set to true, mark project as modified too if (value) { - parent->setModified(true); + mParent->setModified(true); } } @@ -331,7 +627,7 @@ bool ProjectUnit::save() pMainWindow->fileSystemWatcher()->blockSignals(previous); }); bool result=true; - if (!mEditor && !QFile(mFileName).exists()) { + if (!mEditor && !fileExists(mFileName)) { // file is neither open, nor saved QStringList temp; StringsToFile(temp,mFileName); @@ -343,3 +639,13 @@ bool ProjectUnit::save() } return result; } + +PFolderNode &ProjectUnit::node() +{ + return mNode; +} + +void ProjectUnit::setNode(const PFolderNode &newNode) +{ + mNode = newNode; +} diff --git a/RedPandaIDE/project.h b/RedPandaIDE/project.h index 48160c67..4ba66c83 100644 --- a/RedPandaIDE/project.h +++ b/RedPandaIDE/project.h @@ -59,6 +59,9 @@ public: void setModified(bool value); bool save(); + PFolderNode &node(); + void setNode(const PFolderNode &newNode); + private: Project* mParent; Editor* mEditor; @@ -140,57 +143,59 @@ class Project : public QObject public: explicit Project(QObject *parent = nullptr); QString directory(); - QString executableName(); + QString executable(); QString makeFileName(); bool modified(); void setFileName(const QString& value); void setModified(bool value); - int newUnit(bool newProject, - PFolderNode parentNode, - const QString customFileName); + void addFolder(const QString& s); PProjectUnit addUnit(const QString& inFileName, PFolderNode parentNode, bool rebuild); - function GetFolderPath(Node: TTreeNode): AnsiString; - procedure UpdateFolders; - procedure AddFolder(const s: AnsiString); - function OpenUnit(index: integer): TEditor; - procedure CloseUnit(index: integer); - procedure DoAutoOpen; - procedure SaveUnitAs(i: integer; sFileName: AnsiString); // save single [UnitX] - procedure SaveAll; // save [Project] and all [UnitX] - procedure LoadLayout; // load all [UnitX] - procedure LoadUnitLayout(e: TEditor; Index: integer); // load single [UnitX] cursor positions - procedure SaveLayout; // save all [UnitX] - procedure SaveUnitLayout(e: TEditor; Index: integer); // save single [UnitX] cursor positions - function MakeProjectNode: TTreeNode; - function MakeNewFileNode(const s: AnsiString; IsFolder: boolean; NewParent: TTreeNode): TTreeNode; - procedure BuildPrivateResource(ForceSave: boolean = False); - procedure LoadOptions; - procedure SaveOptions; - function SaveUnits: Boolean; + void buildPrivateResource(bool forceSave); + + int newUnit(bool newProject, + PFolderNode parentNode, + const QString customFileName); + QString getFolderPath(PFolderNode node); + void updateFolders(); + Editor* openUnit(int index); + void closeUnit(int index); + void doAutoOpen(); + void saveUnitAs(int i, const QString& sFileName); // save single [UnitX] + void saveAll(); // save [Project] and all [UnitX] + void loadLayout(); // load all [UnitX] + void loadUnitLayout(Editor *e, int index); // load single [UnitX] cursor positions + void saveLayout(); // save all [UnitX] + void saveUnitLayout(Editor* e, int index); // save single [UnitX] cursor positions + PFolderNode makeProjectNode(); + PFolderNode makeNewFileNode(const QString& s, bool isFolder, PFolderNode newParent); + void loadOptions(); + void saveOptions(); + bool saveUnits(); // procedure Open; - function FileAlreadyExists(const s: AnsiString): boolean; - function RemoveFolder(Node: TTreeNode): boolean; - function RemoveEditor(index: integer; DoClose: boolean): boolean; - function GetUnitFromString(const s: AnsiString): integer; - procedure RebuildNodes; - function ListUnitStr(Separator: char): AnsiString; - procedure ExportToHTML; - function ShowOptions: Integer; - function AssignTemplate(const aFileName: AnsiString; aTemplate: TTemplate): boolean; - function FolderNodeFromName(const name: AnsiString): TTreeNode; - procedure CreateFolderNodes; - procedure UpdateNodeIndexes; - procedure SetNodeValue(value: TTreeNode); - procedure CheckProjectFileForUpdate; - procedure IncrementBuildNumber; - function GetCompilerOption(const OptionString: AnsiString): Char; - procedure SetCompilerOption(const OptionString: AnsiString; Value: Char); - procedure SaveToLog; + bool fileAlreadyExists(const QString& s); + bool removeFolder(PFolderNode node); + bool removeEditor(int index, bool doClose); + int getUnitFromString(const QString& s); + void rebuildNodes(); + QString listUnitStr(const QChar& separator); + void exportToHTML(); + void showOptions(); + // bool assignTemplate(const QString& aFileName, const PTemplate& aTemplate); + PFolderNode folderNodeFromName(const QString& name); + void createFolderNodes(); + void updateNodeIndexes(); + void setNodeValue(PFolderNode value); + void checkProjectFileForUpdate(); + void incrementBuildNumber(); + QChar getCompilerOption(const QString& optionString); + void setCompilerOption(const QString& optionString, const QChar& value); + void saveToLog(); signals: - void changed(); + void nodesChanged(); + void modifyChanged(bool value); private: void open(); void sortUnitsByPriority(); diff --git a/RedPandaIDE/settings.cpp b/RedPandaIDE/settings.cpp index 8161cf43..314e5fe6 100644 --- a/RedPandaIDE/settings.cpp +++ b/RedPandaIDE/settings.cpp @@ -1339,22 +1339,22 @@ bool Settings::CompilerSet::dirsValid(QString &msg) bool Settings::CompilerSet::validateExes(QString &msg) { msg =""; - if (!QFile(mCCompiler).exists()) { + if (!fileExists(mCCompiler)) { msg += QObject::tr("Cannot find the %1 \"%2\"") .arg("C Compiler") .arg(mCCompiler); } - if (!QFile(mCppCompiler).exists()) { + if (!fileExists(mCppCompiler)) { msg += QObject::tr("Cannot find the %1 \"%2\"") .arg("C++ Compiler") .arg(mCppCompiler); } - if (!QFile(mMake).exists()) { + if (!fileExists(mMake)) { msg += QObject::tr("Cannot find the %1 \"%2\"") .arg("Maker") .arg(mMake); } - if (!QFile(mDebugger).exists()) { + if (!fileExists(mDebugger)) { msg += QObject::tr("Cannot find the %1 \"%2\"") .arg("Maker") .arg(mDebugger); diff --git a/RedPandaIDE/systemconsts.h b/RedPandaIDE/systemconsts.h index 6c29d043..87f791ef 100644 --- a/RedPandaIDE/systemconsts.h +++ b/RedPandaIDE/systemconsts.h @@ -18,6 +18,10 @@ #error "Only support windows now!" #endif +#define DEV_PROJECT_EXT "dev" +#define RC_EXT "rc" +#define H_EXT "h" + #ifdef Q_OS_WIN # define PATH_SENSITIVITY Qt::CaseInsensitive # define NULL_FILE "NUL" diff --git a/RedPandaIDE/utils.cpp b/RedPandaIDE/utils.cpp index b8178fa5..f4693bdd 100644 --- a/RedPandaIDE/utils.cpp +++ b/RedPandaIDE/utils.cpp @@ -126,7 +126,7 @@ bool isNonPrintableAsciiChar(char ch) bool fileExists(const QString &file) { - return QFileInfo(file).exists(); + return fileExists(file); } bool fileExists(const QString &dir, const QString &fileName) @@ -461,7 +461,7 @@ QString changeFileExt(const QString& filename, const QString& ext) if (suffix.isEmpty()) { return filename+"."+ext; } else { - return filename.mid(0,filename.length()-suffix.length()-1)+"."+ext; + return fileInfo.completeBaseName()+"."+ext; } } @@ -621,3 +621,29 @@ QString baseFileName(const QString &fileName) QFileInfo fileInfo(fileName); return fileInfo.fileName(); } + +QString extractRelativePath(const QString &base, const QString &dest) +{ + QFileInfo baseInfo(base); + QDir baseDir; + if (baseInfo.isDir()) { + baseDir = baseInfo.absoluteDir(); + } else { + baseDir = baseInfo.dir(); + } + return baseDir.relativeFilePath(dest); +} + +QString genMakePath(const QString &fileName, bool escapeSpaces, bool encloseInQuotes) +{ + QString result = fileName; + + // Convert backslashes to slashes + result.replace('\\','/'); + if (escapeSpaces) { + result.replace(' ',"\\ "); + } + if (encloseInQuotes) + result = '"'+result+'"'; + return result; +} diff --git a/RedPandaIDE/utils.h b/RedPandaIDE/utils.h index 7569bd34..05c7ecda 100644 --- a/RedPandaIDE/utils.h +++ b/RedPandaIDE/utils.h @@ -124,6 +124,8 @@ QString includeTrailingPathDelimiter(const QString& path); QString excludeTrailingPathDelimiter(const QString& path); FileType getFileType(const QString& filename); QString changeFileExt(const QString& filename, const QString& ext); +QString extractRelativePath(const QString& base, const QString& dest); +QString genMakePath(const QString& fileName,bool escapeSpaces, bool encloseInQuotes); QString getCompiledExecutableName(const QString& filename); void splitStringArguments(const QString& arguments, QStringList& argumentList); bool programHasConsole(const QString& filename);