kde-workspace/drkonqi/reportassistantpages_base.cpp
2014-11-15 04:16:00 +02:00

465 lines
19 KiB
C++

/*******************************************************************
* reportassistantpages_base.cpp
* Copyright 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
* Copyright 2009 A. L. Spehr <spehr@kde.org>
*
* 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 <http://www.gnu.org/licenses/>.
*
******************************************************************/
#include "reportassistantpages_base.h"
#include <QLabel>
#include <QCheckBox>
#include <QToolTip>
#include <KToolInvocation>
#include <KIcon>
#include <KUrl>
#include <KMessageBox>
#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(KIcon("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, SIGNAL(stateChanged()) , this, SLOT(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 ((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
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()));
connect(ui.m_rememberGroup, SIGNAL(buttonClicked(int)), this, SLOT(updateCheckBoxes()));
ui.m_appSpecificDetailsExamples->setVisible(reportInterface()->appDetailsExamples()->hasExamples());
connect(ui.m_appSpecificDetailsExamples, SIGNAL(linkActivated(QString)), this,
SLOT(showApplicationDetailsExamples()));
}
void BugAwarenessPage::aboutToShow()
{
updateCheckBoxes();
}
void BugAwarenessPage::aboutToHide()
{
//Save data
ReportInterface::Reproducible reproducible = ReportInterface::ReproducibleUnsure;
switch(ui.m_reproducibleBox->currentIndex()) {
case 0: {
reproducible = ReportInterface::ReproducibleUnsure;
break;
}
case 1: {
reproducible = ReportInterface::ReproducibleNever;
break;
}
case 2: {
reproducible = ReportInterface::ReproducibleSometimes;
break;
}
case 3: {
reproducible = ReportInterface::ReproducibleEverytime;
break;
}
}
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);
}
void BugAwarenessPage::showApplicationDetailsExamples()
{
QToolTip::showText(QCursor::pos(),
i18nc("@label examples about information the user can provide",
"Examples: %1", reportInterface()->appDetailsExamples()->examples()),
this);
}
//END BugAwarenessPage
//BEGIN ConclusionPage
ConclusionPage::ConclusionPage(ReportAssistantDialog * parent)
: ReportAssistantPage(parent)
, m_needToReport(false)
{
m_isBKO = DrKonqi::crashedApplication()->bugReportAddress().isKdeBugzilla();
ui.setupUi(this);
ui.m_showReportInformationButton->setGuiItem(
KGuiItem2(i18nc("@action:button", "&Show Contents of the Report"),
KIcon("document-preview"),
i18nc("@info:tooltip", "Use this button to show the generated "
"report information about this crash.")));
connect(ui.m_showReportInformationButton, SIGNAL(clicked()),
this, SLOT(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(false);
if (reportAddress.isEmail()) {
QString subject = "[%1] [%2] Automatic crash report generated by DrKonqi";
subject= subject.arg(crashedApp->name());
subject= subject.arg(crashedApp->datetime().toString("yyyy-MM-dd"));
KToolInvocation::invokeMailer(reportAddress, "", "" , subject, report);
} else {
if (KUrl(reportAddress).isRelative()) { //Scheme is missing
reportAddress = QString::fromLatin1("http://%1").arg(reportAddress);
}
KToolInvocation::invokeBrowser(reportAddress);
}
//Show a copy of the bug reported
openReportInformation();
}
//Restart application
if (ui.m_restartAppOnFinish->isChecked()) {
DrKonqi::crashedApplication()->restart();
}
}
void ConclusionPage::aboutToShow()
{
connect(assistant(), SIGNAL(user1Clicked()), this, SLOT(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("<p><ul>");
bool backtraceGenerated = true;
switch (use) {
case BacktraceParser::ReallyUseful: {
explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","The automatically generated "
"crash information is useful."));
break;
}
case BacktraceParser::MayBeUseful: {
explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","The automatically generated "
"crash information lacks some "
"details "
"but may be still be useful."));
break;
}
case BacktraceParser::ProbablyUseless: {
explanationHTML += QString("<li>%1</li>").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 += QString("<li>%1</li>").arg(i18nc("@info","The crash information was "
"not generated because it was not needed."));
} else {
explanationHTML += QString("<li>%1<br />%2</li>").arg(
i18nc("@info","The automatically generated crash "
"information does not contain enough information to be "
"helpful."),
i18nc("@info","<note>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 "
"<interface>Help</interface> button.</note>"));
//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 += QString("<li>%1</li>").arg(i18nc("@info","The information you can "
"provide could be considered helpful."));
} else {
explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","The information you can "
"provide is not considered helpful enough in this case."));
}
if (isDuplicate) {
explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","Your problem has already been "
"reported as bug <numid>%1</numid>.", reportInterface()->duplicateId()));
}
explanationHTML += QLatin1String("</ul></p>");
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(QString("<p><strong>%1</strong>").arg(i18nc("@info","This "
"report is considered helpful.")));
if (m_isBKO) {
emitCompleteChanged();
ui.m_howToProceedLabel->setText(i18nc("@info","This application's bugs are reported "
"to the KDE bug tracking system: click <interface>Next"
"</interface> to start the reporting process. "
"You can manually report at <link>%1</link>",
reportAddress));
} else {
if (!DrKonqi::crashedApplication()->hasBeenRestarted()) {
ui.m_restartAppOnFinish->setVisible(true);
}
ui.m_howToProceedLabel->setText(i18nc("@info","This application is not supported in the "
"KDE bug tracking system. Click <interface>"
"Finish</interface> to report this bug to "
"the application maintainer. Also, you can manually "
"report at <link>%1</link>.", reportAddress));
emit finished(false);
}
} else { // (m_needToReport)
if (!DrKonqi::crashedApplication()->hasBeenRestarted()) {
ui.m_restartAppOnFinish->setVisible(true);
}
ui.m_conclusionsLabel->setText(QString("<p><strong>%1</strong><br />%2</p>").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(i18nc("@info","You can manually report this bug "
"at <link>%1</link>. "
"Click <interface>Finish</interface> to close the "
"assistant.",
reportAddress));
} else {
ui.m_howToProceedLabel->setText(i18nc("@info","You can manually report this "
"bug to its maintainer at <link>%1</link>. "
"Click <interface>Finish</interface> to close the "
"assistant.", reportAddress));
}
}
emit finished(true);
}
}
void ConclusionPage::aboutToHide()
{
assistant()->disconnect(SIGNAL(user1Clicked()), this, SLOT(finishClicked()));
}
void ConclusionPage::openReportInformation()
{
if (!m_infoDialog) {
QString info = reportInterface()->generateReportFullText(false) + QLatin1Char('\n') +
i18nc("@info/plain 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)
: KDialog()
{
KGlobal::ref();
setAttribute(Qt::WA_DeleteOnClose, true);
setButtons(KDialog::Close | KDialog::User1);
setDefaultButton(KDialog::Close);
setCaption(i18nc("@title:window","Contents of the Report"));
ui.setupUi(mainWidget());
ui.m_reportInformationBrowser->setPlainText(reportText);
setButtonGuiItem(KDialog::User1, KGuiItem2(i18nc("@action:button", "&Save to File..."),
KIcon("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(this, SIGNAL(user1Clicked()), this, SLOT(saveReport()));
setInitialSize(QSize(800, 600));
KConfigGroup config(KGlobal::config(), "ReportInformationDialog");
restoreDialogSize(config);
}
ReportInformationDialog::~ReportInformationDialog()
{
KConfigGroup config(KGlobal::config(), "ReportInformationDialog");
saveDialogSize(config);
KGlobal::deref();
}
void ReportInformationDialog::saveReport()
{
DrKonqi::saveReport(ui.m_reportInformationBrowser->toPlainText(), this);
}
//END ReportInformationDialog