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 | |
28 | using namespace KDEPrivate; |
29 | |
30 | class KXMLGUIBuilderPrivate |
31 | { |
32 | public: |
33 | KXMLGUIBuilderPrivate() |
34 | { |
35 | } |
36 | ~KXMLGUIBuilderPrivate() |
37 | { |
38 | } |
39 | |
40 | QWidget *m_widget = nullptr; |
41 | |
42 | QString tagMainWindow; |
43 | QString ; |
44 | QString ; |
45 | QString tagToolBar; |
46 | QString tagStatusBar; |
47 | |
48 | QString tagSeparator; |
49 | QString tagSpacer; |
50 | QString tagTearOffHandle; |
51 | QString ; |
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 | |
68 | KXMLGUIBuilder::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 | |
97 | KXMLGUIBuilder::~KXMLGUIBuilder() |
98 | { |
99 | delete d->m_menumenuhandler; |
100 | } |
101 | |
102 | QWidget *KXMLGUIBuilder::widget() |
103 | { |
104 | return d->m_widget; |
105 | } |
106 | |
107 | QStringList 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 | |
115 | QWidget *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 * = 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 | |
248 | void 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 | |
286 | QStringList KXMLGUIBuilder::customTags() const |
287 | { |
288 | QStringList res; |
289 | res << d->tagSeparator << d->tagSpacer << d->tagTearOffHandle << d->tagMenuTitle; |
290 | return res; |
291 | } |
292 | |
293 | QAction *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 * = 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 | |
383 | KXMLGUIClient *KXMLGUIBuilder::builderClient() const |
384 | { |
385 | return d->m_client; |
386 | } |
387 | |
388 | void KXMLGUIBuilder::setBuilderClient(KXMLGUIClient *client) |
389 | { |
390 | d->m_client = client; |
391 | } |
392 | |
393 | void 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 | |
401 | void KXMLGUIBuilder::virtual_hook(int, void *) |
402 | { |
403 | /*BASE::virtual_hook( id, data );*/ |
404 | } |
405 | |