diff --git a/cppcheck.cppcheck b/cppcheck.cppcheck new file mode 100644 index 000000000..a76e7db2a --- /dev/null +++ b/cppcheck.cppcheck @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/gui/gui.qrc b/gui/gui.qrc index 35d8a3389..08f069456 100644 --- a/gui/gui.qrc +++ b/gui/gui.qrc @@ -1,23 +1,25 @@ - - - icon.png - images/dialog-error.png - images/dialog-information.png - images/dialog-warning.png - images/edit-clear.png - images/go-down.png - images/help-browser.png - images/media-floppy.png - images/preferences-system.png - images/process-stop.png - images/text-x-generic.png - images/view-refresh.png - images/showerrors.png - images/showstylewarnings.png - ../COPYING - ../AUTHORS - images/go-home.png - images/go-next.png - images/go-previous.png - + + + icon.png + images/dialog-error.png + images/dialog-information.png + images/dialog-warning.png + images/edit-clear.png + images/go-down.png + images/help-browser.png + images/media-floppy.png + images/preferences-system.png + images/process-stop.png + images/text-x-generic.png + images/view-refresh.png + images/showerrors.png + images/showstylewarnings.png + images/openproject.png + + ../COPYING + ../AUTHORS + images/go-home.png + images/go-next.png + images/go-previous.png + diff --git a/gui/images/openproject.png b/gui/images/openproject.png new file mode 100644 index 000000000..472484f11 Binary files /dev/null and b/gui/images/openproject.png differ diff --git a/gui/main.ui b/gui/main.ui index 72f62b145..01ef7fa14 100644 --- a/gui/main.ui +++ b/gui/main.ui @@ -74,8 +74,12 @@ &File + + + + @@ -151,6 +155,7 @@ false + @@ -357,6 +362,10 @@ + + + :/images/openproject.png:/images/openproject.png + Open P&roject File... @@ -374,6 +383,22 @@ Log View + + + false + + + C&lose Project File + + + + + false + + + &Edit Project File... + + diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index c73456a12..6f69ff297 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -44,7 +44,8 @@ MainWindow::MainWindow() : mLanguages(new QActionGroup(this)), mLogView(NULL), mHelpWindow(NULL), - mExiting(false) + mExiting(false), + mProject(NULL) { mUI.setupUi(this); mUI.mResults->Initialize(mSettings, mApplications); @@ -84,6 +85,8 @@ MainWindow::MainWindow() : connect(mUI.mActionNewProjectFile, SIGNAL(triggered()), this, SLOT(NewProjectFile())); connect(mUI.mActionOpenProjectFile, SIGNAL(triggered()), this, SLOT(OpenProjectFile())); + connect(mUI.mActionCloseProjectFile, SIGNAL(triggered()), this, SLOT(CloseProjectFile())); + connect(mUI.mActionEditProjectFile, SIGNAL(triggered()), this, SLOT(EditProjectFile())); connect(mUI.mActionHelpContents, SIGNAL(triggered()), this, SLOT(OpenHelpContents())); @@ -112,6 +115,7 @@ MainWindow::~MainWindow() { delete mLogView; delete mHelpWindow; + delete mProject; } void MainWindow::CreateLanguageMenuItems() @@ -240,6 +244,17 @@ void MainWindow::DoCheckFiles(const QStringList &files) QStringList MainWindow::SelectFilesToCheck(QFileDialog::FileMode mode) { + if (mProject) + { + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Cppcheck")); + const QString msg(tr("You must close the project file before selecting new files or directories!")); + msgBox.setText(msg); + msgBox.setIcon(QMessageBox::Critical); + msgBox.exec(); + return QStringList(); + } + QStringList selected; // NOTE: we use QFileDialog::getOpenFileNames() and @@ -289,48 +304,51 @@ void MainWindow::CheckDirectory() Settings MainWindow::GetCppcheckSettings() { - ProjectFile pfile; + ProjectFile *pfile = NULL; Settings result; + bool projectRead = true; - if (!mCurrentDirectory.isEmpty()) + if (!mCurrentDirectory.isEmpty() && !mProject) { // Format project filename (directory name + .cppcheck) and load // the project file if it is found. QStringList parts = mCurrentDirectory.split("/"); QString projfile = mCurrentDirectory + "/" + parts[parts.count() - 1] + ".cppcheck"; - bool projectRead = false; if (QFile::exists(projfile)) { qDebug() << "Reading project file " << projfile; - projectRead = pfile.Read(projfile); + projectRead = pfile->Read(projfile); } + } + else if (mProject) + { + pfile = mProject->GetProjectFile(); + } - if (projectRead) + if (projectRead) + { + QStringList dirs = pfile->GetIncludeDirs(); + QString dir; + foreach(dir, dirs) { - QStringList dirs = pfile.GetIncludeDirs(); - QString dir; - foreach(dir, dirs) - { - QString incdir; - if (!QDir::isAbsolutePath(dir)) - incdir = mCurrentDirectory + "/"; - incdir += dir; - incdir = QDir::cleanPath(incdir); + QString incdir; + if (!QDir::isAbsolutePath(dir)) + incdir = mCurrentDirectory + "/"; + incdir += dir; + incdir = QDir::cleanPath(incdir); - // include paths must end with '/' - if (!incdir.endsWith("/")) - incdir += "/"; - result._includePaths.push_back(incdir.toStdString()); - } - - QStringList defines = pfile.GetDefines(); - QString define; - foreach(define, defines) - { - if (!result.userDefines.empty()) - result.userDefines += ";"; - result.userDefines += define.toStdString(); - } + // include paths must end with '/' + if (!incdir.endsWith("/")) + incdir += "/"; + result._includePaths.push_back(incdir.toStdString()); + } + QStringList defines = pfile->GetDefines(); + QString define; + foreach(define, defines) + { + if (!result.userDefines.empty()) + result.userDefines += ";"; + result.userDefines += define.toStdString(); } } @@ -667,6 +685,9 @@ void MainWindow::OpenHtmlHelpContents() void MainWindow::OpenProjectFile() { + if (mProject != NULL) + delete mProject; + const QString filter = tr("Project files (*.cppcheck);;All files(*.*)"); QString filepath = QFileDialog::getOpenFileName(this, tr("Select Project File"), @@ -675,9 +696,38 @@ void MainWindow::OpenProjectFile() if (!filepath.isEmpty()) { - Project prj(filepath, this); - if (prj.Open()) - prj.Edit(); + QFileInfo inf(filepath); + const QString filename = inf.fileName(); + FormatAndSetTitle(tr("Project:") + QString(" ") + filename); + + mUI.mActionCloseProjectFile->setEnabled(true); + mUI.mActionEditProjectFile->setEnabled(true); + mProject = new Project(filepath, this); + mProject->Open(); + QString rootpath = mProject->GetProjectFile()->GetRootPath(); + + // If root path not give or "current dir" then use project file's directory + // as check path + if (rootpath.isEmpty() || rootpath == ".") + mCurrentDirectory = inf.canonicalPath(); + else + mCurrentDirectory = rootpath; + + QStringList paths = mProject->GetProjectFile()->GetCheckPaths(); + if (!paths.isEmpty()) + { + for (int i = 0; i < paths.size(); i++) + { + if (!QDir::isAbsolutePath(paths[i])) + { + QString path = mCurrentDirectory + "/"; + path += paths[i]; + paths[i] = QDir::cleanPath(path); + } + } + + DoCheckFiles(paths); + } } } @@ -691,12 +741,44 @@ void MainWindow::NewProjectFile() if (!filepath.isEmpty()) { - Project prj(filepath, this); - prj.Create(); - prj.Edit(); + mUI.mActionCloseProjectFile->setEnabled(true); + mUI.mActionEditProjectFile->setEnabled(true); + QFileInfo inf(filepath); + const QString filename = inf.fileName(); + FormatAndSetTitle(tr("Project:") + QString(" ") + filename); + + if (mProject) + delete mProject; + mProject = new Project(filepath, this); + mProject->Create(); + mProject->Edit(); } } +void MainWindow::CloseProjectFile() +{ + delete mProject; + mProject = NULL; + mUI.mActionCloseProjectFile->setEnabled(false); + mUI.mActionEditProjectFile->setEnabled(false); + FormatAndSetTitle(); +} + +void MainWindow::EditProjectFile() +{ + if (!mProject) + { + QMessageBox msg(QMessageBox::Critical, + tr("Cppcheck"), + QString(tr("No project file loaded")), + QMessageBox::Ok, + this); + msg.exec(); + return; + } + mProject->Edit(); +} + void MainWindow::ShowLogView() { if (mLogView == NULL) diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 146818e43..619fe71a6 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -31,9 +31,11 @@ #include "translationhandler.h" #include "settings.h" #include "ui_main.h" + class ThreadHandler; class LogView; class HelpWindow; +class Project; /// @addtogroup GUI /// @{ @@ -140,11 +142,23 @@ public slots: void NewProjectFile(); /** - * @brief Slot to edit existing project file. + * @brief Slot to open project file and start checking contained paths. * */ void OpenProjectFile(); + /** + * @brief Slot to close open project file. + * + */ + void CloseProjectFile(); + + /** + * @brief Slot to edit project file. + * + */ + void EditProjectFile(); + /** * @brief Slot for showing the log view. * @@ -324,7 +338,7 @@ protected: QString mCurrentDirectory; /** - * @brief Log view.. + * @brief Log view. */ LogView *mLogView; @@ -333,6 +347,11 @@ protected: */ HelpWindow *mHelpWindow; + /** + * @brief Project (file). + */ + Project *mProject; + private: /** diff --git a/gui/project.cpp b/gui/project.cpp index 5d4f89661..3503f799f 100644 --- a/gui/project.cpp +++ b/gui/project.cpp @@ -77,18 +77,25 @@ bool Project::Open() void Project::Edit() { ProjectFileDialog dlg(mFilename, mParentWidget); - + QString root = mPFile->GetRootPath(); + dlg.SetRootPath(root); QStringList includes = mPFile->GetIncludeDirs(); dlg.SetIncludepaths(includes); QStringList defines = mPFile->GetDefines(); dlg.SetDefines(defines); + QStringList paths = mPFile->GetCheckPaths(); + dlg.SetPaths(paths); int rv = dlg.exec(); if (rv == QDialog::Accepted) { + QString root = dlg.GetRootPath(); + mPFile->SetRootPath(root); QStringList includes = dlg.GetIncludePaths(); mPFile->SetIncludes(includes); QStringList defines = dlg.GetDefines(); mPFile->SetDefines(defines); + QStringList paths = dlg.GetPaths(); + mPFile->SetCheckPaths(paths); bool writeSuccess = mPFile->Write(); if (!writeSuccess) { diff --git a/gui/project.h b/gui/project.h index 146375d88..673963c3a 100644 --- a/gui/project.h +++ b/gui/project.h @@ -62,6 +62,15 @@ public: */ void Create(); + /** + * @brief Return current project file. + * @return project file. + */ + ProjectFile * GetProjectFile() const + { + return mPFile; + } + private: QString mFilename; diff --git a/gui/projectfile.cpp b/gui/projectfile.cpp index 9cf2cc7fb..d6c6e5037 100644 --- a/gui/projectfile.cpp +++ b/gui/projectfile.cpp @@ -32,6 +32,11 @@ static const char DirNameAttrib[] = "name"; static const char DefinesElementName[] = "defines"; static const char DefineName[] = "define"; static const char DefineNameAttrib[] = "name"; +static const char PathsElementName[] = "paths"; +static const char PathName[] = "dir"; +static const char PathNameAttrib[] = "name"; +static const char RootPathName[] = "root"; +static const char RootPathNameAttrib[] = "name"; ProjectFile::ProjectFile(QObject *parent) : QObject(parent) @@ -66,6 +71,13 @@ bool ProjectFile::Read(const QString &filename) insideProject = true; projectTagFound = true; } + // Read root path from inside project element + if (insideProject && xmlReader.name() == RootPathName) + ReadRootPath(xmlReader); + + // Find paths to check from inside project element + if (insideProject && xmlReader.name() == PathsElementName) + ReadCheckPaths(xmlReader); // Find include directory from inside project element if (insideProject && xmlReader.name() == IncludDirElementName) @@ -113,6 +125,19 @@ QStringList ProjectFile::GetDefines() const return mDefines; } +QStringList ProjectFile::GetCheckPaths() const +{ + return mPaths; +} + +void ProjectFile::ReadRootPath(QXmlStreamReader &reader) +{ + QXmlStreamAttributes attribs = reader.attributes(); + QString name = attribs.value("", RootPathNameAttrib).toString(); + if (!name.isEmpty()) + mRootPath = name; +} + void ProjectFile::ReadIncludeDirs(QXmlStreamReader &reader) { QXmlStreamReader::TokenType type; @@ -196,6 +221,48 @@ void ProjectFile::ReadDefines(QXmlStreamReader &reader) while (!allRead); } +void ProjectFile::ReadCheckPaths(QXmlStreamReader &reader) +{ + QXmlStreamReader::TokenType type; + bool allRead = false; + do + { + type = reader.readNext(); + switch (type) + { + case QXmlStreamReader::StartElement: + + // Read dir-elements + if (reader.name().toString() == PathName) + { + QXmlStreamAttributes attribs = reader.attributes(); + QString name = attribs.value("", PathNameAttrib).toString(); + if (!name.isEmpty()) + mPaths << name; + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() == PathsElementName) + allRead = true; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + while (!allRead); +} + void ProjectFile::SetIncludes(QStringList includes) { mIncludeDirs = includes; @@ -206,6 +273,11 @@ void ProjectFile::SetDefines(QStringList defines) mDefines = defines; } +void ProjectFile::SetCheckPaths(QStringList paths) +{ + mPaths = paths; +} + bool ProjectFile::Write(const QString &filename) { if (!filename.isEmpty()) @@ -221,6 +293,13 @@ bool ProjectFile::Write(const QString &filename) xmlWriter.writeStartElement(ProjectElementName); xmlWriter.writeAttribute(ProjectVersionAttrib, ProjectFileVersion); + if (!mRootPath.isEmpty()) + { + xmlWriter.writeStartElement(RootPathName); + xmlWriter.writeAttribute(RootPathNameAttrib, mRootPath); + xmlWriter.writeEndElement(); + } + if (!mIncludeDirs.isEmpty()) { xmlWriter.writeStartElement(IncludDirElementName); @@ -246,6 +325,20 @@ bool ProjectFile::Write(const QString &filename) } xmlWriter.writeEndElement(); } + + if (!mPaths.isEmpty()) + { + xmlWriter.writeStartElement(PathsElementName); + QString path; + foreach(path, mPaths) + { + xmlWriter.writeStartElement(PathName); + xmlWriter.writeAttribute(PathNameAttrib, path); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndDocument(); file.close(); return true; diff --git a/gui/projectfile.h b/gui/projectfile.h index afbcb8f85..2243a7c20 100644 --- a/gui/projectfile.h +++ b/gui/projectfile.h @@ -47,6 +47,15 @@ public: */ bool Read(const QString &filename = QString()); + /** + * @brief Get project root path. + * @return project root path. + */ + QString GetRootPath() const + { + return mRootPath; + } + /** * @brief Get list of include directories. * @return list of directories. @@ -59,6 +68,21 @@ public: */ QStringList GetDefines() const; + /** + * @brief Get list of paths to check. + * @return list of paths. + */ + QStringList GetCheckPaths() const; + + /** + * @brief Set project root path. + * @param rootpath new project root path. + */ + void SetRootPath(const QString &rootpath) + { + mRootPath = rootpath; + } + /** * @brief Set list of includes. * @param includes List of defines. @@ -71,6 +95,12 @@ public: */ void SetDefines(QStringList defines); + /** + * @brief Set list of paths to check. + * @param defines List of paths. + */ + void SetCheckPaths(QStringList paths); + /** * @brief Write project file (to disk). * @param filename Filename to use. @@ -87,6 +117,13 @@ public: } protected: + + /** + * @brief Read optional root path from XML. + * @param reader XML stream reader. + */ + void ReadRootPath(QXmlStreamReader &reader); + /** * @brief Read list of include directories from XML. * @param reader XML stream reader. @@ -99,6 +136,12 @@ protected: */ void ReadDefines(QXmlStreamReader &reader); + /** + * @brief Read list paths to check. + * @param reader XML stream reader. + */ + void ReadCheckPaths(QXmlStreamReader &reader); + private: /** @@ -106,6 +149,14 @@ private: */ QString mFilename; + /** + * @brief Root path (optional) for the project. + * This is the project root path. If it is present then all relative paths in + * the project file are relative to this path. Otherwise paths are relative + * to project file's path. + */ + QString mRootPath; + /** * @brief List of include directories used to search include files. */ @@ -115,6 +166,11 @@ private: * @brief List of defines. */ QStringList mDefines; + + /** + * @brief List of paths to check. + */ + QStringList mPaths; }; /// @} #endif // PROJECT_FILE_H diff --git a/gui/projectfile.txt b/gui/projectfile.txt index f22c49a82..2724e1eba 100644 --- a/gui/projectfile.txt +++ b/gui/projectfile.txt @@ -13,7 +13,12 @@ program. The format is: + + + + + @@ -24,4 +29,18 @@ program. The format is: +where: + - optional root element defines the root directory for the project. All + relative paths are considered to be relative to this path. If the root + element is missing or it contains "." as value then the project file's + location is considered to be the root path. +- paths element contains a list of checked paths. The paths can be relative or + absolute paths. +- indludedir element contains a list of additional include paths. These + include paths are used when finding local include files ("#include "file.h") + for source files. The paths can be relative or absolute paths. It is highly + recommended that relative paths are used for paths inside the project root + folder for better portability. +- defines element contains a list of C/C++ preprocessor defines. + See also gui.cppcheck file in gui-directory of cppcheck sources. diff --git a/gui/projectfile.ui b/gui/projectfile.ui index fd005658e..370b165b8 100644 --- a/gui/projectfile.ui +++ b/gui/projectfile.ui @@ -7,13 +7,47 @@ 0 0 400 - 112 + 131 Project File + + + + + + Project: + + + mEditProjectRoot + + + + + + + + + + + + + + Paths: + + + mEditPaths + + + + + + + + @@ -73,6 +107,13 @@ + + mEditProjectRoot + mEditPaths + mEditIncludePaths + mEditDefines + mButtons + diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index 319505380..08f7042c9 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -33,6 +33,13 @@ ProjectFileDialog::ProjectFileDialog(const QString &path, QWidget *parent) connect(mUI.mButtons, SIGNAL(accepted()), this, SLOT(accept())); } +QString ProjectFileDialog::GetRootPath() const +{ + QString root = mUI.mEditProjectRoot->text(); + root = root.trimmed(); + return root; +} + QStringList ProjectFileDialog::GetIncludePaths() const { QString include = mUI.mEditIncludePaths->text(); @@ -63,6 +70,26 @@ QStringList ProjectFileDialog::GetDefines() const return defines; } +QStringList ProjectFileDialog::GetPaths() const +{ + QString path = mUI.mEditPaths->text(); + QStringList paths; + if (!path.isEmpty()) + { + path = path.trimmed(); + if (path.indexOf(';') != -1) + paths = path.split(";"); + else + paths.append(path); + } + return paths; +} + +void ProjectFileDialog::SetRootPath(const QString &root) +{ + mUI.mEditProjectRoot->setText(root); +} + void ProjectFileDialog::SetIncludepaths(const QStringList &includes) { QString includestr; @@ -92,3 +119,18 @@ void ProjectFileDialog::SetDefines(const QStringList &defines) definestr = definestr.left(definestr.length() - 1); mUI.mEditDefines->setText(definestr); } + +void ProjectFileDialog::SetPaths(const QStringList &paths) +{ + QString pathstr; + QString path; + foreach(path, paths) + { + pathstr += path; + pathstr += ";"; + } + // Remove ; from the end of the string + if (pathstr.endsWith(';')) + pathstr = pathstr.left(pathstr.length() - 1); + mUI.mEditPaths->setText(pathstr); +} diff --git a/gui/projectfiledialog.h b/gui/projectfiledialog.h index c3252fe35..783fbfb63 100644 --- a/gui/projectfiledialog.h +++ b/gui/projectfiledialog.h @@ -21,6 +21,7 @@ #include #include +#include #include "ui_projectfile.h" @@ -39,6 +40,12 @@ class ProjectFileDialog : public QDialog public: ProjectFileDialog(const QString &path, QWidget *parent = 0); + /** + * @brief Return project root path from the dialog control. + * @return Project root path. + */ + QString GetRootPath() const; + /** * @brief Return include paths from the dialog control. * @return List of include paths. @@ -51,6 +58,18 @@ public: */ QStringList GetDefines() const; + /** + * @brief Return check paths from the dialog control. + * @return List of check paths. + */ + QStringList GetPaths() const; + + /** + * @brief Set project root path to dialog control. + * @param root Project root path to set to dialog control. + */ + void SetRootPath(const QString &root); + /** * @brief Set include paths to dialog control. * @param includes List of include paths to set to dialog control. @@ -63,6 +82,12 @@ public: */ void SetDefines(const QStringList &defines); + /** + * @brief Set check paths to dialog control. + * @param paths List of path names to set to dialog control. + */ + void SetPaths(const QStringList &paths); + private: Ui::ProjectFile mUI; };