diff --git a/src/bugzillaintegration/reportassistantpages_base.cpp b/src/bugzillaintegration/reportassistantpages_base.cpp
index 86d34f7d..772e285e 100644
--- a/src/bugzillaintegration/reportassistantpages_base.cpp
+++ b/src/bugzillaintegration/reportassistantpages_base.cpp
@@ -1,466 +1,460 @@
/*******************************************************************
* reportassistantpages_base.cpp
* Copyright 2009 Dario Andres Rodriguez
* Copyright 2009 A. L. Spehr
*
* 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 2 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 "reportassistantpages_base.h"
#include
#include
#include
#include
#include
#include
#include
#include "drkonqi.h"
#include "debuggermanager.h"
#include "crashedapplication.h"
#include "reportinterface.h"
#include "parser/backtraceparser.h"
#include "backtracegenerator.h"
#include "backtracewidget.h"
#include "drkonqi_globals.h"
#include "applicationdetailsexamples.h"
//BEGIN IntroductionPage
IntroductionPage::IntroductionPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
{
ui.setupUi(this);
ui.m_warningIcon->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-warning")).pixmap(64,64));
}
//END IntroductionPage
//BEGIN CrashInformationPage
CrashInformationPage::CrashInformationPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
{
m_backtraceWidget = new BacktraceWidget(DrKonqi::debuggerManager()->backtraceGenerator(), this, true);
connect(m_backtraceWidget, &BacktraceWidget::stateChanged, this, &CrashInformationPage::emitCompleteChanged);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0,0,0,0);
layout->addWidget(m_backtraceWidget);
layout->addSpacing(10); //We need this for better usability until we get something better
//If the backtrace was already fetched on the main dialog, save it.
BacktraceGenerator *btGenerator = DrKonqi::debuggerManager()->backtraceGenerator();
if (btGenerator->state() == BacktraceGenerator::Loaded) {
BacktraceParser::Usefulness use = btGenerator->parser()->backtraceUsefulness();
if (use != BacktraceParser::Useless && use != BacktraceParser::InvalidUsefulness) {
reportInterface()->setBacktrace(btGenerator->backtrace());
}
}
}
void CrashInformationPage::aboutToShow()
{
m_backtraceWidget->generateBacktrace();
m_backtraceWidget->hilightExtraDetailsLabel(false);
emitCompleteChanged();
}
void CrashInformationPage::aboutToHide()
{
BacktraceGenerator *btGenerator = DrKonqi::debuggerManager()->backtraceGenerator();
BacktraceParser::Usefulness use = btGenerator->parser()->backtraceUsefulness();
if (use != BacktraceParser::Useless && use != BacktraceParser::InvalidUsefulness) {
reportInterface()->setBacktrace(btGenerator->backtrace());
}
reportInterface()->setFirstBacktraceFunctions(btGenerator->parser()->firstValidFunctions());
}
bool CrashInformationPage::isComplete()
{
BacktraceGenerator *generator = DrKonqi::debuggerManager()->backtraceGenerator();
return (generator->state() != BacktraceGenerator::NotLoaded &&
generator->state() != BacktraceGenerator::Loading);
}
bool CrashInformationPage::showNextPage()
{
BacktraceParser::Usefulness use =
DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness();
if (DrKonqi::ignoreQuality()) {
return true;
}
if ((use == BacktraceParser::InvalidUsefulness || use == BacktraceParser::ProbablyUseless
|| use == BacktraceParser::Useless) && m_backtraceWidget->canInstallDebugPackages()) {
if ( KMessageBox::Yes == KMessageBox::questionYesNo(this,
i18nc("@info","This crash information is not useful enough, "
"do you want to try to improve it? You will need "
"to install some debugging packages."),
i18nc("@title:window","Crash Information is not useful enough")) ) {
m_backtraceWidget->hilightExtraDetailsLabel(true);
m_backtraceWidget->focusImproveBacktraceButton();
return false; //Cancel show next, to allow the user to write more
} else {
return true; //Allow to continue
}
} else {
return true;
}
}
//END CrashInformationPage
//BEGIN BugAwarenessPage
static QHash s_reproducibleIndex {
{ 0, ReportInterface::ReproducibleUnsure },
{ 1, ReportInterface::ReproducibleNever },
{ 2, ReportInterface::ReproducibleSometimes },
{ 3, ReportInterface::ReproducibleEverytime }
};
BugAwarenessPage::BugAwarenessPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
{
ui.setupUi(this);
ui.m_actionsInsideApp->setText(i18nc("@option:check kind of information the user can provide "
"about the crash, %1 is the application name",
"What I was doing when the application \"%1\" crashed",
DrKonqi::crashedApplication()->name()));
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect(ui.m_rememberGroup, static_cast(&QButtonGroup::buttonClicked), this, &BugAwarenessPage::updateCheckBoxes);
// Also listen to toggle so radio buttons are covered.
connect(ui.m_rememberGroup, static_cast(&QButtonGroup::buttonToggled), this, &BugAwarenessPage::updateCheckBoxes);
-#else
- connect(ui.m_rememberGroup, &QButtonGroup::buttonClicked, this, &BugAwarenessPage::updateCheckBoxes);
- // Also listen to toggle so radio buttons are covered.
- connect(ui.m_rememberGroup, &QButtonGroup::buttonToggled, this, &BugAwarenessPage::updateCheckBoxes);
-#endif
ui.m_appSpecificDetailsExamplesWidget->setVisible(
reportInterface()->appDetailsExamples()->hasExamples());
ui.m_appSpecificDetailsExamples->setText(
i18nc("@label examples about information the user can provide",
"Examples: %1", reportInterface()->appDetailsExamples()->examples()));
ui.m_appSpecificDetailsExamples->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
if (qEnvironmentVariableIsSet("DRKONQI_TEST_MODE")) {
ui.m_rememberCrashSituationYes->setChecked(true);
ui.m_reproducibleBox->setCurrentIndex(
s_reproducibleIndex.key(ReportInterface::ReproducibleEverytime));
}
}
void BugAwarenessPage::aboutToShow()
{
updateCheckBoxes();
}
void BugAwarenessPage::aboutToHide()
{
//Save data
ReportInterface::Reproducible reproducible = ReportInterface::ReproducibleUnsure;
reproducible = s_reproducibleIndex.value(ui.m_reproducibleBox->currentIndex());
reportInterface()->setBugAwarenessPageData(ui.m_rememberCrashSituationYes->isChecked(),
reproducible,
ui.m_actionsInsideApp->isChecked(),
ui.m_unusualSituation->isChecked(),
ui.m_appSpecificDetails->isChecked());
}
void BugAwarenessPage::updateCheckBoxes()
{
const bool rememberSituation = ui.m_rememberCrashSituationYes->isChecked();
ui.m_reproducibleLabel->setEnabled(rememberSituation);
ui.m_reproducibleBox->setEnabled(rememberSituation);
ui.m_informationLabel->setEnabled(rememberSituation);
ui.m_actionsInsideApp->setEnabled(rememberSituation);
ui.m_unusualSituation->setEnabled(rememberSituation);
ui.m_appSpecificDetails->setEnabled(rememberSituation);
ui.m_appSpecificDetailsExamples->setEnabled(rememberSituation);
}
//END BugAwarenessPage
//BEGIN ConclusionPage
ConclusionPage::ConclusionPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
, m_needToReport(false)
{
m_isBKO = DrKonqi::crashedApplication()->bugReportAddress().isKdeBugzilla();
ui.setupUi(this);
KGuiItem::assign(ui.m_showReportInformationButton,
KGuiItem2(i18nc("@action:button", "&Show Contents of the Report"),
QIcon::fromTheme(QStringLiteral("document-preview")),
i18nc("@info:tooltip", "Use this button to show the generated "
"report information about this crash.")));
connect(ui.m_showReportInformationButton, &QPushButton::clicked, this, &ConclusionPage::openReportInformation);
ui.m_restartAppOnFinish->setVisible(false);
}
void ConclusionPage::finishClicked()
{
//Manual report
if (m_needToReport && !m_isBKO) {
const CrashedApplication *crashedApp = DrKonqi::crashedApplication();
BugReportAddress reportAddress = crashedApp->bugReportAddress();
QString report = reportInterface()->generateReportFullText(ReportInterface::DrKonqiStamp::Exclude,
ReportInterface::Backtrace::Complete);
if (reportAddress.isEmail()) {
QString subject = QStringLiteral("[%1] [%2] Automatic crash report generated by DrKonqi");
subject= subject.arg(crashedApp->name());
subject= subject.arg(crashedApp->datetime().toString(QStringLiteral("yyyy-MM-dd")));
KToolInvocation::invokeMailer(reportAddress, QLatin1String(""), QLatin1String("") , subject, report);
} else {
QUrl url(reportAddress);
if (QUrl(reportAddress).isRelative()) { //Scheme is missing
url = QUrl(QString::fromLatin1("https://%1").arg(reportAddress));
}
QDesktopServices::openUrl(url);
}
//Show a copy of the bug reported
openReportInformation();
}
//Restart application
if (ui.m_restartAppOnFinish->isChecked()) {
DrKonqi::crashedApplication()->restart();
}
}
void ConclusionPage::aboutToShow()
{
connect(assistant()->finishButton(), &QPushButton::clicked, this, &ConclusionPage::finishClicked);
ui.m_restartAppOnFinish->setVisible(false);
ui.m_restartAppOnFinish->setChecked(false);
const bool isDuplicate = reportInterface()->duplicateId() && !reportInterface()->attachToBugNumber();
m_needToReport = reportInterface()->isWorthReporting() && !isDuplicate;
emitCompleteChanged();
BugReportAddress reportAddress = DrKonqi::crashedApplication()->bugReportAddress();
BacktraceParser::Usefulness use =
DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness();
QString explanationHTML = QLatin1String("
").arg(i18nc("@info","The automatically generated "
"crash information is useful."));
break;
}
case BacktraceParser::MayBeUseful: {
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The automatically generated "
"crash information lacks some "
"details "
"but may be still be useful."));
break;
}
case BacktraceParser::ProbablyUseless: {
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The automatically generated "
"crash information lacks important details "
"and it is probably not helpful."));
break;
}
case BacktraceParser::Useless:
case BacktraceParser::InvalidUsefulness: {
BacktraceGenerator::State state = DrKonqi::debuggerManager()->backtraceGenerator()->state();
if (state == BacktraceGenerator::NotLoaded) {
backtraceGenerated = false;
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The crash information was "
"not generated because it was not needed."));
} else {
explanationHTML += QStringLiteral("
%1 %2
").arg(
i18nc("@info","The automatically generated crash "
"information does not contain enough information to be "
"helpful."),
xi18nc("@info","You can improve it by "
"installing debugging packages and reloading the crash on "
"the Crash Information page. You can get help with the Bug "
"Reporting Guide by clicking on the "
"Help button."));
//but this guide doesn't mention bt packages? that's techbase
//->>and the help guide mention techbase page...
}
break;
}
}
//User can provide enough information
if (reportInterface()->isBugAwarenessPageDataUseful()) {
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The information you can "
"provide could be considered helpful."));
} else {
explanationHTML += QStringLiteral("
%1
").arg(i18nc("@info","The information you can "
"provide is not considered helpful enough in this case."));
}
if (isDuplicate) {
explanationHTML += QStringLiteral("
%1
").arg(xi18nc("@info","Your problem has already been "
"reported as bug %1.", QString::number(reportInterface()->duplicateId())));
}
explanationHTML += QLatin1String("
");
ui.m_explanationLabel->setText(explanationHTML);
//Hide the "Show contents of the report" button if the backtrace was not generated
ui.m_showReportInformationButton->setVisible(backtraceGenerated);
if (m_needToReport) {
ui.m_conclusionsLabel->setText(QStringLiteral("
%1").arg(i18nc("@info","This "
"report is considered helpful.")));
if (m_isBKO) {
emitCompleteChanged();
ui.m_howToProceedLabel->setText(xi18nc("@info","This application's bugs are reported "
"to the KDE bug tracking system: click Next"
" to start the reporting process. "
"You can manually report at %1",
reportAddress));
} else {
if (!DrKonqi::crashedApplication()->hasBeenRestarted()) {
ui.m_restartAppOnFinish->setVisible(true);
}
ui.m_howToProceedLabel->setText(xi18nc("@info","This application is not supported in the "
"KDE bug tracking system. Click "
"Finish to report this bug to "
"the application maintainer. Also, you can manually "
"report at %1.", reportAddress));
emit finished(false);
}
} else { // (m_needToReport)
if (!DrKonqi::crashedApplication()->hasBeenRestarted()) {
ui.m_restartAppOnFinish->setVisible(true);
}
ui.m_conclusionsLabel->setText(QStringLiteral("
%1 %2
").arg(
i18nc("@info","This report does not contain enough information for the "
"developers, so the automated bug reporting process is not "
"enabled for this crash."),
i18nc("@info","If you wish, you can go back and change your "
"answers. ")));
//Only mention "manual reporting" if the backtrace was generated.
//FIXME separate the texts "manual reporting" / "click finish to close"
//"manual reporting" should be ~"manual report using the contents of the report"....
//FIXME for 4.5 (workflow, see ToDo)
if (backtraceGenerated) {
if (m_isBKO) {
ui.m_howToProceedLabel->setText(xi18nc("@info","You can manually report this bug "
"at %1. "
"Click Finish to close the "
"assistant.",
reportAddress));
} else {
ui.m_howToProceedLabel->setText(xi18nc("@info","You can manually report this "
"bug to its maintainer at %1. "
"Click Finish to close the "
"assistant.", reportAddress));
}
}
emit finished(true);
}
}
void ConclusionPage::aboutToHide()
{
disconnect(assistant()->finishButton(), &QPushButton::clicked, this, &ConclusionPage::finishClicked);
}
void ConclusionPage::openReportInformation()
{
if (!m_infoDialog) {
QString info = reportInterface()->generateReportFullText(ReportInterface::DrKonqiStamp::Exclude,
ReportInterface::Backtrace::Complete)
+ QLatin1Char('\n') +
i18nc("@info report to url/mail address","Report to %1",
DrKonqi::crashedApplication()->bugReportAddress());
m_infoDialog = new ReportInformationDialog(info);
}
m_infoDialog->show();
m_infoDialog->raise();
m_infoDialog->activateWindow();
}
bool ConclusionPage::isComplete()
{
return (m_isBKO && m_needToReport);
}
//END ConclusionPage
//BEGIN ReportInformationDialog
ReportInformationDialog::ReportInformationDialog(const QString & reportText)
: QDialog()
{
setAttribute(Qt::WA_DeleteOnClose, true);
setWindowTitle(i18nc("@title:window","Contents of the Report"));
ui.setupUi(this);
ui.m_reportInformationBrowser->setPlainText(reportText);
QPushButton* saveButton = new QPushButton(ui.buttonBox);
KGuiItem::assign(saveButton, KGuiItem2(i18nc("@action:button", "&Save to File..."),
QIcon::fromTheme(QStringLiteral("document-save")),
i18nc("@info:tooltip", "Use this button to save the "
"generated crash report information to "
"a file. You can use this option to report the "
"bug later.")));
connect(saveButton, &QPushButton::clicked, this, &ReportInformationDialog::saveReport);
ui.buttonBox->addButton(saveButton, QDialogButtonBox::ActionRole);
KConfigGroup config(KSharedConfig::openConfig(), "ReportInformationDialog");
KWindowConfig::restoreWindowSize(windowHandle(), config);
}
ReportInformationDialog::~ReportInformationDialog()
{
KConfigGroup config(KSharedConfig::openConfig(), "ReportInformationDialog");
KWindowConfig::saveWindowSize(windowHandle(), config);
}
void ReportInformationDialog::saveReport()
{
DrKonqi::saveReport(ui.m_reportInformationBrowser->toPlainText(), this);
}
//END ReportInformationDialog
diff --git a/src/bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp b/src/bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp
index 58fe9b60..15db8480 100644
--- a/src/bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp
+++ b/src/bugzillaintegration/reportassistantpages_bugzilla_duplicates.cpp
@@ -1,1079 +1,1070 @@
/*******************************************************************
* reportassistantpages_bugzilla_duplicates.cpp
* Copyright 2009 Dario Andres Rodriguez
* Copyright 2019 Harald Sitter
*
* 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 2 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 "reportassistantpages_bugzilla_duplicates.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "drkonqi_globals.h"
#include "reportinterface.h"
#include "statuswidget.h"
//BEGIN BugzillaDuplicatesPage
BugzillaDuplicatesPage::BugzillaDuplicatesPage(ReportAssistantDialog *parent)
: ReportAssistantPage(parent)
{
connect(bugzillaManager(), &BugzillaManager::searchFinished,
this, &BugzillaDuplicatesPage::searchFinished);
connect(bugzillaManager(), &BugzillaManager::searchError,
this, &BugzillaDuplicatesPage::searchError);
ui.setupUi(this);
ui.information->hide();
connect(ui.m_bugListWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
this, SLOT(itemClicked(QTreeWidgetItem*,int)));
connect(ui.m_bugListWidget, &QTreeWidget::itemSelectionChanged,
this, &BugzillaDuplicatesPage::itemSelectionChanged);
QHeaderView * header = ui.m_bugListWidget->header();
header->setSectionResizeMode(0, QHeaderView::ResizeToContents);
header->setSectionResizeMode(1, QHeaderView::Interactive);
//Create manual bug report entry (first one)
QTreeWidgetItem * customBugItem = new QTreeWidgetItem(
QStringList() << i18nc("@item:intable custom/manaul bug report number", "Manual")
<< i18nc("@item:intable custom bug report number description",
"Manually enter a bug report ID"));
customBugItem->setData(0, Qt::UserRole, QLatin1String("custom"));
customBugItem->setIcon(1, QIcon::fromTheme(QStringLiteral("edit-rename")));
QString helpMessage = i18nc("@info:tooltip / whatsthis",
"Select this option to manually load a specific bug report");
customBugItem->setToolTip(0, helpMessage);
customBugItem->setToolTip(1, helpMessage);
customBugItem->setWhatsThis(0, helpMessage);
customBugItem->setWhatsThis(1, helpMessage);
ui.m_bugListWidget->addTopLevelItem(customBugItem);
m_searchMoreGuiItem = KGuiItem2(i18nc("@action:button", "Search for more reports"),
QIcon::fromTheme(QStringLiteral("edit-find")),
i18nc("@info:tooltip", "Use this button to "
"search for more similar bug reports"));
KGuiItem::assign(ui.m_searchMoreButton, m_searchMoreGuiItem);
connect(ui.m_searchMoreButton, &QAbstractButton::clicked, this, &BugzillaDuplicatesPage::searchMore);
m_retrySearchGuiItem = KGuiItem2(i18nc("@action:button", "Retry search"),
QIcon::fromTheme(QStringLiteral("edit-find")),
i18nc("@info:tooltip", "Use this button to "
"retry the search that previously "
"failed."));
KGuiItem::assign(ui.m_openReportButton, KGuiItem2(i18nc("@action:button", "Open selected report"),
QIcon::fromTheme(QStringLiteral("document-preview")),
i18nc("@info:tooltip", "Use this button to view "
"the information of the selected bug report.")));
connect(ui.m_openReportButton, &QAbstractButton::clicked, this, &BugzillaDuplicatesPage::openSelectedReport);
KGuiItem::assign(ui.m_stopSearchButton, KGuiItem2(i18nc("@action:button", "Stop searching"),
QIcon::fromTheme(QStringLiteral("process-stop")),
i18nc("@info:tooltip", "Use this button to stop "
"the current search.")));
ui.m_stopSearchButton->setText(QString()); //FIXME
connect(ui.m_stopSearchButton, &QAbstractButton::clicked, this, &BugzillaDuplicatesPage::stopCurrentSearch);
//Possible duplicates list and buttons
connect(ui.m_selectedDuplicatesList, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
this, SLOT(itemClicked(QListWidgetItem*)));
connect(ui.m_selectedDuplicatesList, &QListWidget::itemSelectionChanged,
this, &BugzillaDuplicatesPage::possibleDuplicateSelectionChanged);
KGuiItem::assign(ui.m_removeSelectedDuplicateButton,
KGuiItem2(i18nc("@action:button remove the selected item from a list", "Remove"),
QIcon::fromTheme(QStringLiteral("list-remove")),
i18nc("@info:tooltip", "Use this button to remove a selected possible duplicate")));
ui.m_removeSelectedDuplicateButton->setEnabled(false);
connect(ui.m_removeSelectedDuplicateButton, &QAbstractButton::clicked, this,
&BugzillaDuplicatesPage::removeSelectedDuplicate);
ui.m_attachToReportIcon->setPixmap(QIcon::fromTheme(QStringLiteral("mail-attachment")).pixmap(16,16));
ui.m_attachToReportIcon->setFixedSize(16,16);
ui.m_attachToReportIcon->setVisible(false);
ui.m_attachToReportLabel->setVisible(false);
ui.m_attachToReportLabel->setContextMenuPolicy(Qt::NoContextMenu);
connect(ui.m_attachToReportLabel, &QLabel::linkActivated, this,
&BugzillaDuplicatesPage::cancelAttachToBugReport);
ui.information->setContextMenuPolicy(Qt::NoContextMenu);
connect(ui.information, &QLabel::linkActivated, this, &BugzillaDuplicatesPage::informationClicked);
showDuplicatesPanel(false);
}
BugzillaDuplicatesPage::~BugzillaDuplicatesPage()
{
}
void BugzillaDuplicatesPage::aboutToShow()
{
//Perform initial search if we are not currently searching and if there are no results yet
if (!m_searching && ui.m_bugListWidget->topLevelItemCount() == 1 && canSearchMore()) {
searchMore();
}
}
void BugzillaDuplicatesPage::aboutToHide()
{
stopCurrentSearch();
//Save selected possible duplicates by user
QStringList possibleDuplicates;
int count = ui.m_selectedDuplicatesList->count();
for(int i = 0; iitem(i)->text();
}
reportInterface()->setPossibleDuplicates(possibleDuplicates);
//Save possible duplicates by query
QStringList duplicatesByQuery;
count = ui.m_bugListWidget->topLevelItemCount();
for(int i = 1; itopLevelItem(i)->text(0);
}
reportInterface()->setPossibleDuplicatesByQuery(duplicatesByQuery);
}
bool BugzillaDuplicatesPage::isComplete()
{
return !m_searching;
}
bool BugzillaDuplicatesPage::showNextPage()
{
//Ask the user to check all the possible duplicates...
if (ui.m_bugListWidget->topLevelItemCount() != 1 && ui.m_selectedDuplicatesList->count() == 0
&& reportInterface()->attachToBugNumber() == 0 && !m_foundDuplicate) {
//The user didn't selected any possible duplicate nor a report to attach the new info.
//Double check this, we need to reduce the duplicate count.
KGuiItem noDuplicatesButton;
noDuplicatesButton.setText(i18n("There are no real duplicates"));
noDuplicatesButton.setWhatsThis(i18n("Press this button to declare that, in your opinion "
"and according to your experience, the reports found "
"as similar do not match the crash you have "
"experienced, and you believe it is unlikely that a "
"better match would be found after further review."));
noDuplicatesButton.setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")));
KGuiItem letMeCheckMoreReportsButton;
letMeCheckMoreReportsButton.setText(i18n("Let me check more reports"));
letMeCheckMoreReportsButton.setWhatsThis(i18n("Press this button if you would rather "
"review more reports in order to find a "
"match for the crash you have experienced."));
letMeCheckMoreReportsButton.setIcon(QIcon::fromTheme(QStringLiteral("document-preview")));
if (KMessageBox::questionYesNo(this,
i18nc("@info","You have not selected any possible duplicates, or a report to which to attach your "
"crash information. Have you read all the reports, and can you confirm that there are no "
"real duplicates?"),
i18nc("@title:window","No selected possible duplicates"), letMeCheckMoreReportsButton,
noDuplicatesButton)
== KMessageBox::Yes) {
return false;
}
}
return true;
}
//BEGIN Search related methods
void BugzillaDuplicatesPage::searchMore()
{
if (m_offset < 0) {
m_offset = 0; // initialize, -1 means no search done yet
}
// This is fairly inefficient, unfortunately the API's offset/limit system
// is not useful to us. The search is always sorting by lowest id, and
// negative offests are not a thing. So, offset=0&limit=1 gives the first
// ever reported bug in the product, while what we want is the latest.
// We also cannot query all perintent bug ids by default. While the API
// is reasonably fast, it'll still produce upwards of 2MiB just for the
// ids of a dolphin crash (as it includes all sorts of extra products).
// So we are left with somewhat shoddy time-based queries.
markAsSearching(true);
ui.m_statusWidget->setBusy(i18nc("@info:status", "Searching for duplicates..."));
// Grab the default severity for newbugs
static QString severity = reportInterface()->newBugReportTemplate().severity;
bugzillaManager()->searchBugs(reportInterface()->relatedBugzillaProducts(),
severity,
reportInterface()->firstBacktraceFunctions().join(QLatin1Char(' ')),
m_offset);
}
void BugzillaDuplicatesPage::stopCurrentSearch()
{
if (m_searching) {
bugzillaManager()->stopCurrentSearch();
markAsSearching(false);
if (m_offset < 0) { //Never searched
ui.m_statusWidget->setIdle(i18nc("@info:status", "Search stopped."));
} else {
ui.m_statusWidget->setIdle(i18nc("@info:status", "Search stopped. Showing results."));
}
}
}
void BugzillaDuplicatesPage::markAsSearching(bool searching)
{
m_searching = searching;
emitCompleteChanged();
ui.m_bugListWidget->setEnabled(!searching);
ui.m_searchMoreButton->setEnabled(!searching);
ui.m_searchMoreButton->setVisible(!searching);
ui.m_stopSearchButton->setEnabled(searching);
ui.m_stopSearchButton->setVisible(searching);
ui.m_selectedDuplicatesList->setEnabled(!searching);
ui.m_selectedPossibleDuplicatesLabel->setEnabled(!searching);
ui.m_removeSelectedDuplicateButton->setEnabled(!searching &&
!ui.m_selectedDuplicatesList->selectedItems().isEmpty());
ui.m_attachToReportLabel->setEnabled(!searching);
if (!searching) {
itemSelectionChanged();
} else {
ui.m_openReportButton->setEnabled(false);
}
}
bool BugzillaDuplicatesPage::canSearchMore()
{
return !m_atEnd;
}
static QString statusString(const Bugzilla::Bug::Ptr &bug)
{
// Generate a non-geek readable status
switch(bug->status()) {
case Bugzilla::Bug::Status::UNCONFIRMED:
case Bugzilla::Bug::Status::CONFIRMED:
case Bugzilla::Bug::Status::ASSIGNED:
case Bugzilla::Bug::Status::REOPENED:
return i18nc("@info bug status", "[Open]");
case Bugzilla::Bug::Status::RESOLVED:
case Bugzilla::Bug::Status::VERIFIED:
case Bugzilla::Bug::Status::CLOSED:
switch(bug->resolution()) {
case Bugzilla::Bug::Resolution::FIXED:
return i18nc("@info bug resolution", "[Fixed]");
case Bugzilla::Bug::Resolution::WORKSFORME:
return i18nc("@info bug resolution", "[Non-reproducible]");
case Bugzilla::Bug::Resolution::DUPLICATE:
return i18nc("@info bug resolution", "[Duplicate report]");
case Bugzilla::Bug::Resolution::INVALID:
return i18nc("@info bug resolution", "[Invalid]");
case Bugzilla::Bug::Resolution::UPSTREAM:
case Bugzilla::Bug::Resolution::DOWNSTREAM:
return i18nc("@info bug resolution", "[External problem]");
case Bugzilla::Bug::Resolution::WONTFIX:
case Bugzilla::Bug::Resolution::LATER:
case Bugzilla::Bug::Resolution::REMIND:
case Bugzilla::Bug::Resolution::MOVED:
case Bugzilla::Bug::Resolution::WAITINGFORINFO:
case Bugzilla::Bug::Resolution::BACKTRACE:
case Bugzilla::Bug::Resolution::UNMAINTAINED:
case Bugzilla::Bug::Resolution::NONE:
return QStringLiteral("[%1]").arg(QVariant::fromValue(bug->resolution()).toString());
case Bugzilla::Bug::Resolution::Unknown:
break;
}
break;
case Bugzilla::Bug::Status::NEEDSINFO:
return i18nc("@info bug status", "[Incomplete]");
case Bugzilla::Bug::Status::Unknown:
break;
}
return QString();
}
void BugzillaDuplicatesPage::searchFinished(const QList &list)
{
KGuiItem::assign(ui.m_searchMoreButton, m_searchMoreGuiItem);
int results = list.count();
m_offset += results;
if (results > 0) {
m_atEnd = false;
markAsSearching(false);
ui.m_statusWidget->setIdle(i18nc("@info:status", "Showing results."));
for (int i = 0; i < results; i++) {
Bugzilla::Bug::Ptr bug = list.at(i);
QString title = statusString(bug) + QLatin1Char(' ') + bug->summary();
QStringList fields = QStringList() << QString::number(bug->id()) << title;
QTreeWidgetItem * item = new QTreeWidgetItem(fields);
item->setToolTip(0, bug->summary());
item->setToolTip(1, bug->summary());
ui.m_bugListWidget->addTopLevelItem(item);
}
if (!m_foundDuplicate) {
markAsSearching(true);
DuplicateFinderJob *job = new DuplicateFinderJob(list, bugzillaManager(), this);
connect(job, &KJob::result, this, &BugzillaDuplicatesPage::analyzedDuplicates);
job->start();
}
ui.m_bugListWidget->sortItems(0 , Qt::DescendingOrder);
ui.m_bugListWidget->resizeColumnToContents(1);
if (!canSearchMore()) {
ui.m_searchMoreButton->setEnabled(false);
}
} else {
m_atEnd = true;
if (canSearchMore()) {
//We don't call markAsSearching(false) to avoid flicker
//Delayed call to searchMore to avoid unexpected behaviour (signal/slot)
//because we are in a slot, and searchMore() will be ending calling this slot again
QTimer::singleShot(0, this, &BugzillaDuplicatesPage::searchMore);
} else {
markAsSearching(false);
ui.m_statusWidget->setIdle(i18nc("@info:status","Search Finished. "
"No reports found."));
ui.m_searchMoreButton->setEnabled(false);
if (ui.m_bugListWidget->topLevelItemCount() == 0) {
//No reports to mark as possible duplicate
ui.m_selectedDuplicatesList->setEnabled(false);
}
}
}
}
static bool isStatusOpen(Bugzilla::Bug::Status status)
{
switch(status) {
case Bugzilla::Bug::Status::UNCONFIRMED:
case Bugzilla::Bug::Status::CONFIRMED:
case Bugzilla::Bug::Status::ASSIGNED:
case Bugzilla::Bug::Status::REOPENED:
return true;
case Bugzilla::Bug::Status::RESOLVED:
case Bugzilla::Bug::Status::NEEDSINFO:
case Bugzilla::Bug::Status::VERIFIED:
case Bugzilla::Bug::Status::CLOSED:
return false;
case Bugzilla::Bug::Status::Unknown:
break;
}
return false;
}
static bool isStatusClosed(Bugzilla::Bug::Status status)
{
return !isStatusOpen(status);
}
void BugzillaDuplicatesPage::analyzedDuplicates(KJob *j)
{
markAsSearching(false);
DuplicateFinderJob *job = static_cast(j);
m_result = job->result();
m_foundDuplicate = m_result.parentDuplicate;
reportInterface()->setDuplicateId(m_result.parentDuplicate);
ui.m_searchMoreButton->setEnabled(!m_foundDuplicate);
ui.information->setVisible(m_foundDuplicate);
auto status = m_result.status;
const int duplicate = m_result.duplicate;
const int parentDuplicate = m_result.parentDuplicate;
if (m_foundDuplicate) {
const QList items = ui.m_bugListWidget->findItems(QString::number(parentDuplicate), Qt::MatchExactly, 0);
const QBrush brush = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NeutralBackground);
Q_FOREACH (QTreeWidgetItem* item, items) {
for (int i = 0; i < item->columnCount(); ++i) {
item->setBackground(i, brush);
}
}
QString text;
if (isStatusOpen(status) || status == Bugzilla::Bug::Status::NEEDSINFO) {
text = (parentDuplicate == duplicate ? i18nc("@label", "Your crash is a duplicate and has already been reported as Bug %1.", QString::number(duplicate)) :
i18nc("@label", "Your crash has already been reported as Bug %1, which is a duplicate of Bug %2", QString::number(duplicate), QString::number(parentDuplicate))) +
QLatin1Char('\n') + i18nc("@label", "Only attach if you can add needed information to the bug report.", QStringLiteral("attach"));
} else if (isStatusClosed(status)) {
text = (parentDuplicate == duplicate ? i18nc("@label", "Your crash has already been reported as Bug %1 which has been closed.", QString::number(duplicate)) :
i18nc("@label", "Your crash has already been reported as Bug %1, which is a duplicate of the closedBug %2.", QString::number(duplicate), QString::number(parentDuplicate)));
}
ui.information->setText(text);
}
}
void BugzillaDuplicatesPage::informationClicked(const QString &activatedLink)
{
if (activatedLink == QLatin1String("attach")) {
attachToBugReport(m_result.parentDuplicate);
} else {
int number = activatedLink.toInt();
if (number) {
showReportInformationDialog(number, false);
}
}
}
void BugzillaDuplicatesPage::searchError(QString err)
{
KGuiItem::assign(ui.m_searchMoreButton, m_retrySearchGuiItem);
markAsSearching(false);
ui.m_statusWidget->setIdle(i18nc("@info:status","Error fetching the bug report list"));
KMessageBox::error(this , xi18nc("@info/rich","Error fetching the bug report list"
"%1."
"Please wait some time and try again.", err));
}
//END Search related methods
//BEGIN Duplicates list related methods
void BugzillaDuplicatesPage::openSelectedReport()
{
QList selected = ui.m_bugListWidget->selectedItems();
if (selected.count() == 1) {
itemClicked(selected.at(0), 0);
}
}
void BugzillaDuplicatesPage::itemClicked(QTreeWidgetItem * item, int col)
{
Q_UNUSED(col);
int bugNumber = 0;
if (item->data(0, Qt::UserRole) == QLatin1String("custom")) {
bool ok = false;
bugNumber = QInputDialog::getInt(this,
i18nc("@title:window", "Enter a custom bug report number"),
i18nc("@label", "Enter the number of the bug report you want to check"),
0, 0, 1000000, 1, &ok);
} else {
bugNumber = item->text(0).toInt();
}
showReportInformationDialog(bugNumber);
}
void BugzillaDuplicatesPage::itemClicked(QListWidgetItem * item)
{
showReportInformationDialog(item->text().toInt());
}
void BugzillaDuplicatesPage::showReportInformationDialog(int bugNumber, bool relatedButtonEnabled)
{
if (bugNumber <= 0) {
return;
}
BugzillaReportInformationDialog * infoDialog = new BugzillaReportInformationDialog(this);
connect(infoDialog, &BugzillaReportInformationDialog::possibleDuplicateSelected, this,
&BugzillaDuplicatesPage::addPossibleDuplicateNumber);
connect(infoDialog, &BugzillaReportInformationDialog::attachToBugReportSelected, this, &BugzillaDuplicatesPage::attachToBugReport);
infoDialog->showBugReport(bugNumber, relatedButtonEnabled);
}
void BugzillaDuplicatesPage::itemSelectionChanged()
{
ui.m_openReportButton->setEnabled(ui.m_bugListWidget->selectedItems().count() == 1);
}
//END Duplicates list related methods
//BEGIN Selected duplicates list related methods
void BugzillaDuplicatesPage::addPossibleDuplicateNumber(int bugNumber)
{
QString stringNumber = QString::number(bugNumber);
if (ui.m_selectedDuplicatesList->findItems(stringNumber, Qt::MatchExactly).isEmpty()) {
ui.m_selectedDuplicatesList->addItem(stringNumber);
}
showDuplicatesPanel(true);
}
void BugzillaDuplicatesPage::removeSelectedDuplicate()
{
QList items = ui.m_selectedDuplicatesList->selectedItems();
if (items.length() > 0) {
delete ui.m_selectedDuplicatesList->takeItem(ui.m_selectedDuplicatesList->row(items.at(0)));
}
if (ui.m_selectedDuplicatesList->count() == 0) {
showDuplicatesPanel(false);
}
}
void BugzillaDuplicatesPage::showDuplicatesPanel(bool show)
{
ui.m_removeSelectedDuplicateButton->setVisible(show);
ui.m_selectedDuplicatesList->setVisible(show);
ui.m_selectedPossibleDuplicatesLabel->setVisible(show);
}
void BugzillaDuplicatesPage::possibleDuplicateSelectionChanged()
{
ui.m_removeSelectedDuplicateButton->setEnabled(
!ui.m_selectedDuplicatesList->selectedItems().isEmpty());
}
//END Selected duplicates list related methods
//BEGIN Attach to bug related methods
void BugzillaDuplicatesPage::attachToBugReport(int bugNumber)
{
ui.m_attachToReportLabel->setText(xi18nc("@label", "The report is going to be "
"attached to bug %1. "
"Cancel", QString::number(bugNumber)));
ui.m_attachToReportLabel->setVisible(true);
ui.m_attachToReportIcon->setVisible(true);
reportInterface()->setAttachToBugNumber(bugNumber);
}
void BugzillaDuplicatesPage::cancelAttachToBugReport()
{
ui.m_attachToReportLabel->setVisible(false);
ui.m_attachToReportIcon->setVisible(false);
reportInterface()->setAttachToBugNumber(0);
}
//END Attach to bug related methods
//END BugzillaDuplicatesPage
//BEGIN BugzillaReportInformationDialog
BugzillaReportInformationDialog::BugzillaReportInformationDialog(BugzillaDuplicatesPage * parent) :
QDialog(parent),
m_relatedButtonEnabled(true),
m_parent(parent),
m_bugNumber(0),
m_duplicatesCount(0)
{
setWindowTitle(i18nc("@title:window","Bug Description"));
ui.setupUi(this);
KGuiItem::assign(ui.m_retryButton, KGuiItem2(i18nc("@action:button", "Retry..."),
QIcon::fromTheme(QStringLiteral("view-refresh")),
i18nc("@info:tooltip", "Use this button to retry "
"loading the bug report.")));
connect(ui.m_retryButton, &QPushButton::clicked, this, &BugzillaReportInformationDialog::reloadReport);
m_suggestButton = new QPushButton(this);
ui.buttonBox->addButton(m_suggestButton, QDialogButtonBox::ActionRole);
KGuiItem::assign(m_suggestButton,
KGuiItem2(i18nc("@action:button", "Suggest this crash is related"),
QIcon::fromTheme(QStringLiteral("list-add")), i18nc("@info:tooltip", "Use this button to suggest that "
"the crash you experienced is related to this bug "
"report")));
connect(m_suggestButton, &QPushButton::clicked, this, &BugzillaReportInformationDialog::relatedReportClicked);
connect(ui.m_showOwnBacktraceCheckBox, &QAbstractButton::toggled, this, &BugzillaReportInformationDialog::toggleShowOwnBacktrace);
//Connect bugzillalib signals
connect(m_parent->bugzillaManager(), &BugzillaManager::bugReportFetched,
this, &BugzillaReportInformationDialog::bugFetchFinished);
connect(m_parent->bugzillaManager(), &BugzillaManager::bugReportError,
this, &BugzillaReportInformationDialog::bugFetchError);
connect(m_parent->bugzillaManager(), &BugzillaManager::commentsFetched,
this, &BugzillaReportInformationDialog::onCommentsFetched);
connect(m_parent->bugzillaManager(), &BugzillaManager::commentsError,
this, &BugzillaReportInformationDialog::bugFetchError);
KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog");
KWindowConfig::restoreWindowSize(windowHandle(), config);
}
BugzillaReportInformationDialog::~BugzillaReportInformationDialog()
{
KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog");
KWindowConfig::saveWindowSize(windowHandle(), config);
}
void BugzillaReportInformationDialog::reloadReport()
{
showBugReport(m_bugNumber);
}
void BugzillaReportInformationDialog::showBugReport(int bugNumber, bool relatedButtonEnabled)
{
m_relatedButtonEnabled = relatedButtonEnabled;
ui.m_retryButton->setVisible(false);
m_closedStateString.clear();
m_bugNumber = bugNumber;
m_parent->bugzillaManager()->fetchBugReport(m_bugNumber, this);
m_suggestButton->setEnabled(false);
m_suggestButton->setVisible(m_relatedButtonEnabled);
ui.m_infoBrowser->setText(i18nc("@info:status","Loading..."));
ui.m_infoBrowser->setEnabled(false);
ui.m_linkLabel->setText(xi18nc("@info","Report's webpage",
m_parent->bugzillaManager()->urlForBug(m_bugNumber)));
ui.m_statusWidget->setBusy(xi18nc("@info:status","Loading information about bug "
"%1 from %2....",
QString::number(m_bugNumber),
QLatin1String(KDE_BUGZILLA_SHORT_URL)));
ui.m_backtraceBrowser->setPlainText(
i18nc("@info","Backtrace of the crash I experienced:\n\n") +
m_parent->reportInterface()->backtrace());
KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog");
bool showOwnBacktrace = config.readEntry("ShowOwnBacktrace", false);
ui.m_showOwnBacktraceCheckBox->setChecked(showOwnBacktrace);
if (!showOwnBacktrace) { //setChecked(false) will not emit toggled(false)
toggleShowOwnBacktrace(false);
}
show();
}
struct Status2 {
QString statusString;
QString closedStateString;
};
static Status2 statusString2(const Bugzilla::Bug::Ptr &bug)
{
// Generate a non-geek readable status
switch(bug->status()) {
case Bugzilla::Bug::Status::UNCONFIRMED:
return { i18nc("@info bug status", "Opened (Unconfirmed)"), QString() };
case Bugzilla::Bug::Status::CONFIRMED:
case Bugzilla::Bug::Status::ASSIGNED:
case Bugzilla::Bug::Status::REOPENED:
return { i18nc("@info bug status", "Opened (Unfixed)"), QString() };
case Bugzilla::Bug::Status::RESOLVED:
case Bugzilla::Bug::Status::VERIFIED:
case Bugzilla::Bug::Status::CLOSED:
switch(bug->resolution()) {
case Bugzilla::Bug::Resolution::FIXED: {
auto fixedIn = bug->customField("cf_versionfixedin").toString();
if (!fixedIn.isEmpty()) {
return { i18nc("@info bug resolution, fixed in version",
"Fixed in version \"%1\"",
fixedIn),
i18nc("@info bug resolution, fixed by kde devs in version",
"the bug was fixed by KDE developers in version \"%1\"",
fixedIn)
};
}
return {
i18nc("@info bug resolution", "Fixed"),
i18nc("@info bug resolution", "the bug was fixed by KDE developers")
};
}
case Bugzilla::Bug::Resolution::WORKSFORME:
return { i18nc("@info bug resolution", "Non-reproducible"), QString() };
case Bugzilla::Bug::Resolution::DUPLICATE:
return { i18nc("@info bug resolution", "Duplicate report (Already reported before)"), QString() };
case Bugzilla::Bug::Resolution::INVALID:
return { i18nc("@info bug resolution", "Not a valid report/crash"), QString() };
case Bugzilla::Bug::Resolution::UPSTREAM:
case Bugzilla::Bug::Resolution::DOWNSTREAM:
return { i18nc("@info bug resolution", "Not caused by a problem in the KDE's Applications or libraries"),
i18nc("@info bug resolution", "the bug is caused by a problem in an external application or library, or by a distribution or packaging issue") };
case Bugzilla::Bug::Resolution::WONTFIX:
case Bugzilla::Bug::Resolution::LATER:
case Bugzilla::Bug::Resolution::REMIND:
case Bugzilla::Bug::Resolution::MOVED:
case Bugzilla::Bug::Resolution::WAITINGFORINFO:
case Bugzilla::Bug::Resolution::BACKTRACE:
case Bugzilla::Bug::Resolution::UNMAINTAINED:
case Bugzilla::Bug::Resolution::NONE:
return { QVariant::fromValue(bug->resolution()).toString(), QString() };
case Bugzilla::Bug::Resolution::Unknown:
break;
}
return {};
case Bugzilla::Bug::Status::NEEDSINFO:
return { i18nc("@info bug status", "Temporarily closed, because of a lack of information"), QString() };
case Bugzilla::Bug::Status::Unknown:
break;
}
return {};
}
void BugzillaReportInformationDialog::bugFetchFinished(Bugzilla::Bug::Ptr bug, QObject *jobOwner)
{
if (jobOwner != this || !isVisible()) {
return;
}
if (!bug) {
bugFetchError(i18nc("@info", "Invalid report information (malformed data). This could "
"mean that the bug report does not exist, or the bug tracking site "
"is experiencing a problem."), this);
return;
}
Q_ASSERT(!m_bug); // m_bug must only be set once we've selected one!
// Handle duplicate state
if (bug->dupe_of() > 0) {
ui.m_statusWidget->setIdle(QString());
KGuiItem yesItem = KStandardGuiItem::yes();
yesItem.setText(i18nc("@action:button let the user to choose to read the "
"main report", "Yes, read the main report"));
KGuiItem noItem = KStandardGuiItem::no();
noItem.setText(i18nc("@action:button let the user choose to read the original "
"report", "No, let me read the report I selected"));
auto ret = KMessageBox::questionYesNo(
this,
xi18nc("@info","The report you selected (bug %1) is already "
"marked as duplicate of bug %2. "
"Do you want to read that report instead? (recommended)",
bug->id(), QString::number(bug->dupe_of())),
i18nc("@title:window","Nested duplicate detected"),
yesItem,
noItem);
if (ret == KMessageBox::Yes) {
qDebug() << "REDIRECT";
showBugReport(bug->dupe_of());
return;
}
}
// Process comments...
m_bug = bug;
m_parent->bugzillaManager()->fetchComments(m_bug, this);
}
void BugzillaReportInformationDialog::onCommentsFetched(QList bugComments,
QObject *jobOwner)
{
if (jobOwner != this || !isVisible()) {
return;
}
Q_ASSERT(m_bug);
// Generate html for comments (with proper numbering)
QLatin1String duplicatesMark = QLatin1String("has been marked as a duplicate of this bug.");
// TODO: the way comment objects are turned into comment strings is fairly
// awkward and does not particularly object-centric. May benefit from a
// slight redesign.
QString comments;
QString description; // aka first comment
if (bugComments.size() > 0) {
description = bugComments.takeFirst()->text();
}
for (auto it = bugComments.constBegin(); it != bugComments.constEnd(); ++it) {
QString comment = (*it)->text();
// Don't add duplicates mark comments
if (!comment.contains(duplicatesMark)) {
comment.replace(QLatin1Char('\n'), QLatin1String(" "));
const int i = it - bugComments.constBegin();
comments += i18nc("comment $number to use as subtitle", "
Comment %1:
", (i+1))
+ QStringLiteral("
") + comment + QStringLiteral("
");
// Count the inline attached crashes (DrKonqi feature)
QLatin1String attachedCrashMark =
QLatin1String("New crash information added by DrKonqi");
if (comment.contains(attachedCrashMark)) {
m_duplicatesCount++;
}
} else {
// Count duplicate
m_duplicatesCount++;
}
}
// Generate a non-geek readable status
auto str = statusString2(m_bug);
QString customStatusString = str.statusString;
m_closedStateString = str.closedStateString;
// Generate notes
QString notes = xi18n("
The bug report's title is often written by its reporter "
"and may not reflect the bug's nature, root cause or other visible "
"symptoms you could use to compare to your crash. Please read the "
"complete report and all the comments below.
");
if (m_duplicatesCount >= 10) { //Consider a possible mass duplicate crash
notes += xi18np("
This bug report has %1 duplicate report. That means this "
"is probably a common crash. Please consider only "
"adding a comment or a note if you can provide new valuable "
"information which was not already mentioned.
",
"
This bug report has %1 duplicate reports. That means this "
"is probably a common crash. Please consider only "
"adding a comment or a note if you can provide new valuable "
"information which was not already mentioned.
",
m_duplicatesCount);
}
// A manually entered bug ID could represent a normal bug
if (m_bug->severity() != QLatin1String("crash")
&& m_bug->severity() != QLatin1String("major")
&& m_bug->severity() != QLatin1String("grave")
&& m_bug->severity() != QLatin1String("critical"))
{
notes += xi18n("
This bug report is not about a crash or about any other "
"critical bug.
");
}
// Generate HTML text
QString text =
i18nc("@info bug report title (quoted)",
"