/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2016 Cppcheck team. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "cppcheck.h" #include "applicationlist.h" #include "aboutdialog.h" #include "common.h" #include "threadhandler.h" #include "fileviewdialog.h" #include "projectfile.h" #include "project.h" #include "report.h" #include "scratchpad.h" #include "statsdialog.h" #include "settingsdialog.h" #include "threadresult.h" #include "translationhandler.h" #include "logview.h" #include "filelist.h" #include "showtypes.h" #include "librarydialog.h" static const QString OnlineHelpURL("http://cppcheck.sourceforge.net/manual.html"); MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) : mSettings(settings), mApplications(new ApplicationList(this)), mTranslation(th), mLogView(NULL), mScratchPad(NULL), mProject(NULL), mPlatformActions(new QActionGroup(this)), mCStandardActions(new QActionGroup(this)), mCppStandardActions(new QActionGroup(this)), mSelectLanguageActions(new QActionGroup(this)), mExiting(false) { mUI.setupUi(this); mThread = new ThreadHandler(this); mUI.mResults->Initialize(mSettings, mApplications, mThread); // Filter timer to delay filtering results slightly while typing mFilterTimer = new QTimer(this); mFilterTimer->setInterval(500); mFilterTimer->setSingleShot(true); connect(mFilterTimer, SIGNAL(timeout()), this, SLOT(FilterResults())); // "Filter" toolbar mLineEditFilter = new QLineEdit(mUI.mToolBarFilter); mLineEditFilter->setPlaceholderText(tr("Quick Filter:")); mUI.mToolBarFilter->addWidget(mLineEditFilter); connect(mLineEditFilter, SIGNAL(textChanged(const QString&)), mFilterTimer, SLOT(start())); connect(mLineEditFilter, SIGNAL(returnPressed()), this, SLOT(FilterResults())); connect(mUI.mActionPrint, SIGNAL(triggered()), mUI.mResults, SLOT(Print())); connect(mUI.mActionPrintPreview, SIGNAL(triggered()), mUI.mResults, SLOT(PrintPreview())); connect(mUI.mActionQuit, SIGNAL(triggered()), this, SLOT(close())); connect(mUI.mActionCheckFiles, SIGNAL(triggered()), this, SLOT(CheckFiles())); connect(mUI.mActionCheckDirectory, SIGNAL(triggered()), this, SLOT(CheckDirectory())); connect(mUI.mActionSettings, SIGNAL(triggered()), this, SLOT(ProgramSettings())); connect(mUI.mActionClearResults, SIGNAL(triggered()), this, SLOT(ClearResults())); connect(mUI.mActionOpenXML, SIGNAL(triggered()), this, SLOT(OpenResults())); connect(mUI.mActionShowStyle, SIGNAL(toggled(bool)), this, SLOT(ShowStyle(bool))); connect(mUI.mActionShowErrors, SIGNAL(toggled(bool)), this, SLOT(ShowErrors(bool))); connect(mUI.mActionShowWarnings, SIGNAL(toggled(bool)), this, SLOT(ShowWarnings(bool))); connect(mUI.mActionShowPortability, SIGNAL(toggled(bool)), this, SLOT(ShowPortability(bool))); connect(mUI.mActionShowPerformance, SIGNAL(toggled(bool)), this, SLOT(ShowPerformance(bool))); connect(mUI.mActionShowInformation, SIGNAL(toggled(bool)), this, SLOT(ShowInformation(bool))); connect(mUI.mActionCheckAll, SIGNAL(triggered()), this, SLOT(CheckAll())); connect(mUI.mActionUncheckAll, SIGNAL(triggered()), this, SLOT(UncheckAll())); connect(mUI.mActionCollapseAll, SIGNAL(triggered()), mUI.mResults, SLOT(CollapseAllResults())); connect(mUI.mActionExpandAll, SIGNAL(triggered()), mUI.mResults, SLOT(ExpandAllResults())); connect(mUI.mActionShowHidden, SIGNAL(triggered()), mUI.mResults, SLOT(ShowHiddenResults())); connect(mUI.mActionViewLog, SIGNAL(triggered()), this, SLOT(ShowLogView())); connect(mUI.mActionViewStats, SIGNAL(triggered()), this, SLOT(ShowStatistics())); connect(mUI.mActionLibraryEditor, SIGNAL(triggered()), this, SLOT(ShowLibraryEditor())); connect(mUI.mActionRecheckModified, SIGNAL(triggered()), this, SLOT(ReCheckModified())); connect(mUI.mActionRecheckAll, SIGNAL(triggered()), this, SLOT(ReCheckAll())); connect(mUI.mActionStop, SIGNAL(triggered()), this, SLOT(StopChecking())); connect(mUI.mActionSave, SIGNAL(triggered()), this, SLOT(Save())); // About menu connect(mUI.mActionAbout, SIGNAL(triggered()), this, SLOT(About())); connect(mUI.mActionLicense, SIGNAL(triggered()), this, SLOT(ShowLicense())); // View > Toolbar menu connect(mUI.mActionToolBarMain, SIGNAL(toggled(bool)), this, SLOT(ToggleMainToolBar())); connect(mUI.mActionToolBarView, SIGNAL(toggled(bool)), this, SLOT(ToggleViewToolBar())); connect(mUI.mActionToolBarFilter, SIGNAL(toggled(bool)), this, SLOT(ToggleFilterToolBar())); connect(mUI.mActionAuthors, SIGNAL(triggered()), this, SLOT(ShowAuthors())); connect(mThread, SIGNAL(Done()), this, SLOT(CheckDone())); connect(mUI.mResults, SIGNAL(GotResults()), this, SLOT(ResultsAdded())); connect(mUI.mResults, SIGNAL(ResultsHidden(bool)), mUI.mActionShowHidden, SLOT(setEnabled(bool))); connect(mUI.mResults, SIGNAL(CheckSelected(QStringList)), this, SLOT(PerformSelectedFilesCheck(QStringList))); connect(mUI.mMenuView, SIGNAL(aboutToShow()), this, SLOT(AboutToShowViewMenu())); // File menu connect(mUI.mActionNewProjectFile, SIGNAL(triggered()), this, SLOT(NewProjectFile())); connect(mUI.mActionOpenProjectFile, SIGNAL(triggered()), this, SLOT(OpenProjectFile())); connect(mUI.mActionShowScratchpad, SIGNAL(triggered()), this, SLOT(ShowScratchpad())); connect(mUI.mActionCloseProjectFile, SIGNAL(triggered()), this, SLOT(CloseProjectFile())); connect(mUI.mActionEditProjectFile, SIGNAL(triggered()), this, SLOT(EditProjectFile())); connect(mUI.mActionHelpContents, SIGNAL(triggered()), this, SLOT(OpenHelpContents())); LoadSettings(); mThread->Initialize(mUI.mResults); FormatAndSetTitle(); EnableCheckButtons(true); mUI.mActionPrint->setShortcut(QKeySequence::Print); mUI.mActionPrint->setEnabled(false); mUI.mActionPrintPreview->setEnabled(false); mUI.mActionClearResults->setEnabled(false); mUI.mActionSave->setEnabled(false); mUI.mActionRecheckModified->setEnabled(false); mUI.mActionRecheckAll->setEnabled(false); EnableProjectOpenActions(true); EnableProjectActions(false); // Must setup MRU menu before CLI param handling as it can load a // project file and update MRU menu. for (int i = 0; i < MaxRecentProjects; ++i) { mRecentProjectActs[i] = new QAction(this); mRecentProjectActs[i]->setVisible(false); connect(mRecentProjectActs[i], SIGNAL(triggered()), this, SLOT(OpenRecentProject())); } mRecentProjectActs[MaxRecentProjects] = NULL; // The separator mUI.mActionProjectMRU->setVisible(false); UpdateMRUMenuItems(); QStringList args = QCoreApplication::arguments(); //Remove the application itself args.removeFirst(); if (!args.isEmpty()) { HandleCLIParams(args); } for (int i = 0; i < mPlatforms.getCount(); i++) { Platform plat = mPlatforms.mPlatforms[i]; QAction *act = new QAction(this); plat.mActMainWindow = act; mPlatforms.mPlatforms[i] = plat; act->setText(plat.mTitle); act->setData(plat.mType); act->setCheckable(true); act->setActionGroup(mPlatformActions); mUI.mMenuCheck->insertAction(mUI.mActionPlatforms, act); connect(act, SIGNAL(triggered()), this, SLOT(SelectPlatform())); } mUI.mActionC89->setActionGroup(mCStandardActions); mUI.mActionC99->setActionGroup(mCStandardActions); mUI.mActionC11->setActionGroup(mCStandardActions); mUI.mActionCpp03->setActionGroup(mCppStandardActions); mUI.mActionCpp11->setActionGroup(mCppStandardActions); mUI.mActionEnforceC->setActionGroup(mSelectLanguageActions); mUI.mActionEnforceCpp->setActionGroup(mSelectLanguageActions); mUI.mActionAutoDetectLanguage->setActionGroup(mSelectLanguageActions); // For Windows platforms default to Win32 checked platform. // For other platforms default to unspecified/default which means the // platform Cppcheck GUI was compiled on. #if defined(_WIN32) const Settings::PlatformType defaultPlat = Settings::Win32W; #else const Settings::PlatformType defaultPlat = Settings::Unspecified; #endif Platform &plat = mPlatforms.get((Settings::PlatformType)mSettings->value(SETTINGS_CHECKED_PLATFORM, defaultPlat).toInt()); plat.mActMainWindow->setChecked(true); } MainWindow::~MainWindow() { delete mLogView; delete mProject; delete mScratchPad; } void MainWindow::HandleCLIParams(const QStringList ¶ms) { int index; if (params.contains("-p")) { index = params.indexOf("-p"); if ((index + 1) < params.length()) LoadProjectFile(params[index + 1]); } else if ((index = params.indexOf(QRegExp(".*\\.cppcheck$", Qt::CaseInsensitive), 0)) >= 0 && index < params.length() && QFile(params[index]).exists()) { LoadProjectFile(params[index]); } else if ((index = params.indexOf(QRegExp(".*\\.xml$", Qt::CaseInsensitive), 0)) >= 0 && index < params.length() && QFile(params[index]).exists()) { LoadResults(params[index]); } else if (params.contains("-l")) { QString logFile; index = params.indexOf("-l"); if ((index + 1) < params.length()) logFile = params[index + 1]; if (params.contains("-d")) { QString checkedDir; index = params.indexOf("-d"); if ((index + 1) < params.length()) checkedDir = params[index + 1]; LoadResults(logFile, checkedDir); } else { LoadResults(logFile); } } else DoCheckFiles(params); } void MainWindow::LoadSettings() { // Window/dialog sizes if (mSettings->value(SETTINGS_WINDOW_MAXIMIZED, false).toBool()) { showMaximized(); } else { resize(mSettings->value(SETTINGS_WINDOW_WIDTH, 800).toInt(), mSettings->value(SETTINGS_WINDOW_HEIGHT, 600).toInt()); } ShowTypes *types = mUI.mResults->GetShowTypes(); mUI.mActionShowStyle->setChecked(types->isShown(ShowTypes::ShowStyle)); mUI.mActionShowErrors->setChecked(types->isShown(ShowTypes::ShowErrors)); mUI.mActionShowWarnings->setChecked(types->isShown(ShowTypes::ShowWarnings)); mUI.mActionShowPortability->setChecked(types->isShown(ShowTypes::ShowPortability)); mUI.mActionShowPerformance->setChecked(types->isShown(ShowTypes::ShowPerformance)); mUI.mActionShowInformation->setChecked(types->isShown(ShowTypes::ShowInformation)); const bool stdCpp03 = mSettings->value(SETTINGS_STD_CPP03, false).toBool(); mUI.mActionCpp03->setChecked(stdCpp03); const bool stdCpp11 = mSettings->value(SETTINGS_STD_CPP11, true).toBool(); mUI.mActionCpp11->setChecked(stdCpp11 || !stdCpp03); const bool stdC89 = mSettings->value(SETTINGS_STD_C89, false).toBool(); mUI.mActionC89->setChecked(stdC89); const bool stdC11 = mSettings->value(SETTINGS_STD_C11, false).toBool(); mUI.mActionC11->setChecked(stdC11); const bool stdC99 = mSettings->value(SETTINGS_STD_C99, true).toBool(); mUI.mActionC99->setChecked(stdC99 || (!stdC89 && !stdC11)); const bool stdPosix = mSettings->value(SETTINGS_STD_POSIX, false).toBool(); mUI.mActionPosix->setChecked(stdPosix); // Main window settings const bool showMainToolbar = mSettings->value(SETTINGS_TOOLBARS_MAIN_SHOW, true).toBool(); mUI.mActionToolBarMain->setChecked(showMainToolbar); mUI.mToolBarMain->setVisible(showMainToolbar); const bool showViewToolbar = mSettings->value(SETTINGS_TOOLBARS_VIEW_SHOW, true).toBool(); mUI.mActionToolBarView->setChecked(showViewToolbar); mUI.mToolBarView->setVisible(showViewToolbar); const bool showFilterToolbar = mSettings->value(SETTINGS_TOOLBARS_FILTER_SHOW, true).toBool(); mUI.mActionToolBarFilter->setChecked(showFilterToolbar); mUI.mToolBarFilter->setVisible(showFilterToolbar); Settings::Language enforcedLanguage = (Settings::Language)mSettings->value(SETTINGS_ENFORCED_LANGUAGE, 0).toInt(); if (enforcedLanguage == Settings::CPP) mUI.mActionEnforceCpp->setChecked(true); else if (enforcedLanguage == Settings::C) mUI.mActionEnforceC->setChecked(true); else mUI.mActionAutoDetectLanguage->setChecked(true); bool succeeded = mApplications->LoadSettings(); if (!succeeded) { const QString msg = tr("There was a problem with loading the editor application settings.\n\n" "This is probably because the settings were changed between the Cppcheck versions. " "Please check (and fix) the editor application settings, otherwise the editor " "program might not start correctly."); QMessageBox msgBox(QMessageBox::Warning, tr("Cppcheck"), msg, QMessageBox::Ok, this); msgBox.exec(); } } void MainWindow::SaveSettings() const { // Window/dialog sizes mSettings->setValue(SETTINGS_WINDOW_WIDTH, size().width()); mSettings->setValue(SETTINGS_WINDOW_HEIGHT, size().height()); mSettings->setValue(SETTINGS_WINDOW_MAXIMIZED, isMaximized()); // Show * states mSettings->setValue(SETTINGS_SHOW_STYLE, mUI.mActionShowStyle->isChecked()); mSettings->setValue(SETTINGS_SHOW_ERRORS, mUI.mActionShowErrors->isChecked()); mSettings->setValue(SETTINGS_SHOW_WARNINGS, mUI.mActionShowWarnings->isChecked()); mSettings->setValue(SETTINGS_SHOW_PORTABILITY, mUI.mActionShowPortability->isChecked()); mSettings->setValue(SETTINGS_SHOW_PERFORMANCE, mUI.mActionShowPerformance->isChecked()); mSettings->setValue(SETTINGS_SHOW_INFORMATION, mUI.mActionShowInformation->isChecked()); mSettings->setValue(SETTINGS_STD_CPP03, mUI.mActionCpp03->isChecked()); mSettings->setValue(SETTINGS_STD_CPP11, mUI.mActionCpp11->isChecked()); mSettings->setValue(SETTINGS_STD_C89, mUI.mActionC89->isChecked()); mSettings->setValue(SETTINGS_STD_C99, mUI.mActionC99->isChecked()); mSettings->setValue(SETTINGS_STD_C11, mUI.mActionC11->isChecked()); mSettings->setValue(SETTINGS_STD_POSIX, mUI.mActionPosix->isChecked()); // Main window settings mSettings->setValue(SETTINGS_TOOLBARS_MAIN_SHOW, mUI.mToolBarMain->isVisible()); mSettings->setValue(SETTINGS_TOOLBARS_VIEW_SHOW, mUI.mToolBarView->isVisible()); mSettings->setValue(SETTINGS_TOOLBARS_FILTER_SHOW, mUI.mToolBarFilter->isVisible()); if (mUI.mActionEnforceCpp->isChecked()) mSettings->setValue(SETTINGS_ENFORCED_LANGUAGE, Settings::CPP); else if (mUI.mActionEnforceC->isChecked()) mSettings->setValue(SETTINGS_ENFORCED_LANGUAGE, Settings::C); else mSettings->setValue(SETTINGS_ENFORCED_LANGUAGE, Settings::None); mApplications->SaveSettings(); mSettings->setValue(SETTINGS_LANGUAGE, mTranslation->GetCurrentLanguage()); mUI.mResults->SaveSettings(mSettings); } void MainWindow::DoCheckFiles(const QStringList &files) { if (files.isEmpty()) { return; } ClearResults(); FileList pathList; pathList.AddPathList(files); if (mProject) pathList.AddExcludeList(mProject->GetProjectFile()->GetExcludedPaths()); QStringList fileNames = pathList.GetFileList(); mUI.mResults->Clear(true); mThread->ClearFiles(); if (fileNames.isEmpty()) { QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), tr("No suitable files found to check!"), QMessageBox::Ok, this); msg.exec(); return; } mUI.mResults->CheckingStarted(fileNames.count()); mThread->SetFiles(fileNames); QDir inf(mCurrentDirectory); const QString checkPath = inf.canonicalPath(); SetPath(SETTINGS_LAST_CHECK_PATH, checkPath); CheckLockDownUI(); // lock UI while checking mUI.mResults->SetCheckDirectory(checkPath); Settings checkSettings = GetCppcheckSettings(); if (mProject) qDebug() << "Checking project file" << mProject->GetProjectFile()->GetFilename(); mThread->SetCheckFiles(true); mThread->Check(checkSettings, true); } void MainWindow::CheckCode(const QString& code, const QString& filename) { // Initialize dummy ThreadResult as ErrorLogger ThreadResult result; result.SetFiles(QStringList(filename)); connect(&result, SIGNAL(Progress(int, const QString&)), mUI.mResults, SLOT(Progress(int, const QString&))); connect(&result, SIGNAL(Error(const ErrorItem &)), mUI.mResults, SLOT(Error(const ErrorItem &))); connect(&result, SIGNAL(Log(const QString &)), this, SLOT(Log(const QString &))); connect(&result, SIGNAL(DebugError(const ErrorItem &)), this, SLOT(DebugError(const ErrorItem &))); // Create CppCheck instance CppCheck cppcheck(result, true); cppcheck.settings() = GetCppcheckSettings(); // Check CheckLockDownUI(); ClearResults(); mUI.mResults->CheckingStarted(1); cppcheck.check(filename.toStdString(), code.toStdString()); CheckDone(); } 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 // QFileDialog::getExistingDirectory() because they show native Windows // selection dialog which is a lot more usable than Qt:s own dialog. if (mode == QFileDialog::ExistingFiles) { selected = QFileDialog::getOpenFileNames(this, tr("Select files to check"), GetPath(SETTINGS_LAST_CHECK_PATH), tr("C/C++ Source (%1)").arg(FileList::GetDefaultFilters().join(" "))); if (selected.isEmpty()) mCurrentDirectory.clear(); else { QFileInfo inf(selected[0]); mCurrentDirectory = inf.absolutePath(); } FormatAndSetTitle(); } else if (mode == QFileDialog::DirectoryOnly) { QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory to check"), GetPath(SETTINGS_LAST_CHECK_PATH)); if (!dir.isEmpty()) { qDebug() << "Setting current directory to: " << dir; mCurrentDirectory = dir; selected.append(dir); dir = QDir::toNativeSeparators(dir); FormatAndSetTitle(dir); } } SetPath(SETTINGS_LAST_CHECK_PATH, mCurrentDirectory); return selected; } void MainWindow::CheckFiles() { DoCheckFiles(SelectFilesToCheck(QFileDialog::ExistingFiles)); } void MainWindow::CheckDirectory() { QStringList dir = SelectFilesToCheck(QFileDialog::DirectoryOnly); if (dir.isEmpty()) return; QDir checkDir(dir[0]); QStringList filters; filters << "*.cppcheck"; checkDir.setFilter(QDir::Files | QDir::Readable); checkDir.setNameFilters(filters); QStringList projFiles = checkDir.entryList(); if (!projFiles.empty()) { if (projFiles.size() == 1) { // If one project file found, suggest loading it QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Cppcheck")); const QString msg(tr("Found project file: %1\n\nDo you want to " "load this project file instead?").arg(projFiles[0])); msgBox.setText(msg); msgBox.setIcon(QMessageBox::Warning); msgBox.addButton(QMessageBox::Yes); msgBox.addButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int dlgResult = msgBox.exec(); if (dlgResult == QMessageBox::Yes) { QString path = checkDir.canonicalPath(); if (!path.endsWith("/")) path += "/"; path += projFiles[0]; LoadProjectFile(path); } else { DoCheckFiles(dir); } } else { // If multiple project files found inform that there are project // files also available. QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Cppcheck")); const QString msg(tr("Found project files from the directory.\n\n" "Do you want to proceed checking without " "using any of these project files?")); msgBox.setText(msg); msgBox.setIcon(QMessageBox::Warning); msgBox.addButton(QMessageBox::Yes); msgBox.addButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int dlgResult = msgBox.exec(); if (dlgResult == QMessageBox::Yes) { DoCheckFiles(dir); } } } else { DoCheckFiles(dir); } } void MainWindow::AddIncludeDirs(const QStringList &includeDirs, Settings &result) { QString dir; foreach (dir, includeDirs) { 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()); } } Library::Error MainWindow::LoadLibrary(Library *library, QString filename) { Library::Error ret; // Try to load the library from the project folder.. if (mProject) { QString path = QFileInfo(mProject->GetProjectFile()->GetFilename()).canonicalPath(); ret = library->load(NULL, (path+"/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; } // Try to load the library from the application folder.. const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); ret = library->load(NULL, (appPath+"/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; ret = library->load(NULL, (appPath+"/cfg/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; // Try to load the library from the cfg subfolder.. const QString datadir = mSettings->value("DATADIR", QString()).toString(); if (!datadir.isEmpty()) { ret = library->load(NULL, (datadir+"/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; ret = library->load(NULL, (datadir+"/cfg/"+filename).toLatin1()); if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) return ret; } return ret; } bool MainWindow::TryLoadLibrary(Library *library, QString filename) { const Library::Error error = LoadLibrary(library, filename); if (error.errorcode != Library::ErrorCode::OK) { if (error.errorcode == Library::UNKNOWN_ELEMENT) { QMessageBox::information(this, tr("Information"), tr("The library '%1' contains unknown elements:\n%2").arg(filename).arg(error.reason.c_str())); return true; } QString errmsg; switch (error.errorcode) { case Library::ErrorCode::OK: break; case Library::ErrorCode::FILE_NOT_FOUND: errmsg = tr("File not found"); break; case Library::ErrorCode::BAD_XML: errmsg = tr("Bad XML"); break; case Library::ErrorCode::MISSING_ATTRIBUTE: errmsg = tr("Missing attribute"); break; case Library::ErrorCode::BAD_ATTRIBUTE_VALUE: errmsg = tr("Bad attribute value"); break; case Library::ErrorCode::UNSUPPORTED_FORMAT: errmsg = tr("Unsupported format"); break; case Library::ErrorCode::DUPLICATE_PLATFORM_TYPE: errmsg = tr("Duplicate platform type"); break; case Library::ErrorCode::PLATFORM_TYPE_REDEFINED: errmsg = tr("Platform type redefined"); break; case Library::ErrorCode::UNKNOWN_ELEMENT: errmsg = tr("Unknown element"); break; default: errmsg = tr("Unknown issue"); break; } if (!error.reason.empty()) errmsg += " '" + QString::fromStdString(error.reason) + "'"; QMessageBox::information(this, tr("Information"), tr("Failed to load the selected library '%1'.\n%2").arg(filename).arg(errmsg)); return false; } return true; } Settings MainWindow::GetCppcheckSettings() { SaveSettings(); // Save settings Settings result; // If project file loaded, read settings from it if (mProject) { ProjectFile *pfile = mProject->GetProjectFile(); QStringList dirs = pfile->GetIncludeDirs(); AddIncludeDirs(dirs, result); const QStringList defines = pfile->GetDefines(); QString define; foreach (define, defines) { if (!result.userDefines.empty()) result.userDefines += ";"; result.userDefines += define.toStdString(); } const QStringList libraries = pfile->GetLibraries(); foreach (QString library, libraries) { const QString filename = library + ".cfg"; TryLoadLibrary(&result.library, filename); } const QStringList suppressions = pfile->GetSuppressions(); foreach (QString suppression, suppressions) { result.nomsg.addSuppressionLine(suppression.toStdString()); } // Only check the given -D configuration if (!defines.isEmpty()) result._maxConfigs = 1; } // Include directories (and files) are searched in listed order. // Global include directories must be added AFTER the per project include // directories so per project include directories can override global ones. const QString globalIncludes = mSettings->value(SETTINGS_GLOBAL_INCLUDE_PATHS).toString(); if (!globalIncludes.isEmpty()) { QStringList includes = globalIncludes.split(";"); AddIncludeDirs(includes, result); } result.addEnabled("warning"); result.addEnabled("style"); result.addEnabled("performance"); result.addEnabled("portability"); result.addEnabled("information"); result.addEnabled("missingInclude"); result.debug = false; result.debugwarnings = mSettings->value(SETTINGS_SHOW_DEBUG_WARNINGS, false).toBool(); result.quiet = false; result._verbose = true; result._force = mSettings->value(SETTINGS_CHECK_FORCE, 1).toBool(); result._xml = false; result._jobs = mSettings->value(SETTINGS_CHECK_THREADS, 1).toInt(); result._inlineSuppressions = mSettings->value(SETTINGS_INLINE_SUPPRESSIONS, false).toBool(); result.inconclusive = mSettings->value(SETTINGS_INCONCLUSIVE_ERRORS, false).toBool(); result.platformType = (Settings::PlatformType) mSettings->value(SETTINGS_CHECKED_PLATFORM, 0).toInt(); result.standards.cpp = mSettings->value(SETTINGS_STD_CPP11, true).toBool() ? Standards::CPP11 : Standards::CPP03; result.standards.c = mSettings->value(SETTINGS_STD_C99, true).toBool() ? Standards::C99 : (mSettings->value(SETTINGS_STD_C11, false).toBool() ? Standards::C11 : Standards::C89); result.standards.posix = mSettings->value(SETTINGS_STD_POSIX, false).toBool(); result.enforcedLang = (Settings::Language)mSettings->value(SETTINGS_ENFORCED_LANGUAGE, 0).toInt(); const bool std = TryLoadLibrary(&result.library, "std.cfg"); bool posix = true; if (result.standards.posix) posix = TryLoadLibrary(&result.library, "posix.cfg"); bool windows = true; if (result.platformType == Settings::Win32A || result.platformType == Settings::Win32W || result.platformType == Settings::Win64) windows = TryLoadLibrary(&result.library, "windows.cfg"); if (!std || !posix || !windows) QMessageBox::critical(this, tr("Error"), tr("Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir= at the command line to specify where this file is located.").arg(!std ? "std.cfg" : !posix ? "posix.cfg" : "windows.cfg")); if (result._jobs <= 1) { result._jobs = 1; } return result; } void MainWindow::CheckDone() { if (mExiting) { close(); return; } mUI.mResults->CheckingFinished(); EnableCheckButtons(true); mUI.mActionSettings->setEnabled(true); mUI.mActionOpenXML->setEnabled(true); EnableProjectActions(true); EnableProjectOpenActions(true); mPlatformActions->setEnabled(true); mCStandardActions->setEnabled(true); mCppStandardActions->setEnabled(true); mSelectLanguageActions->setEnabled(true); mUI.mActionPosix->setEnabled(true); if (mScratchPad) mScratchPad->setEnabled(true); if (mUI.mResults->HasResults()) { mUI.mActionClearResults->setEnabled(true); mUI.mActionSave->setEnabled(true); mUI.mActionPrint->setEnabled(true); mUI.mActionPrintPreview->setEnabled(true); } for (int i = 0; i < MaxRecentProjects + 1; i++) { if (mRecentProjectActs[i] != NULL) mRecentProjectActs[i]->setEnabled(true); } // Notify user - if the window is not active - that check is ready QApplication::alert(this, 3000); ShowStatistics(); } void MainWindow::CheckLockDownUI() { EnableCheckButtons(false); mUI.mActionSettings->setEnabled(false); mUI.mActionOpenXML->setEnabled(false); EnableProjectActions(false); EnableProjectOpenActions(false); mPlatformActions->setEnabled(false); mCStandardActions->setEnabled(false); mCppStandardActions->setEnabled(false); mSelectLanguageActions->setEnabled(false); mUI.mActionPosix->setEnabled(false); if (mScratchPad) mScratchPad->setEnabled(false); for (int i = 0; i < MaxRecentProjects + 1; i++) { if (mRecentProjectActs[i] != NULL) mRecentProjectActs[i]->setEnabled(false); } } void MainWindow::ProgramSettings() { SettingsDialog dialog(mApplications, mTranslation, this); if (dialog.exec() == QDialog::Accepted) { dialog.SaveSettingValues(); mUI.mResults->UpdateSettings(dialog.ShowFullPath(), dialog.SaveFullPath(), dialog.SaveAllErrors(), dialog.ShowNoErrorsMessage(), dialog.ShowErrorId(), dialog.ShowInconclusive()); const QString newLang = mSettings->value(SETTINGS_LANGUAGE, "en").toString(); SetLanguage(newLang); } } void MainWindow::ReCheckModified() { ReCheck(false); } void MainWindow::ReCheckAll() { ReCheck(true); } void MainWindow::ReCheckSelected(QStringList files, bool all) { if (files.empty()) return; if (mThread->IsChecking()) return; // Clear details, statistics and progress mUI.mResults->Clear(false); for (int i = 0; i < files.size(); ++i) mUI.mResults->ClearRecheckFile(files[i]); FileList pathList; pathList.AddPathList(files); if (mProject) pathList.AddExcludeList(mProject->GetProjectFile()->GetExcludedPaths()); QStringList fileNames = pathList.GetFileList(); CheckLockDownUI(); // lock UI while checking mUI.mResults->CheckingStarted(fileNames.size()); mThread->SetCheckFiles(fileNames); // Saving last check start time, otherwise unchecked modified files will not be // considered in "Modified Files Check" performed after "Selected Files Check" // TODO: Should we store per file CheckStartTime? QDateTime saveCheckStartTime = mThread->GetCheckStartTime(); mThread->Check(GetCppcheckSettings(), all); mThread->SetCheckStartTime(saveCheckStartTime); } void MainWindow::ReCheck(bool all) { const QStringList files = mThread->GetReCheckFiles(all); if (files.empty()) return; // Clear details, statistics and progress mUI.mResults->Clear(all); // Clear results for changed files for (int i = 0; i < files.size(); ++i) mUI.mResults->Clear(files[i]); CheckLockDownUI(); // lock UI while checking mUI.mResults->CheckingStarted(files.size()); if (mProject) qDebug() << "Rechecking project file" << mProject->GetProjectFile()->GetFilename(); mThread->SetCheckFiles(all); mThread->Check(GetCppcheckSettings(), all); } void MainWindow::ClearResults() { mUI.mResults->Clear(true); mUI.mActionClearResults->setEnabled(false); mUI.mActionSave->setEnabled(false); mUI.mActionPrint->setEnabled(false); mUI.mActionPrintPreview->setEnabled(false); } void MainWindow::OpenResults() { if (mUI.mResults->HasResults()) { QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Cppcheck")); const QString msg(tr("Current results will be cleared.\n\n" "Opening a new XML file will clear current results." "Do you want to proceed?")); msgBox.setText(msg); msgBox.setIcon(QMessageBox::Warning); msgBox.addButton(QMessageBox::Yes); msgBox.addButton(QMessageBox::No); msgBox.setDefaultButton(QMessageBox::Yes); int dlgResult = msgBox.exec(); if (dlgResult == QMessageBox::No) { return; } } QString selectedFilter; const QString filter(tr("XML files (*.xml)")); QString selectedFile = QFileDialog::getOpenFileName(this, tr("Open the report file"), GetPath(SETTINGS_LAST_RESULT_PATH), filter, &selectedFilter); if (!selectedFile.isEmpty()) { LoadResults(selectedFile); } } void MainWindow::LoadResults(const QString selectedFile) { if (!selectedFile.isEmpty()) { mUI.mResults->Clear(true); mUI.mResults->ReadErrorsXml(selectedFile); SetPath(SETTINGS_LAST_RESULT_PATH, selectedFile); } } void MainWindow::LoadResults(const QString selectedFile, const QString sourceDirectory) { LoadResults(selectedFile); mUI.mResults->SetCheckDirectory(sourceDirectory); } void MainWindow::EnableCheckButtons(bool enable) { mUI.mActionStop->setEnabled(!enable); mUI.mActionCheckFiles->setEnabled(enable); if (!enable || mThread->HasPreviousFiles()) { mUI.mActionRecheckModified->setEnabled(enable); mUI.mActionRecheckAll->setEnabled(enable); } mUI.mActionCheckDirectory->setEnabled(enable); } void MainWindow::ShowStyle(bool checked) { mUI.mResults->ShowResults(ShowTypes::ShowStyle, checked); } void MainWindow::ShowErrors(bool checked) { mUI.mResults->ShowResults(ShowTypes::ShowErrors, checked); } void MainWindow::ShowWarnings(bool checked) { mUI.mResults->ShowResults(ShowTypes::ShowWarnings, checked); } void MainWindow::ShowPortability(bool checked) { mUI.mResults->ShowResults(ShowTypes::ShowPortability, checked); } void MainWindow::ShowPerformance(bool checked) { mUI.mResults->ShowResults(ShowTypes::ShowPerformance, checked); } void MainWindow::ShowInformation(bool checked) { mUI.mResults->ShowResults(ShowTypes::ShowInformation, checked); } void MainWindow::CheckAll() { ToggleAllChecked(true); } void MainWindow::UncheckAll() { ToggleAllChecked(false); } void MainWindow::closeEvent(QCloseEvent *event) { // Check that we aren't checking files if (!mThread->IsChecking()) { SaveSettings(); event->accept(); } else { const QString text(tr("Checking is running.\n\n" \ "Do you want to stop the checking and exit Cppcheck?")); QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), text, QMessageBox::Yes | QMessageBox::No, this); msg.setDefaultButton(QMessageBox::No); int rv = msg.exec(); if (rv == QMessageBox::Yes) { // This isn't really very clean way to close threads but since the app is // exiting it doesn't matter. mThread->Stop(); SaveSettings(); mExiting = true; } event->ignore(); } } void MainWindow::ToggleAllChecked(bool checked) { mUI.mActionShowStyle->setChecked(checked); ShowStyle(checked); mUI.mActionShowErrors->setChecked(checked); ShowErrors(checked); mUI.mActionShowWarnings->setChecked(checked); ShowWarnings(checked); mUI.mActionShowPortability->setChecked(checked); ShowPortability(checked); mUI.mActionShowPerformance->setChecked(checked); ShowPerformance(checked); mUI.mActionShowInformation->setChecked(checked); ShowInformation(checked); } void MainWindow::About() { AboutDialog *dlg = new AboutDialog(CppCheck::version(), CppCheck::extraVersion(), this); dlg->exec(); } void MainWindow::ShowLicense() { FileViewDialog *dlg = new FileViewDialog(":COPYING", tr("License"), this); dlg->resize(570, 400); dlg->exec(); } void MainWindow::ShowAuthors() { FileViewDialog *dlg = new FileViewDialog(":AUTHORS", tr("Authors"), this); dlg->resize(350, 400); dlg->exec(); } void MainWindow::PerformSelectedFilesCheck(QStringList selectedFilesList) { ReCheckSelected(selectedFilesList, true); } void MainWindow::Save() { QString selectedFilter; const QString filter(tr("XML files version 2 (*.xml);;XML files version 1 (*.xml);;Text files (*.txt);;CSV files (*.csv)")); QString selectedFile = QFileDialog::getSaveFileName(this, tr("Save the report file"), GetPath(SETTINGS_LAST_RESULT_PATH), filter, &selectedFilter); if (!selectedFile.isEmpty()) { Report::Type type = Report::TXT; if (selectedFilter == tr("XML files version 1 (*.xml)")) { type = Report::XML; if (!selectedFile.endsWith(".xml", Qt::CaseInsensitive)) selectedFile += ".xml"; } else if (selectedFilter == tr("XML files version 2 (*.xml)")) { type = Report::XMLV2; if (!selectedFile.endsWith(".xml", Qt::CaseInsensitive)) selectedFile += ".xml"; } else if (selectedFilter == tr("Text files (*.txt)")) { type = Report::TXT; if (!selectedFile.endsWith(".txt", Qt::CaseInsensitive)) selectedFile += ".txt"; } else if (selectedFilter == tr("CSV files (*.csv)")) { type = Report::CSV; if (!selectedFile.endsWith(".csv", Qt::CaseInsensitive)) selectedFile += ".csv"; } else { if (selectedFile.endsWith(".xml", Qt::CaseInsensitive)) type = Report::XML; else if (selectedFile.endsWith(".txt", Qt::CaseInsensitive)) type = Report::TXT; else if (selectedFile.endsWith(".csv", Qt::CaseInsensitive)) type = Report::CSV; } mUI.mResults->Save(selectedFile, type); SetPath(SETTINGS_LAST_RESULT_PATH, selectedFile); } } void MainWindow::ResultsAdded() { } void MainWindow::ToggleMainToolBar() { mUI.mToolBarMain->setVisible(mUI.mActionToolBarMain->isChecked()); } void MainWindow::ToggleViewToolBar() { mUI.mToolBarView->setVisible(mUI.mActionToolBarView->isChecked()); } void MainWindow::ToggleFilterToolBar() { mUI.mToolBarFilter->setVisible(mUI.mActionToolBarFilter->isChecked()); mLineEditFilter->clear(); // Clearing the filter also disables filtering } void MainWindow::FormatAndSetTitle(const QString &text) { QString title; if (text.isEmpty()) title = tr("Cppcheck"); else title = QString(tr("Cppcheck - %1")).arg(text); setWindowTitle(title); } void MainWindow::SetLanguage(const QString &code) { const QString currentLang = mTranslation->GetCurrentLanguage(); if (currentLang == code) return; if (mTranslation->SetLanguage(code)) { //Translate everything that is visible here mUI.retranslateUi(this); mUI.mResults->Translate(); delete mLogView; mLogView = 0; } } void MainWindow::AboutToShowViewMenu() { mUI.mActionToolBarMain->setChecked(mUI.mToolBarMain->isVisible()); mUI.mActionToolBarView->setChecked(mUI.mToolBarView->isVisible()); mUI.mActionToolBarFilter->setChecked(mUI.mToolBarFilter->isVisible()); } void MainWindow::StopChecking() { mThread->Stop(); mUI.mResults->DisableProgressbar(); } void MainWindow::OpenHelpContents() { OpenOnlineHelp(); } void MainWindow::OpenOnlineHelp() { QDesktopServices::openUrl(QUrl(OnlineHelpURL)); } void MainWindow::OpenProjectFile() { const QString lastPath = mSettings->value(SETTINGS_LAST_PROJECT_PATH, QString()).toString(); const QString filter = tr("Project files (*.cppcheck);;All files(*.*)"); const QString filepath = QFileDialog::getOpenFileName(this, tr("Select Project File"), GetPath(SETTINGS_LAST_PROJECT_PATH), filter); if (!filepath.isEmpty()) { const QFileInfo fi(filepath); if (fi.exists() && fi.isFile() && fi.isReadable()) { SetPath(SETTINGS_LAST_PROJECT_PATH, filepath); LoadProjectFile(filepath); } } } void MainWindow::ShowScratchpad() { if (!mScratchPad) mScratchPad = new ScratchPad(*this); mScratchPad->show(); if (!mScratchPad->isActiveWindow()) mScratchPad->activateWindow(); } void MainWindow::LoadProjectFile(const QString &filePath) { QFileInfo inf(filePath); const QString filename = inf.fileName(); FormatAndSetTitle(tr("Project:") + QString(" ") + filename); AddProjectMRU(filePath); mUI.mActionCloseProjectFile->setEnabled(true); mUI.mActionEditProjectFile->setEnabled(true); delete mProject; mProject = new Project(filePath, this); CheckProject(mProject); } void MainWindow::CheckProject(Project *project) { if (!project->IsOpen()) { if (!project->Open()) { delete mProject; mProject = 0; return; } } QFileInfo inf(project->Filename()); const QString rootpath = project->GetProjectFile()->GetRootPath(); // If the root path is not given or is not "current dir", use project // file's location directory as root path if (rootpath.isEmpty() || rootpath == ".") mCurrentDirectory = inf.canonicalPath(); else if (rootpath.startsWith(".")) mCurrentDirectory = inf.canonicalPath() + rootpath.mid(1); else mCurrentDirectory = rootpath; QStringList paths = project->GetProjectFile()->GetCheckPaths(); // If paths not given then check the root path (which may be the project // file's location, see above). This is to keep the compatibility with // old "silent" project file loading when we checked the director where the // project file was located. if (paths.isEmpty()) { paths << mCurrentDirectory; } // Convert relative paths to absolute paths 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); } void MainWindow::NewProjectFile() { const QString filter = tr("Project files (*.cppcheck);;All files(*.*)"); QString filepath = QFileDialog::getSaveFileName(this, tr("Select Project Filename"), GetPath(SETTINGS_LAST_PROJECT_PATH), filter); if (filepath.isEmpty()) return; SetPath(SETTINGS_LAST_PROJECT_PATH, filepath); EnableProjectActions(true); QFileInfo inf(filepath); const QString filename = inf.fileName(); FormatAndSetTitle(tr("Project:") + QString(" ") + filename); delete mProject; mProject = new Project(filepath, this); mProject->Create(); if (mProject->Edit()) { AddProjectMRU(filepath); CheckProject(mProject); } } void MainWindow::CloseProjectFile() { delete mProject; mProject = NULL; EnableProjectActions(false); EnableProjectOpenActions(true); 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) mLogView = new LogView; mLogView->show(); if (!mLogView->isActiveWindow()) mLogView->activateWindow(); } void MainWindow::ShowStatistics() { StatsDialog statsDialog(this); // Show a dialog with the previous scan statistics and project information if (mProject) { statsDialog.setProject(*mProject); } statsDialog.setPathSelected(mCurrentDirectory); statsDialog.setNumberOfFilesScanned(mThread->GetPreviousFilesCount()); statsDialog.setScanDuration(mThread->GetPreviousScanDuration() / 1000.0); statsDialog.setStatistics(mUI.mResults->GetStatistics()); statsDialog.exec(); } void MainWindow::ShowLibraryEditor() { LibraryDialog libraryDialog(this); libraryDialog.exec(); } void MainWindow::Log(const QString &logline) { if (mLogView) { mLogView->AppendLine(logline); } } void MainWindow::DebugError(const ErrorItem &item) { if (mLogView) { mLogView->AppendLine(item.ToString()); } } void MainWindow::FilterResults() { mUI.mResults->FilterResults(mLineEditFilter->text()); } void MainWindow::EnableProjectActions(bool enable) { mUI.mActionCloseProjectFile->setEnabled(enable); mUI.mActionEditProjectFile->setEnabled(enable); } void MainWindow::EnableProjectOpenActions(bool enable) { mUI.mActionNewProjectFile->setEnabled(enable); mUI.mActionOpenProjectFile->setEnabled(enable); } void MainWindow::OpenRecentProject() { QAction *action = qobject_cast(sender()); if (action) { const QString project = action->data().toString(); QFileInfo inf(project); if (inf.exists()) { LoadProjectFile(project); } else { const QString text(tr("The project file\n\n%1\n\n could not be found!\n\n" "Do you want to remove the file from the recently " "used projects -list?").arg(project)); QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), text, QMessageBox::Yes | QMessageBox::No, this); msg.setDefaultButton(QMessageBox::No); int rv = msg.exec(); if (rv == QMessageBox::Yes) { RemoveProjectMRU(project); } } } } void MainWindow::UpdateMRUMenuItems() { for (int i = 0; i < MaxRecentProjects + 1; i++) { if (mRecentProjectActs[i] != NULL) mUI.mMenuFile->removeAction(mRecentProjectActs[i]); } QStringList projects = mSettings->value(SETTINGS_MRU_PROJECTS).toStringList(); // Do a sanity check - remove duplicates and empty or space only items int removed = projects.removeDuplicates(); for (int i = projects.size() - 1; i >= 0; i--) { QString text = projects[i].trimmed(); if (text.isEmpty()) { projects.removeAt(i); removed++; } } if (removed) mSettings->setValue(SETTINGS_MRU_PROJECTS, projects); const int numRecentProjects = qMin(projects.size(), (int)MaxRecentProjects); for (int i = 0; i < numRecentProjects; i++) { const QString filename = QFileInfo(projects[i]).fileName(); const QString text = QString("&%1 %2").arg(i + 1).arg(filename); mRecentProjectActs[i]->setText(text); mRecentProjectActs[i]->setData(projects[i]); mRecentProjectActs[i]->setVisible(true); mUI.mMenuFile->insertAction(mUI.mActionProjectMRU, mRecentProjectActs[i]); } if (numRecentProjects > 1) mRecentProjectActs[numRecentProjects] = mUI.mMenuFile->insertSeparator(mUI.mActionProjectMRU); } void MainWindow::AddProjectMRU(const QString &project) { QStringList files = mSettings->value(SETTINGS_MRU_PROJECTS).toStringList(); files.removeAll(project); files.prepend(project); while (files.size() > MaxRecentProjects) files.removeLast(); mSettings->setValue(SETTINGS_MRU_PROJECTS, files); UpdateMRUMenuItems(); } void MainWindow::RemoveProjectMRU(const QString &project) { QStringList files = mSettings->value(SETTINGS_MRU_PROJECTS).toStringList(); files.removeAll(project); mSettings->setValue(SETTINGS_MRU_PROJECTS, files); UpdateMRUMenuItems(); } void MainWindow::SelectPlatform() { QAction *action = qobject_cast(sender()); if (action) { const Settings::PlatformType platform = (Settings::PlatformType) action->data().toInt(); mSettings->setValue(SETTINGS_CHECKED_PLATFORM, platform); } }