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 <KStandardActions>
34
35#include <optional>
36
37using namespace KDEPrivate;
38
39class KHelpMenuPrivate
40{
41public:
42 KHelpMenuPrivate()
43 {
44 }
45 ~KHelpMenuPrivate()
46 {
47 delete mMenu;
48 delete mAboutApp;
49 delete mAboutKDE;
50 delete mBugReport;
51 delete mSwitchApplicationLanguage;
52 }
53
54 void createActions(KHelpMenu *q, bool showWhatsThis);
55
56 QMenu *mMenu = nullptr;
57 QDialog *mAboutApp = nullptr;
58 KAboutKdeDialog *mAboutKDE = nullptr;
59 KBugReport *mBugReport = nullptr;
60 QAction *mDonateAction = nullptr;
61 KSwitchLanguageDialog *mSwitchApplicationLanguage = nullptr;
62
63 // TODO evaluate if we use static_cast<QWidget*>(parent()) instead of mParent to win that bit of memory
64 QWidget *mParent = nullptr;
65
66 QAction *mHandBookAction = nullptr;
67 QAction *mWhatsThisAction = nullptr;
68 QAction *mReportBugAction = nullptr;
69 QAction *mSwitchApplicationLanguageAction = nullptr;
70 QAction *mAboutAppAction = nullptr;
71 QAction *mAboutKDEAction = nullptr;
72
73 std::optional<KAboutData> mAboutData;
74};
75
76#if KXMLGUI_BUILD_DEPRECATED_SINCE(6, 9)
77KHelpMenu::KHelpMenu(QWidget *parent, const QString &, bool showWhatsThis)
78 : QObject(parent)
79 , d(new KHelpMenuPrivate)
80{
81 d->mParent = parent;
82 d->createActions(q: this, showWhatsThis);
83}
84#endif
85
86KHelpMenu::KHelpMenu(QWidget *parent)
87 : QObject(parent)
88 , d(new KHelpMenuPrivate)
89{
90 d->mParent = parent;
91 d->createActions(q: this, showWhatsThis: true);
92}
93
94#if KXMLGUI_BUILD_DEPRECATED_SINCE(6, 9)
95KHelpMenu::KHelpMenu(QWidget *parent, const KAboutData &aboutData, bool showWhatsThis)
96 : QObject(parent)
97 , d(new KHelpMenuPrivate)
98{
99 d->mParent = parent;
100 d->mAboutData = aboutData;
101 d->createActions(q: this, showWhatsThis);
102}
103#endif
104
105KHelpMenu::KHelpMenu(QWidget *parent, const KAboutData &aboutData)
106 : QObject(parent)
107 , d(new KHelpMenuPrivate)
108{
109 d->mParent = parent;
110 d->mAboutData = aboutData;
111 d->createActions(q: this, showWhatsThis: true);
112}
113
114KHelpMenu::~KHelpMenu()
115{
116 delete d;
117}
118
119void KHelpMenu::setShowWhatsThis(bool showWhatsThis)
120{
121 if (!showWhatsThis) {
122 delete d->mWhatsThisAction;
123 d->mWhatsThisAction = nullptr;
124 } else if (KAuthorized::authorizeAction(QStringLiteral("help_whats_this"))) {
125 d->mWhatsThisAction = KStandardActions::whatsThis(recvr: this, slot: &KHelpMenu::contextHelpActivated, parent: this);
126 }
127}
128
129void KHelpMenuPrivate::createActions(KHelpMenu *q, bool showWhatsThis)
130{
131 if (KAuthorized::authorizeAction(QStringLiteral("help_contents"))) {
132 mHandBookAction = KStandardActions::helpContents(recvr: q, slot: &KHelpMenu::appHelpActivated, parent: q);
133 }
134 if (showWhatsThis && KAuthorized::authorizeAction(QStringLiteral("help_whats_this"))) {
135 mWhatsThisAction = KStandardActions::whatsThis(recvr: q, slot: &KHelpMenu::contextHelpActivated, parent: q);
136 }
137
138 const auto bugAddress = mAboutData ? mAboutData->bugAddress() : KAboutData::applicationData().bugAddress();
139 if (KAuthorized::authorizeAction(QStringLiteral("help_report_bug")) && !bugAddress.isEmpty()) {
140 mReportBugAction = KStandardActions::reportBug(recvr: q, slot: &KHelpMenu::reportBug, parent: q);
141 }
142
143 if (KAuthorized::authorizeAction(QStringLiteral("help_donate")) && bugAddress == QLatin1String("submit@bugs.kde.org")) {
144 mDonateAction = KStandardActions::donate(recvr: q, slot: &KHelpMenu::donate, parent: q);
145 }
146
147 if (KAuthorized::authorizeAction(QStringLiteral("switch_application_language"))) {
148 mSwitchApplicationLanguageAction = KStandardActions::switchApplicationLanguage(recvr: q, slot: &KHelpMenu::switchApplicationLanguage, parent: q);
149 }
150
151 if (KAuthorized::authorizeAction(QStringLiteral("help_about_app"))) {
152 mAboutAppAction = KStandardActions::aboutApp(recvr: q, slot: &KHelpMenu::aboutApplication, parent: q);
153 }
154
155 if (KAuthorized::authorizeAction(QStringLiteral("help_about_kde"))) {
156 mAboutKDEAction = KStandardActions::aboutKDE(recvr: q, slot: &KHelpMenu::aboutKDE, parent: q);
157 }
158}
159
160// Used in the non-xml-gui case, like kfind or ksnapshot's help button.
161QMenu *KHelpMenu::menu()
162{
163 if (!d->mMenu) {
164 d->mMenu = new QMenu(d->mParent);
165 connect(sender: d->mMenu, signal: &QObject::destroyed, context: this, slot: &KHelpMenu::menuDestroyed);
166
167 d->mMenu->setTitle(i18n("&Help"));
168
169 bool need_separator = false;
170 if (d->mHandBookAction) {
171 d->mMenu->addAction(action: d->mHandBookAction);
172 need_separator = true;
173 }
174
175 if (d->mWhatsThisAction) {
176 d->mMenu->addAction(action: d->mWhatsThisAction);
177 need_separator = true;
178 }
179
180 if (d->mReportBugAction) {
181 if (need_separator) {
182 d->mMenu->addSeparator();
183 }
184 d->mMenu->addAction(action: d->mReportBugAction);
185 need_separator = true;
186 }
187
188 if (d->mDonateAction) {
189 if (need_separator) {
190 d->mMenu->addSeparator();
191 }
192 d->mMenu->addAction(action: d->mDonateAction);
193 need_separator = true;
194 }
195
196 if (d->mSwitchApplicationLanguageAction) {
197 if (need_separator) {
198 d->mMenu->addSeparator();
199 }
200 d->mMenu->addAction(action: d->mSwitchApplicationLanguageAction);
201 need_separator = true;
202 }
203
204 if (need_separator) {
205 d->mMenu->addSeparator();
206 }
207
208 if (d->mAboutAppAction) {
209 d->mMenu->addAction(action: d->mAboutAppAction);
210 }
211
212 if (d->mAboutKDEAction) {
213 d->mMenu->addAction(action: d->mAboutKDEAction);
214 }
215 }
216
217 return d->mMenu;
218}
219
220QAction *KHelpMenu::action(MenuId id) const
221{
222 switch (id) {
223 case menuHelpContents:
224 return d->mHandBookAction;
225
226 case menuWhatsThis:
227 return d->mWhatsThisAction;
228
229 case menuReportBug:
230 return d->mReportBugAction;
231
232 case menuSwitchLanguage:
233 return d->mSwitchApplicationLanguageAction;
234
235 case menuAboutApp:
236 return d->mAboutAppAction;
237
238 case menuAboutKDE:
239 return d->mAboutKDEAction;
240
241 case menuDonate:
242 return d->mDonateAction;
243 }
244
245 return nullptr;
246}
247
248void KHelpMenu::appHelpActivated()
249{
250 QDesktopServices::openUrl(url: QUrl(QStringLiteral("help:/")));
251}
252
253void KHelpMenu::aboutApplication()
254{
255 if (receivers(SIGNAL(showAboutApplication())) > 0) {
256 Q_EMIT showAboutApplication();
257 } else {
258 if (!d->mAboutApp) {
259 d->mAboutApp = new KAboutApplicationDialog(d->mAboutData ? *d->mAboutData : KAboutData::applicationData(), d->mParent);
260 connect(sender: d->mAboutApp, signal: &QDialog::finished, context: this, slot: &KHelpMenu::dialogFinished);
261 }
262 d->mAboutApp->show();
263 }
264}
265
266void KHelpMenu::aboutKDE()
267{
268 if (!d->mAboutKDE) {
269 d->mAboutKDE = new KAboutKdeDialog(d->mParent);
270 connect(sender: d->mAboutKDE, signal: &QDialog::finished, context: this, slot: &KHelpMenu::dialogFinished);
271 }
272 d->mAboutKDE->show();
273}
274
275void KHelpMenu::reportBug()
276{
277 if (!d->mBugReport) {
278 d->mBugReport = new KBugReport(d->mAboutData ? *d->mAboutData : KAboutData::applicationData(), d->mParent);
279 connect(sender: d->mBugReport, signal: &QDialog::finished, context: this, slot: &KHelpMenu::dialogFinished);
280 }
281 d->mBugReport->show();
282}
283
284void KHelpMenu::switchApplicationLanguage()
285{
286 if (!d->mSwitchApplicationLanguage) {
287 d->mSwitchApplicationLanguage = new KSwitchLanguageDialog(d->mParent);
288 connect(sender: d->mSwitchApplicationLanguage, signal: &QDialog::finished, context: this, slot: &KHelpMenu::dialogFinished);
289 }
290 d->mSwitchApplicationLanguage->show();
291}
292
293void KHelpMenu::donate()
294{
295 const auto componentName = d->mAboutData ? d->mAboutData->componentName() : KAboutData::applicationData().componentName();
296 QDesktopServices::openUrl(url: QUrl(QLatin1String("https://www.kde.org/donate?app=") + componentName));
297}
298
299void KHelpMenu::dialogFinished()
300{
301 QTimer::singleShot(interval: 0, receiver: this, slot: &KHelpMenu::timerExpired);
302}
303
304void KHelpMenu::timerExpired()
305{
306 if (d->mAboutKDE && !d->mAboutKDE->isVisible()) {
307 delete d->mAboutKDE;
308 d->mAboutKDE = nullptr;
309 }
310
311 if (d->mBugReport && !d->mBugReport->isVisible()) {
312 delete d->mBugReport;
313 d->mBugReport = nullptr;
314 }
315
316 if (d->mSwitchApplicationLanguage && !d->mSwitchApplicationLanguage->isVisible()) {
317 delete d->mSwitchApplicationLanguage;
318 d->mSwitchApplicationLanguage = nullptr;
319 }
320
321 if (d->mAboutApp && !d->mAboutApp->isVisible()) {
322 delete d->mAboutApp;
323 d->mAboutApp = nullptr;
324 }
325}
326
327void KHelpMenu::menuDestroyed()
328{
329 d->mMenu = nullptr;
330}
331
332void KHelpMenu::contextHelpActivated()
333{
334 QWhatsThis::enterWhatsThisMode();
335}
336
337#include "moc_khelpmenu.cpp"
338

source code of kxmlgui/src/khelpmenu.cpp