/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(DISABLE_DYNLOADING) || defined(LINUX) #include #endif bool toBool(std::u16string_view rValue) { return (!rValue.empty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1')); } namespace { const OUString & mapStockToImageResource(std::u16string_view sType) { if (sType == u"view-refresh") return SV_RESID_BITMAP_REFRESH; else if (sType == u"dialog-error") return IMG_ERROR; else if (sType == u"list-add") return IMG_ADD; else if (sType == u"list-remove") return IMG_REMOVE; else if (sType == u"edit-copy") return IMG_COPY; else if (sType == u"edit-paste") return IMG_PASTE; else if (sType == u"document-open") return IMG_OPEN; else if (sType == u"open-menu-symbolic") return IMG_MENU; else if (sType == u"window-close-symbolic") return SV_RESID_BITMAP_CLOSEDOC; else if (sType == u"x-office-calendar") return IMG_CALENDAR; else if (sType == u"accessories-character-map") return IMG_CHARACTER_MAP; return EMPTY_OUSTRING; } } SymbolType VclBuilder::mapStockToSymbol(std::u16string_view sType) { SymbolType eRet = SymbolType::DONTKNOW; if (sType == u"media-skip-forward") eRet = SymbolType::NEXT; else if (sType == u"media-skip-backward") eRet = SymbolType::PREV; else if (sType == u"media-playback-start") eRet = SymbolType::PLAY; else if (sType == u"media-playback-stop") eRet = SymbolType::STOP; else if (sType == u"go-first") eRet = SymbolType::FIRST; else if (sType == u"go-last") eRet = SymbolType::LAST; else if (sType == u"go-previous") eRet = SymbolType::ARROW_LEFT; else if (sType == u"go-next") eRet = SymbolType::ARROW_RIGHT; else if (sType == u"go-up") eRet = SymbolType::ARROW_UP; else if (sType == u"go-down") eRet = SymbolType::ARROW_DOWN; else if (sType == u"missing-image") eRet = SymbolType::IMAGE; else if (sType == u"help-browser" || sType == u"help-browser-symbolic") eRet = SymbolType::HELP; else if (sType == u"window-close") eRet = SymbolType::CLOSE; else if (sType == u"document-new") eRet = SymbolType::PLUS; else if (sType == u"pan-down-symbolic") eRet = SymbolType::SPIN_DOWN; else if (sType == u"pan-up-symbolic") eRet = SymbolType::SPIN_UP; else if (!mapStockToImageResource(sType).isEmpty()) eRet = SymbolType::IMAGE; return eRet; } namespace { void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference& rFrame); #if defined SAL_LOG_WARN bool isButtonType(WindowType nType) { return nType == WindowType::PUSHBUTTON || nType == WindowType::OKBUTTON || nType == WindowType::CANCELBUTTON || nType == WindowType::HELPBUTTON || nType == WindowType::IMAGEBUTTON || nType == WindowType::MENUBUTTON || nType == WindowType::MOREBUTTON || nType == WindowType::SPINBUTTON; } #endif } std::unique_ptr Application::CreateBuilder(weld::Widget* pParent, const OUString &rUIFile, bool bMobile, sal_uInt64 nLOKWindowId) { if (comphelper::LibreOfficeKit::isActive()) { if (jsdialog::isBuilderEnabledForSidebar(rUIFile)) return JSInstanceBuilder::CreateSidebarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, nLOKWindowId); else if (jsdialog::isBuilderEnabledForPopup(rUIFile)) return JSInstanceBuilder::CreatePopupBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile); else if (jsdialog::isBuilderEnabled(rUIFile, bMobile)) return JSInstanceBuilder::CreateDialogBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile); } return ImplGetSVData()->mpDefInst->CreateBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile); } std::unique_ptr Application::CreateInterimBuilder(vcl::Window* pParent, const OUString &rUIFile, bool bAllowCycleFocusOut, sal_uInt64 nLOKWindowId) { if (comphelper::LibreOfficeKit::isActive()) { // Notebookbar sub controls if (jsdialog::isInterimBuilderEnabledForNotebookbar(rUIFile)) return JSInstanceBuilder::CreateNotebookbarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, css::uno::Reference(), nLOKWindowId); else if (rUIFile == u"modules/scalc/ui/inputbar.ui") return JSInstanceBuilder::CreateFormulabarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, nLOKWindowId); } return ImplGetSVData()->mpDefInst->CreateInterimBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, bAllowCycleFocusOut, nLOKWindowId); } weld::MessageDialog* Application::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString& rPrimaryMessage, const ILibreOfficeKitNotifier* pNotifier) { if (comphelper::LibreOfficeKit::isActive()) return JSInstanceBuilder::CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage, pNotifier); else return ImplGetSVData()->mpDefInst->CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage); } weld::Window* Application::GetFrameWeld(const css::uno::Reference& rWindow) { return ImplGetSVData()->mpDefInst->GetFrameWeld(rWindow); } namespace weld { OUString MetricSpinButton::MetricToString(FieldUnit rUnit) { const FieldUnitStringList& rList = ImplGetFieldUnits(); // return unit's default string (ie, the first one ) auto it = std::find_if( rList.begin(), rList.end(), [&rUnit](const std::pair& rItem) { return rItem.second == rUnit; }); if (it != rList.end()) return it->first; return OUString(); } IMPL_LINK_NOARG(MetricSpinButton, spin_button_value_changed, SpinButton&, void) { signal_value_changed(); } IMPL_LINK(MetricSpinButton, spin_button_output, SpinButton&, rSpinButton, void) { OUString sNewText(format_number(rSpinButton.get_value())); if (sNewText != rSpinButton.get_text()) rSpinButton.set_text(sNewText); } void MetricSpinButton::update_width_chars() { sal_Int64 min, max; m_xSpinButton->get_range(min, max); auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(), m_xSpinButton->get_pixel_size(format_number(max)).Width()); int chars = ceil(width / m_xSpinButton->get_approximate_digit_width()); m_xSpinButton->set_width_chars(chars); } unsigned int SpinButton::Power10(unsigned int n) { unsigned int nValue = 1; for (unsigned int i = 0; i < n; ++i) nValue *= 10; return nValue; } sal_Int64 SpinButton::denormalize(sal_Int64 nValue) const { const int nFactor = Power10(get_digits()); if ((nValue < (std::numeric_limits::min() + nFactor)) || (nValue > (std::numeric_limits::max() - nFactor))) { return nValue / nFactor; } const int nHalf = nFactor / 2; if (nValue < 0) return (nValue - nHalf) / nFactor; return (nValue + nHalf) / nFactor; } OUString MetricSpinButton::format_number(sal_Int64 nValue) const { OUString aStr; const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); unsigned int nDecimalDigits = m_xSpinButton->get_digits(); //pawn percent off to icu to decide whether percent is separated from its number for this locale if (m_eSrcUnit == FieldUnit::PERCENT) { double fValue = nValue; fValue /= SpinButton::Power10(nDecimalDigits); aStr = unicode::formatPercent(fValue, rLocaleData.getLanguageTag()); } else { aStr = rLocaleData.getNum(nValue, nDecimalDigits, true, true); OUString aSuffix = MetricToString(m_eSrcUnit); if (m_eSrcUnit != FieldUnit::NONE && m_eSrcUnit != FieldUnit::DEGREE && m_eSrcUnit != FieldUnit::INCH && m_eSrcUnit != FieldUnit::FOOT) aStr += " "; if (m_eSrcUnit == FieldUnit::INCH) { OUString sDoublePrime = u"\u2033"_ustr; if (aSuffix != "\"" && aSuffix != sDoublePrime) aStr += " "; else aSuffix = sDoublePrime; } else if (m_eSrcUnit == FieldUnit::FOOT) { OUString sPrime = u"\u2032"_ustr; if (aSuffix != "'" && aSuffix != sPrime) aStr += " "; else aSuffix = sPrime; } assert(m_eSrcUnit != FieldUnit::PERCENT); aStr += aSuffix; } return aStr; } void MetricSpinButton::set_digits(unsigned int digits) { sal_Int64 step, page; get_increments(step, page, m_eSrcUnit); sal_Int64 value = get_value(m_eSrcUnit); m_xSpinButton->set_digits(digits); set_increments(step, page, m_eSrcUnit); set_value(value, m_eSrcUnit); update_width_chars(); } void MetricSpinButton::set_unit(FieldUnit eUnit) { if (eUnit != m_eSrcUnit) { sal_Int64 step, page; get_increments(step, page, m_eSrcUnit); sal_Int64 value = get_value(m_eSrcUnit); m_eSrcUnit = eUnit; set_increments(step, page, m_eSrcUnit); set_value(value, m_eSrcUnit); spin_button_output(*m_xSpinButton); update_width_chars(); } } sal_Int64 MetricSpinButton::ConvertValue(sal_Int64 nValue, FieldUnit eInUnit, FieldUnit eOutUnit) const { return vcl::ConvertValue(nValue, 0, m_xSpinButton->get_digits(), eInUnit, eOutUnit); } IMPL_LINK(MetricSpinButton, spin_button_input, int*, result, bool) { const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); double fResult(0.0); bool bRet = vcl::TextToValue(get_text(), fResult, 0, m_xSpinButton->get_digits(), rLocaleData, m_eSrcUnit); if (bRet) { if (fResult > SAL_MAX_INT32) fResult = SAL_MAX_INT32; else if (fResult < SAL_MIN_INT32) fResult = SAL_MIN_INT32; *result = fResult; } return bRet; } EntryTreeView::EntryTreeView(std::unique_ptr xEntry, std::unique_ptr xTreeView) : m_xEntry(std::move(xEntry)) , m_xTreeView(std::move(xTreeView)) { m_xTreeView->connect_selection_changed(LINK(this, EntryTreeView, ClickHdl)); m_xEntry->connect_changed(LINK(this, EntryTreeView, ModifyHdl)); } IMPL_LINK(EntryTreeView, ClickHdl, weld::TreeView&, rView, void) { m_xEntry->set_text(rView.get_selected_text()); m_aChangeHdl.Call(*this); } IMPL_LINK_NOARG(EntryTreeView, ModifyHdl, weld::Entry&, void) { m_aChangeHdl.Call(*this); } void EntryTreeView::set_height_request_by_rows(int nRows) { int nHeight = nRows == -1 ? -1 : m_xTreeView->get_height_rows(nRows); m_xTreeView->set_size_request(m_xTreeView->get_size_request().Width(), nHeight); } size_t GetAbsPos(const weld::TreeView& rTreeView, const weld::TreeIter& rIter) { size_t nAbsPos = 0; std::unique_ptr xEntry(rTreeView.make_iterator(&rIter)); if (!rTreeView.get_iter_first(*xEntry)) xEntry.reset(); while (xEntry && rTreeView.iter_compare(*xEntry, rIter) != 0) { if (!rTreeView.iter_next(*xEntry)) xEntry.reset(); nAbsPos++; } return nAbsPos; } bool IsEntryVisible(const weld::TreeView& rTreeView, const weld::TreeIter& rIter) { // short circuit for the common case if (rTreeView.get_iter_depth(rIter) == 0) return true; std::unique_ptr xEntry(rTreeView.make_iterator(&rIter)); bool bRetVal = false; do { if (rTreeView.get_iter_depth(*xEntry) == 0) { bRetVal = true; break; } } while (rTreeView.iter_parent(*xEntry) && rTreeView.get_row_expanded(*xEntry)); return bRetVal; } } // static void BuilderBase::reportException(const css::uno::Exception& rExcept) { CrashReporter::addKeyValue(u"VclBuilderException"_ustr, "Unable to read .ui file: " + rExcept.Message, CrashReporter::Write); } BuilderBase::BuilderBase(std::u16string_view sUIDir, const OUString& rUIFile, bool bLegacy) : m_pParserState(new ParserState) , m_sUIFileUrl(sUIDir + rUIFile) , m_sHelpRoot(rUIFile) , m_bLegacy(bLegacy) { const sal_Int32 nIdx = m_sHelpRoot.lastIndexOf('.'); if (nIdx != -1) m_sHelpRoot = m_sHelpRoot.copy(0, nIdx); m_sHelpRoot += "/"; } const std::locale& BuilderBase::getResLocale() const { assert(m_pParserState && "parser state no more valid"); return m_pParserState->m_aResLocale; } const std::vector& BuilderBase::getSizeGroups() const { assert(m_pParserState && "parser state no more valid"); return m_pParserState->m_aSizeGroups; } const std::vector& BuilderBase::getMnemonicWidgetMaps() const { assert(m_pParserState && "parser state no more valid"); return m_pParserState->m_aMnemonicWidgetMaps; } OUString BuilderBase::finalizeValue(const OString& rContext, const OString& rValue, const bool bTranslate) const { OUString sFinalValue; if (bTranslate) { sFinalValue = Translate::get(TranslateId{ rContext.getStr(), rValue.getStr() }, getResLocale()); } else sFinalValue = OUString::fromUtf8(rValue); if (ResHookProc pStringReplace = Translate::GetReadStringHook()) sFinalValue = (*pStringReplace)(sFinalValue); return sFinalValue; } void BuilderBase::resetParserState() { m_pParserState.reset(); } VclBuilder::VclBuilder(vcl::Window* pParent, std::u16string_view sUIDir, const OUString& sUIFile, OUString sID, css::uno::Reference xFrame, bool bLegacy, const NotebookBarAddonsItem* pNotebookBarAddonsItem) : WidgetBuilder(sUIDir, sUIFile, bLegacy) , m_pNotebookBarAddonsItem(pNotebookBarAddonsItem ? new NotebookBarAddonsItem(*pNotebookBarAddonsItem) : new NotebookBarAddonsItem{}) , m_sID(std::move(sID)) , m_pParent(pParent) , m_bToplevelParentFound(false) , m_pVclParserState(new VclParserState) , m_xFrame(std::move(xFrame)) { m_bToplevelHasDeferredInit = pParent && ((pParent->IsSystemWindow() && static_cast(pParent)->isDeferredInit()) || (pParent->IsDockingWindow() && static_cast(pParent)->isDeferredInit())); m_bToplevelHasDeferredProperties = m_bToplevelHasDeferredInit; processUIFile(pParent); //Set a11y relations and role when everything has been imported for (auto const& elemAtk : m_pVclParserState->m_aAtkInfo) { vcl::Window *pSource = elemAtk.first; const stringmap &rMap = elemAtk.second; for (auto const& [ rType, rParam ] : rMap) { if (rType == "role") { sal_Int16 role = BuilderUtils::getRoleFromName(rParam); if (role != css::accessibility::AccessibleRole::UNKNOWN) pSource->SetAccessibleRole(role); } else { vcl::Window *pTarget = get(rParam); SAL_WARN_IF(!pTarget, "vcl", "missing parameter of a11y relation: " << rParam); if (!pTarget) continue; if (rType == "labelled-by") pSource->SetAccessibleRelationLabeledBy(pTarget); else if (rType == "label-for") pSource->SetAccessibleRelationLabelFor(pTarget); else { SAL_WARN("vcl.builder", "unhandled a11y relation :" << rType); } } } } //Set radiobutton groups when everything has been imported for (auto const& elem : m_pVclParserState->m_aGroupMaps) { RadioButton *pOne = get(elem.m_sID); RadioButton *pOther = get(elem.m_sValue); SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton group"); if (pOne && pOther) { if (isLegacy()) pOne->group(*pOther); else { pOther->group(*pOne); std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this)); } } } #ifndef NDEBUG o3tl::sorted_vector models; #endif //Set ComboBox models when everything has been imported for (auto const& elem : m_pVclParserState->m_aModelMaps) { assert(models.insert(elem.m_sValue).second && "a liststore or treestore is used in duplicate widgets"); vcl::Window* pTarget = get(elem.m_sID); ListBox *pListBoxTarget = dynamic_cast(pTarget); ComboBox *pComboBoxTarget = dynamic_cast(pTarget); SvTabListBox *pTreeBoxTarget = dynamic_cast(pTarget); // pStore may be empty const ListStore *pStore = get_model_by_name(elem.m_sValue); SAL_WARN_IF(!pListBoxTarget && !pComboBoxTarget && !pTreeBoxTarget && !dynamic_cast(pTarget), "vcl", "missing elements of combobox"); if (pListBoxTarget && pStore) mungeModel(*pListBoxTarget, *pStore, elem.m_nActiveId); else if (pComboBoxTarget && pStore) mungeModel(*pComboBoxTarget, *pStore, elem.m_nActiveId); else if (pTreeBoxTarget && pStore) mungeModel(*pTreeBoxTarget, *pStore, elem.m_nActiveId); } //Set TextView buffers when everything has been imported for (auto const& elem : m_pVclParserState->m_aTextBufferMaps) { VclMultiLineEdit *pTarget = get(elem.m_sID); const TextBuffer *pBuffer = get_buffer_by_name(elem.m_sValue); SAL_WARN_IF(!pTarget || !pBuffer, "vcl", "missing elements of textview/textbuffer"); if (pTarget && pBuffer) mungeTextBuffer(*pTarget, *pBuffer); } //Set SpinButton adjustments when everything has been imported for (auto const& elem : m_pVclParserState->m_aNumericFormatterAdjustmentMaps) { NumericFormatter *pTarget = dynamic_cast(get(elem.m_sID)); const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue); SAL_WARN_IF(!pTarget, "vcl", "missing NumericFormatter element of spinbutton/adjustment"); SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment"); if (pTarget && pAdjustment) mungeAdjustment(*pTarget, *pAdjustment); } for (auto const& elem : m_pVclParserState->m_aFormattedFormatterAdjustmentMaps) { FormattedField *pTarget = dynamic_cast(get(elem.m_sID)); const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue); SAL_WARN_IF(!pTarget, "vcl", "missing FormattedField element of spinbutton/adjustment"); SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment"); if (pTarget && pAdjustment) mungeAdjustment(*pTarget, *pAdjustment); } //Set ScrollBar adjustments when everything has been imported for (auto const& elem : m_pVclParserState->m_aScrollAdjustmentMaps) { ScrollBar *pTarget = get(elem.m_sID); const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue); SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scrollbar/adjustment"); if (pTarget && pAdjustment) mungeAdjustment(*pTarget, *pAdjustment); } //Set Scale(Slider) adjustments for (auto const& elem : m_pVclParserState->m_aSliderAdjustmentMaps) { Slider* pTarget = dynamic_cast(get(elem.m_sID)); const Adjustment* pAdjustment = get_adjustment_by_name(elem.m_sValue); SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scale(slider)/adjustment"); if (pTarget && pAdjustment) { mungeAdjustment(*pTarget, *pAdjustment); } } //Set size-groups when all widgets have been imported for (auto const& sizeGroup : getSizeGroups()) { std::shared_ptr xGroup(std::make_shared()); for (auto const& [ rKey, rValue ] : sizeGroup.m_aProperties) xGroup->set_property(rKey, rValue); for (auto const& elem : sizeGroup.m_aWidgets) { vcl::Window* pWindow = get(elem); pWindow->add_to_size_group(xGroup); } } //Set button images when everything has been imported std::set aImagesToBeRemoved; for (auto const& elem : m_pVclParserState->m_aButtonImageWidgetMaps) { PushButton *pTargetButton = nullptr; RadioButton *pTargetRadio = nullptr; Button *pTarget = nullptr; if (!elem.m_bRadio) { pTargetButton = get(elem.m_sID); pTarget = pTargetButton; } else { pTargetRadio = get(elem.m_sID); pTarget = pTargetRadio; } FixedImage *pImage = get(elem.m_sValue); SAL_WARN_IF(!pTarget || !pImage, "vcl", "missing elements of button/image/stock"); if (!pTarget || !pImage) continue; aImagesToBeRemoved.insert(elem.m_sValue); if (!elem.m_bRadio) { const Image& rImage = pImage->GetImage(); SymbolType eSymbol = mapStockToSymbol(rImage.GetStock()); if (eSymbol != SymbolType::IMAGE && eSymbol != SymbolType::DONTKNOW) { pTargetButton->SetSymbol(eSymbol); //fdo#76457 keep symbol images small e.g. tools->customize->menu //but images the right size. Really the PushButton::CalcMinimumSize //and PushButton::ImplDrawPushButton are the better place to handle //this, but its such a train-wreck pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE); } else { pTargetButton->SetModeImage(rImage); if (pImage->GetStyle() & WB_SMALLSTYLE) { Size aSz(rImage.GetSizePixel()); aSz.AdjustWidth(6); aSz.AdjustHeight(6); if (pTargetButton->get_width_request() == -1) pTargetButton->set_width_request(aSz.Width()); if (pTargetButton->get_height_request() == -1) pTargetButton->set_height_request(aSz.Height()); } } } else pTargetRadio->SetModeRadioImage(pImage->GetImage()); auto aFind = m_pVclParserState->m_aImageSizeMap.find(elem.m_sValue); if (aFind != m_pVclParserState->m_aImageSizeMap.end()) { switch (aFind->second) { case 1: pTarget->SetSmallSymbol(); break; case 2: assert(pImage->GetStyle() & WB_SMALLSTYLE); pTarget->SetStyle(pTarget->GetStyle() | WB_SMALLSTYLE); break; case 3: pTarget->SetStyle(pTarget->GetStyle() | WB_SMALLSTYLE); // large toolbar, make bigger than normal (4) pTarget->set_width_request(pTarget->GetOptimalSize().Width() * 1.5); pTarget->set_height_request(pTarget->GetOptimalSize().Height() * 1.5); break; case 4: break; default: SAL_WARN("vcl.builder", "unsupported image size " << aFind->second); break; } m_pVclParserState->m_aImageSizeMap.erase(aFind); } } //There may be duplicate use of an Image, so we used a set to collect and //now we can remove them from the tree after their final munge for (auto const& elem : aImagesToBeRemoved) { delete_by_name(elem); } //Set button menus when everything has been imported for (auto const& elem : m_pVclParserState->m_aButtonMenuMaps) { MenuButton *pTarget = get(elem.m_sID); PopupMenu *pMenu = get_menu(elem.m_sValue); SAL_WARN_IF(!pTarget || !pMenu, "vcl", "missing elements of button/menu"); if (!pTarget || !pMenu) continue; pTarget->SetPopupMenu(pMenu); } //Remove ScrollWindow parent widgets whose children in vcl implement scrolling //internally. for (auto const& elem : m_pVclParserState->m_aRedundantParentWidgets) { delete_by_window(elem.first); } //fdo#67378 merge the label into the disclosure button for (auto const& elem : m_pVclParserState->m_aExpanderWidgets) { vcl::Window *pChild = elem->get_child(); vcl::Window* pLabel = elem->GetWindow(GetWindowType::LastChild); if (pLabel && pLabel != pChild && pLabel->GetType() == WindowType::FIXEDTEXT) { FixedText *pLabelWidget = static_cast(pLabel); elem->set_label(pLabelWidget->GetText()); if (pLabelWidget->IsControlFont()) elem->get_label_widget()->SetControlFont(pLabelWidget->GetControlFont()); delete_by_window(pLabel); } } // create message dialog message area now for (auto const& elem : m_pVclParserState->m_aMessageDialogs) elem->create_message_area(); //drop maps, etc. that we don't need again resetParserState(); SAL_WARN_IF(!m_sID.isEmpty() && (!m_bToplevelParentFound && !get_by_name(m_sID)), "vcl.builder", "Requested top level widget \"" << m_sID << "\" not found in " << sUIFile); #if defined SAL_LOG_WARN if (m_bToplevelParentFound && m_pParent->IsDialog()) { int nButtons = 0; bool bHasDefButton = false; for (auto const& child : m_aChildren) { if (isButtonType(child.m_pWindow->GetType())) { ++nButtons; if (child.m_pWindow->GetStyle() & WB_DEFBUTTON) { bHasDefButton = true; break; } } } SAL_WARN_IF(nButtons && !bHasDefButton, "vcl.builder", "No default button defined in " << sUIFile); } #endif const bool bHideHelp = comphelper::LibreOfficeKit::isActive() && officecfg::Office::Common::Help::HelpRootURL::get().isEmpty(); if (bHideHelp) { if (vcl::Window *pHelpButton = get(u"help")) pHelpButton->Hide(); } } VclBuilder::~VclBuilder() { disposeBuilder(); } void VclBuilder::disposeBuilder() { for (std::vector::reverse_iterator aI = m_aChildren.rbegin(), aEnd = m_aChildren.rend(); aI != aEnd; ++aI) { aI->m_pWindow.disposeAndClear(); } m_aChildren.clear(); for (std::vector::reverse_iterator aI = m_aMenus.rbegin(), aEnd = m_aMenus.rend(); aI != aEnd; ++aI) { aI->m_pMenu.disposeAndClear(); } m_aMenus.clear(); m_pParent.clear(); } namespace { inline OUString extractStringEntry(BuilderBase::stringmap& rMap, const OUString& rKey, const OUString& rDefaultValue = OUString()) { BuilderBase::stringmap::iterator aFind = rMap.find(rKey); if (aFind != rMap.end()) { const OUString sValue = aFind->second; rMap.erase(aFind); return sValue; } return rDefaultValue; } inline bool extractBoolEntry(BuilderBase::stringmap& rMap, const OUString& rKey, bool bDefaultValue) { BuilderBase::stringmap::iterator aFind = rMap.find(rKey); if (aFind != rMap.end()) { const bool bValue = toBool(aFind->second); rMap.erase(aFind); return bValue; } return bDefaultValue; } bool extractHasFrame(VclBuilder::stringmap& rMap) { return extractBoolEntry(rMap, u"has-frame"_ustr, true); } bool extractDrawValue(VclBuilder::stringmap& rMap) { return extractBoolEntry(rMap, u"draw-value"_ustr, true); } OUString extractPopupMenu(VclBuilder::stringmap& rMap) { return extractStringEntry(rMap, u"popup"_ustr); } OUString extractWidgetName(VclBuilder::stringmap& rMap) { return extractStringEntry(rMap, u"name"_ustr); } OUString extractValuePos(VclBuilder::stringmap& rMap) { return extractStringEntry(rMap,u"value-pos"_ustr, u"top"_ustr); } OUString extractTypeHint(VclBuilder::stringmap &rMap) { return extractStringEntry(rMap, u"type-hint"_ustr, u"normal"_ustr); } #if HAVE_FEATURE_DESKTOP bool extractModal(VclBuilder::stringmap &rMap) { return extractBoolEntry(rMap, u"modal"_ustr, false); } #endif bool extractDecorated(VclBuilder::stringmap &rMap) { return extractBoolEntry(rMap, u"decorated"_ustr, true); } bool extractCloseable(VclBuilder::stringmap &rMap) { return extractBoolEntry(rMap, u"deletable"_ustr, true); } bool extractVerticalTabPos(VclBuilder::stringmap &rMap) { bool bVertical = false; VclBuilder::stringmap::iterator aFind = rMap.find(u"tab-pos"_ustr); if (aFind != rMap.end()) { bVertical = aFind->second.equalsIgnoreAsciiCase("left") || aFind->second.equalsIgnoreAsciiCase("right"); rMap.erase(aFind); } return bVertical; } bool extractVerticalTabsWithIcons(VclBuilder::stringmap &rMap) { bool bWithIcons = false; VclBuilder::stringmap::iterator aFind = rMap.find(u"group-name"_ustr); if (aFind != rMap.end()) { bWithIcons = aFind->second.equalsIgnoreAsciiCase("icons"); rMap.erase(aFind); } return bWithIcons; } bool extractInconsistent(VclBuilder::stringmap &rMap) { return extractBoolEntry(rMap, u"inconsistent"_ustr, false); } WinBits extractRelief(VclBuilder::stringmap &rMap) { WinBits nBits = WB_3DLOOK; VclBuilder::stringmap::iterator aFind = rMap.find(u"relief"_ustr); if (aFind != rMap.end()) { assert(aFind->second != "half" && "relief of 'half' unsupported"); if (aFind->second == "none") nBits = WB_FLATBUTTON; rMap.erase(aFind); } return nBits; } OUString extractLabel(VclBuilder::stringmap &rMap) { return extractStringEntry(rMap, u"label"_ustr); } OUString extractActionName(VclBuilder::stringmap &rMap) { return extractStringEntry(rMap, u"action-name"_ustr); } Size extractSizeRequest(VclBuilder::stringmap &rMap) { OUString sWidthRequest(u"0"_ustr); OUString sHeightRequest(u"0"_ustr); VclBuilder::stringmap::iterator aFind = rMap.find(u"width-request"_ustr); if (aFind != rMap.end()) { sWidthRequest = aFind->second; rMap.erase(aFind); } aFind = rMap.find(u"height-request"_ustr); if (aFind != rMap.end()) { sHeightRequest = aFind->second; rMap.erase(aFind); } return Size(sWidthRequest.toInt32(), sHeightRequest.toInt32()); } float extractAlignment(VclBuilder::stringmap &rMap) { float f = 0.0; VclBuilder::stringmap::iterator aFind = rMap.find(u"alignment"_ustr); if (aFind != rMap.end()) { f = aFind->second.toFloat(); rMap.erase(aFind); } return f; } bool extractSortIndicator(VclBuilder::stringmap &rMap) { return extractBoolEntry(rMap, u"sort-indicator"_ustr, false); } bool extractClickable(VclBuilder::stringmap &rMap) { return extractBoolEntry(rMap, u"clickable"_ustr, false); } void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference& rFrame) { if (!rFrame.is()) return; OUString aCommand(extractActionName(rMap)); if (aCommand.isEmpty()) return; OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame)); auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, aModuleName); OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties)); if (!aLabel.isEmpty()) pButton->SetText(aLabel); OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(aCommand, aProperties, rFrame)); if (!aTooltip.isEmpty()) pButton->SetQuickHelpText(aTooltip); Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommand, rFrame)); pButton->SetModeImage(aImage); pButton->SetCommandHandler(aCommand, rFrame); } VclPtr