Warning, file /plasma/drkonqi/src/drkonqidialog.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /******************************************************************* 0002 * drkonqidialog.cpp 0003 * SPDX-FileCopyrightText: 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com> 0004 * SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 * 0008 ******************************************************************/ 0009 0010 #include "drkonqidialog.h" 0011 0012 #include <KLocalizedString> 0013 #include <KWindowConfig> 0014 0015 #include "drkonqi_debug.h" 0016 #include <QDesktopServices> 0017 #include <QDialogButtonBox> 0018 #include <QLocale> 0019 #include <QMenu> 0020 #include <QQmlApplicationEngine> 0021 #include <QQmlContext> 0022 #include <QStandardPaths> 0023 #include <QTabBar> 0024 #include <QTabWidget> 0025 #include <QtQml> 0026 0027 #include "backtracegenerator.h" 0028 #include "backtraceparser.h" 0029 #include "backtracewidget.h" 0030 #include "config-drkonqi.h" 0031 #include "crashedapplication.h" 0032 #include "debuggerlaunchers.h" 0033 #include "debuggermanager.h" 0034 #include "drkonqi.h" 0035 #include "drkonqi_globals.h" 0036 #include "qmlextensions/commentmodel.h" 0037 #include "qmlextensions/credentialstore.h" 0038 #include "qmlextensions/doctore.h" 0039 #include "qmlextensions/duplicatemodel.h" 0040 #include "qmlextensions/platformmodel.h" 0041 #include "qmlextensions/reproducibilitymodel.h" 0042 #include "systeminformation.h" 0043 0044 static const QString ABOUT_BUG_REPORTING_URL = QStringLiteral("https://community.kde.org/Get_Involved/Issue_Reporting"); 0045 static const QString DRKONQI_REPORT_BUG_URL = KDE_BUGZILLA_URL + QStringLiteral("enter_bug.cgi?product=drkonqi&format=guided"); 0046 0047 void DrKonqiDialog::show() 0048 { 0049 if (DrKonqi::isSafer()) { 0050 QDialog::show(); 0051 return; 0052 } 0053 0054 auto engine = new QQmlApplicationEngine(this); 0055 0056 static auto l10nContext = new KLocalizedContext(engine); 0057 l10nContext->setTranslationDomain(QStringLiteral(TRANSLATION_DOMAIN)); 0058 engine->rootContext()->setContextObject(l10nContext); 0059 0060 qmlRegisterType<BugzillaManager>("org.kde.drkonqi", 1, 0, "Bugzilla"); 0061 qmlRegisterType<DuplicateModel>("org.kde.drkonqi", 1, 0, "DuplicateModel"); 0062 qmlRegisterType<PlatformModel>("org.kde.drkonqi", 1, 0, "PlatformModel"); 0063 qmlRegisterType<ReproducibilityModel>("org.kde.drkonqi", 1, 0, "ReproducibilityModel"); 0064 qmlRegisterType<ReportInterface>("org.kde.drkonqi", 1, 0, "ReportInterface"); 0065 qmlRegisterType<CredentialStore>("org.kde.drkonqi", 1, 0, "CredentialStore"); 0066 qmlRegisterType<DebugPackageInstaller>("org.kde.drkonqi", 1, 0, "DebugPackageInstaller"); 0067 0068 qmlRegisterSingletonInstance("org.kde.drkonqi", 1, 0, "CrashedApplication", DrKonqi::crashedApplication()); 0069 qmlRegisterSingletonInstance("org.kde.drkonqi", 1, 0, "BacktraceGenerator", DrKonqi::debuggerManager()->backtraceGenerator()); 0070 0071 static Doctore doctore; 0072 qmlRegisterSingletonInstance("org.kde.drkonqi", 1, 0, "DrKonqi", &doctore); 0073 0074 // TODO do we need this second BG? 0075 qmlRegisterUncreatableType<BacktraceGenerator>("org.kde.drkonqi", 1, 0, "BacktraceGenerator1", QStringLiteral("Cannot create WarningLevel in QML")); 0076 qmlRegisterUncreatableType<BacktraceParser>("org.kde.drkonqi", 1, 0, "BacktraceParser", QStringLiteral("Cannot create WarningLevel in QML")); 0077 0078 const QUrl mainUrl(QStringLiteral("qrc:/ui/main.qml")); 0079 QObject::connect( 0080 engine, 0081 &QQmlApplicationEngine::objectCreated, 0082 this, 0083 [mainUrl, this](QObject *obj, const QUrl &objUrl) { 0084 if (!obj && mainUrl == objUrl) { 0085 qWarning() << "Failed to load QML dialog, falling back to QWidget."; 0086 QDialog::show(); 0087 } 0088 }, 0089 Qt::QueuedConnection); 0090 engine->load(mainUrl); 0091 } 0092 0093 DrKonqiDialog::DrKonqiDialog(QWidget *parent) 0094 : QDialog(parent) 0095 { 0096 setAttribute(Qt::WA_DeleteOnClose, true); 0097 QGuiApplication::setQuitOnLastWindowClosed(true); 0098 0099 // Setting dialog title and icon 0100 setWindowTitle(DrKonqi::crashedApplication()->name()); 0101 setWindowIcon(QIcon::fromTheme(QStringLiteral("tools-report-bug"))); 0102 0103 auto *l = new QVBoxLayout(this); 0104 m_tabWidget = new QTabWidget(this); 0105 l->addWidget(m_tabWidget); 0106 m_buttonBox = new QDialogButtonBox(this); 0107 connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accepted); 0108 connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::rejected); 0109 l->addWidget(m_buttonBox); 0110 0111 connect(m_tabWidget, &QTabWidget::currentChanged, this, &DrKonqiDialog::tabIndexChanged); 0112 0113 KConfigGroup config(KSharedConfig::openConfig(), "General"); 0114 0115 if (!config.readEntry(QStringLiteral("ShowOnlyBacktrace"), false)) { 0116 buildIntroWidget(); 0117 m_tabWidget->addTab(m_introWidget, i18nc("@title:tab general information", "&General")); 0118 } 0119 0120 m_backtraceWidget = new BacktraceWidget(DrKonqi::debuggerManager()->backtraceGenerator(), this); 0121 m_tabWidget->addTab(m_backtraceWidget, i18nc("@title:tab", "&Developer Information")); 0122 0123 m_tabWidget->tabBar()->setVisible(m_tabWidget->count() > 1); 0124 0125 buildDialogButtons(); 0126 0127 KWindowConfig::restoreWindowSize(windowHandle(), config); 0128 0129 // Force a 16:9 ratio for nice appearance by default. 0130 QSize aspect(16, 9); 0131 aspect.scale(sizeHint(), Qt::KeepAspectRatioByExpanding); 0132 resize(aspect); 0133 } 0134 0135 DrKonqiDialog::~DrKonqiDialog() 0136 { 0137 KConfigGroup config(KSharedConfig::openConfig(), "General"); 0138 KWindowConfig::saveWindowSize(windowHandle(), config); 0139 } 0140 0141 void DrKonqiDialog::tabIndexChanged(int index) 0142 { 0143 if (index == m_tabWidget->indexOf(m_backtraceWidget)) { 0144 m_backtraceWidget->generateBacktrace(); 0145 } 0146 } 0147 0148 void DrKonqiDialog::buildIntroWidget() 0149 { 0150 const CrashedApplication *crashedApp = DrKonqi::crashedApplication(); 0151 0152 m_introWidget = new QWidget(this); 0153 ui.setupUi(m_introWidget); 0154 0155 ui.titleLabel->setText(xi18nc("@info", 0156 "<para>We are sorry, <application>%1</application> " 0157 "closed unexpectedly.</para>", 0158 crashedApp->name())); 0159 0160 QString reportMessage; 0161 if (!crashedApp->bugReportAddress().isEmpty()) { 0162 if (crashedApp->fakeExecutableBaseName() == QLatin1String("drkonqi")) { // Handle own crashes 0163 reportMessage = xi18nc("@info", 0164 "<para>As the Crash Handler itself has failed, the " 0165 "automatic reporting process is disabled to reduce the " 0166 "risks of failing again.<nl /><nl />" 0167 "Please, <link url='%1'>manually report</link> this error " 0168 "to the KDE bug tracking system. Do not forget to include " 0169 "the backtrace from the <interface>Developer Information</interface> " 0170 "tab.</para>", 0171 DRKONQI_REPORT_BUG_URL); 0172 } else if (DrKonqi::isSafer()) { 0173 reportMessage = xi18nc("@info", 0174 "<para>The reporting assistant is disabled because " 0175 "the crash handler dialog was started in safe mode." 0176 "<nl />You can manually report this bug to %1 " 0177 "(including the backtrace from the " 0178 "<interface>Developer Information</interface> " 0179 "tab.)</para>", 0180 crashedApp->bugReportAddress()); 0181 } else if (crashedApp->hasDeletedFiles()) { 0182 reportMessage = xi18nc("@info", 0183 "<para>The reporting assistant is disabled because " 0184 "the crashed application appears to have been updated or " 0185 "uninstalled since it had been started. This prevents accurate " 0186 "crash reporting and can also be the cause of this crash.</para>" 0187 "<para>After updating it is always a good idea to log out and back " 0188 "in to make sure the update is fully applied and will not cause " 0189 "any side effects.</para>"); 0190 } else { 0191 reportMessage = xi18nc("@info", 0192 "<para>You can help us improve KDE Software by reporting " 0193 "this error.<nl /><link url='%1'>Learn " 0194 "more about bug reporting.</link></para>", 0195 ABOUT_BUG_REPORTING_URL); 0196 } 0197 } else { 0198 reportMessage = xi18nc("@info", 0199 "<para>You cannot report this error, because " 0200 "<application>%1</application> does not provide a bug reporting " 0201 "address.</para>", 0202 crashedApp->name()); 0203 } 0204 ui.infoLabel->setText(reportMessage); 0205 connect(ui.infoLabel, &QLabel::linkActivated, this, &DrKonqiDialog::linkActivated); 0206 0207 ui.detailsTitleLabel->setText(QStringLiteral("<strong>%1</strong>").arg(i18nc("@label", "Details:"))); 0208 0209 QLocale locale; 0210 ui.detailsLabel->setText(xi18nc("@info Note the time information is divided into date and time parts", 0211 "<para>Executable: <application>%1" 0212 "</application> PID: %2 Signal: %3 (%4) " 0213 "Time: %5 %6</para>", 0214 crashedApp->fakeExecutableBaseName(), 0215 // prevent number localization by ki18n 0216 QString::number(crashedApp->pid()), 0217 crashedApp->signalName(), 0218 #if defined(Q_OS_UNIX) 0219 // prevent number localization by ki18n 0220 QString::number(crashedApp->signalNumber()), 0221 #else 0222 // windows uses weird big numbers for exception codes, 0223 // so it doesn't make sense to display them in decimal 0224 QString().asprintf("0x%8x", crashedApp->signalNumber()), 0225 #endif 0226 locale.toString(crashedApp->datetime().date(), QLocale::ShortFormat), 0227 0228 locale.toString(crashedApp->datetime().time()))); 0229 } 0230 0231 void DrKonqiDialog::buildDialogButtons() 0232 { 0233 const CrashedApplication *crashedApp = DrKonqi::crashedApplication(); 0234 0235 // Set dialog buttons 0236 m_buttonBox->setStandardButtons(QDialogButtonBox::Close); 0237 0238 // Default debugger button and menu (only for developer mode): User2 0239 DebuggerManager *debuggerManager = DrKonqi::debuggerManager(); 0240 m_debugButton = new QPushButton(this); 0241 KGuiItem2 debugItem(i18nc("@action:button this is the debug menu button label which contains the debugging applications", "&Debug"), 0242 QIcon::fromTheme(QStringLiteral("applications-development")), 0243 i18nc("@info:tooltip", "Starts a program to debug the crashed application.")); 0244 KGuiItem::assign(m_debugButton, debugItem); 0245 m_debugButton->setVisible(false); 0246 0247 m_debugMenu = new QMenu(this); 0248 m_debugButton->setMenu(m_debugMenu); 0249 0250 // Only add the debugger if requested by the config or if a KDevelop session is running. 0251 const bool showExternal = debuggerManager->showExternalDebuggers(); 0252 QList<AbstractDebuggerLauncher *> debuggers = debuggerManager->availableExternalDebuggers(); 0253 for (AbstractDebuggerLauncher *launcher : std::as_const(debuggers)) { 0254 if (showExternal || qobject_cast<DBusInterfaceLauncher *>(launcher)) { 0255 addDebugger(launcher); 0256 } 0257 } 0258 0259 connect(debuggerManager, &DebuggerManager::externalDebuggerAdded, this, &DrKonqiDialog::addDebugger); 0260 connect(debuggerManager, &DebuggerManager::externalDebuggerRemoved, this, &DrKonqiDialog::removeDebugger); 0261 connect(debuggerManager, &DebuggerManager::debuggerRunning, this, &DrKonqiDialog::enableDebugMenu); 0262 0263 // Restart application button 0264 m_restartButton = new QPushButton(m_buttonBox); 0265 KGuiItem::assign(m_restartButton, DrStandardGuiItem::appRestart()); 0266 m_restartButton->setEnabled(!crashedApp->hasBeenRestarted() && crashedApp->fakeExecutableBaseName() != QLatin1String("drkonqi")); 0267 m_buttonBox->addButton(m_restartButton, QDialogButtonBox::ActionRole); 0268 connect(m_restartButton, &QAbstractButton::clicked, crashedApp, &CrashedApplication::restart); 0269 connect(crashedApp, &CrashedApplication::restarted, this, &DrKonqiDialog::applicationRestarted); 0270 0271 // Close button 0272 QString tooltipText = i18nc("@info:tooltip", "Close this dialog (you will lose the crash information.)"); 0273 m_buttonBox->button(QDialogButtonBox::Close)->setToolTip(tooltipText); 0274 m_buttonBox->button(QDialogButtonBox::Close)->setWhatsThis(tooltipText); 0275 m_buttonBox->button(QDialogButtonBox::Close)->setFocus(); 0276 } 0277 0278 void DrKonqiDialog::addDebugger(AbstractDebuggerLauncher *launcher) 0279 { 0280 auto *action = new QAction(QIcon::fromTheme(QStringLiteral("applications-development")), 0281 i18nc("@action:inmenu 1 is the debugger name", "Debug in %1", launcher->name()), 0282 m_debugMenu); 0283 m_debugMenu->addAction(action); 0284 connect(action, &QAction::triggered, launcher, &AbstractDebuggerLauncher::start); 0285 m_debugMenuActions.insert(launcher, action); 0286 0287 // Make sure that the debug button is the first button with action role to be 0288 // inserted, then add the other buttons. See removeDebugger below for more information. 0289 if (!m_debugButtonInBox) { 0290 auto buttons = m_buttonBox->buttons(); 0291 m_buttonBox->addButton(m_debugButton, QDialogButtonBox::ActionRole); 0292 m_debugButton->setVisible(true); 0293 for (QAbstractButton *button : buttons) { 0294 if (m_buttonBox->buttonRole(button) == QDialogButtonBox::ActionRole) { 0295 m_buttonBox->addButton(button, QDialogButtonBox::ActionRole); 0296 } 0297 } 0298 m_debugButtonInBox = true; 0299 } 0300 } 0301 0302 void DrKonqiDialog::removeDebugger(AbstractDebuggerLauncher *launcher) 0303 { 0304 QAction *action = m_debugMenuActions.take(launcher); 0305 if (action) { 0306 m_debugMenu->removeAction(action); 0307 action->deleteLater(); 0308 // Remove the button from the box, otherwise the box will force 0309 // it visible as it calls show() explicitly. (QTBUG-3651) 0310 if (m_debugMenu->isEmpty()) { 0311 m_buttonBox->removeButton(m_debugButton); 0312 m_debugButton->setVisible(false); 0313 m_debugButton->setParent(this); 0314 m_debugButtonInBox = false; 0315 } 0316 } else { 0317 qCWarning(DRKONQI_LOG) << "Invalid launcher"; 0318 } 0319 } 0320 0321 void DrKonqiDialog::enableDebugMenu(bool debuggerRunning) 0322 { 0323 m_debugButton->setEnabled(!debuggerRunning); 0324 } 0325 0326 void DrKonqiDialog::linkActivated(const QString &link) 0327 { 0328 if (link == DRKONQI_REPORT_BUG_URL || link == ABOUT_BUG_REPORTING_URL) { 0329 QDesktopServices::openUrl(QUrl(link)); 0330 } else if (link.startsWith(QLatin1String("http"))) { 0331 qCWarning(DRKONQI_LOG) << "unexpected link"; 0332 QDesktopServices::openUrl(QUrl(link)); 0333 } 0334 } 0335 0336 void DrKonqiDialog::applicationRestarted(bool success) 0337 { 0338 m_restartButton->setEnabled(!success); 0339 } 0340 0341 #include "drkonqidialog.moc"