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
39static const char KDE_BUGZILLA_DESCRIPTION[] = I18N_NOOP("the KDE Bug Tracking System");
40
41ReportAssistantDialog::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
171ReportAssistantDialog::~ReportAssistantDialog()
172{
173 KGlobal::deref();
174}
175
176void 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
183void 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
211void ReportAssistantDialog::completeChanged(ReportAssistantPage* page, bool isComplete)
212{
213 if (page == dynamic_cast<ReportAssistantPage*>(currentPage()->widget())) {
214 enableNextButton(isComplete);
215 }
216}
217
218void 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
230void 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
238void 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
252void 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
299void 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
327void ReportAssistantDialog::enableNextButton(bool enabled)
328{
329 enableButton(KDialog::User2, enabled);
330}
331
332void ReportAssistantDialog::reject()
333{
334 close();
335}
336
337void 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