1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <QtBuilder.hxx>
12 #include <QtDoubleSpinBox.hxx>
13 #include <QtExpander.hxx>
14 #include <QtInstanceLinkButton.hxx>
15 #include <QtInstanceMenu.hxx>
16 #include <QtInstanceMessageDialog.hxx>
17 #include <QtInstanceNotebook.hxx>
18 #include <QtTools.hxx>
20 #include <rtl/ustrbuf.hxx>
21 #include <vcl/qt/QtUtils.hxx>
23 #include <QtGui/QStandardItemModel>
24 #include <QtWidgets/QButtonGroup>
25 #include <QtWidgets/QCheckBox>
26 #include <QtWidgets/QComboBox>
27 #include <QtWidgets/QDialog>
28 #include <QtWidgets/QDialogButtonBox>
29 #include <QtWidgets/QGroupBox>
30 #include <QtWidgets/QLabel>
31 #include <QtWidgets/QLineEdit>
32 #include <QtWidgets/QListView>
33 #include <QtWidgets/QLayout>
34 #include <QtWidgets/QPlainTextEdit>
35 #include <QtWidgets/QProgressBar>
36 #include <QtWidgets/QPushButton>
37 #include <QtWidgets/QRadioButton>
38 #include <QtWidgets/QScrollArea>
39 #include <QtWidgets/QSlider>
40 #include <QtWidgets/QSplitter>
41 #include <QtWidgets/QTabWidget>
42 #include <QtWidgets/QToolButton>
43 #include <QtWidgets/QTreeView>
47 QString
convertAccelerator(const OUString
& rText
)
49 // preserve literal '&'s and use '&' instead of '_' for the accelerator
50 return toQString(rText
.replaceAll("&", "&&").replace('_', '&'));
54 QtBuilder::QtBuilder(QObject
* pParent
, std::u16string_view sUIRoot
, const OUString
& rUIFile
)
55 : WidgetBuilder(sUIRoot
, rUIFile
, false)
57 processUIFile(pParent
);
59 // tweak widget hierarchy (remove unnecessary parent widgets)
60 for (const std::pair
<QWidget
*, QWidget
*>& rPair
: m_aWidgetReplacements
)
61 replaceWidget(rPair
.first
, rPair
.second
);
64 QtBuilder::~QtBuilder() {}
66 QWidget
* QtBuilder::get_by_name(std::u16string_view sID
)
68 for (auto const& child
: m_aChildren
)
70 if (child
.m_sID
== sID
)
71 return child
.m_pWidget
;
77 void QtBuilder::insertComboBoxOrListBoxItems(QObject
* pObject
, stringmap
& rMap
,
78 const std::vector
<ComboBoxTextItem
>& rItems
)
80 if (QComboBox
* pComboBox
= qobject_cast
<QComboBox
*>(pObject
))
82 for (const ComboBoxTextItem
& rItem
: rItems
)
85 if (!rItem
.m_sId
.isEmpty())
86 aUserData
= QVariant::fromValue(toQString(rItem
.m_sId
));
88 pComboBox
->addItem(toQString(rItem
.m_sItem
), aUserData
);
91 const int nActiveId
= BuilderBase::extractActive(rMap
);
92 pComboBox
->setCurrentIndex(nActiveId
);
96 assert(false && "list boxes are not supported yet");
99 QObject
* QtBuilder::insertObject(QObject
* pParent
, const OUString
& rClass
, std::string_view sType
,
100 const OUString
& rID
, stringmap
& rProps
, stringmap
&, stringmap
&)
102 QObject
* pCurrentChild
= nullptr;
104 pCurrentChild
= makeObject(pParent
, rClass
, sType
, rID
, rProps
);
106 setProperties(pCurrentChild
, rProps
);
110 return pCurrentChild
;
113 QObject
* QtBuilder::makeObject(QObject
* pParent
, std::u16string_view sName
, std::string_view sType
,
114 const OUString
& sID
, stringmap
& rMap
)
116 // ignore placeholders
120 // nothing to do for these
121 if (sName
== u
"GtkCellRendererPixbuf" || sName
== u
"GtkCellRendererText"
122 || sName
== u
"GtkTreeSelection")
125 QWidget
* pParentWidget
= qobject_cast
<QWidget
*>(pParent
);
126 QLayout
* pParentLayout
= qobject_cast
<QLayout
*>(pParent
);
128 QObject
* pObject
= nullptr;
129 // in case a QLayout is created, an additional QWidget parent
130 // will also be created because that is needed for QtInstanceContainer
131 QWidget
* pLayoutParentWidget
= nullptr;
133 if (sName
== u
"GtkMessageDialog")
135 pObject
= new QMessageBox(pParentWidget
);
137 else if (sName
== u
"GtkBox")
139 // for a QMessageBox, return the existing layout instead of creating a new one
140 if (QMessageBox
* pMessageBox
= qobject_cast
<QMessageBox
*>(pParent
))
142 pObject
= pMessageBox
->layout();
143 assert(pObject
&& "QMessageBox has no layout");
147 QWidget
* pBoxParentWidget
= pParentWidget
;
148 // Unless this is the direct GtkBox child of a GtkDialog, create a parent widget
149 // that can be used to create a QtInstanceContainer for this box
150 if (!qobject_cast
<QDialog
*>(pParentWidget
))
152 pLayoutParentWidget
= new QWidget(pParentWidget
);
153 pBoxParentWidget
= pLayoutParentWidget
;
156 const bool bVertical
= hasOrientationVertical(rMap
);
158 pObject
= new QVBoxLayout(pBoxParentWidget
);
160 pObject
= new QHBoxLayout(pBoxParentWidget
);
163 else if (sName
== u
"GtkButtonBox")
165 QWidget
* pTopLevel
= windowForObject(pParent
);
166 if (QMessageBox
* pMessageBox
= qobject_cast
<QMessageBox
*>(pTopLevel
))
168 // for a QMessageBox, return the existing button box instead of creating a new one
169 QDialogButtonBox
* pButtonBox
= findButtonBox(pMessageBox
);
170 assert(pButtonBox
&& "Could not find QMessageBox's button box");
171 pObject
= pButtonBox
;
173 // skip adding to layout below, button box is already contained in dialog
174 pParentLayout
= nullptr;
178 QDialogButtonBox
* pButtonBox
= new QDialogButtonBox(pParentWidget
);
179 if (hasOrientationVertical(rMap
))
180 pButtonBox
->setOrientation(Qt::Vertical
);
181 pObject
= pButtonBox
;
184 else if (sName
== u
"GtkButton")
186 if (QDialogButtonBox
* pButtonBox
= qobject_cast
<QDialogButtonBox
*>(pParentWidget
))
188 pObject
= pButtonBox
->addButton("", QDialogButtonBox::NoRole
);
190 // for message boxes, avoid implicit standard buttons in addition to those explicitly added
191 if (QMessageBox
* pMessageBox
= qobject_cast
<QMessageBox
*>(pParentWidget
->window()))
192 pMessageBox
->setStandardButtons(QMessageBox::NoButton
);
196 pObject
= new QPushButton(pParentWidget
);
199 else if (sName
== u
"GtkCheckButton")
201 pObject
= new QCheckBox(pParentWidget
);
203 else if (sName
== u
"GtkComboBoxText")
205 QComboBox
* pComboBox
= new QComboBox(pParentWidget
);
206 pComboBox
->setEditable(extractEntry(rMap
));
209 else if (sName
== u
"GtkDialog")
211 pObject
= new QDialog(pParentWidget
);
213 else if (sName
== u
"GtkDrawingArea")
215 pObject
= new QLabel(pParentWidget
);
217 else if (sName
== u
"GtkEntry")
219 QLineEdit
* pLineEdit
= new QLineEdit(pParentWidget
);
220 auto aIt
= rMap
.find(u
"visibility"_ustr
);
221 if (aIt
!= rMap
.end() && !toBool(aIt
->second
))
222 pLineEdit
->setEchoMode(QLineEdit::Password
);
226 else if (sName
== u
"GtkExpander")
228 pObject
= new QtExpander(pParentWidget
);
230 else if (sName
== u
"GtkFrame")
232 pObject
= new QGroupBox(pParentWidget
);
234 else if (sName
== u
"GtkGrid")
236 pLayoutParentWidget
= new QWidget(pParentWidget
);
237 pObject
= new QGridLayout(pLayoutParentWidget
);
239 else if (sName
== u
"GtkIconView")
241 QListView
* pListView
= new QListView(pParentWidget
);
242 pListView
->setModel(new QStandardItemModel(pListView
));
243 pListView
->setViewMode(QListView::IconMode
);
246 else if (sName
== u
"GtkImage")
248 QLabel
* pLabel
= new QLabel(pParentWidget
);
249 const OUString sIconName
= extractIconName(rMap
);
250 if (!sIconName
.isEmpty())
252 const Image aImage
= loadThemeImage(sIconName
);
253 pLabel
->setPixmap(toQPixmap(aImage
));
257 else if (sName
== u
"GtkLabel")
259 QLabel
* pLabel
= new QLabel(pParentWidget
);
260 setLabelProperties(*pLabel
, rMap
);
261 extractMnemonicWidget(sID
, rMap
);
264 else if (sName
== u
"GtkLevelBar" || sName
== u
"GtkProgressBar")
266 QProgressBar
* pProgressBar
= new QProgressBar(pParentWidget
);
267 // don't show text (progress in percent) by default
268 pProgressBar
->setTextVisible(false);
269 pObject
= pProgressBar
;
271 else if (sName
== u
"GtkLinkButton")
273 QtHyperlinkLabel
* pLabel
= new QtHyperlinkLabel(pParentWidget
);
274 if (rMap
.contains(u
"label"_ustr
))
275 pLabel
->setDisplayText(toQString(rMap
[u
"label"_ustr
]));
276 if (rMap
.contains(u
"uri"_ustr
))
277 pLabel
->setUri(toQString(rMap
[u
"uri"_ustr
]));
281 else if (sName
== u
"GtkMenuButton")
283 QToolButton
* pMenuButton
= new QToolButton(pParentWidget
);
284 const OUString sMenu
= extractPopupMenu(rMap
);
285 if (!sMenu
.isEmpty())
287 QMenu
* pMenu
= get_menu(sMenu
);
288 assert(pMenu
&& "menu button references non-existing menu");
289 pMenuButton
->setMenu(pMenu
);
291 pObject
= pMenuButton
;
293 else if (sName
== u
"GtkNotebook")
295 pObject
= new QTabWidget(pParentWidget
);
297 else if (sName
== u
"GtkPaned")
299 pObject
= new QSplitter(pParentWidget
);
301 else if (sName
== u
"GtkPopover")
303 QWidget
* pWidget
= new QWidget(pParentWidget
, Qt::Popup
);
304 pWidget
->setLayout(new QVBoxLayout
);
307 else if (sName
== u
"GtkRadioButton")
309 pObject
= new QRadioButton(pParentWidget
);
310 extractRadioButtonGroup(sID
, rMap
);
312 else if (sName
== u
"GtkScrolledWindow")
314 pObject
= new QScrollArea(pParentWidget
);
316 else if (sName
== u
"GtkSeparator")
318 const bool bVertical
= hasOrientationVertical(rMap
);
319 QFrame
* pFrame
= new QFrame(pParentWidget
);
320 pFrame
->setFrameShape(bVertical
? QFrame::VLine
: QFrame::HLine
);
323 else if (sName
== u
"GtkScale")
325 QSlider
* pSlider
= new QSlider(pParentWidget
);
326 setScaleProperties(*pSlider
, rMap
);
329 else if (sName
== u
"GtkSpinButton")
331 QtDoubleSpinBox
* pSpinBox
= new QtDoubleSpinBox(pParentWidget
);
332 setSpinButtonProperties(*pSpinBox
, rMap
);
335 else if (sName
== u
"GtkTextView")
337 pObject
= new QPlainTextEdit(pParentWidget
);
339 else if (sName
== u
"GtkToggleButton")
341 pObject
= new QToolButton(pParentWidget
);
343 else if (sName
== u
"GtkTreeView")
345 QTreeView
* pTreeView
= new QTreeView(pParentWidget
);
346 pTreeView
->setModel(new QStandardItemModel(pTreeView
));
347 pTreeView
->setHeaderHidden(!extractHeadersVisible(rMap
));
348 pTreeView
->setRootIsDecorated(extractShowExpanders(rMap
));
351 else if (sName
== u
"GtkTreeViewColumn")
353 QTreeView
* pTreeView
= qobject_cast
<QTreeView
*>(pParentWidget
);
354 assert(pTreeView
&& "Tree view column doesn't have a tree view parent");
355 QStandardItemModel
* pModel
= qobject_cast
<QStandardItemModel
*>(pTreeView
->model());
356 assert(pModel
&& "Tree view doesn't have QStandardItemModel set");
357 const int nCol
= pModel
->columnCount();
358 pModel
->insertColumn(nCol
);
359 pModel
->setHeaderData(nCol
, Qt::Horizontal
, toQString(extractTitle(rMap
)));
361 // nothing else to do, return tree view parent for the widget
364 else if (sName
== u
"GtkViewport")
366 // GtkViewport is an adaptor to make GtkWidgets scrollable
367 // inside a GtkScrolledWindow; no equivalent needed for widgets
368 // inside QScrollArea - just create a simple QWidget
369 pObject
= new QWidget(pParentWidget
);
373 SAL_WARN("vcl.qt", "Widget type not supported yet: "
374 << OUStringToOString(sName
, RTL_TEXTENCODING_UTF8
));
375 assert(false && "Widget type not supported yet");
378 QWidget
* pWidget
= qobject_cast
<QWidget
*>(pObject
);
380 pWidget
= pLayoutParentWidget
;
382 if (QSplitter
* pSplitterParent
= qobject_cast
<QSplitter
*>(pParentWidget
))
385 pSplitterParent
->addWidget(pWidget
);
386 // unset to not create a layout below
387 pParentWidget
= nullptr;
389 else if (QTabWidget
* pParentTabWidget
= qobject_cast
<QTabWidget
*>(pParentWidget
))
391 // remove QTabWidget child widget, set via QTabWidget::addTab instead
393 pWidget
->setParent(nullptr);
394 // initially, add tab with empty label, QtBuilder::applyTabChildProperties will evaluate actual one
395 pParentTabWidget
->addTab(pWidget
, QStringLiteral());
396 // unset pParentWidget to not create a layout below
397 pParentWidget
= nullptr;
399 else if (QtExpander
* pExpander
= qobject_cast
<QtExpander
*>(pParentWidget
))
401 // set the content (not the label) child as the expander's widget
402 if (sType
!= "label")
404 pExpander
->setContentWidget(pWidget
);
405 // erase "visible" property, QtExpander shows/hides the widget as needed
406 rMap
.erase("visible");
412 if (!pParentLayout
&& pParentWidget
)
414 // if the parent is a widget, use the widget's layout, and ensure it has one set
415 pParentLayout
= pParentWidget
->layout();
417 pParentLayout
= new QVBoxLayout(pParentWidget
);
420 // add widget to parent layout
422 pParentLayout
->addWidget(pWidget
);
424 QtInstanceWidget::setHelpId(*pWidget
, getHelpRoot() + sID
);
426 pWidget
->setToolTip(toQString(extractTooltipText(rMap
)));
427 pWidget
->setVisible(extractVisible(rMap
));
429 #if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
430 // Set GtkBuilder ID as accessible ID
431 pWidget
->setAccessibleIdentifier(toQString(sID
));
434 else if (QLayout
* pLayout
= qobject_cast
<QLayout
*>(pObject
))
436 // add layout to parent layout
437 if (QBoxLayout
* pParentBoxLayout
= qobject_cast
<QBoxLayout
*>(pParentLayout
))
438 pParentBoxLayout
->addLayout(pLayout
);
439 else if (QGridLayout
* pParentGridLayout
= qobject_cast
<QGridLayout
*>(pParentLayout
))
440 pParentGridLayout
->addLayout(pLayout
, pParentGridLayout
->rowCount(), 0);
444 m_aChildren
.emplace_back(sID
, pWidget
);
449 void QtBuilder::tweakInsertedChild(QObject
* pParent
, QObject
* pCurrentChild
, std::string_view sType
,
450 std::string_view sInternalChild
)
452 if (sInternalChild
== "entry" && qobject_cast
<QComboBox
*>(pParent
))
454 // an editable GtkComboBox has an internal GtkEntry child,
455 // but QComboBox doesn't need a separate widget for it, so
457 deleteObject(pCurrentChild
);
460 if (sType
== "label")
462 if (QLabel
* pLabel
= qobject_cast
<QLabel
*>(pCurrentChild
))
464 if (QGroupBox
* pGroupBox
= qobject_cast
<QGroupBox
*>(pParent
))
466 // GtkFrame has a `child-type="label"` child for the GtkFrame label
467 // in the GtkBuilder .ui file, s. https://docs.gtk.org/gtk3/class.Frame.html
468 // For QGroupBox, the title can be set directly. Therefore, take over the
469 // title from the label and delete the separate label widget again
470 pGroupBox
->setTitle(pLabel
->text());
471 deleteObject(pLabel
);
473 else if (QtExpander
* pExpander
= qobject_cast
<QtExpander
*>(pParent
))
475 // GtkExpander has a `child-type="label"` child for the expander label
476 // in the GtkBuilder .ui file, s. https://docs.gtk.org/gtk3/class.Expander.html
477 // For QtExpander, the (button) text can be set directly. Therefore, take over
478 // text from the label and delete the separate label widget again
479 pExpander
->setText(pLabel
->text());
480 deleteObject(pLabel
);
485 if (QScrollArea
* pScrollAreaParent
= qobject_cast
<QScrollArea
*>(pParent
))
487 if (QAbstractScrollArea
* pScrollArea
= qobject_cast
<QAbstractScrollArea
*>(pCurrentChild
))
489 // if the child provides scrolling capabilities itself, it doesn't need
490 // another scroll area parent -> mark parent scroll area for removal
491 m_aWidgetReplacements
.emplace_back(pScrollAreaParent
, pScrollArea
);
495 // set as the scroll area's widget
496 QWidget
* pCurrentWidget
= nullptr;
497 if (pCurrentChild
->isWidgetType())
498 pCurrentWidget
= static_cast<QWidget
*>(pCurrentChild
);
500 pCurrentWidget
= static_cast<QLayout
*>(pCurrentChild
)->parentWidget();
501 assert(pCurrentWidget
);
502 pScrollAreaParent
->setWidget(pCurrentWidget
);
506 if (QDialog
* pDialog
= qobject_cast
<QDialog
*>(pCurrentChild
))
508 // no action needed for QMessageBox, where the default button box is used
509 // and button click is handled in QtInstanceMessageDialog
510 if (!qobject_cast
<QMessageBox
*>(pDialog
))
512 if (QDialogButtonBox
* pButtonBox
= findButtonBox(pDialog
))
514 // ensure that button box is the last item in QDialog's layout
515 // (that seems to be implicitly the case for GtkDialog in GTK)
516 QLayout
* pLayout
= pDialog
->layout();
517 assert(pLayout
&& "dialog has no layout");
518 pLayout
->removeWidget(pButtonBox
);
519 pLayout
->addWidget(pButtonBox
);
521 // connect button click handler
522 const QList
<QAbstractButton
*> aButtons
= pButtonBox
->buttons();
523 for (QAbstractButton
* pButton
: aButtons
)
526 QObject::connect(pButton
, &QAbstractButton::clicked
, pDialog
,
528 QtInstanceDialog::handleButtonClick(*pDialog
, *pButton
);
536 void QtBuilder::setMnemonicWidget(const OUString
& rLabelId
, const OUString
& rMnemonicWidgetId
)
538 QLabel
* pLabel
= get
<QLabel
>(rLabelId
);
539 QWidget
* pBuddy
= get_by_name(rMnemonicWidgetId
);
541 if (!pLabel
|| !pBuddy
)
544 pLabel
->setBuddy(pBuddy
);
547 void QtBuilder::setRadioButtonGroup(const OUString
& rRadioButtonId
, const OUString
& rRadioGroupId
)
549 // insert all buttons into a button group owned by button whose matches the group's
550 QRadioButton
* pGroupOwner
= get
<QRadioButton
>(rRadioGroupId
);
551 assert(pGroupOwner
&& "No radio button with the given group name");
553 QButtonGroup
* pButtonGroup
= nullptr;
554 static const char* const pPropertyKey
= "PROPERTY_BUTTONGROUP";
555 QVariant aVariant
= pGroupOwner
->property(pPropertyKey
);
556 if (aVariant
.canConvert
<QButtonGroup
*>())
558 pButtonGroup
= aVariant
.value
<QButtonGroup
*>();
562 pButtonGroup
= new QButtonGroup(pGroupOwner
);
563 pButtonGroup
->addButton(pGroupOwner
);
566 QRadioButton
* pRadioButton
= get
<QRadioButton
>(rRadioButtonId
);
567 assert(pRadioButton
&& "No radio button with given ID");
568 pButtonGroup
->addButton(pRadioButton
);
570 pGroupOwner
->setProperty(pPropertyKey
, QVariant::fromValue(pButtonGroup
));
573 void QtBuilder::setPriority(QObject
*, int) { SAL_WARN("vcl.qt", "Ignoring priority"); }
575 void QtBuilder::setContext(QObject
*, std::vector
<vcl::EnumContext::Context
>&&)
577 SAL_WARN("vcl.qt", "Ignoring context");
580 bool QtBuilder::isHorizontalTabControl(QObject
* pObject
)
582 QTabWidget
* pTabWidget
= qobject_cast
<QTabWidget
*>(pObject
);
586 const QTabWidget::TabPosition ePosition
= pTabWidget
->tabPosition();
587 return ePosition
== QTabWidget::TabPosition::North
588 || ePosition
== QTabWidget::TabPosition::South
;
591 QMenu
* QtBuilder::createMenu(const OUString
& rId
)
593 QMenu
* pMenu
= new QMenu
;
594 pMenu
->setObjectName(toQString(rId
));
598 void QtBuilder::insertMenuObject(QMenu
* pParent
, QMenu
* pSubMenu
, const OUString
& rClass
,
599 const OUString
& rID
, stringmap
& rProps
, stringmap
&, accelmap
&)
601 assert(!pSubMenu
&& "Handling not implemented yet");
604 if (rClass
== "GtkMenuItem")
606 const OUString sLabel
= extractLabel(rProps
);
607 QAction
* pAction
= pParent
->addAction(toQString(sLabel
));
608 pAction
->setObjectName(toQString(rID
));
610 const OUString
sActionName(extractActionName(rProps
));
611 QtInstanceMenu::setActionName(*pAction
, sActionName
);
615 assert(false && "Not implemented yet");
619 void QtBuilder::applyAtkProperties(QObject
* pObject
, const stringmap
& rProperties
, bool)
621 if (!pObject
|| !pObject
->isWidgetType())
624 QWidget
* pWidget
= static_cast<QWidget
*>(pObject
);
626 for (auto const & [ rKey
, rValue
] : rProperties
)
628 if (rKey
== "AtkObject::accessible-description")
629 pWidget
->setAccessibleDescription(toQString(rValue
));
630 else if (rKey
== "AtkObject::accessible-name")
631 pWidget
->setAccessibleName(toQString(rValue
));
635 void QtBuilder::applyGridPackingProperties(QWidget
* pCurrentChild
, QGridLayout
& rGrid
,
636 const stringmap
& rPackingProperties
)
638 assert(pCurrentChild
);
640 // properties not set when there's no explicit GtkGrid in the .ui file,
641 // like for the QGridLayout that's the (implicit) layout of a QMessageBox
642 if (!rPackingProperties
.contains(u
"left-attach"_ustr
)
643 || !rPackingProperties
.contains(u
"top-attach"_ustr
))
646 const sal_Int32 nColumn
= rPackingProperties
.at(u
"left-attach"_ustr
).toInt32();
647 const sal_Int32 nRow
= rPackingProperties
.at(u
"top-attach"_ustr
).toInt32();
649 auto aWidthIt
= rPackingProperties
.find(u
"width"_ustr
);
650 sal_Int32 nColumnSpan
= (aWidthIt
== rPackingProperties
.end()) ? 1 : aWidthIt
->second
.toInt32();
652 auto aHeightIt
= rPackingProperties
.find(u
"height"_ustr
);
653 sal_Int32 nRowSpan
= (aHeightIt
== rPackingProperties
.end()) ? 1 : aHeightIt
->second
.toInt32();
655 rGrid
.removeWidget(pCurrentChild
);
656 rGrid
.addWidget(pCurrentChild
, nRow
, nColumn
, nRowSpan
, nColumnSpan
);
659 void QtBuilder::applyPackingProperties(QObject
* pCurrentChild
, QObject
* pParent
,
660 const stringmap
& rPackingProperties
)
665 QWidget
* pWidget
= nullptr;
666 if (pCurrentChild
->isWidgetType())
667 pWidget
= static_cast<QWidget
*>(pCurrentChild
);
670 QObject
* pParentObject
= pCurrentChild
->parent();
671 assert(pParent
&& "Non-widget (i.e. layout) has no parent");
672 if (pParentObject
->isWidgetType())
673 pWidget
= static_cast<QWidget
*>(pParentObject
);
679 // check parent's parent, due to extra QWidget parents for layouts
680 if (QGridLayout
* pGrid
= qobject_cast
<QGridLayout
*>(pParent
))
681 applyGridPackingProperties(pWidget
, *pGrid
, rPackingProperties
);
683 SAL_WARN("vcl.qt", "QtBuilder::applyPackingProperties not yet implemented for this case");
686 void QtBuilder::applyTabChildProperties(QObject
* pParent
, const std::vector
<OUString
>& rIDs
,
687 std::vector
<vcl::EnumContext::Context
>&,
688 stringmap
& rProperties
, stringmap
&)
690 QTabWidget
* pTabWidget
= qobject_cast
<QTabWidget
*>(pParent
);
691 assert(pTabWidget
&& "parent must be a QTabWidget");
693 // set ID and label for the last inserted tab
694 assert(rProperties
.contains(u
"label"_ustr
) && "Tab has no label");
695 QtInstanceNotebook::setTabIdAndLabel(*pTabWidget
, pTabWidget
->count() - 1, rIDs
.front(),
696 rProperties
.at(u
"label"_ustr
));
699 void QtBuilder::set_response(std::u16string_view sID
, short nResponse
)
701 QPushButton
* pPushButton
= get
<QPushButton
>(sID
);
703 pPushButton
->setProperty(QtInstanceMessageDialog::PROPERTY_VCL_RESPONSE_CODE
, int(nResponse
));
706 void QtBuilder::deleteObject(QObject
* pObject
)
708 if (pObject
->isWidgetType())
709 static_cast<QWidget
*>(pObject
)->hide();
710 pObject
->deleteLater();
713 void QtBuilder::replaceWidget(QWidget
* pOldWidget
, QWidget
* pNewWidget
)
715 QWidget
* pParent
= pOldWidget
->parentWidget();
718 // replace old with new widget and mark old widget for deletion
719 if (QLayout
* pParentLayout
= pParent
->layout())
721 std::unique_ptr
<QLayoutItem
> pOldItem(pParentLayout
->replaceWidget(pOldWidget
, pNewWidget
));
723 else if (QSplitter
* pSplitter
= qobject_cast
<QSplitter
*>(pParent
))
725 const int nIndex
= pSplitter
->indexOf(pOldWidget
);
726 assert(nIndex
>= 0 && "old widget not a child of the splitter");
727 pSplitter
->replaceWidget(nIndex
, pNewWidget
);
731 assert(false && "Unhandled case in replaceWidget - not implemented (yet)");
734 deleteObject(pOldWidget
);
737 void QtBuilder::setProperties(QObject
* pObject
, stringmap
& rProps
)
739 if (QMessageBox
* pMessageBox
= qobject_cast
<QMessageBox
*>(pObject
))
741 for (auto const & [ rKey
, rValue
] : rProps
)
745 pMessageBox
->setText(toQString(rValue
));
747 else if (rKey
== u
"title")
749 pMessageBox
->setWindowTitle(toQString(rValue
));
751 else if (rKey
== u
"secondary-text")
753 pMessageBox
->setInformativeText(toQString(rValue
));
755 else if (rKey
== u
"message-type")
757 if (rValue
== u
"error")
758 pMessageBox
->setIcon(QMessageBox::Critical
);
759 else if (rValue
== u
"info")
760 pMessageBox
->setIcon(QMessageBox::Information
);
761 else if (rValue
== u
"question")
762 pMessageBox
->setIcon(QMessageBox::Question
);
763 else if (rValue
== u
"warning")
764 pMessageBox
->setIcon(QMessageBox::Warning
);
766 assert(false && "Unhandled message-type");
770 else if (qobject_cast
<QCheckBox
*>(pObject
) || qobject_cast
<QRadioButton
*>(pObject
))
772 QAbstractButton
* pButton
= static_cast<QAbstractButton
*>(pObject
);
773 for (auto const & [ rKey
, rValue
] : rProps
)
775 if (rKey
== u
"active")
776 pButton
->setChecked(toBool(rValue
));
777 else if (rKey
== u
"label")
778 pButton
->setText(convertAccelerator(rValue
));
781 else if (QDialog
* pDialog
= qobject_cast
<QDialog
*>(pObject
))
783 for (auto const & [ rKey
, rValue
] : rProps
)
785 if (rKey
== u
"modal")
786 pDialog
->setModal(toBool(rValue
));
787 else if (rKey
== u
"title")
788 pDialog
->setWindowTitle(toQString(rValue
));
791 else if (QPlainTextEdit
* pTextEdit
= qobject_cast
<QPlainTextEdit
*>(pObject
))
793 for (auto const & [ rKey
, rValue
] : rProps
)
795 if (rKey
== u
"accepts-tab")
796 pTextEdit
->setTabChangesFocus(!toBool(rValue
));
799 else if (QPushButton
* pButton
= qobject_cast
<QPushButton
*>(pObject
))
801 for (auto const & [ rKey
, rValue
] : rProps
)
803 if (rKey
== u
"image")
805 QLabel
* pImageLabel
= get
<QLabel
>(rValue
);
806 assert(pImageLabel
&& "Button has non-existent image set");
807 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
808 pButton
->setIcon(QIcon(pImageLabel
->pixmap()));
810 pButton
->setIcon(QIcon(pImageLabel
->pixmap(Qt::ReturnByValue
)));
812 // parentless GtkImage in .ui file is only used for setting button
813 // image, so the object is no longer needed after doing so
814 if (!pImageLabel
->parent())
815 deleteObject(pImageLabel
);
817 else if (rKey
== u
"label")
819 pButton
->setText(convertAccelerator(rValue
));
825 void QtBuilder::setLabelProperties(QLabel
& rLabel
, stringmap
& rProps
)
827 for (auto const & [ rKey
, rValue
] : rProps
)
829 if (rKey
== u
"label")
830 rLabel
.setText(convertAccelerator(rValue
));
831 else if (rKey
== u
"wrap")
832 rLabel
.setWordWrap(toBool(rValue
));
836 void QtBuilder::setScaleProperties(QSlider
& rSlider
, stringmap
& rProps
)
838 if (!hasOrientationVertical(rProps
))
839 rSlider
.setOrientation(Qt::Horizontal
);
841 auto aAdjustmentIt
= rProps
.find("adjustment");
842 if (aAdjustmentIt
!= rProps
.end())
844 const Adjustment
* pAdjustment
= get_adjustment_by_name(aAdjustmentIt
->second
);
845 assert(pAdjustment
&& "referenced adjustment doesn't exist");
846 for (auto const & [ rKey
, rValue
] : *pAdjustment
)
848 if (rKey
== u
"upper")
849 rSlider
.setMaximum(rValue
.toInt32());
850 else if (rKey
== u
"lower")
851 rSlider
.setMinimum(rValue
.toInt32());
852 else if (rKey
== "value")
853 rSlider
.setValue(rValue
.toInt32());
854 else if (rKey
== "page-increment")
855 rSlider
.setPageStep(rValue
.toInt32());
856 else if (rKey
== "step-increment")
857 rSlider
.setSingleStep(rValue
.toInt32());
862 void QtBuilder::setSpinButtonProperties(QDoubleSpinBox
& rSpinBox
, stringmap
& rProps
)
864 auto aDigitsIt
= rProps
.find(u
"digits"_ustr
);
865 sal_Int32 nDigits
= (aDigitsIt
!= rProps
.end()) ? aDigitsIt
->second
.toInt32() : 0;
866 rSpinBox
.setDecimals(nDigits
);
868 auto aAdjustmentIt
= rProps
.find("adjustment");
869 if (aAdjustmentIt
!= rProps
.end())
871 const Adjustment
* pAdjustment
= get_adjustment_by_name(aAdjustmentIt
->second
);
872 assert(pAdjustment
&& "referenced adjustment doesn't exist");
873 for (auto const & [ rKey
, rValue
] : *pAdjustment
)
875 if (rKey
== u
"upper")
876 rSpinBox
.setMaximum(rValue
.toDouble());
877 else if (rKey
== u
"lower")
878 rSpinBox
.setMinimum(rValue
.toDouble());
879 else if (rKey
== "value")
880 rSpinBox
.setValue(rValue
.toDouble());
881 else if (rKey
== "step-increment")
882 rSpinBox
.setSingleStep(rValue
.toDouble());
887 QWidget
* QtBuilder::windowForObject(QObject
* pObject
)
889 if (QWidget
* pWidget
= qobject_cast
<QWidget
*>(pObject
))
890 return pWidget
->window();
892 if (QLayout
* pLayout
= qobject_cast
<QLayout
*>(pObject
))
894 if (QWidget
* pParentWidget
= pLayout
->parentWidget())
895 return pParentWidget
->window();
901 QDialogButtonBox
* QtBuilder::findButtonBox(QDialog
* pDialog
)
904 QLayout
* pLayout
= pDialog
->layout();
908 for (int i
= 0; i
< pLayout
->count(); i
++)
910 QLayoutItem
* pItem
= pLayout
->itemAt(i
);
911 if (QWidget
* pItemWidget
= pItem
->widget())
913 if (QDialogButtonBox
* pButtonBox
= qobject_cast
<QDialogButtonBox
*>(pItemWidget
))
920 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */