Added the initial version of saving results to a file.

This commit is contained in:
Vesa Pikki 2009-06-03 21:18:22 +03:00
parent 2a7470e986
commit a330e9c35f
11 changed files with 546 additions and 16 deletions

View File

@ -110,6 +110,7 @@ MainWindow::MainWindow() :
connect(&mActionAbout, SIGNAL(triggered()), this, SLOT(About())); connect(&mActionAbout, SIGNAL(triggered()), this, SLOT(About()));
connect(&mThread, SIGNAL(Done()), this, SLOT(CheckDone())); connect(&mThread, SIGNAL(Done()), this, SLOT(CheckDone()));
connect(&mResults, SIGNAL(GotResults()), this, SLOT(ResultsAdded()));
//Toolbar //Toolbar
QToolBar *toolbar = addToolBar("Toolbar"); QToolBar *toolbar = addToolBar("Toolbar");
@ -140,6 +141,9 @@ MainWindow::MainWindow() :
setWindowTitle(tr("Cppcheck")); setWindowTitle(tr("Cppcheck"));
EnableCheckButtons(true); EnableCheckButtons(true);
mActionClearResults.setEnabled(false);
mActionSave.setEnabled(false);
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@ -210,6 +214,7 @@ void MainWindow::DoCheckFiles(QFileDialog::FileMode mode)
mThread.SetFiles(RemoveUnacceptedFiles(fileNames)); mThread.SetFiles(RemoveUnacceptedFiles(fileNames));
mSettings.setValue(tr("Check path"), dialog.directory().absolutePath()); mSettings.setValue(tr("Check path"), dialog.directory().absolutePath());
EnableCheckButtons(false); EnableCheckButtons(false);
mResults.SetCheckDirectory(dialog.directory().absolutePath());
mThread.Check(GetCppcheckSettings(), false); mThread.Check(GetCppcheckSettings(), false);
} }
} }
@ -295,6 +300,9 @@ void MainWindow::ProgramSettings()
if (dialog.exec() == QDialog::Accepted) if (dialog.exec() == QDialog::Accepted)
{ {
dialog.SaveCheckboxValues(); dialog.SaveCheckboxValues();
mResults.UpdateSettings(dialog.ShowFullPath(),
dialog.SaveFullPath(),
dialog.SaveAllErrors());
} }
} }
@ -309,6 +317,8 @@ void MainWindow::ReCheck()
void MainWindow::ClearResults() void MainWindow::ClearResults()
{ {
mResults.Clear(); mResults.Clear();
mActionClearResults.setEnabled(false);
mActionSave.setEnabled(false);
} }
void MainWindow::EnableCheckButtons(bool enable) void MainWindow::EnableCheckButtons(bool enable)
@ -396,9 +406,30 @@ void MainWindow::About()
void MainWindow::Save() void MainWindow::Save()
{ {
QMessageBox msgBox; QFileDialog dialog(this);
msgBox.setWindowTitle(tr("Not implemented yet...")); dialog.setFileMode(QFileDialog::AnyFile);
msgBox.setText(tr("Not implemented yet...")); dialog.setAcceptMode(QFileDialog::AcceptSave);
msgBox.exec();
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);
}

View File

