KXmlGui

kxmlguibuilder.cpp
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 *>(d->m_widget); // could be 0
126 return mainwindow;
127 }
128
129 if (tagName == d->tagMenuBar) {
130 KMainWindow *mainWin = qobject_cast<KMainWindow *>(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 // Look up to see if we are inside a mainwindow. If yes, then
144 // use it as parent widget (to get kaction to plug itself into the
145 // mainwindow). Don't use a popupmenu as parent widget, otherwise
146 // the popup won't be hidden if it is used as a standalone menu as well.
147 // Note: menus with a parent of 0, coming from child clients, can be
148 // leaked if the child client is deleted without a proper removeClient call, though.
149 QWidget *p = parent;
150
151 if (!p && qobject_cast<QMainWindow *>(d->m_widget)) {
152 p = d->m_widget;
153 }
154
155 while (p && !qobject_cast<QMainWindow *>(p)) {
156 p = p->parentWidget();
157 }
158
159 QString name = element.attribute(d->attrName);
160
161 if (!KAuthorized::authorizeAction(name)) {
162 return nullptr;
163 }
164
165 QMenu *popup = new QMenu(p);
166 popup->setObjectName(name);
167
168 d->m_menumenuhandler->insertMenu(popup);
169
170 QString i18nText;
171 QDomElement textElem = element.namedItem(d->attrText1).toElement();
172 if (textElem.isNull()) { // try with capital T
173 textElem = element.namedItem(d->attrText2).toElement();
174 }
175 const QString text = textElem.text();
176 const QString context = textElem.attribute(d->attrContext);
177
178 // qCDebug(DEBUG_KXMLGUI) << "DOMAIN" << KLocalizedString::applicationDomain();
179 // qCDebug(DEBUG_KXMLGUI) << "ELEMENT TEXT:" << text;
180
181 if (text.isEmpty()) { // still no luck
182 i18nText = i18n("No text");
183 } else {
184 QByteArray domain = textElem.attribute(d->attrDomain).toUtf8();
185 if (domain.isEmpty()) {
186 domain = element.ownerDocument().documentElement().attribute(d->attrDomain).toUtf8();
187 if (domain.isEmpty()) {
189 }
190 }
191 if (context.isEmpty()) {
192 i18nText = i18nd(domain.constData(), text.toUtf8().constData());
193 } else {
194 i18nText = i18ndc(domain.constData(), context.toUtf8().constData(), text.toUtf8().constData());
195 }
196 }
197
198 // qCDebug(DEBUG_KXMLGUI) << "ELEMENT i18n TEXT:" << i18nText;
199
200 const QString icon = element.attribute(d->attrIcon);
201 QIcon pix;
202 if (!icon.isEmpty()) {
203 pix = QIcon::fromTheme(icon);
204 }
205
206 if (parent) {
207 QAction *act = popup->menuAction();
208 if (!icon.isEmpty()) {
209 act->setIcon(pix);
210 }
211 act->setText(i18nText);
212 if (index == -1 || index >= parent->actions().count()) {
213 parent->addAction(act);
214 } else {
215 parent->insertAction(parent->actions().value(index), act);
216 }
217 containerAction = act;
218 containerAction->setObjectName(name);
219 }
220
221 return popup;
222 }
223
224 if (tagName == d->tagToolBar) {
225 QString name = element.attribute(d->attrName);
226
227 KToolBar *bar = static_cast<KToolBar *>(d->m_widget->findChild<KToolBar *>(name));
228 if (!bar) {
229 bar = new KToolBar(name, d->m_widget, false);
230 }
231
232 if (qobject_cast<KMainWindow *>(d->m_widget)) {
233 if (d->m_client && !d->m_client->xmlFile().isEmpty()) {
234 bar->addXMLGUIClient(d->m_client);
235 }
236 }
237 if (!bar->mainWindow()) {
238 bar->show();
239 }
240
241 bar->loadState(element);
242
243 return bar;
244 }
245
246 if (tagName == d->tagStatusBar) {
247 KMainWindow *mainWin = qobject_cast<KMainWindow *>(d->m_widget);
248 if (mainWin) {
249 mainWin->statusBar()->show();
250 return mainWin->statusBar();
251 }
252 QStatusBar *bar = new QStatusBar(d->m_widget);
253 return bar;
254 }
255
256 return nullptr;
257}
258
259void KXMLGUIBuilder::removeContainer(QWidget *container, QWidget *parent, QDomElement &element, QAction *containerAction)
260{
261 // Warning parent can be 0L
262
263 if (qobject_cast<QMenu *>(container)) {
264 if (parent) {
265 parent->removeAction(containerAction);
266 }
267
268 delete container;
269 } else if (qobject_cast<KToolBar *>(container)) {
270 KToolBar *tb = static_cast<KToolBar *>(container);
271
272 tb->saveState(element);
273 if (tb->mainWindow()) {
274 delete tb;
275 } else {
276 tb->clear();
277 tb->hide();
278 }
279 } else if (qobject_cast<QMenuBar *>(container)) {
280 QMenuBar *mb = static_cast<QMenuBar *>(container);
281 mb->hide();
282 // Don't delete menubar - it can be reused by createContainer.
283 // If you decide that you do need to delete the menubar, make
284 // sure that QMainWindow::d->mb does not point to a deleted
285 // menubar object.
286 } else if (qobject_cast<QStatusBar *>(container)) {
287 if (qobject_cast<KMainWindow *>(d->m_widget)) {
288 container->hide();
289 } else {
290 delete static_cast<QStatusBar *>(container);
291 }
292 } else {
293 qCWarning(DEBUG_KXMLGUI) << "Unhandled container to remove : " << container->metaObject()->className();
294 }
295}
296
297QStringList KXMLGUIBuilder::customTags() const
298{
299 QStringList res;
300 res << d->tagSeparator << d->tagSpacer << d->tagTearOffHandle << d->tagMenuTitle;
301 return res;
302}
303
304QAction *KXMLGUIBuilder::createCustomElement(QWidget *parent, int index, const QDomElement &element)
305{
306 QAction *before = nullptr;
307 if (index > 0 && index < parent->actions().count()) {
308 before = parent->actions().at(index);
309 }
310
311 const QString tagName = element.tagName().toLower();
312 if (tagName == d->tagSeparator) {
313 if (QMenu *menu = qobject_cast<QMenu *>(parent)) {
314 // QMenu already cares for leading/trailing/repeated separators
315 // no need to check anything
316 return menu->insertSeparator(before);
317 } else if (QMenuBar *bar = qobject_cast<QMenuBar *>(parent)) {
318 QAction *separatorAction = new QAction(bar);
319 separatorAction->setSeparator(true);
320 bar->insertAction(before, separatorAction);
321 return separatorAction;
322 } else if (KToolBar *bar = qobject_cast<KToolBar *>(parent)) {
323 /* FIXME KAction port - any need to provide a replacement for lineSeparator/normal separator?
324 bool isLineSep = true;
325
326 QDomNamedNodeMap attributes = element.attributes();
327 unsigned int i = 0;
328 for (; i < attributes.length(); i++ )
329 {
330 QDomAttr attr = attributes.item( i ).toAttr();
331
332 if ( attr.name().toLower() == d->attrLineSeparator &&
333 attr.value().toLower() == QLatin1String("false") )
334 {
335 isLineSep = false;
336 break;
337 }
338 }
339
340 if ( isLineSep )
341 return bar->insertSeparator( index ? bar->actions()[index - 1] : 0L );
342 else*/
343
344 return bar->insertSeparator(before);
345 }
346 } else if (tagName == d->tagSpacer) {
347 if (QToolBar *bar = qobject_cast<QToolBar *>(parent)) {
348 // Create the simple spacer widget
349 QWidget *spacer = new QWidget(parent);
351 return bar->insertWidget(before, spacer);
352 }
353 } else if (tagName == d->tagTearOffHandle) {
354 static_cast<QMenu *>(parent)->setTearOffEnabled(true);
355 } else if (tagName == d->tagMenuTitle) {
356 if (QMenu *m = qobject_cast<QMenu *>(parent)) {
357 QString i18nText;
358 const QString text = element.text();
359
360 if (text.isEmpty()) {
361 i18nText = i18n("No text");
362 } else {
363 QByteArray domain = element.attribute(d->attrDomain).toUtf8();
364 if (domain.isEmpty()) {
365 domain = element.ownerDocument().documentElement().attribute(d->attrDomain).toUtf8();
366 if (domain.isEmpty()) {
368 }
369 }
370 i18nText = i18nd(domain.constData(), qPrintable(text));
371 }
372
373 QString icon = element.attribute(d->attrIcon);
374 QIcon pix;
375
376 if (!icon.isEmpty()) {
377 pix = QIcon::fromTheme(icon);
378 }
379
380 if (!icon.isEmpty()) {
381 return m->insertSection(before, pix, i18nText);
382 } else {
383 return m->insertSection(before, i18nText);
384 }
385 }
386 }
387
388 QAction *blank = new QAction(parent);
389 blank->setVisible(false);
390 parent->insertAction(before, blank);
391 return blank;
392}
393
394KXMLGUIClient *KXMLGUIBuilder::builderClient() const
395{
396 return d->m_client;
397}
398
399void KXMLGUIBuilder::setBuilderClient(KXMLGUIClient *client)
400{
401 d->m_client = client;
402}
403
404void KXMLGUIBuilder::finalizeGUI(KXMLGUIClient *)
405{
406 KXmlGuiWindow *window = qobject_cast<KXmlGuiWindow *>(d->m_widget);
407 if (window) {
408 window->finalizeGUI(false);
409 }
410}
411
412void KXMLGUIBuilder::virtual_hook(int, void *)
413{
414 /*BASE::virtual_hook( id, data );*/
415}
static Q_INVOKABLE bool authorizeAction(const QString &action)
static QByteArray applicationDomain()
KMainWindow represents a top-level main window.
Definition kmainwindow.h:60
Floatable toolbar with auto resize.
Definition ktoolbar.h:68
void addXMLGUIClient(KXMLGUIClient *client)
Adds an XML gui client that uses this toolbar.
Definition ktoolbar.cpp:942
KMainWindow * mainWindow() const
Returns the main window that this toolbar is docked with.
void loadState(const QDomElement &element)
Load state from an XML.
Definition ktoolbar.cpp:969
void saveState(QDomElement &element) const
Save state into an XML.
virtual QWidget * createContainer(QWidget *parent, int index, const QDomElement &element, QAction *&containerAction)
Creates a container (menubar/menu/toolbar/statusbar/separator/...) from an element in the XML file.
virtual void removeContainer(QWidget *container, QWidget *parent, QDomElement &element, QAction *containerAction)
Removes the given (and previously via createContainer ) created container.
A KXMLGUIClient can be used with KXMLGUIFactory to create a GUI from actions and an XML document,...
KMainWindow with convenience functions and integration with XmlGui files.
QString i18ndc(const char *domain, const char *context, const char *text, const TYPE &arg...)
QString i18nd(const char *domain, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QWidget * window(QObject *job)
void setIcon(const QIcon &icon)
void setSeparator(bool b)
void setText(const QString &text)
void setVisible(bool)
const char * constData() const const
bool isEmpty() const const
QDomElement documentElement() const const
QString attribute(const QString &name, const QString &defValue) const const
QString tagName() const const
QString text() const const
bool isNull() const const
QDomNode namedItem(const QString &name) const const
QDomDocument ownerDocument() const const
QDomElement toElement() const const
QIcon fromTheme(const QString &name)
const_reference at(qsizetype i) const const
qsizetype count() const const
T value(qsizetype i) const const
QMenuBar * menuBar() const const
QStatusBar * statusBar() const const
QAction * menuAction() const const
const char * className() const const
virtual const QMetaObject * metaObject() const const
void setObjectName(QAnyStringView name)
bool isEmpty() const const
QString toLower() const const
QByteArray toUtf8() const const
void clear()
QList< QAction * > actions() const const
QAction * addAction(const QIcon &icon, const QString &text)
void hide()
void insertAction(QAction *before, QAction *action)
QWidget * parentWidget() const const
void removeAction(QAction *action)
void show()
void setSizePolicy(QSizePolicy)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:21:12 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.