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("

    "); bool backtraceGenerated = true; switch (use) { case BacktraceParser::ReallyUseful: { explanationHTML += QStringLiteral("
  • %1
  • ").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 closed Bug %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)", "

\"%1\"

", m_bug->summary()) + notes + i18nc("@info bug report status", "

Bug Report Status: %1

", customStatusString) + i18nc("@info bug report product and component", "

Affected Component: %1 (%2)

", m_bug->product(), m_bug->component()) + i18nc("@info bug report description", "

Description of the bug

%1

", description.replace(QLatin1Char('\n'), QLatin1String("
"))); if (!comments.isEmpty()) { text += i18nc("@label:textbox bug report comments (already formatted)", "

Additional Comments

%1", comments); } ui.m_infoBrowser->setText(text); ui.m_infoBrowser->setEnabled(true); m_suggestButton->setEnabled(m_relatedButtonEnabled); m_suggestButton->setVisible(m_relatedButtonEnabled); ui.m_statusWidget->setIdle(xi18nc("@info:status", "Showing bug %1", QString::number(m_bug->id()))); } void BugzillaReportInformationDialog::markAsDuplicate() { emit possibleDuplicateSelected(m_bugNumber); hide(); } void BugzillaReportInformationDialog::attachToBugReport() { emit attachToBugReportSelected(m_bugNumber); hide(); } void BugzillaReportInformationDialog::cancelAssistant() { m_parent->assistant()->close(); hide(); } void BugzillaReportInformationDialog::relatedReportClicked() { BugzillaReportConfirmationDialog * confirmation = new BugzillaReportConfirmationDialog(m_bugNumber, (m_duplicatesCount >= 10), m_closedStateString, this); confirmation->show(); } void BugzillaReportInformationDialog::bugFetchError(QString err, QObject * jobOwner) { if (jobOwner == this && isVisible()) { KMessageBox::error(this , xi18nc("@info/rich","Error fetching the bug report" "%1." "Please wait some time and try again.", err)); m_suggestButton->setEnabled(false); ui.m_infoBrowser->setText(i18nc("@info","Error fetching the bug report")); ui.m_statusWidget->setIdle(i18nc("@info:status","Error fetching the bug report")); ui.m_retryButton->setVisible(true); } } void BugzillaReportInformationDialog::toggleShowOwnBacktrace(bool show) { QList sizes; if (show) { int size = (ui.m_reportSplitter->sizeHint().width()-ui.m_reportSplitter->handleWidth())/2; sizes << size << size; } else { sizes << ui.m_reportSplitter->sizeHint().width() << 0; //Hide backtrace } ui.m_reportSplitter->setSizes(sizes); //Save the current show value KConfigGroup config(KSharedConfig::openConfig(), "BugzillaReportInformationDialog"); config.writeEntry("ShowOwnBacktrace", show); } //END BugzillaReportInformationDialog //BEGIN BugzillaReportConfirmationDialog BugzillaReportConfirmationDialog::BugzillaReportConfirmationDialog(int bugNumber, bool commonCrash, QString closedState, BugzillaReportInformationDialog * parent) : QDialog(parent), m_parent(parent), m_showProceedQuestion(false), m_bugNumber(bugNumber) { setAttribute(Qt::WA_DeleteOnClose, true); setModal(true); ui.setupUi(this); //Setup dialog setWindowTitle(i18nc("@title:window", "Related Bug Report")); //Setup buttons ui.buttonBox->button(QDialogButtonBox::Cancel)->setText(i18nc("@action:button", "Cancel (Go back to the report)")); ui.buttonBox->button(QDialogButtonBox::Ok)->setText(i18nc("@action:button continue with the selected option " "and close the dialog", "Continue")); ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); connect(this, &BugzillaReportConfirmationDialog::accepted, this, &BugzillaReportConfirmationDialog::proceedClicked); connect(this, &BugzillaReportConfirmationDialog::rejected, this, &BugzillaReportConfirmationDialog::hide); //Set introduction text ui.introLabel->setText(i18n("You are going to mark your crash as related to bug %1", QString::number(m_bugNumber))); if (commonCrash) { //Common ("massive") crash m_showProceedQuestion = true; ui.commonCrashIcon->setPixmap(QIcon::fromTheme(QStringLiteral("edit-bomb")).pixmap(22,22)); } else { ui.commonCrashLabel->setVisible(false); ui.commonCrashIcon->setVisible(false); } if (!closedState.isEmpty()) { //Bug report closed ui.closedReportLabel->setText( i18nc("@info", "The report is closed because %1. " "If the crash is the same, adding further information will be useless " "and will consume developers' time.", closedState)); ui.closedReportIcon->setPixmap(QIcon::fromTheme(QStringLiteral("document-close")).pixmap(22,22)); m_showProceedQuestion = true; } else { ui.closedReportLabel->setVisible(false); ui.closedReportIcon->setVisible(false); } //Disable all the radio buttons ui.proceedRadioYes->setChecked(false); ui.proceedRadioNo->setChecked(false); ui.markAsDuplicateCheck->setChecked(false); ui.attachToBugReportCheck->setChecked(false); connect(ui.buttonGroupProceed, SIGNAL(buttonClicked(int)), this, SLOT(checkProceed())); connect(ui.buttonGroupProceedQuestion, SIGNAL(buttonClicked(int)), this, SLOT(checkProceed())); -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) // Also listen to toggle so radio buttons are covered. connect(ui.buttonGroupProceed, static_cast(&QButtonGroup::buttonToggled), this, &BugzillaReportConfirmationDialog::checkProceed); connect(ui.buttonGroupProceedQuestion, static_cast(&QButtonGroup::buttonToggled), this, &BugzillaReportConfirmationDialog::checkProceed); -#else - // Also listen to toggle so radio buttons are covered. - connect(ui.buttonGroupProceed, &QButtonGroup::buttonToggled, - this, &BugzillaReportConfirmationDialog::checkProceed); - connect(ui.buttonGroupProceedQuestion, &QButtonGroup::buttonToggled, - this, &BugzillaReportConfirmationDialog::checkProceed); -#endif - if (!m_showProceedQuestion) { ui.proceedLabel->setEnabled(false); ui.proceedRadioYes->setEnabled(false); ui.proceedRadioNo->setEnabled(false); ui.proceedLabel->setVisible(false); ui.proceedRadioYes->setVisible(false); ui.proceedRadioNo->setVisible(false); ui.proceedRadioYes->setChecked(true); } checkProceed(); } BugzillaReportConfirmationDialog::~BugzillaReportConfirmationDialog() { } void BugzillaReportConfirmationDialog::checkProceed() { bool yes = ui.proceedRadioYes->isChecked(); bool no = ui.proceedRadioNo->isChecked(); //Enable/disable labels and controls ui.areYouSureLabel->setEnabled(yes); ui.markAsDuplicateCheck->setEnabled(yes); ui.attachToBugReportCheck->setEnabled(yes); //Enable Continue button if valid options are selected bool possibleDupe = ui.markAsDuplicateCheck->isChecked(); bool attach = ui.attachToBugReportCheck->isChecked(); bool enableContinueButton = yes ? (possibleDupe || attach) : no; ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enableContinueButton); } void BugzillaReportConfirmationDialog::proceedClicked() { if (ui.proceedRadioYes->isChecked()) { if (ui.markAsDuplicateCheck->isChecked()) { m_parent->markAsDuplicate(); hide(); } else { m_parent->attachToBugReport(); hide(); } } else { hide(); m_parent->cancelAssistant(); } } //END BugzillaReportConfirmationDialog