1/*******************************************************************
2* reportassistantpages_base.cpp
3* Copyright 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
4* Copyright 2009 A. L. Spehr <spehr@kde.org>
5*
6* This program is free software; you can redistribute it and/or
7* modify it under the terms of the GNU General Public License as
8* published by the Free Software Foundation; either version 2 of
9* the License, or (at your option) any later version.
10*
11* This program is distributed in the hope that it will be useful,
12* but WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14* GNU General Public License for more details.
15*
16* You should have received a copy of the GNU General Public License
17* along with this program. If not, see <http://www.gnu.org/licenses/>.
18*
19******************************************************************/
20
21#include "reportassistantpages_base.h"
22
23#include <QLabel>
24#include <QCheckBox>
25#include <QToolTip>
26
27#include <KToolInvocation>
28#include <KIcon>
29#include <KUrl>
30#include <KMessageBox>
31
32#include "drkonqi.h"
33#include "debuggermanager.h"
34#include "crashedapplication.h"
35#include "reportinterface.h"
36#include "parser/backtraceparser.h"
37#include "backtracegenerator.h"
38#include "backtracewidget.h"
39#include "drkonqi_globals.h"
40#include "applicationdetailsexamples.h"
41
42//BEGIN IntroductionPage
43
44IntroductionPage::IntroductionPage(ReportAssistantDialog * parent)
45 : ReportAssistantPage(parent)
46{
47 ui.setupUi(this);
48 ui.m_warningIcon->setPixmap(KIcon("dialog-warning").pixmap(64,64));
49}
50
51//END IntroductionPage
52
53//BEGIN CrashInformationPage
54
55CrashInformationPage::CrashInformationPage(ReportAssistantDialog * parent)
56 : ReportAssistantPage(parent)
57{
58 m_backtraceWidget = new BacktraceWidget(DrKonqi::debuggerManager()->backtraceGenerator(), this, true);
59 connect(m_backtraceWidget, SIGNAL(stateChanged()) , this, SLOT(emitCompleteChanged()));
60
61 QVBoxLayout *layout = new QVBoxLayout(this);
62 layout->setContentsMargins(0,0,0,0);
63 layout->addWidget(m_backtraceWidget);
64 layout->addSpacing(10); //We need this for better usability until we get something better
65
66 //If the backtrace was already fetched on the main dialog, save it.
67 BacktraceGenerator *btGenerator = DrKonqi::debuggerManager()->backtraceGenerator();
68 if (btGenerator->state() == BacktraceGenerator::Loaded) {
69 BacktraceParser::Usefulness use = btGenerator->parser()->backtraceUsefulness();
70 if (use != BacktraceParser::Useless && use != BacktraceParser::InvalidUsefulness) {
71 reportInterface()->setBacktrace(btGenerator->backtrace());
72 }
73 }
74}
75
76void CrashInformationPage::aboutToShow()
77{
78 m_backtraceWidget->generateBacktrace();
79 m_backtraceWidget->hilightExtraDetailsLabel(false);
80 emitCompleteChanged();
81}
82
83void CrashInformationPage::aboutToHide()
84{
85 BacktraceGenerator *btGenerator = DrKonqi::debuggerManager()->backtraceGenerator();
86 BacktraceParser::Usefulness use = btGenerator->parser()->backtraceUsefulness();
87
88 if (use != BacktraceParser::Useless && use != BacktraceParser::InvalidUsefulness) {
89 reportInterface()->setBacktrace(btGenerator->backtrace());
90 }
91 reportInterface()->setFirstBacktraceFunctions(btGenerator->parser()->firstValidFunctions());
92}
93
94bool CrashInformationPage::isComplete()
95{
96 BacktraceGenerator *generator = DrKonqi::debuggerManager()->backtraceGenerator();
97 return (generator->state() != BacktraceGenerator::NotLoaded &&
98 generator->state() != BacktraceGenerator::Loading);
99}
100
101bool CrashInformationPage::showNextPage()
102{
103 BacktraceParser::Usefulness use =
104 DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness();
105
106 if ((use == BacktraceParser::InvalidUsefulness || use == BacktraceParser::ProbablyUseless
107 || use == BacktraceParser::Useless) && m_backtraceWidget->canInstallDebugPackages()) {
108 if ( KMessageBox::Yes == KMessageBox::questionYesNo(this,
109 i18nc("@info","This crash information is not useful enough, "
110 "do you want to try to improve it? You will need "
111 "to install some debugging packages."),
112 i18nc("@title:window","Crash Information is not useful enough")) ) {
113 m_backtraceWidget->hilightExtraDetailsLabel(true);
114 m_backtraceWidget->focusImproveBacktraceButton();
115 return false; //Cancel show next, to allow the user to write more
116 } else {
117 return true; //Allow to continue
118 }
119 } else {
120 return true;
121 }
122}
123
124//END CrashInformationPage
125
126//BEGIN BugAwarenessPage
127
128BugAwarenessPage::BugAwarenessPage(ReportAssistantDialog * parent)
129 : ReportAssistantPage(parent)
130{
131 ui.setupUi(this);
132
133 ui.m_actionsInsideApp->setText(i18nc("@option:check kind of information the user can provide "
134 "about the crash, %1 is the application name",
135 "What I was doing when the application \"%1\" crashed",
136 DrKonqi::crashedApplication()->name()));
137
138 connect(ui.m_rememberGroup, SIGNAL(buttonClicked(int)), this, SLOT(updateCheckBoxes()));
139
140 ui.m_appSpecificDetailsExamples->setVisible(reportInterface()->appDetailsExamples()->hasExamples());
141
142 connect(ui.m_appSpecificDetailsExamples, SIGNAL(linkActivated(QString)), this,
143 SLOT(showApplicationDetailsExamples()));
144}
145
146void BugAwarenessPage::aboutToShow()
147{
148 updateCheckBoxes();
149}
150
151void BugAwarenessPage::aboutToHide()
152{
153 //Save data
154 ReportInterface::Reproducible reproducible = ReportInterface::ReproducibleUnsure;
155 switch(ui.m_reproducibleBox->currentIndex()) {
156 case 0: {
157 reproducible = ReportInterface::ReproducibleUnsure;
158 break;
159 }
160 case 1: {
161 reproducible = ReportInterface::ReproducibleNever;
162 break;
163 }
164 case 2: {
165 reproducible = ReportInterface::ReproducibleSometimes;
166 break;
167 }
168 case 3: {
169 reproducible = ReportInterface::ReproducibleEverytime;
170 break;
171 }
172 }
173
174 reportInterface()->setBugAwarenessPageData(ui.m_rememberCrashSituationYes->isChecked(),
175 reproducible,
176 ui.m_actionsInsideApp->isChecked(),
177 ui.m_unusualSituation->isChecked(),
178 ui.m_appSpecificDetails->isChecked());
179}
180
181void BugAwarenessPage::updateCheckBoxes()
182{
183 const bool rememberSituation = ui.m_rememberCrashSituationYes->isChecked();
184
185 ui.m_reproducibleLabel->setEnabled(rememberSituation);
186 ui.m_reproducibleBox->setEnabled(rememberSituation);
187
188 ui.m_informationLabel->setEnabled(rememberSituation);
189 ui.m_actionsInsideApp->setEnabled(rememberSituation);
190 ui.m_unusualSituation->setEnabled(rememberSituation);
191
192 ui.m_appSpecificDetails->setEnabled(rememberSituation);
193 ui.m_appSpecificDetailsExamples->setEnabled(rememberSituation);
194}
195
196void BugAwarenessPage::showApplicationDetailsExamples()
197{
198 QToolTip::showText(QCursor::pos(),
199 i18nc("@label examples about information the user can provide",
200 "Examples: %1", reportInterface()->appDetailsExamples()->examples()),
201 this);
202}
203
204//END BugAwarenessPage
205
206//BEGIN ConclusionPage
207
208ConclusionPage::ConclusionPage(ReportAssistantDialog * parent)
209 : ReportAssistantPage(parent)
210 , m_needToReport(false)
211{
212 m_isBKO = DrKonqi::crashedApplication()->bugReportAddress().isKdeBugzilla();
213
214 ui.setupUi(this);
215
216 ui.m_showReportInformationButton->setGuiItem(
217 KGuiItem2(i18nc("@action:button", "&Show Contents of the Report"),
218 KIcon("document-preview"),
219 i18nc("@info:tooltip", "Use this button to show the generated "
220 "report information about this crash.")));
221 connect(ui.m_showReportInformationButton, SIGNAL(clicked()),
222 this, SLOT(openReportInformation()));
223
224 ui.m_restartAppOnFinish->setVisible(false);
225}
226
227void ConclusionPage::finishClicked()
228{
229 //Manual report
230 if (m_needToReport && !m_isBKO) {
231 const CrashedApplication *crashedApp = DrKonqi::crashedApplication();
232 BugReportAddress reportAddress = crashedApp->bugReportAddress();
233 QString report = reportInterface()->generateReportFullText(false);
234
235 if (reportAddress.isEmail()) {
236 QString subject = "[%1] [%2] Automatic crash report generated by DrKonqi";
237 subject= subject.arg(crashedApp->name());
238 subject= subject.arg(crashedApp->datetime().toString("yyyy-MM-dd"));
239 KToolInvocation::invokeMailer(reportAddress, "", "" , subject, report);
240 } else {
241 if (KUrl(reportAddress).isRelative()) { //Scheme is missing
242 reportAddress = QString::fromLatin1("http://%1").arg(reportAddress);
243 }
244 KToolInvocation::invokeBrowser(reportAddress);
245 }
246
247 //Show a copy of the bug reported
248 openReportInformation();
249 }
250
251 //Restart application
252 if (ui.m_restartAppOnFinish->isChecked()) {
253 DrKonqi::crashedApplication()->restart();
254 }
255}
256
257void ConclusionPage::aboutToShow()
258{
259 connect(assistant(), SIGNAL(user1Clicked()), this, SLOT(finishClicked()));
260 ui.m_restartAppOnFinish->setVisible(false);
261 ui.m_restartAppOnFinish->setChecked(false);
262
263 const bool isDuplicate = reportInterface()->duplicateId() && !reportInterface()->attachToBugNumber();
264 m_needToReport = reportInterface()->isWorthReporting() && !isDuplicate;
265 emitCompleteChanged();
266
267 BugReportAddress reportAddress = DrKonqi::crashedApplication()->bugReportAddress();
268 BacktraceParser::Usefulness use =
269 DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness();
270
271 QString explanationHTML = QLatin1String("<p><ul>");
272
273 bool backtraceGenerated = true;
274 switch (use) {
275 case BacktraceParser::ReallyUseful: {
276 explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","The automatically generated "
277 "crash information is useful."));
278 break;
279 }
280 case BacktraceParser::MayBeUseful: {
281 explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","The automatically generated "
282 "crash information lacks some "
283 "details "
284 "but may be still be useful."));
285 break;
286 }
287 case BacktraceParser::ProbablyUseless: {
288 explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","The automatically generated "
289 "crash information lacks important details "
290 "and it is probably not helpful."));
291 break;
292 }
293 case BacktraceParser::Useless:
294 case BacktraceParser::InvalidUsefulness: {
295 BacktraceGenerator::State state = DrKonqi::debuggerManager()->backtraceGenerator()->state();
296 if (state == BacktraceGenerator::NotLoaded) {
297 backtraceGenerated = false;
298 explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","The crash information was "
299 "not generated because it was not needed."));
300 } else {
301 explanationHTML += QString("<li>%1<br />%2</li>").arg(
302 i18nc("@info","The automatically generated crash "
303 "information does not contain enough information to be "
304 "helpful."),
305 i18nc("@info","<note>You can improve it by "
306 "installing debugging packages and reloading the crash on "
307 "the Crash Information page. You can get help with the Bug "
308 "Reporting Guide by clicking on the "
309 "<interface>Help</interface> button.</note>"));
310 //but this guide doesn't mention bt packages? that's techbase
311 //->>and the help guide mention techbase page...
312 }
313 break;
314 }
315 }
316
317 //User can provide enough information
318 if (reportInterface()->isBugAwarenessPageDataUseful()) {
319 explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","The information you can "
320 "provide could be considered helpful."));
321 } else {
322 explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","The information you can "
323 "provide is not considered helpful enough in this case."));
324 }
325
326 if (isDuplicate) {
327 explanationHTML += QString("<li>%1</li>").arg(i18nc("@info","Your problem has already been "
328 "reported as bug <numid>%1</numid>.", reportInterface()->duplicateId()));
329 }
330
331 explanationHTML += QLatin1String("</ul></p>");
332
333 ui.m_explanationLabel->setText(explanationHTML);
334
335 //Hide the "Show contents of the report" button if the backtrace was not generated
336 ui.m_showReportInformationButton->setVisible(backtraceGenerated);
337
338 if (m_needToReport) {
339 ui.m_conclusionsLabel->setText(QString("<p><strong>%1</strong>").arg(i18nc("@info","This "
340 "report is considered helpful.")));
341
342 if (m_isBKO) {
343 emitCompleteChanged();
344 ui.m_howToProceedLabel->setText(i18nc("@info","This application's bugs are reported "
345 "to the KDE bug tracking system: click <interface>Next"
346 "</interface> to start the reporting process. "
347 "You can manually report at <link>%1</link>",
348 reportAddress));
349
350 } else {
351 if (!DrKonqi::crashedApplication()->hasBeenRestarted()) {
352 ui.m_restartAppOnFinish->setVisible(true);
353 }
354
355 ui.m_howToProceedLabel->setText(i18nc("@info","This application is not supported in the "
356 "KDE bug tracking system. Click <interface>"
357 "Finish</interface> to report this bug to "
358 "the application maintainer. Also, you can manually "
359 "report at <link>%1</link>.", reportAddress));
360
361 emit finished(false);
362 }
363
364 } else { // (m_needToReport)
365 if (!DrKonqi::crashedApplication()->hasBeenRestarted()) {
366 ui.m_restartAppOnFinish->setVisible(true);
367 }
368
369 ui.m_conclusionsLabel->setText(QString("<p><strong>%1</strong><br />%2</p>").arg(
370 i18nc("@info","This report does not contain enough information for the "
371 "developers, so the automated bug reporting process is not "
372 "enabled for this crash."),
373 i18nc("@info","If you wish, you can go back and change your "
374 "answers. ")));
375
376 //Only mention "manual reporting" if the backtrace was generated.
377 //FIXME separate the texts "manual reporting" / "click finish to close"
378 //"manual reporting" should be ~"manual report using the contents of the report"....
379 //FIXME for 4.5 (workflow, see ToDo)
380 if (backtraceGenerated) {
381 if (m_isBKO) {
382 ui.m_howToProceedLabel->setText(i18nc("@info","You can manually report this bug "
383 "at <link>%1</link>. "
384 "Click <interface>Finish</interface> to close the "
385 "assistant.",
386 reportAddress));
387 } else {
388 ui.m_howToProceedLabel->setText(i18nc("@info","You can manually report this "
389 "bug to its maintainer at <link>%1</link>. "
390 "Click <interface>Finish</interface> to close the "
391 "assistant.", reportAddress));
392 }
393 }
394 emit finished(true);
395 }
396}
397
398void ConclusionPage::aboutToHide()
399{
400 assistant()->disconnect(SIGNAL(user1Clicked()), this, SLOT(finishClicked()));
401}
402
403void ConclusionPage::openReportInformation()
404{
405 if (!m_infoDialog) {
406 QString info = reportInterface()->generateReportFullText(false) + QLatin1Char('\n') +
407 i18nc("@info/plain report to url/mail address","Report to %1",
408 DrKonqi::crashedApplication()->bugReportAddress());
409
410 m_infoDialog = new ReportInformationDialog(info);
411 }
412 m_infoDialog->show();
413 m_infoDialog->raise();
414 m_infoDialog->activateWindow();
415}
416
417bool ConclusionPage::isComplete()
418{
419 return (m_isBKO && m_needToReport);
420}
421
422//END ConclusionPage
423
424//BEGIN ReportInformationDialog
425
426ReportInformationDialog::ReportInformationDialog(const QString & reportText)
427 : KDialog()
428{
429 KGlobal::ref();
430 setAttribute(Qt::WA_DeleteOnClose, true);
431
432 setButtons(KDialog::Close | KDialog::User1);
433 setDefaultButton(KDialog::Close);
434 setCaption(i18nc("@title:window","Contents of the Report"));
435
436 ui.setupUi(mainWidget());
437 ui.m_reportInformationBrowser->setPlainText(reportText);
438
439 setButtonGuiItem(KDialog::User1, KGuiItem2(i18nc("@action:button", "&Save to File..."),
440 KIcon("document-save"),
441 i18nc("@info:tooltip", "Use this button to save the "
442 "generated crash report information to "
443 "a file. You can use this option to report the "
444 "bug later.")));
445 connect(this, SIGNAL(user1Clicked()), this, SLOT(saveReport()));
446
447 setInitialSize(QSize(800, 600));
448 KConfigGroup config(KGlobal::config(), "ReportInformationDialog");
449 restoreDialogSize(config);
450}
451
452ReportInformationDialog::~ReportInformationDialog()
453{
454 KConfigGroup config(KGlobal::config(), "ReportInformationDialog");
455 saveDialogSize(config);
456 KGlobal::deref();
457}
458
459void ReportInformationDialog::saveReport()
460{
461 DrKonqi::saveReport(ui.m_reportInformationBrowser->toPlainText(), this);
462}
463
464//END ReportInformationDialog
465
466