1/*
2 This file is part of the KDE Libraries
3 SPDX-FileCopyrightText: 1999-2000 Espen Sand <espen@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8// I (espen) prefer that header files are included alphabetically
9
10#include "khelpmenu.h"
11
12#include <QAction>
13#include <QApplication>
14#include <QDesktopServices>
15#include <QDialogButtonBox>
16#include <QLabel>
17#include <QMenu>
18#include <QStandardPaths>
19#include <QStyle>
20#include <QTimer>
21#include <QUrl>
22#include <QWhatsThis>
23#include <QWidget>
24
25#include "kaboutapplicationdialog.h"
26#include "kaboutkdedialog_p.h"
27#include "kbugreport.h"
28#include "kswitchlanguagedialog_p.h"
29
30#include <KAboutData>
31#include <KAuthorized>
32#include <KLocalizedString>
33#include <KStandardAction>
34
35using namespace KDEPrivate;
36
37class KHelpMenuPrivate
38{
39public:
40 KHelpMenuPrivate()
41 : mAboutData(KAboutData::applicationData())
42 {
43 }
44 ~KHelpMenuPrivate()
45 {
46 delete mMenu;
47 delete mAboutApp;
48 delete mAboutKDE;
49 delete mBugReport;
50 delete mSwitchApplicationLanguage;
51 }
52
53 void createActions(KHelpMenu *q);
54
55 QMenu *mMenu = nullptr;
56 QDialog *mAboutApp = nullptr;
57 KAboutKdeDialog *mAboutKDE = nullptr;
58 KBugReport *mBugReport = nullptr;
59 QAction *mDonateAction = nullptr;
60 KSwitchLanguageDialog *mSwitchApplicationLanguage = nullptr;
61
62 // TODO evaluate if we use static_cast<QWidget*>(parent()) instead of mParent to win that bit of memory
63 QWidget *mParent = nullptr;
64 QString mAboutAppText;
65
66 bool mShowWhatsThis = false;
67 bool mActionsCreated = false;
68
69 QAction *mHandBookAction = nullptr;
70 QAction *mWhatsThisAction = nullptr;
71 QAction *mReportBugAction = nullptr;
72 QAction *mSwitchApplicationLanguageAction = nullptr;
73 QAction *mAboutAppAction = nullptr;
74 QAction *mAboutKDEAction = nullptr;
75
76 KAboutData mAboutData;
77};
78
79KHelpMenu::KHelpMenu(QWidget *parent, const QString &aboutAppText, bool showWhatsThis)
80 : QObject(parent)
81 , d(new KHelpMenuPrivate)
82{
83 d->mAboutAppText = aboutAppText;
84 d->mShowWhatsThis = showWhatsThis;
85 d->mParent = parent;
86 d->createActions(q: this);
87}
88
89KHelpMenu::KHelpMenu(QWidget *parent, const KAboutData &aboutData, bool showWhatsThis)
90 : QObject(parent)
91 , d(new KHelpMenuPrivate)
92{
93 d->mShowWhatsThis = showWhatsThis;
94 d->mParent = parent;
95 d->mAboutData = aboutData;
96 d->createActions(q: this);
97}
98
99KHelpMenu::~KHelpMenu()
100{
101 delete d;
102}
103
104void KHelpMenuPrivate::createActions(KHelpMenu *q)
105{
106 if (mActionsCreated) {
107 return;
108 }
109 mActionsCreated = true;
110
111 if (KAuthorized::authorizeAction(QStringLiteral("help_contents"))) {
112 mHandBookAction = KStandardAction::helpContents(recvr: q, slot: &KHelpMenu::appHelpActivated, parent: q);
113 }
114 if (mShowWhatsThis && KAuthorized::authorizeAction(QStringLiteral("help_whats_this"))) {
115 mWhatsThisAction = KStandardAction::whatsThis(recvr: q, slot: &KHelpMenu::contextHelpActivated, parent: q);
116 }
117
118 if (KAuthorized::authorizeAction(QStringLiteral("help_report_bug")) && !mAboutData.bugAddress().isEmpty()) {
119 mReportBugAction = KStandardAction::reportBug(recvr: q, slot: &KHelpMenu::reportBug, parent: q);
120 }
121
122 if (KAuthorized::authorizeAction(QStringLiteral("help_donate")) && mAboutData.bugAddress() == QLatin1String("submit@bugs.kde.org")) {
123 mDonateAction = KStandardAction::donate(recvr: q, slot: &KHelpMenu::donate, parent: q);
124 }
125
126 if (KAuthorized::authorizeAction(QStringLiteral("switch_application_language"))) {
127 mSwitchApplicationLanguageAction = KStandardAction::switchApplicationLanguage(recvr: q, slot: &KHelpMenu::switchApplicationLanguage, parent: q);
128 }
129
130 if (KAuthorized::authorizeAction(QStringLiteral("help_about_app"))) {
131 mAboutAppAction = KStandardAction::aboutApp(recvr: q, slot: &KHelpMenu::aboutApplication, parent: q);
132 }
133
134 if (KAuthorized::authorizeAction(QStringLiteral("help_about_kde"))) {
135 mAboutKDEAction = KStandardAction::aboutKDE(recvr: q, slot: &KHelpMenu::aboutKDE, parent: q);
136 }
137}
138
139// Used in the non-xml-gui case, like kfind or ksnapshot's help button.
140QMenu *KHelpMenu::menu()
141{
142 if (!d->mMenu) {
143 d->mMenu = new QMenu(d->mParent);
144 connect(sender: d->mMenu, signal: &QObject::destroyed, context: this, slot: &KHelpMenu::menuDestroyed);
145
146 d->mMenu->setTitle(i18n("&Help"));
147
148 d->createActions(q: this);
149
150 bool need_separator = false;
151 if (d->mHandBookAction) {
152 d->mMenu->addAction(action: d->mHandBookAction);
153 need_separator = true;
154 }
155
156 if (d->mWhatsThisAction) {
157 d->mMenu->addAction(action: d->mWhatsThisAction);
158 need_separator = true;
159 }
160
161 if (d->mReportBugAction) {
162 if (need_separator) {
163 d->mMenu->addSeparator();
164 }
165 d->mMenu->addAction(action: d->mReportBugAction);
166 need_separator = true;
167 }
168
169 if (d->mDonateAction) {
170 if (need_separator) {
171 d->mMenu->addSeparator();
172 }
173 d->mMenu->addAction(action: d->mDonateAction);
174 need_separator = true;
175 }
176
177 if (d->mSwitchApplicationLanguageAction) {
178 if (need_separator) {
179 d->mMenu->addSeparator();
180 }
181 d->mMenu->addAction(action: d->mSwitchApplicationLanguageAction);
182 need_separator = true;
183 }
184
185 if (need_separator) {
186 d->mMenu->addSeparator();
187 }
188
189 if (d->mAboutAppAction) {
190 d->mMenu->addAction(action: d->mAboutAppAction);
191 }
192
193 if (d->mAboutKDEAction) {
194 d->mMenu->addAction(action: d->mAboutKDEAction);
195 }
196 }
197
198 return d->mMenu;
199}
200
201QAction *KHelpMenu::action(MenuId id) const
202{
203 switch (id) {
204 case menuHelpContents:
205 return d->mHandBookAction;
206
207 case menuWhatsThis:
208 return d->mWhatsThisAction;
209
210 case menuReportBug:
211 return d->mReportBugAction;
212
213 case menuSwitchLanguage:
214 return d->mSwitchApplicationLanguageAction;
215
216 case menuAboutApp:
217 return d->mAboutAppAction;
218
219 case menuAboutKDE:
220 return d->mAboutKDEAction;
221
222 case menuDonate:
223 return d->mDonateAction;
224 }
225
226 return nullptr;
227}
228
229void KHelpMenu::appHelpActivated()
230{
231 QDesktopServices::openUrl(url: QUrl(QStringLiteral("help:/")));
232}
233
234void KHelpMenu::aboutApplication()
235{
236 if (receivers(SIGNAL(showAboutApplication())) > 0) {
237 Q_EMIT showAboutApplication();
238 } else { // if (d->mAboutData)
239 if (!d->mAboutApp) {
240 d->mAboutApp = new KAboutApplicationDialog(d->mAboutData, d->mParent);
241 connect(sender: d->mAboutApp, signal: &QDialog::finished, context: this, slot: &KHelpMenu::dialogFinished);
242 }
243 d->mAboutApp->show();
244 }
245#if 0 // KF5: when can this happen?
246 else {
247 if (!d->mAboutApp) {
248 d->mAboutApp = new QDialog(d->mParent, Qt::Dialog);
249 QString caption = QGuiApplication::applicationDisplayName();
250 if (caption.isEmpty()) {
251 caption = QCoreApplication::applicationName();
252 }
253 d->mAboutApp->setWindowTitle(i18n("About %1", caption));
254 d->mAboutApp->setObjectName(QStringLiteral("about"));
255 connect(d->mAboutApp, SIGNAL(finished(int)), this, SLOT(dialogFinished()));
256
257 const int spacingHint = d->mAboutApp->style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing);
258 const int marginHint = d->mAboutApp->style()->pixelMetric(QStyle::PM_DefaultChildMargin);
259
260 QVBoxLayout *vbox = new QVBoxLayout;
261 d->mAboutApp->setLayout(vbox);
262
263 QHBoxLayout *hbox = new QHBoxLayout;
264 hbox->setSpacing(spacingHint * 3);
265 hbox->setMargin(marginHint * 1);
266
267 const int size = IconSize(KIconLoader::Dialog);
268 QLabel *label1 = new QLabel(d->mAboutApp);
269 label1->setPixmap(qApp->windowIcon().pixmap(size, size));
270 QLabel *label2 = new QLabel(d->mAboutApp);
271 label2->setText(d->mAboutAppText);
272
273 hbox->addWidget(label1);
274 hbox->addWidget(label2);
275
276 vbox->addLayout(hbox);
277
278 QDialogButtonBox *buttonBox = new QDialogButtonBox(d->mAboutApp);
279 buttonBox->setStandardButtons(QDialogButtonBox::Close);
280 connect(buttonBox, SIGNAL(accepted()), d->mAboutApp, SLOT(accept()));
281 connect(buttonBox, SIGNAL(rejected()), d->mAboutApp, SLOT(reject()));
282 vbox->addWidget(buttonBox);
283 }
284 d->mAboutApp->show();
285 }
286#endif
287}
288
289void KHelpMenu::aboutKDE()
290{
291 if (!d->mAboutKDE) {
292 d->mAboutKDE = new KAboutKdeDialog(d->mParent);
293 connect(sender: d->mAboutKDE, signal: &QDialog::finished, context: this, slot: &KHelpMenu::dialogFinished);
294 }
295 d->mAboutKDE->show();
296}
297
298void KHelpMenu::reportBug()
299{
300 if (!d->mBugReport) {
301 d->mBugReport = new KBugReport(d->mAboutData, d->mParent);
302 connect(sender: d->mBugReport, signal: &QDialog::finished, context: this, slot: &KHelpMenu::dialogFinished);
303 }
304 d->mBugReport->show();
305}
306
307void KHelpMenu::switchApplicationLanguage()
308{
309 if (!d->mSwitchApplicationLanguage) {
310 d->mSwitchApplicationLanguage = new KSwitchLanguageDialog(d->mParent);
311 connect(sender: d->mSwitchApplicationLanguage, signal: &QDialog::finished, context: this, slot: &KHelpMenu::dialogFinished);
312 }
313 d->mSwitchApplicationLanguage->show();
314}
315
316void KHelpMenu::donate()
317{
318 QDesktopServices::openUrl(url: QUrl(QLatin1String("https://www.kde.org/donate?app=") + d->mAboutData.componentName()));
319}
320
321void KHelpMenu::dialogFinished()
322{
323 QTimer::singleShot(interval: 0, receiver: this, slot: &KHelpMenu::timerExpired);
324}
325
326void KHelpMenu::timerExpired()
327{
328 if (d->mAboutKDE && !d->mAboutKDE->isVisible()) {
329 delete d->mAboutKDE;
330 d->mAboutKDE = nullptr;
331 }
332
333 if (d->mBugReport && !d->mBugReport->isVisible()) {
334 delete d->mBugReport;
335 d->mBugReport = nullptr;
336 }
337
338 if (d->mSwitchApplicationLanguage && !d->mSwitchApplicationLanguage->isVisible()) {
339 delete d->mSwitchApplicationLanguage;
340 d->mSwitchApplicationLanguage = nullptr;
341 }
342
343 if (d->mAboutApp && !d->mAboutApp->isVisible()) {
344 delete d->mAboutApp;
345 d->mAboutApp = nullptr;
346 }
347}
348
349void KHelpMenu::menuDestroyed()
350{
351 d->mMenu = nullptr;
352}
353
354void KHelpMenu::contextHelpActivated()
355{
356 QWhatsThis::enterWhatsThisMode();
357}
358
359#include "moc_khelpmenu.cpp"
360

source code of kxmlgui/src/khelpmenu.cpp