1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2000 Simon Hausmann <hausmann@kde.org>
4 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kxmlguibuilder.h"
10
11#include "debug.h"
12#include "kmainwindow.h"
13#include "kmenumenuhandler_p.h"
14#include "ktoolbar.h"
15#include "kxmlguiclient.h"
16#include "kxmlguiwindow.h"
17
18#include <KAuthorized>
19#include <KLocalizedString>
20
21#include <QAction>
22#include <QDomElement>
23#include <QMenu>
24#include <QMenuBar>
25#include <QObject>
26#include <QStatusBar>
27
28using namespace KDEPrivate;
29
30class KXMLGUIBuilderPrivate
31{
32public:
33 KXMLGUIBuilderPrivate()
34 {
35 }
36 ~KXMLGUIBuilderPrivate()
37 {
38 }
39
40 QWidget *m_widget = nullptr;
41
42 QString tagMainWindow;
43 QString tagMenuBar;
44 QString tagMenu;
45 QString tagToolBar;
46 QString tagStatusBar;
47
48 QString tagSeparator;
49 QString tagSpacer;
50 QString tagTearOffHandle;
51 QString tagMenuTitle;
52
53 QString attrName;
54 QString attrLineSeparator;
55
56 QString attrDomain;
57 QString attrText1;
58 QString attrText2;
59 QString attrContext;
60
61 QString attrIcon;
62
63 KXMLGUIClient *m_client = nullptr;
64
65 KMenuMenuHandler *m_menumenuhandler = nullptr;
66};
67
68KXMLGUIBuilder::KXMLGUIBuilder(QWidget *widget)
69 : d(new KXMLGUIBuilderPrivate)
70{
71 d->m_widget = widget;
72
73 d->tagMainWindow = QStringLiteral("mainwindow");
74 d->tagMenuBar = QStringLiteral("menubar");
75 d->tagMenu = QStringLiteral("menu");
76 d->tagToolBar = QStringLiteral("toolbar");
77 d->tagStatusBar = QStringLiteral("statusbar");
78
79 d->tagSeparator = QStringLiteral("separator");
80 d->tagSpacer = QStringLiteral("spacer");
81 d->tagTearOffHandle = QStringLiteral("tearoffhandle");
82 d->tagMenuTitle = QStringLiteral("title");
83
84 d->attrName = QStringLiteral("name");
85 d->attrLineSeparator = QStringLiteral("lineseparator");
86
87 d->attrDomain = QStringLiteral("translationDomain");
88 d->attrText1 = QStringLiteral("text");
89 d->attrText2 = QStringLiteral("Text");
90 d->attrContext = QStringLiteral("context");
91
92 d->attrIcon = QStringLiteral("icon");
93
94 d->m_menumenuhandler = new KMenuMenuHandler(this);
95}
96
97KXMLGUIBuilder::~KXMLGUIBuilder()
98{
99 delete d->m_menumenuhandler;
100}
101
102QWidget *KXMLGUIBuilder::widget()
103{
104 return d->m_widget;
105}
106
107QStringList KXMLGUIBuilder::containerTags() const
108{
109 QStringList res;
110 res << d->tagMenu << d->tagToolBar << d->tagMainWindow << d->tagMenuBar << d->tagStatusBar;
111
112 return res;
113}
114
115QWidget *KXMLGUIBuilder::createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction)
116{
117 containerAction = nullptr;
118
119 if (element.attribute(QStringLiteral("deleted")).toLower() == QLatin1String("true")) {
120 return nullptr;
121 }
122
123 const QString tagName = element.tagName().toLower();
124 if (tagName == d->tagMainWindow) {
125 KMainWindow *mainwindow = qobject_cast<KMainWindow *>(object: d->m_widget); // could be 0
126 return mainwindow;
127 }
128
129 if (tagName == d->tagMenuBar) {
130 KMainWindow *mainWin = qobject_cast<KMainWindow *>(object: d->m_widget);
131 QMenuBar *bar = nullptr;
132 if (mainWin) {
133 bar = mainWin->menuBar();
134 }
135 if (!bar) {
136 bar = new QMenuBar(d->m_widget);
137 }
138 bar->show();
139 return bar;
140 }
141
142 if (tagName == d->tagMenu) {
143 QWidget *effectiveParent = parent;
144 if (!effectiveParent) {
145 effectiveParent = d->m_widget;
146 }
147
148 QString name = element.attribute(name: d->attrName);
149
150 if (!KAuthorized::authorizeAction(action: name)) {
151 return nullptr;
152 }
153
154 QMenu *popup = new QMenu(effectiveParent);
155 popup->setObjectName(name);
156
157 d->m_menumenuhandler->insertMenu(menu: popup);
158
159 QString i18nText;
160 QDomElement textElem = element.namedItem(name: d->attrText1).toElement();
161 if (textElem.isNull()) { // try with capital T
162 textElem = element.namedItem(name: d->attrText2).toElement();
163 }
164 const QString text = textElem.text();
165 const QString context = textElem.attribute(name: d->attrContext);
166
167 // qCDebug(DEBUG_KXMLGUI) << "DOMAIN" << KLocalizedString::applicationDomain();
168 // qCDebug(DEBUG_KXMLGUI) << "ELEMENT TEXT:" << text;
169
170 if (text.isEmpty()) { // still no luck
171 i18nText = i18n("No text");
172 } else {
173 QByteArray domain = textElem.attribute(name: d->attrDomain).toUtf8();
174 if (domain.isEmpty()) {
175 domain = element.ownerDocument().documentElement().attribute(name: d->attrDomain).toUtf8();
176 if (domain.isEmpty()) {
177 domain = KLocalizedString::applicationDomain();
178 }
179 }
180 if (context.isEmpty()) {
181 i18nText = i18nd(domain: domain.constData(), text: text.toUtf8().constData());
182 } else {
183 i18nText = i18ndc(domain: domain.constData(), context: context.toUtf8().constData(), text: text.toUtf8().constData());
184 }
185 }
186
187 // qCDebug(DEBUG_KXMLGUI) << "ELEMENT i18n TEXT:" << i18nText;
188
189 const QString icon = element.attribute(name: d->attrIcon);
190 QIcon pix;
191 if (!icon.isEmpty()) {
192 pix = QIcon::fromTheme(name: icon);
193 }
194
195 if (parent) {
196 QAction *act = popup->menuAction();
197 if (!icon.isEmpty()) {
198 act->setIcon(pix);
199 }
200 act->setText(i18nText);
201 if (index == -1 || index >= parent->actions().count()) {
202 parent->addAction(action: act);
203 } else {
204 parent->insertAction(before: parent->actions().value(i: index), action: act);
205 }
206 containerAction = act;
207 containerAction->setObjectName(name);
208 }
209
210 return popup;
211 }
212
213 if (tagName == d->tagToolBar) {
214 QString name = element.attribute(name: d->attrName);
215
216 KToolBar *bar = static_cast<KToolBar *>(d->m_widget->findChild<KToolBar *>(aName: name));
217 if (!bar) {
218 bar = new KToolBar(name, d->m_widget, false);
219 }
220
221 if (qobject_cast<KMainWindow *>(object: d->m_widget)) {
222 if (d->m_client && !d->m_client->xmlFile().isEmpty()) {
223 bar->addXMLGUIClient(client: d->m_client);
224 }
225 }
226 if (!bar->mainWindow()) {
227 bar->show();
228 }
229
230 bar->loadState(element);
231
232 return bar;
233 }
234
235 if (tagName == d->tagStatusBar) {
236 KMainWindow *mainWin = qobject_cast<KMainWindow *>(object: d->m_widget);
237 if (mainWin) {
238 mainWin->statusBar()->show();
239 return mainWin->statusBar();
240 }
241 QStatusBar *bar = new QStatusBar(d->m_widget);
242 return bar;
243 }
244
245 return nullptr;
246}
247
248void KXMLGUIBuilder::removeContainer(QWidget *container, QWidget *parent, QDomElement &element, QAction *containerAction)
249{
250 // Warning parent can be 0L
251
252 if (qobject_cast<QMenu *>(object: container)) {
253 if (parent) {
254 parent->removeAction(action: containerAction);
255 }
256
257 delete container;
258 } else if (qobject_cast<KToolBar *>(object: container)) {
259 KToolBar *tb = static_cast<KToolBar *>(container);
260
261 tb->saveState(element);
262 if (tb->mainWindow()) {
263 delete tb;
264 } else {
265 tb->clear();
266 tb->hide();
267 }
268 } else if (qobject_cast<QMenuBar *>(object: container)) {
269 QMenuBar *mb = static_cast<QMenuBar *>(container);
270 mb->hide();
271 // Don't delete menubar - it can be reused by createContainer.
272 // If you decide that you do need to delete the menubar, make
273 // sure that QMainWindow::d->mb does not point to a deleted
274 // menubar object.
275 } else if (qobject_cast<QStatusBar *>(object: container)) {
276 if (qobject_cast<KMainWindow *>(object: d->m_widget)) {
277 container->hide();
278 } else {
279 delete static_cast<QStatusBar *>(container);
280 }
281 } else {
282 qCWarning(DEBUG_KXMLGUI) << "Unhandled container to remove : " << container->metaObject()->className();
283 }
284}
285
286QStringList KXMLGUIBuilder::customTags() const
287{
288 QStringList res;
289 res << d->tagSeparator << d->tagSpacer << d->tagTearOffHandle << d->tagMenuTitle;
290 return res;
291}
292
293QAction *KXMLGUIBuilder::createCustomElement(QWidget *parent, int index, const QDomElement &element)
294{
295 QAction *before = nullptr;
296 if (index > 0 && index < parent->actions().count()) {
297 before = parent->actions().at(i: index);
298 }
299
300 const QString tagName = element.tagName().toLower();
301 if (tagName == d->tagSeparator) {
302 if (QMenu *menu = qobject_cast<QMenu *>(object: parent)) {
303 // QMenu already cares for leading/trailing/repeated separators
304 // no need to check anything
305 return menu->insertSeparator(before);
306 } else if (QMenuBar *bar = qobject_cast<QMenuBar *>(object: parent)) {
307 QAction *separatorAction = new QAction(bar);
308 separatorAction->setSeparator(true);
309 bar->insertAction(before, action: separatorAction);
310 return separatorAction;
311 } else if (KToolBar *bar = qobject_cast<KToolBar *>(object: parent)) {
312 /* FIXME KAction port - any need to provide a replacement for lineSeparator/normal separator?
313 bool isLineSep = true;
314
315 QDomNamedNodeMap attributes = element.attributes();
316 unsigned int i = 0;
317 for (; i < attributes.length(); i++ )
318 {
319 QDomAttr attr = attributes.item( i ).toAttr();
320
321 if ( attr.name().toLower() == d->attrLineSeparator &&
322 attr.value().toLower() == QLatin1String("false") )
323 {
324 isLineSep = false;
325 break;
326 }
327 }
328
329 if ( isLineSep )
330 return bar->insertSeparator( index ? bar->actions()[index - 1] : 0L );
331 else*/
332
333 return bar->insertSeparator(before);
334 }
335 } else if (tagName == d->tagSpacer) {
336 if (QToolBar *bar = qobject_cast<QToolBar *>(object: parent)) {
337 // Create the simple spacer widget
338 QWidget *spacer = new QWidget(parent);
339 spacer->setSizePolicy(hor: QSizePolicy::MinimumExpanding, ver: QSizePolicy::MinimumExpanding);
340 return bar->insertWidget(before, widget: spacer);
341 }
342 } else if (tagName == d->tagTearOffHandle) {
343 static_cast<QMenu *>(parent)->setTearOffEnabled(true);
344 } else if (tagName == d->tagMenuTitle) {
345 if (QMenu *m = qobject_cast<QMenu *>(object: parent)) {
346 QString i18nText;
347 const QString text = element.text();
348
349 if (text.isEmpty()) {
350 i18nText = i18n("No text");
351 } else {
352 QByteArray domain = element.attribute(name: d->attrDomain).toUtf8();
353 if (domain.isEmpty()) {
354 domain = element.ownerDocument().documentElement().attribute(name: d->attrDomain).toUtf8();
355 if (domain.isEmpty()) {
356 domain = KLocalizedString::applicationDomain();
357 }
358 }
359 i18nText = i18nd(domain: domain.constData(), qPrintable(text));
360 }
361
362 QString icon = element.attribute(name: d->attrIcon);
363 QIcon pix;
364
365 if (!icon.isEmpty()) {
366 pix = QIcon::fromTheme(name: icon);
367 }
368
369 if (!icon.isEmpty()) {
370 return m->insertSection(before, icon: pix, text: i18nText);
371 } else {
372 return m->insertSection(before, text: i18nText);
373 }
374 }
375 }
376
377 QAction *blank = new QAction(parent);
378 blank->setVisible(false);
379 parent->insertAction(before, action: blank);
380 return blank;
381}
382
383KXMLGUIClient *KXMLGUIBuilder::builderClient() const
384{
385 return d->m_client;
386}
387
388void KXMLGUIBuilder::setBuilderClient(KXMLGUIClient *client)
389{
390 d->m_client = client;
391}
392
393void KXMLGUIBuilder::finalizeGUI(KXMLGUIClient *)
394{
395 KXmlGuiWindow *window = qobject_cast<KXmlGuiWindow *>(object: d->m_widget);
396 if (window) {
397 window->finalizeGUI(force: false);
398 }
399}
400
401void KXMLGUIBuilder::virtual_hook(int, void *)
402{
403 /*BASE::virtual_hook( id, data );*/
404}
405

source code of kxmlgui/src/kxmlguibuilder.cpp