1 | /******************************************************************* |
2 | * reportassistantdialog.cpp |
3 | * Copyright 2009,2010 Dario Andres Rodriguez <andresbajotierra@gmail.com> |
4 | * |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License as |
7 | * published by the Free Software Foundation; either version 2 of |
8 | * the License, or (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | ******************************************************************/ |
19 | |
20 | #include "reportassistantdialog.h" |
21 | |
22 | #include <QCloseEvent> |
23 | |
24 | #include <KMessageBox> |
25 | |
26 | #include "drkonqi.h" |
27 | |
28 | #include "parser/backtraceparser.h" |
29 | #include "debuggermanager.h" |
30 | #include "backtracegenerator.h" |
31 | |
32 | #include "crashedapplication.h" |
33 | #include "aboutbugreportingdialog.h" |
34 | #include "reportassistantpages_base.h" |
35 | #include "reportassistantpages_bugzilla.h" |
36 | #include "reportassistantpages_bugzilla_duplicates.h" |
37 | #include "reportinterface.h" |
38 | |
39 | static const char KDE_BUGZILLA_DESCRIPTION[] = I18N_NOOP("the KDE Bug Tracking System" ); |
40 | |
41 | ReportAssistantDialog::ReportAssistantDialog(QWidget * parent) : |
42 | KAssistantDialog(parent), |
43 | m_aboutBugReportingDialog(0), |
44 | m_reportInterface(new ReportInterface(this)), |
45 | m_canClose(false) |
46 | { |
47 | KGlobal::ref(); |
48 | setAttribute(Qt::WA_DeleteOnClose, true); |
49 | |
50 | //Set window properties |
51 | setWindowTitle(i18nc("@title:window" ,"Crash Reporting Assistant" )); |
52 | setWindowIcon(KIcon("tools-report-bug" )); |
53 | |
54 | connect(this, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), |
55 | this, SLOT(currentPageChanged_slot(KPageWidgetItem*,KPageWidgetItem*))); |
56 | connect(this, SIGNAL(helpClicked()), this, SLOT(showHelp())); |
57 | |
58 | //Create the assistant pages |
59 | |
60 | //-Introduction Page |
61 | KConfigGroup group(KGlobal::config(), "ReportAssistant" ); |
62 | const bool skipIntroduction = group.readEntry("SkipIntroduction" , false); |
63 | |
64 | if (!skipIntroduction) { |
65 | IntroductionPage * m_introduction = new IntroductionPage(this); |
66 | |
67 | KPageWidgetItem * m_introductionPage = new KPageWidgetItem(m_introduction, |
68 | QLatin1String(PAGE_INTRODUCTION_ID)); |
69 | m_pageWidgetMap.insert(QLatin1String(PAGE_INTRODUCTION_ID),m_introductionPage); |
70 | m_introductionPage->setHeader(i18nc("@title" ,"Welcome to the Reporting Assistant" )); |
71 | m_introductionPage->setIcon(KIcon("tools-report-bug" )); |
72 | |
73 | addPage(m_introductionPage); |
74 | } |
75 | |
76 | //-Bug Awareness Page |
77 | BugAwarenessPage * m_awareness = new BugAwarenessPage(this); |
78 | connectSignals(m_awareness); |
79 | |
80 | KPageWidgetItem * m_awarenessPage = new KPageWidgetItem(m_awareness, |
81 | QLatin1String(PAGE_AWARENESS_ID)); |
82 | m_pageWidgetMap.insert(QLatin1String(PAGE_AWARENESS_ID),m_awarenessPage); |
83 | m_awarenessPage->setHeader(i18nc("@title" ,"What do you know about the crash?" )); |
84 | m_awarenessPage->setIcon(KIcon("checkbox" )); |
85 | |
86 | //-Crash Information Page |
87 | CrashInformationPage * m_backtrace = new CrashInformationPage(this); |
88 | connectSignals(m_backtrace); |
89 | |
90 | KPageWidgetItem * m_backtracePage = new KPageWidgetItem(m_backtrace, |
91 | QLatin1String(PAGE_CRASHINFORMATION_ID)); |
92 | m_pageWidgetMap.insert(QLatin1String(PAGE_CRASHINFORMATION_ID),m_backtracePage); |
93 | m_backtracePage->setHeader(i18nc("@title" ,"Fetching the Backtrace (Automatic Crash Information)" )); |
94 | m_backtracePage->setIcon(KIcon("run-build" )); |
95 | |
96 | //-Results Page |
97 | ConclusionPage * m_conclusions = new ConclusionPage(this); |
98 | connectSignals(m_conclusions); |
99 | |
100 | KPageWidgetItem * m_conclusionsPage = new KPageWidgetItem(m_conclusions, |
101 | QLatin1String(PAGE_CONCLUSIONS_ID)); |
102 | m_pageWidgetMap.insert(QLatin1String(PAGE_CONCLUSIONS_ID),m_conclusionsPage); |
103 | m_conclusionsPage->setHeader(i18nc("@title" ,"Results of the Analyzed Crash Details" )); |
104 | m_conclusionsPage->setIcon(KIcon("dialog-information" )); |
105 | connect(m_conclusions, SIGNAL(finished(bool)), this, SLOT(assistantFinished(bool))); |
106 | |
107 | //-Bugzilla Login |
108 | BugzillaLoginPage * m_bugzillaLogin = new BugzillaLoginPage(this); |
109 | connectSignals(m_bugzillaLogin); |
110 | |
111 | KPageWidgetItem * m_bugzillaLoginPage = new KPageWidgetItem(m_bugzillaLogin, |
112 | QLatin1String(PAGE_BZLOGIN_ID)); |
113 | m_pageWidgetMap.insert(QLatin1String(PAGE_BZLOGIN_ID),m_bugzillaLoginPage); |
114 | m_bugzillaLoginPage->setHeader(i18nc("@title" , "Login into %1" , i18n(KDE_BUGZILLA_DESCRIPTION))); |
115 | m_bugzillaLoginPage->setIcon(KIcon("user-identity" )); |
116 | connect(m_bugzillaLogin, SIGNAL(loggedTurnToNextPage()), this, SLOT(loginFinished())); |
117 | |
118 | //-Bugzilla duplicates |
119 | BugzillaDuplicatesPage * m_bugzillaDuplicates = new BugzillaDuplicatesPage(this); |
120 | connectSignals(m_bugzillaDuplicates); |
121 | |
122 | KPageWidgetItem * m_bugzillaDuplicatesPage = new KPageWidgetItem(m_bugzillaDuplicates, |
123 | QLatin1String(PAGE_BZDUPLICATES_ID)); |
124 | m_pageWidgetMap.insert(QLatin1String(PAGE_BZDUPLICATES_ID),m_bugzillaDuplicatesPage); |
125 | m_bugzillaDuplicatesPage->setHeader(i18nc("@title" ,"Look for Possible Duplicate Reports" )); |
126 | m_bugzillaDuplicatesPage->setIcon(KIcon("repository" )); |
127 | |
128 | //-Bugzilla information |
129 | BugzillaInformationPage * m_bugzillaInformation = new BugzillaInformationPage(this); |
130 | connectSignals(m_bugzillaInformation); |
131 | |
132 | KPageWidgetItem * m_bugzillaInformationPage = new KPageWidgetItem(m_bugzillaInformation, |
133 | QLatin1String(PAGE_BZDETAILS_ID)); |
134 | m_pageWidgetMap.insert(QLatin1String(PAGE_BZDETAILS_ID),m_bugzillaInformationPage); |
135 | m_bugzillaInformationPage->setHeader(i18nc("@title" ,"Enter the Details about the Crash" )); |
136 | m_bugzillaInformationPage->setIcon(KIcon("document-edit" )); |
137 | |
138 | //-Bugzilla Report Preview |
139 | BugzillaPreviewPage * m_bugzillaPreview = new BugzillaPreviewPage(this); |
140 | |
141 | KPageWidgetItem * m_bugzillaPreviewPage = new KPageWidgetItem(m_bugzillaPreview, |
142 | QLatin1String(PAGE_BZPREVIEW_ID)); |
143 | m_pageWidgetMap.insert(QLatin1String(PAGE_BZPREVIEW_ID),m_bugzillaPreviewPage); |
144 | m_bugzillaPreviewPage->setHeader(i18nc("@title" ,"Preview the Report" )); |
145 | m_bugzillaPreviewPage->setIcon(KIcon("document-preview" )); |
146 | |
147 | //-Bugzilla commit |
148 | BugzillaSendPage * m_bugzillaSend = new BugzillaSendPage(this); |
149 | |
150 | KPageWidgetItem * m_bugzillaSendPage = new KPageWidgetItem(m_bugzillaSend, |
151 | QLatin1String(PAGE_BZSEND_ID)); |
152 | m_pageWidgetMap.insert(QLatin1String(PAGE_BZSEND_ID),m_bugzillaSendPage); |
153 | m_bugzillaSendPage->setHeader(i18nc("@title" ,"Sending the Crash Report" )); |
154 | m_bugzillaSendPage->setIcon(KIcon("applications-internet" )); |
155 | connect(m_bugzillaSend, SIGNAL(finished(bool)), this, SLOT(assistantFinished(bool))); |
156 | |
157 | //TODO Remember to keep the pages ordered |
158 | addPage(m_awarenessPage); |
159 | addPage(m_backtracePage); |
160 | addPage(m_conclusionsPage); |
161 | addPage(m_bugzillaLoginPage); |
162 | addPage(m_bugzillaDuplicatesPage); |
163 | addPage(m_bugzillaInformationPage); |
164 | addPage(m_bugzillaPreviewPage); |
165 | addPage(m_bugzillaSendPage); |
166 | |
167 | setMinimumSize(QSize(600, 400)); |
168 | resize(minimumSize()); |
169 | } |
170 | |
171 | ReportAssistantDialog::~ReportAssistantDialog() |
172 | { |
173 | KGlobal::deref(); |
174 | } |
175 | |
176 | void ReportAssistantDialog::connectSignals(ReportAssistantPage * page) |
177 | { |
178 | //React to the changes in the assistant pages |
179 | connect(page, SIGNAL(completeChanged(ReportAssistantPage*,bool)), |
180 | this, SLOT(completeChanged(ReportAssistantPage*,bool))); |
181 | } |
182 | |
183 | void ReportAssistantDialog::currentPageChanged_slot(KPageWidgetItem * current , KPageWidgetItem * before) |
184 | { |
185 | //Page changed |
186 | |
187 | enableButton(KDialog::Cancel, true); |
188 | m_canClose = false; |
189 | |
190 | //Save data of the previous page |
191 | if (before) { |
192 | ReportAssistantPage* beforePage = dynamic_cast<ReportAssistantPage*>(before->widget()); |
193 | beforePage->aboutToHide(); |
194 | } |
195 | |
196 | //Load data of the current(new) page |
197 | if (current) { |
198 | ReportAssistantPage* currentPage = dynamic_cast<ReportAssistantPage*>(current->widget()); |
199 | enableNextButton(currentPage->isComplete()); |
200 | currentPage->aboutToShow(); |
201 | } |
202 | |
203 | //If the current page is the last one, disable all the buttons until the bug is sent |
204 | if (current->name() == QLatin1String(PAGE_BZSEND_ID)) { |
205 | enableNextButton(false); |
206 | enableButton(KDialog::User3, false); //Back button |
207 | enableButton(KDialog::User1, false); |
208 | } |
209 | } |
210 | |
211 | void ReportAssistantDialog::completeChanged(ReportAssistantPage* page, bool isComplete) |
212 | { |
213 | if (page == dynamic_cast<ReportAssistantPage*>(currentPage()->widget())) { |
214 | enableNextButton(isComplete); |
215 | } |
216 | } |
217 | |
218 | void ReportAssistantDialog::assistantFinished(bool showBack) |
219 | { |
220 | //The assistant finished: allow the user to close the dialog normally |
221 | |
222 | enableNextButton(false); |
223 | enableButton(KDialog::User3, showBack); //Back button |
224 | enableButton(KDialog::User1, true); |
225 | enableButton(KDialog::Cancel, false); |
226 | |
227 | m_canClose = true; |
228 | } |
229 | |
230 | void ReportAssistantDialog::loginFinished() |
231 | { |
232 | //Bugzilla login finished, go to the next page |
233 | if (currentPage()->name() == QLatin1String(PAGE_BZLOGIN_ID)) { |
234 | next(); |
235 | } |
236 | } |
237 | |
238 | void ReportAssistantDialog::showHelp() |
239 | { |
240 | //Show the bug reporting guide dialog |
241 | if (!m_aboutBugReportingDialog) { |
242 | m_aboutBugReportingDialog = new AboutBugReportingDialog(); |
243 | } |
244 | m_aboutBugReportingDialog->show(); |
245 | m_aboutBugReportingDialog->raise(); |
246 | m_aboutBugReportingDialog->activateWindow(); |
247 | m_aboutBugReportingDialog->showSection(QLatin1String(PAGE_HELP_BEGIN_ID)); |
248 | m_aboutBugReportingDialog->showSection(currentPage()->name()); |
249 | } |
250 | |
251 | //Override KAssistantDialog "next" page implementation |
252 | void ReportAssistantDialog::next() |
253 | { |
254 | //Allow the widget to Ask a question to the user before changing the page |
255 | ReportAssistantPage * page = dynamic_cast<ReportAssistantPage*>(currentPage()->widget()); |
256 | if (page) { |
257 | if (!page->showNextPage()) { |
258 | return; |
259 | } |
260 | } |
261 | |
262 | const QString name = currentPage()->name(); |
263 | |
264 | //If the information the user can provide is not useful, skip the backtrace page |
265 | if (name == QLatin1String(PAGE_AWARENESS_ID)) |
266 | { |
267 | //Force save settings in the current page |
268 | page->aboutToHide(); |
269 | |
270 | if (!(m_reportInterface->isBugAwarenessPageDataUseful())) |
271 | { |
272 | setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_CONCLUSIONS_ID))); |
273 | return; |
274 | } |
275 | } else if (name == QLatin1String(PAGE_CRASHINFORMATION_ID)){ |
276 | //Force save settings in current page |
277 | page->aboutToHide(); |
278 | |
279 | //If the crash is worth reporting and it is BKO, skip the Conclusions page |
280 | if (m_reportInterface->isWorthReporting() && |
281 | DrKonqi::crashedApplication()->bugReportAddress().isKdeBugzilla()) |
282 | { |
283 | setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_BZLOGIN_ID))); |
284 | return; |
285 | } |
286 | } else if (name == QLatin1String(PAGE_BZDUPLICATES_ID)) { |
287 | //a duplicate has been found, yet the report is not being attached |
288 | if (m_reportInterface->duplicateId() && !m_reportInterface->attachToBugNumber()) { |
289 | setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_CONCLUSIONS_ID))); |
290 | return; |
291 | } |
292 | } |
293 | |
294 | KAssistantDialog::next(); |
295 | } |
296 | |
297 | //Override KAssistantDialog "back"(previous) page implementation |
298 | //It has to mirror the custom next() implementation |
299 | void ReportAssistantDialog::back() |
300 | { |
301 | if (currentPage()->name() == QLatin1String(PAGE_CONCLUSIONS_ID)) |
302 | { |
303 | if (m_reportInterface->duplicateId() && !m_reportInterface->attachToBugNumber()) { |
304 | setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_BZDUPLICATES_ID))); |
305 | return; |
306 | } |
307 | if (!(m_reportInterface->isBugAwarenessPageDataUseful())) |
308 | { |
309 | setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_AWARENESS_ID))); |
310 | return; |
311 | } |
312 | } |
313 | |
314 | if (currentPage()->name() == QLatin1String(PAGE_BZLOGIN_ID)) |
315 | { |
316 | if (m_reportInterface->isWorthReporting() && |
317 | DrKonqi::crashedApplication()->bugReportAddress().isKdeBugzilla()) |
318 | { |
319 | setCurrentPage(m_pageWidgetMap.value(QLatin1String(PAGE_CRASHINFORMATION_ID))); |
320 | return; |
321 | } |
322 | } |
323 | |
324 | KAssistantDialog::back(); |
325 | } |
326 | |
327 | void ReportAssistantDialog::enableNextButton(bool enabled) |
328 | { |
329 | enableButton(KDialog::User2, enabled); |
330 | } |
331 | |
332 | void ReportAssistantDialog::reject() |
333 | { |
334 | close(); |
335 | } |
336 | |
337 | void ReportAssistantDialog::closeEvent(QCloseEvent * event) |
338 | { |
339 | //Handle the close event |
340 | if (!m_canClose) { |
341 | //If the assistant didn't finished yet, offer the user the possibilities to |
342 | //Close, Cancel, or Save the bug report and Close" |
343 | |
344 | KGuiItem closeItem = KStandardGuiItem::close(); |
345 | closeItem.setText(i18nc("@action:button" , "Close the assistant" )); |
346 | |
347 | KGuiItem keepOpenItem = KStandardGuiItem::cancel(); |
348 | keepOpenItem.setText(i18nc("@action:button" , "Cancel" )); |
349 | |
350 | BacktraceParser::Usefulness use = |
351 | DrKonqi::debuggerManager()->backtraceGenerator()->parser()->backtraceUsefulness(); |
352 | if (use == BacktraceParser::ReallyUseful || use == BacktraceParser::MayBeUseful) { |
353 | //Backtrace is still useful, let the user save it. |
354 | KGuiItem saveBacktraceItem = KStandardGuiItem::save(); |
355 | saveBacktraceItem.setText(i18nc("@action:button" , "Save information and close" )); |
356 | |
357 | int ret = KMessageBox::questionYesNoCancel(this, |
358 | i18nc("@info" ,"Do you really want to close the bug reporting assistant? " |
359 | "<note>The crash information is still valid, so " |
360 | "you can save the report before closing if you want.</note>" ), |
361 | i18nc("@title:window" ,"Close the Assistant" ), |
362 | closeItem, saveBacktraceItem, keepOpenItem, QString(), KMessageBox::Dangerous); |
363 | if(ret == KMessageBox::Yes) |
364 | { |
365 | event->accept(); |
366 | } else if (ret == KMessageBox::No) { |
367 | //Save backtrace and accept event (dialog will be closed) |
368 | DrKonqi::saveReport(reportInterface()->generateReportFullText(false)); |
369 | event->accept(); |
370 | } else { |
371 | event->ignore(); |
372 | } |
373 | } else { |
374 | if (KMessageBox::questionYesNo(this, i18nc("@info" ,"Do you really want to close the bug " |
375 | "reporting assistant?" ), |
376 | i18nc("@title:window" ,"Close the Assistant" ), |
377 | closeItem, keepOpenItem, QString(), KMessageBox::Dangerous) |
378 | == KMessageBox::Yes) { |
379 | event->accept(); |
380 | } else { |
381 | event->ignore(); |
382 | } |
383 | } |
384 | } else { |
385 | event->accept(); |
386 | } |
387 | } |
388 | |