1/*******************************************************************
2* drkonqidialog.cpp
3* Copyright 2009 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 "drkonqidialog.h"
21
22#include <KMenu>
23#include <KIcon>
24#include <KStandardDirs>
25#include <KLocale>
26#include <KTabWidget>
27#include <KDebug>
28#include <KCmdLineArgs>
29#include <KToolInvocation>
30
31#include "drkonqi.h"
32#include "backtracewidget.h"
33#include "reportassistantdialog.h"
34#include "aboutbugreportingdialog.h"
35#include "crashedapplication.h"
36#include "debuggermanager.h"
37#include "debuggerlaunchers.h"
38#include "drkonqi_globals.h"
39
40static const char ABOUT_BUG_REPORTING_URL[] = "#aboutbugreporting";
41static const char DRKONQI_REPORT_BUG_URL[] =
42 KDE_BUGZILLA_URL "enter_bug.cgi?product=drkonqi&format=guided";
43
44DrKonqiDialog::DrKonqiDialog(QWidget * parent) :
45 KDialog(parent),
46 m_aboutBugReportingDialog(0),
47 m_backtraceWidget(0)
48{
49 KGlobal::ref();
50 setAttribute(Qt::WA_DeleteOnClose, true);
51
52 //Setting dialog title and icon
53 setCaption(DrKonqi::crashedApplication()->name());
54 setWindowIcon(KIcon("tools-report-bug"));
55
56 m_tabWidget = new KTabWidget(this);
57 setMainWidget(m_tabWidget);
58
59 connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabIndexChanged(int)));
60
61 buildIntroWidget();
62 m_tabWidget->addTab(m_introWidget, i18nc("@title:tab general information", "&General"));
63
64 m_backtraceWidget = new BacktraceWidget(DrKonqi::debuggerManager()->backtraceGenerator(), this);
65 m_backtraceWidget->setMinimumSize(QSize(575, 240));
66 m_tabWidget->addTab(m_backtraceWidget, i18nc("@title:tab", "&Developer Information"));
67
68 buildDialogButtons();
69
70 setMinimumSize(QSize(640,320));
71 resize(minimumSize());
72 KConfigGroup config(KGlobal::config(), "General");
73 restoreDialogSize(config);
74}
75
76DrKonqiDialog::~DrKonqiDialog()
77{
78 KConfigGroup config(KGlobal::config(), "General");
79 saveDialogSize(config);
80
81 KGlobal::deref();
82}
83
84void DrKonqiDialog::tabIndexChanged(int index)
85{
86 if (index == m_tabWidget->indexOf(m_backtraceWidget)) {
87 m_backtraceWidget->generateBacktrace();
88 }
89}
90
91void DrKonqiDialog::buildIntroWidget()
92{
93 const CrashedApplication *crashedApp = DrKonqi::crashedApplication();
94
95 m_introWidget = new QWidget(this);
96 ui.setupUi(m_introWidget);
97
98 ui.titleLabel->setText(i18nc("@info", "<para>We are sorry, <application>%1</application> "
99 "closed unexpectedly.</para>", crashedApp->name()));
100
101 QString reportMessage;
102 if (!crashedApp->bugReportAddress().isEmpty()) {
103 if (crashedApp->fakeExecutableBaseName() == QLatin1String("drkonqi")) { //Handle own crashes
104 reportMessage = i18nc("@info", "<para>As the Crash Handler itself has failed, the "
105 "automatic reporting process is disabled to reduce the "
106 "risks of failing again.<nl /><nl />"
107 "Please, <link url='%1'>manually report</link> this error "
108 "to the KDE bug tracking system. Do not forget to include "
109 "the backtrace from the <interface>Developer Information</interface> "
110 "tab.</para>",
111 QLatin1String(DRKONQI_REPORT_BUG_URL));
112 } else if (KCmdLineArgs::parsedArgs()->isSet("safer")) {
113 reportMessage = i18nc("@info", "<para>The reporting assistant is disabled because "
114 "the crash handler dialog was started in safe mode."
115 "<nl />You can manually report this bug to %1 "
116 "(including the backtrace from the "
117 "<interface>Developer Information</interface> "
118 "tab.)</para>", crashedApp->bugReportAddress());
119 } else {
120 reportMessage = i18nc("@info", "<para>You can help us improve KDE Software by reporting "
121 "this error.<nl /><link url='%1'>Learn "
122 "more about bug reporting.</link></para><para><note>It is "
123 "safe to close this dialog if you do not want to report "
124 "this bug.</note></para>",
125 QLatin1String(ABOUT_BUG_REPORTING_URL)
126 );
127 }
128 } else {
129 reportMessage = i18nc("@info", "<para>You cannot report this error, because "
130 "<application>%1</application> does not provide a bug reporting "
131 "address.</para>",
132 crashedApp->name()
133 );
134 }
135 ui.infoLabel->setText(reportMessage);
136 connect(ui.infoLabel, SIGNAL(linkActivated(QString)), this, SLOT(linkActivated(QString)));
137
138 ui.iconLabel->setPixmap(
139 QPixmap(KStandardDirs::locate("appdata", QLatin1String("pics/crash.png"))));
140
141 ui.detailsTitleLabel->setText(QString("<strong>%1</strong>").arg(i18nc("@label","Details:")));
142
143 ui.detailsLabel->setText(i18nc("@info Note the time information is divided into date and time parts",
144 "<para>Executable: <application>%1"
145 "</application> PID: <numid>%2</numid> Signal: %3 (%4) "
146 "Time: %5 %6</para>",
147 crashedApp->fakeExecutableBaseName(),
148 crashedApp->pid(),
149 crashedApp->signalName(),
150 #if defined(Q_OS_UNIX)
151 crashedApp->signalNumber(),
152 #else
153 //windows uses weird big numbers for exception codes,
154 //so it doesn't make sense to display them in decimal
155 QString().sprintf("0x%8x", crashedApp->signalNumber()),
156 #endif
157 KGlobal::locale()->formatDate(crashedApp->datetime().date(), KLocale::ShortDate),
158
159 KGlobal::locale()->formatTime(crashedApp->datetime().time(), true)
160 ));
161}
162
163void DrKonqiDialog::buildDialogButtons()
164{
165 const CrashedApplication *crashedApp = DrKonqi::crashedApplication();
166
167 //Set kdialog buttons
168 setButtons(KDialog::User3 | KDialog::User2 | KDialog::User1 | KDialog::Close);
169
170 //Report bug button
171 setButtonGuiItem(KDialog::User1, KGuiItem2(i18nc("@action:button", "Report &Bug"),
172 KIcon("tools-report-bug"),
173 i18nc("@info:tooltip",
174 "Starts the bug report assistant.")));
175
176 bool enableReportAssistant = !crashedApp->bugReportAddress().isEmpty() &&
177 crashedApp->fakeExecutableBaseName() != QLatin1String("drkonqi") &&
178 !KCmdLineArgs::parsedArgs()->isSet("safer");
179 enableButton(KDialog::User1, enableReportAssistant);
180 connect(this, SIGNAL(user1Clicked()), this, SLOT(startBugReportAssistant()));
181
182 //Default debugger button and menu (only for developer mode)
183 DebuggerManager *debuggerManager = DrKonqi::debuggerManager();
184 setButtonGuiItem(KDialog::User2, KGuiItem2(i18nc("@action:button this is the debug menu button "
185 "label which contains the debugging applications",
186 "&Debug"), KIcon("applications-development"),
187 i18nc("@info:tooltip", "Starts a program to debug "
188 "the crashed application.")));
189 showButton(KDialog::User2, debuggerManager->showExternalDebuggers());
190
191 m_debugMenu = new KMenu(this);
192 setButtonMenu(KDialog::User2, m_debugMenu);
193
194 QList<AbstractDebuggerLauncher*> debuggers = debuggerManager->availableExternalDebuggers();
195 foreach(AbstractDebuggerLauncher *launcher, debuggers) {
196 addDebugger(launcher);
197 }
198
199 connect(debuggerManager, SIGNAL(externalDebuggerAdded(AbstractDebuggerLauncher*)),
200 SLOT(addDebugger(AbstractDebuggerLauncher*)));
201 connect(debuggerManager, SIGNAL(externalDebuggerRemoved(AbstractDebuggerLauncher*)),
202 SLOT(removeDebugger(AbstractDebuggerLauncher*)));
203 connect(debuggerManager, SIGNAL(debuggerRunning(bool)), SLOT(enableDebugMenu(bool)));
204
205 //Restart application button
206 setButtonGuiItem(KDialog::User3, KGuiItem2(i18nc("@action:button", "&Restart Application"),
207 KIcon("system-reboot"),
208 i18nc("@info:tooltip", "Use this button to restart "
209 "the crashed application.")));
210 enableButton(KDialog::User3, !crashedApp->hasBeenRestarted() &&
211 crashedApp->fakeExecutableBaseName() != QLatin1String("drkonqi"));
212 connect(this, SIGNAL(user3Clicked()), crashedApp, SLOT(restart()));
213 connect(crashedApp, SIGNAL(restarted(bool)), this, SLOT(applicationRestarted(bool)));
214
215 //Close button
216 QString tooltipText = i18nc("@info:tooltip",
217 "Close this dialog (you will lose the crash information.)");
218 setButtonToolTip(KDialog::Close, tooltipText);
219 setButtonWhatsThis(KDialog::Close, tooltipText);
220 setDefaultButton(KDialog::Close);
221 setButtonFocus(KDialog::Close);
222}
223
224void DrKonqiDialog::addDebugger(AbstractDebuggerLauncher *launcher)
225{
226 QAction *action = new QAction(KIcon("applications-development"),
227 i18nc("@action:inmenu 1 is the debugger name",
228 "Debug in <application>%1</application>",
229 launcher->name()), m_debugMenu);
230 m_debugMenu->addAction(action);
231 connect(action, SIGNAL(triggered()), launcher, SLOT(start()));
232 m_debugMenuActions.insert(launcher, action);
233}
234
235void DrKonqiDialog::removeDebugger(AbstractDebuggerLauncher *launcher)
236{
237 QAction *action = m_debugMenuActions.take(launcher);
238 if ( action ) {
239 m_debugMenu->removeAction(action);
240 action->deleteLater();
241 } else {
242 kError() << "Invalid launcher";
243 }
244}
245
246void DrKonqiDialog::enableDebugMenu(bool debuggerRunning)
247{
248 enableButton(KDialog::User2, !debuggerRunning);
249}
250
251void DrKonqiDialog::startBugReportAssistant()
252{
253 ReportAssistantDialog * bugReportAssistant = new ReportAssistantDialog();
254 close();
255 bugReportAssistant->show();
256}
257
258void DrKonqiDialog::linkActivated(const QString& link)
259{
260 if (link == QLatin1String(ABOUT_BUG_REPORTING_URL)) {
261 showAboutBugReporting();
262 } else if (link == QLatin1String(DRKONQI_REPORT_BUG_URL)) {
263 KToolInvocation::invokeBrowser(link);
264 } else if (link.startsWith(QLatin1String("http"))) {
265 kWarning() << "unexpected link";
266 KToolInvocation::invokeBrowser(link);
267 }
268}
269
270void DrKonqiDialog::showAboutBugReporting()
271{
272 if (!m_aboutBugReportingDialog) {
273 m_aboutBugReportingDialog = new AboutBugReportingDialog();
274 connect(this, SIGNAL(destroyed(QObject*)), m_aboutBugReportingDialog, SLOT(close()));
275 }
276 m_aboutBugReportingDialog->show();
277 m_aboutBugReportingDialog->raise();
278 m_aboutBugReportingDialog->activateWindow();
279}
280
281void DrKonqiDialog::applicationRestarted(bool success)
282{
283 enableButton(KDialog::User3, !success);
284}
285