/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2009 Daniel Marjamäki and 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 "aboutdialog.h" #include "threadhandler.h" #include "fileviewdialog.h" #include "projectfile.h" #include "../src/filelister.h" #include "../src/cppcheckexecutor.h" MainWindow::MainWindow() : mSettings(new QSettings("Cppcheck", "Cppcheck-GUI", this)), mApplications(new ApplicationList(this)), mTranslation(new TranslationHandler(this)), mLanguages(new QActionGroup(this)) { mUI.setupUi(this); mUI.mResults->Initialize(mSettings, mApplications); mThread = new ThreadHandler(this); 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.mActionShowAll, SIGNAL(toggled(bool)), this, SLOT(ShowAll(bool))); connect(mUI.mActionShowSecurity, SIGNAL(toggled(bool)), this, SLOT(ShowSecurity(bool))); connect(mUI.mActionShowStyle, SIGNAL(toggled(bool)), this, SLOT(ShowStyle(bool))); connect(mUI.mActionShowErrors, SIGNAL(toggled(bool)), this, SLOT(ShowErrors(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.mActionRecheck, SIGNAL(triggered()), this, SLOT(ReCheck())); connect(mUI.mActionStop, SIGNAL(triggered()), this, SLOT(StopChecking())); connect(mUI.mActionSave, SIGNAL(triggered()), this, SLOT(Save())); connect(mUI.mActionAbout, SIGNAL(triggered()), this, SLOT(About())); connect(mUI.mActionLicense, SIGNAL(triggered()), this, SLOT(ShowLicense())); connect(mUI.mActionToolbar, SIGNAL(toggled(bool)), this, SLOT(ToggleToolbar())); 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.mMenuView, SIGNAL(aboutToShow()), this, SLOT(AboutToShowViewMenu())); CreateLanguageMenuItems(); LoadSettings(); mThread->Initialize(mUI.mResults); FormatAndSetTitle(); EnableCheckButtons(true); mUI.mActionClearResults->setEnabled(false); mUI.mActionSave->setEnabled(false); } MainWindow::~MainWindow() { SaveSettings(); } void MainWindow::CreateLanguageMenuItems() { QStringList languages = mTranslation->GetNames(); for (int i = 0; i < languages.size(); i++) { //Create an action for each language //Language name is pre translated QAction *temp = new QAction(languages[i], this); temp->setCheckable(true); //Add the action to menu mUI.mMenuLanguage->addAction(temp); //Add action to the group mLanguages->addAction(temp); //Check it if it's the value stored to settings if (i == mSettings->value(SETTINGS_LANGUAGE, 0).toInt()) { temp->setChecked(true); } else { temp->setChecked(false); } } connect(mLanguages, SIGNAL(triggered(QAction *)), this, SLOT(MapLanguage(QAction *))); } void MainWindow::LoadSettings() { 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()); } mUI.mActionShowAll->setChecked(mSettings->value(SETTINGS_SHOW_ALL, true).toBool()); mUI.mActionShowSecurity->setChecked(mSettings->value(SETTINGS_SHOW_SECURITY, true).toBool()); mUI.mActionShowStyle->setChecked(mSettings->value(SETTINGS_SHOW_STYLE, true).toBool()); mUI.mActionShowErrors->setChecked(mSettings->value(SETTINGS_SHOW_ERRORS, true).toBool()); mUI.mResults->ShowResults(SHOW_ALL, mUI.mActionShowAll->isChecked()); mUI.mResults->ShowResults(SHOW_ERRORS, mUI.mActionShowErrors->isChecked()); mUI.mResults->ShowResults(SHOW_SECURITY, mUI.mActionShowSecurity->isChecked()); mUI.mResults->ShowResults(SHOW_STYLE, mUI.mActionShowStyle->isChecked()); mUI.mActionToolbar->setChecked(mSettings->value(SETTINGS_TOOLBARS_SHOW, true).toBool()); mUI.mToolBar->setVisible(mSettings->value(SETTINGS_TOOLBARS_SHOW, true).toBool()); mApplications->LoadSettings(mSettings); SetLanguage(mSettings->value(SETTINGS_LANGUAGE, mTranslation->SuggestLanguage()).toInt()); } void MainWindow::SaveSettings() { //Force toolbar checkbox value to be updated AboutToShowViewMenu(); mSettings->setValue(SETTINGS_WINDOW_WIDTH, size().width()); mSettings->setValue(SETTINGS_WINDOW_HEIGHT, size().height()); mSettings->setValue(SETTINGS_WINDOW_MAXIMIZED, isMaximized()); mSettings->setValue(SETTINGS_SHOW_ALL, mUI.mActionShowAll->isChecked()); mSettings->setValue(SETTINGS_SHOW_SECURITY, mUI.mActionShowSecurity->isChecked()); mSettings->setValue(SETTINGS_SHOW_STYLE, mUI.mActionShowStyle->isChecked()); mSettings->setValue(SETTINGS_SHOW_ERRORS, mUI.mActionShowErrors->isChecked()); mSettings->setValue(SETTINGS_TOOLBARS_SHOW, mUI.mActionToolbar->isChecked()); mApplications->SaveSettings(mSettings); mSettings->setValue(SETTINGS_LANGUAGE, mTranslation->GetCurrentLanguage()); mUI.mResults->SaveSettings(); } void MainWindow::DoCheckFiles(QFileDialog::FileMode mode) { 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"), mSettings->value(SETTINGS_CHECK_PATH, "").toString()); if (selected.isEmpty()) mCurrentDirectory.clear(); FormatAndSetTitle(); } else if (mode == QFileDialog::DirectoryOnly) { QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory to check"), mSettings->value(SETTINGS_CHECK_PATH, "").toString()); if (!dir.isEmpty()) { mCurrentDirectory = dir; selected.append(dir); FormatAndSetTitle(dir); } } if (selected.count() > 0) { ClearResults(); QStringList fileNames; QString selection; foreach(selection, selected) { fileNames << RemoveUnacceptedFiles(GetFilesRecursively(selection)); } mUI.mResults->Clear(); 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(); mThread->SetFiles(RemoveUnacceptedFiles(fileNames)); QFileInfo inf(fileNames[0]); QString absDirectory = inf.absoluteDir().path(); mSettings->setValue(SETTINGS_CHECK_PATH, absDirectory); EnableCheckButtons(false); mUI.mActionSettings->setEnabled(false); mUI.mResults->SetCheckDirectory(absDirectory); Settings checkSettings = GetCppcheckSettings(); mThread->Check(checkSettings, false); } } void MainWindow::CheckFiles() { DoCheckFiles(QFileDialog::ExistingFiles); } void MainWindow::CheckDirectory() { DoCheckFiles(QFileDialog::DirectoryOnly); } Settings MainWindow::GetCppcheckSettings() { ProjectFile pfile; Settings result; if (!mCurrentDirectory.isEmpty()) { // Format project filename (directory name + .cppcheck) and load // the project file if it is found. QStringList parts = mCurrentDirectory.split("/"); QString projfile = parts[parts.count() - 1] + ".cppcheck"; bool projectRead = false; if (QFile::exists(projfile)) projectRead = pfile.Read(mCurrentDirectory + "/" + projfile); if (projectRead) { QStringList classes = pfile.GetDeAllocatedClasses(); QString classname; foreach(classname, classes) { result.addAutoAllocClass(classname.toStdString()); } } } result._debug = false; result._showAll = true; result._checkCodingStyle = true; result._errorsOnly = false; result._verbose = true; result._force = mSettings->value(SETTINGS_CHECK_FORCE, 1).toBool(); result._xml = false; result._unusedFunctions = false; result._security = true; result._jobs = mSettings->value(SETTINGS_CHECK_THREADS, 1).toInt(); if (result._jobs <= 0) { result._jobs = 1; } return result; } QStringList MainWindow::GetFilesRecursively(const QString &path) { QFileInfo info(path); QStringList list; if (info.isDir()) { QDirIterator it(path, QDirIterator::Subdirectories); while (it.hasNext()) { list << it.next(); } } else { list << path; } return list; } QStringList MainWindow::RemoveUnacceptedFiles(const QStringList &list) { QStringList result; QString str; foreach(str, list) { if (FileLister::AcceptFile(str.toStdString())) { result << str; } } return result; } void MainWindow::CheckDone() { EnableCheckButtons(true); mUI.mActionSettings->setEnabled(true); if (mUI.mResults->HasResults()) { mUI.mActionClearResults->setEnabled(true); mUI.mActionSave->setEnabled(true); } // Notify user - if the window is not active - that check is ready QApplication::alert(this, 3000); } void MainWindow::ProgramSettings() { SettingsDialog dialog(mSettings, mApplications, this); if (dialog.exec() == QDialog::Accepted) { dialog.SaveCheckboxValues(); mUI.mResults->UpdateSettings(dialog.ShowFullPath(), dialog.SaveFullPath(), dialog.SaveAllErrors(), dialog.ShowNoErrorsMessage()); } } void MainWindow::ReCheck() { ClearResults(); EnableCheckButtons(false); mThread->Check(GetCppcheckSettings(), true); } void MainWindow::ClearResults() { mUI.mResults->Clear(); mUI.mActionClearResults->setEnabled(false); mUI.mActionSave->setEnabled(false); } void MainWindow::EnableCheckButtons(bool enable) { mUI.mActionStop->setEnabled(!enable); mUI.mActionCheckFiles->setEnabled(enable); mUI.mActionRecheck->setEnabled(enable); mUI.mActionCheckDirectory->setEnabled(enable); } void MainWindow::ShowAll(bool checked) { mUI.mResults->ShowResults(SHOW_ALL, checked); } void MainWindow::ShowSecurity(bool checked) { mUI.mResults->ShowResults(SHOW_SECURITY, checked); } void MainWindow::ShowStyle(bool checked) { mUI.mResults->ShowResults(SHOW_STYLE, checked); } void MainWindow::ShowErrors(bool checked) { mUI.mResults->ShowResults(SHOW_ERRORS, 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()) event->accept(); else { QString text(tr("Cannot exit while checking.\n\n" \ "Stop the checking before exiting.")); QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), text, QMessageBox::Ok, this); msg.exec(); event->ignore(); } } void MainWindow::ToggleAllChecked(bool checked) { mUI.mActionShowAll->setChecked(checked); ShowAll(checked); mUI.mActionShowSecurity->setChecked(checked); ShowSecurity(checked); mUI.mActionShowStyle->setChecked(checked); ShowStyle(checked); mUI.mActionShowErrors->setChecked(checked); ShowErrors(checked); } void MainWindow::About() { //TODO make a "GetVersionNumber" function to core cppcheck CppCheckExecutor exec; CppCheck check(exec); const char *argv[] = {"", "--version"}; QString version = check.parseFromArgs(2, argv).c_str(); version.replace("Cppcheck ", ""); AboutDialog *dlg = new AboutDialog(version, 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::Save() { QString selectedFilter; QString filter(tr("XML files (*.xml);;Text files (*.txt)")); QString selectedFile = QFileDialog::getSaveFileName(this, tr("Save the report file"), QString(), filter, &selectedFilter); if (!selectedFile.isEmpty()) { //Check if xml file type was selected bool xml = selectedFilter == tr("XML files (*.xml)"); //Force xml extension to the file if (xml && !selectedFile.endsWith(".xml", Qt::CaseInsensitive)) { selectedFile += ".xml"; } //Force .txt extension if (!xml && !selectedFile.endsWith(".txt", Qt::CaseInsensitive)) { selectedFile += ".txt"; } mUI.mResults->Save(selectedFile, xml); } } void MainWindow::ResultsAdded() { } void MainWindow::ToggleToolbar() { mUI.mToolBar->setVisible(mUI.mActionToolbar->isChecked()); } 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(int index) { if (mTranslation->GetCurrentLanguage() == index) { return; } QString error; if (!mTranslation->SetLanguage(index, error)) { QMessageBox msg(QMessageBox::Warning, tr("Cppcheck"), QString(tr("Failed to change language:\n\n%1")).arg(error), QMessageBox::Ok, this); msg.exec(); } else { //Translate everything that is visible here mUI.retranslateUi(this); mUI.mResults->Translate(); QStringList languages = mTranslation->GetNames(); QList actions = mLanguages->actions(); if (languages.size() <= actions.size()) { for (int i = 0;i < languages.size();i++) { actions[i]->setText(tr(languages[i].toLatin1())); } } } } void MainWindow::MapLanguage(QAction *action) { //Find the action that has the language that user clicked QList actions = mLanguages->actions(); for (int i = 0; i < actions.size(); i++) { if (actions[i] == action) { SetLanguage(i); } } } void MainWindow::AboutToShowViewMenu() { mUI.mActionToolbar->setChecked(mUI.mToolBar->isVisible()); } void MainWindow::StopChecking() { mThread->Stop(); mUI.mResults->DisableProgressbar(); }