KXmlGui

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

KDE's Doxygen guidelines are available online.