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 | |
40 | static const char ABOUT_BUG_REPORTING_URL[] = "#aboutbugreporting" ; |
41 | static const char DRKONQI_REPORT_BUG_URL[] = |
42 | KDE_BUGZILLA_URL "enter_bug.cgi?product=drkonqi&format=guided" ; |
43 | |
44 | DrKonqiDialog::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 | |
76 | DrKonqiDialog::~DrKonqiDialog() |
77 | { |
78 | KConfigGroup config(KGlobal::config(), "General" ); |
79 | saveDialogSize(config); |
80 | |
81 | KGlobal::deref(); |
82 | } |
83 | |
84 | void DrKonqiDialog::tabIndexChanged(int index) |
85 | { |
86 | if (index == m_tabWidget->indexOf(m_backtraceWidget)) { |
87 | m_backtraceWidget->generateBacktrace(); |
88 | } |
89 | } |
90 | |
91 | void 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 | |
163 | void 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 | |
224 | void 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 | |
235 | void 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 | |
246 | void DrKonqiDialog::(bool debuggerRunning) |
247 | { |
248 | enableButton(KDialog::User2, !debuggerRunning); |
249 | } |
250 | |
251 | void DrKonqiDialog::startBugReportAssistant() |
252 | { |
253 | ReportAssistantDialog * bugReportAssistant = new ReportAssistantDialog(); |
254 | close(); |
255 | bugReportAssistant->show(); |
256 | } |
257 | |
258 | void 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 | |
270 | void 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 | |
281 | void DrKonqiDialog::applicationRestarted(bool success) |
282 | { |
283 | enableButton(KDialog::User3, !success); |
284 | } |
285 | |