@ -133,6 +133,12 @@ protected slots:
* *
*/ */
void CheckDone(); void CheckDone();
/**
* @brief Slot for enabling save and clear button
*
*/
void ResultsAdded();
protected: protected:
/** /**

View File

@ -22,11 +22,13 @@
#include <QMenu> #include <QMenu>
#include <QSignalMapper> #include <QSignalMapper>
#include <QProcess> #include <QProcess>
#include <QDir>
ResultsTree::ResultsTree(QSettings &settings, ApplicationList &list) : ResultsTree::ResultsTree(QSettings &settings, ApplicationList &list) :
mSettings(settings), mSettings(settings),
mApplications(list), mApplications(list),
mContextItem(0) mContextItem(0),
mCheckPath("")
{ {
setModel(&mModel); setModel(&mModel);
QStringList labels; QStringList labels;
@ -58,7 +60,8 @@ void ResultsTree::AddErrorItem(const QString &file,
const QString &severity, const QString &severity,
const QString &message, const QString &message,
const QStringList &files, const QStringList &files,
const QVariantList &lines) const QVariantList &lines,
const QString &id)
{ {
Q_UNUSED(file); Q_UNUSED(file);
@ -68,7 +71,7 @@ void ResultsTree::AddErrorItem(const QString &file,
return; return;
} }
QString realfile = files[0]; QString realfile = StripPath(files[0], false);
if (realfile.isEmpty()) if (realfile.isEmpty())
{ {
@ -93,6 +96,7 @@ void ResultsTree::AddErrorItem(const QString &file,
data["message"] = message; data["message"] = message;
data["files"] = files; data["files"] = files;
data["lines"] = lines; data["lines"] = lines;
data["id"] = id;
item->setData(QVariant(data)); 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++) for (int i = 1;i < files.size() && i < lines.size();i++)
{ {
AddBacktraceFiles(item, AddBacktraceFiles(item,
files[i], StripPath(files[i], false),
lines[i].toInt(), lines[i].toInt(),
severity, severity,
message, message,
@ -143,6 +147,7 @@ QStandardItem *ResultsTree::AddBacktraceFiles(QStandardItem *parent,
setRowHidden(parent->rowCount() - 1, parent->index(), hide); setRowHidden(parent->rowCount() - 1, parent->index(), hide);
if (!icon.isEmpty()) if (!icon.isEmpty())
{ {
list[0]->setIcon(QIcon(icon)); list[0]->setIcon(QIcon(icon));
@ -200,6 +205,10 @@ void ResultsTree::LoadSettings()
QString temp = QString(tr("Result column %1 width")).arg(i); QString temp = QString(tr("Result column %1 width")).arg(i);
setColumnWidth(i, mSettings.value(temp, 800 / mModel.columnCount()).toInt()); 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() void ResultsTree::SaveSettings()
@ -422,3 +431,251 @@ QString ResultsTree::SeverityToIcon(const QString &severity)
return ""; return "";
} }
void ResultsTree::SaveResults(QTextStream &out, bool xml)
{
if (xml)
{
out << "<?xml version=\"1.0\"?>" << endl << "<results>" << endl;
}
for (int i = 0;i < mModel.rowCount();i++)
{
QStandardItem *item = mModel.item(i, 0);
SaveErrors(out, item, xml);
}
if (xml)
{
out << "</results>" << 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
<error file="gui/test.cpp" line="14" id="mismatchAllocDealloc" severity="error" msg="Mismatching allocation and deallocation: k"/>
The callstack seems to be ignored here aswell, instead last item of the stack is used
*/
line = QString("<error file=\"%1\" line=\"%2\" id=\"%3\" severity=\"%4\" msg=\"%5\"/>").
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));
}
}

View File

@ -53,7 +53,8 @@ public:
const QString &severity, const QString &severity,
const QString &message, const QString &message,
const QStringList &files, const QStringList &files,
const QVariantList &lines); const QVariantList &lines,
const QString &id);
/** /**
* @brief Clear all errors from the tree * @brief Clear all errors from the tree
@ -69,6 +70,29 @@ public:
* @param Should specified errors be shown (true) or hidden (false) * @param Should specified errors be shown (true) or hidden (false)
*/ */
void ShowResults(ShowTypes type, bool show); 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: protected slots:
/** /**
* @brief Slot to quickstart an error with default application * @brief Slot to quickstart an error with default application
@ -84,6 +108,38 @@ protected slots:
*/ */
void Context(int application); void Context(int application);
protected: 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 * @brief Convert a severity string to a icon filename
* *
@ -151,6 +207,13 @@ protected:
*/ */
ShowTypes SeverityToShowType(const QString &severity); 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 * @brief Load all settings
* Colum widths * Colum widths
@ -225,6 +288,30 @@ protected:
* *
*/ */
QStandardItem *mContextItem; 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: private:
}; };

View File

