diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index f28cf6502..218e0db6a 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -110,6 +110,7 @@ MainWindow::MainWindow() : connect(&mActionAbout, SIGNAL(triggered()), this, SLOT(About())); connect(&mThread, SIGNAL(Done()), this, SLOT(CheckDone())); + connect(&mResults, SIGNAL(GotResults()), this, SLOT(ResultsAdded())); //Toolbar QToolBar *toolbar = addToolBar("Toolbar"); @@ -140,6 +141,9 @@ MainWindow::MainWindow() : setWindowTitle(tr("Cppcheck")); EnableCheckButtons(true); + + mActionClearResults.setEnabled(false); + mActionSave.setEnabled(false); } MainWindow::~MainWindow() @@ -210,6 +214,7 @@ void MainWindow::DoCheckFiles(QFileDialog::FileMode mode) mThread.SetFiles(RemoveUnacceptedFiles(fileNames)); mSettings.setValue(tr("Check path"), dialog.directory().absolutePath()); EnableCheckButtons(false); + mResults.SetCheckDirectory(dialog.directory().absolutePath()); mThread.Check(GetCppcheckSettings(), false); } } @@ -295,6 +300,9 @@ void MainWindow::ProgramSettings() if (dialog.exec() == QDialog::Accepted) { dialog.SaveCheckboxValues(); + mResults.UpdateSettings(dialog.ShowFullPath(), + dialog.SaveFullPath(), + dialog.SaveAllErrors()); } } @@ -309,6 +317,8 @@ void MainWindow::ReCheck() void MainWindow::ClearResults() { mResults.Clear(); + mActionClearResults.setEnabled(false); + mActionSave.setEnabled(false); } void MainWindow::EnableCheckButtons(bool enable) @@ -396,9 +406,30 @@ void MainWindow::About() void MainWindow::Save() { - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Not implemented yet...")); - msgBox.setText(tr("Not implemented yet...")); - msgBox.exec(); + QFileDialog dialog(this); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setAcceptMode(QFileDialog::AcceptSave); + + QStringList filters; + filters << tr("XML files (*.xml)") << tr("Text files (*.txt)"); + dialog.setNameFilters(filters); + + if (dialog.exec()) + { + QStringList list = dialog.selectedFiles(); + + if (list.size() > 0) + { + bool xml = (dialog.selectedNameFilter() == filters[0] && list[0].endsWith(".xml", Qt::CaseInsensitive)); + mResults.Save(list[0], xml); + } + } } +void MainWindow::ResultsAdded() +{ + mActionClearResults.setEnabled(true); + mActionSave.setEnabled(true); +} + + diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 1deebb635..6c1b5572b 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -133,6 +133,12 @@ protected slots: * */ void CheckDone(); + + /** + * @brief Slot for enabling save and clear button + * + */ + void ResultsAdded(); protected: /** diff --git a/gui/resultstree.cpp b/gui/resultstree.cpp index 8ea9594c9..8486877f3 100644 --- a/gui/resultstree.cpp +++ b/gui/resultstree.cpp @@ -22,11 +22,13 @@ #include #include #include +#include ResultsTree::ResultsTree(QSettings &settings, ApplicationList &list) : mSettings(settings), mApplications(list), - mContextItem(0) + mContextItem(0), + mCheckPath("") { setModel(&mModel); QStringList labels; @@ -58,7 +60,8 @@ void ResultsTree::AddErrorItem(const QString &file, const QString &severity, const QString &message, const QStringList &files, - const QVariantList &lines) + const QVariantList &lines, + const QString &id) { Q_UNUSED(file); @@ -68,7 +71,7 @@ void ResultsTree::AddErrorItem(const QString &file, return; } - QString realfile = files[0]; + QString realfile = StripPath(files[0], false); if (realfile.isEmpty()) { @@ -93,6 +96,7 @@ void ResultsTree::AddErrorItem(const QString &file, data["message"] = message; data["files"] = files; data["lines"] = lines; + data["id"] = id; item->setData(QVariant(data)); @@ -100,7 +104,7 @@ void ResultsTree::AddErrorItem(const QString &file, for (int i = 1;i < files.size() && i < lines.size();i++) { AddBacktraceFiles(item, - files[i], + StripPath(files[i], false), lines[i].toInt(), severity, message, @@ -143,6 +147,7 @@ QStandardItem *ResultsTree::AddBacktraceFiles(QStandardItem *parent, setRowHidden(parent->rowCount() - 1, parent->index(), hide); + if (!icon.isEmpty()) { list[0]->setIcon(QIcon(icon)); @@ -200,6 +205,10 @@ void ResultsTree::LoadSettings() QString temp = QString(tr("Result column %1 width")).arg(i); setColumnWidth(i, mSettings.value(temp, 800 / mModel.columnCount()).toInt()); } + + mSaveFullPath = mSettings.value(tr("Save full path"), false).toBool(); + mSaveAllErrors = mSettings.value(tr("Save all errors"), false).toBool(); + mShowFullPath = mSettings.value(tr("Show full path"), false).toBool(); } void ResultsTree::SaveSettings() @@ -422,3 +431,251 @@ QString ResultsTree::SeverityToIcon(const QString &severity) return ""; } + +void ResultsTree::SaveResults(QTextStream &out, bool xml) +{ + if (xml) + { + out << "" << endl << "" << endl; + } + + for (int i = 0;i < mModel.rowCount();i++) + { + QStandardItem *item = mModel.item(i, 0); + SaveErrors(out, item, xml); + } + + if (xml) + { + out << "" << endl; + } +} + +void ResultsTree::SaveErrors(QTextStream &out, QStandardItem *item, bool xml) +{ + if (!item) + { + return; + } + + //qDebug() << item->text() << "has" << item->rowCount() << "errors"; + + for (int i = 0;i < item->rowCount();i++) + { + QStandardItem *error = item->child(i, 0); + + if (!error) + { + continue; + } + + if (isRowHidden(i, item->index()) && !mSaveAllErrors) + { + continue; + } + + //Get error's user data + QVariant userdata = error->data(); + //Convert it to QVariantMap + QVariantMap data = userdata.toMap(); + + QString line; + QString severity = ShowTypeToString(VariantToShowType(data["severity"])); + QString message = data["message"].toString(); + QString id = data["id"].toString(); + QStringList files = data["files"].toStringList(); + QVariantList lines = data["lines"].toList(); + + if (files.size() <= 0 || lines.size() <= 0 || lines.size() != files.size()) + { + continue; + } + + + + if (xml) + { + /* + Error example from the core program in xml + + The callstack seems to be ignored here aswell, instead last item of the stack is used + */ + line = QString(""). + arg(StripPath(files[files.size()-1], true)). //filename + arg(lines[lines.size()-1].toInt()). //line + arg(id). //ID + arg(severity). //severity + arg(message); //Message + + } + else + { + /* + Error example from the core program in text + [gui/test.cpp:23] -> [gui/test.cpp:14]: (error) Mismatching allocation and deallocation: k + */ + for (int i = 0;i < lines.size();i++) + { + line += QString("[%1:%2]").arg(StripPath(files[i], true)).arg(lines[i].toInt()); + if (i < lines.size() - 1 && lines.size() > 0) + { + line += " -> "; + } + + if (i == lines.size() - 1) + { + line += ": "; + } + } + + line += QString("(%1) %2").arg(severity).arg(message); + } + + out << line << endl; + + } +} + +QString ResultsTree::ShowTypeToString(ShowTypes type) +{ + switch (type) + { + case SHOW_ALL: + return "all"; + break; + + case SHOW_STYLE: + return "style"; + break; + + case SHOW_SECURITY: + return "security"; + break; + + case SHOW_UNUSED: + return "unused"; + break; + + case SHOW_ERRORS: + return "error"; + break; + + case SHOW_NONE: + return ""; + break; + } + + return ""; +} + +void ResultsTree::UpdateSettings(bool showFullPath, + bool saveFullPath, + bool saveAllErrors) +{ + if (mShowFullPath != showFullPath) + { + mShowFullPath = showFullPath; + RefreshFilePaths(); + } + + mSaveFullPath = saveFullPath; + mSaveAllErrors = saveAllErrors; +} + +void ResultsTree::SetCheckDirectory(const QString &dir) +{ + mCheckPath = dir; +} + +QString ResultsTree::StripPath(const QString &path, bool saving) +{ + if ((!saving && mShowFullPath) || (saving && mSaveFullPath)) + { + return QString(path); + } + + QDir dir(mCheckPath); + return dir.relativeFilePath(path); +} + +void ResultsTree::RefreshFilePaths(QStandardItem *item) +{ + if (!item) + { + return; + } + + //Mark that this file's path hasn't been updated yet + bool updated = false; + + //Loop through all errors within this file + for (int i = 0;i < item->rowCount();i++) + { + + //Get error i + QStandardItem *error = item->child(i, 0); + + if (!error) + { + continue; + } + + + //Get error's user data + QVariant userdata = error->data(); + //Convert it to QVariantMap + QVariantMap data = userdata.toMap(); + + //Get list of files + QStringList files = data["files"].toStringList(); + + //We should always have at least 1 file per error + if (files.size() == 0) + { + continue; + } + + //Update this error's text + error->setText(StripPath(files[0], false)); + + + //If this error has backtraces make sure the files list has enough filenames + if (error->rowCount() <= files.size() - 1) + { + //Loop through all files within the error + for (int j = 0;j < error->rowCount();j++) + { + //Get file + QStandardItem *file = error->child(j, 0); + if (!file) + { + continue; + } + //Update file's path + file->setText(StripPath(files[j+1], false)); + } + } + + //if the main file hasn't been updated yet, update it now + if (!updated) + { + updated = true; + item->setText(error->text()); + } + + } +} + + +void ResultsTree::RefreshFilePaths() +{ + qDebug("Refreshing file paths"); + + //Go through all file items (these are parent items that contain the errors) + for (int i = 0;i < mModel.rowCount();i++) + { + RefreshFilePaths(mModel.item(i, 0)); + } + +} + diff --git a/gui/resultstree.h b/gui/resultstree.h index 6bd098d48..baa9ee266 100644 --- a/gui/resultstree.h +++ b/gui/resultstree.h @@ -53,7 +53,8 @@ public: const QString &severity, const QString &message, const QStringList &files, - const QVariantList &lines); + const QVariantList &lines, + const QString &id); /** * @brief Clear all errors from the tree @@ -69,6 +70,29 @@ public: * @param Should specified errors be shown (true) or hidden (false) */ void ShowResults(ShowTypes type, bool show); + + /** + * @brief Save results to a text stream + * + */ + void SaveResults(QTextStream &out, bool xml); + + /** + * @brief Update tree settings + * + * @param showFullPath Show full path of files in the tree + * @param saveFullPath Save full path of files in reports + * @param saveAllErrors Save all visible errors + */ + void UpdateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors); + + /** + * @brief Set the directory we are checking + * + * This is used to split error file path to relative if necessary + * @param dir Directory we are checking + */ + void SetCheckDirectory(const QString &dir); protected slots: /** * @brief Slot to quickstart an error with default application @@ -84,6 +108,38 @@ protected slots: */ void Context(int application); protected: + + /** + * @brief Hides/shows full file path on all error file items according to mShowFullPath + * + */ + void RefreshFilePaths(); + + /** + * @brief Hides/shows full file path on all error file items according to mShowFullPath + * @param item Parent item whose childrens paths to change + */ + void RefreshFilePaths(QStandardItem *item); + + + /** + * @brief Removes checking directory from given path if mShowFullPath is false + * + * @param path Path to remove checking directory + * @param saving are we saving? Check mSaveFullPath instead + * @return Path that has checking directory removed + */ + QString StripPath(const QString &path, bool saving); + + + /** + * @brief Save all errors under spesified item + * + * @param item Item whose errors to save + * @param xml Should errors be saved as xml (true) or as text (false) + */ + void SaveErrors(QTextStream &out, QStandardItem *item, bool xml); + /** * @brief Convert a severity string to a icon filename * @@ -151,6 +207,13 @@ protected: */ ShowTypes SeverityToShowType(const QString &severity); + /** + * @brief Convert ShowType to severity string + * @param type ShowType to convert + * @return ShowType converted to string + */ + QString ShowTypeToString(ShowTypes type); + /** * @brief Load all settings * Colum widths @@ -225,6 +288,30 @@ protected: * */ QStandardItem *mContextItem; + + /** + * @brief Should full path of files be shown (true) or relative (false) + * + */ + bool mShowFullPath; + + /** + * @brief Should full path of files be saved + * + */ + bool mSaveFullPath; + + /** + * @brief Save all errors (true) or only visible (false) + * + */ + bool mSaveAllErrors; + + /** + * @brief Path we are currently checking + * + */ + QString mCheckPath; private: }; diff --git a/gui/resultsview.cpp b/gui/resultsview.cpp index 01d696fbf..aa26994ff 100644 --- a/gui/resultsview.cpp +++ b/gui/resultsview.cpp @@ -20,6 +20,7 @@ #include "resultsview.h" #include #include +#include ResultsView::ResultsView(QSettings &settings, ApplicationList &list) { @@ -58,9 +59,11 @@ void ResultsView::Error(const QString &file, const QString &severity, const QString &message, const QStringList &files, - const QVariantList &lines) + const QVariantList &lines, + const QString &id) { - mTree->AddErrorItem(file, severity, message, files, lines); + mTree->AddErrorItem(file, severity, message, files, lines, id); + emit GotResults(); } void ResultsView::ShowResults(ShowTypes type, bool show) @@ -77,3 +80,29 @@ void ResultsView::ExpandAllResults() { mTree->expandAll(); } + +void ResultsView::Save(const QString &filename, bool xml) +{ + QFile file(filename); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + return; + } + + QTextStream out(&file); + mTree->SaveResults(out, xml); +} + + +void ResultsView::UpdateSettings(bool showFullPath, + bool saveFullPath, + bool saveAllErrors) +{ + mTree->UpdateSettings(showFullPath, saveFullPath, saveAllErrors); +} + +void ResultsView::SetCheckDirectory(const QString &dir) +{ + mTree->SetCheckDirectory(dir); +} + diff --git a/gui/resultsview.h b/gui/resultsview.h index 70fde003f..261938174 100644 --- a/gui/resultsview.h +++ b/gui/resultsview.h @@ -54,6 +54,41 @@ public: * */ void Clear(); + + /** + * @brief Save results to a file + * + * @param filename Filename to save results to + * @param xml should results be saved as xml (true) or txt (false) + */ + void Save(const QString &filename, bool xml); + + /** + * @brief Update tree settings + * + * @param showFullPath Show full path of files in the tree + * @param saveFullPath Save full path of files in reports + * @param saveAllErrors Save all visible errors + */ + void UpdateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors); + + /** + * @brief Set the directory we are checking + * + * This is used to split error file path to relative if necessary + * @param dir Directory we are checking + */ + void SetCheckDirectory(const QString &dir); + +signals: + + /** + * @brief Signal to be emitted when we have results + * + */ + void GotResults(); + + public slots: /** @@ -77,7 +112,8 @@ public slots: const QString &severity, const QString &message, const QStringList &files, - const QVariantList &lines); + const QVariantList &lines, + const QString &error); /** * @brief Collapse all results in the result list. diff --git a/gui/settingsdialog.cpp b/gui/settingsdialog.cpp index 6567ae738..682ee1b38 100644 --- a/gui/settingsdialog.cpp +++ b/gui/settingsdialog.cpp @@ -83,6 +83,11 @@ SettingsDialog::SettingsDialog(QSettings &programSettings, ApplicationList &list tr("Check force"), false); + mShowFullPath = AddCheckbox(layout, + tr("Show full path of files"), + tr("Show full path"), + false); + general->setLayout(layout); //Add tab for setting user startable applications @@ -121,6 +126,23 @@ SettingsDialog::SettingsDialog(QSettings &programSettings, ApplicationList &list PopulateListWidget(); + //report tab + QWidget *report = new QWidget(); + tabs->addTab(report, tr("Reports")); + + + QVBoxLayout *reportlayout = new QVBoxLayout(); + mSaveAllErrors = AddCheckbox(reportlayout, + tr("Save all errors when creating report"), + tr("Save all errors"), + false); + + mSaveFullPath = AddCheckbox(reportlayout, + tr("Save full path to files in reports"), + tr("Save full path"), + false); + report->setLayout(reportlayout); + setLayout(dialoglayout); setWindowTitle(tr("Settings")); LoadSettings(); @@ -181,6 +203,9 @@ void SettingsDialog::SaveCheckboxValues() mSettings.setValue(tr("Check threads"), jobs); SaveCheckboxValue(mForce, tr("Check force")); + SaveCheckboxValue(mSaveAllErrors, tr("Save all errors")); + SaveCheckboxValue(mSaveFullPath, tr("Save full path")); + SaveCheckboxValue(mShowFullPath, tr("Show full path")); } void SettingsDialog::SaveCheckboxValue(QCheckBox *box, const QString &name) @@ -259,4 +284,20 @@ void SettingsDialog::Ok() accept(); } +bool SettingsDialog::ShowFullPath() +{ + return CheckStateToBool(mShowFullPath->checkState()); +} + +bool SettingsDialog::SaveFullPath() +{ + return CheckStateToBool(mSaveFullPath->checkState()); +} + +bool SettingsDialog::SaveAllErrors() +{ + return CheckStateToBool(mSaveAllErrors->checkState()); +} + + diff --git a/gui/settingsdialog.h b/gui/settingsdialog.h index 4dce226d8..57eea9d52 100644 --- a/gui/settingsdialog.h +++ b/gui/settingsdialog.h @@ -50,6 +50,27 @@ public: */ void SaveCheckboxValues(); + /** + * @brief Get checkbox value for mShowFullPath + * + * @return should full path of errors be shown in the tree + */ + bool ShowFullPath(); + + /** + * @brief Get checkbox value for mSaveFullPath + * + * @return should full path of files be saved when creating a report + */ + bool SaveFullPath(); + + /** + * @brief Get checkbox value for mSaveAllErrors + * + * @return should all errors be saved to report + */ + bool SaveAllErrors(); + protected slots: /** * @brief Slot for clicking OK. @@ -152,6 +173,24 @@ protected: */ QCheckBox *mForce; + /** + * @brief Save all errors + * + */ + QCheckBox *mSaveAllErrors; + + /** + * @brief Save full path of the file in error reports + * + */ + QCheckBox *mSaveFullPath; + + /** + * @brief Show full path of file in results view + * + */ + QCheckBox *mShowFullPath; + /** * @brief List of all applications that can be started when right clicking * an error diff --git a/gui/threadhandler.cpp b/gui/threadhandler.cpp index a367dd7cc..c7a5a271c 100644 --- a/gui/threadhandler.cpp +++ b/gui/threadhandler.cpp @@ -138,12 +138,14 @@ void ThreadHandler::Initialize(ResultsView *view) const QString &, const QString &, const QStringList &, - const QVariantList &)), + const QVariantList &, + const QString &)), view, SLOT(Error(const QString &, const QString &, const QString &, const QStringList &, - const QVariantList &))); + const QVariantList &, + const QString &))); } diff --git a/gui/threadresult.cpp b/gui/threadresult.cpp index 6c2c0fb12..04a01922d 100644 --- a/gui/threadresult.cpp +++ b/gui/threadresult.cpp @@ -61,7 +61,8 @@ void ThreadResult::reportErr(const ErrorLogger::ErrorMessage &msg) QString(msg._severity.c_str()), QString(msg._msg.c_str()), files, - lines); + lines, + QString(msg._id.c_str())); diff --git a/gui/threadresult.h b/gui/threadresult.h index 8706e9d5d..3224e413f 100644 --- a/gui/threadresult.h +++ b/gui/threadresult.h @@ -95,7 +95,8 @@ signals: const QString &severity, const QString &message, const QStringList &files, - const QVariantList &lines); + const QVariantList &lines, + const QString &id); protected: