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 | |
44 | IntroductionPage::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 | |
55 | CrashInformationPage::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 | |
76 | void CrashInformationPage::aboutToShow() |
77 | { |
78 | m_backtraceWidget->generateBacktrace(); |
79 | m_backtraceWidget->hilightExtraDetailsLabel(false); |
80 | emitCompleteChanged(); |
81 | } |
82 | |
83 | void 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 | |
94 | bool CrashInformationPage::isComplete() |
95 | { |
96 | BacktraceGenerator *generator = DrKonqi::debuggerManager()->backtraceGenerator(); |
97 | return (generator->state() != BacktraceGenerator::NotLoaded && |
98 | generator->state() != BacktraceGenerator::Loading); |
99 | } |
100 | |
101 | bool 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 | |
128 | BugAwarenessPage::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 | |
146 | void BugAwarenessPage::aboutToShow() |
147 | { |
148 | updateCheckBoxes(); |
149 | } |
150 | |
151 | void 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 | |
181 | void 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 | |
196 | void 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 | |
208 | ConclusionPage::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 | |
227 | void 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 | |
257 | void 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 | |
398 | void ConclusionPage::aboutToHide() |
399 | { |
400 | assistant()->disconnect(SIGNAL(user1Clicked()), this, SLOT(finishClicked())); |
401 | } |
402 | |
403 | void 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 | |
417 | bool ConclusionPage::isComplete() |
418 | { |
419 | return (m_isBKO && m_needToReport); |
420 | } |
421 | |
422 | //END ConclusionPage |
423 | |
424 | //BEGIN ReportInformationDialog |
425 | |
426 | ReportInformationDialog::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 | |
452 | ReportInformationDialog::~ReportInformationDialog() |
453 | { |
454 | KConfigGroup config(KGlobal::config(), "ReportInformationDialog" ); |
455 | saveDialogSize(config); |
456 | KGlobal::deref(); |
457 | } |
458 | |
459 | void ReportInformationDialog::saveReport() |
460 | { |
461 | DrKonqi::saveReport(ui.m_reportInformationBrowser->toPlainText(), this); |
462 | } |
463 | |
464 | //END ReportInformationDialog |
465 | |
466 | |