@ -20,6 +20,7 @@
#include "resultsview.h" #include "resultsview.h"
#include <QDebug> #include <QDebug>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QFile>
ResultsView::ResultsView(QSettings &settings, ApplicationList &list) ResultsView::ResultsView(QSettings &settings, ApplicationList &list)
{ {
@ -58,9 +59,11 @@ void ResultsView::Error(const QString &file,
const QString &severity, const QString &severity,
const QString &message, const QString &message,
const QStringList &files, 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) void ResultsView::ShowResults(ShowTypes type, bool show)
@ -77,3 +80,29 @@ void ResultsView::ExpandAllResults()
{ {
mTree->expandAll(); 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);
}

View File

@ -54,6 +54,41 @@ public:
* *
*/ */
void Clear(); 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: public slots:
/** /**
@ -77,7 +112,8 @@ public slots:
const QString &severity, const QString &severity,
const QString &message, const QString &message,
const QStringList &files, const QStringList &files,
const QVariantList &lines); const QVariantList &lines,
const QString &error);
/** /**
* @brief Collapse all results in the result list. * @brief Collapse all results in the result list.

View File

@ -83,6 +83,11 @@ SettingsDialog::SettingsDialog(QSettings &programSettings, ApplicationList &list
tr("Check force"), tr("Check force"),
false); false);
mShowFullPath = AddCheckbox(layout,
tr("Show full path of files"),
tr("Show full path"),
false);
general->setLayout(layout); general->setLayout(layout);
//Add tab for setting user startable applications //Add tab for setting user startable applications
@ -121,6 +126,23 @@ SettingsDialog::SettingsDialog(QSettings &programSettings, ApplicationList &list
PopulateListWidget(); 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); setLayout(dialoglayout);
setWindowTitle(tr("Settings")); setWindowTitle(tr("Settings"));
LoadSettings(); LoadSettings();
@ -181,6 +203,9 @@ void SettingsDialog::SaveCheckboxValues()
mSettings.setValue(tr("Check threads"), jobs); mSettings.setValue(tr("Check threads"), jobs);
SaveCheckboxValue(mForce, tr("Check force")); 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) void SettingsDialog::SaveCheckboxValue(QCheckBox *box, const QString &name)
@ -259,4 +284,20 @@ void SettingsDialog::Ok()
accept(); accept();
} }
bool SettingsDialog::ShowFullPath()
{
return CheckStateToBool(mShowFullPath->checkState());
}
bool SettingsDialog::SaveFullPath()
{
return CheckStateToBool(mSaveFullPath->checkState());
}
bool SettingsDialog::SaveAllErrors()
{
return CheckStateToBool(mSaveAllErrors->checkState());
}

View File

@ -50,6 +50,27 @@ public:
*/ */
void SaveCheckboxValues(); 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: protected slots:
/** /**
* @brief Slot for clicking OK. * @brief Slot for clicking OK.
@ -152,6 +173,24 @@ protected:
*/ */
QCheckBox *mForce; 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 * @brief List of all applications that can be started when right clicking
* an error * an error

View File

@ -138,12 +138,14 @@ void ThreadHandler::Initialize(ResultsView *view)
const QString &, const QString &,
const QString &, const QString &,
const QStringList &, const QStringList &,
const QVariantList &)), const QVariantList &,
const QString &)),
view, SLOT(Error(const QString &, view, SLOT(Error(const QString &,
const QString &, const QString &,
const QString &, const QString &,
const QStringList &, const QStringList &,
const QVariantList &))); const QVariantList &,
const QString &)));
} }

View File

@ -61,7 +61,8 @@ void ThreadResult::reportErr(const ErrorLogger::ErrorMessage &msg)
QString(msg._severity.c_str()), QString(msg._severity.c_str()),
QString(msg._msg.c_str()), QString(msg._msg.c_str()),
files, files,
lines); lines,
QString(msg._id.c_str()));

View File

@ -95,7 +95,8 @@ signals:
const QString &severity, const QString &severity,
const QString &message, const QString &message,
const QStringList &files, const QStringList &files,
const QVariantList &lines); const QVariantList &lines,
const QString &id);
protected: protected: