Mark some Calc slots as inactive in readonly mode
[LibreOffice.git] / vcl / qt5 / QtBuilder.cxx
blob253049625844748d86ea84410c61179b662646ad
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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/.
8 */
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>
45 namespace
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;
74 return nullptr;
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)
84 QVariant aUserData;
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);
93 return;
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);
108 rProps.clear();
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
117 if (sName.empty())
118 return nullptr;
120 // nothing to do for these
121 if (sName == u"GtkCellRendererPixbuf" || sName == u"GtkCellRendererText"
122 || sName == u"GtkTreeSelection")
123 return nullptr;
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");
145 else
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);
157 if (bVertical)
158 pObject = new QVBoxLayout(pBoxParentWidget);
159 else
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;
176 else
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);
194 else
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));
207 pObject = pComboBox;
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);
224 pObject = pLineEdit;
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);
244 pObject = pListView;
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));
255 pObject = pLabel;
257 else if (sName == u"GtkLabel")
259 QLabel* pLabel = new QLabel(pParentWidget);
260 setLabelProperties(*pLabel, rMap);
261 extractMnemonicWidget(sID, rMap);
262 pObject = pLabel;
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]));
279 pObject = pLabel;
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);
305 pObject = pWidget;
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);
321 pObject = pFrame;
323 else if (sName == u"GtkScale")
325 QSlider* pSlider = new QSlider(pParentWidget);
326 setScaleProperties(*pSlider, rMap);
327 pObject = pSlider;
329 else if (sName == u"GtkSpinButton")
331 QtDoubleSpinBox* pSpinBox = new QtDoubleSpinBox(pParentWidget);
332 setSpinButtonProperties(*pSpinBox, rMap);
333 pObject = pSpinBox;
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));
349 pObject = pTreeView;
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
362 return pTreeView;
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);
371 else
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);
379 if (!pWidget)
380 pWidget = pLayoutParentWidget;
382 if (QSplitter* pSplitterParent = qobject_cast<QSplitter*>(pParentWidget))
384 assert(pWidget);
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
392 assert(pWidget);
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");
410 if (pWidget)
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();
416 if (!pParentLayout)
417 pParentLayout = new QVBoxLayout(pParentWidget);
420 // add widget to parent layout
421 if (pParentLayout)
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));
432 #endif
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);
443 if (pWidget)
444 m_aChildren.emplace_back(sID, pWidget);
446 return pObject;
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
456 // delete it
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);
493 else
495 // set as the scroll area's widget
496 QWidget* pCurrentWidget = nullptr;
497 if (pCurrentChild->isWidgetType())
498 pCurrentWidget = static_cast<QWidget*>(pCurrentChild);
499 else
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)
525 assert(pButton);
526 QObject::connect(pButton, &QAbstractButton::clicked, pDialog,
527 [pDialog, pButton] {
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)
542 return;
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*>();
560 else
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);
583 if (!pTabWidget)
584 return false;
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));
595 return pMenu;
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");
602 (void)pSubMenu;
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);
613 else
615 assert(false && "Not implemented yet");
619 void QtBuilder::applyAtkProperties(QObject* pObject, const stringmap& rProperties, bool)
621 if (!pObject || !pObject->isWidgetType())
622 return;
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))
644 return;
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)
662 if (!pCurrentChild)
663 return;
665 QWidget* pWidget = nullptr;
666 if (pCurrentChild->isWidgetType())
667 pWidget = static_cast<QWidget*>(pCurrentChild);
668 else
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);
676 if (!pWidget)
677 return;
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);
682 else
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);
702 assert(pPushButton);
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();
716 assert(pParent);
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);
729 else
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)
743 if (rKey == u"text")
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);
765 else
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()));
809 #else
810 pButton->setIcon(QIcon(pImageLabel->pixmap(Qt::ReturnByValue)));
811 #endif
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();
898 return nullptr;
901 QDialogButtonBox* QtBuilder::findButtonBox(QDialog* pDialog)
903 assert(pDialog);
904 QLayout* pLayout = pDialog->layout();
905 if (!pLayout)
906 return nullptr;
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))
914 return pButtonBox;
917 return nullptr;
920 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */