/* -*- 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 #if defined(DISABLE_DYNLOADING) || defined(LINUX) #include #endif static bool toBool(const OString &rValue) { return (!rValue.isEmpty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1')); } namespace { OUString mapStockToImageResource(const OUString& sType) { if (sType == "gtk-index") return SV_RESID_BITMAP_INDEX; else if (sType == "gtk-refresh") return SV_RESID_BITMAP_REFRESH; else if (sType == "gtk-apply") return IMG_APPLY; else if (sType == "gtk-dialog-error") return IMG_ERROR; else if (sType == "gtk-add") return IMG_ADD; else if (sType == "gtk-remove") return IMG_REMOVE; else if (sType == "gtk-copy") return IMG_COPY; else if (sType == "gtk-paste") return IMG_PASTE; return OUString(); } SymbolType mapStockToSymbol(const OUString& sType) { SymbolType eRet = SymbolType::DONTKNOW; if (sType == "gtk-media-next") eRet = SymbolType::NEXT; else if (sType == "gtk-media-previous") eRet = SymbolType::PREV; else if (sType == "gtk-media-play") eRet = SymbolType::PLAY; else if (sType == "gtk-media-stop") eRet = SymbolType::STOP; else if (sType == "gtk-goto-first") eRet = SymbolType::FIRST; else if (sType == "gtk-goto-last") eRet = SymbolType::LAST; else if (sType == "gtk-go-back") eRet = SymbolType::ARROW_LEFT; else if (sType == "gtk-go-forward") eRet = SymbolType::ARROW_RIGHT; else if (sType == "gtk-go-up") eRet = SymbolType::ARROW_UP; else if (sType == "gtk-go-down") eRet = SymbolType::ARROW_DOWN; else if (sType == "gtk-missing-image") eRet = SymbolType::IMAGE; else if (sType == "gtk-help") eRet = SymbolType::HELP; else if (sType == "gtk-close") eRet = SymbolType::CLOSE; else if (!mapStockToImageResource(sType).isEmpty()) eRet = SymbolType::IMAGE; return eRet; } void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference& rFrame); } #if defined SAL_LOG_WARN namespace { 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 weld::Builder* Application::CreateBuilder(weld::Widget* pParent, const OUString &rUIFile, bool bMobile) { bool bUseJSBuilder = false; if (bMobile) { if (rUIFile == "modules/swriter/ui/wordcount-mobile.ui") bUseJSBuilder = true; } if (bUseJSBuilder) return new JSInstanceBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile); else return ImplGetSVData()->mpDefInst->CreateBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile); } weld::Builder* Application::CreateInterimBuilder(vcl::Window* pParent, const OUString &rUIFile) { return ImplGetSVData()->mpDefInst->CreateInterimBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile); } weld::MessageDialog* Application::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString& rPrimaryMessage) { 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() { int 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; } int SpinButton::denormalize(int nValue) const { const int nFactor = Power10(get_digits()); if ((nValue < (SAL_MIN_INT32 + nFactor)) || (nValue > (SAL_MAX_INT32 - nFactor))) { return nValue / nFactor; } const int nHalf = nFactor / 2; if (nValue < 0) return (nValue - nHalf) / nFactor; return (nValue + nHalf) / nFactor; } OUString MetricSpinButton::format_number(int 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"; if (aSuffix != "\"" && aSuffix != sDoublePrime) aStr += " "; else aSuffix = sDoublePrime; } else if (m_eSrcUnit == FieldUnit::FOOT) { OUString sPrime = u"\u2032"; if (aSuffix != "'" && aSuffix != sPrime) aStr += " "; else aSuffix = sPrime; } assert(m_eSrcUnit != FieldUnit::PERCENT); aStr += aSuffix; } return aStr; } void MetricSpinButton::set_digits(unsigned int digits) { int step, page; get_increments(step, page, m_eSrcUnit); int 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) { int step, page; get_increments(step, page, m_eSrcUnit); int 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(); } } int MetricSpinButton::ConvertValue(int nValue, FieldUnit eInUnit, FieldUnit eOutUnit) const { auto nRet = vcl::ConvertValue(nValue, 0, m_xSpinButton->get_digits(), eInUnit, eOutUnit); if (nRet > SAL_MAX_INT32) nRet = SAL_MAX_INT32; else if (nRet < SAL_MIN_INT32) nRet = SAL_MIN_INT32; return nRet; } 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; } IMPL_LINK_NOARG(TimeSpinButton, spin_button_cursor_position, Entry&, void) { int nStartPos, nEndPos; m_xSpinButton->get_selection_bounds(nStartPos, nEndPos); const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); const int nTimeArea = TimeFormatter::GetTimeArea(m_eFormat, m_xSpinButton->get_text(), nEndPos, rLocaleData); int nIncrements = 1; if (nTimeArea == 1) nIncrements = 1000 * 60 * 60; else if (nTimeArea == 2) nIncrements = 1000 * 60; else if (nTimeArea == 3) nIncrements = 1000; m_xSpinButton->set_increments(nIncrements, nIncrements * 10); } IMPL_LINK_NOARG(TimeSpinButton, spin_button_value_changed, SpinButton&, void) { signal_value_changed(); } IMPL_LINK(TimeSpinButton, spin_button_output, SpinButton&, rSpinButton, void) { int nStartPos, nEndPos; rSpinButton.get_selection_bounds(nStartPos, nEndPos); rSpinButton.set_text(format_number(rSpinButton.get_value())); rSpinButton.set_position(nEndPos); } IMPL_LINK(TimeSpinButton, spin_button_input, int*, result, bool) { int nStartPos, nEndPos; m_xSpinButton->get_selection_bounds(nStartPos, nEndPos); const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); tools::Time aResult(0); bool bRet = TimeFormatter::TextToTime(m_xSpinButton->get_text(), aResult, m_eFormat, true, rLocaleData); if (bRet) *result = ConvertValue(aResult); return bRet; } void TimeSpinButton::update_width_chars() { int 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); } tools::Time TimeSpinButton::ConvertValue(int nValue) { tools::Time aTime(0); aTime.MakeTimeFromMS(nValue); return aTime; } int TimeSpinButton::ConvertValue(const tools::Time& rTime) { return rTime.GetMSFromTime(); } OUString TimeSpinButton::format_number(int nValue) const { const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); return TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, TimeFormat::Hour24, true, rLocaleData); } EntryTreeView::EntryTreeView(std::unique_ptr xEntry, std::unique_ptr xTreeView) : m_xEntry(std::move(xEntry)) , m_xTreeView(std::move(xTreeView)) { m_xTreeView->connect_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; } } VclBuilder::VclBuilder(vcl::Window* pParent, const OUString& sUIDir, const OUString& sUIFile, const OString& sID, const css::uno::Reference& rFrame, bool bLegacy, const NotebookBarAddonsItem* pNotebookBarAddonsItem) : m_pNotebookBarAddonsItem(pNotebookBarAddonsItem ? new NotebookBarAddonsItem(*pNotebookBarAddonsItem) : new NotebookBarAddonsItem{}) , m_sID(sID) , m_sHelpRoot(OUStringToOString(sUIFile, RTL_TEXTENCODING_UTF8)) , m_pStringReplace(Translate::GetReadStringHook()) , m_pParent(pParent) , m_bToplevelParentFound(false) , m_bLegacy(bLegacy) , m_pParserState(new ParserState) , m_xFrame(rFrame) { m_bToplevelHasDeferredInit = pParent && ((pParent->IsSystemWindow() && static_cast(pParent)->isDeferredInit()) || (pParent->IsDockingWindow() && static_cast(pParent)->isDeferredInit())); m_bToplevelHasDeferredProperties = m_bToplevelHasDeferredInit; sal_Int32 nIdx = m_sHelpRoot.lastIndexOf('.'); if (nIdx != -1) m_sHelpRoot = m_sHelpRoot.copy(0, nIdx); m_sHelpRoot += OString('/'); OUString sUri = sUIDir + sUIFile; try { xmlreader::XmlReader reader(sUri); handleChild(pParent, reader); } catch (const css::uno::Exception &rExcept) { DBG_UNHANDLED_EXCEPTION("vcl.builder", "Unable to read .ui file"); CrashReporter::addKeyValue("VclBuilderException", "Unable to read .ui file: " + rExcept.Message, CrashReporter::Write); throw; } //Set Mnemonic widgets when everything has been imported for (auto const& mnemonicWidget : m_pParserState->m_aMnemonicWidgetMaps) { FixedText *pOne = get(mnemonicWidget.m_sID); vcl::Window *pOther = get(mnemonicWidget.m_sValue.toUtf8()); SAL_WARN_IF(!pOne || !pOther, "vcl", "missing either source " << mnemonicWidget.m_sID << " or target " << mnemonicWidget.m_sValue << " member of Mnemonic Widget Mapping"); if (pOne && pOther) pOne->set_mnemonic_widget(pOther); } //Set a11y relations and role when everything has been imported for (auto const& elemAtk : m_pParserState->m_aAtkInfo) { vcl::Window *pSource = elemAtk.first; const stringmap &rMap = elemAtk.second; for (auto const& elemMap : rMap) { const OString &rType = elemMap.first; const OUString &rParam = elemMap.second; if (rType == "role") { sal_Int16 role = BuilderUtils::getRoleFromName(rParam.toUtf8()); if (role != com::sun::star::accessibility::AccessibleRole::UNKNOWN) pSource->SetAccessibleRole(role); } else { vcl::Window *pTarget = get(rParam.toUtf8()); 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 if (rType == "member-of") pSource->SetAccessibleRelationMemberOf(pTarget); else { SAL_WARN("vcl.builder", "unhandled a11y relation :" << rType); } } } } //Set radiobutton groups when everything has been imported for (auto const& elem : m_pParserState->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 (m_bLegacy) pOne->group(*pOther); else { pOther->group(*pOne); std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this)); } } } //Set ComboBox models when everything has been imported for (auto const& elem : m_pParserState->m_aModelMaps) { 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.toUtf8()); SAL_WARN_IF(!pListBoxTarget && !pComboBoxTarget && !pTreeBoxTarget, "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_pParserState->m_aTextBufferMaps) { VclMultiLineEdit *pTarget = get(elem.m_sID); const TextBuffer *pBuffer = get_buffer_by_name(elem.m_sValue.toUtf8()); 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_pParserState->m_aNumericFormatterAdjustmentMaps) { NumericFormatter *pTarget = dynamic_cast(get(elem.m_sID)); const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); 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_pParserState->m_aFormattedFormatterAdjustmentMaps) { FormattedField *pTarget = dynamic_cast(get(elem.m_sID)); const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); 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); } for (auto const& elem : m_pParserState->m_aTimeFormatterAdjustmentMaps) { TimeField *pTarget = dynamic_cast(get(elem.m_sID)); const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of spinbutton/adjustment"); if (pTarget && pAdjustment) mungeAdjustment(*pTarget, *pAdjustment); } for (auto const& elem : m_pParserState->m_aDateFormatterAdjustmentMaps) { DateField *pTarget = dynamic_cast(get(elem.m_sID)); const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of spinbutton/adjustment"); if (pTarget && pAdjustment) mungeAdjustment(*pTarget, *pAdjustment); } //Set ScrollBar adjustments when everything has been imported for (auto const& elem : m_pParserState->m_aScrollAdjustmentMaps) { ScrollBar *pTarget = get(elem.m_sID); const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); 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_pParserState->m_aSliderAdjustmentMaps) { Slider* pTarget = dynamic_cast(get(elem.m_sID)); const Adjustment* pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); 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 : m_pParserState->m_aSizeGroups) { std::shared_ptr xGroup(std::make_shared()); for (auto const& elem : sizeGroup.m_aProperties) { const OString &rKey = elem.first; const OUString &rValue = elem.second; xGroup->set_property(rKey, rValue); } for (auto const& elem : sizeGroup.m_aWidgets) { vcl::Window* pWindow = get(elem.getStr()); pWindow->add_to_size_group(xGroup); } } //Set button images when everything has been imported std::set aImagesToBeRemoved; for (auto const& elem : m_pParserState->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.toUtf8()); SAL_WARN_IF(!pTarget || !pImage, "vcl", "missing elements of button/image/stock"); if (!pTarget || !pImage) continue; aImagesToBeRemoved.insert(elem.m_sValue); VclBuilder::StockMap::iterator aFind = m_pParserState->m_aStockMap.find(elem.m_sValue.toUtf8()); if (aFind == m_pParserState->m_aStockMap.end()) { if (!elem.m_bRadio) { pTargetButton->SetModeImage(pImage->GetImage()); if (pImage->GetStyle() & WB_SMALLSTYLE) { pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE); Size aSz(pTargetButton->GetModeImage().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()); } else { const stockinfo &rImageInfo = aFind->second; SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock); SAL_WARN_IF(eType == SymbolType::DONTKNOW, "vcl", "missing stock image element for button"); if (eType == SymbolType::DONTKNOW) continue; if (!elem.m_bRadio) { pTargetButton->SetSymbol(eType); //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 if (eType != SymbolType::IMAGE) pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE); } else SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl.builder", "unimplemented symbol type for radiobuttons"); if (eType == SymbolType::IMAGE) { Image const aImage(StockImage::Yes, mapStockToImageResource(rImageInfo.m_sStock)); if (!elem.m_bRadio) pTargetButton->SetModeImage(aImage); else pTargetRadio->SetModeRadioImage(aImage); } switch (rImageInfo.m_nSize) { case 1: pTarget->SetSmallSymbol(); break; case 3: // 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 " << rImageInfo.m_nSize); break; } } } //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.toUtf8()); } //fill in any stock icons in surviving images for (auto const& elem : m_pParserState->m_aStockMap) { FixedImage *pImage = get(elem.first); SAL_WARN_IF(!pImage, "vcl", "missing elements of image/stock: " << elem.first); if (!pImage) continue; const stockinfo &rImageInfo = elem.second; if (rImageInfo.m_sStock == "gtk-missing-image") continue; SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock); SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl", "unimplemented symbol type for images"); if (eType != SymbolType::IMAGE) continue; Image const aImage(StockImage::Yes, mapStockToImageResource(rImageInfo.m_sStock)); pImage->SetImage(aImage); } //Set button menus when everything has been imported for (auto const& elem : m_pParserState->m_aButtonMenuMaps) { MenuButton *pTarget = get(elem.m_sID); PopupMenu *pMenu = get_menu(elem.m_sValue.toUtf8()); 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_pParserState->m_aRedundantParentWidgets) { delete_by_window(elem.first); } //fdo#67378 merge the label into the disclosure button for (auto const& elem : m_pParserState->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()); delete_by_window(pLabel); } } // create message dialog message area now for (auto const& elem : m_pParserState->m_aMessageDialogs) elem->create_message_area(); //drop maps, etc. that we don't need again m_pParserState.reset(); 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("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 { bool extractDrawValue(VclBuilder::stringmap& rMap) { bool bDrawValue = true; VclBuilder::stringmap::iterator aFind = rMap.find("draw_value"); if (aFind != rMap.end()) { bDrawValue = toBool(aFind->second); rMap.erase(aFind); } return bDrawValue; } OUString extractPopupMenu(VclBuilder::stringmap& rMap) { OUString sRet; VclBuilder::stringmap::iterator aFind = rMap.find("popup"); if (aFind != rMap.end()) { sRet = aFind->second; rMap.erase(aFind); } return sRet; } OUString extractValuePos(VclBuilder::stringmap& rMap) { OUString sRet("top"); VclBuilder::stringmap::iterator aFind = rMap.find("value_pos"); if (aFind != rMap.end()) { sRet = aFind->second; rMap.erase(aFind); } return sRet; } OUString extractTypeHint(VclBuilder::stringmap &rMap) { OUString sRet("normal"); VclBuilder::stringmap::iterator aFind = rMap.find("type-hint"); if (aFind != rMap.end()) { sRet = aFind->second; rMap.erase(aFind); } return sRet; } bool extractResizable(VclBuilder::stringmap &rMap) { bool bResizable = true; VclBuilder::stringmap::iterator aFind = rMap.find("resizable"); if (aFind != rMap.end()) { bResizable = toBool(aFind->second); rMap.erase(aFind); } return bResizable; } #if HAVE_FEATURE_DESKTOP bool extractModal(VclBuilder::stringmap &rMap) { bool bModal = false; VclBuilder::stringmap::iterator aFind = rMap.find("modal"); if (aFind != rMap.end()) { bModal = toBool(aFind->second); rMap.erase(aFind); } return bModal; } #endif bool extractDecorated(VclBuilder::stringmap &rMap) { bool bDecorated = true; VclBuilder::stringmap::iterator aFind = rMap.find("decorated"); if (aFind != rMap.end()) { bDecorated = toBool(aFind->second); rMap.erase(aFind); } return bDecorated; } bool extractCloseable(VclBuilder::stringmap &rMap) { bool bCloseable = true; VclBuilder::stringmap::iterator aFind = rMap.find("deletable"); if (aFind != rMap.end()) { bCloseable = toBool(aFind->second); rMap.erase(aFind); } return bCloseable; } bool extractEntry(VclBuilder::stringmap &rMap) { bool bHasEntry = false; VclBuilder::stringmap::iterator aFind = rMap.find("has-entry"); if (aFind != rMap.end()) { bHasEntry = toBool(aFind->second); rMap.erase(aFind); } return bHasEntry; } bool extractOrientation(VclBuilder::stringmap &rMap) { bool bVertical = false; VclBuilder::stringmap::iterator aFind = rMap.find("orientation"); if (aFind != rMap.end()) { bVertical = aFind->second.equalsIgnoreAsciiCase("vertical"); rMap.erase(aFind); } return bVertical; } bool extractVerticalTabPos(VclBuilder::stringmap &rMap) { bool bVertical = false; VclBuilder::stringmap::iterator aFind = rMap.find("tab-pos"); if (aFind != rMap.end()) { bVertical = aFind->second.equalsIgnoreAsciiCase("left") || aFind->second.equalsIgnoreAsciiCase("right"); rMap.erase(aFind); } return bVertical; } bool extractInconsistent(VclBuilder::stringmap &rMap) { bool bInconsistent = false; VclBuilder::stringmap::iterator aFind = rMap.find("inconsistent"); if (aFind != rMap.end()) { bInconsistent = toBool(aFind->second); rMap.erase(aFind); } return bInconsistent; } OUString extractIconName(VclBuilder::stringmap &rMap) { OUString sIconName; VclBuilder::stringmap::iterator aFind = rMap.find(OString("icon-name")); if (aFind != rMap.end()) { sIconName = aFind->second; rMap.erase(aFind); } return sIconName; } OUString extractStockId(VclBuilder::stringmap &rMap) { OUString sIconName; VclBuilder::stringmap::iterator aFind = rMap.find(OString("stock-id")); if (aFind != rMap.end()) { sIconName = aFind->second; rMap.erase(aFind); } return sIconName; } OUString getStockText(const OUString &rType) { if (rType == "gtk-ok") return VclResId(SV_BUTTONTEXT_OK); else if (rType == "gtk-cancel") return VclResId(SV_BUTTONTEXT_CANCEL); else if (rType == "gtk-help") return VclResId(SV_BUTTONTEXT_HELP); else if (rType == "gtk-close") return VclResId(SV_BUTTONTEXT_CLOSE); else if (rType == "gtk-revert-to-saved") return VclResId(SV_BUTTONTEXT_RESET); else if (rType == "gtk-add") return VclResId(SV_BUTTONTEXT_ADD); else if (rType == "gtk-delete") return VclResId(SV_BUTTONTEXT_DELETE); else if (rType == "gtk-remove") return VclResId(SV_BUTTONTEXT_REMOVE); else if (rType == "gtk-new") return VclResId(SV_BUTTONTEXT_NEW); else if (rType == "gtk-edit") return VclResId(SV_BUTTONTEXT_EDIT); else if (rType == "gtk-apply") return VclResId(SV_BUTTONTEXT_APPLY); else if (rType == "gtk-save") return VclResId(SV_BUTTONTEXT_SAVE); else if (rType == "gtk-open") return VclResId(SV_BUTTONTEXT_OPEN); else if (rType == "gtk-undo") return VclResId(SV_BUTTONTEXT_UNDO); else if (rType == "gtk-paste") return VclResId(SV_BUTTONTEXT_PASTE); else if (rType == "gtk-media-next") return VclResId(SV_BUTTONTEXT_NEXT); else if (rType == "gtk-media-previous") return VclResId(SV_BUTTONTEXT_PREV); else if (rType == "gtk-go-up") return VclResId(SV_BUTTONTEXT_GO_UP); else if (rType == "gtk-go-down") return VclResId(SV_BUTTONTEXT_GO_DOWN); else if (rType == "gtk-clear") return VclResId(SV_BUTTONTEXT_CLEAR); else if (rType == "gtk-media-play") return VclResId(SV_BUTTONTEXT_PLAY); else if (rType == "gtk-find") return VclResId(SV_BUTTONTEXT_FIND); else if (rType == "gtk-stop") return VclResId(SV_BUTTONTEXT_STOP); else if (rType == "gtk-connect") return VclResId(SV_BUTTONTEXT_CONNECT); else if (rType == "gtk-yes") return VclResId(SV_BUTTONTEXT_YES); else if (rType == "gtk-no") return VclResId(SV_BUTTONTEXT_NO); SAL_WARN("vcl.builder", "unknown stock type: " << rType); return OUString(); } bool extractStock(VclBuilder::stringmap &rMap) { bool bIsStock = false; VclBuilder::stringmap::iterator aFind = rMap.find(OString("use-stock")); if (aFind != rMap.end()) { bIsStock = toBool(aFind->second); rMap.erase(aFind); } return bIsStock; } WinBits extractRelief(VclBuilder::stringmap &rMap) { WinBits nBits = WB_3DLOOK; VclBuilder::stringmap::iterator aFind = rMap.find(OString("relief")); if (aFind != rMap.end()) { if (aFind->second == "half") nBits = WB_FLATBUTTON | WB_BEVELBUTTON; else if (aFind->second == "none") nBits = WB_FLATBUTTON; rMap.erase(aFind); } return nBits; } OUString extractLabel(VclBuilder::stringmap &rMap) { OUString sType; VclBuilder::stringmap::iterator aFind = rMap.find(OString("label")); if (aFind != rMap.end()) { sType = aFind->second; rMap.erase(aFind); } return sType; } OUString extractActionName(VclBuilder::stringmap &rMap) { OUString sActionName; VclBuilder::stringmap::iterator aFind = rMap.find(OString("action-name")); if (aFind != rMap.end()) { sActionName = aFind->second; rMap.erase(aFind); } return sActionName; } bool extractVisible(VclBuilder::stringmap &rMap) { bool bRet = false; VclBuilder::stringmap::iterator aFind = rMap.find(OString("visible")); if (aFind != rMap.end()) { bRet = toBool(aFind->second); rMap.erase(aFind); } return bRet; } Size extractSizeRequest(VclBuilder::stringmap &rMap) { OUString sWidthRequest("0"); OUString sHeightRequest("0"); VclBuilder::stringmap::iterator aFind = rMap.find(OString("width-request")); if (aFind != rMap.end()) { sWidthRequest = aFind->second; rMap.erase(aFind); } aFind = rMap.find("height-request"); if (aFind != rMap.end()) { sHeightRequest = aFind->second; rMap.erase(aFind); } return Size(sWidthRequest.toInt32(), sHeightRequest.toInt32()); } OUString extractTooltipText(VclBuilder::stringmap &rMap) { OUString sTooltipText; VclBuilder::stringmap::iterator aFind = rMap.find(OString("tooltip-text")); if (aFind == rMap.end()) aFind = rMap.find(OString("tooltip-markup")); if (aFind != rMap.end()) { sTooltipText = aFind->second; rMap.erase(aFind); } return sTooltipText; } float extractAlignment(VclBuilder::stringmap &rMap) { float f = 0.0; VclBuilder::stringmap::iterator aFind = rMap.find(OString("alignment")); if (aFind != rMap.end()) { f = aFind->second.toFloat(); rMap.erase(aFind); } return f; } OUString extractTitle(VclBuilder::stringmap &rMap) { OUString sTitle; VclBuilder::stringmap::iterator aFind = rMap.find(OString("title")); if (aFind != rMap.end()) { sTitle = aFind->second; rMap.erase(aFind); } return sTitle; } bool extractHeadersVisible(VclBuilder::stringmap &rMap) { bool bHeadersVisible = true; VclBuilder::stringmap::iterator aFind = rMap.find(OString("headers-visible")); if (aFind != rMap.end()) { bHeadersVisible = toBool(aFind->second); rMap.erase(aFind); } return bHeadersVisible; } bool extractSortIndicator(VclBuilder::stringmap &rMap) { bool bSortIndicator = false; VclBuilder::stringmap::iterator aFind = rMap.find(OString("sort-indicator")); if (aFind != rMap.end()) { bSortIndicator = toBool(aFind->second); rMap.erase(aFind); } return bSortIndicator; } bool extractClickable(VclBuilder::stringmap &rMap) { bool bClickable = false; VclBuilder::stringmap::iterator aFind = rMap.find(OString("clickable")); if (aFind != rMap.end()) { bClickable = toBool(aFind->second); rMap.erase(aFind); } return bClickable; } 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); } VclPtr