diff options
Diffstat (limited to '')
81 files changed, 36367 insertions, 0 deletions
diff --git a/svx/source/dialog/ClassificationCommon.cxx b/svx/source/dialog/ClassificationCommon.cxx new file mode 100644 index 0000000000..885dff644e --- /dev/null +++ b/svx/source/dialog/ClassificationCommon.cxx @@ -0,0 +1,125 @@ +/* -*- 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 <svx/ClassificationCommon.hxx> +#include <svx/ClassificationField.hxx> +#include <rtl/ustrbuf.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyContainer.hpp> + +using namespace css; + +namespace svx::classification +{ +OUString convertClassificationResultToString(std::vector<svx::ClassificationResult> const& rResults) +{ + OUStringBuffer sRepresentation; + + for (svx::ClassificationResult const& rResult : rResults) + { + switch (rResult.meType) + { + case svx::ClassificationType::CATEGORY: + case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART: + case svx::ClassificationType::MARKING: + case svx::ClassificationType::TEXT: + sRepresentation.append(rResult.msName); + break; + + case svx::ClassificationType::PARAGRAPH: + sRepresentation.append(" "); + break; + } + } + return sRepresentation.makeStringAndClear(); +} + +OUString getProperty(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer, + OUString const& rName) +{ + try + { + uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY); + return xPropertySet->getPropertyValue(rName).get<OUString>(); + } + catch (const css::uno::Exception&) + { + } + + return OUString(); +} + +bool containsProperty(uno::Sequence<beans::Property> const& rProperties, std::u16string_view rName) +{ + return std::any_of(rProperties.begin(), rProperties.end(), + [&](const beans::Property& rProperty) { return rProperty.Name == rName; }); +} + +void removeAllProperties(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer) +{ + uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY); + const uno::Sequence<beans::Property> aProperties + = xPropertySet->getPropertySetInfo()->getProperties(); + + for (const beans::Property& rProperty : aProperties) + { + rxPropertyContainer->removeProperty(rProperty.Name); + } +} + +bool addOrInsertDocumentProperty( + uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer, OUString const& rsKey, + OUString const& rsValue) +{ + uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY); + + try + { + if (containsProperty(xPropertySet->getPropertySetInfo()->getProperties(), rsKey)) + xPropertySet->setPropertyValue(rsKey, uno::Any(rsValue)); + else + rxPropertyContainer->addProperty(rsKey, beans::PropertyAttribute::REMOVABLE, + uno::Any(rsValue)); + } + catch (const uno::Exception& /*rException*/) + { + return false; + } + return true; +} + +void insertFullTextualRepresentationAsDocumentProperty( + uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer, + sfx::ClassificationKeyCreator const& rKeyCreator, + std::vector<svx::ClassificationResult> const& rResults) +{ + OUString sString = convertClassificationResultToString(rResults); + addOrInsertDocumentProperty(rxPropertyContainer, rKeyCreator.makeFullTextualRepresentationKey(), + sString); +} + +void insertCreationOrigin(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer, + sfx::ClassificationKeyCreator const& rKeyCreator, + sfx::ClassificationCreationOrigin eOrigin) +{ + // Nothing to do if origin is "NONE" + if (eOrigin == sfx::ClassificationCreationOrigin::NONE) + return; + + OUString sValue = (eOrigin == sfx::ClassificationCreationOrigin::BAF_POLICY) + ? OUString("BAF_POLICY") + : OUString("MANUAL"); + addOrInsertDocumentProperty(rxPropertyContainer, rKeyCreator.makeCreationOriginKey(), sValue); +} +} // end svx::classification namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/ClassificationDialog.cxx b/svx/source/dialog/ClassificationDialog.cxx new file mode 100644 index 0000000000..03f38bb2f3 --- /dev/null +++ b/svx/source/dialog/ClassificationDialog.cxx @@ -0,0 +1,694 @@ +/* -*- 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 <svx/ClassificationDialog.hxx> +#include <svx/ClassificationCommon.hxx> + +#include <editeng/flditem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/section.hxx> +#include <editeng/editobj.hxx> +#include <editeng/wghtitem.hxx> +#include <svl/itemset.hxx> +#include <osl/file.hxx> +#include <rtl/bootstrap.hxx> +#include <config_folders.h> +#include <tools/stream.hxx> +#include <tools/XmlWriter.hxx> +#include <tools/XmlWalker.hxx> +#include <utility> +#include <vcl/customweld.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <sfx2/objsh.hxx> + +#include <officecfg/Office/Common.hxx> + +#include "ClassificationEditView.hxx" + +namespace svx { + +IMPL_STATIC_LINK(ClassificationDialog, KeyInput, const KeyEvent&, rKeyEvent, bool) +{ + bool bTextIsFreeForm = officecfg::Office::Common::Classification::IntellectualPropertyTextInputIsFreeForm::get(); + + if (!bTextIsFreeForm) + { + // Ignore key combination with modifier keys + if (rKeyEvent.GetKeyCode().IsMod3() + || rKeyEvent.GetKeyCode().IsMod2() + || rKeyEvent.GetKeyCode().IsMod1()) + { + return true; + } + + switch (rKeyEvent.GetKeyCode().GetCode()) + { + // Allowed characters + case KEY_BACKSPACE: + case KEY_DELETE: + case KEY_DIVIDE: + case KEY_SEMICOLON: + case KEY_SPACE: + return false; + // Anything else is ignored + default: + return true; + } + } + + return false; +} + +namespace { + +constexpr size_t RECENTLY_USED_LIMIT = 5; + +constexpr OUString constRecentlyUsedFileName(u"recentlyUsed.xml"_ustr); + +OUString lcl_getClassificationUserPath() +{ + OUString sPath("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/user/classification/"); + rtl::Bootstrap::expandMacros(sPath); + return sPath; +} + +const SvxFieldItem* findField(editeng::Section const & rSection) +{ + for (SfxPoolItem const * pPool : rSection.maAttributes) + { + if (pPool->Which() == EE_FEATURE_FIELD) + return static_cast<const SvxFieldItem*>(pPool); + } + return nullptr; +} + +bool fileExists(OUString const & sFilename) +{ + osl::File aFile(sFilename); + osl::FileBase::RC eRC = aFile.open(osl_File_OpenFlag_Read); + return osl::FileBase::E_None == eRC; +} + +bool stringToClassificationType(std::string_view rsType, svx::ClassificationType & reType) +{ + if (rsType == "CATEGORY") + reType = svx::ClassificationType::CATEGORY; + else if (rsType == "INTELLECTUAL_PROPERTY_PART") + reType = svx::ClassificationType::INTELLECTUAL_PROPERTY_PART; + else if (rsType == "MARKING") + reType = svx::ClassificationType::MARKING; + else if (rsType == "PARAGRAPH") + reType = svx::ClassificationType::PARAGRAPH; + else if (rsType == "TEXT") + reType = svx::ClassificationType::TEXT; + else + return false; + return true; +} + +OUString classificationTypeToString(svx::ClassificationType const & reType) +{ + switch(reType) + { + case svx::ClassificationType::CATEGORY: + return "CATEGORY"; break; + case svx::ClassificationType::MARKING: + return "MARKING"; break; + case svx::ClassificationType::TEXT: + return "TEXT"; break; + case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART: + return "INTELLECTUAL_PROPERTY_PART"; break; + case svx::ClassificationType::PARAGRAPH: + return "PARAGRAPH"; break; + } + return OUString(); +} + +void writeResultToXml(tools::XmlWriter & rXmlWriter, + std::vector<ClassificationResult> const & rResultCollection) +{ + for (ClassificationResult const & rResult : rResultCollection) + { + rXmlWriter.startElement("element"); + OUString sType = classificationTypeToString(rResult.meType); + rXmlWriter.attribute("type", sType); + rXmlWriter.startElement("string"); + rXmlWriter.content(rResult.msName); + rXmlWriter.endElement(); + rXmlWriter.startElement("abbreviatedString"); + rXmlWriter.content(rResult.msAbbreviatedName); + rXmlWriter.endElement(); + rXmlWriter.startElement("identifier"); + rXmlWriter.content(rResult.msIdentifier); + rXmlWriter.endElement(); + rXmlWriter.endElement(); + } +} + +} // end anonymous namespace + +ClassificationDialog::ClassificationDialog(weld::Window* pParent, const css::uno::Reference<css::document::XDocumentProperties>& rDocProps, + const bool bPerParagraph, std::function<void()> aParagraphSignHandler) + : GenericDialogController(pParent, "svx/ui/classificationdialog.ui", "AdvancedDocumentClassificationDialog") + , maHelper(rDocProps) + , maInternationalHelper(rDocProps, /*bUseLocalizedPolicy*/ false) + , m_bPerParagraph(bPerParagraph) + , m_aParagraphSignHandler(std::move(aParagraphSignHandler)) + , m_nCurrentSelectedCategory(-1) + , m_xOkButton(m_xBuilder->weld_button("ok")) + , m_xSignButton(m_xBuilder->weld_button("signButton")) + , m_xToolBox(m_xBuilder->weld_toggle_button("toolbox")) + , m_xRecentlyUsedListBox(m_xBuilder->weld_combo_box("recentlyUsedCB")) + , m_xClassificationListBox(m_xBuilder->weld_combo_box("classificationCB")) + , m_xInternationalClassificationListBox(m_xBuilder->weld_combo_box("internationalClassificationCB")) + , m_xMarkingLabel(m_xBuilder->weld_label("markingLabel")) + , m_xMarkingListBox(m_xBuilder->weld_tree_view("markingLB")) + , m_xIntellectualPropertyPartListBox(m_xBuilder->weld_tree_view("intellectualPropertyPartLB")) + , m_xIntellectualPropertyPartNumberListBox(m_xBuilder->weld_tree_view("intellectualPropertyPartNumberLB")) + , m_xIntellectualPropertyPartAddButton(m_xBuilder->weld_button("intellectualPropertyPartAddButton")) + , m_xIntellectualPropertyPartEdit(m_xBuilder->weld_entry("intellectualPropertyPartEntry")) + , m_xIntellectualPropertyExpander(m_xBuilder->weld_expander("intellectualPropertyExpander")) + , m_xEditWindow(new ClassificationEditView) + , m_xEditWindowWeld(new weld::CustomWeld(*m_xBuilder, "classificationEditWindow", *m_xEditWindow)) +{ + m_xOkButton->connect_clicked(LINK(this, ClassificationDialog, OkHdl)); + m_xSignButton->connect_clicked(LINK(this, ClassificationDialog, ButtonClicked)); + m_xSignButton->set_visible(m_bPerParagraph); + + m_xIntellectualPropertyPartEdit->connect_key_press(LINK(this, ClassificationDialog, KeyInput)); + + // no need for BOLD if we do paragraph classification + if (m_bPerParagraph) + { + m_xToolBox->hide(); + } + else + { + m_xToolBox->connect_toggled(LINK(this, ClassificationDialog, SelectToolboxHdl)); + } + + m_xIntellectualPropertyPartAddButton->connect_clicked(LINK(this, ClassificationDialog, ButtonClicked)); + + m_xClassificationListBox->set_size_request(m_xClassificationListBox->get_approximate_digit_width() * 20, -1); + m_xClassificationListBox->connect_changed(LINK(this, ClassificationDialog, SelectClassificationHdl)); + for (const OUString& rName : maHelper.GetBACNames()) + m_xClassificationListBox->append_text(rName); + + m_xInternationalClassificationListBox->set_size_request(m_xInternationalClassificationListBox->get_approximate_digit_width() * 20, -1); + m_xInternationalClassificationListBox->connect_changed(LINK(this, ClassificationDialog, SelectClassificationHdl)); + for (const OUString& rName : maInternationalHelper.GetBACNames()) + m_xInternationalClassificationListBox->append_text(rName); + + if (!maHelper.GetMarkings().empty()) + { + m_xMarkingListBox->set_size_request(m_xMarkingListBox->get_approximate_digit_width() * 10, + m_xMarkingListBox->get_height_rows(4)); + m_xMarkingListBox->connect_row_activated(LINK(this, ClassificationDialog, SelectMarkingHdl)); + + for (const OUString& rName : maHelper.GetMarkings()) + m_xMarkingListBox->append_text(rName); + } + else + { + m_xMarkingListBox->hide(); + m_xMarkingLabel->hide(); + } + + m_xIntellectualPropertyPartNumberListBox->set_size_request(m_xIntellectualPropertyPartNumberListBox->get_approximate_digit_width() * 10, + m_xIntellectualPropertyPartNumberListBox->get_height_rows(5)); + m_xIntellectualPropertyPartNumberListBox->connect_row_activated(LINK(this, ClassificationDialog, SelectIPPartNumbersHdl)); + for (const OUString& rName : maHelper.GetIntellectualPropertyPartNumbers()) + m_xIntellectualPropertyPartNumberListBox->append_text(rName); + + m_xIntellectualPropertyPartNumberListBox->set_size_request(m_xIntellectualPropertyPartNumberListBox->get_approximate_digit_width() * 20, + m_xIntellectualPropertyPartListBox->get_height_rows(5)); + m_xIntellectualPropertyPartListBox->connect_row_activated(LINK(this, ClassificationDialog, SelectIPPartHdl)); + for (const OUString& rName : maHelper.GetIntellectualPropertyParts()) + m_xIntellectualPropertyPartListBox->append_text(rName); + + m_xRecentlyUsedListBox->set_size_request(m_xRecentlyUsedListBox->get_approximate_digit_width() * 5, -1); + m_xRecentlyUsedListBox->connect_changed(LINK(this, ClassificationDialog, SelectRecentlyUsedHdl)); + + m_xIntellectualPropertyExpander->connect_expanded(LINK(this, ClassificationDialog, ExpandedHdl)); + if (officecfg::Office::Common::Classification::IntellectualPropertySectionExpanded::get()) + m_nAsyncExpandEvent = Application::PostUserEvent(LINK(this, ClassificationDialog, OnAsyncExpandHdl)); + else + m_nAsyncExpandEvent = nullptr; + + m_xEditWindow->SetModifyHdl(LINK(this, ClassificationDialog, EditWindowModifiedHdl)); + + readRecentlyUsed(); + toggleWidgetsDependingOnCategory(); + + int nNumber = 1; + if (m_aRecentlyUsedValuesCollection.empty()) + { + m_xRecentlyUsedListBox->set_sensitive(false); + } + else + { + for (std::vector<ClassificationResult> const & rResults : m_aRecentlyUsedValuesCollection) + { + OUString rContentRepresentation = svx::classification::convertClassificationResultToString(rResults); + OUString rDescription = OUString::number(nNumber) + ": " + rContentRepresentation; + nNumber++; + + m_xRecentlyUsedListBox->append_text(rDescription); + } + } +} + +//do it async so gtk has a chance to shrink it to best size, otherwise its larger than min +IMPL_LINK_NOARG(ClassificationDialog, OnAsyncExpandHdl, void*, void) +{ + m_nAsyncExpandEvent = nullptr; + m_xIntellectualPropertyExpander->set_expanded(true); +} + +ClassificationDialog::~ClassificationDialog() +{ + if (m_nAsyncExpandEvent) + Application::RemoveUserEvent(m_nAsyncExpandEvent); +} + +void ClassificationDialog::insertCategoryField(sal_Int32 nID) +{ + const OUString aFullString = maHelper.GetBACNames()[nID]; + const OUString aAbbreviatedString = maHelper.GetAbbreviatedBACNames()[nID]; + const OUString aIdentifierString = maHelper.GetBACIdentifiers()[nID]; + insertField(ClassificationType::CATEGORY, aAbbreviatedString, aFullString, aIdentifierString); +} + +void ClassificationDialog::insertField(ClassificationType eType, OUString const & rString, OUString const & rFullString, OUString const & rIdentifier) +{ + ClassificationField aField(eType, rString, rFullString, rIdentifier); + m_xEditWindow->InsertField(SvxFieldItem(aField, EE_FEATURE_FIELD)); +} + +void ClassificationDialog::setupValues(std::vector<ClassificationResult> && rInput) +{ + m_aInitialValues = std::move(rInput); + readIn(m_aInitialValues); +} + +void ClassificationDialog::readRecentlyUsed() +{ + OUString sPath = lcl_getClassificationUserPath(); + OUString sFilePath(sPath + constRecentlyUsedFileName); + + if (!fileExists(sFilePath)) + return; + + SvFileStream aFileStream(sFilePath, StreamMode::READ); + tools::XmlWalker aWalker; + if (!aWalker.open(&aFileStream)) + return; + + if (aWalker.name() != "recentlyUsedClassifications") + return; + + aWalker.children(); + while (aWalker.isValid()) + { + if (aWalker.name() == "elementGroup") + { + std::vector<ClassificationResult> aResults; + + aWalker.children(); + + while (aWalker.isValid()) + { + if (aWalker.name() == "element") + { + svx::ClassificationType eType = svx::ClassificationType::TEXT; + OUString sString; + OUString sAbbreviatedString; + OUString sIdentifier; + + // Convert string to classification type, but continue only if + // conversion was successful. + if (stringToClassificationType(aWalker.attribute("type"_ostr), eType)) + { + aWalker.children(); + + while (aWalker.isValid()) + { + if (aWalker.name() == "string") + { + sString = OStringToOUString(aWalker.content(), RTL_TEXTENCODING_UTF8); + } + else if (aWalker.name() == "abbreviatedString") + { + sAbbreviatedString = OStringToOUString(aWalker.content(), RTL_TEXTENCODING_UTF8); + } + else if (aWalker.name() == "identifier") + { + sIdentifier = OStringToOUString(aWalker.content(), RTL_TEXTENCODING_UTF8); + } + aWalker.next(); + } + aWalker.parent(); + + aResults.push_back({ eType, sString, sAbbreviatedString, sIdentifier }); + } + } + aWalker.next(); + } + aWalker.parent(); + m_aRecentlyUsedValuesCollection.push_back(aResults); + } + aWalker.next(); + } + aWalker.parent(); +} + +void ClassificationDialog::writeRecentlyUsed() +{ + OUString sPath = lcl_getClassificationUserPath(); + osl::Directory::createPath(sPath); + OUString sFilePath(sPath + constRecentlyUsedFileName); + + std::unique_ptr<SvStream> pStream; + pStream.reset(new SvFileStream(sFilePath, StreamMode::STD_READWRITE | StreamMode::TRUNC)); + + tools::XmlWriter aXmlWriter(pStream.get()); + + if (!aXmlWriter.startDocument()) + return; + + aXmlWriter.startElement("recentlyUsedClassifications"); + + aXmlWriter.startElement("elementGroup"); + + writeResultToXml(aXmlWriter, getResult()); + + aXmlWriter.endElement(); + + if (m_aRecentlyUsedValuesCollection.size() >= RECENTLY_USED_LIMIT) + m_aRecentlyUsedValuesCollection.pop_back(); + + for (std::vector<ClassificationResult> const & rResultCollection : m_aRecentlyUsedValuesCollection) + { + aXmlWriter.startElement("elementGroup"); + + writeResultToXml(aXmlWriter, rResultCollection); + + aXmlWriter.endElement(); + } + + aXmlWriter.endElement(); + + aXmlWriter.endDocument(); +} + +void ClassificationDialog::readIn(std::vector<ClassificationResult> const & rInput) +{ + sal_Int32 nParagraph = -1; + + for (ClassificationResult const & rClassificationResult : rInput) + { + + switch (rClassificationResult.meType) + { + case svx::ClassificationType::TEXT: + { + m_xEditWindow->getEditView().InsertText(rClassificationResult.msName); + } + break; + + case svx::ClassificationType::CATEGORY: + { + OUString sName; + if (rClassificationResult.msName.isEmpty()) + sName = maHelper.GetBACNameForIdentifier(rClassificationResult.msIdentifier); + else + sName = rClassificationResult.msName; + + OUString sAbbreviatedName = rClassificationResult.msAbbreviatedName; + if (sAbbreviatedName.isEmpty()) + sAbbreviatedName = maHelper.GetAbbreviatedBACName(sName); + + m_xClassificationListBox->set_active_text(sName); + m_nCurrentSelectedCategory = m_xClassificationListBox->get_active(); + m_xInternationalClassificationListBox->set_active(m_xClassificationListBox->get_active()); + + insertField(rClassificationResult.meType, sAbbreviatedName, sName, rClassificationResult.msIdentifier); + } + break; + + case svx::ClassificationType::MARKING: + { + m_xMarkingListBox->select_text(rClassificationResult.msName); + insertField(rClassificationResult.meType, rClassificationResult.msName, rClassificationResult.msName, rClassificationResult.msIdentifier); + } + break; + + case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART: + { + insertField(rClassificationResult.meType, rClassificationResult.msName, rClassificationResult.msName, rClassificationResult.msIdentifier); + } + break; + + case svx::ClassificationType::PARAGRAPH: + { + nParagraph++; + + if (nParagraph != 0) + m_xEditWindow->getEditView().InsertParaBreak(); + + // Set paragraph font weight + FontWeight eWeight = (rClassificationResult.msName == "BOLD") ? WEIGHT_BOLD : WEIGHT_NORMAL; + + ClassificationEditEngine& rEdEngine = m_xEditWindow->getEditEngine(); + SfxItemSet aSet(rEdEngine.GetParaAttribs(nParagraph)); + aSet.Put(SvxWeightItem(eWeight, EE_CHAR_WEIGHT)); + rEdEngine.SetParaAttribs(nParagraph, aSet); + } + break; + + default: + break; + } + } + toggleWidgetsDependingOnCategory(); +} + +void ClassificationDialog::toggleWidgetsDependingOnCategory() +{ + const EditEngine& rEditEngine = m_xEditWindow->getEditEngine(); + + for (sal_Int32 nParagraph = 0; nParagraph < rEditEngine.GetParagraphCount(); ++nParagraph) + { + sal_uInt16 nFieldCount = rEditEngine.GetFieldCount(nParagraph); + for (sal_uInt16 nField = 0; nField < nFieldCount; ++nField) + { + EFieldInfo aFieldInfo = rEditEngine.GetFieldInfo(nParagraph, nField); + if (aFieldInfo.pFieldItem) + { + const ClassificationField* pClassificationField = dynamic_cast<const ClassificationField*>(aFieldInfo.pFieldItem->GetField()); + if (pClassificationField && pClassificationField->meType == ClassificationType::CATEGORY) + { + m_xOkButton->set_sensitive(true); + return; + } + } + } + } + + // Category field in the text edit has been deleted, so reset the list boxes + m_xOkButton->set_sensitive(false); + m_xClassificationListBox->set_active(-1); + m_xInternationalClassificationListBox->set_active(-1); +} + +std::vector<ClassificationResult> ClassificationDialog::getResult() +{ + std::vector<ClassificationResult> aClassificationResults; + + ClassificationEditEngine& rEdEngine = m_xEditWindow->getEditEngine(); + std::unique_ptr<EditTextObject> pEditText(rEdEngine.CreateTextObject()); + + sal_Int32 nCurrentParagraph = -1; + + std::vector<editeng::Section> aSections; + pEditText->GetAllSections(aSections); + for (editeng::Section const & rSection : aSections) + { + while (nCurrentParagraph < rSection.mnParagraph) + { + nCurrentParagraph++; + + // Get Weight of current paragraph + FontWeight eFontWeight = WEIGHT_NORMAL; + SfxItemSet aItemSet(rEdEngine.GetParaAttribs(nCurrentParagraph)); + if (const SfxPoolItem* pItem = aItemSet.GetItem(EE_CHAR_WEIGHT, false)) + { + const SvxWeightItem* pWeightItem = dynamic_cast<const SvxWeightItem*>(pItem); + if (pWeightItem && pWeightItem->GetWeight() == WEIGHT_BOLD) + eFontWeight = WEIGHT_BOLD; + } + // Font weight to string + OUString sWeightProperty = "NORMAL"; + if (eFontWeight == WEIGHT_BOLD) + sWeightProperty = "BOLD"; + // Insert into collection + OUString sBlank; + aClassificationResults.push_back({ ClassificationType::PARAGRAPH, sWeightProperty, sBlank, sBlank }); + } + + const SvxFieldItem* pFieldItem = findField(rSection); + + ESelection aSelection(rSection.mnParagraph, rSection.mnStart, rSection.mnParagraph, rSection.mnEnd); + const OUString sDisplayString = rEdEngine.GetText(aSelection); + if (!sDisplayString.isEmpty()) + { + const ClassificationField* pClassificationField = pFieldItem ? dynamic_cast<const ClassificationField*>(pFieldItem->GetField()) : nullptr; + + if (pClassificationField) + { + aClassificationResults.push_back({ pClassificationField->meType, pClassificationField->msFullClassName, + pClassificationField->msDescription, pClassificationField->msIdentifier }); + } + else + { + aClassificationResults.push_back({ ClassificationType::TEXT, sDisplayString, sDisplayString, OUString() }); + } + } + } + + return aClassificationResults; +} + +IMPL_LINK(ClassificationDialog, SelectClassificationHdl, weld::ComboBox&, rBox, void) +{ + const sal_Int32 nSelected = rBox.get_active(); + if (nSelected < 0 || m_nCurrentSelectedCategory == nSelected) + return; + + std::unique_ptr<EditTextObject> pEditText(m_xEditWindow->getEditEngine().CreateTextObject()); + std::vector<editeng::Section> aSections; + pEditText->GetAllSections(aSections); + + // if we are replacing an existing field + bool bReplaceExisting = false; + // selection of the existing field, which will be replaced + ESelection aExistingFieldSelection; + + for (editeng::Section const & rSection : aSections) + { + const SvxFieldItem* pFieldItem = findField(rSection); + if (pFieldItem) + { + const ClassificationField* pClassificationField = dynamic_cast<const ClassificationField*>(pFieldItem->GetField()); + if (pClassificationField && pClassificationField->meType == ClassificationType::CATEGORY) + { + aExistingFieldSelection = ESelection(rSection.mnParagraph, rSection.mnStart, + rSection.mnParagraph, rSection.mnEnd); + bReplaceExisting = true; + } + } + } + + if (bReplaceExisting) + m_xEditWindow->getEditView().SetSelection(aExistingFieldSelection); + + insertCategoryField(nSelected); + + // Change category to the new selection + m_xInternationalClassificationListBox->set_active(nSelected); + m_xClassificationListBox->set_active(nSelected); + m_nCurrentSelectedCategory = nSelected; +} + +IMPL_LINK(ClassificationDialog, SelectMarkingHdl, weld::TreeView&, rBox, bool) +{ + sal_Int32 nSelected = rBox.get_selected_index(); + if (nSelected >= 0) + { + const OUString aString = maHelper.GetMarkings()[nSelected]; + insertField(ClassificationType::MARKING, aString, aString); + } + return true; +} + +IMPL_LINK(ClassificationDialog, SelectIPPartNumbersHdl, weld::TreeView&, rBox, bool) +{ + sal_Int32 nSelected = rBox.get_selected_index(); + if (nSelected >= 0) + { + OUString sString = maHelper.GetIntellectualPropertyPartNumbers()[nSelected]; + m_xIntellectualPropertyPartEdit->replace_selection(sString); + m_xIntellectualPropertyPartEdit->grab_focus(); + } + return true; +} + +IMPL_LINK(ClassificationDialog, SelectRecentlyUsedHdl, weld::ComboBox&, rBox, void) +{ + sal_Int32 nSelected = rBox.get_active(); + if (nSelected >= 0) + { + m_xEditWindow->getEditEngine().Clear(); + readIn(m_aRecentlyUsedValuesCollection[nSelected]); + } +} + +IMPL_LINK(ClassificationDialog, SelectIPPartHdl, weld::TreeView&, rBox, bool) +{ + const sal_Int32 nSelected = rBox.get_selected_index(); + if (nSelected >= 0) + { + const OUString sString = maHelper.GetIntellectualPropertyParts()[nSelected]; + m_xIntellectualPropertyPartEdit->replace_selection(sString); + m_xIntellectualPropertyPartEdit->grab_focus(); + } + return true; +} + +IMPL_LINK(ClassificationDialog, ButtonClicked, weld::Button&, rButton, void) +{ + if (&rButton == m_xSignButton.get()) + { + m_aParagraphSignHandler(); + } + else if (&rButton == m_xIntellectualPropertyPartAddButton.get()) + { + const OUString sString = m_xIntellectualPropertyPartEdit->get_text(); + insertField(ClassificationType::INTELLECTUAL_PROPERTY_PART, sString, sString); + } +} + +IMPL_LINK_NOARG(ClassificationDialog, OkHdl, weld::Button&, void) +{ + writeRecentlyUsed(); + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(ClassificationDialog, SelectToolboxHdl, weld::Toggleable&, void) +{ + m_xEditWindow->InvertSelectionWeight(); +} + +IMPL_LINK_NOARG(ClassificationDialog, EditWindowModifiedHdl, LinkParamNone*, void) +{ + toggleWidgetsDependingOnCategory(); +} + +IMPL_STATIC_LINK(ClassificationDialog, ExpandedHdl, weld::Expander&, rExpander, void) +{ + std::shared_ptr<comphelper::ConfigurationChanges> aConfigurationChanges(comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Classification::IntellectualPropertySectionExpanded::set(rExpander.get_expanded(), aConfigurationChanges); + aConfigurationChanges->commit(); +} + +} // end svx + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/ClassificationEditView.cxx b/svx/source/dialog/ClassificationEditView.cxx new file mode 100644 index 0000000000..fa4388017f --- /dev/null +++ b/svx/source/dialog/ClassificationEditView.cxx @@ -0,0 +1,82 @@ +/* -*- 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 <svx/ClassificationField.hxx> + +#include <svl/itempool.hxx> +#include <svl/itemset.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/eeitem.hxx> + +#include "ClassificationEditView.hxx" + +namespace svx { + +ClassificationEditEngine::ClassificationEditEngine(SfxItemPool* pItemPool) + : EditEngine(pItemPool) +{} + +OUString ClassificationEditEngine::CalcFieldValue(const SvxFieldItem& rField, sal_Int32 /*nPara*/, + sal_Int32 /*nPos*/, std::optional<Color>& /*rTxtColor*/, std::optional<Color>& /*rFldColor*/, std::optional<FontLineStyle>& /*rFldLineStyle*/) +{ + OUString aString; + const ClassificationField* pClassificationField = dynamic_cast<const ClassificationField*>(rField.GetField()); + if (pClassificationField) + aString = pClassificationField->msDescription; + else + aString = "Unknown"; + return aString; +} + +ClassificationEditView::ClassificationEditView() +{ +} + +void ClassificationEditView::makeEditEngine() +{ + m_xEditEngine.reset(new ClassificationEditEngine(EditEngine::CreatePool().get())); +} + +ClassificationEditView::~ClassificationEditView() +{ +} + +void ClassificationEditView::InsertField(const SvxFieldItem& rFieldItem) +{ + m_xEditView->InsertField(rFieldItem); + m_xEditView->Invalidate(); +} + +void ClassificationEditView::InvertSelectionWeight() +{ + ESelection aSelection = m_xEditView->GetSelection(); + + for (sal_Int32 nParagraph = aSelection.nStartPara; nParagraph <= aSelection.nEndPara; ++nParagraph) + { + FontWeight eFontWeight = WEIGHT_BOLD; + + SfxItemSet aSet(m_xEditEngine->GetParaAttribs(nParagraph)); + if (const SfxPoolItem* pItem = aSet.GetItem(EE_CHAR_WEIGHT, false)) + { + const SvxWeightItem* pWeightItem = dynamic_cast<const SvxWeightItem*>(pItem); + if (pWeightItem && pWeightItem->GetWeight() == WEIGHT_BOLD) + eFontWeight = WEIGHT_NORMAL; + } + SvxWeightItem aWeight(eFontWeight, EE_CHAR_WEIGHT); + aSet.Put(aWeight); + m_xEditEngine->SetParaAttribs(nParagraph, aSet); + } + + m_xEditView->Invalidate(); +} + +} // end sfx2 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/ClassificationEditView.hxx b/svx/source/dialog/ClassificationEditView.hxx new file mode 100644 index 0000000000..225efe1243 --- /dev/null +++ b/svx/source/dialog/ClassificationEditView.hxx @@ -0,0 +1,54 @@ +/* -*- 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/. + * + */ + +#ifndef INCLUDED_SVX_CLASSIFICATIONEDITVIEW_HXX +#define INCLUDED_SVX_CLASSIFICATIONEDITVIEW_HXX + +#include <sal/config.h> +#include <svx/weldeditview.hxx> + +namespace svx { + +class ClassificationEditEngine final : public EditEngine +{ +public: + ClassificationEditEngine(SfxItemPool* pItemPool); + + virtual OUString CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rTxtColor, std::optional<Color>& rFldColor, std::optional<FontLineStyle>& rFldLineStyle) override; +}; + +class ClassificationEditView final : public WeldEditView +{ +public: + ClassificationEditView(); + virtual ~ClassificationEditView() override; + + virtual void makeEditEngine() override; + + void InsertField(const SvxFieldItem& rField); + + void InvertSelectionWeight(); + + ClassificationEditEngine& getEditEngine() + { + return *static_cast<ClassificationEditEngine*>(m_xEditEngine.get()); + } + + EditView& getEditView() + { + return *m_xEditView; + } +}; + +} // end svx namespace + +#endif // INCLUDED_SVX_CLASSIFICATIONEDITVIEW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/FileExportedDialog.cxx b/svx/source/dialog/FileExportedDialog.cxx new file mode 100644 index 0000000000..787e0a20d3 --- /dev/null +++ b/svx/source/dialog/FileExportedDialog.cxx @@ -0,0 +1,42 @@ +/* -*- 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 <svx/FileExportedDialog.hxx> + +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/backupfilehelper.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/system/XSystemShellExecute.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> +#include <com/sun/star/system/SystemShellExecute.hpp> + +FileExportedDialog::FileExportedDialog(weld::Window* pParent, OUString atitle) + : GenericDialogController(pParent, "svx/ui/fileexporteddialog.ui", "FileExportedDialog") + , m_xFileLabel(m_xBuilder->weld_label("Filelabel")) + , m_xButton(m_xBuilder->weld_button("ok")) +{ + m_xFileLabel->set_label(atitle); + m_xButton->connect_clicked(LINK(this, FileExportedDialog, OpenHdl)); +} + +IMPL_LINK_NOARG(FileExportedDialog, OpenHdl, weld::Button&, void) +{ + const OUString uri(comphelper::BackupFileHelper::getUserProfileURL()); + css::uno::Reference<css::system::XSystemShellExecute> exec( + css::system::SystemShellExecute::create(comphelper::getProcessComponentContext())); + try + { + exec->execute(uri, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("svx.dialog", "opening <" << uri << "> failed:"); + } + m_xDialog->response(RET_OK); +}
\ No newline at end of file diff --git a/svx/source/dialog/GenericCheckDialog.cxx b/svx/source/dialog/GenericCheckDialog.cxx new file mode 100644 index 0000000000..09fc3d6787 --- /dev/null +++ b/svx/source/dialog/GenericCheckDialog.cxx @@ -0,0 +1,70 @@ +/* -*- 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 <svx/GenericCheckDialog.hxx> +#include <vcl/svapp.hxx> + +namespace svx +{ +GenericCheckEntry::GenericCheckEntry(weld::Container* pParent, + std::unique_ptr<CheckData>& pCheckData) + : m_xBuilder(Application::CreateBuilder(pParent, "svx/ui/genericcheckentry.ui")) + , m_xContainer(m_xBuilder->weld_container("checkEntryBox")) + , m_xLabel(m_xBuilder->weld_label("label")) + , m_xMarkButton(m_xBuilder->weld_button("markButton")) + , m_xPropertiesButton(m_xBuilder->weld_button("propertiesButton")) + , m_pCheckData(pCheckData) +{ + m_xLabel->set_label(m_pCheckData->getText()); + m_xMarkButton->set_visible(m_pCheckData->canMarkObject()); + m_xMarkButton->connect_clicked(LINK(this, GenericCheckEntry, MarkButtonClicked)); + m_xPropertiesButton->set_visible(m_pCheckData->hasProperties()); + m_xPropertiesButton->connect_clicked(LINK(this, GenericCheckEntry, PropertiesButtonClicked)); + + m_xContainer->show(); +} + +IMPL_LINK_NOARG(GenericCheckEntry, MarkButtonClicked, weld::Button&, void) +{ + m_pCheckData->markObject(); +} + +IMPL_LINK_NOARG(GenericCheckEntry, PropertiesButtonClicked, weld::Button&, void) +{ + m_pCheckData->runProperties(); +} + +GenericCheckDialog::GenericCheckDialog(weld::Window* pParent, + CheckDataCollection& rCheckDataCollection) + : GenericDialogController(pParent, "svx/ui/genericcheckdialog.ui", "GenericCheckDialog") + , m_rCheckDataCollection(rCheckDataCollection) + , m_xCheckBox(m_xBuilder->weld_box("checkBox")) +{ + set_title(m_rCheckDataCollection.getTitle()); +} + +GenericCheckDialog::~GenericCheckDialog() {} + +short GenericCheckDialog::run() +{ + sal_Int32 i = 0; + + for (std::unique_ptr<CheckData>& pCheckData : m_rCheckDataCollection.getCollection()) + { + auto xEntry = std::make_unique<GenericCheckEntry>(m_xCheckBox.get(), pCheckData); + m_xCheckBox->reorder_child(xEntry->get_widget(), i++); + m_aCheckEntries.push_back(std::move(xEntry)); + } + return GenericDialogController::run(); +} + +} // end svx namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/SafeModeDialog.cxx b/svx/source/dialog/SafeModeDialog.cxx new file mode 100644 index 0000000000..5aaa30e437 --- /dev/null +++ b/svx/source/dialog/SafeModeDialog.cxx @@ -0,0 +1,308 @@ +/* -*- 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 "SafeModeDialog.hxx" + +#include <osl/file.hxx> +#include <sfx2/safemode.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <comphelper/processfactory.hxx> +#include <unotools/ZipPackageHelper.hxx> +#include <unotools/configmgr.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <svx/FileExportedDialog.hxx> +#include <officecfg/Office/Common.hxx> + +#include <com/sun/star/task/OfficeRestartManager.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> + +using namespace css; + +SafeModeDialog::SafeModeDialog(weld::Window* pParent) + : GenericDialogController(pParent, "svx/ui/safemodedialog.ui", "SafeModeDialog") + , mxBtnContinue(m_xBuilder->weld_button("btn_continue")) + , mxBtnRestart(m_xBuilder->weld_button("btn_restart")) + , mxBtnApply(m_xBuilder->weld_button("btn_apply")) + , mxBoxRestore(m_xBuilder->weld_container("group_restore")) + , mxBoxConfigure(m_xBuilder->weld_container("group_configure")) + , mxBoxDeinstall(m_xBuilder->weld_container("group_deinstall")) + , mxBoxReset(m_xBuilder->weld_container("group_reset")) + , mxRadioRestore(m_xBuilder->weld_radio_button("radio_restore")) + , mxRadioConfigure(m_xBuilder->weld_radio_button("radio_configure")) + , mxRadioExtensions(m_xBuilder->weld_radio_button("radio_extensions")) + , mxRadioReset(m_xBuilder->weld_radio_button("radio_reset")) + , mxCBCheckProfilesafeConfig(m_xBuilder->weld_check_button("check_profilesafe_config")) + , mxCBCheckProfilesafeExtensions(m_xBuilder->weld_check_button("check_profilesafe_extensions")) + , mxCBDisableAllExtensions(m_xBuilder->weld_check_button("check_disable_all_extensions")) + , mxCBDeinstallUserExtensions(m_xBuilder->weld_check_button("check_deinstall_user_extensions")) + , mxCBResetSharedExtensions(m_xBuilder->weld_check_button("check_reset_shared_extensions")) + , mxCBResetBundledExtensions(m_xBuilder->weld_check_button("check_reset_bundled_extensions")) + , mxCBDisableHWAcceleration(m_xBuilder->weld_check_button("check_disable_hw_acceleration")) + , mxCBResetCustomizations(m_xBuilder->weld_check_button("check_reset_customizations")) + , mxCBResetWholeUserProfile(m_xBuilder->weld_check_button("check_reset_whole_userprofile")) + , mxBugLink(m_xBuilder->weld_link_button("linkbutton_bugs")) + , mxUserProfileLink(m_xBuilder->weld_link_button("linkbutton_profile")) + , mxBtnCreateZip(m_xBuilder->weld_button("btn_create_zip")) +{ + m_xDialog->set_centered_on_parent(false); + mxRadioRestore->connect_toggled(LINK(this, SafeModeDialog, RadioBtnHdl)); + mxRadioConfigure->connect_toggled(LINK(this, SafeModeDialog, RadioBtnHdl)); + mxRadioExtensions->connect_toggled(LINK(this, SafeModeDialog, RadioBtnHdl)); + mxRadioReset->connect_toggled(LINK(this, SafeModeDialog, RadioBtnHdl)); + + mxBtnContinue->connect_clicked(LINK(this, SafeModeDialog, DialogBtnHdl)); + mxBtnRestart->connect_clicked(LINK(this, SafeModeDialog, DialogBtnHdl)); + mxBtnApply->connect_clicked(LINK(this, SafeModeDialog, DialogBtnHdl)); + + mxCBCheckProfilesafeConfig->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl)); + mxCBCheckProfilesafeExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl)); + mxCBDisableAllExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl)); + mxCBDeinstallUserExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl)); + mxCBResetSharedExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl)); + mxCBResetBundledExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl)); + mxCBDisableHWAcceleration->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl)); + mxCBResetCustomizations->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl)); + mxCBResetWholeUserProfile->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl)); + + mxBtnCreateZip->connect_clicked(LINK(this, SafeModeDialog, CreateZipBtnHdl)); + + // Disable restart btn until some checkbox is active + mxBtnApply->set_sensitive(false); + + // Check the first radio button and call its handler, + // it'll disable the relevant parts + mxRadioRestore->set_active(true); + RadioBtnHdl(*mxRadioRestore); + + // Set URL for help button (module=safemode) + OUString sURL(officecfg::Office::Common::Menus::SendFeedbackURL::get() //officecfg/registry/data/org/openoffice/Office/Common.xcu => https://hub.libreoffice.org/send-feedback/ + + "?LOversion=" + utl::ConfigManager::getAboutBoxProductVersion() + + "&LOlocale=" + utl::ConfigManager::getUILocale() + "&LOmodule=safemode"); + mxBugLink->set_uri(sURL); + + mxUserProfileLink->set_uri(comphelper::BackupFileHelper::getUserProfileURL()); +} + +SafeModeDialog::~SafeModeDialog() +{ +} + +void SafeModeDialog::enableDisableWidgets() +{ + mxCBCheckProfilesafeConfig->set_sensitive(maBackupFileHelper.isPopPossible()); + mxCBCheckProfilesafeExtensions->set_sensitive(maBackupFileHelper.isPopPossibleExtensionInfo()); + mxCBDisableAllExtensions->set_sensitive(comphelper::BackupFileHelper::isTryDisableAllExtensionsPossible()); + mxCBDeinstallUserExtensions->set_sensitive(comphelper::BackupFileHelper::isTryDeinstallUserExtensionsPossible()); + mxCBResetSharedExtensions->set_sensitive(comphelper::BackupFileHelper::isTryResetSharedExtensionsPossible()); + mxCBResetBundledExtensions->set_sensitive(comphelper::BackupFileHelper::isTryResetBundledExtensionsPossible()); + mxCBResetCustomizations->set_sensitive(comphelper::BackupFileHelper::isTryResetCustomizationsPossible()); + + // no disable of mxCBResetWholeUserProfile, always possible (as last choice) +} + +short SafeModeDialog::run() +{ + short nRet = weld::GenericDialogController::run(); + // Remove the safe mode flag before exiting this dialog + sfx2::SafeMode::removeFlag(); + return nRet; +} + +void SafeModeDialog::applyChanges() +{ + // Restore + if (mxRadioRestore->get_active()) + { + if (mxCBCheckProfilesafeConfig->get_active()) + { + // reset UserConfiguration to last known working state + // ProfileSafeMode/BackupFileHelper + maBackupFileHelper.tryPop(); + } + + if (mxCBCheckProfilesafeExtensions->get_active()) + { + // reset State of installed Extensions to last known working state + // ProfileSafeMode/BackupFileHelper + maBackupFileHelper.tryPopExtensionInfo(); + } + } + + // Configure + if (mxRadioConfigure->get_active()) + { + if (mxCBDisableAllExtensions->get_active()) + { + // Disable all extensions + comphelper::BackupFileHelper::tryDisableAllExtensions(); + } + + if (mxCBDisableHWAcceleration->get_active()) + { + comphelper::BackupFileHelper::tryDisableHWAcceleration(); + } + } + + // Deinstall + if (mxRadioExtensions->get_active()) + { + if (mxCBDeinstallUserExtensions->get_active()) + { + // Deinstall all User Extensions (installed for User only) + comphelper::BackupFileHelper::tryDeinstallUserExtensions(); + } + + if (mxCBResetSharedExtensions->get_active()) + { + // Reset shared Extensions + comphelper::BackupFileHelper::tryResetSharedExtensions(); + } + if (mxCBResetBundledExtensions->get_active()) + { + // Reset bundled Extensions + comphelper::BackupFileHelper::tryResetBundledExtensions(); + } + } + + // Reset + if (mxRadioReset->get_active()) + { + if (mxCBResetCustomizations->get_active()) + { + // Reset customizations (Settings and UserInterface modifications) + comphelper::BackupFileHelper::tryResetCustomizations(); + } + + if (mxCBResetWholeUserProfile->get_active()) + { + // Reset the whole UserProfile + comphelper::BackupFileHelper::tryResetUserProfile(); + } + } + + // finally, restart + css::task::OfficeRestartManager::get(comphelper::getProcessComponentContext())->requestRestart( + css::uno::Reference< css::task::XInteractionHandler >()); +} + +IMPL_LINK(SafeModeDialog, RadioBtnHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + if (mxRadioRestore->get_active()) + { + // Enable the currently selected box + mxBoxRestore->set_sensitive(true); + // Make sure only possible choices are active + enableDisableWidgets(); + // Disable the unselected boxes + mxBoxReset->set_sensitive(false); + mxBoxConfigure->set_sensitive(false); + mxBoxDeinstall->set_sensitive(false); + } + else if (mxRadioConfigure->get_active()) + { + // Enable the currently selected box + mxBoxConfigure->set_sensitive(true); + // Make sure only possible choices are active + enableDisableWidgets(); + // Disable the unselected boxes + mxBoxRestore->set_sensitive(false); + mxBoxReset->set_sensitive(false); + mxBoxDeinstall->set_sensitive(false); + + } + else if (mxRadioExtensions->get_active()) + { + // Enable the currently selected box + mxBoxDeinstall->set_sensitive(true); + // Make sure only possible choices are active + enableDisableWidgets(); + // Disable the unselected boxes + mxBoxRestore->set_sensitive(false); + mxBoxConfigure->set_sensitive(false); + mxBoxReset->set_sensitive(false); + } + else if (mxRadioReset->get_active()) + { + // Enable the currently selected box + mxBoxReset->set_sensitive(true); + // Make sure only possible choices are active + enableDisableWidgets(); + // Disable the unselected boxes + mxBoxConfigure->set_sensitive(false); + mxBoxRestore->set_sensitive(false); + mxBoxDeinstall->set_sensitive(false); + } +} + +IMPL_LINK(SafeModeDialog, DialogBtnHdl, weld::Button&, rBtn, void) +{ + if (&rBtn == mxBtnContinue.get()) + { + m_xDialog->response(RET_CLOSE); + } + else if (&rBtn == mxBtnRestart.get()) + { + sfx2::SafeMode::putRestartFlag(); + m_xDialog->response(RET_CLOSE); + uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + css::task::OfficeRestartManager::get(xContext)->requestRestart( + css::uno::Reference< css::task::XInteractionHandler >()); + } + else if (&rBtn == mxBtnApply.get()) + { + sfx2::SafeMode::putRestartFlag(); + m_xDialog->response(RET_CLOSE); + applyChanges(); + } +} + +IMPL_LINK(SafeModeDialog, CreateZipBtnHdl, weld::Button&, /*rBtn*/, void) +{ + const OUString zipFileURL(comphelper::BackupFileHelper::getUserProfileURL() + "/libreoffice-profile.zip"); + osl::File::remove(zipFileURL); // Remove previous exports + try + { + utl::ZipPackageHelper aZipHelper(comphelper::getProcessComponentContext(), zipFileURL); + aZipHelper.addFolderWithContent(aZipHelper.getRootFolder(), comphelper::BackupFileHelper::getUserProfileWorkURL()); + aZipHelper.savePackage(); + } + catch (const uno::Exception &) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + SvxResId(RID_SVXSTR_SAFEMODE_ZIP_FAILURE))); + xBox->run(); + return; + } + + FileExportedDialog aDialog(m_xDialog.get(), SvxResId(RID_SVXSTR_SAFEMODE_USER_PROFILE_EXPORTED)); + aDialog.run(); +} + +IMPL_LINK(SafeModeDialog, CheckBoxHdl, weld::Toggleable&, /*pCheckBox*/, void) +{ + const bool bEnable( + mxCBCheckProfilesafeConfig->get_active() || + mxCBCheckProfilesafeExtensions->get_active() || + mxCBDisableAllExtensions->get_active() || + mxCBDeinstallUserExtensions->get_active() || + mxCBResetSharedExtensions->get_active() || + mxCBResetBundledExtensions->get_active() || + mxCBDisableHWAcceleration->get_active() || + mxCBResetCustomizations->get_active() || + mxCBResetWholeUserProfile->get_active()); + + mxBtnApply->set_sensitive(bEnable); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/SafeModeDialog.hxx b/svx/source/dialog/SafeModeDialog.hxx new file mode 100644 index 0000000000..dd7db077e0 --- /dev/null +++ b/svx/source/dialog/SafeModeDialog.hxx @@ -0,0 +1,66 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SVX_SOURCE_DIALOG_SAFEMODEDIALOG_HXX +#define INCLUDED_SVX_SOURCE_DIALOG_SAFEMODEDIALOG_HXX + +#include <comphelper/backupfilehelper.hxx> +#include <vcl/weld.hxx> + +class SafeModeDialog : public weld::GenericDialogController +{ +public: + explicit SafeModeDialog(weld::Window* pParent); + virtual short run() override; + virtual ~SafeModeDialog() override; + +private: + std::unique_ptr<weld::Button> mxBtnContinue; + std::unique_ptr<weld::Button> mxBtnRestart; + std::unique_ptr<weld::Button> mxBtnApply; + + std::unique_ptr<weld::Container> mxBoxRestore; + std::unique_ptr<weld::Container> mxBoxConfigure; + std::unique_ptr<weld::Container> mxBoxDeinstall; + std::unique_ptr<weld::Container> mxBoxReset; + + std::unique_ptr<weld::RadioButton> mxRadioRestore; + std::unique_ptr<weld::RadioButton> mxRadioConfigure; + std::unique_ptr<weld::RadioButton> mxRadioExtensions; + std::unique_ptr<weld::RadioButton> mxRadioReset; + + std::unique_ptr<weld::CheckButton> mxCBCheckProfilesafeConfig; + std::unique_ptr<weld::CheckButton> mxCBCheckProfilesafeExtensions; + std::unique_ptr<weld::CheckButton> mxCBDisableAllExtensions; + std::unique_ptr<weld::CheckButton> mxCBDeinstallUserExtensions; + std::unique_ptr<weld::CheckButton> mxCBResetSharedExtensions; + std::unique_ptr<weld::CheckButton> mxCBResetBundledExtensions; + std::unique_ptr<weld::CheckButton> mxCBDisableHWAcceleration; + std::unique_ptr<weld::CheckButton> mxCBResetCustomizations; + std::unique_ptr<weld::CheckButton> mxCBResetWholeUserProfile; + + std::unique_ptr<weld::LinkButton> mxBugLink; + std::unique_ptr<weld::LinkButton> mxUserProfileLink; + std::unique_ptr<weld::Button> mxBtnCreateZip; + + // local BackupFileHelper for handling possible restores + comphelper::BackupFileHelper maBackupFileHelper; + + void enableDisableWidgets(); + void applyChanges(); + + DECL_LINK(RadioBtnHdl, weld::Toggleable&, void); + DECL_LINK(CheckBoxHdl, weld::Toggleable&, void); + DECL_LINK(CreateZipBtnHdl, weld::Button&, void); + DECL_LINK(DialogBtnHdl, weld::Button&, void); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/SafeModeUI.cxx b/svx/source/dialog/SafeModeUI.cxx new file mode 100644 index 0000000000..4e32669f02 --- /dev/null +++ b/svx/source/dialog/SafeModeUI.cxx @@ -0,0 +1,79 @@ +/* -*- 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 <cppuhelper/implbase.hxx> +#include <com/sun/star/frame/XSynchronousDispatch.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <cppuhelper/supportsservice.hxx> + +#include <vcl/svapp.hxx> + +#include "SafeModeDialog.hxx" + +namespace { + +class SafeModeUI : public ::cppu::WeakImplHelper< css::lang::XServiceInfo, + css::frame::XSynchronousDispatch > // => XDispatch! +{ +public: + SafeModeUI(); + + // css.lang.XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + + virtual css::uno::Any SAL_CALL dispatchWithReturnValue(const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; +}; + +SafeModeUI::SafeModeUI() +{ +} + +OUString SAL_CALL SafeModeUI::getImplementationName() +{ + return "com.sun.star.comp.svx.SafeModeUI"; +} + +sal_Bool SAL_CALL SafeModeUI::supportsService(const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL SafeModeUI::getSupportedServiceNames() +{ + return { "com.sun.star.dialog.SafeModeUI" }; +} + +css::uno::Any SAL_CALL SafeModeUI::dispatchWithReturnValue(const css::util::URL&, + const css::uno::Sequence< css::beans::PropertyValue >& ) +{ + SolarMutexGuard aGuard; + css::uno::Any aRet; + SafeModeDialog aDialog(Application::GetDefDialogParent()); + aDialog.run(); + return aRet; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_svx_SafeModeUI_get_implementation( + css::uno::XComponentContext * /*context*/, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SafeModeUI); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/SpellDialogChildWindow.cxx b/svx/source/dialog/SpellDialogChildWindow.cxx new file mode 100644 index 0000000000..ae82769dfb --- /dev/null +++ b/svx/source/dialog/SpellDialogChildWindow.cxx @@ -0,0 +1,90 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/SpellDialogChildWindow.hxx> + +#include <svx/svxdlg.hxx> +#include <osl/diagnose.h> + +namespace svx { + + +SpellDialogChildWindow::SpellDialogChildWindow ( + vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings) + : SfxChildWindow (_pParent, nId) +{ + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + m_xAbstractSpellDialog = pFact->CreateSvxSpellDialog(_pParent->GetFrameWeld(), + pBindings, + this ); + SetController(m_xAbstractSpellDialog->GetController()); + SetHideNotDelete(true); +} + +SpellDialogChildWindow::~SpellDialogChildWindow() +{ + m_xAbstractSpellDialog.disposeAndClear(); +} + +SfxBindings& SpellDialogChildWindow::GetBindings() const +{ + assert(m_xAbstractSpellDialog); + return m_xAbstractSpellDialog->GetBindings(); +} + +void SpellDialogChildWindow::InvalidateSpellDialog() +{ + OSL_ASSERT (m_xAbstractSpellDialog); + if (m_xAbstractSpellDialog) + m_xAbstractSpellDialog->InvalidateDialog(); +} + +bool SpellDialogChildWindow::HasAutoCorrection() +{ + return false; +} + +void SpellDialogChildWindow::AddAutoCorrection( + const OUString& /*rOld*/, + const OUString& /*rNew*/, + LanguageType /*eLanguage*/) +{ + OSL_FAIL("AutoCorrection should have been overridden - if available"); +} + +bool SpellDialogChildWindow::HasGrammarChecking() +{ + return false; +} + +bool SpellDialogChildWindow::IsGrammarChecking() +{ + OSL_FAIL("Grammar checking should have been overridden - if available"); + return false; +} + +void SpellDialogChildWindow::SetGrammarChecking(bool ) +{ + OSL_FAIL("Grammar checking should have been overridden - if available"); +} +} // end of namespace ::svx + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/SvxNumOptionsTabPageHelper.cxx b/svx/source/dialog/SvxNumOptionsTabPageHelper.cxx new file mode 100644 index 0000000000..0236c88353 --- /dev/null +++ b/svx/source/dialog/SvxNumOptionsTabPageHelper.cxx @@ -0,0 +1,88 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/SvxNumOptionsTabPageHelper.hxx> +#include <com/sun/star/style/NumberingType.hpp> +#include <com/sun/star/text/DefaultNumberingProvider.hpp> +#include <com/sun/star/text/XNumberingTypeInfo.hpp> +#include <comphelper/processfactory.hxx> + +using namespace css; +using namespace css::uno; +using namespace css::text; +using namespace css::style; + +Reference<XDefaultNumberingProvider> SvxNumOptionsTabPageHelper::GetNumberingProvider() +{ + Reference<XComponentContext> xContext(::comphelper::getProcessComponentContext()); + Reference<XDefaultNumberingProvider> xRet = text::DefaultNumberingProvider::create(xContext); + return xRet; +} + +void SvxNumOptionsTabPageHelper::GetI18nNumbering(weld::ComboBox& rFmtLB, sal_uInt16 nDoNotRemove) +{ + Reference<XDefaultNumberingProvider> xDefNum = GetNumberingProvider(); + Reference<XNumberingTypeInfo> xInfo(xDefNum, UNO_QUERY); + + // Extended numbering schemes present in the resource but not offered by + // the i18n framework per configuration must be removed from the listbox. + // Do not remove a special entry matching nDoNotRemove. + const sal_uInt16 nDontRemove = SAL_MAX_UINT16; + ::std::vector<sal_uInt16> aRemove(rFmtLB.get_count(), nDontRemove); + for (size_t i = 0; i < aRemove.size(); ++i) + { + sal_uInt16 nEntryData = rFmtLB.get_id(i).toInt32(); + if (nEntryData > NumberingType::CHARS_LOWER_LETTER_N && nEntryData != nDoNotRemove) + aRemove[i] = nEntryData; + } + if (xInfo.is()) + { + const Sequence<sal_Int16> aTypes = xInfo->getSupportedNumberingTypes(); + for (const sal_Int16 nCurrent : aTypes) + { + if (nCurrent > NumberingType::CHARS_LOWER_LETTER_N) + { + bool bInsert = true; + for (int nEntry = 0; nEntry < rFmtLB.get_count(); ++nEntry) + { + sal_uInt16 nEntryData = rFmtLB.get_id(nEntry).toInt32(); + if (nEntryData == static_cast<sal_uInt16>(nCurrent)) + { + bInsert = false; + aRemove[nEntry] = nDontRemove; + break; + } + } + if (bInsert) + { + OUString aIdent = xInfo->getNumberingIdentifier(nCurrent); + rFmtLB.append(OUString::number(nCurrent), aIdent); + } + } + } + } + for (unsigned short i : aRemove) + { + if (i == nDontRemove) + continue; + rFmtLB.remove_id(OUString::number(i)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/ThemeColorEditDialog.cxx b/svx/source/dialog/ThemeColorEditDialog.cxx new file mode 100644 index 0000000000..4b5e60a8b2 --- /dev/null +++ b/svx/source/dialog/ThemeColorEditDialog.cxx @@ -0,0 +1,89 @@ +/* -*- 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 <svx/dialog/ThemeColorEditDialog.hxx> +#include <vcl/svapp.hxx> +#include <svx/colorbox.hxx> + +namespace svx +{ +ThemeColorEditDialog::ThemeColorEditDialog(weld::Window* pParent, model::ColorSet& rColorSet) + : GenericDialogController(pParent, "svx/ui/themecoloreditdialog.ui", "ThemeColorEditDialog") + , maColorSet(rColorSet) + , mxThemeColorsNameEntry(m_xBuilder->weld_entry("entryThemeColorsName")) + , mxDark1(new ColorListBox(m_xBuilder->weld_menu_button("buttonDark1"), + [pParent] { return pParent; })) + , mxLight1(new ColorListBox(m_xBuilder->weld_menu_button("buttonLight1"), + [pParent] { return pParent; })) + , mxDark2(new ColorListBox(m_xBuilder->weld_menu_button("buttonDark2"), + [pParent] { return pParent; })) + , mxLight2(new ColorListBox(m_xBuilder->weld_menu_button("buttonLight2"), + [pParent] { return pParent; })) + , mxAccent1(new ColorListBox(m_xBuilder->weld_menu_button("buttonAccent1"), + [pParent] { return pParent; })) + , mxAccent2(new ColorListBox(m_xBuilder->weld_menu_button("buttonAccent2"), + [pParent] { return pParent; })) + , mxAccent3(new ColorListBox(m_xBuilder->weld_menu_button("buttonAccent3"), + [pParent] { return pParent; })) + , mxAccent4(new ColorListBox(m_xBuilder->weld_menu_button("buttonAccent4"), + [pParent] { return pParent; })) + , mxAccent5(new ColorListBox(m_xBuilder->weld_menu_button("buttonAccent5"), + [pParent] { return pParent; })) + , mxAccent6(new ColorListBox(m_xBuilder->weld_menu_button("buttonAccent6"), + [pParent] { return pParent; })) + , mxHyperlink(new ColorListBox(m_xBuilder->weld_menu_button("buttonHyperlink"), + [pParent] { return pParent; })) + , mxFollowHyperlink(new ColorListBox(m_xBuilder->weld_menu_button("buttonFollowHyperlink"), + [pParent] { return pParent; })) +{ + mxThemeColorsNameEntry->set_text(rColorSet.getName()); + mxDark1->SelectEntry(rColorSet.getColor(model::ThemeColorType::Dark1)); + mxLight1->SelectEntry(rColorSet.getColor(model::ThemeColorType::Light1)); + mxDark2->SelectEntry(rColorSet.getColor(model::ThemeColorType::Dark2)); + mxLight2->SelectEntry(rColorSet.getColor(model::ThemeColorType::Light2)); + mxAccent1->SelectEntry(rColorSet.getColor(model::ThemeColorType::Accent1)); + mxAccent2->SelectEntry(rColorSet.getColor(model::ThemeColorType::Accent2)); + mxAccent3->SelectEntry(rColorSet.getColor(model::ThemeColorType::Accent3)); + mxAccent4->SelectEntry(rColorSet.getColor(model::ThemeColorType::Accent4)); + mxAccent5->SelectEntry(rColorSet.getColor(model::ThemeColorType::Accent5)); + mxAccent6->SelectEntry(rColorSet.getColor(model::ThemeColorType::Accent6)); + mxHyperlink->SelectEntry(rColorSet.getColor(model::ThemeColorType::Hyperlink)); + mxFollowHyperlink->SelectEntry(rColorSet.getColor(model::ThemeColorType::FollowedHyperlink)); +} + +ThemeColorEditDialog::~ThemeColorEditDialog() = default; + +model::ColorSet ThemeColorEditDialog::getColorSet() +{ + OUString aName = mxThemeColorsNameEntry->get_text(); + + model::ColorSet aColorSet(aName); + + if (!aName.isEmpty()) + { + aColorSet.add(model::ThemeColorType::Dark1, mxDark1->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Light1, mxLight1->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Dark2, mxDark2->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Light2, mxLight2->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Accent1, mxAccent1->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Accent2, mxAccent2->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Accent3, mxAccent3->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Accent4, mxAccent4->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Accent5, mxAccent5->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Accent6, mxAccent6->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::Hyperlink, mxHyperlink->GetSelectEntryColor()); + aColorSet.add(model::ThemeColorType::FollowedHyperlink, + mxFollowHyperlink->GetSelectEntryColor()); + } + return aColorSet; +} + +} // end svx namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/ThemeColorValueSet.cxx b/svx/source/dialog/ThemeColorValueSet.cxx new file mode 100644 index 0000000000..d33e2fbc85 --- /dev/null +++ b/svx/source/dialog/ThemeColorValueSet.cxx @@ -0,0 +1,98 @@ +/* -*- 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 <svx/dialog/ThemeColorValueSet.hxx> +#include <docmodel/uno/UnoComplexColor.hxx> +#include <vcl/event.hxx> + +namespace svx +{ +constexpr tools::Long BORDER = 4; +constexpr tools::Long SIZE = 16; +constexpr tools::Long LABEL_HEIGHT = 16; +constexpr tools::Long LABEL_TEXT_HEIGHT = 14; +constexpr tools::Long constElementNumber = 8; + +void ThemeColorValueSet::insert(model::ColorSet const& rColorSet) +{ + maColorSets.push_back(std::cref(rColorSet)); + InsertItem(maColorSets.size()); +} + +void ThemeColorValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + ValueSet::SetDrawingArea(pDrawingArea); + SetStyle(WB_TABSTOP | WB_ITEMBORDER | WB_DOUBLEBORDER); + Size aSize(BORDER * 7 + SIZE * 6 + BORDER * 2, BORDER * 3 + SIZE * 2 + LABEL_HEIGHT); + SetItemWidth(aSize.Width()); + SetItemHeight(aSize.Height()); +} + +void ThemeColorValueSet::UserDraw(const UserDrawEvent& rUserDrawEvent) +{ + vcl::RenderContext* pDev = rUserDrawEvent.GetRenderContext(); + tools::Rectangle aRect = rUserDrawEvent.GetRect(); + const Point aPosition = aRect.GetPos(); + const sal_uInt16 nItemId = rUserDrawEvent.GetItemId(); + model::ColorSet const& rColorSet = maColorSets[nItemId - 1]; + + Size aSize = aRect.GetSize(); + Size aMin(BORDER * 7 + SIZE * constElementNumber / 2 + BORDER * 2, + BORDER * 3 + SIZE * 2 + LABEL_HEIGHT); + tools::Long startX = (aSize.Width() / 2.0) - (aMin.Width() / 2.0); + tools::Long x = BORDER; + tools::Long y1 = BORDER + LABEL_HEIGHT; + tools::Long y2 = y1 + SIZE + BORDER; + + pDev->SetLineColor(COL_LIGHTGRAY); + pDev->SetFillColor(COL_LIGHTGRAY); + tools::Rectangle aNameRect(aPosition, Size(aSize.Width(), LABEL_HEIGHT)); + pDev->DrawRect(aNameRect); + + vcl::Font aFont; + OUString aName = rColorSet.getName(); + aFont.SetFontHeight(LABEL_TEXT_HEIGHT); + pDev->SetFont(aFont); + + Size aTextSize(pDev->GetTextWidth(aName), pDev->GetTextHeight()); + + Point aPoint(aPosition.X() + (aNameRect.GetWidth() / 2.0) - (aTextSize.Width() / 2.0), + aPosition.Y() + (aNameRect.GetHeight() / 2.0) - (aTextSize.Height() / 2.0)); + + pDev->DrawText(aPoint, aName); + + pDev->SetLineColor(COL_LIGHTGRAY); + pDev->SetFillColor(); + + for (sal_uInt32 i = 2; i < 10; i += 2) + { + pDev->SetFillColor(rColorSet.getColor(model::convertToThemeColorType(i))); + pDev->DrawRect(tools::Rectangle(Point(aPosition.X() + x + startX, aPosition.Y() + y1), + Size(SIZE, SIZE))); + + pDev->SetFillColor(rColorSet.getColor(model::convertToThemeColorType(i + 1))); + pDev->DrawRect(tools::Rectangle(Point(aPosition.X() + x + startX, aPosition.Y() + y2), + Size(SIZE, SIZE))); + + x += SIZE + BORDER; + if (i == 2 || i == 8) + x += BORDER; + } +} + +void ThemeColorValueSet::StyleUpdated() +{ + SetFormat(); + Invalidate(); + ValueSet::StyleUpdated(); +} + +} // end svx namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/ThemeDialog.cxx b/svx/source/dialog/ThemeDialog.cxx new file mode 100644 index 0000000000..e3a206be6c --- /dev/null +++ b/svx/source/dialog/ThemeDialog.cxx @@ -0,0 +1,132 @@ +/* -*- 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 <svx/dialog/ThemeDialog.hxx> +#include <docmodel/theme/ColorSet.hxx> +#include <docmodel/theme/Theme.hxx> +#include <svx/ColorSets.hxx> +#include <vcl/svapp.hxx> +#include <svx/colorbox.hxx> +#include <comphelper/lok.hxx> + +namespace svx +{ +ThemeDialog::ThemeDialog(weld::Window* pParent, model::Theme* pTheme) + : GenericDialogController(pParent, "svx/ui/themedialog.ui", "ThemeDialog") + , mpWindow(pParent) + , mpTheme(pTheme) + , mxValueSetThemeColors(new svx::ThemeColorValueSet) + , mxValueSetThemeColorsWindow( + new weld::CustomWeld(*m_xBuilder, "valueset_theme_colors", *mxValueSetThemeColors)) + , mxAdd(m_xBuilder->weld_button("button_add")) +{ + mxValueSetThemeColors->SetColCount(3); + mxValueSetThemeColors->SetLineCount(4); + mxValueSetThemeColors->SetColor(Application::GetSettings().GetStyleSettings().GetFaceColor()); + mxValueSetThemeColors->SetDoubleClickHdl(LINK(this, ThemeDialog, DoubleClickValueSetHdl)); + mxValueSetThemeColors->SetSelectHdl(LINK(this, ThemeDialog, SelectItem)); + + mxAdd->connect_clicked(LINK(this, ThemeDialog, ButtonClicked)); + + initColorSets(); + + if (!maColorSets.empty()) + { + mxValueSetThemeColors->SelectItem(1); // ItemId 1, position 0 + mpCurrentColorSet = std::make_shared<model::ColorSet>(maColorSets[0]); + } +} + +ThemeDialog::~ThemeDialog() +{ + if (mxSubDialog) + mxSubDialog->response(RET_CANCEL); +} + +void ThemeDialog::initColorSets() +{ + if (mpTheme) + maColorSets.push_back(*mpTheme->getColorSet()); + + auto const& rColorSetVector = ColorSets::get().getColorSetVector(); + maColorSets.insert(maColorSets.end(), rColorSetVector.begin(), rColorSetVector.end()); + + for (auto const& rColorSet : maColorSets) + { + mxValueSetThemeColors->insert(rColorSet); + } + + mxValueSetThemeColors->SetOptimalSize(); +} + +IMPL_LINK_NOARG(ThemeDialog, DoubleClickValueSetHdl, ValueSet*, void) +{ + SelectItem(nullptr); + if (!comphelper::LibreOfficeKit::isActive()) + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(ThemeDialog, SelectItem, ValueSet*, void) +{ + sal_uInt32 nItemId = mxValueSetThemeColors->GetSelectedItemId(); + if (!nItemId) + return; + + sal_uInt32 nIndex = nItemId - 1; + + if (nIndex >= maColorSets.size()) + return; + + mpCurrentColorSet = std::make_shared<model::ColorSet>(maColorSets[nIndex]); +} + +void ThemeDialog::runThemeColorEditDialog() +{ + if (mxSubDialog) + return; + + mxSubDialog = std::make_shared<svx::ThemeColorEditDialog>(mpWindow, *mpCurrentColorSet); + + weld::DialogController::runAsync(mxSubDialog, [this](sal_uInt32 nResult) { + if (nResult != RET_OK) + { + mxAdd->set_sensitive(true); + mxSubDialog = nullptr; + return; + } + auto aColorSet = mxSubDialog->getColorSet(); + if (!aColorSet.getName().isEmpty()) + { + ColorSets::get().insert(aColorSet, ColorSets::IdenticalNameAction::AutoRename); + maColorSets.clear(); + mxValueSetThemeColors->Clear(); + + initColorSets(); + + mxValueSetThemeColors->SelectItem(maColorSets.size() - 1); + mpCurrentColorSet + = std::make_shared<model::ColorSet>(maColorSets[maColorSets.size() - 1]); + } + mxAdd->set_sensitive(true); + mxSubDialog = nullptr; + }); +} + +IMPL_LINK(ThemeDialog, ButtonClicked, weld::Button&, rButton, void) +{ + mxAdd->set_sensitive(false); + if (mpCurrentColorSet && mxAdd.get() == &rButton) + { + runThemeColorEditDialog(); + } +} + +} // end svx namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/_bmpmask.cxx b/svx/source/dialog/_bmpmask.cxx new file mode 100644 index 0000000000..1385aea0c9 --- /dev/null +++ b/svx/source/dialog/_bmpmask.cxx @@ -0,0 +1,1055 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/event.hxx> +#include <vcl/metaact.hxx> +#include <vcl/virdev.hxx> +#include <svtools/valueset.hxx> +#include <svl/eitem.hxx> +#include <svl/itemset.hxx> +#include <sfx2/dispatch.hxx> +#include <svtools/colrdlg.hxx> + +#include <svx/colorbox.hxx> +#include <svx/dialmgr.hxx> +#include <svx/bmpmask.hxx> +#include <svx/strings.hrc> +#include <svx/svxids.hrc> +#include <memory> +#include <helpids.h> + +#define OWN_CALLMODE SfxCallMode::ASYNCHRON | SfxCallMode::RECORD + + +#define TEST_COLS() \ +{ \ + nR = aCol.GetRed(); nG = aCol.GetGreen(); nB = aCol.GetBlue(); \ + for( i = 0; i < nCount; i++ ) \ + { \ + if ( ( pMinR[i] <= nR ) && ( pMaxR[i] >= nR ) && \ + ( pMinG[i] <= nG ) && ( pMaxG[i] >= nG ) && \ + ( pMinB[i] <= nB ) && ( pMaxB[i] >= nB ) ) \ + { \ + aCol = pDstCols[i]; bReplace = true; break; \ + } \ + } \ +} + +SFX_IMPL_DOCKINGWINDOW_WITHID( SvxBmpMaskChildWindow, SID_BMPMASK ) + +class BmpColorWindow : public weld::CustomWidgetController +{ + Color aColor; + + +public: + explicit BmpColorWindow() + : aColor( COL_WHITE ) + { + } + + void SetColor( const Color& rColor ) + { + aColor = rColor; + Invalidate(); + } + + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + + virtual void SetDrawingArea(weld::DrawingArea* pArea) override + { + Size aSize(pArea->get_ref_device().LogicToPixel(Size(43, 14), MapMode(MapUnit::MapAppFont))); + CustomWidgetController::SetDrawingArea(pArea); + pArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); + } +}; + +class MaskSet : public ValueSet +{ + VclPtr<SvxBmpMask> pSvxBmpMask; + +public: + MaskSet(SvxBmpMask* pMask); + virtual void Select() override; + virtual bool KeyInput( const KeyEvent& rKEvt ) override; + virtual void GetFocus() override; + virtual void SetDrawingArea(weld::DrawingArea* pArea) override + { + Size aSize(pArea->get_ref_device().LogicToPixel(Size(24, 12), MapMode(MapUnit::MapAppFont))); + ValueSet::SetDrawingArea(pArea); + pArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); + SetHelpId(HID_BMPMASK_CTL_QCOL_1); + } + void onEditColor(); +}; + +MaskSet::MaskSet(SvxBmpMask* pMask) + : ValueSet(nullptr) + , pSvxBmpMask(pMask) +{ +} + +void MaskSet::Select() +{ + ValueSet::Select(); + + pSvxBmpMask->onSelect( this ); +} + +void MaskSet::GetFocus() +{ + ValueSet::GetFocus(); + SelectItem( 1 ); + pSvxBmpMask->onSelect( this ); +} + +bool MaskSet::KeyInput( const KeyEvent& rKEvt ) +{ + bool bRet = false; + + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + // if the key has a modifier we don't care + if( aCode.GetModifier() ) + { + bRet = ValueSet::KeyInput( rKEvt ); + } + else + { + // check for keys that interests us + switch ( aCode.GetCode() ) + { + case KEY_SPACE: + onEditColor(); + bRet = true; + break; + default: + bRet = ValueSet::KeyInput( rKEvt ); + } + } + return bRet; +} + +void MaskSet::onEditColor() +{ + SvColorDialog aColorDlg; + + aColorDlg.SetColor(GetItemColor(1)); + + if (aColorDlg.Execute(pSvxBmpMask->GetFrameWeld())) + SetItemColor(1, aColorDlg.GetColor()); +} + +class MaskData +{ + VclPtr<SvxBmpMask> pMask; + bool bIsReady; + bool bExecState; + SfxBindings& rBindings; + +public: + MaskData( SvxBmpMask* pBmpMask, SfxBindings& rBind ); + + bool IsCbxReady() const { return bIsReady; } + void SetExecState( bool bState ) { bExecState = bState; } + bool IsExecReady() const { return bExecState; } + + DECL_LINK( PipetteHdl, const OUString&, void ); + DECL_LINK( CbxHdl, weld::Toggleable&, void); + DECL_LINK( CbxTransHdl, weld::Toggleable&, void ); + DECL_LINK( FocusLbHdl, weld::Widget&, void ); + DECL_LINK(ExecHdl, weld::Button&, void); +}; + + +MaskData::MaskData( SvxBmpMask* pBmpMask, SfxBindings& rBind ) : + + pMask ( pBmpMask ), + bIsReady ( false ), + bExecState ( false ), + rBindings ( rBind ) + +{ +} + +IMPL_LINK( MaskData, PipetteHdl, const OUString&, rId, void ) +{ + SfxBoolItem aBItem( SID_BMPMASK_PIPETTE, + pMask->m_xTbxPipette->get_item_active(rId) ); + + rBindings.GetDispatcher()->ExecuteList(SID_BMPMASK_PIPETTE, OWN_CALLMODE, + { &aBItem }); +} + +IMPL_LINK( MaskData, CbxHdl, weld::Toggleable&, rCbx, void ) +{ + bIsReady = pMask->m_xCbx1->get_active() || pMask->m_xCbx2->get_active() || + pMask->m_xCbx3->get_active() || pMask->m_xCbx4->get_active(); + + if ( bIsReady && IsExecReady() ) + pMask->m_xBtnExec->set_sensitive(true); + else + pMask->m_xBtnExec->set_sensitive(false); + + // When a checkbox is checked, the pipette is enabled + if ( !rCbx.get_active() ) + return; + + MaskSet* pSet = nullptr; + + if (&rCbx == pMask->m_xCbx1.get()) + pSet = pMask->m_xQSet1.get(); + else if (&rCbx == pMask->m_xCbx2.get()) + pSet = pMask->m_xQSet2.get(); + else if (&rCbx == pMask->m_xCbx3.get()) + pSet = pMask->m_xQSet3.get(); + else // if ( &rCbx == pMask->m_xCbx4 ) + pSet = pMask->m_xQSet4.get(); + + pSet->SelectItem( 1 ); + pSet->Select(); + + pMask->m_xTbxPipette->set_item_active("pipette", true); + PipetteHdl("pipette"); +} + +IMPL_LINK( MaskData, CbxTransHdl, weld::Toggleable&, rCbx, void ) +{ + bIsReady = rCbx.get_active(); + if ( bIsReady ) + { + pMask->m_xQSet1->Disable(); + pMask->m_xQSet2->Disable(); + pMask->m_xQSet3->Disable(); + pMask->m_xQSet4->Disable(); + pMask->m_xCtlPipette->Disable(); + pMask->m_xCbx1->set_sensitive(false); + pMask->m_xSp1->set_sensitive(false); + pMask->m_xCbx2->set_sensitive(false); + pMask->m_xSp2->set_sensitive(false); + pMask->m_xCbx3->set_sensitive(false); + pMask->m_xSp3->set_sensitive(false); + pMask->m_xCbx4->set_sensitive(false); + pMask->m_xSp4->set_sensitive(false); + pMask->m_xTbxPipette->set_sensitive(false); + + pMask->m_xLbColor1->set_sensitive(false); + pMask->m_xLbColor2->set_sensitive(false); + pMask->m_xLbColor3->set_sensitive(false); + pMask->m_xLbColor4->set_sensitive(false); + pMask->m_xLbColorTrans->set_sensitive(true); + } + else + { + pMask->m_xQSet1->Enable(); + pMask->m_xQSet2->Enable(); + pMask->m_xQSet3->Enable(); + pMask->m_xQSet4->Enable(); + pMask->m_xCtlPipette->Enable(); + pMask->m_xCbx1->set_sensitive(true); + pMask->m_xSp1->set_sensitive(true); + pMask->m_xCbx2->set_sensitive(true); + pMask->m_xSp2->set_sensitive(true); + pMask->m_xCbx3->set_sensitive(true); + pMask->m_xSp3->set_sensitive(true); + pMask->m_xCbx4->set_sensitive(true); + pMask->m_xSp4->set_sensitive(true); + pMask->m_xTbxPipette->set_sensitive(true); + + pMask->m_xLbColor1->set_sensitive(true); + pMask->m_xLbColor2->set_sensitive(true); + pMask->m_xLbColor3->set_sensitive(true); + pMask->m_xLbColor4->set_sensitive(true); + pMask->m_xLbColorTrans->set_sensitive(false); + + bIsReady = pMask->m_xCbx1->get_active() || pMask->m_xCbx2->get_active() || + pMask->m_xCbx3->get_active() || pMask->m_xCbx4->get_active(); + } + + if ( bIsReady && IsExecReady() ) + pMask->m_xBtnExec->set_sensitive(true); + else + pMask->m_xBtnExec->set_sensitive(false); +} + +IMPL_LINK( MaskData, FocusLbHdl, weld::Widget&, rLb, void ) +{ + pMask->m_xQSet1->SelectItem( &rLb == &pMask->m_xLbColor1->get_widget() ? 1 : 0 /* , false */ ); + pMask->m_xQSet2->SelectItem( &rLb == &pMask->m_xLbColor2->get_widget() ? 1 : 0 /* , false */ ); + pMask->m_xQSet3->SelectItem( &rLb == &pMask->m_xLbColor3->get_widget() ? 1 : 0 /* , false */ ); + pMask->m_xQSet4->SelectItem( &rLb == &pMask->m_xLbColor4->get_widget() ? 1 : 0 /* , false */ ); +} + +IMPL_LINK_NOARG(MaskData, ExecHdl, weld::Button&, void) +{ + SfxBoolItem aBItem( SID_BMPMASK_EXEC, true ); + rBindings.GetDispatcher()->ExecuteList(SID_BMPMASK_EXEC, OWN_CALLMODE, + { &aBItem }); +} + +void BmpColorWindow::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& /*Rect*/) +{ + rRenderContext.Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR); + rRenderContext.SetLineColor(aColor); + rRenderContext.SetFillColor(aColor); + rRenderContext.DrawRect(tools::Rectangle(Point(), GetOutputSizePixel())); + rRenderContext.Pop(); +} + +SvxBmpMaskSelectItem::SvxBmpMaskSelectItem( SvxBmpMask& rMask, + SfxBindings& rBindings ) : + SfxControllerItem ( SID_BMPMASK_EXEC, rBindings ), + rBmpMask ( rMask) +{ +} + +void SvxBmpMaskSelectItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState /*eState*/, + const SfxPoolItem* pItem ) +{ + if ( ( nSID == SID_BMPMASK_EXEC ) && pItem ) + { + const SfxBoolItem* pStateItem = dynamic_cast<const SfxBoolItem*>( pItem ); + assert(pStateItem); // SfxBoolItem expected + if (pStateItem) + rBmpMask.SetExecState( pStateItem->GetValue() ); + } +} + +SvxBmpMaskChildWindow::SvxBmpMaskChildWindow(vcl::Window* pParent_, sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo) + : SfxChildWindow(pParent_, nId) +{ + VclPtr<SvxBmpMask> pDlg = VclPtr<SvxBmpMask>::Create(pBindings, this, pParent_); + + SetWindow( pDlg ); + + pDlg->Initialize( pInfo ); +} + +SvxBmpMask::SvxBmpMask(SfxBindings *pBindinx, SfxChildWindow *pCW, vcl::Window* pParent) + : SfxDockingWindow(pBindinx, pCW, pParent, "DockingColorReplace", + "svx/ui/dockingcolorreplace.ui") + , m_xTbxPipette(m_xBuilder->weld_toolbar("toolbar")) + , m_xCtlPipette(new BmpColorWindow) + , m_xCtlPipetteWin(new weld::CustomWeld(*m_xBuilder, "toolcolor", *m_xCtlPipette)) + , m_xBtnExec(m_xBuilder->weld_button("replace")) + , m_xCbx1(m_xBuilder->weld_check_button("cbx1")) + , m_xQSet1(new MaskSet(this)) + , m_xQSetWin1(new weld::CustomWeld(*m_xBuilder, "qset1", *m_xQSet1)) + , m_xSp1(m_xBuilder->weld_metric_spin_button("tol1", FieldUnit::PERCENT)) + , m_xLbColor1(new ColorListBox(m_xBuilder->weld_menu_button("color1"), [this]{ return GetFrameWeld(); })) + , m_xCbx2(m_xBuilder->weld_check_button("cbx2")) + , m_xQSet2(new MaskSet(this)) + , m_xQSetWin2(new weld::CustomWeld(*m_xBuilder, "qset2", *m_xQSet2)) + , m_xSp2(m_xBuilder->weld_metric_spin_button("tol2", FieldUnit::PERCENT)) + , m_xLbColor2(new ColorListBox(m_xBuilder->weld_menu_button("color2"), [this]{ return GetFrameWeld(); })) + , m_xCbx3(m_xBuilder->weld_check_button("cbx3")) + , m_xQSet3(new MaskSet(this)) + , m_xQSetWin3(new weld::CustomWeld(*m_xBuilder, "qset3", *m_xQSet3)) + , m_xSp3(m_xBuilder->weld_metric_spin_button("tol3", FieldUnit::PERCENT)) + , m_xLbColor3(new ColorListBox(m_xBuilder->weld_menu_button("color3"), [this]{ return GetFrameWeld(); })) + , m_xCbx4(m_xBuilder->weld_check_button("cbx4")) + , m_xQSet4(new MaskSet(this)) + , m_xQSetWin4(new weld::CustomWeld(*m_xBuilder, "qset4", *m_xQSet4)) + , m_xSp4(m_xBuilder->weld_metric_spin_button("tol4", FieldUnit::PERCENT)) + , m_xLbColor4(new ColorListBox(m_xBuilder->weld_menu_button("color4"), [this]{ return GetFrameWeld(); })) + , m_xCbxTrans(m_xBuilder->weld_check_button("cbx5")) + , m_xLbColorTrans(new ColorListBox(m_xBuilder->weld_menu_button("color5"), [this]{ return GetFrameWeld(); })) + , m_xData(new MaskData(this, *pBindinx)) + , aPipetteColor(COL_WHITE) + , aSelItem(*this, *pBindinx) +{ + SetText(SvxResId(RID_SVXDLG_BMPMASK_STR_TITLE)); + + m_xLbColor1->SetSlotId(SID_BMPMASK_COLOR); + m_xLbColor2->SetSlotId(SID_BMPMASK_COLOR); + m_xLbColor3->SetSlotId(SID_BMPMASK_COLOR); + m_xLbColor4->SetSlotId(SID_BMPMASK_COLOR); + + m_xLbColorTrans->SelectEntry(COL_BLACK); + m_xLbColor1->SelectEntry(COL_TRANSPARENT); + m_xLbColor2->SelectEntry(COL_TRANSPARENT); + m_xLbColor3->SelectEntry(COL_TRANSPARENT); + m_xLbColor4->SelectEntry(COL_TRANSPARENT); + + m_xTbxPipette->connect_clicked( LINK( m_xData.get(), MaskData, PipetteHdl ) ); + m_xBtnExec->connect_clicked( LINK( m_xData.get(), MaskData, ExecHdl ) ); + + m_xCbx1->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) ); + m_xCbx2->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) ); + m_xCbx3->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) ); + m_xCbx4->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) ); + m_xCbxTrans->connect_toggled( LINK( m_xData.get(), MaskData, CbxTransHdl ) ); + + SetAccessibleNames (); + + m_xLbColor1->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) ); + m_xLbColor2->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) ); + m_xLbColor3->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) ); + m_xLbColor4->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) ); + m_xLbColorTrans->set_sensitive(false); + + OUString sColorPalette (SvxResId( RID_SVXDLG_BMPMASK_STR_PALETTE)); + OUString sColorPaletteN; + + m_xQSet1->SetStyle( m_xQSet1->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER ); + m_xQSet1->SetColCount(); + m_xQSet1->SetLineCount( 1 ); + sColorPaletteN = sColorPalette + " 1"; + m_xQSet1->InsertItem( 1, aPipetteColor, sColorPaletteN); + m_xQSet1->SelectItem( 1 ); + + m_xQSet2->SetStyle( m_xQSet2->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER ); + m_xQSet2->SetColCount(); + m_xQSet2->SetLineCount( 1 ); + sColorPaletteN = sColorPalette + " 2"; + m_xQSet2->InsertItem( 1, aPipetteColor, sColorPaletteN); + m_xQSet2->SelectItem( 0 ); + + m_xQSet3->SetStyle( m_xQSet3->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER ); + m_xQSet3->SetColCount(); + m_xQSet3->SetLineCount( 1 ); + sColorPaletteN = sColorPalette + " 3"; + m_xQSet3->InsertItem( 1, aPipetteColor, sColorPaletteN); + m_xQSet3->SelectItem( 0 ); + + m_xQSet4->SetStyle( m_xQSet4->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER ); + m_xQSet4->SetColCount(); + m_xQSet4->SetLineCount( 1 ); + sColorPaletteN = sColorPalette + " 4"; + m_xQSet4->InsertItem( 1, aPipetteColor, sColorPaletteN); + m_xQSet4->SelectItem( 0 ); + + m_xQSet1->Show(); + m_xQSet2->Show(); + m_xQSet3->Show(); + m_xQSet4->Show(); +} + +SvxBmpMask::~SvxBmpMask() +{ + disposeOnce(); +} + +void SvxBmpMask::dispose() +{ + m_xQSetWin1.reset(); + m_xQSet1.reset(); + m_xQSetWin2.reset(); + m_xQSet2.reset(); + m_xQSetWin3.reset(); + m_xQSet3.reset(); + m_xQSetWin4.reset(); + m_xQSet4.reset(); + m_xCtlPipetteWin.reset(); + m_xCtlPipette.reset(); + m_xData.reset(); + m_xTbxPipette.reset(); + m_xBtnExec.reset(); + m_xCbx1.reset(); + m_xSp1.reset(); + m_xLbColor1.reset(); + m_xCbx2.reset(); + m_xSp2.reset(); + m_xLbColor2.reset(); + m_xCbx3.reset(); + m_xSp3.reset(); + m_xLbColor3.reset(); + m_xCbx4.reset(); + m_xSp4.reset(); + m_xLbColor4.reset(); + m_xCbxTrans.reset(); + m_xLbColorTrans.reset(); + aSelItem.dispose(); + SfxDockingWindow::dispose(); +} + +/** is called by a MaskSet when it is selected */ +void SvxBmpMask::onSelect( const MaskSet* pSet ) +{ + // now deselect all other value sets + if( pSet != m_xQSet1.get() ) + m_xQSet1->SelectItem( 0 ); + + if( pSet != m_xQSet2.get() ) + m_xQSet2->SelectItem( 0 ); + + if( pSet != m_xQSet3.get() ) + m_xQSet3->SelectItem( 0 ); + + if( pSet != m_xQSet4.get() ) + m_xQSet4->SelectItem( 0 ); +} + +bool SvxBmpMask::Close() +{ + SfxBoolItem aItem2( SID_BMPMASK_PIPETTE, false ); + GetBindings().GetDispatcher()->ExecuteList(SID_BMPMASK_PIPETTE, + OWN_CALLMODE, { &aItem2 }); + + return SfxDockingWindow::Close(); +} + +void SvxBmpMask::SetColor( const Color& rColor ) +{ + aPipetteColor = rColor; + m_xCtlPipette->SetColor( aPipetteColor ); +} + +void SvxBmpMask::PipetteClicked() +{ + if( m_xQSet1->GetSelectedItemId() == 1 ) + { + m_xCbx1->set_active(true); + m_xData->CbxHdl(*m_xCbx1); + m_xQSet1->SetItemColor( 1, aPipetteColor ); + m_xQSet1->SetFormat(); + } + else if( m_xQSet2->GetSelectedItemId() == 1 ) + { + m_xCbx2->set_active(true); + m_xData->CbxHdl(*m_xCbx2); + m_xQSet2->SetItemColor( 1, aPipetteColor ); + m_xQSet2->SetFormat(); + } + else if( m_xQSet3->GetSelectedItemId() == 1 ) + { + m_xCbx3->set_active(true); + m_xData->CbxHdl(*m_xCbx3); + m_xQSet3->SetItemColor( 1, aPipetteColor ); + m_xQSet3->SetFormat(); + } + else if( m_xQSet4->GetSelectedItemId() == 1 ) + { + m_xCbx4->set_active(true); + m_xData->CbxHdl(*m_xCbx4); + m_xQSet4->SetItemColor( 1, aPipetteColor ); + m_xQSet4->SetFormat(); + } + + m_xTbxPipette->set_item_active("pipette", false); + m_xData->PipetteHdl("pipette"); +} + +void SvxBmpMask::SetExecState( bool bEnable ) +{ + m_xData->SetExecState( bEnable ); + + if ( m_xData->IsExecReady() && m_xData->IsCbxReady() ) + m_xBtnExec->set_sensitive(true); + else + m_xBtnExec->set_sensitive(false); +} + + +sal_uInt16 SvxBmpMask::InitColorArrays( Color* pSrcCols, Color* pDstCols, sal_uInt8* pTols ) +{ + sal_uInt16 nCount = 0; + + if ( m_xCbx1->get_active() ) + { + pSrcCols[nCount] = m_xQSet1->GetItemColor( 1 ); + pDstCols[nCount] = m_xLbColor1->GetSelectEntryColor(); + pTols[nCount++] = static_cast<sal_uInt8>(m_xSp1->get_value(FieldUnit::PERCENT)); + } + + if ( m_xCbx2->get_active() ) + { + pSrcCols[nCount] = m_xQSet2->GetItemColor( 1 ); + pDstCols[nCount] = m_xLbColor2->GetSelectEntryColor(); + pTols[nCount++] = static_cast<sal_uInt8>(m_xSp2->get_value(FieldUnit::PERCENT)); + } + + if ( m_xCbx3->get_active() ) + { + pSrcCols[nCount] = m_xQSet3->GetItemColor( 1 ); + pDstCols[nCount] = m_xLbColor3->GetSelectEntryColor(); + pTols[nCount++] = static_cast<sal_uInt8>(m_xSp3->get_value(FieldUnit::PERCENT)); + } + + if ( m_xCbx4->get_active() ) + { + pSrcCols[nCount] = m_xQSet4->GetItemColor( 1 ); + pDstCols[nCount] = m_xLbColor4->GetSelectEntryColor(); + pTols[nCount++] = static_cast<sal_uInt8>(m_xSp4->get_value(FieldUnit::PERCENT)); + } + + return nCount; +} + +void SvxBmpMask::ImpMask( BitmapEx& rBitmap ) +{ + Color pSrcCols[4]; + Color pDstCols[4]; + sal_uInt8 pTols[4]; + const sal_uInt16 nCount = InitColorArrays( pSrcCols, pDstCols, pTols ); + + EnterWait(); + rBitmap.Replace( pSrcCols, pDstCols, nCount, pTols ); + LeaveWait(); +} + +BitmapEx SvxBmpMask::ImpMaskTransparent( const BitmapEx& rBitmapEx, const Color& rColor, const sal_uInt8 nTol ) +{ + EnterWait(); + + BitmapEx aBmpEx; + AlphaMask aMask( rBitmapEx.GetBitmap().CreateAlphaMask( rColor, nTol ) ); + + if( rBitmapEx.IsAlpha() ) + aMask.AlphaCombineOr( rBitmapEx.GetAlphaMask() ); + + aBmpEx = BitmapEx( rBitmapEx.GetBitmap(), aMask ); + LeaveWait(); + + return aBmpEx; +} + + +Animation SvxBmpMask::ImpMask( const Animation& rAnimation ) +{ + Animation aAnimation( rAnimation ); + Color pSrcCols[4]; + Color pDstCols[4]; + sal_uInt8 pTols[4]; + InitColorArrays( pSrcCols, pDstCols, pTols ); + sal_uInt16 nAnimationCount = aAnimation.Count(); + + for( sal_uInt16 i = 0; i < nAnimationCount; i++ ) + { + AnimationFrame aAnimationFrame( aAnimation.Get( i ) ); + aAnimationFrame.maBitmapEx = Mask(aAnimationFrame.maBitmapEx).GetBitmapEx(); + aAnimation.Replace(aAnimationFrame, i); + } + + return aAnimation; +} + + +GDIMetaFile SvxBmpMask::ImpMask( const GDIMetaFile& rMtf ) +{ + GDIMetaFile aMtf; + Color pSrcCols[4]; + Color pDstCols[4]; + sal_uInt8 pTols[4]; + sal_uInt16 nCount = InitColorArrays( pSrcCols, pDstCols, pTols ); + + // If no color is selected, we copy only the Mtf + if( !nCount ) + aMtf = rMtf; + else + { + bool pTrans[4]; + Color aCol; + tools::Long nR; + tools::Long nG; + tools::Long nB; + std::unique_ptr<tools::Long[]> pMinR(new tools::Long[nCount]); + std::unique_ptr<tools::Long[]> pMaxR(new tools::Long[nCount]); + std::unique_ptr<tools::Long[]> pMinG(new tools::Long[nCount]); + std::unique_ptr<tools::Long[]> pMaxG(new tools::Long[nCount]); + std::unique_ptr<tools::Long[]> pMinB(new tools::Long[nCount]); + std::unique_ptr<tools::Long[]> pMaxB(new tools::Long[nCount]); + sal_uInt16 i; + + aMtf.SetPrefSize( rMtf.GetPrefSize() ); + aMtf.SetPrefMapMode( rMtf.GetPrefMapMode() ); + + // Prepare Color comparison array + for( i = 0; i < nCount; i++ ) + { + tools::Long nTol = ( pTols[i] * 255 ) / 100; + + tools::Long nVal = static_cast<tools::Long>(pSrcCols[i].GetRed()); + pMinR[i] = std::max( nVal - nTol, tools::Long(0) ); + pMaxR[i] = std::min( nVal + nTol, tools::Long(255) ); + + nVal = static_cast<tools::Long>(pSrcCols[i].GetGreen()); + pMinG[i] = std::max( nVal - nTol, tools::Long(0) ); + pMaxG[i] = std::min( nVal + nTol, tools::Long(255) ); + + nVal = static_cast<tools::Long>(pSrcCols[i].GetBlue()); + pMinB[i] = std::max( nVal - nTol, tools::Long(0) ); + pMaxB[i] = std::min( nVal + nTol, tools::Long(255) ); + + pTrans[ i ] = (pDstCols[ i ] == COL_TRANSPARENT); + } + + // Investigate actions and if necessary replace colors + for( size_t nAct = 0, nActCount = rMtf.GetActionSize(); nAct < nActCount; nAct++ ) + { + MetaAction* pAction = rMtf.GetAction( nAct ); + + bool bReplace = false; + + switch( pAction->GetType() ) + { + case MetaActionType::PIXEL: + { + MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction); + + aCol = pAct->GetColor(); + TEST_COLS(); + + if( bReplace ) + pAct = new MetaPixelAction( pAct->GetPoint(), aCol ); + + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::LINECOLOR: + { + MetaLineColorAction* pAct = static_cast<MetaLineColorAction*>(pAction); + + aCol = pAct->GetColor(); + TEST_COLS(); + + if( bReplace ) + pAct = new MetaLineColorAction( aCol, !pTrans[ i ] ); + + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::FILLCOLOR: + { + MetaFillColorAction* pAct = static_cast<MetaFillColorAction*>(pAction); + + aCol = pAct->GetColor(); + TEST_COLS(); + + if( bReplace ) + pAct = new MetaFillColorAction( aCol, !pTrans[ i ] ); + + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::TEXTCOLOR: + { + MetaTextColorAction* pAct = static_cast<MetaTextColorAction*>(pAction); + + aCol = pAct->GetColor(); + TEST_COLS(); + + if( bReplace ) + pAct = new MetaTextColorAction( aCol ); + + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::TEXTFILLCOLOR: + { + MetaTextFillColorAction* pAct = static_cast<MetaTextFillColorAction*>(pAction); + + aCol = pAct->GetColor(); + TEST_COLS(); + + if( bReplace ) + pAct = new MetaTextFillColorAction( aCol, !pTrans[ i ] ); + + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::FONT: + { + MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction); + vcl::Font aFont( pAct->GetFont() ); + + aCol = aFont.GetColor(); + TEST_COLS(); + + if( bReplace ) + { + aFont.SetColor( aCol ); + pAct = new MetaFontAction( std::move(aFont) ); + } + + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::WALLPAPER: + { + MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction); + Wallpaper aWall( pAct->GetWallpaper() ); + + aCol = aWall.GetColor(); + TEST_COLS(); + + if( bReplace ) + { + aWall.SetColor( aCol ); + pAct = new MetaWallpaperAction( pAct->GetRect(), std::move(aWall) ); + } + + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::BMP: + { + MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction); + const Bitmap aBmp( Mask(BitmapEx(pAct->GetBitmap())).GetBitmapEx().GetBitmap() ); + + pAct = new MetaBmpAction( pAct->GetPoint(), aBmp ); + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::BMPSCALE: + { + MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction); + const Bitmap aBmp( Mask(BitmapEx(pAct->GetBitmap())).GetBitmapEx().GetBitmap() ); + + pAct = new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(), aBmp ); + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::BMPSCALEPART: + { + MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction); + const Bitmap aBmp( Mask(BitmapEx(pAct->GetBitmap())).GetBitmapEx().GetBitmap() ); + + pAct = new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(), + pAct->GetSrcPoint(), pAct->GetSrcSize(), aBmp ); + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::BMPEX: + { + MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction); + const BitmapEx aBmpEx( Mask( pAct->GetBitmapEx() ).GetBitmapEx() ); + + pAct = new MetaBmpExAction( pAct->GetPoint(), aBmpEx ); + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::BMPEXSCALE: + { + MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction); + const BitmapEx aBmpEx( Mask( pAct->GetBitmapEx() ).GetBitmapEx() ); + + pAct = new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(), aBmpEx ); + aMtf.AddAction( pAct ); + } + break; + + case MetaActionType::BMPEXSCALEPART: + { + MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction); + const BitmapEx aBmpEx( Mask( pAct->GetBitmapEx() ).GetBitmapEx() ); + + pAct = new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(), + pAct->GetSrcPoint(), pAct->GetSrcSize(), aBmpEx ); + aMtf.AddAction( pAct ); + } + break; + + default: + { + aMtf.AddAction( pAction ); + } + break; + } + } + } + + LeaveWait(); + + return aMtf; +} + + +Animation SvxBmpMask::ImpReplaceTransparency( const Animation& rAnim, const Color& rColor ) +{ + Animation aAnimation( rAnim ); + sal_uInt16 nAnimationCount = aAnimation.Count(); + + for( sal_uInt16 i = 0; i < nAnimationCount; i++ ) + { + AnimationFrame aAnimationFrame(aAnimation.Get(i)); + aAnimationFrame.maBitmapEx.ReplaceTransparency(rColor); + aAnimation.Replace(aAnimationFrame, i); + } + + return aAnimation; +} + + +GDIMetaFile SvxBmpMask::ImpReplaceTransparency( const GDIMetaFile& rMtf, const Color& rColor ) +{ + ScopedVclPtrInstance< VirtualDevice > pVDev; + GDIMetaFile aMtf; + const MapMode& rPrefMap = rMtf.GetPrefMapMode(); + const Size& rPrefSize = rMtf.GetPrefSize(); + const size_t nActionCount = rMtf.GetActionSize(); + + pVDev->EnableOutput( false ); + aMtf.Record( pVDev ); + aMtf.SetPrefSize( rPrefSize ); + aMtf.SetPrefMapMode( rPrefMap ); + pVDev->SetLineColor( rColor ); + pVDev->SetFillColor( rColor ); + + // retrieve one action at the time; first + // set the whole area to the replacement color. + pVDev->DrawRect( tools::Rectangle( rPrefMap.GetOrigin(), rPrefSize ) ); + for ( size_t i = 0; i < nActionCount; i++ ) + { + MetaAction* pAct = rMtf.GetAction( i ); + aMtf.AddAction( pAct ); + } + + aMtf.Stop(); + aMtf.WindStart(); + + return aMtf; +} + +GDIMetaFile SvxBmpMask::GetMetaFile(const Graphic& rGraphic) +{ + // Replace transparency? + if (m_xCbxTrans->get_active()) + return ImpReplaceTransparency(rGraphic.GetGDIMetaFile(), m_xLbColorTrans->GetSelectEntryColor()); + return ImpMask(rGraphic.GetGDIMetaFile()); +} + +Graphic SvxBmpMask::Mask( const Graphic& rGraphic ) +{ + Graphic aGraphic( rGraphic ); + const Color aReplColor( m_xLbColorTrans->GetSelectEntryColor() ); + + switch( rGraphic.GetType() ) + { + case GraphicType::Bitmap: + { + if( rGraphic.IsAnimated() ) + { + // Replace transparency? + if ( m_xCbxTrans->get_active() ) + aGraphic = ImpReplaceTransparency( rGraphic.GetAnimation(), aReplColor ); + else + aGraphic = ImpMask( rGraphic.GetAnimation() ); + } + else + { + // Replace transparency? + if( m_xCbxTrans->get_active() ) + { + BitmapEx aBmpEx = aGraphic.GetBitmapEx(); + aBmpEx.ReplaceTransparency(aReplColor); + aGraphic = aBmpEx; + } + else + { + Color pSrcCols[4]; + Color pDstCols[4]; + sal_uInt8 pTols[4]; + sal_uInt16 nCount = InitColorArrays( pSrcCols, pDstCols, pTols ); + + if( nCount ) + { + // first set all transparent colors + for( sal_uInt16 i = 0; i < nCount; i++ ) + { + // Do we have a transparent color? + if (pDstCols[i] == COL_TRANSPARENT) + { + BitmapEx aBmpEx( ImpMaskTransparent( aGraphic.GetBitmapEx(), + pSrcCols[ i ], pTols[ i ] ) ); + const Size aSize( aBmpEx.GetSizePixel() ); + + if( aSize.Width() && aSize.Height() ) + aGraphic = aBmpEx; + } + } + + // now replace it again with the normal colors + BitmapEx aBitmapEx( aGraphic.GetBitmapEx() ); + if ( aBitmapEx.GetSizePixel().Width() && aBitmapEx.GetSizePixel().Height() ) + { + ImpMask( aBitmapEx ); + aGraphic = Graphic( aBitmapEx ); + } + } + } + } + } + break; + + case GraphicType::GdiMetafile: + { + GDIMetaFile aMtf(GetMetaFile(rGraphic)); + Size aSize( aMtf.GetPrefSize() ); + if ( aSize.Width() && aSize.Height() ) + aGraphic = Graphic( aMtf ); + else + aGraphic = rGraphic; + } + break; + + default: + aGraphic = rGraphic; + break; + } + + if( aGraphic != rGraphic ) + { + aGraphic.SetPrefSize( rGraphic.GetPrefSize() ); + aGraphic.SetPrefMapMode( rGraphic.GetPrefMapMode() ); + } + + return aGraphic; +} + +bool SvxBmpMask::IsEyedropping() const +{ + return m_xTbxPipette->get_item_active("pipette"); +} + +/** Set an accessible name for the source color check boxes. Without this + the lengthy description is read. +*/ +void SvxBmpMask::SetAccessibleNames() +{ + // set the accessible name for valueset + OUString sColorPalette (SvxResId( RID_SVXDLG_BMPMASK_STR_PALETTE)); + OUString sColorPaletteN; + + sColorPaletteN = sColorPalette + " 1"; + m_xQSet1->SetText (sColorPaletteN); + sColorPaletteN = sColorPalette + " 2"; + m_xQSet2->SetText (sColorPaletteN); + sColorPaletteN = sColorPalette + " 3"; + m_xQSet3->SetText (sColorPaletteN); + sColorPaletteN = sColorPalette + " 4"; + m_xQSet4->SetText (sColorPaletteN); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/_contdlg.cxx b/svx/source/dialog/_contdlg.cxx new file mode 100644 index 0000000000..da70779569 --- /dev/null +++ b/svx/source/dialog/_contdlg.cxx @@ -0,0 +1,675 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <tools/helpers.hxx> +#include <svl/eitem.hxx> +#include <sfx2/ctrlitem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/module.hxx> +#include <unotools/localedatawrapper.hxx> + +#include <svx/svxids.hrc> +#include <svx/contdlg.hxx> +#include "contimp.hxx" +#include "contwnd.hxx" +#include <svx/svdopath.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> +#include "dlgunit.hxx" +#include <vcl/weld.hxx> + +SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(SvxContourDlgChildWindow, SID_CONTOUR_DLG); + +SvxContourDlgItem::SvxContourDlgItem( SvxSuperContourDlg& rContourDlg, SfxBindings& rBindings ) : + SfxControllerItem ( SID_CONTOUR_EXEC, rBindings ), + rDlg ( rContourDlg ) +{ +} + +void SvxContourDlgItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState /*eState*/, const SfxPoolItem* pItem ) +{ + if ( pItem && ( SID_CONTOUR_EXEC == nSID ) ) + { + const SfxBoolItem* pStateItem = dynamic_cast<const SfxBoolItem*>( pItem ); + assert(pStateItem); //SfxBoolItem expected + if (pStateItem) + rDlg.SetExecState(!pStateItem->GetValue()); + } +} + +SvxContourDlgChildWindow::SvxContourDlgChildWindow(vcl::Window* _pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo const * pInfo) + : SfxChildWindow( _pParent, nId ) +{ + SetController(std::make_shared<SvxContourDlg>(pBindings, this, _pParent->GetFrameWeld())); + SvxContourDlg* pDlg = static_cast<SvxContourDlg*>(GetController().get()); + pDlg->Initialize( pInfo ); +} + +SvxContourDlg::SvxContourDlg(SfxBindings* _pBindings, SfxChildWindow* pCW, + weld::Window* _pParent) + : SfxModelessDialogController(_pBindings, pCW, _pParent, "svx/ui/floatingcontour.ui", "FloatingContour") + , m_xImpl(std::make_unique<SvxSuperContourDlg>(*m_xBuilder, *m_xDialog, _pBindings)) +{ +} + +SvxContourDlg::~SvxContourDlg() +{ +} + +tools::PolyPolygon SvxContourDlg::CreateAutoContour( const Graphic& rGraphic, + const tools::Rectangle* pRect ) +{ + Bitmap aBmp; + bool bContourEdgeDetect = false; + + if ( rGraphic.GetType() == GraphicType::Bitmap ) + { + if( rGraphic.IsAnimated() ) + { + ScopedVclPtrInstance< VirtualDevice > pVDev; + MapMode aTransMap; + const Animation aAnim( rGraphic.GetAnimation() ); + const Size& rSizePix = aAnim.GetDisplaySizePixel(); + const sal_uInt16 nCount = aAnim.Count(); + + if ( pVDev->SetOutputSizePixel( rSizePix ) ) + { + pVDev->SetLineColor( COL_BLACK ); + pVDev->SetFillColor( COL_BLACK ); + + for( sal_uInt16 i = 0; i < nCount; i++ ) + { + const AnimationFrame& rStepBmp = aAnim.Get( i ); + + // Push Polygon output to the right place; this is the + // offset of the sub-image within the total animation + aTransMap.SetOrigin( Point( rStepBmp.maPositionPixel.X(), rStepBmp.maPositionPixel.Y() ) ); + pVDev->SetMapMode( aTransMap ); + pVDev->DrawPolyPolygon( CreateAutoContour( rStepBmp.maBitmapEx, pRect ) ); + } + + aTransMap.SetOrigin( Point() ); + pVDev->SetMapMode( aTransMap ); + aBmp = pVDev->GetBitmap( Point(), rSizePix ); + aBmp.Convert( BmpConversion::N1BitThreshold ); + } + } + else if( rGraphic.IsTransparent() ) + aBmp = rGraphic.GetBitmapEx().GetAlphaMask().GetBitmap(); + else + { + aBmp = rGraphic.GetBitmapEx().GetBitmap(); + bContourEdgeDetect = true; + } + } + else if( rGraphic.GetType() != GraphicType::NONE ) + { + const Graphic aTmpGrf( rGraphic.GetGDIMetaFile().GetMonochromeMtf( COL_BLACK ) ); + ScopedVclPtrInstance< VirtualDevice > pVDev; + Size aSizePix( pVDev->LogicToPixel( aTmpGrf.GetPrefSize(), aTmpGrf.GetPrefMapMode() ) ); + + if( aSizePix.Width() && aSizePix.Height() && ( aSizePix.Width() > 512 || aSizePix.Height() > 512 ) ) + { + double fWH = static_cast<double>(aSizePix.Width()) / aSizePix.Height(); + + if( fWH <= 1.0 ) + { + aSizePix.setHeight(512); + aSizePix.setWidth( FRound( ( aSizePix.Height() ) * fWH ) ); + } + else + { + aSizePix.setWidth(512); + aSizePix.setHeight( FRound( ( aSizePix.Width() ) / fWH ) ); + } + } + + if( pVDev->SetOutputSizePixel( aSizePix ) ) + { + const Point aPt; + aTmpGrf.Draw(*pVDev, aPt, aSizePix); + aBmp = pVDev->GetBitmap( aPt, aSizePix ); + } + + bContourEdgeDetect = true; + } + + aBmp.SetPrefSize( rGraphic.GetPrefSize() ); + aBmp.SetPrefMapMode( rGraphic.GetPrefMapMode() ); + + return tools::PolyPolygon( BitmapEx(aBmp).GetContour( bContourEdgeDetect, pRect ) ); +} + +// Loop through to super class, no virtual Methods to not become incompatible +// due to IF changes + +const Graphic& SvxContourDlg::GetGraphic() const +{ + return m_xImpl->GetGraphic(); +} + +bool SvxContourDlg::IsGraphicChanged() const +{ + return m_xImpl->IsGraphicChanged(); +} + +tools::PolyPolygon SvxContourDlg::GetPolyPolygon() +{ + return m_xImpl->GetPolyPolygon(); +} + +const void* SvxContourDlg::GetEditingObject() const +{ + return m_xImpl->GetEditingObject(); +} + +void SvxContourDlg::Update( const Graphic& rGraphic, bool bGraphicLinked, + const tools::PolyPolygon* pPolyPoly, void* pEditingObj ) +{ + m_xImpl->UpdateGraphic( rGraphic, bGraphicLinked, pPolyPoly, pEditingObj ); +} + +SvxSuperContourDlg::SvxSuperContourDlg(weld::Builder& rBuilder, + weld::Dialog& rDialog, SfxBindings* pBindings) + : aUpdateIdle( "SvxSuperContourDlg UpdateIdle" ) + , aCreateIdle( "SvxSuperContourDlg CreateIdle" ) + , mpBindings(pBindings) + , pUpdateEditingObject( nullptr ) + , pCheckObj( nullptr ) + , aContourItem( *this, *pBindings ) + , mnGrfChanged( 0 ) + , bExecState( false ) + , bUpdateGraphicLinked( false ) + , bGraphicLinked( false ) + , m_rDialog(rDialog) + , m_xContourWnd(new ContourWindow(&rDialog)) + , m_xStbStatusColor(new StatusColor(*m_xContourWnd)) + , m_xTbx1(rBuilder.weld_toolbar("toolbar")) + , m_xMtfTolerance(rBuilder.weld_metric_spin_button("spinbutton", FieldUnit::PERCENT)) + , m_xStbStatus2(rBuilder.weld_label("statuspos")) + , m_xStbStatus3(rBuilder.weld_label("statussize")) + , m_xCancelBtn(rBuilder.weld_button("cancel")) + , m_xStbStatusColorWeld(new weld::CustomWeld(rBuilder, "statuscolor", *m_xStbStatusColor)) + , m_xContourWndWeld(new weld::CustomWeld(rBuilder, "container", *m_xContourWnd)) +{ + m_xCancelBtn->connect_clicked(LINK(this, SvxSuperContourDlg, CancelHdl)); + + m_xContourWnd->SetMousePosLink( LINK( this, SvxSuperContourDlg, MousePosHdl ) ); + m_xContourWnd->SetGraphSizeLink( LINK( this, SvxSuperContourDlg, GraphSizeHdl ) ); + m_xContourWnd->SetUpdateLink( LINK( this, SvxSuperContourDlg, StateHdl ) ); + m_xContourWnd->SetPipetteHdl( LINK( this, SvxSuperContourDlg, PipetteHdl ) ); + m_xContourWnd->SetPipetteClickHdl( LINK( this, SvxSuperContourDlg, PipetteClickHdl ) ); + m_xContourWnd->SetWorkplaceClickHdl( LINK( this, SvxSuperContourDlg, WorkplaceClickHdl ) ); + + m_xTbx1->connect_clicked( LINK( this, SvxSuperContourDlg, Tbx1ClickHdl ) ); + + m_xMtfTolerance->set_value(10, FieldUnit::PERCENT); + + aUpdateIdle.SetInvokeHandler( LINK( this, SvxSuperContourDlg, UpdateHdl ) ); + + aCreateIdle.SetPriority( TaskPriority::RESIZE ); + aCreateIdle.SetInvokeHandler( LINK( this, SvxSuperContourDlg, CreateHdl ) ); +} + +SvxSuperContourDlg::~SvxSuperContourDlg() +{ + m_xContourWnd->SetUpdateLink( Link<GraphCtrl*,void>() ); + m_xContourWnd.reset(); +} + +IMPL_LINK_NOARG(SvxSuperContourDlg, CancelHdl, weld::Button&, void) +{ + bool bRet = true; + + if (m_xTbx1->get_item_sensitive("TBI_APPLY")) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&m_rDialog, "svx/ui/querysavecontchangesdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QuerySaveContourChangesDialog")); + const short nRet = xQBox->run(); + + if ( nRet == RET_YES ) + { + SfxBoolItem aBoolItem( SID_CONTOUR_EXEC, true ); + GetBindings().GetDispatcher()->ExecuteList( + SID_CONTOUR_EXEC, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aBoolItem }); + } + else if ( nRet == RET_CANCEL ) + bRet = false; + } + + if (bRet) + m_rDialog.response(RET_CANCEL); +} + +// Enabled or disabled all Controls + +void SvxSuperContourDlg::SetExecState( bool bEnable ) +{ + bExecState = bEnable; +} + +void SvxSuperContourDlg::SetGraphic( const Graphic& rGraphic ) +{ + aUndoGraphic = aRedoGraphic = Graphic(); + aGraphic = rGraphic; + mnGrfChanged = 0; + m_xContourWnd->SetGraphic( aGraphic ); +} + +void SvxSuperContourDlg::SetPolyPolygon( const tools::PolyPolygon& rPolyPoly ) +{ + DBG_ASSERT( m_xContourWnd->GetGraphic().GetType() != GraphicType::NONE, "Graphic must've been set first!" ); + + tools::PolyPolygon aPolyPoly( rPolyPoly ); + const MapMode aMap100( MapUnit::Map100thMM ); + const MapMode aGrfMap( aGraphic.GetPrefMapMode() ); + OutputDevice* pOutDev = Application::GetDefaultDevice(); + bool bPixelMap = aGrfMap.GetMapUnit() == MapUnit::MapPixel; + + for ( sal_uInt16 j = 0, nPolyCount = aPolyPoly.Count(); j < nPolyCount; j++ ) + { + tools::Polygon& rPoly = aPolyPoly[ j ]; + + for ( sal_uInt16 i = 0, nCount = rPoly.GetSize(); i < nCount; i++ ) + { + Point& rPt = rPoly[ i ]; + + if ( !bPixelMap ) + rPt = pOutDev->LogicToPixel( rPt, aGrfMap ); + + rPt = pOutDev->PixelToLogic( rPt, aMap100 ); + } + } + + m_xContourWnd->SetPolyPolygon( aPolyPoly ); + m_xContourWnd->GetSdrModel()->SetChanged(); +} + +tools::PolyPolygon SvxSuperContourDlg::GetPolyPolygon() +{ + tools::PolyPolygon aRetPolyPoly( m_xContourWnd->GetPolyPolygon() ); + + const MapMode aMap100( MapUnit::Map100thMM ); + const MapMode aGrfMap( aGraphic.GetPrefMapMode() ); + OutputDevice* pOutDev = Application::GetDefaultDevice(); + bool bPixelMap = aGrfMap.GetMapUnit() == MapUnit::MapPixel; + + for ( sal_uInt16 j = 0, nPolyCount = aRetPolyPoly.Count(); j < nPolyCount; j++ ) + { + tools::Polygon& rPoly = aRetPolyPoly[ j ]; + + for ( sal_uInt16 i = 0, nCount = rPoly.GetSize(); i < nCount; i++ ) + { + Point& rPt = rPoly[ i ]; + + rPt = pOutDev->LogicToPixel( rPt, aMap100 ); + + if ( !bPixelMap ) + rPt = pOutDev->PixelToLogic( rPt, aGrfMap ); + } + } + + return aRetPolyPoly; +} + +void SvxSuperContourDlg::UpdateGraphic( const Graphic& rGraphic, bool _bGraphicLinked, + const tools::PolyPolygon* pPolyPoly, void* pEditingObj ) +{ + aUpdateGraphic = rGraphic; + bUpdateGraphicLinked = _bGraphicLinked; + pUpdateEditingObject = pEditingObj; + + if ( pPolyPoly ) + aUpdatePolyPoly = *pPolyPoly; + else + aUpdatePolyPoly = tools::PolyPolygon(); + + aUpdateIdle.Start(); +} + +// Click handler for ToolBox + +IMPL_LINK(SvxSuperContourDlg, Tbx1ClickHdl, const OUString&, rId, void) +{ + if (rId == "TBI_APPLY") + { + SfxBoolItem aBoolItem( SID_CONTOUR_EXEC, true ); + GetBindings().GetDispatcher()->ExecuteList( + SID_CONTOUR_EXEC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aBoolItem }); + } + else if (rId == "TBI_WORKPLACE") + { + if (m_xTbx1->get_item_active("TBI_WORKPLACE")) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&m_rDialog, "svx/ui/querydeletecontourdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QueryDeleteContourDialog")); + + if (!m_xContourWnd->IsContourChanged() || (xQBox->run() == RET_YES)) + m_xContourWnd->SetWorkplaceMode( true ); + else + m_xTbx1->set_item_active("TBI_WORKPLACE", false); + } + else + m_xContourWnd->SetWorkplaceMode( false ); + } + else if (rId == "TBI_SELECT") + { + SetActiveTool(rId); + m_xContourWnd->SetEditMode( true ); + } + else if (rId == "TBI_RECT") + { + SetActiveTool(rId); + m_xContourWnd->SetObjKind( SdrObjKind::Rectangle ); + } + else if (rId == "TBI_CIRCLE") + { + SetActiveTool(rId); + m_xContourWnd->SetObjKind( SdrObjKind::CircleOrEllipse ); + } + else if (rId == "TBI_POLY") + { + SetActiveTool(rId); + m_xContourWnd->SetObjKind( SdrObjKind::Polygon ); + } + else if (rId == "TBI_POLYEDIT") + { + m_xContourWnd->SetPolyEditMode(m_xTbx1->get_item_active("TBI_POLYEDIT") ? SID_BEZIER_MOVE : 0); + } + else if (rId == "TBI_POLYMOVE") + { + SetActivePoly(rId); + m_xContourWnd->SetPolyEditMode( SID_BEZIER_MOVE ); + } + else if (rId == "TBI_POLYINSERT") + { + SetActivePoly(rId); + m_xContourWnd->SetPolyEditMode( SID_BEZIER_INSERT ); + } + else if (rId == "TBI_POLYDELETE") + { + m_xContourWnd->GetSdrView()->DeleteMarkedPoints(); + } + else if (rId == "TBI_UNDO") + { + mnGrfChanged = mnGrfChanged ? mnGrfChanged - 1 : 0; + aRedoGraphic = aGraphic; + aGraphic = aUndoGraphic; + aUndoGraphic = Graphic(); + m_xContourWnd->SetGraphic( aGraphic, false ); + } + else if (rId == "TBI_REDO") + { + mnGrfChanged++; + aUndoGraphic = aGraphic; + aGraphic = aRedoGraphic; + aRedoGraphic = Graphic(); + m_xContourWnd->SetGraphic( aGraphic, false ); + } + else if (rId == "TBI_AUTOCONTOUR") + { + aCreateIdle.Start(); + } + else if (rId == "TBI_PIPETTE") + { + bool bPipette = m_xTbx1->get_item_active("TBI_PIPETTE"); + + if ( !bPipette ) + m_xStbStatusColor->Invalidate(); + else if ( bGraphicLinked ) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&m_rDialog, "svx/ui/queryunlinkgraphicsdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QueryUnlinkGraphicsDialog")); + + if (xQBox->run() != RET_YES) + { + bPipette = false; + m_xTbx1->set_item_active("TBI_PIPETTE", bPipette); + m_xStbStatusColor->Invalidate(); + } + } + + m_xContourWnd->SetPipetteMode( bPipette ); + } + m_xContourWnd->QueueIdleUpdate(); +} + +void SvxSuperContourDlg::SetActiveTool(std::u16string_view rId) +{ + m_xTbx1->set_item_active("TBI_SELECT", rId == u"TBI_SELECT"); + m_xTbx1->set_item_active("TBI_RECT", rId == u"TBI_RECT"); + m_xTbx1->set_item_active("TBI_CIRCLE", rId == u"TBI_CIRCLE"); + m_xTbx1->set_item_active("TBI_POLY", rId == u"TBI_POLY"); +} + +void SvxSuperContourDlg::SetActivePoly(std::u16string_view rId) +{ + m_xTbx1->set_item_active("TBI_POLYMOVE", rId == u"TBI_POLYMOVE"); + m_xTbx1->set_item_active("TBI_POLYINSERT", rId == u"TBI_POLYINSERT"); +} + +IMPL_LINK( SvxSuperContourDlg, MousePosHdl, GraphCtrl*, pWnd, void ) +{ + OUString aStr; + const FieldUnit eFieldUnit = GetBindings().GetDispatcher()->GetModule()->GetFieldUnit(); + const Point& rMousePos = pWnd->GetMousePos(); + const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() ); + const sal_Unicode cSep = rLocaleWrapper.getNumDecimalSep()[0]; + + aStr = GetUnitString( rMousePos.X(), eFieldUnit, cSep ) + + " / " + + GetUnitString( rMousePos.Y(), eFieldUnit, cSep ); + + m_xStbStatus2->set_label( aStr ); +} + +IMPL_LINK( SvxSuperContourDlg, GraphSizeHdl, GraphCtrl*, pWnd, void ) +{ + OUString aStr; + const FieldUnit eFieldUnit = GetBindings().GetDispatcher()->GetModule()->GetFieldUnit(); + const Size& rSize = pWnd->GetGraphicSize(); + const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() ); + const sal_Unicode cSep = rLocaleWrapper.getNumDecimalSep()[0]; + + aStr = GetUnitString( rSize.Width(), eFieldUnit, cSep ) + + " x " + + GetUnitString( rSize.Height(), eFieldUnit, cSep ); + + m_xStbStatus3->set_label( aStr ); +} + +IMPL_LINK_NOARG(SvxSuperContourDlg, UpdateHdl, Timer *, void) +{ + aUpdateIdle.Stop(); + + if ( pUpdateEditingObject != pCheckObj ) + { + if( !GetEditingObject() ) + m_xContourWnd->GrabFocus(); + + SetGraphic( aUpdateGraphic ); + SetPolyPolygon( aUpdatePolyPoly ); + pCheckObj = pUpdateEditingObject; + bGraphicLinked = bUpdateGraphicLinked; + + aUpdateGraphic = Graphic(); + aUpdatePolyPoly = tools::PolyPolygon(); + bUpdateGraphicLinked = false; + + m_xContourWnd->GetSdrModel()->SetChanged( false ); + } + + GetBindings().Invalidate( SID_CONTOUR_EXEC ); + m_xContourWnd->QueueIdleUpdate(); +} + +IMPL_LINK_NOARG(SvxSuperContourDlg, CreateHdl, Timer *, void) +{ + aCreateIdle.Stop(); + + const tools::Rectangle aWorkRect = m_xContourWnd->GetDrawingArea()->get_ref_device().LogicToPixel( + m_xContourWnd->GetWorkRect(), MapMode( MapUnit::Map100thMM)); + + const Graphic& rGraphic = m_xContourWnd->GetGraphic(); + const bool bValid = aWorkRect.Left() != aWorkRect.Right() && aWorkRect.Top() != aWorkRect.Bottom(); + + weld::WaitObject aWaitObj(&m_rDialog); + SetPolyPolygon( SvxContourDlg::CreateAutoContour( rGraphic, bValid ? &aWorkRect : nullptr ) ); +} + +IMPL_LINK( SvxSuperContourDlg, StateHdl, GraphCtrl*, pWnd, void ) +{ + const SdrObject* pObj = pWnd->GetSelectedSdrObject(); + const SdrView* pView = pWnd->GetSdrView(); + const bool bPolyEdit = ( pObj != nullptr ) && dynamic_cast<const SdrPathObj*>( pObj) != nullptr; + const bool bDrawEnabled = !(bPolyEdit && m_xTbx1->get_item_active("TBI_POLYEDIT")); + const bool bPipette = m_xTbx1->get_item_active("TBI_PIPETTE"); + const bool bWorkplace = m_xTbx1->get_item_active("TBI_WORKPLACE"); + const bool bDontHide = !( bPipette || bWorkplace ); + const bool bBitmap = pWnd->GetGraphic().GetType() == GraphicType::Bitmap; + + m_xTbx1->set_item_sensitive("TBI_APPLY", bDontHide && bExecState && pWnd->IsChanged()); + + m_xTbx1->set_item_sensitive("TBI_WORKPLACE", !bPipette && bDrawEnabled); + + m_xTbx1->set_item_sensitive("TBI_SELECT", bDontHide && bDrawEnabled); + m_xTbx1->set_item_sensitive("TBI_RECT", bDontHide && bDrawEnabled); + m_xTbx1->set_item_sensitive("TBI_CIRCLE", bDontHide && bDrawEnabled); + m_xTbx1->set_item_sensitive("TBI_POLY", bDontHide && bDrawEnabled); + + m_xTbx1->set_item_sensitive("TBI_POLYEDIT", bDontHide && bPolyEdit); + m_xTbx1->set_item_sensitive("TBI_POLYMOVE", bDontHide && !bDrawEnabled); + m_xTbx1->set_item_sensitive("TBI_POLYINSERT", bDontHide && !bDrawEnabled); + m_xTbx1->set_item_sensitive("TBI_POLYDELETE", bDontHide && !bDrawEnabled && pView->IsDeleteMarkedPointsPossible()); + + m_xTbx1->set_item_sensitive("TBI_AUTOCONTOUR", bDontHide && bDrawEnabled); + m_xTbx1->set_item_sensitive("TBI_PIPETTE", !bWorkplace && bDrawEnabled && bBitmap); + + m_xTbx1->set_item_sensitive("TBI_UNDO", bDontHide && aUndoGraphic.GetType() != GraphicType::NONE); + m_xTbx1->set_item_sensitive("TBI_REDO", bDontHide && aRedoGraphic.GetType() != GraphicType::NONE); + + if ( bPolyEdit ) + { + switch( pWnd->GetPolyEditMode() ) + { + case SID_BEZIER_MOVE: + SetActivePoly(u"TBI_POLYMOVE"); + break; + case SID_BEZIER_INSERT: + SetActivePoly(u"TBI_POLYINSERT"); + break; + default: + break; + } + } + else + { + m_xTbx1->set_item_active("TBI_POLYEDIT", false); + SetActivePoly(u"TBI_POLYMOVE"); + pWnd->SetPolyEditMode( 0 ); + } +} + +IMPL_LINK_NOARG(SvxSuperContourDlg, PipetteHdl, ContourWindow&, void) +{ + m_xStbStatusColor->Invalidate(); +} + +void StatusColor::Paint(vcl::RenderContext& rDevice, const tools::Rectangle&) +{ + const Color& rOldLineColor = rDevice.GetLineColor(); + const Color& rOldFillColor = rDevice.GetFillColor(); + + tools::Rectangle aRect(Point(), GetOutputSizePixel()); + const Color& rColor = m_rWnd.GetPipetteColor(); + + rDevice.SetLineColor(rColor); + rDevice.SetFillColor(rColor); + + aRect.AdjustLeft(4 ); + aRect.AdjustTop(4 ); + aRect.AdjustRight( -4 ); + aRect.AdjustBottom( -4 ); + + rDevice.DrawRect( aRect ); + + rDevice.SetLineColor(rOldLineColor); + rDevice.SetFillColor(rOldFillColor); +} + +IMPL_LINK( SvxSuperContourDlg, PipetteClickHdl, ContourWindow&, rWnd, void ) +{ + if ( rWnd.IsClickValid() ) + { + const Color& rColor = rWnd.GetPipetteColor(); + + weld::WaitObject aWaitObj(&m_rDialog); + + if( aGraphic.GetType() == GraphicType::Bitmap ) + { + const tools::Long nTol = static_cast<tools::Long>(m_xMtfTolerance->get_value(FieldUnit::PERCENT) * 255 / 100); + + AlphaMask aMask = aGraphic.GetBitmapEx().GetBitmap().CreateAlphaMask( rColor, nTol ); + + if( aGraphic.IsTransparent() ) + aMask.AlphaCombineOr( aGraphic.GetBitmapEx().GetAlphaMask() ); + + if( !aMask.IsEmpty() ) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&m_rDialog, "svx/ui/querynewcontourdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QueryNewContourDialog")); + + bool bNewContour; + + aRedoGraphic = Graphic(); + aUndoGraphic = aGraphic; + Bitmap aBmp = aGraphic.GetBitmapEx().GetBitmap(); + aGraphic = Graphic( BitmapEx( aBmp, aMask ) ); + mnGrfChanged++; + + bNewContour = (xQBox->run() == RET_YES); + rWnd.SetGraphic( aGraphic, bNewContour ); + + if( bNewContour ) + aCreateIdle.Start(); + } + } + } + + m_xTbx1->set_item_active("TBI_PIPETTE", false); + rWnd.SetPipetteMode( false ); + m_xStbStatusColor->Invalidate(); +} + +IMPL_LINK( SvxSuperContourDlg, WorkplaceClickHdl, ContourWindow&, rWnd, void ) +{ + m_xTbx1->set_item_active("TBI_WORKPLACE", false); + m_xTbx1->set_item_active("TBI_SELECT", true); + rWnd.SetWorkplaceMode( false ); + + m_xContourWnd->QueueIdleUpdate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/charmap.cxx b/svx/source/dialog/charmap.cxx new file mode 100644 index 0000000000..dcb1205584 --- /dev/null +++ b/svx/source/dialog/charmap.cxx @@ -0,0 +1,1967 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <utility> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/fontcharmap.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> + +#include <svx/ucsubset.hxx> + + +#include <svx/strings.hrc> + +#include <svx/charmap.hxx> +#include <svx/dialmgr.hxx> + +#include <charmapacc.hxx> +#include <uiobject.hxx> + +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp> +#include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp> +#include <officecfg/Office/Common.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <unicode/uchar.h> +#include <vcl/textview.hxx> +#include <rtl/ustrbuf.hxx> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +sal_uInt32& SvxShowCharSet::getSelectedChar() +{ + static sal_uInt32 cSelectedChar = ' '; // keeps selected character over app lifetime + return cSelectedChar; +} + +FactoryFunction SvxShowCharSet::GetUITestFactory() const +{ + return SvxShowCharSetUIObject::create; +} + +SvxShowCharSet::SvxShowCharSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow, const VclPtr<VirtualDevice>& rVirDev) + : mxVirDev(rVirDev) + , mxScrollArea(std::move(pScrolledWindow)) + , nX(0) + , nY(0) + , maFontSize(0, 0) + , mbRecalculateFont(true) + , mbUpdateForeground(true) + , mbUpdateBackground(true) +{ + init(); +} + +void SvxShowCharSet::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + + Size aSize(COLUMN_COUNT * pDrawingArea->get_approximate_digit_width() * 5.25, + ROW_COUNT * pDrawingArea->get_text_height() * 2); + + nX = aSize.Width() / COLUMN_COUNT; + nY = aSize.Height() / ROW_COUNT; + + // tdf#121232 set a size request that will result in a 0 m_nXGap by default + mxScrollArea->set_size_request(COLUMN_COUNT * nX + mxScrollArea->get_scroll_thickness() + 2, + ROW_COUNT * nY); +} + +void SvxShowCharSet::init() +{ + nSelectedIndex = -1; // TODO: move into init list when it is no longer static + m_nXGap = 0; + m_nYGap = 0; + + mxScrollArea->connect_vadjustment_changed(LINK(this, SvxShowCharSet, VscrollHdl)); + getFavCharacterList(); + // other settings depend on selected font => see RecalculateFont + + bDrag = false; +} + +void SvxShowCharSet::Resize() +{ + mbRecalculateFont = true; +} + +void SvxShowCharSet::GetFocus() +{ + SelectIndex(nSelectedIndex, true); +} + +void SvxShowCharSet::LoseFocus() +{ + SelectIndex(nSelectedIndex); +} + +bool SvxShowCharSet::MouseButtonDown(const MouseEvent& rMEvt) +{ + if ( rMEvt.IsLeft() ) + { + if ( rMEvt.GetClicks() == 1 ) + { + GrabFocus(); + bDrag = true; + CaptureMouse(); + + int nIndex = PixelToMapIndex( rMEvt.GetPosPixel() ); + // Fire the focus event + SelectIndex( nIndex, true); + } + + if ( !(rMEvt.GetClicks() % 2) ) + aDoubleClkHdl.Call( this ); + + return true; + } + + return CustomWidgetController::MouseButtonDown(rMEvt); +} + +bool SvxShowCharSet::MouseButtonUp(const MouseEvent& rMEvt) +{ + if ( bDrag && rMEvt.IsLeft() ) + { + // released mouse over character map + if ( tools::Rectangle(Point(), GetOutputSizePixel()).Contains(rMEvt.GetPosPixel())) + aSelectHdl.Call( this ); + ReleaseMouse(); + bDrag = false; + } + + return true; +} + +bool SvxShowCharSet::MouseMove(const MouseEvent& rMEvt) +{ + if ( rMEvt.IsLeft() && bDrag ) + { + Point aPos = rMEvt.GetPosPixel(); + Size aSize = GetOutputSizePixel(); + + if ( aPos.X() < 0 ) + aPos.setX( 0 ); + else if ( aPos.X() > aSize.Width()-5 ) + aPos.setX( aSize.Width()-5 ); + if ( aPos.Y() < 0 ) + aPos.setY( 0 ); + else if ( aPos.Y() > aSize.Height()-5 ) + aPos.setY( aSize.Height()-5 ); + + int nIndex = PixelToMapIndex( aPos ); + // Fire the focus event. + SelectIndex( nIndex, true ); + } + + return true; +} + +bool SvxShowCharSet::Command(const CommandEvent& rCEvt) +{ + if (rCEvt.GetCommand() == CommandEventId::ContextMenu) + { + Point aPosition; + if (rCEvt.IsMouseEvent()) + { + aPosition = rCEvt.GetMousePosPixel(); + int nIndex = PixelToMapIndex(aPosition); + // Fire the focus event + SelectIndex(nIndex, true); + } + else + { + svx::SvxShowCharSetItem* pItem = ImplGetItem(nSelectedIndex); + if (!pItem) + return true; + + // position context menu at centre of currently selected item + aPosition = MapIndexToPixel(nSelectedIndex); + aPosition.AdjustX(pItem->maRect.GetWidth() / 2); + aPosition.AdjustY(pItem->maRect.GetHeight() / 2); + } + createContextMenu(aPosition); + return true; + } + return weld::CustomWidgetController::Command(rCEvt); +} + +sal_uInt16 SvxShowCharSet::GetRowPos(sal_uInt16 _nPos) +{ + return _nPos / COLUMN_COUNT ; +} + +void SvxShowCharSet::getFavCharacterList() +{ + maFavCharList.clear(); + maFavCharFontList.clear(); + //retrieve recent character list + css::uno::Sequence< OUString > rFavCharList( officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterList::get() ); + comphelper::sequenceToContainer(maFavCharList, rFavCharList); + + //retrieve recent character font list + css::uno::Sequence< OUString > rFavCharFontList( officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterFontList::get() ); + comphelper::sequenceToContainer(maFavCharFontList, rFavCharFontList); +} + +bool SvxShowCharSet::isFavChar(std::u16string_view sTitle, std::u16string_view rFont) +{ + assert(maFavCharList.size() == maFavCharFontList.size()); + for (size_t i = 0; i < maFavCharList.size(); i++) + { + if (maFavCharList[i] == sTitle && maFavCharFontList[i] == rFont) + return true; + } + return false; +} + +void SvxShowCharSet::createContextMenu(const Point& rPosition) +{ + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetDrawingArea(), "svx/ui/charsetmenu.ui")); + std::unique_ptr<weld::Menu> xItemMenu(xBuilder->weld_menu("charsetmenu")); + + sal_UCS4 cChar = GetSelectCharacter(); + OUString aOUStr( &cChar, 1 ); + if (isFavChar(aOUStr, mxVirDev->GetFont().GetFamilyName()) || maFavCharList.size() >= 16) + xItemMenu->set_visible("add", false); + else + xItemMenu->set_visible("remove", false); + + ContextMenuSelect(xItemMenu->popup_at_rect(GetDrawingArea(), tools::Rectangle(rPosition, Size(1,1)))); + GrabFocus(); + Invalidate(); +} + +void SvxShowCharSet::ContextMenuSelect(std::u16string_view rIdent) +{ + sal_UCS4 cChar = GetSelectCharacter(); + OUString aOUStr(&cChar, 1); + + if (rIdent == u"insert") + aDoubleClkHdl.Call(this); + else if (rIdent == u"add" || rIdent == u"remove") + { + updateFavCharacterList(aOUStr, mxVirDev->GetFont().GetFamilyName()); + aFavClickHdl.Call(this); + } + else if (rIdent == u"copy") + CopyToClipboard(aOUStr); +} + +void SvxShowCharSet::CopyToClipboard(const OUString& rOUStr) +{ + css::uno::Reference<css::datatransfer::clipboard::XClipboard> xClipboard = + css::datatransfer::clipboard::SystemClipboard::create(comphelper::getProcessComponentContext()); + + if (!xClipboard.is()) + return; + + rtl::Reference<TETextDataObject> pDataObj = new TETextDataObject(rOUStr); + + try + { + xClipboard->setContents( pDataObj, nullptr ); + + css::uno::Reference<css::datatransfer::clipboard::XFlushableClipboard> xFlushableClipboard(xClipboard, css::uno::UNO_QUERY); + if( xFlushableClipboard.is() ) + xFlushableClipboard->flushClipboard(); + } + catch( const css::uno::Exception& ) + { + } +} + +void SvxShowCharSet::updateFavCharacterList(const OUString& sTitle, const OUString& rFont) +{ + if (isFavChar(sTitle, rFont)) + { + assert(maFavCharList.size() == maFavCharFontList.size()); + auto fontIt = maFavCharFontList.begin(); + for (auto charIt = maFavCharList.begin(); charIt != maFavCharList.end(); charIt++) + { + if (*charIt == sTitle && *fontIt == rFont) + { + maFavCharList.erase(charIt); + maFavCharFontList.erase(fontIt); + break; + } + fontIt++; + } + } + else + { + if (maFavCharList.size() == 16) + { + maFavCharList.pop_back(); + maFavCharFontList.pop_back(); + } + + maFavCharList.push_back(sTitle); + maFavCharFontList.push_back(rFont); + } + + css::uno::Sequence< OUString > aFavCharList(maFavCharList.size()); + auto aFavCharListRange = asNonConstRange(aFavCharList); + css::uno::Sequence< OUString > aFavCharFontList(maFavCharFontList.size()); + auto aFavCharFontListRange = asNonConstRange(aFavCharFontList); + + for (size_t i = 0; i < maFavCharList.size(); ++i) + { + aFavCharListRange[i] = maFavCharList[i]; + aFavCharFontListRange[i] = maFavCharFontList[i]; + } + + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterList::set(aFavCharList, batch); + officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterFontList::set(aFavCharFontList, batch); + batch->commit(); +} + +sal_uInt16 SvxShowCharSet::GetColumnPos(sal_uInt16 _nPos) +{ + return _nPos % COLUMN_COUNT ; +} + +int SvxShowCharSet::FirstInView() const +{ + return mxScrollArea->vadjustment_get_value() * COLUMN_COUNT; +} + +int SvxShowCharSet::LastInView() const +{ + sal_uInt32 nIndex = FirstInView(); + nIndex += ROW_COUNT * COLUMN_COUNT - 1; + sal_uInt32 nCompare = mxFontCharMap->GetCharCount() - 1; + if (nIndex > nCompare) + nIndex = nCompare; + return nIndex; +} + +Point SvxShowCharSet::MapIndexToPixel( int nIndex ) const +{ + const int nBase = FirstInView(); + int x = ((nIndex - nBase) % COLUMN_COUNT) * nX; + int y = ((nIndex - nBase) / COLUMN_COUNT) * nY; + return Point( x + m_nXGap, y + m_nYGap ); +} + + +int SvxShowCharSet::PixelToMapIndex( const Point& point) const +{ + int nBase = FirstInView(); + assert(nX != 0); + int x = nX == 0 ? 0 : (point.X() - m_nXGap)/nX; + assert(nY != 0); + int y = nY == 0 ? 0 : (point.Y() - m_nYGap)/nY; + return (nBase + x + y * COLUMN_COUNT); +} + +bool SvxShowCharSet::KeyInput(const KeyEvent& rKEvt) +{ + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + if (aCode.GetModifier()) + return false; + + bool bRet = true; + + int tmpSelected = nSelectedIndex; + + switch (aCode.GetCode()) + { + case KEY_RETURN: + m_aReturnKeypressHdl.Call(this); + return true; + case KEY_SPACE: + aDoubleClkHdl.Call(this); + return true; + case KEY_LEFT: + --tmpSelected; + break; + case KEY_RIGHT: + ++tmpSelected; + break; + case KEY_UP: + tmpSelected -= COLUMN_COUNT; + break; + case KEY_DOWN: + tmpSelected += COLUMN_COUNT; + break; + case KEY_PAGEUP: + tmpSelected -= ROW_COUNT * COLUMN_COUNT; + break; + case KEY_PAGEDOWN: + tmpSelected += ROW_COUNT * COLUMN_COUNT; + break; + case KEY_HOME: + tmpSelected = 0; + break; + case KEY_END: + tmpSelected = mxFontCharMap->GetCharCount() - 1; + break; + case KEY_TAB: // some fonts have a character at these unicode control codes + case KEY_ESCAPE: + tmpSelected = - 1; // mark as invalid + bRet = false; + break; + default: + { + sal_UCS4 cChar = rKEvt.GetCharCode(); + sal_UCS4 cNext = mxFontCharMap->GetNextChar(cChar - 1); + tmpSelected = mxFontCharMap->GetIndexFromChar(cNext); + if (tmpSelected < 0 || (cChar != cNext)) + { + tmpSelected = - 1; // mark as invalid + bRet = false; + } + break; + } + } + + if ( tmpSelected >= 0 ) + { + SelectIndex( tmpSelected, true ); + aPreSelectHdl.Call( this ); + } + + return bRet; +} + +void SvxShowCharSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + InitSettings(rRenderContext); + RecalculateFont(rRenderContext); + DrawChars_Impl(rRenderContext, FirstInView(), LastInView()); +} + +void SvxShowCharSet::SetFont( const vcl::Font& rFont ) +{ + maFont = rFont; + mbRecalculateFont = true; + Invalidate(); +} + +void SvxShowCharSet::DeSelect() +{ + Invalidate(); +} + +// stretch a grid rectangle if it's at the edge to fill unused space +tools::Rectangle SvxShowCharSet::getGridRectangle(const Point &rPointUL, const Size &rOutputSize) const +{ + tools::Long x = rPointUL.X() - 1; + tools::Long y = rPointUL.Y() - 1; + Point aPointUL(x+1, y+1); + Size aGridSize(nX-1, nY-1); + + tools::Long nXDistFromLeft = x - m_nXGap; + if (nXDistFromLeft <= 1) + { + aPointUL.setX( 1 ); + aGridSize.AdjustWidth(m_nXGap + nXDistFromLeft ); + } + tools::Long nXDistFromRight = rOutputSize.Width() - m_nXGap - nX - x; + if (nXDistFromRight <= 1) + aGridSize.AdjustWidth(m_nXGap + nXDistFromRight ); + + tools::Long nXDistFromTop = y - m_nYGap; + if (nXDistFromTop <= 1) + { + aPointUL.setY( 1 ); + aGridSize.AdjustHeight(m_nYGap + nXDistFromTop ); + } + tools::Long nXDistFromBottom = rOutputSize.Height() - m_nYGap - nY - y; + if (nXDistFromBottom <= 1) + aGridSize.AdjustHeight(m_nYGap + nXDistFromBottom ); + + return tools::Rectangle(aPointUL, aGridSize); +} + +void SvxShowCharSet::DrawChars_Impl(vcl::RenderContext& rRenderContext, int n1, int n2) +{ + if (n1 > LastInView() || n2 < FirstInView()) + return; + + Size aOutputSize(GetOutputSizePixel()); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + const Color aWindowTextColor(rStyleSettings.GetFieldTextColor()); + Color aHighlightColor(rStyleSettings.GetHighlightColor()); + Color aHighlightTextColor(rStyleSettings.GetHighlightTextColor()); + Color aFaceColor(rStyleSettings.GetFaceColor()); + Color aLightColor(rStyleSettings.GetLightColor()); + Color aShadowColor(rStyleSettings.GetShadowColor()); + + int i; + rRenderContext.SetLineColor(aShadowColor); + for (i = 1; i < COLUMN_COUNT; ++i) + { + rRenderContext.DrawLine(Point(nX * i + m_nXGap, 0), + Point(nX * i + m_nXGap, aOutputSize.Height())); + } + for (i = 1; i < ROW_COUNT; ++i) + { + rRenderContext.DrawLine(Point(0, nY * i + m_nYGap), + Point(aOutputSize.Width(), nY * i + m_nYGap)); + } + + int nTextHeight = rRenderContext.GetTextHeight(); + tools::Rectangle aBoundRect; + for (i = n1; i <= n2; ++i) + { + sal_UCS4 charValue = GetCharFromIndex(i); + + if (charValue == 0) + continue; + + OUString aCharStr(&charValue, 1); + + Point pix = MapIndexToPixel(i); + int x = pix.X(); + int y = pix.Y(); + + int nTextWidth = rRenderContext.GetTextWidth(aCharStr); + int tx = x + (nX - nTextWidth + 1) / 2; + int ty = y + (nY - nTextHeight + 1) / 2; + Point aPointTxTy(tx, ty); + + // adjust position before it gets out of bounds + if (rRenderContext.GetTextBoundRect(aBoundRect, aCharStr) && !aBoundRect.IsEmpty()) + { + // zero advance width => use ink width to center glyph + if (!nTextWidth) + { + aPointTxTy.setX( x - aBoundRect.Left() + (nX - aBoundRect.GetWidth() + 1) / 2 ); + } + + aBoundRect += aPointTxTy; + + // shift back vertically if needed + int nYLDelta = aBoundRect.Top() - y; + int nYHDelta = (y + nY) - aBoundRect.Bottom(); + if (nYLDelta <= 0) + aPointTxTy.AdjustY( -(nYLDelta - 1) ); + else if (nYHDelta <= 0) + aPointTxTy.AdjustY(nYHDelta - 1 ); + + // shift back horizontally if needed + int nXLDelta = aBoundRect.Left() - x; + int nXHDelta = (x + nX) - aBoundRect.Right(); + if (nXLDelta <= 0) + aPointTxTy.AdjustX( -(nXLDelta - 1) ); + else if (nXHDelta <= 0) + aPointTxTy.AdjustX(nXHDelta - 1 ); + } + + // tdf#109214 - highlight the favorite characters + if (isFavChar(aCharStr, mxVirDev->GetFont().GetFamilyName())) + { + const Color aLineCol = rRenderContext.GetLineColor(); + rRenderContext.SetLineColor(aHighlightColor); + rRenderContext.SetFillColor(COL_TRANSPARENT); + // Outer border + rRenderContext.DrawRect(tools::Rectangle(Point(x - 1, y - 1), Size(nX + 3, nY + 3)), 1, 1); + // Inner border + rRenderContext.DrawRect(tools::Rectangle(Point(x, y), Size(nX + 1, nY + 1)), 1, 1); + rRenderContext.SetLineColor(aLineCol); + } + + Color aTextCol = rRenderContext.GetTextColor(); + if (i != nSelectedIndex) + { + rRenderContext.SetTextColor(aWindowTextColor); + rRenderContext.DrawText(aPointTxTy, aCharStr); + } + else + { + Color aLineCol = rRenderContext.GetLineColor(); + Color aFillCol = rRenderContext.GetFillColor(); + rRenderContext.SetLineColor(); + Point aPointUL(x + 1, y + 1); + if (HasFocus()) + { + rRenderContext.SetFillColor(aHighlightColor); + rRenderContext.DrawRect(getGridRectangle(aPointUL, aOutputSize)); + + rRenderContext.SetTextColor(aHighlightTextColor); + rRenderContext.DrawText(aPointTxTy, aCharStr); + } + else + { + rRenderContext.SetFillColor(aFaceColor); + rRenderContext.DrawRect(getGridRectangle(aPointUL, aOutputSize)); + + rRenderContext.SetLineColor(aLightColor); + rRenderContext.DrawLine(aPointUL, Point(x + nX - 1, y + 1)); + rRenderContext.DrawLine(aPointUL, Point(x + 1, y + nY - 1)); + + rRenderContext.SetLineColor(aShadowColor); + rRenderContext.DrawLine(Point(x + 1, y + nY - 1), Point(x + nX - 1, y + nY - 1)); + rRenderContext.DrawLine(Point(x + nX - 1, y + nY - 1), Point(x + nX - 1, y + 1)); + + rRenderContext.DrawText(aPointTxTy, aCharStr); + } + rRenderContext.SetLineColor(aLineCol); + rRenderContext.SetFillColor(aFillCol); + } + rRenderContext.SetTextColor(aTextCol); + } + + // tdf#141319 - mark empty/unused cells + if (n2 - n1 < ROW_COUNT * COLUMN_COUNT) + { + rRenderContext.SetFillColor(rStyleSettings.GetDisableColor()); + for (i = n2 - n1 + 1; i < ROW_COUNT * COLUMN_COUNT; i++) + { + rRenderContext.DrawRect( + tools::Rectangle(MapIndexToPixel(i + n1), Size(nX + 2, nY + 2))); + } + } +} + + +void SvxShowCharSet::InitSettings(vcl::RenderContext& rRenderContext) +{ + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + + if (mbUpdateForeground) + { + rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor()); + mbUpdateForeground = false; + } + + if (mbUpdateBackground) + { + rRenderContext.SetBackground(rStyleSettings.GetWindowColor()); + rRenderContext.Erase(); + mbUpdateBackground = false; + } + + vcl::Font aFont(maFont); + aFont.SetWeight(WEIGHT_LIGHT); + aFont.SetAlignment(ALIGN_TOP); + aFont.SetFontSize(maFontSize); + aFont.SetTransparent(true); + rRenderContext.SetFont(aFont); +} + +sal_UCS4 SvxShowCharSet::GetSelectCharacter() const +{ + if( nSelectedIndex >= 0 ) + getSelectedChar() = mxFontCharMap->GetCharFromIndex( nSelectedIndex ); + return getSelectedChar(); +} + +sal_UCS4 SvxShowCharSet::GetCharFromIndex(int index) const +{ + return mxFontCharMap->GetCharFromIndex(index); +} + +void SvxShowCharSet::RecalculateFont(vcl::RenderContext& rRenderContext) +{ + if (!mbRecalculateFont) + return; + + // save last selected unicode + if (nSelectedIndex >= 0) + getSelectedChar() = mxFontCharMap->GetCharFromIndex(nSelectedIndex); + + Size aSize(GetOutputSizePixel()); + + vcl::Font aFont = maFont; + aFont.SetWeight(WEIGHT_LIGHT); + aFont.SetAlignment(ALIGN_TOP); + int nFontHeight = (aSize.Height() - 5) * 2 / (3 * ROW_COUNT); + maFontSize = rRenderContext.PixelToLogic(Size(0, nFontHeight)); + aFont.SetFontSize(maFontSize); + aFont.SetTransparent(true); + rRenderContext.SetFont(aFont); + rRenderContext.GetFontCharMap(mxFontCharMap); + m_aItems.clear(); + getFavCharacterList(); + + nX = aSize.Width() / COLUMN_COUNT; + nY = aSize.Height() / ROW_COUNT; + + const int nLastRow = (mxFontCharMap->GetCharCount() - 1 + COLUMN_COUNT) / COLUMN_COUNT; + mxScrollArea->vadjustment_configure(mxScrollArea->vadjustment_get_value(), 0, nLastRow, 1, ROW_COUNT - 1, ROW_COUNT); + + // restore last selected unicode + int nMapIndex = mxFontCharMap->GetIndexFromChar(getSelectedChar()); + if (nMapIndex != nSelectedIndex) + SelectIndex(nMapIndex); + + // rearrange CharSet element in sync with nX- and nY-multiples + Size aDrawSize(nX * COLUMN_COUNT, nY * ROW_COUNT); + m_nXGap = (aSize.Width() - aDrawSize.Width()) / 2; + m_nYGap = (aSize.Height() - aDrawSize.Height()) / 2; + + mbRecalculateFont = false; +} + +void SvxShowCharSet::SelectIndex(int nNewIndex, bool bFocus) +{ + if (!mxFontCharMap.is()) + RecalculateFont(*mxVirDev); + + if( nNewIndex < 0 ) + { + // need to scroll see closest unicode + sal_uInt32 cPrev = mxFontCharMap->GetPrevChar( getSelectedChar() ); + int nMapIndex = mxFontCharMap->GetIndexFromChar( cPrev ); + int nNewPos = nMapIndex / COLUMN_COUNT; + mxScrollArea->vadjustment_set_value(nNewPos); + nSelectedIndex = bFocus ? nMapIndex+1 : -1; + Invalidate(); + } + else if( nNewIndex < FirstInView() ) + { + // need to scroll up to see selected item + int nOldPos = mxScrollArea->vadjustment_get_value(); + int nDelta = (FirstInView() - nNewIndex + COLUMN_COUNT-1) / COLUMN_COUNT; + mxScrollArea->vadjustment_set_value(nOldPos - nDelta); + nSelectedIndex = nNewIndex; + Invalidate(); + } + else if( nNewIndex > LastInView() ) + { + // need to scroll down to see selected item + int nOldPos = mxScrollArea->vadjustment_get_value(); + int nDelta = (nNewIndex - LastInView() + COLUMN_COUNT) / COLUMN_COUNT; + mxScrollArea->vadjustment_set_value(nOldPos + nDelta); + if( nNewIndex < mxFontCharMap->GetCharCount() ) + { + nSelectedIndex = nNewIndex; + Invalidate(); + } + else if (nOldPos != mxScrollArea->vadjustment_get_value()) + { + Invalidate(); + } + } + else + { + nSelectedIndex = nNewIndex; + Invalidate(); + } + + if( nSelectedIndex >= 0 ) + { + getSelectedChar() = mxFontCharMap->GetCharFromIndex( nSelectedIndex ); +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if( m_xAccessible.is() ) + { + svx::SvxShowCharSetItem* pItem = ImplGetItem(nSelectedIndex); + rtl::Reference<svx::SvxShowCharSetItemAcc> xItemAcc = pItem->GetAccessible(); + // Don't fire the focus event. + if ( bFocus ) + m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, Any(), + Any(uno::Reference<XAccessible>(xItemAcc)) ); // this call assures that m_pItem is set + else + m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS, Any(), + Any(uno::Reference<XAccessible>(xItemAcc)) ); // this call assures that m_pItem is set + + assert(pItem->m_xItem.is() && "No accessible created!"); + Any aOldAny, aNewAny; + aNewAny <<= AccessibleStateType::FOCUSED; + // Don't fire the focus event. + if ( bFocus ) + pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny ); + + aNewAny <<= AccessibleStateType::SELECTED; + pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny ); + } + aSelectHdl.Call(this); +#endif + } + aHighHdl.Call( this ); +} + +void SvxShowCharSet::OutputIndex( int nNewIndex ) +{ + SelectIndex( nNewIndex, true ); + aSelectHdl.Call( this ); +} + + +void SvxShowCharSet::SelectCharacter( sal_UCS4 cNew ) +{ + if ( !mxFontCharMap.is() ) + RecalculateFont(*mxVirDev); + + // get next available char of current font + sal_UCS4 cNext = mxFontCharMap->GetNextChar( (cNew > 0) ? cNew - 1 : cNew ); + + int nMapIndex = mxFontCharMap->GetIndexFromChar( cNext ); + SelectIndex( nMapIndex ); + // move selected item to top row if not in focus + mxScrollArea->vadjustment_set_value(nMapIndex / COLUMN_COUNT); + Invalidate(); +} + +IMPL_LINK_NOARG(SvxShowCharSet, VscrollHdl, weld::ScrolledWindow&, void) +{ + if( nSelectedIndex < FirstInView() ) + { + SelectIndex( FirstInView() + (nSelectedIndex % COLUMN_COUNT) ); + } + else if( nSelectedIndex > LastInView() ) + { +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if( m_xAccessible.is() ) + { + css::uno::Any aOldAny, aNewAny; + int nLast = LastInView(); + for ( ; nLast != nSelectedIndex; ++nLast) + { + aOldAny <<= uno::Reference<XAccessible>(ImplGetItem(nLast)->GetAccessible()); + m_xAccessible ->fireEvent( AccessibleEventId::CHILD, aOldAny, aNewAny ); + } + } +#endif + SelectIndex( (LastInView() - COLUMN_COUNT + 1) + (nSelectedIndex % COLUMN_COUNT) ); + } + + Invalidate(); +} + +SvxShowCharSet::~SvxShowCharSet() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (m_xAccessible.is()) + { + m_aItems.clear(); + m_xAccessible->clearCharSetControl(); + m_xAccessible.clear(); + } +#endif +} + +css::uno::Reference< XAccessible > SvxShowCharSet::CreateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + OSL_ENSURE(!m_xAccessible.is(),"Accessible already created!"); + m_xAccessible = new svx::SvxShowCharSetAcc(this); +#endif + return m_xAccessible; +} + +svx::SvxShowCharSetItem* SvxShowCharSet::ImplGetItem( int _nPos ) +{ + ItemsMap::iterator aFind = m_aItems.find(_nPos); + if ( aFind == m_aItems.end() ) + { +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + OSL_ENSURE(m_xAccessible.is(), "Who wants to create a child of my table without a parent?"); +#endif + auto xItem = std::make_shared<svx::SvxShowCharSetItem>(*this, + m_xAccessible.get(), sal::static_int_cast< sal_uInt16 >(_nPos)); + aFind = m_aItems.emplace(_nPos, xItem).first; + OUStringBuffer buf; + buf.appendUtf32( mxFontCharMap->GetCharFromIndex( _nPos ) ); + aFind->second->maText = buf.makeStringAndClear(); + Point pix = MapIndexToPixel( _nPos ); + aFind->second->maRect = tools::Rectangle( Point( pix.X() + 1, pix.Y() + 1 ), Size(nX-1,nY-1) ); + } + + return aFind->second.get(); +} + +sal_Int32 SvxShowCharSet::getMaxCharCount() const +{ + return mxFontCharMap->GetCharCount(); +} + +FontCharMapRef const & SvxShowCharSet::GetFontCharMap() +{ + RecalculateFont(*mxVirDev); + return mxFontCharMap; +} + +// TODO: should be moved into Font Attributes stuff +// we let it mature here though because it is currently the only use + +SubsetMap::SubsetMap( const FontCharMapRef& rxFontCharMap ) +{ + InitList(); + ApplyCharMap(rxFontCharMap); +} + +const SubsetVec& SubsetMap::GetSubsetMap() const +{ + return maSubsets; +} + +const Subset* SubsetMap::GetSubsetByUnicode( sal_UCS4 cChar ) const +{ + for (auto const& subset : maSubsets) + if( (subset.GetRangeMin() <= cChar) && (cChar <= subset.GetRangeMax()) ) + return ⊂ + return nullptr; +} + +inline Subset::Subset(sal_UCS4 nMin, sal_UCS4 nMax, OUString aName) +: mnRangeMin(nMin), mnRangeMax(nMax), maRangeName(std::move(aName)) +{ +} + +void SubsetMap::InitList() +{ + static SubsetVec s_aAllSubsets = []() + { + SubsetVec aAllSubsets; + //I wish icu had a way to give me the block ranges + for (int i = UBLOCK_BASIC_LATIN; i < UBLOCK_COUNT; ++i) + { + UBlockCode eBlock = static_cast<UBlockCode>(i); + switch (eBlock) + { + case UBLOCK_NO_BLOCK: + case UBLOCK_INVALID_CODE: + case UBLOCK_COUNT: + case UBLOCK_HIGH_SURROGATES: + case UBLOCK_HIGH_PRIVATE_USE_SURROGATES: + case UBLOCK_LOW_SURROGATES: + break; + case UBLOCK_BASIC_LATIN: + aAllSubsets.emplace_back( 0x0000, 0x007F, SvxResId(RID_SUBSETSTR_BASIC_LATIN) ); + break; + case UBLOCK_LATIN_1_SUPPLEMENT: + aAllSubsets.emplace_back( 0x0080, 0x00FF, SvxResId(RID_SUBSETSTR_LATIN_1) ); + break; + case UBLOCK_LATIN_EXTENDED_A: + aAllSubsets.emplace_back( 0x0100, 0x017F, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_A) ); + break; + case UBLOCK_LATIN_EXTENDED_B: + aAllSubsets.emplace_back( 0x0180, 0x024F, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_B) ); + break; + case UBLOCK_IPA_EXTENSIONS: + aAllSubsets.emplace_back( 0x0250, 0x02AF, SvxResId(RID_SUBSETSTR_IPA_EXTENSIONS) ); + break; + case UBLOCK_SPACING_MODIFIER_LETTERS: + aAllSubsets.emplace_back( 0x02B0, 0x02FF, SvxResId(RID_SUBSETSTR_SPACING_MODIFIERS) ); + break; + case UBLOCK_COMBINING_DIACRITICAL_MARKS: + aAllSubsets.emplace_back( 0x0300, 0x036F, SvxResId(RID_SUBSETSTR_COMB_DIACRITICAL) ); + break; + case UBLOCK_GREEK: + aAllSubsets.emplace_back( 0x0370, 0x03FF, SvxResId(RID_SUBSETSTR_BASIC_GREEK) ); + break; + case UBLOCK_CYRILLIC: + aAllSubsets.emplace_back( 0x0400, 0x04FF, SvxResId(RID_SUBSETSTR_CYRILLIC) ); + break; + case UBLOCK_ARMENIAN: + aAllSubsets.emplace_back( 0x0530, 0x058F, SvxResId(RID_SUBSETSTR_ARMENIAN) ); + break; + case UBLOCK_HEBREW: + aAllSubsets.emplace_back( 0x0590, 0x05FF, SvxResId(RID_SUBSETSTR_BASIC_HEBREW) ); + break; + case UBLOCK_ARABIC: + aAllSubsets.emplace_back( 0x0600, 0x065F, SvxResId(RID_SUBSETSTR_BASIC_ARABIC) ); + break; + case UBLOCK_SYRIAC: + aAllSubsets.emplace_back( 0x0700, 0x074F, SvxResId(RID_SUBSETSTR_SYRIAC) ); + break; + case UBLOCK_THAANA: + aAllSubsets.emplace_back( 0x0780, 0x07BF, SvxResId(RID_SUBSETSTR_THAANA) ); + break; + case UBLOCK_DEVANAGARI: + aAllSubsets.emplace_back( 0x0900, 0x097F, SvxResId(RID_SUBSETSTR_DEVANAGARI) ); + break; + case UBLOCK_BENGALI: + aAllSubsets.emplace_back( 0x0980, 0x09FF, SvxResId(RID_SUBSETSTR_BENGALI) ); + break; + case UBLOCK_GURMUKHI: + aAllSubsets.emplace_back( 0x0A00, 0x0A7F, SvxResId(RID_SUBSETSTR_GURMUKHI) ); + break; + case UBLOCK_GUJARATI: + aAllSubsets.emplace_back( 0x0A80, 0x0AFF, SvxResId(RID_SUBSETSTR_GUJARATI) ); + break; + case UBLOCK_ORIYA: + aAllSubsets.emplace_back( 0x0B00, 0x0B7F, SvxResId(RID_SUBSETSTR_ODIA) ); + break; + case UBLOCK_TAMIL: + aAllSubsets.emplace_back( 0x0B80, 0x0BFF, SvxResId(RID_SUBSETSTR_TAMIL) ); + break; + case UBLOCK_TELUGU: + aAllSubsets.emplace_back( 0x0C00, 0x0C7F, SvxResId(RID_SUBSETSTR_TELUGU) ); + break; + case UBLOCK_KANNADA: + aAllSubsets.emplace_back( 0x0C80, 0x0CFF, SvxResId(RID_SUBSETSTR_KANNADA) ); + break; + case UBLOCK_MALAYALAM: + aAllSubsets.emplace_back( 0x0D00, 0x0D7F, SvxResId(RID_SUBSETSTR_MALAYALAM) ); + break; + case UBLOCK_SINHALA: + aAllSubsets.emplace_back( 0x0D80, 0x0DFF, SvxResId(RID_SUBSETSTR_SINHALA) ); + break; + case UBLOCK_THAI: + aAllSubsets.emplace_back( 0x0E00, 0x0E7F, SvxResId(RID_SUBSETSTR_THAI) ); + break; + case UBLOCK_LAO: + aAllSubsets.emplace_back( 0x0E80, 0x0EFF, SvxResId(RID_SUBSETSTR_LAO) ); + break; + case UBLOCK_TIBETAN: + aAllSubsets.emplace_back( 0x0F00, 0x0FBF, SvxResId(RID_SUBSETSTR_TIBETAN) ); + break; + case UBLOCK_MYANMAR: + aAllSubsets.emplace_back( 0x1000, 0x109F, SvxResId(RID_SUBSETSTR_MYANMAR) ); + break; + case UBLOCK_GEORGIAN: + aAllSubsets.emplace_back( 0x10A0, 0x10FF, SvxResId(RID_SUBSETSTR_BASIC_GEORGIAN) ); + break; + case UBLOCK_HANGUL_JAMO: + aAllSubsets.emplace_back( 0x1100, 0x11FF, SvxResId(RID_SUBSETSTR_HANGUL_JAMO) ); + break; + case UBLOCK_ETHIOPIC: + aAllSubsets.emplace_back( 0x1200, 0x137F, SvxResId(RID_SUBSETSTR_ETHIOPIC) ); + break; + case UBLOCK_CHEROKEE: + aAllSubsets.emplace_back( 0x13A0, 0x13FF, SvxResId(RID_SUBSETSTR_CHEROKEE) ); + break; + case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS: + aAllSubsets.emplace_back( 0x1400, 0x167F, SvxResId(RID_SUBSETSTR_CANADIAN_ABORIGINAL) ); + break; + case UBLOCK_OGHAM: + aAllSubsets.emplace_back( 0x1680, 0x169F, SvxResId(RID_SUBSETSTR_OGHAM) ); + break; + case UBLOCK_RUNIC: + aAllSubsets.emplace_back( 0x16A0, 0x16F0, SvxResId(RID_SUBSETSTR_RUNIC) ); + break; + case UBLOCK_KHMER: + aAllSubsets.emplace_back( 0x1780, 0x17FF, SvxResId(RID_SUBSETSTR_KHMER) ); + break; + case UBLOCK_MONGOLIAN: + aAllSubsets.emplace_back( 0x1800, 0x18AF, SvxResId(RID_SUBSETSTR_MONGOLIAN) ); + break; + case UBLOCK_LATIN_EXTENDED_ADDITIONAL: + aAllSubsets.emplace_back( 0x1E00, 0x1EFF, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_ADDS) ); + break; + case UBLOCK_GREEK_EXTENDED: + aAllSubsets.emplace_back( 0x1F00, 0x1FFF, SvxResId(RID_SUBSETSTR_GREEK_EXTENDED) ); + break; + case UBLOCK_GENERAL_PUNCTUATION: + aAllSubsets.emplace_back( 0x2000, 0x206F, SvxResId(RID_SUBSETSTR_GENERAL_PUNCTUATION) ); + break; + case UBLOCK_SUPERSCRIPTS_AND_SUBSCRIPTS: + aAllSubsets.emplace_back( 0x2070, 0x209F, SvxResId(RID_SUBSETSTR_SUB_SUPER_SCRIPTS) ); + break; + case UBLOCK_CURRENCY_SYMBOLS: + aAllSubsets.emplace_back( 0x20A0, 0x20CF, SvxResId(RID_SUBSETSTR_CURRENCY_SYMBOLS) ); + break; + case UBLOCK_COMBINING_MARKS_FOR_SYMBOLS: + aAllSubsets.emplace_back( 0x20D0, 0x20FF, SvxResId(RID_SUBSETSTR_COMB_DIACRITIC_SYMS) ); + break; + case UBLOCK_LETTERLIKE_SYMBOLS: + aAllSubsets.emplace_back( 0x2100, 0x214F, SvxResId(RID_SUBSETSTR_LETTERLIKE_SYMBOLS) ); + break; + case UBLOCK_NUMBER_FORMS: + aAllSubsets.emplace_back( 0x2150, 0x218F, SvxResId(RID_SUBSETSTR_NUMBER_FORMS) ); + break; + case UBLOCK_ARROWS: + aAllSubsets.emplace_back( 0x2190, 0x21FF, SvxResId(RID_SUBSETSTR_ARROWS) ); + break; + case UBLOCK_MATHEMATICAL_OPERATORS: + aAllSubsets.emplace_back( 0x2200, 0x22FF, SvxResId(RID_SUBSETSTR_MATH_OPERATORS) ); + break; + case UBLOCK_MISCELLANEOUS_TECHNICAL: + aAllSubsets.emplace_back( 0x2300, 0x23FF, SvxResId(RID_SUBSETSTR_MISC_TECHNICAL) ); + break; + case UBLOCK_CONTROL_PICTURES: + aAllSubsets.emplace_back( 0x2400, 0x243F, SvxResId(RID_SUBSETSTR_CONTROL_PICTURES) ); + break; + case UBLOCK_OPTICAL_CHARACTER_RECOGNITION: + aAllSubsets.emplace_back( 0x2440, 0x245F, SvxResId(RID_SUBSETSTR_OPTICAL_CHAR_REC) ); + break; + case UBLOCK_ENCLOSED_ALPHANUMERICS: + aAllSubsets.emplace_back( 0x2460, 0x24FF, SvxResId(RID_SUBSETSTR_ENCLOSED_ALPHANUM) ); + break; + case UBLOCK_BOX_DRAWING: + aAllSubsets.emplace_back( 0x2500, 0x257F, SvxResId(RID_SUBSETSTR_BOX_DRAWING) ); + break; + case UBLOCK_BLOCK_ELEMENTS: + aAllSubsets.emplace_back( 0x2580, 0x259F, SvxResId(RID_SUBSETSTR_BLOCK_ELEMENTS) ); + break; + case UBLOCK_GEOMETRIC_SHAPES: + aAllSubsets.emplace_back( 0x25A0, 0x25FF, SvxResId(RID_SUBSETSTR_GEOMETRIC_SHAPES) ); + break; + case UBLOCK_MISCELLANEOUS_SYMBOLS: + aAllSubsets.emplace_back( 0x2600, 0x26FF, SvxResId(RID_SUBSETSTR_MISC_DINGBATS) ); + break; + case UBLOCK_DINGBATS: + aAllSubsets.emplace_back( 0x2700, 0x27BF, SvxResId(RID_SUBSETSTR_DINGBATS) ); + break; + case UBLOCK_BRAILLE_PATTERNS: + aAllSubsets.emplace_back( 0x2800, 0x28FF, SvxResId(RID_SUBSETSTR_BRAILLE_PATTERNS) ); + break; + case UBLOCK_CJK_RADICALS_SUPPLEMENT: + aAllSubsets.emplace_back( 0x2E80, 0x2EFF, SvxResId(RID_SUBSETSTR_CJK_RADICAL_SUPPL) ); + break; + case UBLOCK_KANGXI_RADICALS: + aAllSubsets.emplace_back( 0x2F00, 0x2FDF, SvxResId(RID_SUBSETSTR_KANGXI_RADICALS) ); + break; + case UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS: + aAllSubsets.emplace_back( 0x2FF0, 0x2FFF, SvxResId(RID_SUBSETSTR_IDEO_DESC_CHARS) ); + break; + case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION: + aAllSubsets.emplace_back( 0x3000, 0x303F, SvxResId(RID_SUBSETSTR_CJK_SYMS_PUNCTUATION) ); + break; + case UBLOCK_HIRAGANA: + aAllSubsets.emplace_back( 0x3040, 0x309F, SvxResId(RID_SUBSETSTR_HIRAGANA) ); + break; + case UBLOCK_KATAKANA: + aAllSubsets.emplace_back( 0x30A0, 0x30FF, SvxResId(RID_SUBSETSTR_KATAKANA) ); + break; + case UBLOCK_BOPOMOFO: + aAllSubsets.emplace_back( 0x3100, 0x312F, SvxResId(RID_SUBSETSTR_BOPOMOFO) ); + break; + case UBLOCK_HANGUL_COMPATIBILITY_JAMO: + aAllSubsets.emplace_back( 0x3130, 0x318F, SvxResId(RID_SUBSETSTR_HANGUL_COMPAT_JAMO) ); + break; + case UBLOCK_KANBUN: + aAllSubsets.emplace_back( 0x3190, 0x319F, SvxResId(RID_SUBSETSTR_KANBUN) ); + break; + case UBLOCK_BOPOMOFO_EXTENDED: + aAllSubsets.emplace_back( 0x31A0, 0x31BF, SvxResId(RID_SUBSETSTR_BOPOMOFO_EXTENDED) ); + break; + case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS: + aAllSubsets.emplace_back( 0x3200, 0x32FF, SvxResId(RID_SUBSETSTR_ENCLOSED_CJK_LETTERS) ); + break; + case UBLOCK_CJK_COMPATIBILITY: + aAllSubsets.emplace_back( 0x3300, 0x33FF, SvxResId(RID_SUBSETSTR_CJK_COMPATIBILITY) ); + break; + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A: + aAllSubsets.emplace_back( 0x3400, 0x4DBF, SvxResId(RID_SUBSETSTR_CJK_EXT_A_UNIFIED_IDGRAPH) ); + break; + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS: + aAllSubsets.emplace_back( 0x4E00, 0x9FA5, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDGRAPH) ); + break; + case UBLOCK_YI_SYLLABLES: + aAllSubsets.emplace_back( 0xA000, 0xA48F, SvxResId(RID_SUBSETSTR_YI_SYLLABLES) ); + break; + case UBLOCK_YI_RADICALS: + aAllSubsets.emplace_back( 0xA490, 0xA4CF, SvxResId(RID_SUBSETSTR_YI_RADICALS) ); + break; + case UBLOCK_HANGUL_SYLLABLES: + aAllSubsets.emplace_back( 0xAC00, 0xD7AF, SvxResId(RID_SUBSETSTR_HANGUL) ); + break; + case UBLOCK_PRIVATE_USE_AREA: + aAllSubsets.emplace_back( 0xE000, 0xF8FF, SvxResId(RID_SUBSETSTR_PRIVATE_USE_AREA) ); + break; + case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS: + aAllSubsets.emplace_back( 0xF900, 0xFAFF, SvxResId(RID_SUBSETSTR_CJK_COMPAT_IDGRAPHS) ); + break; + case UBLOCK_ALPHABETIC_PRESENTATION_FORMS: + aAllSubsets.emplace_back( 0xFB00, 0xFB4F, SvxResId(RID_SUBSETSTR_ALPHA_PRESENTATION) ); + break; + case UBLOCK_ARABIC_PRESENTATION_FORMS_A: + aAllSubsets.emplace_back( 0xFB50, 0xFDFF, SvxResId(RID_SUBSETSTR_ARABIC_PRESENT_A) ); + break; + case UBLOCK_COMBINING_HALF_MARKS: + aAllSubsets.emplace_back( 0xFE20, 0xFE2F, SvxResId(RID_SUBSETSTR_COMBINING_HALF_MARKS) ); + break; + case UBLOCK_CJK_COMPATIBILITY_FORMS: + aAllSubsets.emplace_back( 0xFE30, 0xFE4F, SvxResId(RID_SUBSETSTR_CJK_COMPAT_FORMS) ); + break; + case UBLOCK_SMALL_FORM_VARIANTS: + aAllSubsets.emplace_back( 0xFE50, 0xFE6F, SvxResId(RID_SUBSETSTR_SMALL_FORM_VARIANTS) ); + break; + case UBLOCK_ARABIC_PRESENTATION_FORMS_B: + aAllSubsets.emplace_back( 0xFE70, 0xFEFF, SvxResId(RID_SUBSETSTR_ARABIC_PRESENT_B) ); + break; + case UBLOCK_SPECIALS: + aAllSubsets.emplace_back( 0xFFF0, 0xFFFF, SvxResId(RID_SUBSETSTR_SPECIALS) ); + break; + case UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS: + aAllSubsets.emplace_back( 0xFF00, 0xFFEF, SvxResId(RID_SUBSETSTR_HALFW_FULLW_FORMS) ); + break; + case UBLOCK_OLD_ITALIC: + aAllSubsets.emplace_back( 0x10300, 0x1032F, SvxResId(RID_SUBSETSTR_OLD_ITALIC) ); + break; + case UBLOCK_GOTHIC: + aAllSubsets.emplace_back( 0x10330, 0x1034F, SvxResId(RID_SUBSETSTR_GOTHIC) ); + break; + case UBLOCK_DESERET: + aAllSubsets.emplace_back( 0x10400, 0x1044F, SvxResId(RID_SUBSETSTR_DESERET) ); + break; + case UBLOCK_BYZANTINE_MUSICAL_SYMBOLS: + aAllSubsets.emplace_back( 0x1D000, 0x1D0FF, SvxResId(RID_SUBSETSTR_BYZANTINE_MUSICAL_SYMBOLS) ); + break; + case UBLOCK_MUSICAL_SYMBOLS: + aAllSubsets.emplace_back( 0x1D100, 0x1D1FF, SvxResId(RID_SUBSETSTR_MUSICAL_SYMBOLS) ); + break; + case UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS: + aAllSubsets.emplace_back( 0x1D400, 0x1D7FF, SvxResId(RID_SUBSETSTR_MATHEMATICAL_ALPHANUMERIC_SYMBOLS) ); + break; + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B: + aAllSubsets.emplace_back( 0x20000, 0x2A6DF, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B) ); + break; + case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT: + aAllSubsets.emplace_back( 0x2F800, 0x2FA1F, SvxResId(RID_SUBSETSTR_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT) ); + break; + case UBLOCK_TAGS: + aAllSubsets.emplace_back( 0xE0000, 0xE007F, SvxResId(RID_SUBSETSTR_TAGS) ); + break; + case UBLOCK_CYRILLIC_SUPPLEMENTARY: + aAllSubsets.emplace_back( 0x0500, 0x052F, SvxResId(RID_SUBSETSTR_CYRILLIC_SUPPLEMENTARY) ); + break; + case UBLOCK_TAGALOG: + aAllSubsets.emplace_back( 0x1700, 0x171F, SvxResId(RID_SUBSETSTR_TAGALOG) ); + break; + case UBLOCK_HANUNOO: + aAllSubsets.emplace_back( 0x1720, 0x173F, SvxResId(RID_SUBSETSTR_HANUNOO) ); + break; + case UBLOCK_BUHID: + aAllSubsets.emplace_back( 0x1740, 0x175F, SvxResId(RID_SUBSETSTR_BUHID) ); + break; + case UBLOCK_TAGBANWA: + aAllSubsets.emplace_back( 0x1760, 0x177F, SvxResId(RID_SUBSETSTR_TAGBANWA) ); + break; + case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A: + aAllSubsets.emplace_back( 0x27C0, 0x27EF, SvxResId(RID_SUBSETSTR_MISC_MATH_SYMS_A) ); + break; + case UBLOCK_SUPPLEMENTAL_ARROWS_A: + aAllSubsets.emplace_back( 0x27F0, 0x27FF, SvxResId(RID_SUBSETSTR_SUPPL_ARROWS_A) ); + break; + case UBLOCK_SUPPLEMENTAL_ARROWS_B: + aAllSubsets.emplace_back( 0x2900, 0x297F, SvxResId(RID_SUBSETSTR_SUPPL_ARROWS_B) ); + break; + case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B: + aAllSubsets.emplace_back( 0x2980, 0x29FF, SvxResId(RID_SUBSETSTR_MISC_MATH_SYMS_B) ); + break; + case UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS: + aAllSubsets.emplace_back( 0x2A00, 0x2AFF, SvxResId(RID_SUBSETSTR_MISC_MATH_SYMS_B) ); + break; + case UBLOCK_KATAKANA_PHONETIC_EXTENSIONS: + aAllSubsets.emplace_back( 0x31F0, 0x31FF, SvxResId(RID_SUBSETSTR_KATAKANA_PHONETIC) ); + break; + case UBLOCK_VARIATION_SELECTORS: + aAllSubsets.emplace_back( 0xFE00, 0xFE0F, SvxResId(RID_SUBSETSTR_VARIATION_SELECTORS) ); + break; + case UBLOCK_SUPPLEMENTARY_PRIVATE_USE_AREA_A: + aAllSubsets.emplace_back( 0xF0000, 0xFFFFF, SvxResId(RID_SUBSETSTR_SUPPLEMENTARY_PRIVATE_USE_AREA_A) ); + break; + case UBLOCK_SUPPLEMENTARY_PRIVATE_USE_AREA_B: + aAllSubsets.emplace_back( 0x100000, 0x10FFFF, SvxResId(RID_SUBSETSTR_SUPPLEMENTARY_PRIVATE_USE_AREA_B) ); + break; + case UBLOCK_LIMBU: + aAllSubsets.emplace_back( 0x1900, 0x194F, SvxResId(RID_SUBSETSTR_LIMBU) ); + break; + case UBLOCK_TAI_LE: + aAllSubsets.emplace_back( 0x1950, 0x197F, SvxResId(RID_SUBSETSTR_TAI_LE) ); + break; + case UBLOCK_KHMER_SYMBOLS: + aAllSubsets.emplace_back( 0x19E0, 0x19FF, SvxResId(RID_SUBSETSTR_KHMER_SYMBOLS) ); + break; + case UBLOCK_PHONETIC_EXTENSIONS: + aAllSubsets.emplace_back( 0x1D00, 0x1D7F, SvxResId(RID_SUBSETSTR_PHONETIC_EXTENSIONS) ); + break; + case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_ARROWS: + aAllSubsets.emplace_back( 0x2B00, 0x2BFF, SvxResId(RID_SUBSETSTR_MISCELLANEOUS_SYMBOLS_AND_ARROWS) ); + break; + case UBLOCK_YIJING_HEXAGRAM_SYMBOLS: + aAllSubsets.emplace_back( 0x4DC0, 0x4DFF, SvxResId(RID_SUBSETSTR_YIJING_HEXAGRAM_SYMBOLS) ); + break; + case UBLOCK_LINEAR_B_SYLLABARY: + aAllSubsets.emplace_back( 0x10000, 0x1007F, SvxResId(RID_SUBSETSTR_LINEAR_B_SYLLABARY) ); + break; + case UBLOCK_LINEAR_B_IDEOGRAMS: + aAllSubsets.emplace_back( 0x10080, 0x100FF, SvxResId(RID_SUBSETSTR_LINEAR_B_IDEOGRAMS) ); + break; + case UBLOCK_AEGEAN_NUMBERS: + aAllSubsets.emplace_back( 0x10100, 0x1013F, SvxResId(RID_SUBSETSTR_AEGEAN_NUMBERS) ); + break; + case UBLOCK_UGARITIC: + aAllSubsets.emplace_back( 0x10380, 0x1039F, SvxResId(RID_SUBSETSTR_UGARITIC) ); + break; + case UBLOCK_SHAVIAN: + aAllSubsets.emplace_back( 0x10450, 0x1047F, SvxResId(RID_SUBSETSTR_SHAVIAN) ); + break; + case UBLOCK_OSMANYA: + aAllSubsets.emplace_back( 0x10480, 0x104AF, SvxResId(RID_SUBSETSTR_OSMANYA) ); + break; + case UBLOCK_CYPRIOT_SYLLABARY: + aAllSubsets.emplace_back( 0x10800, 0x1083F, SvxResId(RID_SUBSETSTR_CYPRIOT_SYLLABARY) ); + break; + case UBLOCK_TAI_XUAN_JING_SYMBOLS: + aAllSubsets.emplace_back( 0x1D300, 0x1D35F, SvxResId(RID_SUBSETSTR_TAI_XUAN_JING_SYMBOLS) ); + break; + case UBLOCK_VARIATION_SELECTORS_SUPPLEMENT: + aAllSubsets.emplace_back( 0xE0100, 0xE01EF, SvxResId(RID_SUBSETSTR_VARIATION_SELECTORS_SUPPLEMENT) ); + break; + case UBLOCK_ANCIENT_GREEK_MUSICAL_NOTATION: + aAllSubsets.emplace_back(0x1D200, 0x1D24F, SvxResId(RID_SUBSETSTR_ANCIENT_GREEK_MUSICAL_NOTATION) ); + break; + case UBLOCK_ANCIENT_GREEK_NUMBERS: + aAllSubsets.emplace_back(0x10140, 0x1018F , SvxResId(RID_SUBSETSTR_ANCIENT_GREEK_NUMBERS) ); + break; + case UBLOCK_ARABIC_SUPPLEMENT: + aAllSubsets.emplace_back(0x0750, 0x077F , SvxResId(RID_SUBSETSTR_ARABIC_SUPPLEMENT) ); + break; + case UBLOCK_BUGINESE: + aAllSubsets.emplace_back(0x1A00, 0x1A1F , SvxResId(RID_SUBSETSTR_BUGINESE) ); + break; + case UBLOCK_CJK_STROKES: + aAllSubsets.emplace_back( 0x31C0, 0x31EF, SvxResId(RID_SUBSETSTR_CJK_STROKES) ); + break; + case UBLOCK_COMBINING_DIACRITICAL_MARKS_SUPPLEMENT: + aAllSubsets.emplace_back( 0x1DC0, 0x1DFF , SvxResId(RID_SUBSETSTR_COMBINING_DIACRITICAL_MARKS_SUPPLEMENT) ); + break; + case UBLOCK_COPTIC: + aAllSubsets.emplace_back( 0x2C80, 0x2CFF , SvxResId(RID_SUBSETSTR_COPTIC) ); + break; + case UBLOCK_ETHIOPIC_EXTENDED: + aAllSubsets.emplace_back( 0x2D80, 0x2DDF , SvxResId(RID_SUBSETSTR_ETHIOPIC_EXTENDED) ); + break; + case UBLOCK_ETHIOPIC_SUPPLEMENT: + aAllSubsets.emplace_back( 0x1380, 0x139F, SvxResId(RID_SUBSETSTR_ETHIOPIC_SUPPLEMENT) ); + break; + case UBLOCK_GEORGIAN_SUPPLEMENT: + aAllSubsets.emplace_back( 0x2D00, 0x2D2F, SvxResId(RID_SUBSETSTR_GEORGIAN_SUPPLEMENT) ); + break; + case UBLOCK_GLAGOLITIC: + aAllSubsets.emplace_back( 0x2C00, 0x2C5F, SvxResId(RID_SUBSETSTR_GLAGOLITIC) ); + break; + case UBLOCK_KHAROSHTHI: + aAllSubsets.emplace_back( 0x10A00, 0x10A5F, SvxResId(RID_SUBSETSTR_KHAROSHTHI) ); + break; + case UBLOCK_MODIFIER_TONE_LETTERS: + aAllSubsets.emplace_back( 0xA700, 0xA71F, SvxResId(RID_SUBSETSTR_MODIFIER_TONE_LETTERS) ); + break; + case UBLOCK_NEW_TAI_LUE: + aAllSubsets.emplace_back( 0x1980, 0x19DF, SvxResId(RID_SUBSETSTR_NEW_TAI_LUE) ); + break; + case UBLOCK_OLD_PERSIAN: + aAllSubsets.emplace_back( 0x103A0, 0x103DF, SvxResId(RID_SUBSETSTR_OLD_PERSIAN) ); + break; + case UBLOCK_PHONETIC_EXTENSIONS_SUPPLEMENT: + aAllSubsets.emplace_back( 0x1D80, 0x1DBF, SvxResId(RID_SUBSETSTR_PHONETIC_EXTENSIONS_SUPPLEMENT) ); + break; + case UBLOCK_SUPPLEMENTAL_PUNCTUATION: + aAllSubsets.emplace_back( 0x2E00, 0x2E7F, SvxResId(RID_SUBSETSTR_SUPPLEMENTAL_PUNCTUATION) ); + break; + case UBLOCK_SYLOTI_NAGRI: + aAllSubsets.emplace_back( 0xA800, 0xA82F, SvxResId(RID_SUBSETSTR_SYLOTI_NAGRI) ); + break; + case UBLOCK_TIFINAGH: + aAllSubsets.emplace_back( 0x2D30, 0x2D7F, SvxResId(RID_SUBSETSTR_TIFINAGH) ); + break; + case UBLOCK_VERTICAL_FORMS: + aAllSubsets.emplace_back( 0xFE10, 0xFE1F, SvxResId(RID_SUBSETSTR_VERTICAL_FORMS) ); + break; + case UBLOCK_NKO: + aAllSubsets.emplace_back( 0x07C0, 0x07FF, SvxResId(RID_SUBSETSTR_NKO) ); + break; + case UBLOCK_BALINESE: + aAllSubsets.emplace_back( 0x1B00, 0x1B7F, SvxResId(RID_SUBSETSTR_BALINESE) ); + break; + case UBLOCK_LATIN_EXTENDED_C: + aAllSubsets.emplace_back( 0x2C60, 0x2C7F, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_C) ); + break; + case UBLOCK_LATIN_EXTENDED_D: + aAllSubsets.emplace_back( 0xA720, 0xA7FF, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_D) ); + break; + case UBLOCK_PHAGS_PA: + aAllSubsets.emplace_back( 0xA840, 0xA87F, SvxResId(RID_SUBSETSTR_PHAGS_PA) ); + break; + case UBLOCK_PHOENICIAN: + aAllSubsets.emplace_back( 0x10900, 0x1091F, SvxResId(RID_SUBSETSTR_PHOENICIAN) ); + break; + case UBLOCK_CUNEIFORM: + aAllSubsets.emplace_back( 0x12000, 0x123FF, SvxResId(RID_SUBSETSTR_CUNEIFORM) ); + break; + case UBLOCK_CUNEIFORM_NUMBERS_AND_PUNCTUATION: + aAllSubsets.emplace_back( 0x12400, 0x1247F, SvxResId(RID_SUBSETSTR_CUNEIFORM_NUMBERS_AND_PUNCTUATION) ); + break; + case UBLOCK_COUNTING_ROD_NUMERALS: + aAllSubsets.emplace_back( 0x1D360, 0x1D37F, SvxResId(RID_SUBSETSTR_COUNTING_ROD_NUMERALS) ); + break; + case UBLOCK_SUNDANESE: + aAllSubsets.emplace_back( 0x1B80, 0x1BBF, SvxResId(RID_SUBSETSTR_SUNDANESE) ); + break; + case UBLOCK_LEPCHA: + aAllSubsets.emplace_back( 0x1C00, 0x1C4F, SvxResId(RID_SUBSETSTR_LEPCHA) ); + break; + case UBLOCK_OL_CHIKI: + aAllSubsets.emplace_back( 0x1C50, 0x1C7F, SvxResId(RID_SUBSETSTR_OL_CHIKI) ); + break; + case UBLOCK_CYRILLIC_EXTENDED_A: + aAllSubsets.emplace_back( 0x2DE0, 0x2DFF, SvxResId(RID_SUBSETSTR_CYRILLIC_EXTENDED_A) ); + break; + case UBLOCK_VAI: + aAllSubsets.emplace_back( 0xA500, 0xA63F, SvxResId(RID_SUBSETSTR_VAI) ); + break; + case UBLOCK_CYRILLIC_EXTENDED_B: + aAllSubsets.emplace_back( 0xA640, 0xA69F, SvxResId(RID_SUBSETSTR_CYRILLIC_EXTENDED_B) ); + break; + case UBLOCK_SAURASHTRA: + aAllSubsets.emplace_back( 0xA880, 0xA8DF, SvxResId(RID_SUBSETSTR_SAURASHTRA) ); + break; + case UBLOCK_KAYAH_LI: + aAllSubsets.emplace_back( 0xA900, 0xA92F, SvxResId(RID_SUBSETSTR_KAYAH_LI) ); + break; + case UBLOCK_REJANG: + aAllSubsets.emplace_back( 0xA930, 0xA95F, SvxResId(RID_SUBSETSTR_REJANG) ); + break; + case UBLOCK_CHAM: + aAllSubsets.emplace_back( 0xAA00, 0xAA5F, SvxResId(RID_SUBSETSTR_CHAM) ); + break; + case UBLOCK_ANCIENT_SYMBOLS: + aAllSubsets.emplace_back( 0x10190, 0x101CF, SvxResId(RID_SUBSETSTR_ANCIENT_SYMBOLS) ); + break; + case UBLOCK_PHAISTOS_DISC: + aAllSubsets.emplace_back( 0x101D0, 0x101FF, SvxResId(RID_SUBSETSTR_PHAISTOS_DISC) ); + break; + case UBLOCK_LYCIAN: + aAllSubsets.emplace_back( 0x10280, 0x1029F, SvxResId(RID_SUBSETSTR_LYCIAN) ); + break; + case UBLOCK_CARIAN: + aAllSubsets.emplace_back( 0x102A0, 0x102DF, SvxResId(RID_SUBSETSTR_CARIAN) ); + break; + case UBLOCK_LYDIAN: + aAllSubsets.emplace_back( 0x10920, 0x1093F, SvxResId(RID_SUBSETSTR_LYDIAN) ); + break; + case UBLOCK_MAHJONG_TILES: + aAllSubsets.emplace_back( 0x1F000, 0x1F02F, SvxResId(RID_SUBSETSTR_MAHJONG_TILES) ); + break; + case UBLOCK_DOMINO_TILES: + aAllSubsets.emplace_back( 0x1F030, 0x1F09F, SvxResId(RID_SUBSETSTR_DOMINO_TILES) ); + break; + case UBLOCK_SAMARITAN: + aAllSubsets.emplace_back( 0x0800, 0x083F, SvxResId(RID_SUBSETSTR_SAMARITAN) ); + break; + case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED: + aAllSubsets.emplace_back( 0x18B0, 0x18FF, SvxResId(RID_SUBSETSTR_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED) ); + break; + case UBLOCK_TAI_THAM: + aAllSubsets.emplace_back( 0x1A20, 0x1AAF, SvxResId(RID_SUBSETSTR_TAI_THAM) ); + break; + case UBLOCK_VEDIC_EXTENSIONS: + aAllSubsets.emplace_back( 0x1CD0, 0x1CFF, SvxResId(RID_SUBSETSTR_VEDIC_EXTENSIONS) ); + break; + case UBLOCK_LISU: + aAllSubsets.emplace_back( 0xA4D0, 0xA4FF, SvxResId(RID_SUBSETSTR_LISU) ); + break; + case UBLOCK_BAMUM: + aAllSubsets.emplace_back( 0xA6A0, 0xA6FF, SvxResId(RID_SUBSETSTR_BAMUM) ); + break; + case UBLOCK_COMMON_INDIC_NUMBER_FORMS: + aAllSubsets.emplace_back( 0xA830, 0xA83F, SvxResId(RID_SUBSETSTR_COMMON_INDIC_NUMBER_FORMS) ); + break; + case UBLOCK_DEVANAGARI_EXTENDED: + aAllSubsets.emplace_back( 0xA8E0, 0xA8FF, SvxResId(RID_SUBSETSTR_DEVANAGARI_EXTENDED) ); + break; + case UBLOCK_HANGUL_JAMO_EXTENDED_A: + aAllSubsets.emplace_back( 0xA960, 0xA97F, SvxResId(RID_SUBSETSTR_HANGUL_JAMO_EXTENDED_A) ); + break; + case UBLOCK_JAVANESE: + aAllSubsets.emplace_back( 0xA980, 0xA9DF, SvxResId(RID_SUBSETSTR_JAVANESE) ); + break; + case UBLOCK_MYANMAR_EXTENDED_A: + aAllSubsets.emplace_back( 0xAA60, 0xAA7F, SvxResId(RID_SUBSETSTR_MYANMAR_EXTENDED_A) ); + break; + case UBLOCK_TAI_VIET: + aAllSubsets.emplace_back( 0xAA80, 0xAADF, SvxResId(RID_SUBSETSTR_TAI_VIET) ); + break; + case UBLOCK_MEETEI_MAYEK: + aAllSubsets.emplace_back( 0xABC0, 0xABFF, SvxResId(RID_SUBSETSTR_MEETEI_MAYEK) ); + break; + case UBLOCK_HANGUL_JAMO_EXTENDED_B: + aAllSubsets.emplace_back( 0xD7B0, 0xD7FF, SvxResId(RID_SUBSETSTR_HANGUL_JAMO_EXTENDED_B) ); + break; + case UBLOCK_IMPERIAL_ARAMAIC: + aAllSubsets.emplace_back( 0x10840, 0x1085F, SvxResId(RID_SUBSETSTR_IMPERIAL_ARAMAIC) ); + break; + case UBLOCK_OLD_SOUTH_ARABIAN: + aAllSubsets.emplace_back( 0x10A60, 0x10A7F, SvxResId(RID_SUBSETSTR_OLD_SOUTH_ARABIAN) ); + break; + case UBLOCK_AVESTAN: + aAllSubsets.emplace_back( 0x10B00, 0x10B3F, SvxResId(RID_SUBSETSTR_AVESTAN) ); + break; + case UBLOCK_INSCRIPTIONAL_PARTHIAN: + aAllSubsets.emplace_back( 0x10B40, 0x10B5F, SvxResId(RID_SUBSETSTR_INSCRIPTIONAL_PARTHIAN) ); + break; + case UBLOCK_INSCRIPTIONAL_PAHLAVI: + aAllSubsets.emplace_back( 0x10B60, 0x10B7F, SvxResId(RID_SUBSETSTR_INSCRIPTIONAL_PAHLAVI) ); + break; + case UBLOCK_OLD_TURKIC: + aAllSubsets.emplace_back( 0x10C00, 0x10C4F, SvxResId(RID_SUBSETSTR_OLD_TURKIC) ); + break; + case UBLOCK_RUMI_NUMERAL_SYMBOLS: + aAllSubsets.emplace_back( 0x10E60, 0x10E7F, SvxResId(RID_SUBSETSTR_RUMI_NUMERAL_SYMBOLS) ); + break; + case UBLOCK_KAITHI: + aAllSubsets.emplace_back( 0x11080, 0x110CF, SvxResId(RID_SUBSETSTR_KAITHI) ); + break; + case UBLOCK_EGYPTIAN_HIEROGLYPHS: + aAllSubsets.emplace_back( 0x13000, 0x1342F, SvxResId(RID_SUBSETSTR_EGYPTIAN_HIEROGLYPHS) ); + break; + case UBLOCK_ENCLOSED_ALPHANUMERIC_SUPPLEMENT: + aAllSubsets.emplace_back( 0x1F100, 0x1F1FF, SvxResId(RID_SUBSETSTR_ENCLOSED_ALPHANUMERIC_SUPPLEMENT) ); + break; + case UBLOCK_ENCLOSED_IDEOGRAPHIC_SUPPLEMENT: + aAllSubsets.emplace_back( 0x1F200, 0x1F2FF, SvxResId(RID_SUBSETSTR_ENCLOSED_IDEOGRAPHIC_SUPPLEMENT) ); + break; + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C: + aAllSubsets.emplace_back( 0x2A700, 0x2B73F, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C) ); + break; + case UBLOCK_MANDAIC: + aAllSubsets.emplace_back( 0x0840, 0x085F, SvxResId(RID_SUBSETSTR_MANDAIC) ); + break; + case UBLOCK_BATAK: + aAllSubsets.emplace_back( 0x1BC0, 0x1BFF, SvxResId(RID_SUBSETSTR_BATAK) ); + break; + case UBLOCK_ETHIOPIC_EXTENDED_A: + aAllSubsets.emplace_back( 0xAB00, 0xAB2F, SvxResId(RID_SUBSETSTR_ETHIOPIC_EXTENDED_A) ); + break; + case UBLOCK_BRAHMI: + aAllSubsets.emplace_back( 0x11000, 0x1107F, SvxResId(RID_SUBSETSTR_BRAHMI) ); + break; + case UBLOCK_BAMUM_SUPPLEMENT: + aAllSubsets.emplace_back( 0x16800, 0x16A3F, SvxResId(RID_SUBSETSTR_BAMUM_SUPPLEMENT) ); + break; + case UBLOCK_KANA_SUPPLEMENT: + aAllSubsets.emplace_back( 0x1B000, 0x1B0FF, SvxResId(RID_SUBSETSTR_KANA_SUPPLEMENT) ); + break; + case UBLOCK_PLAYING_CARDS: + aAllSubsets.emplace_back( 0x1F0A0, 0x1F0FF, SvxResId(RID_SUBSETSTR_PLAYING_CARDS) ); + break; + case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS: + aAllSubsets.emplace_back( 0x1F300, 0x1F5FF, SvxResId(RID_SUBSETSTR_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS) ); + break; + case UBLOCK_EMOTICONS: + aAllSubsets.emplace_back( 0x1F600, 0x1F64F, SvxResId(RID_SUBSETSTR_EMOTICONS) ); + break; + case UBLOCK_TRANSPORT_AND_MAP_SYMBOLS: + aAllSubsets.emplace_back( 0x1F680, 0x1F6FF, SvxResId(RID_SUBSETSTR_TRANSPORT_AND_MAP_SYMBOLS) ); + break; + case UBLOCK_ALCHEMICAL_SYMBOLS: + aAllSubsets.emplace_back( 0x1F700, 0x1F77F, SvxResId(RID_SUBSETSTR_ALCHEMICAL_SYMBOLS) ); + break; + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D: + aAllSubsets.emplace_back( 0x2B740, 0x2B81F, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D) ); + break; + case UBLOCK_ARABIC_EXTENDED_A: + aAllSubsets.emplace_back( 0x08A0, 0x08FF, SvxResId(RID_SUBSETSTR_ARABIC_EXTENDED_A) ); + break; + case UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS: + aAllSubsets.emplace_back( 0x1EE00, 0x1EEFF, SvxResId(RID_SUBSETSTR_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS) ); + break; + case UBLOCK_CHAKMA: + aAllSubsets.emplace_back( 0x11100, 0x1114F, SvxResId(RID_SUBSETSTR_CHAKMA) ); + break; + case UBLOCK_MEETEI_MAYEK_EXTENSIONS: + aAllSubsets.emplace_back( 0xAAE0, 0xAAFF, SvxResId(RID_SUBSETSTR_MEETEI_MAYEK_EXTENSIONS) ); + break; + case UBLOCK_MEROITIC_CURSIVE: + aAllSubsets.emplace_back( 0x109A0, 0x109FF, SvxResId(RID_SUBSETSTR_MEROITIC_CURSIVE) ); + break; + case UBLOCK_MEROITIC_HIEROGLYPHS: + aAllSubsets.emplace_back( 0x10980, 0x1099F, SvxResId(RID_SUBSETSTR_MEROITIC_HIEROGLYPHS) ); + break; + case UBLOCK_MIAO: + aAllSubsets.emplace_back( 0x16F00, 0x16F9F, SvxResId(RID_SUBSETSTR_MIAO) ); + break; + case UBLOCK_SHARADA: + aAllSubsets.emplace_back( 0x11180, 0x111DF, SvxResId(RID_SUBSETSTR_SHARADA) ); + break; + case UBLOCK_SORA_SOMPENG: + aAllSubsets.emplace_back( 0x110D0, 0x110FF, SvxResId(RID_SUBSETSTR_SORA_SOMPENG) ); + break; + case UBLOCK_SUNDANESE_SUPPLEMENT: + aAllSubsets.emplace_back( 0x1CC0, 0x1CCF, SvxResId(RID_SUBSETSTR_SUNDANESE_SUPPLEMENT) ); + break; + case UBLOCK_TAKRI: + aAllSubsets.emplace_back( 0x11680, 0x116CF, SvxResId(RID_SUBSETSTR_TAKRI) ); + break; + case UBLOCK_BASSA_VAH: + aAllSubsets.emplace_back( 0x16AD0, 0x16AFF, SvxResId(RID_SUBSETSTR_BASSA_VAH) ); + break; + case UBLOCK_CAUCASIAN_ALBANIAN: + aAllSubsets.emplace_back( 0x10530, 0x1056F, SvxResId(RID_SUBSETSTR_CAUCASIAN_ALBANIAN) ); + break; + case UBLOCK_COPTIC_EPACT_NUMBERS: + aAllSubsets.emplace_back( 0x102E0, 0x102FF, SvxResId(RID_SUBSETSTR_COPTIC_EPACT_NUMBERS) ); + break; + case UBLOCK_COMBINING_DIACRITICAL_MARKS_EXTENDED: + aAllSubsets.emplace_back( 0x1AB0, 0x1AFF, SvxResId(RID_SUBSETSTR_COMBINING_DIACRITICAL_MARKS_EXTENDED) ); + break; + case UBLOCK_DUPLOYAN: + aAllSubsets.emplace_back( 0x1BC00, 0x1BC9F, SvxResId(RID_SUBSETSTR_DUPLOYAN) ); + break; + case UBLOCK_ELBASAN: + aAllSubsets.emplace_back( 0x10500, 0x1052F, SvxResId(RID_SUBSETSTR_ELBASAN) ); + break; + case UBLOCK_GEOMETRIC_SHAPES_EXTENDED: + aAllSubsets.emplace_back( 0x1F780, 0x1F7FF, SvxResId(RID_SUBSETSTR_GEOMETRIC_SHAPES_EXTENDED) ); + break; + case UBLOCK_GRANTHA: + aAllSubsets.emplace_back( 0x11300, 0x1137F, SvxResId(RID_SUBSETSTR_GRANTHA) ); + break; + case UBLOCK_KHOJKI: + aAllSubsets.emplace_back( 0x11200, 0x1124F, SvxResId(RID_SUBSETSTR_KHOJKI) ); + break; + case UBLOCK_KHUDAWADI: + aAllSubsets.emplace_back( 0x112B0, 0x112FF, SvxResId(RID_SUBSETSTR_KHUDAWADI) ); + break; + case UBLOCK_LATIN_EXTENDED_E: + aAllSubsets.emplace_back( 0xAB30, 0xAB6F, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_E) ); + break; + case UBLOCK_LINEAR_A: + aAllSubsets.emplace_back( 0x10600, 0x1077F, SvxResId(RID_SUBSETSTR_LINEAR_A) ); + break; + case UBLOCK_MAHAJANI: + aAllSubsets.emplace_back( 0x11150, 0x1117F, SvxResId(RID_SUBSETSTR_MAHAJANI) ); + break; + case UBLOCK_MANICHAEAN: + aAllSubsets.emplace_back( 0x10AC0, 0x10AFF, SvxResId(RID_SUBSETSTR_MANICHAEAN) ); + break; + case UBLOCK_MENDE_KIKAKUI: + aAllSubsets.emplace_back( 0x1E800, 0x1E8DF, SvxResId(RID_SUBSETSTR_MENDE_KIKAKUI) ); + break; + case UBLOCK_MODI: + aAllSubsets.emplace_back( 0x11600, 0x1165F, SvxResId(RID_SUBSETSTR_MODI) ); + break; + case UBLOCK_MRO: + aAllSubsets.emplace_back( 0x16A40, 0x16A6F, SvxResId(RID_SUBSETSTR_MRO) ); + break; + case UBLOCK_MYANMAR_EXTENDED_B: + aAllSubsets.emplace_back( 0xA9E0, 0xA9FF, SvxResId(RID_SUBSETSTR_MYANMAR_EXTENDED_B) ); + break; + case UBLOCK_NABATAEAN: + aAllSubsets.emplace_back( 0x10880, 0x108AF, SvxResId(RID_SUBSETSTR_NABATAEAN) ); + break; + case UBLOCK_OLD_NORTH_ARABIAN: + aAllSubsets.emplace_back( 0x10A80, 0x10A9F, SvxResId(RID_SUBSETSTR_OLD_NORTH_ARABIAN) ); + break; + case UBLOCK_OLD_PERMIC: + aAllSubsets.emplace_back( 0x10350, 0x1037F, SvxResId(RID_SUBSETSTR_OLD_PERMIC) ); + break; + case UBLOCK_ORNAMENTAL_DINGBATS: + aAllSubsets.emplace_back( 0x1F650, 0x1F67F, SvxResId(RID_SUBSETSTR_ORNAMENTAL_DINGBATS) ); + break; + case UBLOCK_PAHAWH_HMONG: + aAllSubsets.emplace_back( 0x16B00, 0x16B8F, SvxResId(RID_SUBSETSTR_PAHAWH_HMONG) ); + break; + case UBLOCK_PALMYRENE: + aAllSubsets.emplace_back( 0x10860, 0x1087F, SvxResId(RID_SUBSETSTR_PALMYRENE) ); + break; + case UBLOCK_PAU_CIN_HAU: + aAllSubsets.emplace_back( 0x11AC0, 0x11AFF, SvxResId(RID_SUBSETSTR_PAU_CIN_HAU) ); + break; + case UBLOCK_PSALTER_PAHLAVI: + aAllSubsets.emplace_back( 0x10B80, 0x10BAF, SvxResId(RID_SUBSETSTR_PSALTER_PAHLAVI) ); + break; + case UBLOCK_SHORTHAND_FORMAT_CONTROLS: + aAllSubsets.emplace_back( 0x1BCA0, 0x1BCAF, SvxResId(RID_SUBSETSTR_SHORTHAND_FORMAT_CONTROLS) ); + break; + case UBLOCK_SIDDHAM: + aAllSubsets.emplace_back( 0x11580, 0x115FF, SvxResId(RID_SUBSETSTR_SIDDHAM) ); + break; + case UBLOCK_SINHALA_ARCHAIC_NUMBERS: + aAllSubsets.emplace_back( 0x111E0, 0x111FF, SvxResId(RID_SUBSETSTR_SINHALA_ARCHAIC_NUMBERS) ); + break; + case UBLOCK_SUPPLEMENTAL_ARROWS_C: + aAllSubsets.emplace_back( 0x1F800, 0x1F8FF, SvxResId(RID_SUBSETSTR_SUPPLEMENTAL_ARROWS_C) ); + break; + case UBLOCK_TIRHUTA: + aAllSubsets.emplace_back( 0x11480, 0x114DF, SvxResId(RID_SUBSETSTR_TIRHUTA) ); + break; + case UBLOCK_WARANG_CITI: + aAllSubsets.emplace_back( 0x118A0, 0x118FF, SvxResId(RID_SUBSETSTR_WARANG_CITI) ); + break; + case UBLOCK_AHOM: + aAllSubsets.emplace_back( 0x11700, 0x1173F, SvxResId(RID_SUBSETSTR_AHOM) ); + break; + case UBLOCK_ANATOLIAN_HIEROGLYPHS: + aAllSubsets.emplace_back( 0x14400, 0x1467F, SvxResId(RID_SUBSETSTR_ANATOLIAN_HIEROGLYPHS) ); + break; + case UBLOCK_CHEROKEE_SUPPLEMENT: + aAllSubsets.emplace_back( 0xAB70, 0xABBF, SvxResId(RID_SUBSETSTR_CHEROKEE_SUPPLEMENT) ); + break; + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E: + aAllSubsets.emplace_back( 0x2B820, 0x2CEAF, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E) ); + break; + case UBLOCK_EARLY_DYNASTIC_CUNEIFORM: + aAllSubsets.emplace_back( 0x12480, 0x1254F, SvxResId(RID_SUBSETSTR_EARLY_DYNASTIC_CUNEIFORM) ); + break; + case UBLOCK_HATRAN: + aAllSubsets.emplace_back( 0x108E0, 0x108FF, SvxResId(RID_SUBSETSTR_HATRAN) ); + break; + case UBLOCK_MULTANI: + aAllSubsets.emplace_back( 0x11280, 0x112AF, SvxResId(RID_SUBSETSTR_MULTANI) ); + break; + case UBLOCK_OLD_HUNGARIAN: + aAllSubsets.emplace_back( 0x10C80, 0x10CFF, SvxResId(RID_SUBSETSTR_OLD_HUNGARIAN) ); + break; + case UBLOCK_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS: + aAllSubsets.emplace_back( 0x1F900, 0x1F9FF, SvxResId(RID_SUBSETSTR_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS) ); + break; + case UBLOCK_SUTTON_SIGNWRITING: + aAllSubsets.emplace_back( 0x1D800, 0x1DAAF, SvxResId(RID_SUBSETSTR_SUTTON_SIGNWRITING) ); + break; + case UBLOCK_ADLAM: + aAllSubsets.emplace_back( 0x1E900, 0x1E95F, SvxResId(RID_SUBSETSTR_ADLAM) ); + break; + case UBLOCK_BHAIKSUKI: + aAllSubsets.emplace_back( 0x11C00, 0x11C6F, SvxResId(RID_SUBSETSTR_BHAIKSUKI) ); + break; + case UBLOCK_CYRILLIC_EXTENDED_C: + aAllSubsets.emplace_back( 0x1C80, 0x1C8F, SvxResId(RID_SUBSETSTR_CYRILLIC_EXTENDED_C) ); + break; + case UBLOCK_GLAGOLITIC_SUPPLEMENT: + aAllSubsets.emplace_back( 0x1E000, 0x1E02F, SvxResId(RID_SUBSETSTR_GLAGOLITIC_SUPPLEMENT) ); + break; + case UBLOCK_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION: + aAllSubsets.emplace_back( 0x16FE0, 0x16FFF, SvxResId(RID_SUBSETSTR_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION) ); + break; + case UBLOCK_MARCHEN: + aAllSubsets.emplace_back( 0x11C70, 0x11CBF, SvxResId(RID_SUBSETSTR_MARCHEN) ); + break; + case UBLOCK_MONGOLIAN_SUPPLEMENT: + aAllSubsets.emplace_back( 0x11660, 0x1167F, SvxResId(RID_SUBSETSTR_MONGOLIAN_SUPPLEMENT) ); + break; + case UBLOCK_NEWA: + aAllSubsets.emplace_back( 0x11400, 0x1147F, SvxResId(RID_SUBSETSTR_NEWA) ); + break; + case UBLOCK_OSAGE: + aAllSubsets.emplace_back( 0x104B0, 0x104FF, SvxResId(RID_SUBSETSTR_OSAGE) ); + break; + case UBLOCK_TANGUT: + aAllSubsets.emplace_back( 0x17000, 0x187FF, SvxResId(RID_SUBSETSTR_TANGUT) ); + break; + case UBLOCK_TANGUT_COMPONENTS: + aAllSubsets.emplace_back( 0x18800, 0x18AFF, SvxResId(RID_SUBSETSTR_TANGUT_COMPONENTS) ); + break; + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F: + aAllSubsets.emplace_back( 0x2CEB0, 0x2EBE0, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F) ); + break; + case UBLOCK_KANA_EXTENDED_A: + aAllSubsets.emplace_back( 0x1B100, 0x1B12F, SvxResId(RID_SUBSETSTR_KANA_EXTENDED_A) ); + break; + case UBLOCK_MASARAM_GONDI: + aAllSubsets.emplace_back( 0x11D00, 0x11D5F, SvxResId(RID_SUBSETSTR_MASARAM_GONDI) ); + break; + case UBLOCK_NUSHU: + aAllSubsets.emplace_back( 0x1B170, 0x1B2FF, SvxResId(RID_SUBSETSTR_NUSHU) ); + break; + case UBLOCK_SOYOMBO: + aAllSubsets.emplace_back( 0x11A50, 0x11AAF, SvxResId(RID_SUBSETSTR_SOYOMBO) ); + break; + case UBLOCK_SYRIAC_SUPPLEMENT: + aAllSubsets.emplace_back( 0x0860, 0x086f, SvxResId(RID_SUBSETSTR_SYRIAC_SUPPLEMENT) ); + break; + case UBLOCK_ZANABAZAR_SQUARE: + aAllSubsets.emplace_back( 0x11A00, 0x11A4F, SvxResId(RID_SUBSETSTR_ZANABAZAR_SQUARE) ); + break; + case UBLOCK_CHESS_SYMBOLS: + aAllSubsets.emplace_back( 0x1FA00, 0x1FA6F, SvxResId(RID_SUBSETSTR_CHESS_SYMBOLS) ); + break; + case UBLOCK_DOGRA: + aAllSubsets.emplace_back( 0x11800, 0x1184F, SvxResId(RID_SUBSETSTR_DOGRA) ); + break; + case UBLOCK_GEORGIAN_EXTENDED: + aAllSubsets.emplace_back( 0x1C90, 0x1CBF, SvxResId(RID_SUBSETSTR_GEORGIAN_EXTENDED) ); + break; + case UBLOCK_GUNJALA_GONDI: + aAllSubsets.emplace_back( 0x11D60, 0x11DAF, SvxResId(RID_SUBSETSTR_GUNJALA_GONDI) ); + break; + case UBLOCK_HANIFI_ROHINGYA: + aAllSubsets.emplace_back( 0x10D00, 0x10D3F, SvxResId(RID_SUBSETSTR_HANIFI_ROHINGYA) ); + break; + case UBLOCK_INDIC_SIYAQ_NUMBERS: + aAllSubsets.emplace_back( 0x1EC70, 0x1ECBF, SvxResId(RID_SUBSETSTR_INDIC_SIYAQ_NUMBERS) ); + break; + case UBLOCK_MAKASAR: + aAllSubsets.emplace_back( 0x11EE0, 0x11EFF, SvxResId(RID_SUBSETSTR_MAKASAR) ); + break; + case UBLOCK_MAYAN_NUMERALS: + aAllSubsets.emplace_back( 0x1D2E0, 0x1D2FF, SvxResId(RID_SUBSETSTR_MAYAN_NUMERALS) ); + break; + case UBLOCK_MEDEFAIDRIN: + aAllSubsets.emplace_back( 0x16E40, 0x16E9F, SvxResId(RID_SUBSETSTR_MEDEFAIDRIN) ); + break; + case UBLOCK_OLD_SOGDIAN: + aAllSubsets.emplace_back( 0x10F00, 0x10F2F, SvxResId(RID_SUBSETSTR_OLD_SOGDIAN) ); + break; + case UBLOCK_SOGDIAN: + aAllSubsets.emplace_back( 0x10F30, 0x10F6F, SvxResId(RID_SUBSETSTR_SOGDIAN) ); + break; + case UBLOCK_EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS: + aAllSubsets.emplace_back( 0x13430, 0x1343F, SvxResId(RID_SUBSETSTR_EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS) ); + break; + case UBLOCK_ELYMAIC: + aAllSubsets.emplace_back( 0x10FE0, 0x10FFF, SvxResId(RID_SUBSETSTR_ELYMAIC) ); + break; + case UBLOCK_NANDINAGARI: + aAllSubsets.emplace_back( 0x119A0, 0x119FF, SvxResId(RID_SUBSETSTR_NANDINAGARI) ); + break; + case UBLOCK_NYIAKENG_PUACHUE_HMONG: + aAllSubsets.emplace_back( 0x1E100, 0x1E14F, SvxResId(RID_SUBSETSTR_NYIAKENG_PUACHUE_HMONG) ); + break; + case UBLOCK_OTTOMAN_SIYAQ_NUMBERS: + aAllSubsets.emplace_back( 0x1ED00, 0x1ED4F, SvxResId(RID_SUBSETSTR_OTTOMAN_SIYAQ_NUMBERS) ); + break; + case UBLOCK_SMALL_KANA_EXTENSION: + aAllSubsets.emplace_back( 0x1B130, 0x1B16F, SvxResId(RID_SUBSETSTR_SMALL_KANA_EXTENSION) ); + break; + case UBLOCK_SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A: + aAllSubsets.emplace_back( 0x1FA70, 0x1FAFF, SvxResId(RID_SUBSETSTR_SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A) ); + break; + case UBLOCK_TAMIL_SUPPLEMENT: + aAllSubsets.emplace_back( 0x11FC0, 0x11FFF, SvxResId(RID_SUBSETSTR_TAMIL_SUPPLEMENT) ); + break; + case UBLOCK_WANCHO: + aAllSubsets.emplace_back( 0x1E2C0, 0x1E2FF, SvxResId(RID_SUBSETSTR_WANCHO) ); + break; + case UBLOCK_CHORASMIAN: + aAllSubsets.emplace_back( 0x10FB0, 0x10FDF, SvxResId(RID_SUBSETSTR_CHORASMIAN) ); + break; + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G: + aAllSubsets.emplace_back( 0x30000, 0x3134F, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G) ); + break; + case UBLOCK_DIVES_AKURU: + aAllSubsets.emplace_back( 0x11900, 0x1195F, SvxResId(RID_SUBSETSTR_DIVES_AKURU) ); + break; + case UBLOCK_KHITAN_SMALL_SCRIPT: + aAllSubsets.emplace_back( 0x18B00, 0x18CFF, SvxResId(RID_SUBSETSTR_KHITAN_SMALL_SCRIPT) ); + break; + case UBLOCK_LISU_SUPPLEMENT: + aAllSubsets.emplace_back( 0x11FB0, 0x11FBF, SvxResId(RID_SUBSETSTR_LISU_SUPPLEMENT) ); + break; + case UBLOCK_SYMBOLS_FOR_LEGACY_COMPUTING: + aAllSubsets.emplace_back( 0x1FB00, 0x1FBFF, SvxResId(RID_SUBSETSTR_SYMBOLS_FOR_LEGACY_COMPUTING) ); + break; + case UBLOCK_TANGUT_SUPPLEMENT: + aAllSubsets.emplace_back( 0x18D00, 0x18D7F, SvxResId(RID_SUBSETSTR_TANGUT_SUPPLEMENT) ); + break; + case UBLOCK_YEZIDI: + aAllSubsets.emplace_back( 0x10E80, 0x10EBF, SvxResId(RID_SUBSETSTR_YEZIDI) ); + break; +#if (U_ICU_VERSION_MAJOR_NUM >= 70) + case UBLOCK_ARABIC_EXTENDED_B: + aAllSubsets.emplace_back( 0x0870, 0x089F, SvxResId(RID_SUBSETSTR_ARABIC_EXTENDED_B) ); + break; + case UBLOCK_CYPRO_MINOAN: + aAllSubsets.emplace_back( 0x12F90, 0x12FFF, SvxResId(RID_SUBSETSTR_CYPRO_MINOAN) ); + break; + case UBLOCK_ETHIOPIC_EXTENDED_B: + aAllSubsets.emplace_back( 0x1E7E0, 0x1E7FF, SvxResId(RID_SUBSETSTR_ETHIOPIC_EXTENDED_B) ); + break; + case UBLOCK_KANA_EXTENDED_B: + aAllSubsets.emplace_back( 0x1AFF0, 0x1AFFF, SvxResId(RID_SUBSETSTR_KANA_EXTENDED_B) ); + break; + case UBLOCK_LATIN_EXTENDED_F: + aAllSubsets.emplace_back( 0x10780, 0x107BF, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_F) ); + break; + case UBLOCK_LATIN_EXTENDED_G: + aAllSubsets.emplace_back( 0x1DF00, 0x1DFFF, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_G) ); + break; + case UBLOCK_OLD_UYGHUR: + aAllSubsets.emplace_back( 0x10F70, 0x10FAF, SvxResId(RID_SUBSETSTR_OLD_UYGHUR) ); + break; + case UBLOCK_TANGSA: + aAllSubsets.emplace_back( 0x16A70, 0x16ACF, SvxResId(RID_SUBSETSTR_TANGSA) ); + break; + case UBLOCK_TOTO: + aAllSubsets.emplace_back( 0x1E290, 0x1E2BF, SvxResId(RID_SUBSETSTR_TOTO) ); + break; + case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED_A: + aAllSubsets.emplace_back( 0x11AB0, 0x11ABF, SvxResId(RID_SUBSETSTR_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED_A) ); + break; + case UBLOCK_VITHKUQI: + aAllSubsets.emplace_back( 0x10570, 0x105BF, SvxResId(RID_SUBSETSTR_VITHKUQI) ); + break; + case UBLOCK_ZNAMENNY_MUSICAL_NOTATION: + aAllSubsets.emplace_back( 0x1CF00, 0x1CFCF, SvxResId(RID_SUBSETSTR_ZNAMENNY_MUSICAL_NOTATION) ); + break; +#endif +#if (U_ICU_VERSION_MAJOR_NUM >= 72) + case UBLOCK_ARABIC_EXTENDED_C: + aAllSubsets.emplace_back( 0x10EC0, 0x10EFF, SvxResId(RID_SUBSETSTR_ARABIC_EXTENDED_C) ); + break; + case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_H: + aAllSubsets.emplace_back( 0x31350, 0x323AF, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_H) ); + break; + case UBLOCK_CYRILLIC_EXTENDED_D: + aAllSubsets.emplace_back( 0x1E030, 0x1E08F, SvxResId(RID_SUBSETSTR_CYRILLIC_EXTENDED_D) ); + break; + case UBLOCK_DEVANAGARI_EXTENDED_A: + aAllSubsets.emplace_back( 0x11B00, 0x11B5F, SvxResId(RID_SUBSETSTR_DEVANAGARI_EXTENDED_A) ); + break; + case UBLOCK_KAKTOVIK_NUMERALS: + aAllSubsets.emplace_back( 0x1D2C0, 0x1D2DF, SvxResId(RID_SUBSETSTR_KAKTOVIK_NUMERALS) ); + break; + case UBLOCK_KAWI: + aAllSubsets.emplace_back( 0x11F00, 0x11F5F, SvxResId(RID_SUBSETSTR_KAWI) ); + break; + case UBLOCK_NAG_MUNDARI: + aAllSubsets.emplace_back( 0x1E4D0, 0x1E4FF, SvxResId(RID_SUBSETSTR_NAG_MUNDARI) ); + break; +#endif + } + +#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG + if (eBlock != UBLOCK_NO_BLOCK && + eBlock != UBLOCK_INVALID_CODE && + eBlock != UBLOCK_COUNT && + eBlock != UBLOCK_HIGH_SURROGATES && + eBlock != UBLOCK_HIGH_PRIVATE_USE_SURROGATES && + eBlock != UBLOCK_LOW_SURROGATES) + + { + UBlockCode eBlockStart = ublock_getCode(aAllSubsets.back().GetRangeMin()); + UBlockCode eBlockEnd = ublock_getCode(aAllSubsets.back().GetRangeMax()); + assert(eBlockStart == eBlockEnd && eBlockStart == eBlock); + } +#endif + } + + std::stable_sort(aAllSubsets.begin(), aAllSubsets.end()); + return aAllSubsets; + }(); + + maSubsets = s_aAllSubsets; +} + +void SubsetMap::ApplyCharMap( const FontCharMapRef& rxFontCharMap ) +{ + if( !rxFontCharMap.is() ) + return; + + // remove subsets that are not matched in any range + std::erase_if(maSubsets, + [&rxFontCharMap](const Subset& rSubset) { + sal_uInt32 cMin = rSubset.GetRangeMin(); + sal_uInt32 cMax = rSubset.GetRangeMax(); + int nCount = rxFontCharMap->CountCharsInRange( cMin, cMax ); + return nCount <= 0; + }); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/compressgraphicdialog.cxx b/svx/source/dialog/compressgraphicdialog.cxx new file mode 100644 index 0000000000..8fcf479d88 --- /dev/null +++ b/svx/source/dialog/compressgraphicdialog.cxx @@ -0,0 +1,456 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "dlgunit.hxx" +#include <utility> +#include <vcl/fieldvalues.hxx> +#include <vcl/graph.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/virdev.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/weld.hxx> +#include <svx/strings.hrc> +#include <svx/svdograf.hxx> +#include <svx/sdgcpitm.hxx> +#include <svx/dialmgr.hxx> +#include <svx/graphichelper.hxx> +#include <svx/compressgraphicdialog.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/module.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/propertyvalue.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <tools/stream.hxx> +#include <unotools/localedatawrapper.hxx> + +// tdf#146929 - remember user settings within the current session +// memp is filled in dtor and restored after initialization +namespace +{ + struct memParam { + bool ReduceResolutionCB = false; + int MFNewWidth = 1; + int MFNewHeight = 1; + bool LosslessRB = true; + bool JpegCompRB = false; + int CompressionMF = 6; + int QualityMF = 80; + int InterpolationCombo = 3; + }; + memParam memp; +} + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; + +CompressGraphicsDialog::CompressGraphicsDialog( weld::Window* pParent, SdrGrafObj* pGraphicObj, SfxBindings& rBindings ) : + GenericDialogController( pParent, "svx/ui/compressgraphicdialog.ui", "CompressGraphicDialog" ), + m_xGraphicObj ( pGraphicObj ), + m_aGraphic ( pGraphicObj->GetGraphicObject().GetGraphic() ), + m_aViewSize100mm ( pGraphicObj->GetLogicRect().GetSize() ), + m_rBindings ( rBindings ), + m_dResolution ( 300 ) +{ + const SdrGrafCropItem& rCrop = m_xGraphicObj->GetMergedItem(SDRATTR_GRAFCROP); + m_aCropRectangle = tools::Rectangle(rCrop.GetLeft(), rCrop.GetTop(), rCrop.GetRight(), rCrop.GetBottom()); + + Initialize(); + recallParameter(); +} + +CompressGraphicsDialog::CompressGraphicsDialog( weld::Window* pParent, Graphic aGraphic, Size rViewSize100mm, tools::Rectangle const & rCropRectangle, SfxBindings& rBindings ) : + GenericDialogController( pParent, "svx/ui/compressgraphicdialog.ui", "CompressGraphicDialog" ), + m_xGraphicObj ( nullptr ), + m_aGraphic (std::move( aGraphic )), + m_aViewSize100mm ( rViewSize100mm ), + m_aCropRectangle ( rCropRectangle ), + m_rBindings ( rBindings ), + m_dResolution ( 300 ) +{ + Initialize(); + recallParameter(); +} + +CompressGraphicsDialog::~CompressGraphicsDialog() +{ +} + +void CompressGraphicsDialog::recallParameter() +{ + m_xReduceResolutionCB->set_active( memp.ReduceResolutionCB ); + if (memp.ReduceResolutionCB && (memp.MFNewWidth > 1)) + m_xMFNewWidth->set_value( memp.MFNewWidth ); + if (memp.ReduceResolutionCB && (memp.MFNewHeight > 1)) + m_xMFNewHeight->set_value( memp.MFNewHeight ); + + m_xLosslessRB->set_active( memp.LosslessRB ); + m_xJpegCompRB->set_active( memp.JpegCompRB ); + m_xCompressionMF->set_value( memp.CompressionMF ); + m_xCompressionSlider->set_value( memp.CompressionMF ); + m_xQualityMF->set_value( memp.QualityMF ); + m_xQualitySlider->set_value( memp.QualityMF ); + + m_xInterpolationCombo->set_active( memp.InterpolationCombo ); +} + +void CompressGraphicsDialog::Initialize() +{ + m_xLabelGraphicType = m_xBuilder->weld_label("label-graphic-type"); + m_xFixedText2 = m_xBuilder->weld_label("label-original-size"); + m_xFixedText3 = m_xBuilder->weld_label("label-view-size"); + m_xFixedText5 = m_xBuilder->weld_label("label-image-capacity"); + m_xFixedText6 = m_xBuilder->weld_label("label-new-capacity"); + m_xJpegCompRB = m_xBuilder->weld_radio_button("radio-jpeg"); + m_xCompressionMF = m_xBuilder->weld_spin_button("spin-compression"); + m_xCompressionSlider = m_xBuilder->weld_scale("scale-compression"); + m_xLosslessRB = m_xBuilder->weld_radio_button("radio-lossless"); + m_xQualityMF = m_xBuilder->weld_spin_button("spin-quality"); + m_xQualitySlider = m_xBuilder->weld_scale("scale-quality"); + m_xReduceResolutionCB = m_xBuilder->weld_check_button("checkbox-reduce-resolution"); + m_xMFNewWidth = m_xBuilder->weld_spin_button("spin-new-width"); + m_xMFNewHeight = m_xBuilder->weld_spin_button("spin-new-height"); + m_xResolutionLB = m_xBuilder->weld_combo_box("combo-resolution"); + m_xBtnCalculate = m_xBuilder->weld_button("calculate"); + m_xInterpolationCombo = m_xBuilder->weld_combo_box("interpolation-method-combo"); + m_xBtnOkay = m_xBuilder->weld_button("ok"); + + m_xInterpolationCombo->set_active_text("Lanczos"); + + m_xInterpolationCombo->connect_changed(LINK(this, CompressGraphicsDialog, NewInterpolationModifiedHdl)); + + m_xMFNewWidth->connect_value_changed( LINK( this, CompressGraphicsDialog, NewWidthModifiedHdl )); + m_xMFNewHeight->connect_value_changed( LINK( this, CompressGraphicsDialog, NewHeightModifiedHdl )); + + m_xResolutionLB->connect_changed( LINK( this, CompressGraphicsDialog, ResolutionModifiedHdl )); + m_xBtnCalculate->connect_clicked( LINK( this, CompressGraphicsDialog, CalculateClickHdl ) ); + + m_xLosslessRB->connect_toggled( LINK( this, CompressGraphicsDialog, ToggleCompressionRB ) ); + m_xJpegCompRB->connect_toggled( LINK( this, CompressGraphicsDialog, ToggleCompressionRB ) ); + + m_xReduceResolutionCB->connect_toggled( LINK( this, CompressGraphicsDialog, ToggleReduceResolutionRB ) ); + + m_xQualitySlider->connect_value_changed( LINK( this, CompressGraphicsDialog, SlideHdl )); + m_xCompressionSlider->connect_value_changed( LINK( this, CompressGraphicsDialog, SlideHdl )); + m_xQualityMF->connect_value_changed( LINK( this, CompressGraphicsDialog, NewQualityModifiedHdl )); + m_xCompressionMF->connect_value_changed( LINK( this, CompressGraphicsDialog, NewCompressionModifiedHdl )); + + m_xJpegCompRB->set_active(true); + m_xReduceResolutionCB->set_active(true); + + m_xBtnOkay->connect_clicked( LINK( this, CompressGraphicsDialog, OkayClickHdl ) ); + UpdateNewWidthMF(); + UpdateNewHeightMF(); + UpdateResolutionLB(); + Update(); +} + +void CompressGraphicsDialog::Update() +{ + auto pGfxLink = m_aGraphic.GetSharedGfxLink(); + + m_xLabelGraphicType->set_label(GraphicHelper::GetImageType(m_aGraphic)); + + const FieldUnit eFieldUnit = m_rBindings.GetDispatcher()->GetModule()->GetFieldUnit(); + const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() ); + sal_Unicode cSeparator = rLocaleWrapper.getNumDecimalSep()[0]; + + ScopedVclPtrInstance<VirtualDevice> pDummyVDev; + pDummyVDev->EnableOutput( false ); + pDummyVDev->SetMapMode( m_aGraphic.GetPrefMapMode() ); + + Size aPixelSize = m_aGraphic.GetSizePixel(); + Size aOriginalSize100mm(pDummyVDev->PixelToLogic(m_aGraphic.GetSizePixel(), MapMode(MapUnit::Map100thMM))); + + OUString aBitmapSizeString = SvxResId(STR_IMAGE_ORIGINAL_SIZE); + OUString aWidthString = GetUnitString( aOriginalSize100mm.Width(), eFieldUnit, cSeparator ); + OUString aHeightString = GetUnitString( aOriginalSize100mm.Height(), eFieldUnit, cSeparator ); + aBitmapSizeString = aBitmapSizeString.replaceAll("$(WIDTH)", aWidthString); + aBitmapSizeString = aBitmapSizeString.replaceAll("$(HEIGHT)", aHeightString); + aBitmapSizeString = aBitmapSizeString.replaceAll("$(WIDTH_IN_PX)", OUString::number(aPixelSize.Width())); + aBitmapSizeString = aBitmapSizeString.replaceAll("$(HEIGHT_IN_PX)", OUString::number(aPixelSize.Height())); + m_xFixedText2->set_label(aBitmapSizeString); + + int aValX = static_cast<int>(aPixelSize.Width() / GetViewWidthInch()); + + OUString aViewSizeString = SvxResId(STR_IMAGE_VIEW_SIZE); + + aWidthString = GetUnitString( m_aViewSize100mm.Width(), eFieldUnit, cSeparator ); + aHeightString = GetUnitString( m_aViewSize100mm.Height(), eFieldUnit, cSeparator ); + aViewSizeString = aViewSizeString.replaceAll("$(WIDTH)", aWidthString); + aViewSizeString = aViewSizeString.replaceAll("$(HEIGHT)", aHeightString); + aViewSizeString = aViewSizeString.replaceAll("$(DPI)", OUString::number(aValX)); + m_xFixedText3->set_label(aViewSizeString); + + m_aNativeSize = pGfxLink ? pGfxLink->GetDataSize() : 0; + + OUString aNativeSizeString = SvxResId(STR_IMAGE_CAPACITY); + aNativeSizeString = aNativeSizeString.replaceAll("$(CAPACITY)", OUString::number( m_aNativeSize / 1024 )); + m_xFixedText5->set_label(aNativeSizeString); + + m_xFixedText6->set_label("??"); +} + +void CompressGraphicsDialog::UpdateNewWidthMF() +{ + int nPixelX = static_cast<sal_Int32>( GetViewWidthInch() * m_dResolution ); + m_xMFNewWidth->set_value(nPixelX); +} + +void CompressGraphicsDialog::UpdateNewHeightMF() +{ + int nPixelY = static_cast<sal_Int32>( GetViewHeightInch() * m_dResolution ); + m_xMFNewHeight->set_value(nPixelY); +} + +void CompressGraphicsDialog::UpdateResolutionLB() +{ + m_xResolutionLB->set_entry_text( OUString::number( static_cast<sal_Int32>(m_dResolution) ) ); +} + +double CompressGraphicsDialog::GetViewWidthInch() const +{ + return static_cast<double>(vcl::ConvertValue(m_aViewSize100mm.Width(), 2, MapUnit::Map100thMM, FieldUnit::INCH)) / 100.0; +} + +double CompressGraphicsDialog::GetViewHeightInch() const +{ + return static_cast<double>(vcl::ConvertValue(m_aViewSize100mm.Height(), 2, MapUnit::Map100thMM, FieldUnit::INCH)) / 100.0; +} + +BmpScaleFlag CompressGraphicsDialog::GetSelectedInterpolationType() const +{ + OUString aSelectionText = m_xInterpolationCombo->get_active_text(); + + if( aSelectionText == "Lanczos" ) { + return BmpScaleFlag::Lanczos; + } else if( aSelectionText == "Bilinear" ) { + return BmpScaleFlag::BiLinear; + } else if( aSelectionText == "Bicubic" ) { + return BmpScaleFlag::BiCubic; + } else if ( aSelectionText == "None" ) { + return BmpScaleFlag::Fast; + } + return BmpScaleFlag::BestQuality; +} + +void CompressGraphicsDialog::Compress(SvStream& aStream) +{ + BitmapEx aBitmap = m_aGraphic.GetBitmapEx(); + if ( m_xReduceResolutionCB->get_active() ) + { + tools::Long nPixelX = static_cast<tools::Long>( GetViewWidthInch() * m_dResolution ); + tools::Long nPixelY = static_cast<tools::Long>( GetViewHeightInch() * m_dResolution ); + + aBitmap.Scale( Size( nPixelX, nPixelY ), GetSelectedInterpolationType() ); + } + Graphic aScaledGraphic( aBitmap ); + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + + Sequence< PropertyValue > aFilterData{ + comphelper::makePropertyValue("Interlaced", sal_Int32(0)), + comphelper::makePropertyValue("Compression", static_cast<sal_Int32>(m_xCompressionMF->get_value())), + comphelper::makePropertyValue("Quality", static_cast<sal_Int32>(m_xQualityMF->get_value())) + }; + + OUString aGraphicFormatName = m_xLosslessRB->get_active() ? OUString( "png" ) : OUString( "jpg" ); + + sal_uInt16 nFilterFormat = rFilter.GetExportFormatNumberForShortName( aGraphicFormatName ); + rFilter.ExportGraphic( aScaledGraphic, u"none", aStream, nFilterFormat, &aFilterData ); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, OkayClickHdl, weld::Button&, void ) +{ + memp.ReduceResolutionCB = m_xReduceResolutionCB->get_active(); + memp.MFNewWidth = m_xMFNewWidth->get_value(); + memp.MFNewHeight = m_xMFNewHeight->get_value(); + memp.LosslessRB = m_xLosslessRB->get_active(); + memp.JpegCompRB = m_xJpegCompRB->get_active(); + memp.CompressionMF = m_xCompressionMF->get_value(); + memp.QualityMF = m_xQualityMF->get_value(); + memp.InterpolationCombo = m_xInterpolationCombo->get_active(); + CompressGraphicsDialog::response(RET_OK); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, NewWidthModifiedHdl, weld::SpinButton&, void ) +{ + m_dResolution = m_xMFNewWidth->get_value() / GetViewWidthInch(); + + UpdateNewHeightMF(); + UpdateResolutionLB(); + Update(); +} + +IMPL_LINK( CompressGraphicsDialog, SlideHdl, weld::Scale&, rScale, void ) +{ + if (&rScale == m_xQualitySlider.get()) + m_xQualityMF->set_value(m_xQualitySlider->get_value()); + else + m_xCompressionMF->set_value(m_xCompressionSlider->get_value()); + Update(); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, NewInterpolationModifiedHdl, weld::ComboBox&, void ) +{ + Update(); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, NewQualityModifiedHdl, weld::SpinButton&, void ) +{ + m_xQualitySlider->set_value(m_xQualityMF->get_value()); + Update(); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, NewCompressionModifiedHdl, weld::SpinButton&, void ) +{ + m_xCompressionSlider->set_value(m_xCompressionMF->get_value()); + Update(); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, NewHeightModifiedHdl, weld::SpinButton&, void ) +{ + m_dResolution = m_xMFNewHeight->get_value() / GetViewHeightInch(); + + UpdateNewWidthMF(); + UpdateResolutionLB(); + Update(); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, ResolutionModifiedHdl, weld::ComboBox&, void ) +{ + m_dResolution = static_cast<double>(m_xResolutionLB->get_active_text().toInt32()); + + UpdateNewWidthMF(); + UpdateNewHeightMF(); + Update(); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, ToggleCompressionRB, weld::Toggleable&, void ) +{ + bool choice = m_xLosslessRB->get_active(); + m_xCompressionMF->set_sensitive(choice); + m_xCompressionSlider->set_sensitive(choice); + m_xQualityMF->set_sensitive(!choice); + m_xQualitySlider->set_sensitive(!choice); + Update(); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, ToggleReduceResolutionRB, weld::Toggleable&, void ) +{ + bool choice = m_xReduceResolutionCB->get_active(); + m_xMFNewWidth->set_sensitive(choice); + m_xMFNewHeight->set_sensitive(choice); + m_xResolutionLB->set_sensitive(choice); + m_xInterpolationCombo->set_sensitive(choice); + Update(); +} + +IMPL_LINK_NOARG( CompressGraphicsDialog, CalculateClickHdl, weld::Button&, void ) +{ + sal_Int32 aSize = 0; + + if ( m_dResolution > 0.0 ) + { + SvMemoryStream aMemStream; + aMemStream.SetVersion( SOFFICE_FILEFORMAT_CURRENT ); + Compress( aMemStream ); + aSize = aMemStream.TellEnd(); + } + + if ( aSize > 0 ) + { + OUString aSizeAsString = OUString::number(aSize / 1024); + + OUString aReductionSizeAsString; + if (m_aNativeSize > 0 ) + aReductionSizeAsString = OUString::number( static_cast<sal_Int32>((m_aNativeSize - aSize) * 100.0 / m_aNativeSize) ); + else + aReductionSizeAsString = "0"; + + OUString aNewSizeString = SvxResId(STR_IMAGE_CAPACITY_WITH_REDUCTION); + aNewSizeString = aNewSizeString.replaceAll("$(CAPACITY)", aSizeAsString); + aNewSizeString = aNewSizeString.replaceAll("$(REDUCTION)", aReductionSizeAsString); + m_xFixedText6->set_label(aNewSizeString); + } +} + +tools::Rectangle CompressGraphicsDialog::GetScaledCropRectangle() const +{ + if ( m_xReduceResolutionCB->get_active() ) + { + tools::Long nPixelX = static_cast<tools::Long>( GetViewWidthInch() * m_dResolution ); + tools::Long nPixelY = static_cast<tools::Long>( GetViewHeightInch() * m_dResolution ); + Size aSize = m_aGraphic.GetBitmapEx().GetSizePixel(); + double aScaleX = nPixelX / static_cast<double>(aSize.Width()); + double aScaleY = nPixelY / static_cast<double>(aSize.Height()); + + return tools::Rectangle( + m_aCropRectangle.Left() * aScaleX, + m_aCropRectangle.Top() * aScaleY, + m_aCropRectangle.Right() * aScaleX, + m_aCropRectangle.Bottom()* aScaleY); + } + else + { + return m_aCropRectangle; + } +} + +Graphic CompressGraphicsDialog::GetCompressedGraphic() +{ + if ( m_dResolution > 0.0 ) + { + SvMemoryStream aMemStream; + aMemStream.SetVersion( SOFFICE_FILEFORMAT_CURRENT ); + Compress( aMemStream ); + aMemStream.Seek( STREAM_SEEK_TO_BEGIN ); + Graphic aResultGraphic; + GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter(); + rFilter.ImportGraphic( aResultGraphic, u"import", aMemStream ); + + return aResultGraphic; + } + return Graphic(); +} + +rtl::Reference<SdrGrafObj> CompressGraphicsDialog::GetCompressedSdrGrafObj() +{ + if ( m_dResolution > 0.0 ) + { + rtl::Reference<SdrGrafObj> pNewObject = SdrObject::Clone(*m_xGraphicObj, m_xGraphicObj->getSdrModelFromSdrObject()); + + if ( m_xReduceResolutionCB->get_active() ) + { + tools::Rectangle aScaledCropedRectangle = GetScaledCropRectangle(); + SdrGrafCropItem aNewCrop( + aScaledCropedRectangle.Left(), + aScaledCropedRectangle.Top(), + aScaledCropedRectangle.Right(), + aScaledCropedRectangle.Bottom()); + + pNewObject->SetMergedItem(aNewCrop); + } + pNewObject->SetGraphic( GetCompressedGraphic() ); + + return pNewObject; + } + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/connctrl.cxx b/svx/source/dialog/connctrl.cxx new file mode 100644 index 0000000000..13677849fa --- /dev/null +++ b/svx/source/dialog/connctrl.cxx @@ -0,0 +1,304 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/svapp.hxx> + +#include <svx/connctrl.hxx> +#include <svx/dlgutil.hxx> + +#include <svx/sdr/contact/displayinfo.hxx> +#include <sdr/contact/objectcontactofobjlistpainter.hxx> +#include <svx/svdmark.hxx> +#include <svx/svdoedge.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdview.hxx> +#include <svx/sxelditm.hxx> + +#include <vcl/settings.hxx> +#include <memory> + +SvxXConnectionPreview::SvxXConnectionPreview() + : pView(nullptr) +{ + SetMapMode(MapMode(MapUnit::Map100thMM)); +} + +void SvxXConnectionPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(118 , 121), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); +} + +SvxXConnectionPreview::~SvxXConnectionPreview() +{ +} + +void SvxXConnectionPreview::Resize() +{ + AdaptSize(); + + Invalidate(); +} + +void SvxXConnectionPreview::AdaptSize() +{ + // Adapt size + if( !mxSdrPage ) + return; + + SetMapMode(MapMode(MapUnit::Map100thMM)); + + OutputDevice* pOD = pView->GetFirstOutputDevice(); // GetWin( 0 ); + tools::Rectangle aRect = mxSdrPage->GetAllObjBoundRect(); + + MapMode aMapMode = GetMapMode(); + aMapMode.SetMapUnit( pOD->GetMapMode().GetMapUnit() ); + SetMapMode( aMapMode ); + + MapMode aDisplayMap( aMapMode ); + Point aNewPos; + Size aNewSize; + const Size aWinSize = GetDrawingArea()->get_ref_device().PixelToLogic(GetOutputSizePixel(), aDisplayMap); + const tools::Long nWidth = aWinSize.Width(); + const tools::Long nHeight = aWinSize.Height(); + if (aRect.GetHeight() == 0) + return; + double fRectWH = static_cast<double>(aRect.GetWidth()) / aRect.GetHeight(); + if (nHeight == 0) + return; + double fWinWH = static_cast<double>(nWidth) / nHeight; + + // Adapt bitmap to Thumb size (not here!) + if ( fRectWH < fWinWH) + { + aNewSize.setWidth( static_cast<tools::Long>( static_cast<double>(nHeight) * fRectWH ) ); + aNewSize.setHeight( nHeight ); + } + else + { + aNewSize.setWidth( nWidth ); + aNewSize.setHeight( static_cast<tools::Long>( static_cast<double>(nWidth) / fRectWH ) ); + } + + Fraction aFrac1( aWinSize.Width(), aRect.GetWidth() ); + Fraction aFrac2( aWinSize.Height(), aRect.GetHeight() ); + Fraction aMinFrac( aFrac1 <= aFrac2 ? aFrac1 : aFrac2 ); + + // Implement MapMode + aDisplayMap.SetScaleX( aMinFrac ); + aDisplayMap.SetScaleY( aMinFrac ); + + // Centering + aNewPos.setX( ( nWidth - aNewSize.Width() ) >> 1 ); + aNewPos.setY( ( nHeight - aNewSize.Height() ) >> 1 ); + + aDisplayMap.SetOrigin(OutputDevice::LogicToLogic(aNewPos, aMapMode, aDisplayMap)); + SetMapMode( aDisplayMap ); + + // Origin + aNewPos = aDisplayMap.GetOrigin(); + aNewPos -= Point( aRect.Left(), aRect.Top() ); + aDisplayMap.SetOrigin( aNewPos ); + SetMapMode( aDisplayMap ); + + MouseEvent aMEvt( Point(), 1, MouseEventModifiers::NONE, MOUSE_RIGHT ); + MouseButtonDown( aMEvt ); +} + +void SvxXConnectionPreview::Construct() +{ + DBG_ASSERT( pView, "No valid view is passed on! "); + + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + + if( nMarkCount >= 1 ) + { + bool bFound = false; + + for( size_t i = 0; i < nMarkCount && !bFound; ++i ) + { + const SdrObject* pObj = rMarkList.GetMark( i )->GetMarkedSdrObj(); + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nId = pObj->GetObjIdentifier(); + if( nInv == SdrInventor::Default && nId == SdrObjKind::Edge ) + { + bFound = true; + + // potential memory leak here (!). Create SdrObjList only when there is + // not yet one. + if(!mxSdrPage) + { + mxSdrPage = new SdrPage( + pView->getSdrModelFromSdrView(), + false); + } + + const SdrEdgeObj* pTmpEdgeObj = static_cast<const SdrEdgeObj*>(pObj); + pEdgeObj = SdrObject::Clone(*pTmpEdgeObj, mxSdrPage->getSdrModelFromSdrPage()); + + SdrObjConnection& rConn1 = pEdgeObj->GetConnection( true ); + SdrObjConnection& rConn2 = pEdgeObj->GetConnection( false ); + + rConn1 = pTmpEdgeObj->GetConnection( true ); + rConn2 = pTmpEdgeObj->GetConnection( false ); + + SdrObject* pTmpObj1 = pTmpEdgeObj->GetConnectedNode( true ); + SdrObject* pTmpObj2 = pTmpEdgeObj->GetConnectedNode( false ); + + if( pTmpObj1 ) + { + rtl::Reference<SdrObject> pObj1 = pTmpObj1->CloneSdrObject(mxSdrPage->getSdrModelFromSdrPage()); + mxSdrPage->InsertObject( pObj1.get() ); + pEdgeObj->ConnectToNode( true, pObj1.get() ); + } + + if( pTmpObj2 ) + { + rtl::Reference<SdrObject> pObj2 = pTmpObj2->CloneSdrObject(mxSdrPage->getSdrModelFromSdrPage()); + mxSdrPage->InsertObject( pObj2.get() ); + pEdgeObj->ConnectToNode( false, pObj2.get() ); + } + + mxSdrPage->InsertObject( pEdgeObj.get() ); + } + } + } + + if( !pEdgeObj ) + { + pEdgeObj = new SdrEdgeObj(pView->getSdrModelFromSdrView()); + } + + AdaptSize(); +} + +void SvxXConnectionPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.Push(vcl::PushFlags::ALL); + + rRenderContext.SetMapMode(GetMapMode()); + + const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); + rRenderContext.SetDrawMode(rStyles.GetHighContrastMode() ? OUTPUT_DRAWMODE_CONTRAST : OUTPUT_DRAWMODE_COLOR); + rRenderContext.SetBackground(Wallpaper(rStyles.GetFieldColor())); + + if (mxSdrPage) + { + // This will not work anymore. To not start at Adam and Eve, i will + // ATM not try to change all this stuff to really using an own model + // and a view. I will just try to provide a mechanism to paint such + // objects without own model and without a page/view with the new + // mechanism. + + // New stuff: Use an ObjectContactOfObjListPainter. + sdr::contact::SdrObjectVector aObjectVector; + aObjectVector.reserve(mxSdrPage->GetObjCount()); + for (const rtl::Reference<SdrObject>& pObject : *mxSdrPage) + aObjectVector.push_back(pObject.get()); + + sdr::contact::ObjectContactOfObjListPainter aPainter(rRenderContext, std::move(aObjectVector), nullptr); + sdr::contact::DisplayInfo aDisplayInfo; + + // do processing + aPainter.ProcessDisplay(aDisplayInfo); + } + + rRenderContext.Pop(); +} + +void SvxXConnectionPreview::SetAttributes( const SfxItemSet& rInAttrs ) +{ + pEdgeObj->SetMergedItemSetAndBroadcast(rInAttrs); + + Invalidate(); +} + +// Get number of lines which are offset based on the preview object + +sal_uInt16 SvxXConnectionPreview::GetLineDeltaCount() const +{ + const SfxItemSet& rSet = pEdgeObj->GetMergedItemSet(); + sal_uInt16 nCount(0); + + if(SfxItemState::DONTCARE != rSet.GetItemState(SDRATTR_EDGELINEDELTACOUNT)) + nCount = rSet.Get(SDRATTR_EDGELINEDELTACOUNT).GetValue(); + + return nCount; +} + +bool SvxXConnectionPreview::MouseButtonDown( const MouseEvent& rMEvt ) +{ + bool bZoomIn = rMEvt.IsLeft() && !rMEvt.IsShift(); + bool bZoomOut = rMEvt.IsRight() || rMEvt.IsShift(); + bool bCtrl = rMEvt.IsMod1(); + + if( bZoomIn || bZoomOut ) + { + MapMode aMapMode = GetMapMode(); + Fraction aXFrac = aMapMode.GetScaleX(); + Fraction aYFrac = aMapMode.GetScaleY(); + std::unique_ptr<Fraction> pMultFrac; + + if( bZoomIn ) + { + if( bCtrl ) + pMultFrac.reset(new Fraction( 3, 2 )); + else + pMultFrac.reset(new Fraction( 11, 10 )); + } + else + { + if( bCtrl ) + pMultFrac.reset(new Fraction( 2, 3 )); + else + pMultFrac.reset(new Fraction( 10, 11 )); + } + + aXFrac *= *pMultFrac; + aYFrac *= *pMultFrac; + if( static_cast<double>(aXFrac) > 0.001 && static_cast<double>(aXFrac) < 1000.0 && + static_cast<double>(aYFrac) > 0.001 && static_cast<double>(aYFrac) < 1000.0 ) + { + aMapMode.SetScaleX( aXFrac ); + aMapMode.SetScaleY( aYFrac ); + SetMapMode( aMapMode ); + + Size aOutSize(GetOutputSizePixel()); + aOutSize = GetDrawingArea()->get_ref_device().PixelToLogic(aOutSize); + + Point aPt( aMapMode.GetOrigin() ); + tools::Long nX = static_cast<tools::Long>( ( static_cast<double>(aOutSize.Width()) - ( static_cast<double>(aOutSize.Width()) * static_cast<double>(*pMultFrac) ) ) / 2.0 + 0.5 ); + tools::Long nY = static_cast<tools::Long>( ( static_cast<double>(aOutSize.Height()) - ( static_cast<double>(aOutSize.Height()) * static_cast<double>(*pMultFrac) ) ) / 2.0 + 0.5 ); + aPt.AdjustX(nX ); + aPt.AdjustY(nY ); + + aMapMode.SetOrigin( aPt ); + SetMapMode( aMapMode ); + + Invalidate(); + } + } + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/contimp.hxx b/svx/source/dialog/contimp.hxx new file mode 100644 index 0000000000..2bf5aca4d1 --- /dev/null +++ b/svx/source/dialog/contimp.hxx @@ -0,0 +1,132 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_SVX_SOURCE_DIALOG_CONTIMP_HXX +#define INCLUDED_SVX_SOURCE_DIALOG_CONTIMP_HXX + +#include <sfx2/ctrlitem.hxx> +#include "contwnd.hxx" +#include <vcl/idle.hxx> + +class SvxSuperContourDlg; + +class SvxContourDlgItem : public SfxControllerItem +{ + SvxSuperContourDlg& rDlg; + +protected: + + virtual void StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override; + +public: + + SvxContourDlgItem( SvxSuperContourDlg& rDlg, SfxBindings& rBindings ); +}; + +class ContourWindow; + +class StatusColor : public weld::CustomWidgetController +{ +private: + ContourWindow& m_rWnd; +public: + StatusColor(ContourWindow& rWnd) + : m_rWnd(rWnd) + { + } + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override + { + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aSize(pDrawingArea->get_approximate_digit_width() * 3, + pDrawingArea->get_text_height()); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); + } +}; + +class SvxSuperContourDlg +{ + Graphic aGraphic; + Graphic aUndoGraphic; + Graphic aRedoGraphic; + Graphic aUpdateGraphic; + tools::PolyPolygon aUpdatePolyPoly; + Idle aUpdateIdle; + Idle aCreateIdle; + SfxBindings* mpBindings; + void* pUpdateEditingObject; + void* pCheckObj; + SvxContourDlgItem aContourItem; + sal_Int32 mnGrfChanged; + bool bExecState; + bool bUpdateGraphicLinked; + bool bGraphicLinked; + + weld::Dialog& m_rDialog; + std::unique_ptr<ContourWindow> m_xContourWnd; + std::unique_ptr<StatusColor> m_xStbStatusColor; + std::unique_ptr<weld::Toolbar> m_xTbx1; + std::unique_ptr<weld::MetricSpinButton> m_xMtfTolerance; + std::unique_ptr<weld::Label> m_xStbStatus2; + std::unique_ptr<weld::Label> m_xStbStatus3; + std::unique_ptr<weld::Button> m_xCancelBtn; + std::unique_ptr<weld::CustomWeld> m_xStbStatusColorWeld; + std::unique_ptr<weld::CustomWeld> m_xContourWndWeld; + + DECL_LINK( Tbx1ClickHdl, const OUString&, void ); + DECL_LINK( MousePosHdl, GraphCtrl*, void ); + DECL_LINK( GraphSizeHdl, GraphCtrl*, void ); + DECL_LINK( UpdateHdl, Timer *, void ); + DECL_LINK( CreateHdl, Timer *, void ); + DECL_LINK( StateHdl, GraphCtrl*, void ); + DECL_LINK( PipetteHdl, ContourWindow&, void ); + DECL_LINK( PipetteClickHdl, ContourWindow&, void ); + DECL_LINK( WorkplaceClickHdl, ContourWindow&, void ); + DECL_LINK( CancelHdl, weld::Button&, void ); + + void SetActiveTool(std::u16string_view rId); + void SetActivePoly(std::u16string_view rId); + + SfxBindings& GetBindings() { return *mpBindings; } + +public: + + SvxSuperContourDlg(weld::Builder& rBuilder, weld::Dialog& rDialog, SfxBindings* pBindings); + ~SvxSuperContourDlg(); + + void SetExecState( bool bEnable ); + + void SetGraphic( const Graphic& rGraphic ); + const Graphic& GetGraphic() const { return aGraphic; } + bool IsGraphicChanged() const { return mnGrfChanged > 0; } + + void SetPolyPolygon( const tools::PolyPolygon& rPolyPoly ); + tools::PolyPolygon GetPolyPolygon(); + + const void* GetEditingObject() const { return pCheckObj; } + + void UpdateGraphic( const Graphic& rGraphic, bool bGraphicLinked, + const tools::PolyPolygon* pPolyPoly, + void* pEditingObj ); +}; + + +#endif // INCLUDED_SVX_SOURCE_DIALOG_CONTIMP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/contwnd.cxx b/svx/source/dialog/contwnd.cxx new file mode 100644 index 0000000000..976897b6ba --- /dev/null +++ b/svx/source/dialog/contwnd.cxx @@ -0,0 +1,271 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "contwnd.hxx" +#include <svx/svdpage.hxx> +#include <svx/svdopath.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xfltrit.hxx> +#include <svx/xflclit.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <vcl/ptrstyle.hxx> + +using namespace css; + +#define TRANSCOL COL_WHITE + +ContourWindow::ContourWindow(weld::Dialog* pDialog) + : GraphCtrl(pDialog) + , aWorkRect(0, 0, 0, 0) + , bPipetteMode(false) + , bWorkplaceMode(false) + , bClickValid(false) +{ +} + +void ContourWindow::SetPolyPolygon(const tools::PolyPolygon& rPolyPoly) +{ + SdrPage* pPage = pModel->GetPage(0); + const sal_uInt16 nPolyCount = rPolyPoly.Count(); + + // First delete all drawing objects + aPolyPoly = rPolyPoly; + + // To avoid to have destroyed objects which are still selected, it is necessary to deselect + // them first (!) + pView->UnmarkAllObj(); + + // clear SdrObjects with broadcasting + pPage->ClearSdrObjList(); + + for (sal_uInt16 i = 0; i < nPolyCount; i++) + { + basegfx::B2DPolyPolygon aPolyPolygon; + aPolyPolygon.append(aPolyPoly[ i ].getB2DPolygon()); + rtl::Reference<SdrPathObj> pPathObj = new SdrPathObj( + *pModel, + SdrObjKind::PathFill, + std::move(aPolyPolygon)); + + SfxItemSet aSet(pModel->GetItemPool()); + + aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); + aSet.Put(XFillColorItem("", TRANSCOL)); + aSet.Put(XFillTransparenceItem(50) ); + + pPathObj->SetMergedItemSetAndBroadcast(aSet); + + pPage->InsertObject( pPathObj.get() ); + } + + if (nPolyCount) + { + pView->MarkAll(); + pView->CombineMarkedObjects(false); + } + + pModel->SetChanged(false); +} + +const tools::PolyPolygon& ContourWindow::GetPolyPolygon() +{ + if ( pModel->IsChanged() ) + { + SdrPage* pPage = pModel->GetPage( 0 ); + + aPolyPoly = tools::PolyPolygon(); + + if ( pPage && pPage->GetObjCount() ) + { + SdrPathObj* pPathObj = static_cast<SdrPathObj*>(pPage->GetObj(0)); + // Not sure if subdivision is needed for ContourWindow, but maybe it cannot handle + // curves at all. Keeping subdivision here for security + const basegfx::B2DPolyPolygon aB2DPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(pPathObj->GetPathPoly())); + aPolyPoly = tools::PolyPolygon(aB2DPolyPolygon); + } + + pModel->SetChanged( false ); + } + + return aPolyPoly; +} + +void ContourWindow::InitSdrModel() +{ + GraphCtrl::InitSdrModel(); + + SfxItemSet aSet( pModel->GetItemPool() ); + + aSet.Put( XFillColorItem( "", TRANSCOL ) ); + aSet.Put( XFillTransparenceItem( 50 ) ); + pView->SetAttributes( aSet ); + pView->SetFrameDragSingles(); +} + +void ContourWindow::SdrObjCreated( const SdrObject& ) +{ + pView->MarkAll(); + pView->CombineMarkedObjects( false ); +} + +bool ContourWindow::IsContourChanged() const +{ + SdrPage* pPage = pModel->GetPage( 0 ); + bool bRet = false; + + if ( pPage && pPage->GetObjCount() ) + bRet = static_cast<SdrPathObj*>( pPage->GetObj( 0 ) )->GetPathPoly().count() && pModel->IsChanged(); + + return bRet; +} + +bool ContourWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( bWorkplaceMode ) + { + const Point aLogPt(GetDrawingArea()->get_ref_device().PixelToLogic(rMEvt.GetPosPixel())); + + SetPolyPolygon( tools::PolyPolygon() ); + aWorkRect = tools::Rectangle( aLogPt, aLogPt ); + Invalidate(tools::Rectangle(Point(), GetGraphicSize())); + SetEditMode( true ); + } + + if (!bPipetteMode) + return GraphCtrl::MouseButtonDown( rMEvt ); + + return true; +} + +bool ContourWindow::MouseMove( const MouseEvent& rMEvt ) +{ + bClickValid = false; + + if ( bPipetteMode ) + { + const Point aLogPt( GetDrawingArea()->get_ref_device().PixelToLogic( rMEvt.GetPosPixel() ) ); + + aPipetteColor = GetDrawingArea()->get_ref_device().GetPixel( aLogPt ); + weld::CustomWidgetController::MouseMove( rMEvt ); + + if ( aPipetteLink.IsSet() && tools::Rectangle( Point(), GetGraphicSize() ).Contains( aLogPt ) ) + { + SetPointer( PointerStyle::RefHand ); + aPipetteLink.Call( *this ); + } + + return true; + } + + return GraphCtrl::MouseMove( rMEvt ); +} + +bool ContourWindow::MouseButtonUp(const MouseEvent& rMEvt) +{ + const tools::Rectangle aGraphRect( Point(), GetGraphicSize() ); + const Point aLogPt( GetDrawingArea()->get_ref_device().PixelToLogic( rMEvt.GetPosPixel() ) ); + + bClickValid = aGraphRect.Contains( aLogPt ); + ReleaseMouse(); + + if ( bPipetteMode ) + { + weld::CustomWidgetController::MouseButtonUp( rMEvt ); + + aPipetteClickLink.Call( *this ); + + return true; + } + else if ( bWorkplaceMode ) + { + GraphCtrl::MouseButtonUp( rMEvt ); + + aWorkRect.SetRight( aLogPt.X() ); + aWorkRect.SetBottom( aLogPt.Y() ); + aWorkRect.Intersection( aGraphRect ); + aWorkRect.Normalize(); + + if ( aWorkRect.Left() != aWorkRect.Right() && aWorkRect.Top() != aWorkRect.Bottom() ) + { + tools::PolyPolygon _aPolyPoly( GetPolyPolygon() ); + + _aPolyPoly.Clip( aWorkRect ); + SetPolyPolygon( _aPolyPoly ); + pView->SetWorkArea( aWorkRect ); + } + else + pView->SetWorkArea( aGraphRect ); + + Invalidate( aGraphRect ); + + aWorkplaceClickLink.Call( *this ); + + return false; + } + + return GraphCtrl::MouseButtonUp( rMEvt ); +} + +void ContourWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + // #i75482# + // encapsulate the redraw using Begin/End and use the returned + // data to get the target output device (e.g. when pre-rendering) + SdrPaintWindow* pPaintWindow = pView->BeginCompleteRedraw(&rRenderContext); + pPaintWindow->SetOutputToWindow(true); + OutputDevice& rTarget = pPaintWindow->GetTargetOutputDevice(); + + const Graphic& rGraphic = GetGraphic(); + rTarget.Push(vcl::PushFlags::LINECOLOR |vcl::PushFlags::FILLCOLOR); + rTarget.SetLineColor(COL_BLACK); + rTarget.SetFillColor(COL_WHITE); + rTarget.DrawRect( tools::Rectangle( Point(), GetGraphicSize() ) ); + rTarget.Pop(); + + if (rGraphic.GetType() != GraphicType::NONE) + rGraphic.Draw(rTarget, Point(), GetGraphicSize()); + + if (aWorkRect.Left() != aWorkRect.Right() && aWorkRect.Top() != aWorkRect.Bottom()) + { + tools::PolyPolygon _aPolyPoly(2); + rTarget.Push(vcl::PushFlags::FILLCOLOR); + _aPolyPoly.Insert(tools::Polygon(tools::Rectangle(Point(), GetGraphicSize()))); + _aPolyPoly.Insert(tools::Polygon(aWorkRect)); + rTarget.SetFillColor(COL_LIGHTRED); + rTarget.DrawTransparent(_aPolyPoly, 50); + rTarget.Pop(); + } + + // #i75482# + const vcl::Region aRepaintRegion(rRect); + pView->DoCompleteRedraw(*pPaintWindow, aRepaintRegion); + pView->EndCompleteRedraw(*pPaintWindow, true); +} + +void ContourWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + GraphCtrl::SetDrawingArea(pDrawingArea); + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(270, 170), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); + SetSdrMode(true); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/contwnd.hxx b/svx/source/dialog/contwnd.hxx new file mode 100644 index 0000000000..1bad623c05 --- /dev/null +++ b/svx/source/dialog/contwnd.hxx @@ -0,0 +1,70 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SVX_SOURCE_DIALOG_CONTWND_HXX +#define INCLUDED_SVX_SOURCE_DIALOG_CONTWND_HXX + +#include <tools/poly.hxx> +#include <svx/graphctl.hxx> + +class ContourWindow final : public GraphCtrl +{ + tools::PolyPolygon aPolyPoly; + Color aPipetteColor; + tools::Rectangle aWorkRect; + Link<ContourWindow&,void> aPipetteLink; + Link<ContourWindow&,void> aPipetteClickLink; + Link<ContourWindow&,void> aWorkplaceClickLink; + bool bPipetteMode; + bool bWorkplaceMode; + bool bClickValid; + + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void SdrObjCreated( const SdrObject& rObj ) override; + virtual void InitSdrModel() override; + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + +public: + + ContourWindow(weld::Dialog* pDialog); + + void SetPolyPolygon( const tools::PolyPolygon& rPolyPoly ); + const tools::PolyPolygon& GetPolyPolygon(); + + void SetPipetteMode( const bool bPipette ) { bPipetteMode = bPipette; } + const Color& GetPipetteColor() const { return aPipetteColor; } + + bool IsClickValid() const { return bClickValid; } + bool IsContourChanged() const; + + void SetWorkplaceMode( const bool bWorkplace ) { bWorkplaceMode = bWorkplace; } + const tools::Rectangle& GetWorkRect() const { return aWorkRect; } + + void SetPipetteHdl( const Link<ContourWindow&,void>& rLink ) { aPipetteLink = rLink; } + void SetPipetteClickHdl( const Link<ContourWindow&,void>& rLink ) { aPipetteClickLink = rLink; } + void SetWorkplaceClickHdl( const Link<ContourWindow&,void>& rLink ) { aWorkplaceClickLink = rLink; } +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/crashreportdlg.cxx b/svx/source/dialog/crashreportdlg.cxx new file mode 100644 index 0000000000..aad28436ee --- /dev/null +++ b/svx/source/dialog/crashreportdlg.cxx @@ -0,0 +1,124 @@ +/* -*- 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 "crashreportdlg.hxx" + +#include <desktop/crashreport.hxx> +#include <sfx2/safemode.hxx> +#include <comphelper/processfactory.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <unotools/configmgr.hxx> +#include <officecfg/Office/Common.hxx> +#include <vcl/svapp.hxx> + +#include <com/sun/star/task/OfficeRestartManager.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> + +IMPL_STATIC_LINK_NOARG(CrashReportDialog, InstallLOKNotifierHdl, void*, + vcl::ILibreOfficeKitNotifier*) +{ + return GetpApp(); +} + +CrashReportDialog::CrashReportDialog(weld::Window* pParent) + : GenericDialogController(pParent, "svx/ui/crashreportdlg.ui", "CrashReportDialog") + , mxBtnSend(m_xBuilder->weld_button("btn_send")) + , mxBtnCancel(m_xBuilder->weld_button("btn_cancel")) + , mxBtnClose(m_xBuilder->weld_button("btn_close")) + , mxEditPreUpload(m_xBuilder->weld_label("ed_pre")) + , mxEditPostUpload(m_xBuilder->weld_label("ed_post")) + , mxLinkButton(m_xBuilder->weld_link_button("linkbutton")) + , mxFtBugReport(m_xBuilder->weld_label("ed_bugreport")) + , mxCBSafeMode(m_xBuilder->weld_check_button("check_safemode")) + , mxPrivacyPolicyButton(m_xBuilder->weld_link_button("btnPrivacyPolicy")) +{ + maLinkTemplate = mxLinkButton->get_uri(); + + auto nWidth = mxEditPreUpload->get_preferred_size().Width(); + nWidth = std::max(nWidth, mxCBSafeMode->get_size_request().Width()); + mxEditPreUpload->set_size_request(nWidth, -1); + mxCBSafeMode->set_size_request(nWidth, -1); + + mxBtnSend->connect_clicked(LINK(this, CrashReportDialog, BtnHdl)); + mxBtnCancel->connect_clicked(LINK(this, CrashReportDialog, BtnHdl)); + mxBtnClose->connect_clicked(LINK(this, CrashReportDialog, BtnHdl)); + + mxPrivacyPolicyButton->set_uri( + officecfg::Office::Common::Menus::PrivacyPolicyURL::get() + + "?type=crashreport&LOvers=" + utl::ConfigManager::getProductVersion() + + "&LOlocale=" + LanguageTag(utl::ConfigManager::getUILocale()).getBcp47()); + + m_xDialog->SetInstallLOKNotifierHdl(LINK(this, CrashReportDialog, InstallLOKNotifierHdl)); +} + +CrashReportDialog::~CrashReportDialog() {} + +short CrashReportDialog::run() +{ + short nRet = GenericDialogController::run(); + + // Check whether to go to safe mode + if (mxCBSafeMode->get_active()) + { + sfx2::SafeMode::putFlag(); + css::task::OfficeRestartManager::get(comphelper::getProcessComponentContext()) + ->requestRestart(css::uno::Reference<css::task::XInteractionHandler>()); + } + return nRet; +} + +IMPL_LINK(CrashReportDialog, BtnHdl, weld::Button&, rBtn, void) +{ + if (&rBtn == mxBtnSend.get()) + { + std::string response; + bool bSuccess = CrashReporter::readSendConfig(response); + + OUString aCrashID = OUString::createFromAscii(response); + + if (bSuccess) + { + OUString aProcessedLink + = maLinkTemplate.replaceAll("%CRASHID", aCrashID.replaceAll("Crash-ID=", "")); + + // vclbuilder seems to replace _ with ~ even in text + mxLinkButton->set_label(aProcessedLink.replaceAll("~", "_")); + mxLinkButton->set_uri(aProcessedLink); + } + else + { + mxEditPostUpload->set_label(aCrashID); + } + + mxLinkButton->set_visible(bSuccess); + + mxBtnClose->show(); + mxFtBugReport->show(); + mxEditPostUpload->show(); + mxBtnSend->set_sensitive(false); + mxBtnCancel->set_sensitive(false); + mxBtnClose->grab_focus(); + + mxEditPreUpload->hide(); + mxBtnSend->hide(); + mxBtnCancel->hide(); + + m_xDialog->resize_to_request(); + } + else if (&rBtn == mxBtnCancel.get()) + { + m_xDialog->response(RET_CLOSE); + } + else if (&rBtn == mxBtnClose.get()) + { + m_xDialog->response(RET_CLOSE); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/crashreportdlg.hxx b/svx/source/dialog/crashreportdlg.hxx new file mode 100644 index 0000000000..3f2d9cb121 --- /dev/null +++ b/svx/source/dialog/crashreportdlg.hxx @@ -0,0 +1,42 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SVX_SOURCE_DIALOG_CRASHREPORTDLG_HXX +#define INCLUDED_SVX_SOURCE_DIALOG_CRASHREPORTDLG_HXX + +#include <vcl/weld.hxx> + +class CrashReportDialog : public weld::GenericDialogController +{ +public: + explicit CrashReportDialog(weld::Window* pParent); + virtual short run() override; + virtual ~CrashReportDialog() override; + +private: + std::unique_ptr<weld::Button> mxBtnSend; + std::unique_ptr<weld::Button> mxBtnCancel; + std::unique_ptr<weld::Button> mxBtnClose; + std::unique_ptr<weld::Label> mxEditPreUpload; + std::unique_ptr<weld::Label> mxEditPostUpload; + std::unique_ptr<weld::LinkButton> mxLinkButton; + std::unique_ptr<weld::Label> mxFtBugReport; + std::unique_ptr<weld::CheckButton> mxCBSafeMode; + std::unique_ptr<weld::LinkButton> mxPrivacyPolicyButton; + + OUString maLinkTemplate; + + DECL_LINK(BtnHdl, weld::Button&, void); + DECL_STATIC_LINK(CrashReportDialog, InstallLOKNotifierHdl, void*, + vcl::ILibreOfficeKitNotifier*); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/crashreportui.cxx b/svx/source/dialog/crashreportui.cxx new file mode 100644 index 0000000000..5384e3c18c --- /dev/null +++ b/svx/source/dialog/crashreportui.cxx @@ -0,0 +1,81 @@ +/* -*- 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 <cppuhelper/implbase.hxx> +#include <com/sun/star/frame/XSynchronousDispatch.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <cppuhelper/supportsservice.hxx> + +#include <vcl/svapp.hxx> + +#include "crashreportdlg.hxx" + +namespace { + +class CrashReportUI : public ::cppu::WeakImplHelper< css::lang::XServiceInfo , + css::frame::XSynchronousDispatch > // => XDispatch! +{ +public: + explicit CrashReportUI(); + + // css.lang.XServiceInfo + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + + virtual css::uno::Any SAL_CALL dispatchWithReturnValue(const css::util::URL& aURL, + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; +}; + +CrashReportUI::CrashReportUI() +{ + +} + +OUString SAL_CALL CrashReportUI::getImplementationName() +{ + return "com.sun.star.comp.svx.CrashReportUI"; +} + +sal_Bool SAL_CALL CrashReportUI::supportsService(const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL CrashReportUI::getSupportedServiceNames() +{ + return { "com.sun.star.dialog.CrashReportUI" }; +} + +css::uno::Any SAL_CALL CrashReportUI::dispatchWithReturnValue(const css::util::URL&, + const css::uno::Sequence< css::beans::PropertyValue >& ) +{ + SolarMutexGuard aGuard; + css::uno::Any aRet; + CrashReportDialog aDialog(nullptr); + aDialog.run(); + return aRet; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_svx_CrashReportUI_get_implementation( + css::uno::XComponentContext * /*context*/, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new CrashReportUI()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/ctredlin.cxx b/svx/source/dialog/ctredlin.cxx new file mode 100644 index 0000000000..6df9dbd283 --- /dev/null +++ b/svx/source/dialog/ctredlin.cxx @@ -0,0 +1,1002 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <o3tl/safeint.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/weldutils.hxx> +#include <svtools/ctrlbox.hxx> +#include <unotools/textsearch.hxx> + +#include <helpids.h> + +#include <svx/ctredlin.hxx> + +#define WRITER_DATE 2 +#define CALC_DATE 3 + +RedlinData::RedlinData() + : aDateTime(DateTime::EMPTY) + , pData(nullptr) + , eType(RedlineType::Any) + , bDisabled(false) +{ +} + +RedlinData::~RedlinData() +{ +} + +SvxRedlinTable::SvxRedlinTable(std::unique_ptr<weld::TreeView> xWriterControl, + std::unique_ptr<weld::TreeView> xCalcControl) + : xSorter(new comphelper::string::NaturalStringSorter(::comphelper::getProcessComponentContext(), + Application::GetSettings().GetUILanguageTag().getLocale())) + , xWriterTreeView(std::move(xWriterControl)) + , xCalcTreeView(std::move(xCalcControl)) + , pTreeView(nullptr) + , nDatePos(WRITER_DATE) + , bAuthor(false) + , bDate(false) + , bComment(false) + , bSorted(false) + , nDaTiMode(SvxRedlinDateMode::BEFORE) + , aDaTiFirst( DateTime::EMPTY ) + , aDaTiLast( DateTime::EMPTY ) + , aDaTiFilterFirst( DateTime::EMPTY ) + , aDaTiFilterLast( DateTime::EMPTY ) +{ + if (xWriterTreeView) + { + xWriterTreeView->set_size_request(-1, xWriterTreeView->get_height_rows(8)); + xWriterTreeView->connect_column_clicked(LINK(this, SvxRedlinTable, HeaderBarClick)); + xWriterTreeView->set_sort_func([this](const weld::TreeIter& rLeft, const weld::TreeIter& rRight){ + return ColCompare(rLeft, rRight); + }); + pTreeView = xWriterTreeView.get(); + } + if (xCalcTreeView) + { + xCalcTreeView->set_size_request(-1, xCalcTreeView->get_height_rows(8)); + xCalcTreeView->connect_column_clicked(LINK(this, SvxRedlinTable, HeaderBarClick)); + xCalcTreeView->set_sort_func([this](const weld::TreeIter& rLeft, const weld::TreeIter& rRight){ + return ColCompare(rLeft, rRight); + }); + pTreeView = xCalcTreeView.get(); + } +} + +SvxRedlinTable::~SvxRedlinTable() +{ +} + +IMPL_LINK(SvxRedlinTable, HeaderBarClick, int, nColumn, void) +{ + if (!bSorted) + { + pTreeView->make_sorted(); + bSorted = true; + } + + bool bSortAtoZ = pTreeView->get_sort_order(); + + //set new arrow positions in headerbar + if (nColumn == pTreeView->get_sort_column()) + { + bSortAtoZ = !bSortAtoZ; + pTreeView->set_sort_order(bSortAtoZ); + } + else + { + int nOldSortColumn = pTreeView->get_sort_column(); + if (nOldSortColumn != -1) + pTreeView->set_sort_indicator(TRISTATE_INDET, nOldSortColumn); + pTreeView->set_sort_column(nColumn); + } + + if (nColumn != -1) + { + //sort lists + pTreeView->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn); + } +} + +int SvxRedlinTable::ColCompare(const weld::TreeIter& rLeft, const weld::TreeIter& rRight) +{ + sal_Int32 nCompare = 0; + + int nSortCol = pTreeView->get_sort_column(); + + if (pTreeView == xWriterTreeView.get() && nSortCol == 0) + { + RedlinData *pLeftData = weld::fromId<RedlinData*>(pTreeView->get_id(rLeft)); + RedlinData *pRightData = weld::fromId<RedlinData*>(pTreeView->get_id(rRight)); + + if (pLeftData && pRightData) + { + if (pLeftData->eType < pRightData->eType) + nCompare = -1; + else if (pLeftData->eType > pRightData->eType) + nCompare = 1; + return nCompare; + } + } + + if (nSortCol == nDatePos) + { + RedlinData *pLeftData = weld::fromId<RedlinData*>(pTreeView->get_id(rLeft)); + RedlinData *pRightData = weld::fromId<RedlinData*>(pTreeView->get_id(rRight)); + + if (pLeftData && pRightData) + { + if (pLeftData->aDateTime < pRightData->aDateTime) + nCompare = -1; + else if (pLeftData->aDateTime > pRightData->aDateTime) + nCompare = 1; + return nCompare; + } + } + + return xSorter->compare(pTreeView->get_text(rLeft, nSortCol), + pTreeView->get_text(rRight, nSortCol)); +} + +void SvxRedlinTable::UpdateFilterTest() +{ + Date aDateMax( Date::SYSTEM ); + aDateMax.AddYears(100); + Date aDateMin(1,1,1989); + tools::Time aTMin(0); + tools::Time aTMax(23,59,59); + + DateTime aDTMin(aDateMin); + DateTime aDTMax(aDateMax); + + switch(nDaTiMode) + { + case SvxRedlinDateMode::BEFORE: + aDaTiFilterFirst=aDTMin; + aDaTiFilterLast=aDaTiFirst; + break; + case SvxRedlinDateMode::SAVE: + case SvxRedlinDateMode::SINCE: + aDaTiFilterFirst=aDaTiFirst; + aDaTiFilterLast=aDTMax; + break; + case SvxRedlinDateMode::EQUAL: + aDaTiFilterFirst=aDaTiFirst; + aDaTiFilterLast=aDaTiFirst; + aDaTiFilterFirst.SetTime(aTMin.GetTime()); + aDaTiFilterLast.SetTime(aTMax.GetTime()); + break; + case SvxRedlinDateMode::NOTEQUAL: + aDaTiFilterFirst=aDaTiFirst; + aDaTiFilterLast=aDaTiFirst; + aDaTiFilterFirst.SetTime(aTMin.GetTime()); + aDaTiFilterLast.SetTime(aTMax.GetTime()); + break; + case SvxRedlinDateMode::BETWEEN: + aDaTiFilterFirst=aDaTiFirst; + aDaTiFilterLast=aDaTiLast; + break; + case SvxRedlinDateMode::NONE: + break; + } +} + +void SvxRedlinTable::SetFilterDate(bool bFlag) +{ + bDate=bFlag; +} + +void SvxRedlinTable::SetDateTimeMode(SvxRedlinDateMode nMode) +{ + nDaTiMode=nMode; +} + +void SvxRedlinTable::SetFirstDate(const Date& aDate) +{ + aDaTiFirst.SetDate(aDate.GetDate()); +} + +void SvxRedlinTable::SetLastDate(const Date& aDate) +{ + aDaTiLast.SetDate(aDate.GetDate()); +} + +void SvxRedlinTable::SetFirstTime(const tools::Time& aTime) +{ + aDaTiFirst.SetTime(aTime.GetTime()); +} + +void SvxRedlinTable::SetLastTime(const tools::Time& aTime) +{ + aDaTiLast.SetTime(aTime.GetTime()); +} + +void SvxRedlinTable::SetFilterAuthor(bool bFlag) +{ + bAuthor=bFlag; +} + +void SvxRedlinTable::SetAuthor(const OUString &aString) +{ + aAuthor=aString; +} + +void SvxRedlinTable::SetFilterComment(bool bFlag) +{ + bComment=bFlag; +} + +void SvxRedlinTable::SetCommentParams( const utl::SearchParam* pSearchPara ) +{ + if(pSearchPara!=nullptr) + { + pCommentSearcher.reset(new utl::TextSearch(*pSearchPara, LANGUAGE_SYSTEM )); + } +} + +bool SvxRedlinTable::IsValidEntry(std::u16string_view rAuthorStr, + const DateTime &rDateTime, + const OUString &rCommentStr) +{ + return IsValidEntry(rAuthorStr, rDateTime) && IsValidComment(rCommentStr); +} + +bool SvxRedlinTable::IsValidEntry(std::u16string_view rAuthorStr, const DateTime &rDateTime) +{ + if (bAuthor && aAuthor!=rAuthorStr) + return false; + + if (!bDate) + return true; + + const bool bRes = rDateTime.IsBetween(aDaTiFilterFirst, aDaTiFilterLast); + return nDaTiMode!=SvxRedlinDateMode::NOTEQUAL ? bRes : !bRes; +} + +bool SvxRedlinTable::IsValidComment(const OUString &rCommentStr) +{ + if (!bComment) + return true; + + sal_Int32 nStartPos = 0; + sal_Int32 nEndPos = rCommentStr.getLength(); + return pCommentSearcher->SearchForward( rCommentStr, &nStartPos, &nEndPos); +} + +SvxTPage::~SvxTPage() +{ +} + +void SvxTPage::ActivatePage() +{ +} + +SvxTPView::SvxTPView(weld::Container* pParent) + : SvxTPage(pParent, "svx/ui/redlineviewpage.ui", "RedlineViewPage") + , bEnableAccept(true) + , bEnableAcceptAll(true) + , bEnableReject(true) + , bEnableRejectAll(true) + , bEnableUndo(true) + , bEnableClearFormat(false) + , bEnableClearFormatAll(false) + , m_xAccept(m_xBuilder->weld_button("accept")) + , m_xReject(m_xBuilder->weld_button("reject")) + , m_xAcceptAll(m_xBuilder->weld_button("acceptall")) + , m_xRejectAll(m_xBuilder->weld_button("rejectall")) + , m_xUndo(m_xBuilder->weld_button("undo")) + , m_xViewData(new SvxRedlinTable(m_xBuilder->weld_tree_view("writerchanges"), + m_xBuilder->weld_tree_view("calcchanges"))) +{ + Link<weld::Button&,void> aLink=LINK( this, SvxTPView, PbClickHdl); + + m_xAccept->connect_clicked(aLink); + m_xAcceptAll->connect_clicked(aLink); + m_xReject->connect_clicked(aLink); + m_xRejectAll->connect_clicked(aLink); + m_xUndo->connect_clicked(aLink); +} + +void SvxTPView::ActivatePage() +{ + m_xAccept->set_sensitive(bEnableAccept); + m_xReject->set_sensitive(bEnableReject); + m_xAcceptAll->set_sensitive(bEnableAcceptAll); + m_xRejectAll->set_sensitive(bEnableRejectAll); + m_xUndo->set_sensitive(bEnableUndo); +} + +void SvxTPView::DeactivatePage() +{ + m_xAccept->set_sensitive(false); + m_xReject->set_sensitive(false); + m_xAcceptAll->set_sensitive(false); + m_xRejectAll->set_sensitive(false); + m_xUndo->set_sensitive(false); +} + +SvxTPView::~SvxTPView() +{ +} + +void SvxRedlinTable::SetWriterView() +{ + nDatePos = WRITER_DATE; + if (xCalcTreeView) + xCalcTreeView->hide(); + xWriterTreeView->show(); + pTreeView = xWriterTreeView.get(); + + auto nDigitWidth = pTreeView->get_approximate_digit_width(); + std::vector<int> aWidths + { + o3tl::narrowing<int>(nDigitWidth * 10), + o3tl::narrowing<int>(nDigitWidth * 20), + o3tl::narrowing<int>(nDigitWidth * 20) + }; + pTreeView->set_column_fixed_widths(aWidths); +} + +void SvxRedlinTable::SetCalcView() +{ + nDatePos = CALC_DATE; + if (xWriterTreeView) + xWriterTreeView->hide(); + xCalcTreeView->show(); + pTreeView = xCalcTreeView.get(); + + auto nDigitWidth = pTreeView->get_approximate_digit_width(); + std::vector<int> aWidths + { + o3tl::narrowing<int>(nDigitWidth * 20), + o3tl::narrowing<int>(nDigitWidth * 20), + o3tl::narrowing<int>(nDigitWidth * 20), + o3tl::narrowing<int>(nDigitWidth * 20) + }; + pTreeView->set_column_fixed_widths(aWidths); +} + +void SvxTPView::EnableAccept(bool bFlag) +{ + bEnableAccept = bFlag; + m_xAccept->set_sensitive(bFlag); +} + +void SvxTPView::EnableAcceptAll(bool bFlag) +{ + bEnableAcceptAll = bFlag; + m_xAcceptAll->set_sensitive(bFlag); +} + +void SvxTPView::EnableReject(bool bFlag) +{ + bEnableReject = bFlag; + m_xReject->set_sensitive(bFlag); +} + +void SvxTPView::EnableRejectAll(bool bFlag) +{ + bEnableRejectAll = bFlag; + m_xRejectAll->set_sensitive(bFlag); +} + +void SvxTPView::EnableClearFormat(bool bFlag) +{ + if (bEnableClearFormat == bFlag) + return; + bEnableClearFormat = bFlag; +} + +void SvxTPView::EnableClearFormatAll(bool bFlag) +{ + if (bEnableClearFormatAll == bFlag) + return; + bEnableClearFormatAll = bFlag; +} + +void SvxTPView::ShowUndo() +{ + m_xUndo->show(); +} + +void SvxTPView::EnableUndo(bool bFlag) +{ + bEnableUndo = bFlag; + m_xUndo->set_sensitive(bFlag); +} + +IMPL_LINK( SvxTPView, PbClickHdl, weld::Button&, rPushB, void) +{ + if (&rPushB == m_xAccept.get()) + { + AcceptClickLk.Call(this); + } + else if (&rPushB == m_xAcceptAll.get()) + { + AcceptAllClickLk.Call(this); + } + else if (&rPushB == m_xReject.get()) + { + RejectClickLk.Call(this); + } + else if (&rPushB == m_xRejectAll.get()) + { + RejectAllClickLk.Call(this); + } + else if (&rPushB == m_xUndo.get()) + { + UndoClickLk.Call(this); + } +} + +SvxTPage::SvxTPage(weld::Container* pParent, const OUString& rUIXMLDescription, const OUString& rID) + : m_xBuilder(Application::CreateBuilder(pParent, rUIXMLDescription)) + , m_xContainer(m_xBuilder->weld_container(rID)) +{ +} + +SvxTPFilter::SvxTPFilter(weld::Container* pParent) + : SvxTPage(pParent, "svx/ui/redlinefilterpage.ui", "RedlineFilterPage") + , bModified(false) + , m_pRedlinTable(nullptr) + , m_xCbDate(m_xBuilder->weld_check_button("date")) + , m_xLbDate(m_xBuilder->weld_combo_box("datecond")) + , m_xDfDate(new SvtCalendarBox(m_xBuilder->weld_menu_button("startdate"))) + , m_xTfDate(m_xBuilder->weld_formatted_spin_button("starttime")) + , m_xTfDateFormatter(new weld::TimeFormatter(*m_xTfDate)) + , m_xIbClock(m_xBuilder->weld_button("startclock")) + , m_xFtDate2(m_xBuilder->weld_label("and")) + , m_xDfDate2(new SvtCalendarBox(m_xBuilder->weld_menu_button("enddate"))) + , m_xTfDate2(m_xBuilder->weld_formatted_spin_button("endtime")) + , m_xTfDate2Formatter(new weld::TimeFormatter(*m_xTfDate2)) + , m_xIbClock2(m_xBuilder->weld_button("endclock")) + , m_xCbAuthor(m_xBuilder->weld_check_button("author")) + , m_xLbAuthor(m_xBuilder->weld_combo_box("authorlist")) + , m_xCbRange(m_xBuilder->weld_check_button("range")) + , m_xEdRange(m_xBuilder->weld_entry("rangeedit")) + , m_xBtnRange(m_xBuilder->weld_button("dotdotdot")) + , m_xCbAction(m_xBuilder->weld_check_button("action")) + , m_xLbAction(m_xBuilder->weld_combo_box("actionlist")) + , m_xCbComment(m_xBuilder->weld_check_button("comment")) + , m_xEdComment(m_xBuilder->weld_entry("commentedit")) +{ + m_xTfDateFormatter->EnableEmptyField(false); + m_xTfDate2Formatter->EnableEmptyField(false); + + m_xLbDate->set_active(0); + m_xLbDate->connect_changed( LINK( this, SvxTPFilter, SelDateHdl ) ); + m_xIbClock->connect_clicked( LINK( this, SvxTPFilter, TimeHdl) ); + m_xIbClock2->connect_clicked( LINK( this, SvxTPFilter,TimeHdl) ); + m_xBtnRange->connect_clicked( LINK( this, SvxTPFilter, RefHandle)); + + Link<weld::Toggleable&,void> aLink=LINK( this, SvxTPFilter, RowEnableHdl) ; + m_xCbDate->connect_toggled(aLink); + m_xCbAuthor->connect_toggled(aLink); + m_xCbRange->connect_toggled(aLink); + m_xCbAction->connect_toggled(aLink); + m_xCbComment->connect_toggled(aLink); + + Link<SvtCalendarBox&,void> a2Link=LINK(this, SvxTPFilter, ModifyDate); + m_xDfDate->connect_activated(a2Link); + m_xDfDate2->connect_activated(a2Link); + + Link<weld::FormattedSpinButton&,void> a3Link=LINK(this, SvxTPFilter, ModifyTime); + m_xTfDate->connect_value_changed(a3Link); + m_xTfDate2->connect_value_changed(a3Link); + + Link<weld::Entry&,void> a4Link=LINK( this, SvxTPFilter, ModifyHdl); + m_xEdRange->connect_changed(a4Link); + m_xEdComment->connect_changed(a4Link); + m_xLbAction->connect_changed(LINK( this, SvxTPFilter, ModifyListBoxHdl)); + m_xLbAuthor->connect_changed(LINK( this, SvxTPFilter, ModifyListBoxHdl)); + + RowEnableHdl(*m_xCbDate); + RowEnableHdl(*m_xCbAuthor); + RowEnableHdl(*m_xCbRange); + RowEnableHdl(*m_xCbAction); + RowEnableHdl(*m_xCbComment); + + DateTime aDateTime(DateTime::SYSTEM); + SetFirstDate(aDateTime); + SetLastDate(aDateTime); + SetFirstTime(aDateTime); + SetLastTime(aDateTime); + HideRange(); + ShowAction(); + bModified=false; +} + +SvxTPFilter::~SvxTPFilter() +{ +} + +void SvxTPFilter::SetRedlinTable(SvxRedlinTable* pTable) +{ + m_pRedlinTable = pTable; +} + +void SvxTPFilter::EnableDateLine1(bool bFlag) +{ + if(bFlag && m_xCbDate->get_active()) + { + m_xDfDate->set_sensitive(true); + m_xTfDate->set_sensitive(true); + m_xIbClock->set_sensitive(true); + } + else + { + m_xDfDate->set_sensitive(false); + m_xTfDate->set_sensitive(false); + m_xIbClock->set_sensitive(false); + } +} +void SvxTPFilter::EnableDateLine2(bool bFlag) +{ + if(bFlag && m_xCbDate->get_active()) + { + m_xFtDate2->set_sensitive(true); + m_xDfDate2->set_sensitive(true); + m_xTfDate2->set_sensitive(true); + m_xIbClock2->set_sensitive(true); + } + else + { + m_xFtDate2->set_sensitive(false); + m_xDfDate2->set_sensitive(false); + m_xDfDate2->set_label(OUString()); + m_xTfDate2->set_sensitive(false); + m_xTfDate2->set_text(OUString()); + m_xIbClock2->set_sensitive(false); + } +} + +Date SvxTPFilter::GetFirstDate() const +{ + return m_xDfDate->get_date(); +} + +void SvxTPFilter::SetFirstDate(const Date &aDate) +{ + m_xDfDate->set_date(aDate); +} + +tools::Time SvxTPFilter::GetFirstTime() const +{ + return m_xTfDateFormatter->GetTime(); +} + +void SvxTPFilter::SetFirstTime(const tools::Time &aTime) +{ + m_xTfDateFormatter->SetTime(aTime); +} + +Date SvxTPFilter::GetLastDate() const +{ + return m_xDfDate2->get_date(); +} + +void SvxTPFilter::SetLastDate(const Date &aDate) +{ + m_xDfDate2->set_date(aDate); +} + +tools::Time SvxTPFilter::GetLastTime() const +{ + return m_xTfDate2Formatter->GetTime(); +} + +void SvxTPFilter::SetLastTime(const tools::Time &aTime) +{ + m_xTfDate2Formatter->SetTime(aTime); +} + +void SvxTPFilter::SetDateMode(sal_uInt16 nMode) +{ + m_xLbDate->set_active(nMode); + SelDateHdl(*m_xLbDate); +} + +SvxRedlinDateMode SvxTPFilter::GetDateMode() const +{ + return static_cast<SvxRedlinDateMode>(m_xLbDate->get_active()); +} +void SvxTPFilter::ClearAuthors() +{ + m_xLbAuthor->clear(); +} + +void SvxTPFilter::InsertAuthor( const OUString& rString) +{ + m_xLbAuthor->append_text(rString); +} + +OUString SvxTPFilter::GetSelectedAuthor() const +{ + return m_xLbAuthor->get_active_text(); +} + +void SvxTPFilter::SelectedAuthorPos(sal_Int32 nPos) +{ + m_xLbAuthor->set_active(nPos); +} + +sal_Int32 SvxTPFilter::SelectAuthor(const OUString& aString) +{ + m_xLbAuthor->set_active_text(aString); + return m_xLbAuthor->get_active(); +} + +void SvxTPFilter::SetRange(const OUString& rString) +{ + m_xEdRange->set_text(rString); +} + +OUString SvxTPFilter::GetRange() const +{ + return m_xEdRange->get_text(); +} + +void SvxTPFilter::SetFocusToRange() +{ + m_xEdRange->grab_focus(); +} + +void SvxTPFilter::HideRange(bool bHide) +{ + if (bHide) + { + m_xCbRange->hide(); + m_xEdRange->hide(); + m_xBtnRange->hide(); + } + else + { + ShowAction(false); + m_xCbRange->show(); + m_xEdRange->show(); + m_xBtnRange->show(); + } +} + +void SvxTPFilter::SetComment(const OUString &rComment) +{ + m_xEdComment->set_text(rComment); +} + +OUString SvxTPFilter::GetComment()const +{ + return m_xEdComment->get_text(); +} + +bool SvxTPFilter::IsDate() const +{ + return m_xCbDate->get_active(); +} + +bool SvxTPFilter::IsAuthor() const +{ + return m_xCbAuthor->get_active(); +} + +bool SvxTPFilter::IsRange() const +{ + return m_xCbRange->get_active(); +} + +bool SvxTPFilter::IsAction() const +{ + return m_xCbAction->get_active(); +} + +bool SvxTPFilter::IsComment() const +{ + return m_xCbComment->get_active(); +} + +void SvxTPFilter::CheckDate(bool bFlag) +{ + m_xCbDate->set_active(bFlag); + RowEnableHdl(*m_xCbDate); + bModified=false; +} + +void SvxTPFilter::CheckAuthor(bool bFlag) +{ + m_xCbAuthor->set_active(bFlag); + RowEnableHdl(*m_xCbAuthor); + bModified=false; +} + +void SvxTPFilter::CheckRange(bool bFlag) +{ + m_xCbRange->set_active(bFlag); + RowEnableHdl(*m_xCbRange); + bModified=false; +} + +void SvxTPFilter::CheckAction(bool bFlag) +{ + m_xCbAction->set_active(bFlag); + RowEnableHdl(*m_xCbAction); + bModified=false; +} + +void SvxTPFilter::CheckComment(bool bFlag) +{ + m_xCbComment->set_active(bFlag); + RowEnableHdl(*m_xCbComment); + bModified=false; +} + +void SvxTPFilter::ShowAction(bool bShow) +{ + if(!bShow) + { + m_xCbAction->hide(); + m_xLbAction->hide(); + } + else + { + HideRange(); + m_xCbAction->show(); + m_xLbAction->show(); + } +} + +IMPL_LINK_NOARG(SvxTPFilter, SelDateHdl, weld::ComboBox&, void) +{ + SvxRedlinDateMode nKind = static_cast<SvxRedlinDateMode>(m_xLbDate->get_active()); + switch(nKind) + { + case SvxRedlinDateMode::BEFORE: + EnableDateLine1(true); + EnableDateLine2(false); + break; + case SvxRedlinDateMode::SINCE: + EnableDateLine1(true); + EnableDateLine2(false); + break; + case SvxRedlinDateMode::EQUAL: + EnableDateLine1(true); + m_xTfDate->set_sensitive(false); + m_xTfDate->set_text(OUString()); + EnableDateLine2(false); + break; + case SvxRedlinDateMode::NOTEQUAL: + EnableDateLine1(true); + m_xTfDate->set_sensitive(false); + m_xTfDate->set_text(OUString()); + EnableDateLine2(false); + break; + case SvxRedlinDateMode::BETWEEN: + EnableDateLine1(true); + EnableDateLine2(true); + break; + case SvxRedlinDateMode::SAVE: + EnableDateLine1(false); + EnableDateLine2(false); + break; + case SvxRedlinDateMode::NONE: + break; + } + bModified = true; +} + +IMPL_LINK(SvxTPFilter, RowEnableHdl, weld::Toggleable&, rCB, void) +{ + if (&rCB == m_xCbDate.get()) + { + m_xLbDate->set_sensitive(m_xCbDate->get_active()); + EnableDateLine1(false); + EnableDateLine2(false); + if(m_xCbDate->get_active()) SelDateHdl(*m_xLbDate); + } + else if (&rCB == m_xCbAuthor.get()) + { + m_xLbAuthor->set_sensitive(m_xCbAuthor->get_active()); + } + else if (&rCB == m_xCbRange.get()) + { + m_xEdRange->set_sensitive(m_xCbRange->get_active()); + m_xBtnRange->set_sensitive(m_xCbRange->get_active()); + } + else if (&rCB == m_xCbAction.get()) + { + m_xLbAction->set_sensitive(m_xCbAction->get_active()); + } + else if (&rCB == m_xCbComment.get()) + { + m_xEdComment->set_sensitive(m_xCbComment->get_active()); + } + bModified = true; +} + +IMPL_LINK(SvxTPFilter, TimeHdl, weld::Button&, rIB, void) +{ + DateTime aDateTime( DateTime::SYSTEM ); + if (&rIB == m_xIbClock.get()) + { + SetFirstDate(aDateTime); + SetFirstTime(aDateTime); + } + else if (&rIB == m_xIbClock2.get()) + { + SetLastDate(aDateTime); + SetLastTime(aDateTime); + } + bModified=true; +} + +IMPL_LINK_NOARG(SvxTPFilter, ModifyHdl, weld::Entry&, void) +{ + bModified=true; +} + +IMPL_LINK_NOARG(SvxTPFilter, ModifyListBoxHdl, weld::ComboBox&, void) +{ + bModified=true; +} + +void SvxTPFilter::DeactivatePage() +{ + if(bModified) + { + if (m_pRedlinTable) + { + m_pRedlinTable->SetFilterDate(IsDate()); + m_pRedlinTable->SetDateTimeMode(GetDateMode()); + m_pRedlinTable->SetFirstDate(GetFirstDate()); + m_pRedlinTable->SetLastDate(GetLastDate()); + m_pRedlinTable->SetFirstTime(GetFirstTime()); + m_pRedlinTable->SetLastTime(GetLastTime()); + m_pRedlinTable->SetFilterAuthor(IsAuthor()); + m_pRedlinTable->SetAuthor(GetSelectedAuthor()); + + m_pRedlinTable->SetFilterComment(IsComment()); + + utl::SearchParam aSearchParam( m_xEdComment->get_text(), + utl::SearchParam::SearchType::Regexp,false ); + + m_pRedlinTable->SetCommentParams(&aSearchParam); + + m_pRedlinTable->UpdateFilterTest(); + } + + aReadyLink.Call(this); + } + bModified=false; +} + +void SvxTPFilter::Enable(bool bEnable) +{ + m_xContainer->set_sensitive(bEnable); + if (m_xCbDate->get_sensitive()) + { + RowEnableHdl(*m_xCbDate); + RowEnableHdl(*m_xCbAuthor); + RowEnableHdl(*m_xCbRange); + RowEnableHdl(*m_xCbComment); + } +} + +IMPL_LINK(SvxTPFilter, ModifyDate, SvtCalendarBox&, rTF, void) +{ + Date aDate( Date::SYSTEM ); + if (m_xDfDate.get() == &rTF) + { + if (m_xDfDate->get_label().isEmpty()) + m_xDfDate->set_date(aDate); + + if(m_pRedlinTable!=nullptr) + m_pRedlinTable->SetFirstDate(m_xDfDate->get_date()); + } + else if (m_xDfDate2.get() == &rTF) + { + if (m_xDfDate2->get_label().isEmpty()) + m_xDfDate2->set_date(aDate); + + if (m_pRedlinTable) + m_pRedlinTable->SetLastDate(m_xDfDate2->get_date()); + } + bModified=true; +} + +IMPL_LINK(SvxTPFilter, ModifyTime, weld::FormattedSpinButton&, rTF, void) +{ + tools::Time aTime(0); + if (m_xTfDate.get() == &rTF) + { + if (m_xTfDate->get_text().isEmpty()) + SetFirstTime(aTime); + + if (m_pRedlinTable!=nullptr) + m_pRedlinTable->SetFirstTime(GetFirstTime()); + } + else if (m_xTfDate2.get() == &rTF) + { + if (m_xTfDate2->get_text().isEmpty()) + SetLastTime(aTime); + + if (m_pRedlinTable!=nullptr) + m_pRedlinTable->SetLastTime(GetLastTime()); + + } + bModified=true; +} + +IMPL_LINK_NOARG(SvxTPFilter, RefHandle, weld::Button&, void) +{ + aRefLink.Call(this); +} + +SvxAcceptChgCtr::SvxAcceptChgCtr(weld::Container* pParent) + : m_xBuilder(Application::CreateBuilder(pParent, "svx/ui/redlinecontrol.ui")) + , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) +{ + m_xTabCtrl->connect_enter_page(LINK(this, SvxAcceptChgCtr, ActivatePageHdl)); + m_xTabCtrl->connect_leave_page(LINK(this, SvxAcceptChgCtr, DeactivatePageHdl)); + + m_xTPFilter.reset(new SvxTPFilter(m_xTabCtrl->get_page("filter"))); + m_xTPView.reset(new SvxTPView(m_xTabCtrl->get_page("view"))); + m_xTPFilter->SetRedlinTable(m_xTPView->GetTableControl()); + m_xTabCtrl->set_current_page("view"); + m_xTabCtrl->set_help_id(HID_REDLINE_CTRL_VIEW); + m_xTabCtrl->show(); +} + +SvxAcceptChgCtr::~SvxAcceptChgCtr() +{ + m_xTPFilter.reset(); + m_xTPView.reset(); +} + +void SvxAcceptChgCtr::ShowFilterPage() +{ + m_xTabCtrl->set_current_page("filter"); +} + +IMPL_LINK(SvxAcceptChgCtr, ActivatePageHdl, const OUString&, rPage, void) +{ + if (rPage == "filter") + { + m_xTPFilter->ActivatePage(); + m_xTabCtrl->set_help_id(HID_REDLINE_CTRL_FILTER); + } + else if (rPage == "view") + { + m_xTPView->ActivatePage(); + m_xTabCtrl->set_help_id(HID_REDLINE_CTRL_VIEW); + } +} + +IMPL_LINK(SvxAcceptChgCtr, DeactivatePageHdl, const OUString&, rPage, bool) +{ + if (rPage == "filter") + m_xTPFilter->DeactivatePage(); + else if (rPage == "view") + m_xTPView->DeactivatePage(); + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/databaseregistrationui.cxx b/svx/source/dialog/databaseregistrationui.cxx new file mode 100644 index 0000000000..aa62b5b388 --- /dev/null +++ b/svx/source/dialog/databaseregistrationui.cxx @@ -0,0 +1,45 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/databaseregistrationui.hxx> + +#include <svx/svxdlg.hxx> +#include <svx/dialogs.hrc> + +#include <sfx2/app.hxx> +#include <svl/itemset.hxx> + +namespace svx +{ + sal_uInt16 administrateDatabaseRegistration(weld::Window* parentWindow) + { + sal_uInt16 nResult = RET_CANCEL; + + SfxItemSetFixed<SID_SB_DB_REGISTER, SID_SB_DB_REGISTER> aRegistrationItems( SfxGetpApp()->GetPool() ); + + SvxAbstractDialogFactory* pDialogFactory = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pDialog(pDialogFactory->CreateSfxDialog(parentWindow, aRegistrationItems, nullptr, RID_SFXPAGE_DBREGISTER)); + nResult = pDialog->Execute(); + + return nResult; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/dialcontrol.cxx b/svx/source/dialog/dialcontrol.cxx new file mode 100644 index 0000000000..adda37b22e --- /dev/null +++ b/svx/source/dialog/dialcontrol.cxx @@ -0,0 +1,479 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dialcontrol.hxx> +#include <svx/svdtrans.hxx> +#include <cmath> +#include <vcl/virdev.hxx> +#include <vcl/svapp.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <i18nlangtag/languagetag.hxx> + +namespace svx { + +const tools::Long DIAL_OUTER_WIDTH = 8; + +DialControlBmp::DialControlBmp(OutputDevice& rReference) + : VirtualDevice(rReference, DeviceFormat::WITH_ALPHA) + , mbEnabled(true) + , mrParent(rReference) + , mnCenterX(0) + , mnCenterY(0) +{ + EnableRTL(false); +} + +void DialControlBmp::InitBitmap(const vcl::Font& rFont) +{ + Init(); + SetFont(rFont); +} + +void DialControlBmp::CopyBackground( const DialControlBmp& rSrc ) +{ + Init(); + SetSize(rSrc.maRect.GetSize()); + mbEnabled = rSrc.mbEnabled; + Point aPos; + DrawBitmapEx( aPos, rSrc.GetBitmapEx( aPos, maRect.GetSize() ) ); +} + +void DialControlBmp::DrawBackground( const Size& rSize, bool bEnabled ) +{ + Init(); + SetSize(rSize); + mbEnabled = bEnabled; + DrawBackground(); +} + +void DialControlBmp::DrawElements( const OUString& rText, Degree100 nAngle ) +{ + double fAngle = toRadians(nAngle); + double fSin = sin( fAngle ); + double fCos = cos( fAngle ); + double fWidth = GetTextWidth( rText ) / 2.0; + double fHeight = GetTextHeight() / 2.0; + + if ( !rText.isEmpty() ) + { + // rotated text + vcl::Font aFont( GetFont() ); + aFont.SetColor( GetTextColor() ); + aFont.SetOrientation( to<Degree10>(nAngle) ); // Font uses 1/10 degrees + aFont.SetWeight( WEIGHT_BOLD ); + SetFont( aFont ); + + tools::Long nX = static_cast< tools::Long >( mnCenterX - fWidth * fCos - fHeight * fSin ); + tools::Long nY = static_cast< tools::Long >( mnCenterY + fWidth * fSin - fHeight * fCos ); + tools::Rectangle aRect( nX, nY, 2 * mnCenterX - nX, 2 * mnCenterY - nY ); + DrawText( aRect, rText, mbEnabled ? DrawTextFlags::NONE : DrawTextFlags::Disable ); + } + else + { + // only a line + const sal_Int32 nDx (fCos * (maRect.GetWidth()-4) / 2); + const sal_Int32 nDy (-fSin * (maRect.GetHeight()-4) / 2); + Point pt1( maRect.Center() ); + Point pt2( pt1.X() + nDx, pt1.Y() + nDy); + + SetLineColor( GetTextColor() ); + DrawLine( pt1, pt2 ); + } + + // *** drag button *** + + bool bMain = (nAngle % 4500_deg100) != 0_deg100; + SetLineColor( GetButtonLineColor() ); + SetFillColor( GetButtonFillColor( bMain ) ); + + tools::Long nX = mnCenterX - static_cast< tools::Long >( (DIAL_OUTER_WIDTH / 2 - mnCenterX) * fCos ); + tools::Long nY = mnCenterY - static_cast< tools::Long >( (mnCenterY - DIAL_OUTER_WIDTH / 2) * fSin ); + tools::Long nSize = bMain ? (DIAL_OUTER_WIDTH / 4) : (DIAL_OUTER_WIDTH / 2 - 1); + DrawEllipse( tools::Rectangle( nX - nSize, nY - nSize, nX + nSize, nY + nSize ) ); +} + +Color DialControlBmp::GetBackgroundColor() const +{ + return GetSettings().GetStyleSettings().GetDialogColor(); +} + +const Color& DialControlBmp::GetTextColor() const +{ + return GetSettings().GetStyleSettings().GetLabelTextColor(); +} + +const Color& DialControlBmp::GetScaleLineColor() const +{ + const StyleSettings& rSett = GetSettings().GetStyleSettings(); + return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor(); +} + +const Color& DialControlBmp::GetButtonLineColor() const +{ + const StyleSettings& rSett = GetSettings().GetStyleSettings(); + return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor(); +} + +const Color& DialControlBmp::GetButtonFillColor( bool bMain ) const +{ + const StyleSettings& rSett = GetSettings().GetStyleSettings(); + return mbEnabled ? (bMain ? rSett.GetMenuColor() : rSett.GetHighlightColor()) : rSett.GetDisableColor(); +} + +void DialControlBmp::Init() +{ + SetSettings(mrParent.GetSettings()); + SetBackground(GetBackgroundColor()); +} + +void DialControlBmp::SetSize( const Size& rSize ) +{ + maRect.SetPos( Point( 0, 0 ) ); + maRect.SetSize( rSize ); + mnCenterX = rSize.Width() / 2; + mnCenterY = rSize.Height() / 2; + SetOutputSize( rSize ); +} + +void DialControlBmp::DrawBackground() +{ + // *** background with 3D effect *** + + SetLineColor(); + SetFillColor(); + Erase(); + + EnableRTL(); // draw 3D effect in correct direction + + sal_uInt8 nDiff = mbEnabled ? 0x18 : 0x10; + Color aColor; + + aColor = GetBackgroundColor(); + SetFillColor( aColor ); + DrawPie( maRect, maRect.TopRight(), maRect.TopCenter() ); + DrawPie( maRect, maRect.BottomLeft(), maRect.BottomCenter() ); + + aColor.DecreaseLuminance( nDiff ); + SetFillColor( aColor ); + DrawPie( maRect, maRect.BottomCenter(), maRect.TopRight() ); + + aColor.DecreaseLuminance( nDiff ); + SetFillColor( aColor ); + DrawPie( maRect, maRect.BottomRight(), maRect.RightCenter() ); + + aColor = GetBackgroundColor(); + aColor.IncreaseLuminance( nDiff ); + SetFillColor( aColor ); + DrawPie( maRect, maRect.TopCenter(), maRect.BottomLeft() ); + + aColor.IncreaseLuminance( nDiff ); + SetFillColor( aColor ); + DrawPie( maRect, maRect.TopLeft(), maRect.LeftCenter() ); + + EnableRTL( false ); + + // *** calibration *** + + Point aStartPos( mnCenterX, mnCenterY ); + Color aFullColor( GetScaleLineColor() ); + Color aLightColor( GetBackgroundColor() ); + aLightColor.Merge( aFullColor, 128 ); + + for( int nAngle = 0; nAngle < 360; nAngle += 15 ) + { + SetLineColor( (nAngle % 45) ? aLightColor : aFullColor ); + double fAngle = basegfx::deg2rad(nAngle); + tools::Long nX = static_cast< tools::Long >( -mnCenterX * cos( fAngle ) ); + tools::Long nY = static_cast< tools::Long >( mnCenterY * sin( fAngle ) ); + DrawLine( aStartPos, Point( mnCenterX - nX, mnCenterY - nY ) ); + } + + // *** clear inner area *** + + SetLineColor(); + SetFillColor( GetBackgroundColor() ); + tools::Rectangle aEllipseRect = maRect; + aEllipseRect.shrink(DIAL_OUTER_WIDTH); + DrawEllipse( aEllipseRect ); +} + +DialControl::DialControl_Impl::DialControl_Impl(OutputDevice& rReference) : + mxBmpEnabled(VclPtr<DialControlBmp>::Create(rReference)), + mxBmpDisabled(VclPtr<DialControlBmp>::Create(rReference)), + mxBmpBuffered(VclPtr<DialControlBmp>::Create(rReference)), + mpLinkField( nullptr ), + mnLinkedFieldValueMultiplyer( 0 ), + mnAngle( 0 ), + mnInitialAngle( 0 ), + mnOldAngle( 0 ), + mnCenterX( 0 ), + mnCenterY( 0 ), + mbNoRot( false ) +{ +} + +void DialControl::DialControl_Impl::Init( const Size& rWinSize, const vcl::Font& rWinFont ) +{ + maWinFont = rWinFont; + maWinFont.SetTransparent(true); + mxBmpBuffered->InitBitmap(maWinFont); + SetSize(rWinSize); +} + +void DialControl::DialControl_Impl::SetSize( const Size& rWinSize ) +{ + // make the control squared, and adjusted so that we have a well-defined + // center ["(x - 1) | 1" creates odd value <= x] + tools::Long nMin = (std::min(rWinSize.Width(), rWinSize.Height()) - 1) | 1; + + maWinSize = Size( nMin, nMin ); + + mnCenterX = maWinSize.Width() / 2; + mnCenterY = maWinSize.Height() / 2; + + mxBmpEnabled->DrawBackground( maWinSize, true ); + mxBmpDisabled->DrawBackground( maWinSize, false ); + mxBmpBuffered->SetSize( maWinSize ); +} + +void DialControl::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + //use same logic as DialControl_Impl::SetSize + int nDim = (std::min<int>(pDrawingArea->get_approximate_digit_width() * 12, + pDrawingArea->get_text_height() * 6) - 1) | 1; + Size aSize(nDim, nDim); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + mpImpl.reset(new DialControl_Impl(pDrawingArea->get_ref_device())); + //set size and use that + Init(aSize); +} + +void DialControl::Resize() +{ + mpImpl->SetSize(GetOutputSizePixel()); + InvalidateControl(); +} + +void DialControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + Point aPos; + rRenderContext.DrawBitmapEx(aPos, mpImpl->mxBmpBuffered->GetBitmapEx(aPos, mpImpl->maWinSize)); +} + +void DialControl::StyleUpdated() +{ + CustomWidgetController::StyleUpdated(); + Init( mpImpl->maWinSize, mpImpl->maWinFont ); + InvalidateControl(); +} + +bool DialControl::MouseButtonDown(const MouseEvent& rMEvt) +{ + if( rMEvt.IsLeft() ) + { + GrabFocus(); + CaptureMouse(); + mpImpl->mnOldAngle = mpImpl->mnAngle; + HandleMouseEvent( rMEvt.GetPosPixel(), true ); + } + return true; +} + +bool DialControl::MouseMove( const MouseEvent& rMEvt ) +{ + if( IsMouseCaptured() && rMEvt.IsLeft() ) + HandleMouseEvent( rMEvt.GetPosPixel(), false ); + return true; +} + +bool DialControl::MouseButtonUp(const MouseEvent&) +{ + if( IsMouseCaptured() ) + { + ReleaseMouse(); + if( mpImpl->mpLinkField ) + mpImpl->mpLinkField->grab_focus(); + } + return true; +} + +bool DialControl::KeyInput( const KeyEvent& rKEvt ) +{ + const vcl::KeyCode& rKCode = rKEvt.GetKeyCode(); + if( !rKCode.GetModifier() && (rKCode.GetCode() == KEY_ESCAPE) ) + { + HandleEscapeEvent(); + return true; + } + return CustomWidgetController::KeyInput(rKEvt); +} + +void DialControl::LoseFocus() +{ + // release captured mouse + HandleEscapeEvent(); +} + +bool DialControl::HasRotation() const +{ + return !mpImpl->mbNoRot; +} + +void DialControl::SetNoRotation() +{ + if( !mpImpl->mbNoRot ) + { + mpImpl->mbNoRot = true; + InvalidateControl(); + if (mpImpl->mpLinkField) + mpImpl->mpLinkField->set_text(""); + } +} + +Degree100 DialControl::GetRotation() const +{ + return mpImpl->mnAngle; +} + +void DialControl::SetRotation(Degree100 nAngle) +{ + SetRotation(nAngle, false); +} + +void DialControl::SetLinkedField(weld::MetricSpinButton* pField, sal_Int32 nDecimalPlaces) +{ + mpImpl->mnLinkedFieldValueMultiplyer = 100 / std::pow(10.0, double(nDecimalPlaces)); + + // remove modify handler from old linked field + if( mpImpl->mpLinkField ) + { + weld::MetricSpinButton& rField = *mpImpl->mpLinkField; + rField.connect_value_changed(Link<weld::MetricSpinButton&,void>()); + } + // remember the new linked field + mpImpl->mpLinkField = pField; + // set modify handler at new linked field + if( mpImpl->mpLinkField ) + { + weld::MetricSpinButton& rField = *mpImpl->mpLinkField; + rField.connect_value_changed(LINK(this, DialControl, LinkedFieldModifyHdl)); + } +} + +IMPL_LINK_NOARG(DialControl, LinkedFieldModifyHdl, weld::MetricSpinButton&, void) +{ + SetRotation(Degree100(mpImpl->mpLinkField->get_value(FieldUnit::DEGREE) * mpImpl->mnLinkedFieldValueMultiplyer), true); +} + +void DialControl::SaveValue() +{ + mpImpl->mnInitialAngle = mpImpl->mnAngle; +} + +bool DialControl::IsValueModified() const +{ + return mpImpl->mnInitialAngle != mpImpl->mnAngle; +} + +void DialControl::Init( const Size& rWinSize, const vcl::Font& rWinFont ) +{ + mpImpl->Init( rWinSize, rWinFont ); + EnableRTL( false ); // don't mirror mouse handling + SetOutputSizePixel( mpImpl->maWinSize ); +} + +void DialControl::Init( const Size& rWinSize ) +{ + //hidpi TODO: GetDefaultFont() picks a font size too small, so fix it here. + vcl::Font aDefaultSize = Application::GetSettings().GetStyleSettings().GetLabelFont(); + + vcl::Font aFont( OutputDevice::GetDefaultFont( + DefaultFontType::UI_SANS, Application::GetSettings().GetUILanguageTag().getLanguageType(), GetDefaultFontFlags::OnlyOne ) ); + + aFont.SetFontHeight(aDefaultSize.GetFontHeight()); + Init( rWinSize, aFont ); +} + +void DialControl::InvalidateControl() +{ + mpImpl->mxBmpBuffered->CopyBackground( IsEnabled() ? *mpImpl->mxBmpEnabled : *mpImpl->mxBmpDisabled ); + if( !mpImpl->mbNoRot ) + mpImpl->mxBmpBuffered->DrawElements(GetText(), mpImpl->mnAngle); + Invalidate(); +} + +void DialControl::SetRotation(Degree100 nAngle, bool bBroadcast) +{ + bool bOldSel = mpImpl->mbNoRot; + mpImpl->mbNoRot = false; + + nAngle = NormAngle36000(nAngle); + + if (!bOldSel || (mpImpl->mnAngle != nAngle)) + { + mpImpl->mnAngle = nAngle; + InvalidateControl(); + if( mpImpl->mpLinkField ) + mpImpl->mpLinkField->set_value(GetRotation().get() / mpImpl->mnLinkedFieldValueMultiplyer, FieldUnit::DEGREE); + if( bBroadcast ) + mpImpl->maModifyHdl.Call(*this); + } +} + +void DialControl::SetModifyHdl( const Link<DialControl&,void>& rLink ) +{ + mpImpl->maModifyHdl = rLink; +} + +void DialControl::HandleMouseEvent( const Point& rPos, bool bInitial ) +{ + tools::Long nX = rPos.X() - mpImpl->mnCenterX; + tools::Long nY = mpImpl->mnCenterY - rPos.Y(); + double fH = std::hypot( nX, nY ); + if( fH != 0.0 ) + { + double fAngle = acos( nX / fH ); + sal_Int32 nAngle = basegfx::rad2deg<100>(fAngle); + if( nY < 0 ) + nAngle = 36000 - nAngle; + if( bInitial ) // round to entire 15 degrees + nAngle = ((nAngle + 750) / 1500) * 1500; + // Round up to 1 degree + nAngle = (((nAngle + 50) / 100) * 100) % 36000; + SetRotation(Degree100(nAngle), true); + } +} + +void DialControl::HandleEscapeEvent() +{ + if( IsMouseCaptured() ) + { + ReleaseMouse(); + SetRotation(mpImpl->mnOldAngle, true); + if( mpImpl->mpLinkField ) + mpImpl->mpLinkField->grab_focus(); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/dialmgr.cxx b/svx/source/dialog/dialmgr.cxx new file mode 100644 index 0000000000..9fdd50ddab --- /dev/null +++ b/svx/source/dialog/dialmgr.cxx @@ -0,0 +1,26 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dialmgr.hxx> + +std::locale SvxResLocale() { return Translate::Create("svx"); } + +OUString SvxResId(TranslateId aId) { return Translate::get(aId, SvxResLocale()); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/dlgctl3d.cxx b/svx/source/dialog/dlgctl3d.cxx new file mode 100644 index 0000000000..e315819b06 --- /dev/null +++ b/svx/source/dialog/dlgctl3d.cxx @@ -0,0 +1,1165 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <svx/dlgctl3d.hxx> +#include <svx/strings.hrc> +#include <svx/view3d.hxx> +#include <svx/fmmodel.hxx> +#include <svl/itempool.hxx> +#include <svx/fmpage.hxx> +#include <svx/sphere3d.hxx> +#include <svx/cube3d.hxx> +#include <svx/scene3d.hxx> +#include <vcl/svapp.hxx> +#include <svx/helperhittest3d.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <polygn3d.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xlnclit.hxx> +#include <svx/xlnwtit.hxx> +#include <helpids.h> +#include <svx/dialmgr.hxx> +#include <tools/helpers.hxx> +#include <vcl/settings.hxx> + +using namespace com::sun::star; + +Svx3DPreviewControl::Svx3DPreviewControl() + : mnObjectType(SvxPreviewObjectType::SPHERE) +{ +} + +void Svx3DPreviewControl::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(80, 100), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + CustomWidgetController::SetDrawingArea(pDrawingArea); + SetOutputSizePixel(aSize); + + Construct(); +} + +Svx3DPreviewControl::~Svx3DPreviewControl() +{ + mp3DObj.clear(); + mxFmPage.clear(); + mpScene.clear(); + mp3DView.reset(); + mpModel.reset(); +} + +void Svx3DPreviewControl::Construct() +{ + // Do never mirror the preview window. This explicitly includes right + // to left writing environments. + EnableRTL (false); + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + rDevice.SetMapMode(MapMode(MapUnit::Map100thMM)); + + // Model + mpModel.reset(new FmFormModel()); + mpModel->GetItemPool().FreezeIdRanges(); + + // Page + mxFmPage = new FmFormPage( *mpModel ); + mpModel->InsertPage( mxFmPage.get(), 0 ); + + // 3D View + mp3DView.reset(new E3dView(*mpModel, &rDevice)); + mp3DView->SetBufferedOutputAllowed(true); + mp3DView->SetBufferedOverlayAllowed(true); + + // 3D Scene + mpScene = new E3dScene(*mpModel); + + // initially create object + SetObjectType(SvxPreviewObjectType::SPHERE); + + // camera and perspective + Camera3D rCamera = mpScene->GetCamera(); + const basegfx::B3DRange& rVolume = mpScene->GetBoundVolume(); + double fW = rVolume.getWidth(); + double fH = rVolume.getHeight(); + double fCamZ = rVolume.getMaxZ() + ((fW + fH) / 2.0); + + rCamera.SetAutoAdjustProjection(false); + rCamera.SetViewWindow(- fW / 2, - fH / 2, fW, fH); + basegfx::B3DPoint aLookAt; + double fDefaultCamPosZ = mp3DView->GetDefaultCamPosZ(); + basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ); + rCamera.SetPosAndLookAt(aCamPos, aLookAt); + double fDefaultCamFocal = mp3DView->GetDefaultCamFocal(); + rCamera.SetFocalLength(fDefaultCamFocal); + + mpScene->SetCamera( rCamera ); + mxFmPage->InsertObject( mpScene.get() ); + + basegfx::B3DHomMatrix aRotation; + aRotation.rotate(basegfx::deg2rad( 25 ), 0.0, 0.0); + aRotation.rotate(0.0, basegfx::deg2rad( 40 ), 0.0); + mpScene->SetTransform(aRotation * mpScene->GetTransform()); + + // invalidate SnapRects of objects + mpScene->SetBoundAndSnapRectsDirty(); + + SfxItemSetFixed<XATTR_LINESTYLE, XATTR_LINESTYLE, + XATTR_FILL_FIRST, XATTR_FILLBITMAP> aSet( mpModel->GetItemPool() ); + aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); + aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); + aSet.Put( XFillColorItem( "", COL_WHITE ) ); + + mpScene->SetMergedItemSet(aSet); + + // PageView + SdrPageView* pPageView = mp3DView->ShowSdrPage( mxFmPage.get() ); + mp3DView->hideMarkHandles(); + + // mark scene + mp3DView->MarkObj( mpScene.get(), pPageView ); +} + +void Svx3DPreviewControl::Resize() +{ + // size of page + Size aSize(GetOutputSizePixel()); + aSize = GetDrawingArea()->get_ref_device().PixelToLogic(aSize); + mxFmPage->SetSize(aSize); + + // set size + Size aObjSize( aSize.Width()*5/6, aSize.Height()*5/6 ); + Point aObjPoint( (aSize.Width() - aObjSize.Width()) / 2, + (aSize.Height() - aObjSize.Height()) / 2); + tools::Rectangle aRect( aObjPoint, aObjSize); + mpScene->SetSnapRect( aRect ); +} + +void Svx3DPreviewControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + mp3DView->CompleteRedraw(&rRenderContext, vcl::Region(rRect)); +} + +bool Svx3DPreviewControl::MouseButtonDown(const MouseEvent& rMEvt) +{ + if (rMEvt.IsShift() && rMEvt.IsMod1()) + { + if(SvxPreviewObjectType::SPHERE == GetObjectType()) + { + SetObjectType(SvxPreviewObjectType::CUBE); + } + else + { + SetObjectType(SvxPreviewObjectType::SPHERE); + } + } + return false; +} + +void Svx3DPreviewControl::SetObjectType(SvxPreviewObjectType nType) +{ + if(mnObjectType == nType && mp3DObj) + return; + + SfxItemSetFixed<SDRATTR_START, SDRATTR_END> aSet(mpModel->GetItemPool()); + mnObjectType = nType; + + if( mp3DObj ) + { + aSet.Put(mp3DObj->GetMergedItemSet()); + mpScene->RemoveObject( mp3DObj->GetOrdNum() ); + mp3DObj.clear(); + } + + switch( nType ) + { + case SvxPreviewObjectType::SPHERE: + { + mp3DObj = new E3dSphereObj( + *mpModel, + mp3DView->Get3DDefaultAttributes(), + basegfx::B3DPoint( 0, 0, 0 ), + basegfx::B3DVector( 5000, 5000, 5000 )); + } + break; + + case SvxPreviewObjectType::CUBE: + { + mp3DObj = new E3dCubeObj( + *mpModel, + mp3DView->Get3DDefaultAttributes(), + basegfx::B3DPoint( -2500, -2500, -2500 ), + basegfx::B3DVector( 5000, 5000, 5000 )); + } + break; + } + + if (mp3DObj) + { + mpScene->InsertObject( mp3DObj.get() ); + mp3DObj->SetMergedItemSet(aSet); + } + + Invalidate(); +} + +SfxItemSet const & Svx3DPreviewControl::Get3DAttributes() const +{ + return mp3DObj->GetMergedItemSet(); +} + +void Svx3DPreviewControl::Set3DAttributes( const SfxItemSet& rAttr ) +{ + mp3DObj->SetMergedItemSet(rAttr, true); + Resize(); + Invalidate(); +} + +#define RADIUS_LAMP_PREVIEW_SIZE (4500.0) +#define RADIUS_LAMP_SMALL (600.0) +#define RADIUS_LAMP_BIG (1000.0) +#define NO_LIGHT_SELECTED (0xffffffff) +#define MAX_NUMBER_LIGHTS (8) + +const sal_Int32 g_nInteractionStartDistance = 5 * 5 * 2; + +Svx3DLightControl::Svx3DLightControl() +: maSelectedLight(NO_LIGHT_SELECTED), + maLightObjects(MAX_NUMBER_LIGHTS, nullptr), + mfRotateX(-20.0), + mfRotateY(45.0), + mfRotateZ(0.0), + mfSaveActionStartHor(0.0), + mfSaveActionStartVer(0.0), + mfSaveActionStartRotZ(0.0), + mbMouseMoved(false), + mbMouseCaptured(false), + mbGeometrySelected(false) +{ +} + +void Svx3DLightControl::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Svx3DPreviewControl::SetDrawingArea(pDrawingArea); + Construct2(); +} + +void Svx3DLightControl::Construct2() +{ + { + // hide all page stuff, use control background (normally gray) + const Color aDialogColor(Application::GetSettings().GetStyleSettings().GetDialogColor()); + mp3DView->SetPageVisible(false); + mp3DView->SetApplicationBackgroundColor(aDialogColor); + mp3DView->SetApplicationDocumentColor(aDialogColor); + } + + { + // create invisible expansion object + const double fMaxExpansion(RADIUS_LAMP_BIG + RADIUS_LAMP_PREVIEW_SIZE); + mpExpansionObject = new E3dCubeObj( + *mpModel, + mp3DView->Get3DDefaultAttributes(), + basegfx::B3DPoint(-fMaxExpansion, -fMaxExpansion, -fMaxExpansion), + basegfx::B3DVector(2.0 * fMaxExpansion, 2.0 * fMaxExpansion, 2.0 * fMaxExpansion)); + mpScene->InsertObject( mpExpansionObject.get() ); + SfxItemSet aSet(mpModel->GetItemPool()); + aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); + aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) ); + mpExpansionObject->SetMergedItemSet(aSet); + } + + { + // create lamp control object (Yellow lined object) + // base circle + const basegfx::B2DPolygon a2DCircle(basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), RADIUS_LAMP_PREVIEW_SIZE)); + basegfx::B3DPolygon a3DCircle(basegfx::utils::createB3DPolygonFromB2DPolygon(a2DCircle)); + basegfx::B3DHomMatrix aTransform; + + aTransform.rotate(M_PI_2, 0.0, 0.0); + aTransform.translate(0.0, -RADIUS_LAMP_PREVIEW_SIZE, 0.0); + a3DCircle.transform(aTransform); + + // create object for it + mpLampBottomObject = new E3dPolygonObj( + *mpModel, + basegfx::B3DPolyPolygon(a3DCircle)); + mpScene->InsertObject( mpLampBottomObject.get() ); + + // half circle with stand + basegfx::B2DPolygon a2DHalfCircle; + a2DHalfCircle.append(basegfx::B2DPoint(RADIUS_LAMP_PREVIEW_SIZE, 0.0)); + a2DHalfCircle.append(basegfx::B2DPoint(RADIUS_LAMP_PREVIEW_SIZE, -RADIUS_LAMP_PREVIEW_SIZE)); + a2DHalfCircle.append(basegfx::utils::createPolygonFromEllipseSegment( + basegfx::B2DPoint(0.0, 0.0), RADIUS_LAMP_PREVIEW_SIZE, RADIUS_LAMP_PREVIEW_SIZE, 2 * M_PI - M_PI_2, M_PI_2)); + basegfx::B3DPolygon a3DHalfCircle(basegfx::utils::createB3DPolygonFromB2DPolygon(a2DHalfCircle)); + + // create object for it + mpLampShaftObject = new E3dPolygonObj( + *mpModel, + basegfx::B3DPolyPolygon(a3DHalfCircle)); + mpScene->InsertObject( mpLampShaftObject.get() ); + + // initially invisible + SfxItemSet aSet(mpModel->GetItemPool()); + aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); + aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) ); + + mpLampBottomObject->SetMergedItemSet(aSet); + mpLampShaftObject->SetMergedItemSet(aSet); + } + + { + // change camera settings + Camera3D rCamera = mpScene->GetCamera(); + const basegfx::B3DRange& rVolume = mpScene->GetBoundVolume(); + double fW = rVolume.getWidth(); + double fH = rVolume.getHeight(); + double fCamZ = rVolume.getMaxZ() + ((fW + fH) / 2.0); + + rCamera.SetAutoAdjustProjection(false); + rCamera.SetViewWindow(- fW / 2, - fH / 2, fW, fH); + basegfx::B3DPoint aLookAt; + double fDefaultCamPosZ = mp3DView->GetDefaultCamPosZ(); + basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ); + rCamera.SetPosAndLookAt(aCamPos, aLookAt); + double fDefaultCamFocal = mp3DView->GetDefaultCamFocal(); + rCamera.SetFocalLength(fDefaultCamFocal); + + mpScene->SetCamera( rCamera ); + + basegfx::B3DHomMatrix aNeutral; + mpScene->SetTransform(aNeutral); + } + + // invalidate SnapRects of objects + mpScene->SetBoundAndSnapRectsDirty(); +} + +void Svx3DLightControl::ConstructLightObjects() +{ + for(sal_uInt32 a(0); a < MAX_NUMBER_LIGHTS; a++) + { + // get rid of possible existing light object + if(maLightObjects[a]) + { + mpScene->RemoveObject(maLightObjects[a]->GetOrdNum()); + maLightObjects[a] = nullptr; + } + + if(GetLightOnOff(a)) + { + const bool bIsSelectedLight(a == maSelectedLight); + basegfx::B3DVector aDirection(GetLightDirection(a)); + aDirection.normalize(); + aDirection *= RADIUS_LAMP_PREVIEW_SIZE; + + const double fLampSize(bIsSelectedLight ? RADIUS_LAMP_BIG : RADIUS_LAMP_SMALL); + rtl::Reference<E3dObject> pNewLight = new E3dSphereObj( + *mpModel, + mp3DView->Get3DDefaultAttributes(), + basegfx::B3DPoint( 0, 0, 0 ), + basegfx::B3DVector( fLampSize, fLampSize, fLampSize)); + mpScene->InsertObject(pNewLight.get()); + + basegfx::B3DHomMatrix aTransform; + aTransform.translate(aDirection.getX(), aDirection.getY(), aDirection.getZ()); + pNewLight->SetTransform(aTransform); + + SfxItemSet aSet(mpModel->GetItemPool()); + aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); + aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); + aSet.Put( XFillColorItem(OUString(), GetLightColor(a))); + pNewLight->SetMergedItemSet(aSet); + + maLightObjects[a] = pNewLight.get(); + } + } +} + +void Svx3DLightControl::AdaptToSelectedLight() +{ + if(NO_LIGHT_SELECTED == maSelectedLight) + { + // make mpLampBottomObject/mpLampShaftObject invisible + SfxItemSet aSet(mpModel->GetItemPool()); + aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); + aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) ); + mpLampBottomObject->SetMergedItemSet(aSet); + mpLampShaftObject->SetMergedItemSet(aSet); + } + else + { + basegfx::B3DVector aDirection(GetLightDirection(maSelectedLight)); + aDirection.normalize(); + + // make mpLampBottomObject/mpLampShaftObject visible (yellow hairline) + SfxItemSet aSet(mpModel->GetItemPool()); + aSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) ); + aSet.Put( XLineColorItem(OUString(), COL_YELLOW)); + aSet.Put( XLineWidthItem(0)); + aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) ); + mpLampBottomObject->SetMergedItemSet(aSet); + mpLampShaftObject->SetMergedItemSet(aSet); + + // adapt transformation of mpLampShaftObject + basegfx::B3DHomMatrix aTransform; + double fRotateY(0.0); + + if(!basegfx::fTools::equalZero(aDirection.getZ()) || !basegfx::fTools::equalZero(aDirection.getX())) + { + fRotateY = atan2(-aDirection.getZ(), aDirection.getX()); + } + + aTransform.rotate(0.0, fRotateY, 0.0); + mpLampShaftObject->SetTransform(aTransform); + + // adapt transformation of selected light + E3dObject* pSelectedLight = maLightObjects[sal_Int32(maSelectedLight)]; + + if(pSelectedLight) + { + aTransform.identity(); + aTransform.translate( + aDirection.getX() * RADIUS_LAMP_PREVIEW_SIZE, + aDirection.getY() * RADIUS_LAMP_PREVIEW_SIZE, + aDirection.getZ() * RADIUS_LAMP_PREVIEW_SIZE); + pSelectedLight->SetTransform(aTransform); + } + } +} + +void Svx3DLightControl::TrySelection(Point aPosPixel) +{ + if(!mpScene) + return; + + const Point aPosLogic(GetDrawingArea()->get_ref_device().PixelToLogic(aPosPixel)); + const basegfx::B2DPoint aPoint(aPosLogic.X(), aPosLogic.Y()); + std::vector< const E3dCompoundObject* > aResult; + getAllHit3DObjectsSortedFrontToBack(aPoint, *mpScene, aResult); + + if(aResult.empty()) + return; + + // exclude expansion object which will be part of + // the hits. It's invisible, but for HitTest, it's included + const E3dCompoundObject* pResult = nullptr; + + for(auto const & b: aResult) + { + if(b && b != mpExpansionObject.get()) + { + pResult = b; + break; + } + } + + if(pResult == mp3DObj.get()) + { + if(!mbGeometrySelected) + { + mbGeometrySelected = true; + maSelectedLight = NO_LIGHT_SELECTED; + ConstructLightObjects(); + AdaptToSelectedLight(); + Invalidate(); + + if(maSelectionChangeCallback.IsSet()) + { + maSelectionChangeCallback.Call(this); + } + } + } + else + { + sal_uInt32 aNewSelectedLight(NO_LIGHT_SELECTED); + + for(sal_uInt32 a(0); a < MAX_NUMBER_LIGHTS; a++) + { + if(maLightObjects[a] && maLightObjects[a] == pResult) + { + aNewSelectedLight = a; + } + } + + if(aNewSelectedLight != maSelectedLight) + { + SelectLight(aNewSelectedLight); + + if(maSelectionChangeCallback.IsSet()) + { + maSelectionChangeCallback.Call(this); + } + } + } +} + +void Svx3DLightControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + Svx3DPreviewControl::Paint(rRenderContext, rRect); +} + +tools::Rectangle Svx3DLightControl::GetFocusRect() +{ + if (!HasFocus()) + return tools::Rectangle(); + Size aFocusSize = GetOutputSizePixel(); + aFocusSize.AdjustWidth( -4 ); + aFocusSize.AdjustHeight( -4 ); + return tools::Rectangle(Point(2, 2), aFocusSize); +} + +bool Svx3DLightControl::MouseButtonDown( const MouseEvent& rMEvt ) +{ + bool bCallParent(true); + + // switch state + if(rMEvt.IsLeft()) + { + if(IsSelectionValid() || mbGeometrySelected) + { + mbMouseMoved = false; + bCallParent = false; + maActionStartPoint = rMEvt.GetPosPixel(); + CaptureMouse(); + mbMouseCaptured = true; + } + else + { + // Single click without moving much trying to do a selection + TrySelection(rMEvt.GetPosPixel()); + bCallParent = false; + } + } + + // call parent + if (bCallParent) + return Svx3DPreviewControl::MouseButtonDown(rMEvt); + return true; +} + +bool Svx3DLightControl::MouseMove(const MouseEvent& rMEvt) +{ + if (!mbMouseCaptured) + return false; + + Point aDeltaPos = rMEvt.GetPosPixel() - maActionStartPoint; + + if(!mbMouseMoved) + { + if(sal_Int32(aDeltaPos.X() * aDeltaPos.X() + aDeltaPos.Y() * aDeltaPos.Y()) > g_nInteractionStartDistance) + { + if(mbGeometrySelected) + { + GetRotation(mfSaveActionStartVer, mfSaveActionStartHor, mfSaveActionStartRotZ); + } + else + { + // interaction start, save values + GetPosition(mfSaveActionStartHor, mfSaveActionStartVer); + } + + mbMouseMoved = true; + } + } + + if(mbMouseMoved) + { + if(mbGeometrySelected) + { + double fNewRotX = mfSaveActionStartVer - basegfx::deg2rad(aDeltaPos.Y()); + double fNewRotY = mfSaveActionStartHor + basegfx::deg2rad(aDeltaPos.X()); + + // cut horizontal + while(fNewRotY < 0.0) + { + fNewRotY += 2 * M_PI; + } + + while(fNewRotY >= 2 * M_PI) + { + fNewRotY -= 2 * M_PI; + } + + // cut vertical + if(fNewRotX < -M_PI_2) + { + fNewRotX = -M_PI_2; + } + + if(fNewRotX > M_PI_2) + { + fNewRotX = M_PI_2; + } + + SetRotation(fNewRotX, fNewRotY, mfSaveActionStartRotZ); + + if(maChangeCallback.IsSet()) + { + maChangeCallback.Call(this); + } + } + else + { + // interaction in progress + double fNewPosHor = mfSaveActionStartHor + static_cast<double>(aDeltaPos.X()); + double fNewPosVer = mfSaveActionStartVer - static_cast<double>(aDeltaPos.Y()); + + // cut horizontal + fNewPosHor = NormAngle360(fNewPosHor); + + // cut vertical + if(fNewPosVer < -90.0) + { + fNewPosVer = -90.0; + } + + if(fNewPosVer > 90.0) + { + fNewPosVer = 90.0; + } + + SetPosition(fNewPosHor, fNewPosVer); + + if(maChangeCallback.IsSet()) + { + maChangeCallback.Call(this); + } + } + } + return true; +} + +bool Svx3DLightControl::MouseButtonUp(const MouseEvent& rMEvt) +{ + if (!mbMouseCaptured) + return false; + ReleaseMouse(); + mbMouseCaptured = false; + + if (mbMouseMoved) + { + // was change interactively + } + else + { + // simple click without much movement, try selection + TrySelection(rMEvt.GetPosPixel()); + } + + return true; +} + +void Svx3DLightControl::Resize() +{ + // set size of page + const Size aSize(GetDrawingArea()->get_ref_device().PixelToLogic(GetOutputSizePixel())); + mxFmPage->SetSize(aSize); + + // set position and size of scene + mpScene->SetSnapRect(tools::Rectangle(Point(0, 0), aSize)); +} + +void Svx3DLightControl::SetObjectType(SvxPreviewObjectType nType) +{ + // call parent + Svx3DPreviewControl::SetObjectType(nType); + + // apply object rotation + if(mp3DObj) + { + basegfx::B3DHomMatrix aObjectRotation; + aObjectRotation.rotate(mfRotateX, mfRotateY, mfRotateZ); + mp3DObj->SetTransform(aObjectRotation); + } +} + +bool Svx3DLightControl::IsSelectionValid() +{ + return (NO_LIGHT_SELECTED != maSelectedLight) && GetLightOnOff(maSelectedLight); +} + +void Svx3DLightControl::GetPosition(double& rHor, double& rVer) +{ + if(IsSelectionValid()) + { + basegfx::B3DVector aDirection(GetLightDirection(maSelectedLight)); + aDirection.normalize(); + rHor = basegfx::rad2deg(atan2(-aDirection.getX(), -aDirection.getZ()) + M_PI); // 0..360.0 + rVer = basegfx::rad2deg(atan2(aDirection.getY(), aDirection.getXZLength())); // -90.0..90.0 + } + if(IsGeometrySelected()) + { + rHor = basegfx::rad2deg(mfRotateY); // 0..360.0 + rVer = basegfx::rad2deg(mfRotateX); // -90.0..90.0 + } +} + +void Svx3DLightControl::SetPosition(double fHor, double fVer) +{ + if(IsSelectionValid()) + { + // set selected light's direction + fHor = basegfx::deg2rad(fHor) - M_PI; // -PI..PI + fVer = basegfx::deg2rad(fVer); // -PI2..PI2 + basegfx::B3DVector aDirection(cos(fVer) * -sin(fHor), sin(fVer), cos(fVer) * -cos(fHor)); + aDirection.normalize(); + + if(!aDirection.equal(GetLightDirection(maSelectedLight))) + { + // set changed light direction at SdrScene + SfxItemSet aSet(mpModel->GetItemPool()); + + switch(maSelectedLight) + { + case 0: aSet.Put(makeSvx3DLightDirection1Item(aDirection)); break; + case 1: aSet.Put(makeSvx3DLightDirection2Item(aDirection)); break; + case 2: aSet.Put(makeSvx3DLightDirection3Item(aDirection)); break; + case 3: aSet.Put(makeSvx3DLightDirection4Item(aDirection)); break; + case 4: aSet.Put(makeSvx3DLightDirection5Item(aDirection)); break; + case 5: aSet.Put(makeSvx3DLightDirection6Item(aDirection)); break; + case 6: aSet.Put(makeSvx3DLightDirection7Item(aDirection)); break; + default: + case 7: aSet.Put(makeSvx3DLightDirection8Item(aDirection)); break; + } + + mpScene->SetMergedItemSet(aSet); + + // correct 3D light's and LampFrame's geometries + AdaptToSelectedLight(); + Invalidate(); + } + } + if(!IsGeometrySelected()) + return; + + if(mfRotateX == fVer && mfRotateY == fHor) + return; + + mfRotateX = basegfx::deg2rad(fVer); + mfRotateY = basegfx::deg2rad(fHor); + + if(mp3DObj) + { + basegfx::B3DHomMatrix aObjectRotation; + aObjectRotation.rotate(mfRotateX, mfRotateY, mfRotateZ); + mp3DObj->SetTransform(aObjectRotation); + + Invalidate(); + } +} + +void Svx3DLightControl::SetRotation(double fRotX, double fRotY, double fRotZ) +{ + if(!IsGeometrySelected()) + return; + + if(fRotX == mfRotateX && fRotY == mfRotateY && fRotZ == mfRotateZ) + return; + + mfRotateX = fRotX; + mfRotateY = fRotY; + mfRotateZ = fRotZ; + + if(mp3DObj) + { + basegfx::B3DHomMatrix aObjectRotation; + aObjectRotation.rotate(mfRotateX, mfRotateY, mfRotateZ); + mp3DObj->SetTransform(aObjectRotation); + + Invalidate(); + } +} + +void Svx3DLightControl::GetRotation(double& rRotX, double& rRotY, double& rRotZ) +{ + rRotX = mfRotateX; + rRotY = mfRotateY; + rRotZ = mfRotateZ; +} + +void Svx3DLightControl::Set3DAttributes( const SfxItemSet& rAttr ) +{ + // call parent + Svx3DPreviewControl::Set3DAttributes(rAttr); + + if(maSelectedLight != NO_LIGHT_SELECTED && !GetLightOnOff(maSelectedLight)) + { + // selected light is no more active, select new one + maSelectedLight = NO_LIGHT_SELECTED; + } + + // local updates + ConstructLightObjects(); + AdaptToSelectedLight(); + Invalidate(); +} + +void Svx3DLightControl::SelectLight(sal_uInt32 nLightNumber) +{ + if(nLightNumber > 7) + { + nLightNumber = NO_LIGHT_SELECTED; + } + + if(NO_LIGHT_SELECTED != nLightNumber) + { + if(!GetLightOnOff(nLightNumber)) + { + nLightNumber = NO_LIGHT_SELECTED; + } + } + + if(nLightNumber != maSelectedLight) + { + maSelectedLight = nLightNumber; + mbGeometrySelected = false; + ConstructLightObjects(); + AdaptToSelectedLight(); + Invalidate(); + } +} + +bool Svx3DLightControl::GetLightOnOff(sal_uInt32 nNum) const +{ + if(nNum <= 7) + { + const SfxItemSet aLightItemSet(Get3DAttributes()); + + switch(nNum) + { + case 0 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_1).GetValue(); + case 1 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_2).GetValue(); + case 2 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_3).GetValue(); + case 3 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_4).GetValue(); + case 4 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_5).GetValue(); + case 5 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_6).GetValue(); + case 6 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_7).GetValue(); + case 7 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_8).GetValue(); + } + } + + return false; +} + +Color Svx3DLightControl::GetLightColor(sal_uInt32 nNum) const +{ + if(nNum <= 7) + { + const SfxItemSet aLightItemSet(Get3DAttributes()); + + switch(nNum) + { + case 0 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_1).GetValue(); + case 1 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_2).GetValue(); + case 2 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_3).GetValue(); + case 3 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_4).GetValue(); + case 4 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_5).GetValue(); + case 5 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_6).GetValue(); + case 6 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_7).GetValue(); + case 7 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_8).GetValue(); + } + } + + return COL_BLACK; +} + +basegfx::B3DVector Svx3DLightControl::GetLightDirection(sal_uInt32 nNum) const +{ + if(nNum <= 7) + { + const SfxItemSet aLightItemSet(Get3DAttributes()); + + switch(nNum) + { + case 0 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_1).GetValue(); + case 1 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_2).GetValue(); + case 2 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_3).GetValue(); + case 3 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_4).GetValue(); + case 4 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_5).GetValue(); + case 5 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_6).GetValue(); + case 6 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_7).GetValue(); + case 7 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_8).GetValue(); + } + } + + return basegfx::B3DVector(); +} + +SvxLightCtl3D::SvxLightCtl3D(Svx3DLightControl& rLightControl, weld::Scale& rHori, + weld::Scale& rVert, weld::Button& rSwitcher) + : mrLightControl(rLightControl) + , mrHorScroller(rHori) + , mrVerScroller(rVert) + , mrSwitcher(rSwitcher) +{ + // init members + Init(); +} + +void SvxLightCtl3D::Init() +{ + Size aSize(mrLightControl.GetDrawingArea()->get_ref_device().LogicToPixel(Size(80, 100), MapMode(MapUnit::MapAppFont))); + mrLightControl.set_size_request(aSize.Width(), aSize.Height()); + + // #i58240# set HelpIDs for scrollbars and switcher + mrHorScroller.set_help_id(HID_CTRL3D_HSCROLL); + mrVerScroller.set_help_id(HID_CTRL3D_VSCROLL); + mrSwitcher.set_help_id(HID_CTRL3D_SWITCHER); + mrSwitcher.set_accessible_name(SvxResId(STR_SWITCH)); + + // Light preview + mrLightControl.Show(); + mrLightControl.SetChangeCallback( LINK(this, SvxLightCtl3D, InternalInteractiveChange) ); + mrLightControl.SetSelectionChangeCallback( LINK(this, SvxLightCtl3D, InternalSelectionChange) ); + + // Horiz Scrollbar + mrHorScroller.show(); + mrHorScroller.set_range(0, 36000); + mrHorScroller.connect_value_changed( LINK(this, SvxLightCtl3D, ScrollBarMove) ); + + // Vert Scrollbar + mrVerScroller.show(); + mrVerScroller.set_range(0, 18000); + mrVerScroller.connect_value_changed( LINK(this, SvxLightCtl3D, ScrollBarMove) ); + + // Switch Button + mrSwitcher.show(); + mrSwitcher.connect_clicked( LINK(this, SvxLightCtl3D, ButtonPress) ); + + weld::DrawingArea* pArea = mrLightControl.GetDrawingArea(); + pArea->connect_key_press(Link<const KeyEvent&, bool>()); //acknowledge we first remove the old one + pArea->connect_key_press(LINK(this, SvxLightCtl3D, KeyInput)); + + pArea->connect_focus_in(Link<weld::Widget&, void>()); //acknowledge we first remove the old one + pArea->connect_focus_in(LINK(this, SvxLightCtl3D, FocusIn)); + + // check selection + CheckSelection(); +} + +void SvxLightCtl3D::CheckSelection() +{ + const bool bSelectionValid(mrLightControl.IsSelectionValid() || mrLightControl.IsGeometrySelected()); + mrHorScroller.set_sensitive(bSelectionValid); + mrVerScroller.set_sensitive(bSelectionValid); + + if (bSelectionValid) + { + double fHor(0.0), fVer(0.0); + mrLightControl.GetPosition(fHor, fVer); + mrHorScroller.set_value( sal_Int32(fHor * 100.0) ); + mrVerScroller.set_value( 18000 - sal_Int32((fVer + 90.0) * 100.0) ); + } +} + +void SvxLightCtl3D::move( double fDeltaHor, double fDeltaVer ) +{ + double fHor(0.0), fVer(0.0); + + mrLightControl.GetPosition(fHor, fVer); + fHor += fDeltaHor; + fVer += fDeltaVer; + + if( fVer > 90.0 ) + return; + + if ( fVer < -90.0 ) + return; + + mrLightControl.SetPosition(fHor, fVer); + mrHorScroller.set_value( sal_Int32(fHor * 100.0) ); + mrVerScroller.set_value( 18000 - sal_Int32((fVer + 90.0) * 100.0) ); + + if(maUserInteractiveChangeCallback.IsSet()) + { + maUserInteractiveChangeCallback.Call(this); + } +} + +IMPL_LINK(SvxLightCtl3D, KeyInput, const KeyEvent&, rKEvt, bool) +{ + const vcl::KeyCode aCode(rKEvt.GetKeyCode()); + + if (aCode.GetModifier()) + return false; + + bool bHandled = true; + + switch ( aCode.GetCode() ) + { + case KEY_SPACE: + { + break; + } + case KEY_LEFT: + { + move( -4.0, 0.0 ); // #i58242# changed move direction in X + break; + } + case KEY_RIGHT: + { + move( 4.0, 0.0 ); // #i58242# changed move direction in X + break; + } + case KEY_UP: + { + move( 0.0, 4.0 ); + break; + } + case KEY_DOWN: + { + move( 0.0, -4.0 ); + break; + } + case KEY_PAGEUP: + { + sal_Int32 nLight(mrLightControl.GetSelectedLight() - 1); + + while((nLight >= 0) && !mrLightControl.GetLightOnOff(nLight)) + { + nLight--; + } + + if(nLight < 0) + { + nLight = 7; + + while((nLight >= 0) && !mrLightControl.GetLightOnOff(nLight)) + { + nLight--; + } + } + + if(nLight >= 0) + { + mrLightControl.SelectLight(nLight); + CheckSelection(); + + if(maUserSelectionChangeCallback.IsSet()) + { + maUserSelectionChangeCallback.Call(this); + } + } + + break; + } + case KEY_PAGEDOWN: + { + sal_Int32 nLight(mrLightControl.GetSelectedLight() - 1); + + while(nLight <= 7 && !mrLightControl.GetLightOnOff(nLight)) + { + nLight++; + } + + if(nLight > 7) + { + nLight = 0; + + while(nLight <= 7 && !mrLightControl.GetLightOnOff(nLight)) + { + nLight++; + } + } + + if(nLight <= 7) + { + mrLightControl.SelectLight(nLight); + CheckSelection(); + + if(maUserSelectionChangeCallback.IsSet()) + { + maUserSelectionChangeCallback.Call(this); + } + } + + break; + } + default: + { + bHandled = false; + break; + } + } + return bHandled; +} + +IMPL_LINK_NOARG(SvxLightCtl3D, FocusIn, weld::Widget&, void) +{ + if (mrLightControl.IsEnabled()) + { + CheckSelection(); + } +} + +IMPL_LINK_NOARG(SvxLightCtl3D, ScrollBarMove, weld::Scale&, void) +{ + const sal_Int32 nHor(mrHorScroller.get_value()); + const sal_Int32 nVer(mrVerScroller.get_value()); + + mrLightControl.SetPosition( + static_cast<double>(nHor) / 100.0, + static_cast<double>((18000 - nVer) - 9000) / 100.0); + + if (maUserInteractiveChangeCallback.IsSet()) + { + maUserInteractiveChangeCallback.Call(this); + } +} + +IMPL_LINK_NOARG(SvxLightCtl3D, ButtonPress, weld::Button&, void) +{ + if(SvxPreviewObjectType::SPHERE == GetSvx3DLightControl().GetObjectType()) + { + GetSvx3DLightControl().SetObjectType(SvxPreviewObjectType::CUBE); + } + else + { + GetSvx3DLightControl().SetObjectType(SvxPreviewObjectType::SPHERE); + } +} + +IMPL_LINK_NOARG(SvxLightCtl3D, InternalInteractiveChange, Svx3DLightControl*, void) +{ + double fHor(0.0), fVer(0.0); + + mrLightControl.GetPosition(fHor, fVer); + mrHorScroller.set_value( sal_Int32(fHor * 100.0) ); + mrVerScroller.set_value( 18000 - sal_Int32((fVer + 90.0) * 100.0) ); + + if(maUserInteractiveChangeCallback.IsSet()) + { + maUserInteractiveChangeCallback.Call(this); + } +} + +IMPL_LINK_NOARG(SvxLightCtl3D, InternalSelectionChange, Svx3DLightControl*, void) +{ + CheckSelection(); + + if(maUserSelectionChangeCallback.IsSet()) + { + maUserSelectionChangeCallback.Call(this); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/dlgctrl.cxx b/svx/source/dialog/dlgctrl.cxx new file mode 100644 index 0000000000..df3d7b1c8b --- /dev/null +++ b/svx/source/dialog/dlgctrl.cxx @@ -0,0 +1,1464 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> +#include <vcl/event.hxx> +#include <sfx2/dialoghelper.hxx> +#include <sfx2/weldutils.hxx> +#include <svx/relfld.hxx> +#include <svx/xlineit0.hxx> +#include <svx/xtable.hxx> +#include <bitmaps.hlst> +#include <svx/dlgctrl.hxx> +#include <tools/debug.hxx> +#include <svxpixelctlaccessiblecontext.hxx> +#include <svtools/colorcfg.hxx> +#include <svxrectctaccessiblecontext.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <svx/svdorect.hxx> +#include <svx/svdmodel.hxx> +#include <svx/svdopath.hxx> +#include <sdr/contact/objectcontactofobjlistpainter.hxx> +#include <svx/sdr/contact/displayinfo.hxx> +#include <vcl/BitmapTools.hxx> + +#define OUTPUT_DRAWMODE_COLOR (DrawModeFlags::Default) +#define OUTPUT_DRAWMODE_CONTRAST (DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient) + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; + +// Control for display and selection of the corner points and +// mid point of an object + +BitmapEx& SvxRectCtl::GetRectBitmap() +{ + if( !pBitmap ) + InitRectBitmap(); + + return *pBitmap; +} + +SvxRectCtl::SvxRectCtl(SvxTabPage* pPage) + : m_pPage(pPage) + , nBorderWidth(Application::GetDefaultDevice()->LogicToPixel(Size(200, 0), MapMode(MapUnit::Map100thMM)).Width()) + , eRP(RectPoint::MM) + , eDefRP(RectPoint::MM) + , m_nState(CTL_STATE::NONE) + , mbCompleteDisable(false) +{ +} + +void SvxRectCtl::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aSize(pDrawingArea->get_approximate_digit_width() * 25, + pDrawingArea->get_text_height() * 5); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + Resize_Impl(aSize); +} + +void SvxRectCtl::SetControlSettings(RectPoint eRpt, sal_uInt16 nBorder) +{ + nBorderWidth = Application::GetDefaultDevice()->LogicToPixel(Size(nBorder, 0), MapMode(MapUnit::Map100thMM)).Width(); + eDefRP = eRpt; + Resize(); +} + +SvxRectCtl::~SvxRectCtl() +{ + pBitmap.reset(); +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + pAccContext.clear(); +#endif +} + +void SvxRectCtl::Resize() +{ + Resize_Impl(GetOutputSizePixel()); +} + +void SvxRectCtl::Resize_Impl(const Size &rSize) +{ + aPtLT = Point( 0 + nBorderWidth, 0 + nBorderWidth ); + aPtMT = Point( rSize.Width() / 2, 0 + nBorderWidth ); + aPtRT = Point( rSize.Width() - nBorderWidth, 0 + nBorderWidth ); + + aPtLM = Point( 0 + nBorderWidth, rSize.Height() / 2 ); + aPtMM = Point( rSize.Width() / 2, rSize.Height() / 2 ); + aPtRM = Point( rSize.Width() - nBorderWidth, rSize.Height() / 2 ); + + aPtLB = Point( 0 + nBorderWidth, rSize.Height() - nBorderWidth ); + aPtMB = Point( rSize.Width() / 2, rSize.Height() - nBorderWidth ); + aPtRB = Point( rSize.Width() - nBorderWidth, rSize.Height() - nBorderWidth ); + + Reset(); + StyleUpdated(); +} + +void SvxRectCtl::InitRectBitmap() +{ + pBitmap.reset(); + + const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); + svtools::ColorConfig aColorConfig; + + pBitmap.reset(new BitmapEx(RID_SVXCTRL_RECTBTNS)); + + // set bitmap-colors + Color aColorAry1[7]; + Color aColorAry2[7]; + aColorAry1[0] = Color( 0xC0, 0xC0, 0xC0 ); // light-gray + aColorAry1[1] = Color( 0xFF, 0xFF, 0x00 ); // yellow + aColorAry1[2] = Color( 0xFF, 0xFF, 0xFF ); // white + aColorAry1[3] = Color( 0x80, 0x80, 0x80 ); // dark-gray + aColorAry1[4] = Color( 0x00, 0x00, 0x00 ); // black + aColorAry1[5] = Color( 0x00, 0xFF, 0x00 ); // green + aColorAry1[6] = Color( 0x00, 0x00, 0xFF ); // blue + aColorAry2[0] = rStyles.GetDialogColor(); // background + aColorAry2[1] = rStyles.GetWindowColor(); + aColorAry2[2] = rStyles.GetLightColor(); + aColorAry2[3] = rStyles.GetShadowColor(); + aColorAry2[4] = rStyles.GetDarkShadowColor(); + aColorAry2[5] = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + aColorAry2[6] = rStyles.GetDialogColor(); + +#ifdef DBG_UTIL + static bool bModify = false; + bool& rModify = bModify; + if( rModify ) + { + static int n = 0; + static sal_uInt8 r = 0xFF; + static sal_uInt8 g = 0x00; + static sal_uInt8 b = 0xFF; + int& rn = n; + sal_uInt8& rr = r; + sal_uInt8& rg = g; + sal_uInt8& rb = b; + aColorAry2[ rn ] = Color( rr, rg, rb ); + } +#endif + + pBitmap->Replace( aColorAry1, aColorAry2, 7 ); +} + +void SvxRectCtl::StyleUpdated() +{ + pBitmap.reset(); // forces new creating of bitmap + CustomWidgetController::StyleUpdated(); +} + +void SvxRectCtl::InitSettings(vcl::RenderContext& rRenderContext) +{ + svtools::ColorConfig aColorConfig; + Color aTextColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor); + rRenderContext.SetTextColor(aTextColor); + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + rRenderContext.SetBackground(rStyleSettings.GetWindowColor()); +} + +// The clicked rectangle (3 x 3) is determined and the parent (dialog) +// is notified that the item was changed +bool SvxRectCtl::MouseButtonDown(const MouseEvent& rMEvt) +{ + // CompletelyDisabled() added to have a disabled state for SvxRectCtl + if(!IsCompletelyDisabled()) + { + aPtNew = GetApproxLogPtFromPixPt( rMEvt.GetPosPixel() ); + eRP = GetRPFromPoint( aPtNew ); + SetActualRP( eRP ); + + if (m_pPage) + m_pPage->PointChanged(GetDrawingArea(), eRP); + } + return true; +} + +bool SvxRectCtl::KeyInput(const KeyEvent& rKeyEvt) +{ + // CompletelyDisabled() added to have a disabled state for SvxRectCtl + if (IsCompletelyDisabled()) + return false; + + RectPoint eNewRP = eRP; + + switch( rKeyEvt.GetKeyCode().GetCode() ) + { + case KEY_DOWN: + { + if( !(m_nState & CTL_STATE::NOVERT) ) + switch( eNewRP ) + { + case RectPoint::LT: eNewRP = RectPoint::LM; break; + case RectPoint::MT: eNewRP = RectPoint::MM; break; + case RectPoint::RT: eNewRP = RectPoint::RM; break; + case RectPoint::LM: eNewRP = RectPoint::LB; break; + case RectPoint::MM: eNewRP = RectPoint::MB; break; + case RectPoint::RM: eNewRP = RectPoint::RB; break; + default: ; //prevent warning + } + } + break; + case KEY_UP: + { + if( !(m_nState & CTL_STATE::NOVERT) ) + switch( eNewRP ) + { + case RectPoint::LM: eNewRP = RectPoint::LT; break; + case RectPoint::MM: eNewRP = RectPoint::MT; break; + case RectPoint::RM: eNewRP = RectPoint::RT; break; + case RectPoint::LB: eNewRP = RectPoint::LM; break; + case RectPoint::MB: eNewRP = RectPoint::MM; break; + case RectPoint::RB: eNewRP = RectPoint::RM; break; + default: ; //prevent warning + } + } + break; + case KEY_LEFT: + { + if( !(m_nState & CTL_STATE::NOHORZ) ) + switch( eNewRP ) + { + case RectPoint::MT: eNewRP = RectPoint::LT; break; + case RectPoint::RT: eNewRP = RectPoint::MT; break; + case RectPoint::MM: eNewRP = RectPoint::LM; break; + case RectPoint::RM: eNewRP = RectPoint::MM; break; + case RectPoint::MB: eNewRP = RectPoint::LB; break; + case RectPoint::RB: eNewRP = RectPoint::MB; break; + default: ; //prevent warning + } + } + break; + case KEY_RIGHT: + { + if( !(m_nState & CTL_STATE::NOHORZ) ) + switch( eNewRP ) + { + case RectPoint::LT: eNewRP = RectPoint::MT; break; + case RectPoint::MT: eNewRP = RectPoint::RT; break; + case RectPoint::LM: eNewRP = RectPoint::MM; break; + case RectPoint::MM: eNewRP = RectPoint::RM; break; + case RectPoint::LB: eNewRP = RectPoint::MB; break; + case RectPoint::MB: eNewRP = RectPoint::RB; break; + default: ; //prevent warning + } + } + break; + default: + return false; + } + if( eNewRP != eRP ) + { + SetActualRP( eNewRP ); + + if (m_pPage) + m_pPage->PointChanged(GetDrawingArea(), eRP); + } + return true; +} + +// the control (rectangle with 9 circles) +void SvxRectCtl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + InitSettings(rRenderContext); + + Point aPtDiff(1, 1); + + const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); + + rRenderContext.SetLineColor(rStyles.GetDialogColor()); + rRenderContext.SetFillColor(rStyles.GetDialogColor()); + rRenderContext.DrawRect(tools::Rectangle(Point(0,0), rRenderContext.GetOutputSize())); + + if (IsEnabled()) + rRenderContext.SetLineColor(rStyles.GetLabelTextColor()); + else + rRenderContext.SetLineColor(rStyles.GetShadowColor()); + + rRenderContext.SetFillColor(); + + if (!IsEnabled()) + { + Color aOldCol = rRenderContext.GetLineColor(); + rRenderContext.SetLineColor(rStyles.GetLightColor()); + rRenderContext.DrawRect(tools::Rectangle(aPtLT + aPtDiff, aPtRB + aPtDiff)); + rRenderContext.SetLineColor(aOldCol); + } + rRenderContext.DrawRect(tools::Rectangle(aPtLT, aPtRB)); + + rRenderContext.SetFillColor(rRenderContext.GetBackground().GetColor()); + + Size aBtnSize(11, 11); + Size aDstBtnSize(aBtnSize); + Point aToCenter(aDstBtnSize.Width() >> 1, aDstBtnSize.Height() >> 1); + Point aBtnPnt1(IsEnabled() ? 0 : 22, 0); + Point aBtnPnt2(11, 0); + Point aBtnPnt3(22, 0); + + bool bNoHorz = bool(m_nState & CTL_STATE::NOHORZ); + bool bNoVert = bool(m_nState & CTL_STATE::NOVERT); + + BitmapEx& rBitmap = GetRectBitmap(); + + // CompletelyDisabled() added to have a disabled state for SvxRectCtl + if (IsCompletelyDisabled()) + { + rRenderContext.DrawBitmapEx(aPtLT - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtMT - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtRT - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtLM - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtMM - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtRM - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtLB - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtMB - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtRB - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap); + } + else + { + rRenderContext.DrawBitmapEx(aPtLT - aToCenter, aDstBtnSize, (bNoHorz || bNoVert)?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtMT - aToCenter, aDstBtnSize, bNoVert?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtRT - aToCenter, aDstBtnSize, (bNoHorz || bNoVert)?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtLM - aToCenter, aDstBtnSize, bNoHorz?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap); + + // Center for rectangle and line + rRenderContext.DrawBitmapEx(aPtMM - aToCenter, aDstBtnSize, aBtnPnt1, aBtnSize, rBitmap); + + rRenderContext.DrawBitmapEx(aPtRM - aToCenter, aDstBtnSize, bNoHorz?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtLB - aToCenter, aDstBtnSize, (bNoHorz || bNoVert)?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtMB - aToCenter, aDstBtnSize, bNoVert?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap); + rRenderContext.DrawBitmapEx(aPtRB - aToCenter, aDstBtnSize, (bNoHorz || bNoVert)?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap); + } + + // draw active button, avoid center pos for angle + // CompletelyDisabled() added to have a disabled state for SvxRectCtl + if (!IsCompletelyDisabled()) + { + if (IsEnabled()) + { + Point aCenterPt(aPtNew); + aCenterPt -= aToCenter; + + rRenderContext.DrawBitmapEx(aCenterPt, aDstBtnSize, aBtnPnt2, aBtnSize, rBitmap); + } + } +} + +tools::Rectangle SvxRectCtl::GetFocusRect() +{ + tools::Rectangle aRet; + if (HasFocus()) + aRet = CalculateFocusRectangle(); + return aRet; +} + +// Convert RectPoint Point + +const Point& SvxRectCtl::GetPointFromRP( RectPoint _eRP) const +{ + switch( _eRP ) + { + case RectPoint::LT: return aPtLT; + case RectPoint::MT: return aPtMT; + case RectPoint::RT: return aPtRT; + case RectPoint::LM: return aPtLM; + case RectPoint::MM: return aPtMM; + case RectPoint::RM: return aPtRM; + case RectPoint::LB: return aPtLB; + case RectPoint::MB: return aPtMB; + case RectPoint::RB: return aPtRB; + } + return aPtMM; // default +} + +Point SvxRectCtl::SetActualRPWithoutInvalidate( RectPoint eNewRP ) +{ + Point aPtLast = aPtNew; + aPtNew = GetPointFromRP( eNewRP ); + + if( m_nState & CTL_STATE::NOHORZ ) + aPtNew.setX( aPtMM.X() ); + + if( m_nState & CTL_STATE::NOVERT ) + aPtNew.setY( aPtMM.Y() ); + + // fdo#74751 this fix reverse base point on RTL UI. + bool bRTL = AllSettings::GetLayoutRTL(); + eNewRP = GetRPFromPoint( aPtNew, bRTL ); + + eDefRP = eNewRP; + eRP = eNewRP; + + return aPtLast; +} + +void SvxRectCtl::GetFocus() +{ + Invalidate(); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + // Send accessibility event. + if (pAccContext.is()) + { + pAccContext->FireChildFocus(GetActualRP()); + } +#endif +} + +void SvxRectCtl::LoseFocus() +{ + Invalidate(); +} + +Point SvxRectCtl::GetApproxLogPtFromPixPt( const Point& rPt ) const +{ + Point aPt = rPt; + tools::Long x; + tools::Long y; + + Size aSize(GetOutputSizePixel()); + + if( !( m_nState & CTL_STATE::NOHORZ ) ) + { + if( aPt.X() < aSize.Width() / 3 ) + x = aPtLT.X(); + else if( aPt.X() < aSize.Width() * 2 / 3 ) + x = aPtMM.X(); + else + x = aPtRB.X(); + } + else + x = aPtMM.X(); + + if( !( m_nState & CTL_STATE::NOVERT ) ) + { + if( aPt.Y() < aSize.Height() / 3 ) + y = aPtLT.Y(); + else if( aPt.Y() < aSize.Height() * 2 / 3 ) + y = aPtMM.Y(); + else + y = aPtRB.Y(); + } + else + y = aPtMM.Y(); + + return Point( x, y ); +} + + +// Converts Point in RectPoint + +RectPoint SvxRectCtl::GetRPFromPoint( Point aPt, bool bRTL ) const +{ + RectPoint rPoint = RectPoint::MM; // default + + if (aPt == aPtLT) rPoint = bRTL ? RectPoint::RT : RectPoint::LT; + else if( aPt == aPtMT) rPoint = RectPoint::MT; + else if( aPt == aPtRT) rPoint = bRTL ? RectPoint::LT : RectPoint::RT; + else if( aPt == aPtLM) rPoint = bRTL ? RectPoint::RM : RectPoint::LM; + else if( aPt == aPtRM) rPoint = bRTL ? RectPoint::LM : RectPoint::RM; + else if( aPt == aPtLB) rPoint = bRTL ? RectPoint::RB : RectPoint::LB; + else if( aPt == aPtMB) rPoint = RectPoint::MB; + else if( aPt == aPtRB) rPoint = bRTL ? RectPoint::LB : RectPoint::RB; + + return rPoint; +} + +// Resets to the original state of the control + +void SvxRectCtl::Reset() +{ + aPtNew = GetPointFromRP( eDefRP ); + eRP = eDefRP; + Invalidate(); +} + +// Returns the currently selected RectPoint + + +void SvxRectCtl::SetActualRP( RectPoint eNewRP ) +{ + SetActualRPWithoutInvalidate(eNewRP); + + Invalidate(); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + // notify accessibility object about change + if (pAccContext.is()) + pAccContext->selectChild( eNewRP /* MT, bFireFocus */ ); +#endif +} + +void SvxRectCtl::SetState( CTL_STATE nState ) +{ + m_nState = nState; + + Point aPtLast( GetPointFromRP( eRP ) ); + Point _aPtNew( aPtLast ); + + if( m_nState & CTL_STATE::NOHORZ ) + _aPtNew.setX( aPtMM.X() ); + + if( m_nState & CTL_STATE::NOVERT) + _aPtNew.setY( aPtMM.Y() ); + + eRP = GetRPFromPoint( _aPtNew ); + Invalidate(); + + if (m_pPage) + m_pPage->PointChanged(GetDrawingArea(), eRP); +} + +tools::Rectangle SvxRectCtl::CalculateFocusRectangle() const +{ + Size aDstBtnSize(15, 15); + return tools::Rectangle( aPtNew - Point( aDstBtnSize.Width() >> 1, aDstBtnSize.Height() >> 1 ), aDstBtnSize ); +} + +tools::Rectangle SvxRectCtl::CalculateFocusRectangle( RectPoint eRectPoint ) const +{ + tools::Rectangle aRet; + RectPoint eOldRectPoint = GetActualRP(); + + if( eOldRectPoint == eRectPoint ) + aRet = CalculateFocusRectangle(); + else + { + SvxRectCtl* pThis = const_cast<SvxRectCtl*>(this); + + pThis->SetActualRPWithoutInvalidate( eRectPoint ); // no invalidation because it's only temporary! + aRet = CalculateFocusRectangle(); + + pThis->SetActualRPWithoutInvalidate( eOldRectPoint ); // no invalidation because nothing has changed! + } + + return aRet; +} + +Reference< XAccessible > SvxRectCtl::CreateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + pAccContext = new SvxRectCtlAccessibleContext(this); +#endif + return pAccContext; +} + +RectPoint SvxRectCtl::GetApproxRPFromPixPt( const css::awt::Point& r ) const +{ + return GetRPFromPoint( GetApproxLogPtFromPixPt( Point( r.X, r.Y ) ) ); +} + +// CompletelyDisabled() added to have a disabled state for SvxRectCtl +void SvxRectCtl::DoCompletelyDisable(bool bNew) +{ + mbCompleteDisable = bNew; + Invalidate(); +} + +// Control for editing bitmaps + +css::uno::Reference< css::accessibility::XAccessible > SvxPixelCtl::CreateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (!m_xAccess.is()) + m_xAccess = new SvxPixelCtlAccessible(this); +#endif + return m_xAccess; +} + +tools::Long SvxPixelCtl::PointToIndex(const Point &aPt) const +{ + tools::Long nX = aPt.X() * nLines / aRectSize.Width(); + tools::Long nY = aPt.Y() * nLines / aRectSize.Height(); + + return nX + nY * nLines ; +} + +Point SvxPixelCtl::IndexToPoint(tools::Long nIndex) const +{ + DBG_ASSERT(nIndex >= 0 && nIndex < nSquares ," Check Index"); + + sal_Int32 nXIndex = nIndex % nLines; + sal_Int32 nYIndex = nIndex / nLines; + + Point aPtTl; + aPtTl.setY( aRectSize.Height() * nYIndex / nLines + 1 ); + aPtTl.setX( aRectSize.Width() * nXIndex / nLines + 1 ); + + return aPtTl; +} + +tools::Long SvxPixelCtl::GetFocusPosIndex() const +{ + return aFocusPosition.getX() + aFocusPosition.getY() * nLines ; +} + +tools::Long SvxPixelCtl::ShowPosition( const Point &rPt) +{ + sal_Int32 nX = rPt.X() * nLines / aRectSize.Width(); + sal_Int32 nY = rPt.Y() * nLines / aRectSize.Height(); + + ChangePixel( nX + nY * nLines ); + + //Solution:Set new focus position and repaint + aFocusPosition.setX(nX); + aFocusPosition.setY(nY); + Invalidate(tools::Rectangle(Point(0,0),aRectSize)); + + if (m_pPage) + m_pPage->PointChanged(GetDrawingArea(), RectPoint::MM ); // RectPoint is dummy + + return GetFocusPosIndex(); + +} + +SvxPixelCtl::SvxPixelCtl(SvxTabPage* pPage) + : m_pPage(pPage) + , bPaintable(true) + , aFocusPosition(0,0) +{ + maPixelData.fill(0); +} + +void SvxPixelCtl::Resize() +{ + CustomWidgetController::Resize(); + aRectSize = GetOutputSizePixel(); +} + +void SvxPixelCtl::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 25, + pDrawingArea->get_text_height() * 10); +} + +SvxPixelCtl::~SvxPixelCtl() +{ +} + +// Changes the foreground or Background color + +void SvxPixelCtl::ChangePixel( sal_uInt16 nPixel ) +{ + if( maPixelData[nPixel] == 0 ) + maPixelData[nPixel] = 1; // could be extended to more colors + else + maPixelData[nPixel] = 0; +} + +// The clicked rectangle is identified, to change its color + +bool SvxPixelCtl::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if (!aRectSize.Width() || !aRectSize.Height()) + return true; + + //Grab focus when click in window + if (!HasFocus()) + { + GrabFocus(); + } + + tools::Long nIndex = ShowPosition(rMEvt.GetPosPixel()); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if(m_xAccess.is()) + { + m_xAccess->NotifyChild(nIndex,true, true); + } +#else + (void)nIndex; +#endif + + return true; +} + +tools::Rectangle SvxPixelCtl::GetFocusRect() +{ + tools::Rectangle aRet; + //Draw visual focus when has focus + if (HasFocus()) + aRet = implCalFocusRect(aFocusPosition); + return aRet; +} + +// Draws the Control (Rectangle with nine circles) +void SvxPixelCtl::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) +{ + if (!aRectSize.Width() || !aRectSize.Height()) + return; + + sal_uInt16 i, j, nTmp; + Point aPtTl, aPtBr; + + if (bPaintable) + { + // Draw lines + rRenderContext.SetLineColor(Color()); + for (i = 1; i < nLines; i++) + { + // horizontal + nTmp = static_cast<sal_uInt16>(aRectSize.Height() * i / nLines); + rRenderContext.DrawLine(Point(0, nTmp), Point(aRectSize.Width(), nTmp)); + // vertically + nTmp = static_cast<sal_uInt16>( aRectSize.Width() * i / nLines ); + rRenderContext.DrawLine(Point(nTmp, 0), Point(nTmp, aRectSize.Height())); + } + + //Draw Rectangles (squares) + rRenderContext.SetLineColor(); + sal_uInt16 nLastPixel = maPixelData[0] ? 0 : 1; + + for (i = 0; i < nLines; i++) + { + aPtTl.setY( aRectSize.Height() * i / nLines + 1 ); + aPtBr.setY( aRectSize.Height() * (i + 1) / nLines - 1 ); + + for (j = 0; j < nLines; j++) + { + aPtTl.setX( aRectSize.Width() * j / nLines + 1 ); + aPtBr.setX( aRectSize.Width() * (j + 1) / nLines - 1 ); + + if (maPixelData[i * nLines + j] != nLastPixel) + { + nLastPixel = maPixelData[i * nLines + j]; + // Change color: 0 -> Background color + rRenderContext.SetFillColor(nLastPixel ? aPixelColor : aBackgroundColor); + } + rRenderContext.DrawRect(tools::Rectangle(aPtTl, aPtBr)); + } + } + } + else + { + rRenderContext.SetBackground(Wallpaper(COL_LIGHTGRAY)); + rRenderContext.SetLineColor(COL_LIGHTRED); + rRenderContext.DrawLine(Point(0, 0), Point(aRectSize.Width(), aRectSize.Height())); + rRenderContext.DrawLine(Point(0, aRectSize.Height()), Point(aRectSize.Width(), 0)); + } +} + +//Calculate visual focus rectangle via focus position +tools::Rectangle SvxPixelCtl::implCalFocusRect( const Point& aPosition ) +{ + tools::Long nLeft,nTop,nRight,nBottom; + tools::Long i,j; + i = aPosition.Y(); + j = aPosition.X(); + nLeft = aRectSize.Width() * j / nLines + 1; + nRight = aRectSize.Width() * (j + 1) / nLines - 1; + nTop = aRectSize.Height() * i / nLines + 1; + nBottom = aRectSize.Height() * (i + 1) / nLines - 1; + return tools::Rectangle(nLeft,nTop,nRight,nBottom); +} + +//Solution:Keyboard function +bool SvxPixelCtl::KeyInput( const KeyEvent& rKEvt ) +{ + vcl::KeyCode aKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nCode = aKeyCode.GetCode(); + bool bIsMod = aKeyCode.IsShift() || aKeyCode.IsMod1() || aKeyCode.IsMod2(); + + if( !bIsMod ) + { + Point aRepaintPoint( aRectSize.Width() *( aFocusPosition.getX() - 1)/ nLines - 1, + aRectSize.Height() *( aFocusPosition.getY() - 1)/ nLines -1 + ); + Size aRepaintSize( aRectSize.Width() *3/ nLines + 2,aRectSize.Height() *3/ nLines + 2); + tools::Rectangle aRepaintRect( aRepaintPoint, aRepaintSize ); + bool bFocusPosChanged=false; + switch(nCode) + { + case KEY_LEFT: + if(aFocusPosition.getX() >= 1) + { + aFocusPosition.setX( aFocusPosition.getX() - 1 ); + Invalidate(aRepaintRect); + bFocusPosChanged=true; + } + break; + case KEY_RIGHT: + if( aFocusPosition.getX() < (nLines - 1) ) + { + aFocusPosition.setX( aFocusPosition.getX() + 1 ); + Invalidate(aRepaintRect); + bFocusPosChanged=true; + } + break; + case KEY_UP: + if(aFocusPosition.getY() >= 1) + { + aFocusPosition.setY( aFocusPosition.getY() - 1 ); + Invalidate(aRepaintRect); + bFocusPosChanged=true; + } + break; + case KEY_DOWN: + if( aFocusPosition.getY() < ( nLines - 1 ) ) + { + aFocusPosition.setY( aFocusPosition.getY() + 1 ); + Invalidate(aRepaintRect); + bFocusPosChanged=true; + } + break; + case KEY_SPACE: + ChangePixel( sal_uInt16(aFocusPosition.getX() + aFocusPosition.getY() * nLines) ); + Invalidate( implCalFocusRect(aFocusPosition) ); + break; + default: + return CustomWidgetController::KeyInput( rKEvt ); + } +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if(m_xAccess.is()) + { + tools::Long nIndex = GetFocusPosIndex(); + switch(nCode) + { + case KEY_LEFT: + case KEY_RIGHT: + case KEY_UP: + case KEY_DOWN: + if (bFocusPosChanged) + { + m_xAccess->NotifyChild(nIndex,false,false); + } + break; + case KEY_SPACE: + m_xAccess->NotifyChild(nIndex,false,true); + break; + default: + break; + } + } +#else + (void)bFocusPosChanged; +#endif + return true; + } + else + { + return CustomWidgetController::KeyInput( rKEvt ); + } +} + +//Draw focus when get focus +void SvxPixelCtl::GetFocus() +{ + Invalidate(implCalFocusRect(aFocusPosition)); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (m_xAccess.is()) + { + m_xAccess->NotifyChild(GetFocusPosIndex(),true,false); + } +#endif +} + +void SvxPixelCtl::LoseFocus() +{ + Invalidate(); +} + +void SvxPixelCtl::SetXBitmap(const BitmapEx& rBitmapEx) +{ + if (vcl::bitmap::isHistorical8x8(rBitmapEx, aBackgroundColor, aPixelColor)) + { + for (sal_uInt16 i = 0; i < nSquares; i++) + { + Color aColor = rBitmapEx.GetPixelColor(i%8, i/8); + maPixelData[i] = (aColor == aBackgroundColor) ? 0 : 1; + } + } +} + +// Returns a specific pixel + +sal_uInt8 SvxPixelCtl::GetBitmapPixel( const sal_uInt16 nPixel ) const +{ + return maPixelData[nPixel]; +} + +// Resets to the original state of the control + +void SvxPixelCtl::Reset() +{ + // clear pixel area + maPixelData.fill(0); + Invalidate(); +} + +SvxLineLB::SvxLineLB(std::unique_ptr<weld::ComboBox> pControl) + : m_xControl(std::move(pControl)) + , mbAddStandardFields(true) +{ +} + +void SvxLineLB::setAddStandardFields(bool bNew) +{ + if(getAddStandardFields() != bNew) + { + mbAddStandardFields = bNew; + } +} + +// Fills the listbox (provisional) with strings + +void SvxLineLB::Fill( const XDashListRef &pList ) +{ + m_xControl->clear(); + + if( !pList.is() ) + return; + + ScopedVclPtrInstance< VirtualDevice > pVD; + + if(getAddStandardFields()) + { + // entry for 'none' + m_xControl->append_text(pList->GetStringForUiNoLine()); + + // entry for solid line + const BitmapEx aBitmap = pList->GetBitmapForUISolidLine(); + const Size aBmpSize(aBitmap.GetSizePixel()); + pVD->SetOutputSizePixel(aBmpSize, false); + pVD->DrawBitmapEx(Point(), aBitmap); + m_xControl->append("", pList->GetStringForUiSolidLine(), *pVD); + } + + // entries for dashed lines + + tools::Long nCount = pList->Count(); + m_xControl->freeze(); + + for( tools::Long i = 0; i < nCount; i++ ) + { + const XDashEntry* pEntry = pList->GetDash(i); + const BitmapEx aBitmap = pList->GetUiBitmap( i ); + if( !aBitmap.IsEmpty() ) + { + const Size aBmpSize(aBitmap.GetSizePixel()); + pVD->SetOutputSizePixel(aBmpSize, false); + pVD->DrawBitmapEx(Point(), aBitmap); + m_xControl->append("", pEntry->GetName(), *pVD); + } + else + { + m_xControl->append_text(pEntry->GetName()); + } + } + + m_xControl->thaw(); +} + +void SvxLineLB::Append( const XDashEntry& rEntry, const BitmapEx& rBitmap ) +{ + if (!rBitmap.IsEmpty()) + { + ScopedVclPtrInstance< VirtualDevice > pVD; + + const Size aBmpSize(rBitmap.GetSizePixel()); + pVD->SetOutputSizePixel(aBmpSize, false); + pVD->DrawBitmapEx(Point(), rBitmap); + m_xControl->append("", rEntry.GetName(), *pVD); + } + else + { + m_xControl->append_text(rEntry.GetName()); + } +} + +void SvxLineLB::Modify(const XDashEntry& rEntry, sal_Int32 nPos, const BitmapEx& rBitmap) +{ + m_xControl->remove(nPos); + + if (!rBitmap.IsEmpty()) + { + ScopedVclPtrInstance< VirtualDevice > pVD; + + const Size aBmpSize(rBitmap.GetSizePixel()); + pVD->SetOutputSizePixel(aBmpSize, false); + pVD->DrawBitmapEx(Point(), rBitmap); + m_xControl->insert(nPos, rEntry.GetName(), nullptr, nullptr, pVD); + } + else + { + m_xControl->insert_text(nPos, rEntry.GetName()); + } +} + +SvxLineEndLB::SvxLineEndLB(std::unique_ptr<weld::ComboBox> pControl) + : m_xControl(std::move(pControl)) +{ +} + +void SvxLineEndLB::Fill( const XLineEndListRef &pList, bool bStart ) +{ + if( !pList.is() ) + return; + + tools::Long nCount = pList->Count(); + ScopedVclPtrInstance< VirtualDevice > pVD; + m_xControl->freeze(); + + for( tools::Long i = 0; i < nCount; i++ ) + { + const XLineEndEntry* pEntry = pList->GetLineEnd(i); + const BitmapEx aBitmap = pList->GetUiBitmap( i ); + if( !aBitmap.IsEmpty() ) + { + const Size aBmpSize(aBitmap.GetSizePixel()); + pVD->SetOutputSizePixel(Size(aBmpSize.Width() / 2, aBmpSize.Height()), false); + pVD->DrawBitmapEx(bStart ? Point() : Point(-aBmpSize.Width() / 2, 0), aBitmap); + m_xControl->append("", pEntry->GetName(), *pVD); + } + else + m_xControl->append_text(pEntry->GetName()); + } + + m_xControl->thaw(); +} + +void SvxLineEndLB::Append( const XLineEndEntry& rEntry, const BitmapEx& rBitmap ) +{ + if(!rBitmap.IsEmpty()) + { + ScopedVclPtrInstance< VirtualDevice > pVD; + + const Size aBmpSize(rBitmap.GetSizePixel()); + pVD->SetOutputSizePixel(Size(aBmpSize.Width() / 2, aBmpSize.Height()), false); + pVD->DrawBitmapEx(Point(-aBmpSize.Width() / 2, 0), rBitmap); + m_xControl->append("", rEntry.GetName(), *pVD); + } + else + { + m_xControl->append_text(rEntry.GetName()); + } +} + +void SvxLineEndLB::Modify( const XLineEndEntry& rEntry, sal_Int32 nPos, const BitmapEx& rBitmap ) +{ + m_xControl->remove(nPos); + + if(!rBitmap.IsEmpty()) + { + ScopedVclPtrInstance< VirtualDevice > pVD; + + const Size aBmpSize(rBitmap.GetSizePixel()); + pVD->SetOutputSizePixel(Size(aBmpSize.Width() / 2, aBmpSize.Height()), false); + pVD->DrawBitmapEx(Point(-aBmpSize.Width() / 2, 0), rBitmap); + m_xControl->insert(nPos, rEntry.GetName(), nullptr, nullptr, pVD); + } + else + { + m_xControl->insert_text(nPos, rEntry.GetName()); + } +} + +void SvxXLinePreview::Resize() +{ + SvxPreviewBase::Resize(); + + const Size aOutputSize(GetOutputSize()); + const sal_Int32 nDistance(500); + const sal_Int32 nAvailableLength(aOutputSize.Width() - (4 * nDistance)); + + // create DrawObjectA + const sal_Int32 aYPosA(aOutputSize.Height() / 2); + const basegfx::B2DPoint aPointA1( nDistance, aYPosA); + const basegfx::B2DPoint aPointA2( aPointA1.getX() + ((nAvailableLength * 14) / 20), aYPosA ); + basegfx::B2DPolygon aPolygonA; + aPolygonA.append(aPointA1); + aPolygonA.append(aPointA2); + mpLineObjA->SetPathPoly(basegfx::B2DPolyPolygon(aPolygonA)); + + // create DrawObjectB + const sal_Int32 aYPosB1((aOutputSize.Height() * 3) / 4); + const sal_Int32 aYPosB2((aOutputSize.Height() * 1) / 4); + const basegfx::B2DPoint aPointB1( aPointA2.getX() + nDistance, aYPosB1); + const basegfx::B2DPoint aPointB2( aPointB1.getX() + ((nAvailableLength * 2) / 20), aYPosB2 ); + const basegfx::B2DPoint aPointB3( aPointB2.getX() + ((nAvailableLength * 2) / 20), aYPosB1 ); + basegfx::B2DPolygon aPolygonB; + aPolygonB.append(aPointB1); + aPolygonB.append(aPointB2); + aPolygonB.append(aPointB3); + mpLineObjB->SetPathPoly(basegfx::B2DPolyPolygon(aPolygonB)); + + // create DrawObjectC + basegfx::B2DPolygon aPolygonC; + const basegfx::B2DPoint aPointC1( aPointB3.getX() + nDistance, aYPosB1); + const basegfx::B2DPoint aPointC2( aPointC1.getX() + ((nAvailableLength * 1) / 20), aYPosB2 ); + const basegfx::B2DPoint aPointC3( aPointC2.getX() + ((nAvailableLength * 1) / 20), aYPosB1 ); + aPolygonC.append(aPointC1); + aPolygonC.append(aPointC2); + aPolygonC.append(aPointC3); + mpLineObjC->SetPathPoly(basegfx::B2DPolyPolygon(aPolygonC)); +} + +SvxXLinePreview::SvxXLinePreview() + : mpGraphic(nullptr) + , mbWithSymbol(false) +{ +} + +void SvxXLinePreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + SvxPreviewBase::SetDrawingArea(pDrawingArea); + + mpLineObjA = new SdrPathObj(getModel(), SdrObjKind::Line); + mpLineObjB = new SdrPathObj(getModel(), SdrObjKind::PolyLine); + mpLineObjC = new SdrPathObj(getModel(), SdrObjKind::PolyLine); + + Resize(); + Invalidate(); +} + +SvxXLinePreview::~SvxXLinePreview() +{ +} + +void SvxXLinePreview::SetSymbol(Graphic* p,const Size& s) +{ + mpGraphic = p; + maSymbolSize = s; +} + +void SvxXLinePreview::ResizeSymbol(const Size& s) +{ + if ( s != maSymbolSize ) + { + maSymbolSize = s; + Invalidate(); + } +} + +void SvxXLinePreview::SetLineAttributes(const SfxItemSet& rItemSet) +{ + // Set ItemSet at objects + mpLineObjA->SetMergedItemSet(rItemSet); + + // At line joints, do not use arrows + SfxItemSet aTempSet(rItemSet); + aTempSet.ClearItem(XATTR_LINESTART); + aTempSet.ClearItem(XATTR_LINEEND); + + mpLineObjB->SetMergedItemSet(aTempSet); + mpLineObjC->SetMergedItemSet(aTempSet); +} + +void SvxXLinePreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + LocalPrePaint(rRenderContext); + + // paint objects to buffer device + sdr::contact::SdrObjectVector aObjectVector; + aObjectVector.push_back(mpLineObjA.get()); + aObjectVector.push_back(mpLineObjB.get()); + aObjectVector.push_back(mpLineObjC.get()); + + sdr::contact::ObjectContactOfObjListPainter aPainter(getBufferDevice(), std::move(aObjectVector), nullptr); + sdr::contact::DisplayInfo aDisplayInfo; + + // do processing + aPainter.ProcessDisplay(aDisplayInfo); + + if ( mbWithSymbol && mpGraphic ) + { + const Size aOutputSize(GetOutputSize()); + Point aPos( aOutputSize.Width() / 3, aOutputSize.Height() / 2 ); + aPos.AdjustX( -(maSymbolSize.Width() / 2) ); + aPos.AdjustY( -(maSymbolSize.Height() / 2) ); + mpGraphic->Draw(getBufferDevice(), aPos, maSymbolSize); + } + + LocalPostPaint(rRenderContext); +} + +SvxXShadowPreview::SvxXShadowPreview() +{ +} + +void SvxXShadowPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + SvxPreviewBase::SetDrawingArea(pDrawingArea); + InitSettings(); + + // prepare size + Size aSize = GetPreviewSize().GetSize(); + aSize.setWidth( aSize.Width() / 3 ); + aSize.setHeight( aSize.Height() / 3 ); + + // create RectangleObject + const tools::Rectangle aObjectSize( Point( aSize.Width(), aSize.Height() ), aSize ); + mpRectangleObject = new SdrRectObj( + getModel(), + aObjectSize); + + // create ShadowObject + const tools::Rectangle aShadowSize( Point( aSize.Width(), aSize.Height() ), aSize ); + mpRectangleShadow = new SdrRectObj( + getModel(), + aShadowSize); +} + +SvxXShadowPreview::~SvxXShadowPreview() +{ +} + +void SvxXShadowPreview::SetRectangleAttributes(const SfxItemSet& rItemSet) +{ + mpRectangleObject->SetMergedItemSet(rItemSet, true); + mpRectangleObject->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE)); +} + +void SvxXShadowPreview::SetShadowAttributes(const SfxItemSet& rItemSet) +{ + mpRectangleShadow->SetMergedItemSet(rItemSet, true); + mpRectangleShadow->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE)); +} + +void SvxXShadowPreview::SetShadowPosition(const Point& rPos) +{ + maShadowOffset = rPos; +} + +void SvxXShadowPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.Push(vcl::PushFlags::MAPMODE); + rRenderContext.SetMapMode(MapMode(MapUnit::Map100thMM)); + + LocalPrePaint(rRenderContext); + + // prepare size + Size aSize = rRenderContext.GetOutputSize(); + aSize.setWidth( aSize.Width() / 3 ); + aSize.setHeight( aSize.Height() / 3 ); + + tools::Rectangle aObjectRect(Point(aSize.Width(), aSize.Height()), aSize); + mpRectangleObject->SetSnapRect(aObjectRect); + aObjectRect.Move(maShadowOffset.X(), maShadowOffset.Y()); + mpRectangleShadow->SetSnapRect(aObjectRect); + + sdr::contact::SdrObjectVector aObjectVector; + + aObjectVector.push_back(mpRectangleShadow.get()); + aObjectVector.push_back(mpRectangleObject.get()); + + sdr::contact::ObjectContactOfObjListPainter aPainter(getBufferDevice(), std::move(aObjectVector), nullptr); + sdr::contact::DisplayInfo aDisplayInfo; + + aPainter.ProcessDisplay(aDisplayInfo); + + LocalPostPaint(rRenderContext); + + rRenderContext.Pop(); +} + +void SvxPreviewBase::InitSettings() +{ + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + svtools::ColorConfig aColorConfig; + Color aTextColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor); + getBufferDevice().SetTextColor(aTextColor); + + getBufferDevice().SetBackground(rStyleSettings.GetWindowColor()); + + getBufferDevice().SetDrawMode(rStyleSettings.GetHighContrastMode() ? OUTPUT_DRAWMODE_CONTRAST : OUTPUT_DRAWMODE_COLOR); + + Invalidate(); +} + +SvxPreviewBase::SvxPreviewBase() + : mpModel(new SdrModel(nullptr, nullptr, true)) +{ + // init model + mpModel->GetItemPool().FreezeIdRanges(); +} + +void SvxPreviewBase::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aSize(getPreviewStripSize(pDrawingArea->get_ref_device())); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); + + mpBufferDevice = VclPtr<VirtualDevice>::Create(pDrawingArea->get_ref_device()); + mpBufferDevice->SetMapMode(MapMode(MapUnit::Map100thMM)); +} + +SvxPreviewBase::~SvxPreviewBase() +{ + mpModel.reset(); + mpBufferDevice.disposeAndClear(); +} + +void SvxPreviewBase::LocalPrePaint(vcl::RenderContext const & rRenderContext) +{ + // init BufferDevice + if (mpBufferDevice->GetOutputSizePixel() != GetOutputSizePixel()) + mpBufferDevice->SetOutputSizePixel(GetOutputSizePixel()); + mpBufferDevice->SetAntialiasing(rRenderContext.GetAntialiasing()); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + if (rStyleSettings.GetPreviewUsesCheckeredBackground()) + { + const Point aNull(0, 0); + static const sal_uInt32 nLen(8); + static const Color aW(COL_WHITE); + static const Color aG(0xef, 0xef, 0xef); + const bool bWasEnabled(mpBufferDevice->IsMapModeEnabled()); + + mpBufferDevice->EnableMapMode(false); + mpBufferDevice->DrawCheckered(aNull, mpBufferDevice->GetOutputSizePixel(), nLen, aW, aG); + mpBufferDevice->EnableMapMode(bWasEnabled); + } + else + { + mpBufferDevice->Erase(); + } +} + +void SvxPreviewBase::LocalPostPaint(vcl::RenderContext& rRenderContext) +{ + // copy to front (in pixel mode) + const bool bWasEnabledSrc(mpBufferDevice->IsMapModeEnabled()); + const bool bWasEnabledDst(rRenderContext.IsMapModeEnabled()); + const Point aEmptyPoint; + + mpBufferDevice->EnableMapMode(false); + rRenderContext.EnableMapMode(false); + + rRenderContext.DrawOutDev(aEmptyPoint, GetOutputSizePixel(), + aEmptyPoint, GetOutputSizePixel(), + *mpBufferDevice); + + mpBufferDevice->EnableMapMode(bWasEnabledSrc); + rRenderContext.EnableMapMode(bWasEnabledDst); +} + +void SvxPreviewBase::StyleUpdated() +{ + InitSettings(); + CustomWidgetController::StyleUpdated(); +} + +SvxXRectPreview::SvxXRectPreview() +{ +} + +tools::Rectangle SvxPreviewBase::GetPreviewSize() const +{ + tools::Rectangle aObjectSize(Point(), getBufferDevice().PixelToLogic(GetOutputSizePixel())); + return aObjectSize; +} + +void SvxXRectPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + SvxPreviewBase::SetDrawingArea(pDrawingArea); + InitSettings(); + + // create RectangleObject + mpRectangleObject = new SdrRectObj(getModel(), GetPreviewSize()); +} + +void SvxXRectPreview::Resize() +{ + rtl::Reference<SdrObject> pOrigObject = mpRectangleObject; + if (pOrigObject) + { + mpRectangleObject = new SdrRectObj(getModel(), GetPreviewSize()); + SetAttributes(pOrigObject->GetMergedItemSet()); + pOrigObject.clear(); + } + SvxPreviewBase::Resize(); +} + +SvxXRectPreview::~SvxXRectPreview() +{ +} + +void SvxXRectPreview::SetAttributes(const SfxItemSet& rItemSet) +{ + mpRectangleObject->SetMergedItemSet(rItemSet, true); + mpRectangleObject->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE)); +} + +void SvxXRectPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.Push(vcl::PushFlags::MAPMODE); + rRenderContext.SetMapMode(MapMode(MapUnit::Map100thMM)); + LocalPrePaint(rRenderContext); + + sdr::contact::SdrObjectVector aObjectVector; + + aObjectVector.push_back(mpRectangleObject.get()); + + sdr::contact::ObjectContactOfObjListPainter aPainter(getBufferDevice(), std::move(aObjectVector), nullptr); + sdr::contact::DisplayInfo aDisplayInfo; + + aPainter.ProcessDisplay(aDisplayInfo); + + LocalPostPaint(rRenderContext); + rRenderContext.Pop(); +} + +void limitWidthForSidebar(weld::SpinButton& rSpinButton) +{ + // space is limited in the sidebar, so limit MetricSpinButtons to a width of 4 digits + const int nMaxDigits = 4; + rSpinButton.set_width_chars(std::min(rSpinButton.get_width_chars(), nMaxDigits)); +} + +void limitWidthForSidebar(SvxRelativeField& rMetricSpinButton) +{ + weld::SpinButton& rSpinButton = rMetricSpinButton.get_widget(); + limitWidthForSidebar(rSpinButton); +} + +void padWidthForSidebar(weld::Toolbar& rToolbar, const css::uno::Reference<css::frame::XFrame>& rFrame) +{ + static int nColumnWidth = -1; + static vcl::ImageType eSize; + if (nColumnWidth != -1 && eSize != rToolbar.get_icon_size()) + nColumnWidth = -1; + if (nColumnWidth == -1) + { + // use the, filled-in by dispatcher, width of measurewidth as the width + // of a "standard" column in a two column panel + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&rToolbar, "svx/ui/measurewidthbar.ui")); + std::unique_ptr<weld::Toolbar> xToolbar1(xBuilder->weld_toolbar("measurewidth1")); + ToolbarUnoDispatcher aDispatcher1(*xToolbar1, *xBuilder, rFrame); + std::unique_ptr<weld::Toolbar> xToolbar2(xBuilder->weld_toolbar("measurewidth2")); + ToolbarUnoDispatcher aDispatcher2(*xToolbar2, *xBuilder, rFrame); + nColumnWidth = std::max(xToolbar1->get_preferred_size().Width(), xToolbar2->get_preferred_size().Width()); + eSize = rToolbar.get_icon_size(); + } + rToolbar.set_size_request(nColumnWidth, -1); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/dlgunit.hxx b/svx/source/dialog/dlgunit.hxx new file mode 100644 index 0000000000..a1e79b1182 --- /dev/null +++ b/svx/source/dialog/dlgunit.hxx @@ -0,0 +1,55 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SVX_SOURCE_DIALOG_DLGUNIT_HXX +#define INCLUDED_SVX_SOURCE_DIALOG_DLGUNIT_HXX + +#include <rtl/ustrbuf.hxx> +#include <svx/svdtrans.hxx> +#include <vcl/fieldvalues.hxx> + +inline OUString GetUnitString(tools::Long nVal_100, FieldUnit eFieldUnit, sal_Unicode cSep) +{ + OUStringBuffer aVal + = OUString::number(vcl::ConvertValue(nVal_100, 2, MapUnit::Map100thMM, eFieldUnit)); + + while (aVal.getLength() < 3) + aVal.insert(0, "0"); + + aVal.insert(aVal.getLength() - 2, cSep); + OUString aSuffix = SdrFormatter::GetUnitStr(eFieldUnit); + if (eFieldUnit != FieldUnit::NONE && eFieldUnit != FieldUnit::DEGREE + && eFieldUnit != FieldUnit::INCH) + aVal.append(" "); + if (eFieldUnit == FieldUnit::INCH) + { + OUString sDoublePrime = u"\u2033"_ustr; + if (aSuffix != "\"" && aSuffix != sDoublePrime) + aVal.append(" "); + else + aSuffix = sDoublePrime; + } + aVal.append(aSuffix); + + return aVal.makeStringAndClear(); +} + +#endif // INCLUDED_SVX_SOURCE_DIALOG_DLGUNIT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/dlgutil.cxx b/svx/source/dialog/dlgutil.cxx new file mode 100644 index 0000000000..a33e1ca947 --- /dev/null +++ b/svx/source/dialog/dlgutil.cxx @@ -0,0 +1,74 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dlgutil.hxx> +#include <svl/itemset.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/module.hxx> +#include <svl/intitem.hxx> +#include <svl/eitem.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/objsh.hxx> +#include <sal/log.hxx> + + +FieldUnit GetModuleFieldUnit( const SfxItemSet& rSet ) +{ + if (const SfxUInt16Item* pItem = rSet.GetItemIfSet(SID_ATTR_METRIC, false)) + return static_cast<FieldUnit>(pItem->GetValue()); + + return SfxModule::GetCurrentFieldUnit(); +} + +bool GetApplyCharUnit( const SfxItemSet& rSet ) +{ + bool bUseCharUnit = false; + if ( const SfxBoolItem* pItem = rSet.GetItemIfSet( SID_ATTR_APPLYCHARUNIT, false ) ) + bUseCharUnit = pItem->GetValue(); + else + { + // FIXME - this might be wrong, cf. the DEV300 changes in GetModuleFieldUnit() + SfxViewFrame* pFrame = SfxViewFrame::Current(); + SfxObjectShell* pSh = nullptr; + if ( pFrame ) + pSh = pFrame->GetObjectShell(); + if ( pSh ) // the object shell is not always available during reload + { + SfxModule* pModule = pSh->GetModule(); + if ( pModule ) + { + pItem = pModule->GetItem( SID_ATTR_APPLYCHARUNIT ); + if ( pItem ) + bUseCharUnit = pItem->GetValue(); + } + else + { + SAL_WARN( "svx.dialog", "GetApplyCharUnit(): no module found" ); + } + } + } + return bUseCharUnit; +} + +FieldUnit GetModuleFieldUnit() +{ + return SfxModule::GetCurrentFieldUnit(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/docrecovery.cxx b/svx/source/dialog/docrecovery.cxx new file mode 100644 index 0000000000..1e40115270 --- /dev/null +++ b/svx/source/dialog/docrecovery.cxx @@ -0,0 +1,1239 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <bitmaps.hlst> +#include <docrecovery.hxx> + +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/string.hxx> +#include <o3tl/safeint.hxx> +#include <svtools/imagemgr.hxx> +#include <sfx2/filedlghelper.hxx> +#include <tools/urlobj.hxx> +#include <utility> +#include <vcl/weld.hxx> +#include <vcl/svapp.hxx> + +#include <com/sun/star/util/URL.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/frame/theAutoRecovery.hpp> +#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <osl/file.hxx> +#include <unotools/pathoptions.hxx> + +namespace svx::DocRecovery +{ + +using namespace ::osl; + +#define COLUMN_STANDARDIMAGE -1 +#define COLUMN_DISPLAYNAME 0 +#define COLUMN_STATUSIMAGE 1 +#define COLUMN_STATUSTEXT 2 + +RecoveryCore::RecoveryCore(css::uno::Reference< css::uno::XComponentContext > xContext, + bool bUsedForSaving) + : m_xContext (std::move( xContext )) + , m_pListener ( nullptr ) + , m_bListenForSaving(bUsedForSaving) +{ + impl_startListening(); +} + + +RecoveryCore::~RecoveryCore() +{ + impl_stopListening(); +} + + +const css::uno::Reference< css::uno::XComponentContext >& RecoveryCore::getComponentContext() const +{ + return m_xContext; +} + + +TURLList& RecoveryCore::getURLListAccess() +{ + return m_lURLs; +} + + +bool RecoveryCore::isBrokenTempEntry(const TURLInfo& rInfo) +{ + if (rInfo.TempURL.isEmpty()) + return false; + + // Note: If the original files was recovery ... but a temp file + // exists ... an error inside the temp file exists! + if ( + (rInfo.RecoveryState != E_RECOVERY_FAILED ) && + (rInfo.RecoveryState != E_ORIGINAL_DOCUMENT_RECOVERED) + ) + return false; + + return true; +} + + +void RecoveryCore::saveBrokenTempEntries(const OUString& rPath) +{ + if (rPath.isEmpty()) + return; + + if (!m_xRealCore.is()) + return; + + // prepare all needed parameters for the following dispatch() request. + css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP); + css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3); + auto plCopyArgs = lCopyArgs.getArray(); + plCopyArgs[0].Name = PROP_DISPATCHASYNCHRON; + plCopyArgs[0].Value <<= false; + plCopyArgs[1].Name = PROP_SAVEPATH; + plCopyArgs[1].Value <<= rPath; + plCopyArgs[2].Name = PROP_ENTRYID; + // lCopyArgs[2].Value will be changed during next loop... + + // work on a copied list only... + // Reason: We will get notifications from the core for every + // changed or removed element. And that will change our m_lURLs list. + // That's not a good idea, if we use a stl iterator inbetween .-) + TURLList lURLs = m_lURLs; + for (const TURLInfo& rInfo : lURLs) + { + if (!RecoveryCore::isBrokenTempEntry(rInfo)) + continue; + + plCopyArgs[2].Value <<= rInfo.ID; + m_xRealCore->dispatch(aCopyURL, lCopyArgs); + } +} + + +void RecoveryCore::saveAllTempEntries(const OUString& rPath) +{ + if (rPath.isEmpty()) + return; + + if (!m_xRealCore.is()) + return; + + // prepare all needed parameters for the following dispatch() request. + css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP); + css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3); + auto plCopyArgs = lCopyArgs.getArray(); + plCopyArgs[0].Name = PROP_DISPATCHASYNCHRON; + plCopyArgs[0].Value <<= false; + plCopyArgs[1].Name = PROP_SAVEPATH; + plCopyArgs[1].Value <<= rPath; + plCopyArgs[2].Name = PROP_ENTRYID; + // lCopyArgs[2].Value will be changed during next loop ... + + // work on a copied list only ... + // Reason: We will get notifications from the core for every + // changed or removed element. And that will change our m_lURLs list. + // That's not a good idea, if we use a stl iterator inbetween .-) + TURLList lURLs = m_lURLs; + for (const TURLInfo& rInfo : lURLs) + { + if (rInfo.TempURL.isEmpty()) + continue; + + plCopyArgs[2].Value <<= rInfo.ID; + m_xRealCore->dispatch(aCopyURL, lCopyArgs); + } +} + + +void RecoveryCore::forgetBrokenTempEntries() +{ + if (!m_xRealCore.is()) + return; + + css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP); + css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2); + auto plRemoveArgs = lRemoveArgs.getArray(); + plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON; + plRemoveArgs[0].Value <<= false; + plRemoveArgs[1].Name = PROP_ENTRYID; + // lRemoveArgs[1].Value will be changed during next loop ... + + // work on a copied list only ... + // Reason: We will get notifications from the core for every + // changed or removed element. And that will change our m_lURLs list. + // That's not a good idea, if we use a stl iterator inbetween .-) + TURLList lURLs = m_lURLs; + for (const TURLInfo& rInfo : lURLs) + { + if (!RecoveryCore::isBrokenTempEntry(rInfo)) + continue; + + plRemoveArgs[1].Value <<= rInfo.ID; + m_xRealCore->dispatch(aRemoveURL, lRemoveArgs); + } +} + +// should only be called with valid m_xRealCore +void RecoveryCore::forgetAllRecoveryEntriesMarkedForDiscard() +{ + assert(m_xRealCore); + + // potential to move in a separate function + css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP); + css::uno::Sequence<css::beans::PropertyValue> lRemoveArgs(2); + auto plRemoveArgs = lRemoveArgs.getArray(); + plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON; + plRemoveArgs[0].Value <<= false; + plRemoveArgs[1].Name = PROP_ENTRYID; + + // work on a copied list only ... + // Reason: We will get notifications from the core for every + // changed or removed element. And that will change our m_lURLs list. + // That's not a good idea, if we use a stl iterator inbetween .-) + TURLList lURLs = m_lURLs; + for (const TURLInfo& rInfo : lURLs) + { + if (!rInfo.ShouldDiscard) + continue; + + plRemoveArgs[1].Value <<= rInfo.ID; + m_xRealCore->dispatch(aRemoveURL, lRemoveArgs); + } +} + +void RecoveryCore::forgetAllRecoveryEntries() +{ + if (!m_xRealCore.is()) + return; + + css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP); + css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2); + auto plRemoveArgs = lRemoveArgs.getArray(); + plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON; + plRemoveArgs[0].Value <<= false; + plRemoveArgs[1].Name = PROP_ENTRYID; + // lRemoveArgs[1].Value will be changed during next loop ... + + // work on a copied list only ... + // Reason: We will get notifications from the core for every + // changed or removed element. And that will change our m_lURLs list. + // That's not a good idea, if we use a stl iterator inbetween .-) + TURLList lURLs = m_lURLs; + for (const TURLInfo& rInfo : lURLs) + { + plRemoveArgs[1].Value <<= rInfo.ID; + m_xRealCore->dispatch(aRemoveURL, lRemoveArgs); + } +} + + +void RecoveryCore::forgetBrokenRecoveryEntries() +{ + if (!m_xRealCore.is()) + return; + + css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP); + css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2); + auto plRemoveArgs = lRemoveArgs.getArray(); + plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON; + plRemoveArgs[0].Value <<= false; + plRemoveArgs[1].Name = PROP_ENTRYID; + // lRemoveArgs[1].Value will be changed during next loop ... + + // work on a copied list only ... + // Reason: We will get notifications from the core for every + // changed or removed element. And that will change our m_lURLs list. + // That's not a good idea, if we use a stl iterator inbetween .-) + TURLList lURLs = m_lURLs; + for (const TURLInfo& rInfo : lURLs) + { + if (!RecoveryCore::isBrokenTempEntry(rInfo)) + continue; + + plRemoveArgs[1].Value <<= rInfo.ID; + m_xRealCore->dispatch(aRemoveURL, lRemoveArgs); + } +} + + +void RecoveryCore::setProgressHandler(const css::uno::Reference< css::task::XStatusIndicator >& xProgress) +{ + m_xProgress = xProgress; +} + + +void RecoveryCore::setUpdateListener(IRecoveryUpdateListener* pListener) +{ + m_pListener = pListener; +} + + +void RecoveryCore::doEmergencySavePrepare() +{ + if (!m_xRealCore.is()) + return; + + css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_PREPARE_EMERGENCY_SAVE); + + css::uno::Sequence< css::beans::PropertyValue > lArgs{ comphelper::makePropertyValue( + PROP_DISPATCHASYNCHRON, false) }; + + m_xRealCore->dispatch(aURL, lArgs); +} + + +void RecoveryCore::doEmergencySave() +{ + if (!m_xRealCore.is()) + return; + + css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_EMERGENCY_SAVE); + + css::uno::Sequence< css::beans::PropertyValue > lArgs{ + comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress), + comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true) + }; + + m_xRealCore->dispatch(aURL, lArgs); +} + + +void RecoveryCore::doRecovery() +{ + if (!m_xRealCore.is()) + return; + + forgetAllRecoveryEntriesMarkedForDiscard(); + + css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_RECOVERY); + + css::uno::Sequence< css::beans::PropertyValue > lArgs{ + comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress), + comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true) + }; + + m_xRealCore->dispatch(aURL, lArgs); +} + + +ERecoveryState RecoveryCore::mapDocState2RecoverState(EDocStates eDocState) +{ + // ??? + ERecoveryState eRecState = E_NOT_RECOVERED_YET; + + /* Attention: + Some of the following states can occur at the + same time. So we have to check for the "worst case" first! + + DAMAGED -> INCOMPLETE -> HANDLED + */ + + // running ... + if ( + (eDocState & EDocStates::TryLoadBackup ) || + (eDocState & EDocStates::TryLoadOriginal) + ) + eRecState = E_RECOVERY_IS_IN_PROGRESS; + // red + else if (eDocState & EDocStates::Damaged) + eRecState = E_RECOVERY_FAILED; + // yellow + else if (eDocState & EDocStates::Incomplete) + eRecState = E_ORIGINAL_DOCUMENT_RECOVERED; + // green + else if (eDocState & EDocStates::Succeeded) + eRecState = E_SUCCESSFULLY_RECOVERED; + + return eRecState; +} + + +void SAL_CALL RecoveryCore::statusChanged(const css::frame::FeatureStateEvent& aEvent) +{ + // a) special notification about start/stop async dispatch! + // FeatureDescriptor = "start" || "stop" + if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_START) + { + return; + } + + if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_STOP) + { + if (m_pListener) + m_pListener->end(); + return; + } + + // b) normal notification about changed items + // FeatureDescriptor = "Update" + // State = List of information [seq< NamedValue >] + if (aEvent.FeatureDescriptor != RECOVERY_OPERATIONSTATE_UPDATE) + return; + + ::comphelper::SequenceAsHashMap lInfo(aEvent.State); + TURLInfo aNew; + + aNew.ID = lInfo.getUnpackedValueOrDefault(STATEPROP_ID , sal_Int32(0) ); + aNew.DocState = static_cast<EDocStates>(lInfo.getUnpackedValueOrDefault(STATEPROP_STATE , sal_Int32(0) )); + aNew.OrgURL = lInfo.getUnpackedValueOrDefault(STATEPROP_ORGURL , OUString()); + aNew.TempURL = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPURL , OUString()); + aNew.FactoryURL = lInfo.getUnpackedValueOrDefault(STATEPROP_FACTORYURL , OUString()); + aNew.TemplateURL = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPLATEURL, OUString()); + aNew.DisplayName = lInfo.getUnpackedValueOrDefault(STATEPROP_TITLE , OUString()); + aNew.Module = lInfo.getUnpackedValueOrDefault(STATEPROP_MODULE , OUString()); + + if (aNew.OrgURL.isEmpty()) { + // If there is no file URL, the window title is used for the display name. + // Remove any unwanted elements such as " - LibreOffice Writer". + sal_Int32 i = aNew.DisplayName.indexOf(" - "); + if (i > 0) + aNew.DisplayName = aNew.DisplayName.copy(0, i); + } else { + // If there is a file URL, parse out the filename part as the display name. + INetURLObject aOrgURL(aNew.OrgURL); + aNew.DisplayName = aOrgURL.getName(INetURLObject::LAST_SEGMENT, true, + INetURLObject::DecodeMechanism::WithCharset); + } + + // search for already existing items and update her nState value ... + for (TURLInfo& aOld : m_lURLs) + { + if (aOld.ID == aNew.ID) + { + // change existing + aOld.DocState = aNew.DocState; + aOld.RecoveryState = RecoveryCore::mapDocState2RecoverState(aOld.DocState); + if (m_pListener) + { + m_pListener->updateItems(); + m_pListener->stepNext(&aOld); + } + return; + } + } + + // append as new one + // TODO think about matching Module name to a corresponding icon + OUString sURL = aNew.OrgURL; + if (sURL.isEmpty()) + sURL = aNew.FactoryURL; + if (sURL.isEmpty()) + sURL = aNew.TempURL; + if (sURL.isEmpty()) + sURL = aNew.TemplateURL; + INetURLObject aURL(sURL); + aNew.StandardImageId = SvFileInformationManager::GetFileImageId(aURL); + + /* set the right UI state for this item to NOT_RECOVERED_YET... because nDocState shows the state of + the last emergency save operation before and is interesting for the used recovery core service only... + for now! But if there is a further notification for this item (see lines above!) we must + map the doc state to an UI state. */ + aNew.RecoveryState = E_NOT_RECOVERED_YET; + + m_lURLs.push_back(aNew); + + if (m_pListener) + m_pListener->updateItems(); +} + + +void SAL_CALL RecoveryCore::disposing(const css::lang::EventObject& /*aEvent*/) +{ + m_xRealCore.clear(); +} + + +void RecoveryCore::impl_startListening() +{ + // listening already initialized ? + if (m_xRealCore.is()) + return; + m_xRealCore = css::frame::theAutoRecovery::get(m_xContext); + + css::util::URL aURL; + if (m_bListenForSaving) + aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE; + else + aURL.Complete = RECOVERY_CMD_DO_RECOVERY; + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext)); + xParser->parseStrict(aURL); + + /* Note: addStatusListener() call us synchronous back ... so we + will get the complete list of currently open documents! */ + m_xRealCore->addStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL); +} + + +void RecoveryCore::impl_stopListening() +{ + // Ignore it, if this instance doesn't listen currently + if (!m_xRealCore.is()) + return; + + css::util::URL aURL; + if (m_bListenForSaving) + aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE; + else + aURL.Complete = RECOVERY_CMD_DO_RECOVERY; + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext)); + xParser->parseStrict(aURL); + + m_xRealCore->removeStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL); + m_xRealCore.clear(); +} + + +css::util::URL RecoveryCore::impl_getParsedURL(const OUString& sURL) +{ + css::util::URL aURL; + aURL.Complete = sURL; + + css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext)); + xParser->parseStrict(aURL); + + return aURL; +} + +PluginProgress::PluginProgress(weld::ProgressBar* pProgressBar) + : m_pProgressBar(pProgressBar) + , m_nRange(100) +{ +} + +PluginProgress::~PluginProgress() +{ +} + +void SAL_CALL PluginProgress::dispose() +{ + m_pProgressBar = nullptr; +} + +void SAL_CALL PluginProgress::addEventListener(const css::uno::Reference< css::lang::XEventListener >& ) +{ +} + +void SAL_CALL PluginProgress::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& ) +{ +} + +void SAL_CALL PluginProgress::start(const OUString&, sal_Int32 nRange) +{ + m_nRange = nRange; + if (m_pProgressBar) + m_pProgressBar->set_percentage(0); +} + +void SAL_CALL PluginProgress::end() +{ + if (m_pProgressBar) + m_pProgressBar->set_percentage(m_nRange); +} + +void SAL_CALL PluginProgress::setText(const OUString& rText) +{ + if (m_pProgressBar) + m_pProgressBar->set_text(rText); +} + +void SAL_CALL PluginProgress::setValue(sal_Int32 nValue) +{ + if (m_pProgressBar) + m_pProgressBar->set_percentage((nValue * 100) / m_nRange); +} + +void SAL_CALL PluginProgress::reset() +{ + if (m_pProgressBar) + m_pProgressBar->set_percentage(0); +} + +SaveDialog::SaveDialog(weld::Window* pParent, RecoveryCore* pCore) + : GenericDialogController(pParent, "svx/ui/docrecoverysavedialog.ui", "DocRecoverySaveDialog") + , m_pCore(pCore) + , m_xFileListLB(m_xBuilder->weld_tree_view("filelist")) + , m_xOkBtn(m_xBuilder->weld_button("ok")) +{ + m_xFileListLB->set_size_request(-1, m_xFileListLB->get_height_rows(10)); + + // Prepare the office for the following crash save step. + // E.g. hide all open windows so the user can't influence our + // operation .-) + m_pCore->doEmergencySavePrepare(); + + m_xOkBtn->connect_clicked(LINK(this, SaveDialog, OKButtonHdl)); + + // fill listbox with current open documents + + TURLList& rURLs = m_pCore->getURLListAccess(); + + for (const TURLInfo& rInfo : rURLs) + { + m_xFileListLB->append("", rInfo.DisplayName, rInfo.StandardImageId); + } +} + +SaveDialog::~SaveDialog() +{ +} + +IMPL_LINK_NOARG(SaveDialog, OKButtonHdl, weld::Button&, void) +{ + // start crash-save with progress + std::unique_ptr<SaveProgressDialog> xProgress(new SaveProgressDialog(m_xDialog.get(), m_pCore)); + short nResult = xProgress->run(); + xProgress.reset(); + + // if "CANCEL" => return "CANCEL" + // if "OK" => request a restart always! + if (nResult == DLG_RET_OK) + nResult = DLG_RET_OK_AUTOLAUNCH; + + m_xDialog->response(nResult); +} + +SaveProgressDialog::SaveProgressDialog(weld::Window* pParent, RecoveryCore* pCore) + : GenericDialogController(pParent, "svx/ui/docrecoveryprogressdialog.ui", "DocRecoveryProgressDialog") + , m_pCore(pCore) + , m_xProgressBar(m_xBuilder->weld_progress_bar("progress")) +{ + m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1); + m_xProgress = new PluginProgress(m_xProgressBar.get()); +} + +SaveProgressDialog::~SaveProgressDialog() +{ + css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY); + if (xComp) + xComp->dispose(); +} + +short SaveProgressDialog::run() +{ + ::SolarMutexGuard aLock; + + m_pCore->setProgressHandler(m_xProgress); + m_pCore->setUpdateListener(this); + m_pCore->doEmergencySave(); + short nRet = DialogController::run(); + m_pCore->setUpdateListener(nullptr); + return nRet; +} + +void SaveProgressDialog::updateItems() +{ +} + +void SaveProgressDialog::stepNext(TURLInfo* ) +{ + /* TODO + + if m_pCore would have a member m_mCurrentItem, you could see, + who is current, who is next ... You can show this information + in progress report FixText + */ +} + +void SaveProgressDialog::end() +{ + m_xDialog->response(DLG_RET_OK); +} + +static short impl_askUserForWizardCancel(weld::Widget* pParent, TranslateId pRes) +{ + std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(pParent, + VclMessageType::Question, VclButtonsType::YesNo, SvxResId(pRes))); + if (xQuery->run() == RET_YES) + return DLG_RET_OK; + else + return DLG_RET_CANCEL; +} + +RecoveryDialog::RecoveryDialog(weld::Window* pParent, RecoveryCore* pCore) + : GenericDialogController(pParent, "svx/ui/docrecoveryrecoverdialog.ui", "DocRecoveryRecoverDialog") + , m_aTitleRecoveryInProgress(SvxResId(RID_SVXSTR_RECOVERY_INPROGRESS)) + , m_aRecoveryOnlyFinish (SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH)) + , m_aRecoveryOnlyFinishDescr(SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH_DESCR)) + , m_pCore(pCore) + , m_eRecoveryState(RecoveryDialog::E_RECOVERY_PREPARED) + , m_bWaitForCore(false) + , m_bWasRecoveryStarted(false) +// , m_aColumnOffset(0) + , m_aToggleCount(0) + , m_aSuccessRecovStr(SvxResId(RID_SVXSTR_SUCCESSRECOV)) + , m_aOrigDocRecovStr(SvxResId(RID_SVXSTR_ORIGDOCRECOV)) + , m_aRecovFailedStr(SvxResId(RID_SVXSTR_RECOVFAILED)) + , m_aRecovInProgrStr(SvxResId(RID_SVXSTR_RECOVINPROGR)) + , m_aNotRecovYetStr(SvxResId(RID_SVXSTR_NOTRECOVYET)) + , m_aWillBeDiscStr(SvxResId(RID_SVXSTR_WILLDISCARD)) + , m_xDescrFT(m_xBuilder->weld_label("desc")) + , m_xProgressBar(m_xBuilder->weld_progress_bar("progress")) + , m_xFileListLB(m_xBuilder->weld_tree_view("filelist")) + , m_xNextBtn(m_xBuilder->weld_button("next")) + , m_xCancelBtn(m_xBuilder->weld_button("cancel")) +{ + const auto nWidth = m_xFileListLB->get_approximate_digit_width() * 80; + m_xFileListLB->set_size_request(nWidth, m_xFileListLB->get_height_rows(10)); + m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1); + m_xProgress = new PluginProgress(m_xProgressBar.get()); + + std::vector<int> aWidths; + aWidths.push_back(60 * nWidth / 100); + aWidths.push_back(5 * nWidth / 100); + m_xFileListLB->set_column_fixed_widths(aWidths); + m_xFileListLB->enable_toggle_buttons(weld::ColumnToggleType::Check); + m_xFileListLB->connect_toggled( LINK(this, RecoveryDialog, ToggleRowHdl) ); + + m_xNextBtn->set_sensitive(true); + m_xNextBtn->connect_clicked( LINK( this, RecoveryDialog, NextButtonHdl ) ); + m_xCancelBtn->connect_clicked( LINK( this, RecoveryDialog, CancelButtonHdl ) ); + + // fill list box first time + TURLList& rURLList = m_pCore->getURLListAccess(); + for (size_t i = 0, nCount = rURLList.size(); i < nCount; ++i) + { + const TURLInfo& rInfo = rURLList[i]; + m_xFileListLB->append(); + m_xFileListLB->set_toggle(i, TRISTATE_TRUE); + m_xFileListLB->set_id(i, weld::toId(&rInfo)); + m_xFileListLB->set_image(i, rInfo.StandardImageId, COLUMN_STANDARDIMAGE); + m_xFileListLB->set_text(i, rInfo.DisplayName, COLUMN_DISPLAYNAME); + m_xFileListLB->set_image(i, impl_getStatusImage(rInfo), COLUMN_STATUSIMAGE); + m_xFileListLB->set_text(i, impl_getStatusString(rInfo), COLUMN_STATUSTEXT); + m_aToggleCount++; + } + + // mark first item + if (m_xFileListLB->n_children()) + m_xFileListLB->set_cursor(0); +} + +RecoveryDialog::~RecoveryDialog() +{ + css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY); + if (xComp) + xComp->dispose(); +} + +bool RecoveryDialog::allSuccessfullyRecovered() +{ + const int c = m_xFileListLB->n_children(); + for (int i = 0; i < c; ++i) + { + TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i)); + if (!pInfo) + continue; + + if (pInfo->RecoveryState != E_SUCCESSFULLY_RECOVERED) + return false; + } + return true; +} + +short RecoveryDialog::execute() +{ + ::SolarMutexGuard aSolarLock; + + switch (m_eRecoveryState) + { + case RecoveryDialog::E_RECOVERY_IN_PROGRESS : + { + // user decided to start recovery ... + m_bWasRecoveryStarted = true; + // do it asynchronous (to allow repaints) + // and wait for this asynchronous operation. + m_xDescrFT->set_label( m_aTitleRecoveryInProgress ); + m_xNextBtn->set_sensitive(false); + m_xCancelBtn->set_sensitive(false); + m_pCore->setProgressHandler(m_xProgress); + m_pCore->setUpdateListener(this); + m_pCore->doRecovery(); + + m_bWaitForCore = true; + while(m_bWaitForCore && !Application::IsQuit()) + Application::Yield(); + + m_pCore->setUpdateListener(nullptr); + + // Skip FINISH button if everything was successfully recovered + if (allSuccessfullyRecovered()) + m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE; + else + m_eRecoveryState = RecoveryDialog::E_RECOVERY_CORE_DONE; + return execute(); + } + + case RecoveryDialog::E_RECOVERY_CORE_DONE : + { + // the core finished it's task. + // let the user decide the next step. + m_xDescrFT->set_label(m_aRecoveryOnlyFinishDescr); + m_xNextBtn->set_label(m_aRecoveryOnlyFinish); + m_xNextBtn->set_sensitive(true); + m_xCancelBtn->set_sensitive(false); + return 0; + } + + case RecoveryDialog::E_RECOVERY_DONE : + { + // All documents were recovered. + // User decided to step to the "next" wizard page. + // Do it ... but check first, if there exist some + // failed recovery documents. They must be saved to + // a user selected directory. + short nRet = DLG_RET_UNKNOWN; + BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted); + OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default dir + if (aBrokenRecoveryDialog.isExecutionNeeded()) + { + nRet = aBrokenRecoveryDialog.run(); + sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); + } + + switch(nRet) + { + // no broken temp files exists + // step to the next wizard page + case DLG_RET_UNKNOWN : + { + m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED; + return DLG_RET_OK; + } + + // user decided to save the broken temp files + // do and forget it + // step to the next wizard page + case DLG_RET_OK : + { + m_pCore->saveBrokenTempEntries(sSaveDir); + m_pCore->forgetBrokenTempEntries(); + m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED; + return DLG_RET_OK; + } + + // user decided to ignore broken temp files. + // Ask it again ... may be this decision was wrong. + // Results: + // IGNORE => remove broken temp files + // => step to the next wizard page + // CANCEL => step back to the recovery page + case DLG_RET_CANCEL : + { + // TODO ask user ... + m_pCore->forgetBrokenTempEntries(); + m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED; + return DLG_RET_OK; + } + } + + m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED; + return DLG_RET_OK; + } + + case RecoveryDialog::E_RECOVERY_CANCELED : + { + // "YES" => break recovery + // But there exist different states, where "cancel" can be called. + // Handle it different. + if (m_bWasRecoveryStarted) + m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS; + else + m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_BEFORE; + return execute(); + } + + case RecoveryDialog::E_RECOVERY_CANCELED_BEFORE : + case RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS : + { + // We have to check if there exists some temp. files. + // They should be saved to a user defined location. + // If no temp files exists or user decided to ignore it ... + // we have to remove all recovery/session data anyway! + short nRet = DLG_RET_UNKNOWN; + BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted); + OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default save location + + // dialog itself checks if there is a need to copy files for this mode. + // It uses the information m_bWasRecoveryStarted doing so. + if (aBrokenRecoveryDialog.isExecutionNeeded()) + { + nRet = aBrokenRecoveryDialog.run(); + sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); + } + + // Possible states: + // a) nRet == DLG_RET_UNKNOWN + // dialog was not shown ... + // because there exists no temp file for copy. + // => remove all recovery data + // b) nRet == DLG_RET_OK + // dialog was shown ... + // user decided to save temp files + // => save all OR broken temp files (depends from the time, where cancel was called) + // => remove all recovery data + // c) nRet == DLG_RET_CANCEL + // dialog was shown ... + // user decided to ignore temp files + // => remove all recovery data + // => a)/c) are the same ... b) has one additional operation + + // b) + if (nRet == DLG_RET_OK) + { + if (m_bWasRecoveryStarted) + m_pCore->saveBrokenTempEntries(sSaveDir); + else + m_pCore->saveAllTempEntries(sSaveDir); + } + + // a,b,c) + if (m_bWasRecoveryStarted) + m_pCore->forgetBrokenRecoveryEntries(); + else + m_pCore->forgetAllRecoveryEntries(); + m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED; + + // THERE IS NO WAY BACK. see impl_askUserForWizardCancel()! + return DLG_RET_CANCEL; + } + } + + // should never be reached .-) + OSL_FAIL("Should never be reached!"); + return DLG_RET_OK; +} + +void RecoveryDialog::updateItems() +{ + int c = m_xFileListLB->n_children(); + for (int i = 0; i < c; ++i) + { + TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i)); + if ( !pInfo ) + continue; + + m_xFileListLB->set_image(i, impl_getStatusImage(*pInfo), COLUMN_STATUSIMAGE); + OUString sStatus = impl_getStatusString( *pInfo ); + if (!sStatus.isEmpty()) + m_xFileListLB->set_text(i, sStatus, COLUMN_STATUSTEXT); + } +} + +void RecoveryDialog::stepNext(TURLInfo* pItem) +{ + int c = m_xFileListLB->n_children(); + for (int i=0; i < c; ++i) + { + TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i)); + if (pInfo->ID != pItem->ID) + continue; + + m_xFileListLB->set_cursor(i); + m_xFileListLB->scroll_to_row(i); + break; + } +} + +void RecoveryDialog::end() +{ + m_bWaitForCore = false; +} + +IMPL_LINK_NOARG(RecoveryDialog, NextButtonHdl, weld::Button&, void) +{ + switch (m_eRecoveryState) + { + case RecoveryDialog::E_RECOVERY_PREPARED: + m_eRecoveryState = RecoveryDialog::E_RECOVERY_IN_PROGRESS; + execute(); + break; + case RecoveryDialog::E_RECOVERY_CORE_DONE: + m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE; + execute(); + break; + } + + if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED) + { + m_xDialog->response(DLG_RET_OK); + } +} + +IMPL_LINK_NOARG(RecoveryDialog, CancelButtonHdl, weld::Button&, void) +{ + switch (m_eRecoveryState) + { + case RecoveryDialog::E_RECOVERY_PREPARED: + if (impl_askUserForWizardCancel(m_xDialog.get(), RID_SVXSTR_QUERY_EXIT_RECOVERY) != DLG_RET_CANCEL) + { + m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED; + execute(); + } + break; + case RecoveryDialog::E_RECOVERY_CORE_DONE: + m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED; + execute(); + break; + } + + if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED) + { + m_xDialog->response(RET_CANCEL); + } +} + +IMPL_LINK_NOARG(RecoveryDialog, ToggleRowHdl, const weld::TreeView::iter_col&, void) +{ + int aIndex = m_xFileListLB->get_selected_index(); + TriState eState = m_xFileListLB->get_toggle(aIndex); + + if (m_bWasRecoveryStarted) + { + switch (eState) + { + case TRISTATE_FALSE: + eState = TRISTATE_TRUE; + break; + case TRISTATE_TRUE: + eState = TRISTATE_FALSE; + break; + default: + // should never happen + assert(false); + break; + } + + // revert toggle + m_xFileListLB->set_toggle(aIndex, eState); + } + else + { + impl_updateItemDescription(aIndex, eState); + + switch (eState) + { + case TRISTATE_FALSE: + m_aToggleCount--; + break; + case TRISTATE_TRUE: + m_aToggleCount++; + break; + default: + // should never happen + assert(false); + break; + } + + m_xNextBtn->set_sensitive(m_aToggleCount != 0); + } +} + +OUString RecoveryDialog::impl_getStatusString( const TURLInfo& rInfo ) const +{ + OUString sStatus; + switch ( rInfo.RecoveryState ) + { + case E_SUCCESSFULLY_RECOVERED : + sStatus = m_aSuccessRecovStr; + break; + case E_ORIGINAL_DOCUMENT_RECOVERED : + sStatus = m_aOrigDocRecovStr; + break; + case E_RECOVERY_FAILED : + sStatus = m_aRecovFailedStr; + break; + case E_RECOVERY_IS_IN_PROGRESS : + sStatus = m_aRecovInProgrStr; + break; + case E_NOT_RECOVERED_YET : + sStatus = m_aNotRecovYetStr; + break; + case E_WILL_BE_DISCARDED: + sStatus = m_aWillBeDiscStr; + break; + default: + break; + } + return sStatus; +} + +OUString RecoveryDialog::impl_getStatusImage( const TURLInfo& rInfo ) +{ + OUString sStatus; + switch ( rInfo.RecoveryState ) + { + case E_SUCCESSFULLY_RECOVERED : + sStatus = RID_SVXBMP_GREENCHECK; + break; + case E_ORIGINAL_DOCUMENT_RECOVERED : + sStatus = RID_SVXBMP_YELLOWCHECK; + break; + case E_RECOVERY_FAILED : + sStatus = RID_SVXBMP_REDCROSS; + break; + default: + break; + } + return sStatus; +} + +void RecoveryDialog::impl_updateItemDescription(int row, const TriState& rState) +{ + TURLInfo* pInfo = reinterpret_cast<TURLInfo*>(m_xFileListLB->get_id(row).toInt64()); + if (!pInfo) + return; + + switch (rState) + { + case TRISTATE_FALSE: + pInfo->RecoveryState = ERecoveryState::E_WILL_BE_DISCARDED; + pInfo->ShouldDiscard = true; + break; + case TRISTATE_TRUE: + pInfo->RecoveryState = ERecoveryState::E_NOT_RECOVERED_YET; + pInfo->ShouldDiscard = false; + break; + default: + // should never happen + assert(false); + break; + } + + OUString sStatus = impl_getStatusString(*pInfo); + if (!sStatus.isEmpty()) + m_xFileListLB->set_text(row, sStatus, COLUMN_STATUSTEXT); +} + +BrokenRecoveryDialog::BrokenRecoveryDialog(weld::Window* pParent, + RecoveryCore* pCore, + bool bBeforeRecovery) + : GenericDialogController(pParent, "svx/ui/docrecoverybrokendialog.ui", "DocRecoveryBrokenDialog") + , m_pCore(pCore) + , m_bBeforeRecovery(bBeforeRecovery) + , m_bExecutionNeeded(false) + , m_xFileListLB(m_xBuilder->weld_tree_view("filelist")) + , m_xSaveDirED(m_xBuilder->weld_entry("savedir")) + , m_xSaveDirBtn(m_xBuilder->weld_button("change")) + , m_xOkBtn(m_xBuilder->weld_button("ok")) + , m_xCancelBtn(m_xBuilder->weld_button("cancel")) +{ + m_xSaveDirBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, SaveButtonHdl ) ); + m_xOkBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, OkButtonHdl ) ); + m_xCancelBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, CancelButtonHdl ) ); + + m_sSavePath = SvtPathOptions().GetWorkPath(); + INetURLObject aObj( m_sSavePath ); + OUString sPath; + osl::FileBase::getSystemPathFromFileURL(aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), sPath); + m_xSaveDirED->set_text(sPath); + + impl_refresh(); +} + +BrokenRecoveryDialog::~BrokenRecoveryDialog() +{ +} + +void BrokenRecoveryDialog::impl_refresh() +{ + m_bExecutionNeeded = false; + TURLList& rURLList = m_pCore->getURLListAccess(); + for (const TURLInfo& rInfo : rURLList) + { + if (m_bBeforeRecovery) + { + // "Cancel" before recovery -> + // search for any temp files! + if (rInfo.TempURL.isEmpty()) + continue; + } + else + { + // "Cancel" after recovery -> + // search for broken temp files + if (!RecoveryCore::isBrokenTempEntry(rInfo)) + continue; + } + + m_bExecutionNeeded = true; + + m_xFileListLB->append(weld::toId(&rInfo), rInfo.DisplayName, rInfo.StandardImageId); + } + m_sSavePath.clear(); + m_xOkBtn->grab_focus(); +} + +bool BrokenRecoveryDialog::isExecutionNeeded() const +{ + return m_bExecutionNeeded; +} + +const OUString& BrokenRecoveryDialog::getSaveDirURL() const +{ + return m_sSavePath; +} + +IMPL_LINK_NOARG(BrokenRecoveryDialog, OkButtonHdl, weld::Button&, void) +{ + OUString sPhysicalPath = comphelper::string::strip(m_xSaveDirED->get_text(), ' '); + OUString sURL; + osl::FileBase::getFileURLFromSystemPath( sPhysicalPath, sURL ); + m_sSavePath = sURL; + while (m_sSavePath.isEmpty()) + impl_askForSavePath(); + + m_xDialog->response(DLG_RET_OK); +} + +IMPL_LINK_NOARG(BrokenRecoveryDialog, CancelButtonHdl, weld::Button&, void) +{ + m_xDialog->response(RET_CANCEL); +} + +IMPL_LINK_NOARG(BrokenRecoveryDialog, SaveButtonHdl, weld::Button&, void) +{ + impl_askForSavePath(); +} + +void BrokenRecoveryDialog::impl_askForSavePath() +{ + css::uno::Reference< css::ui::dialogs::XFolderPicker2 > xFolderPicker = + sfx2::createFolderPicker(m_pCore->getComponentContext(), m_xDialog.get()); + + INetURLObject aURL(m_sSavePath, INetProtocol::File); + xFolderPicker->setDisplayDirectory(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)); + short nRet = xFolderPicker->execute(); + if (nRet == css::ui::dialogs::ExecutableDialogResults::OK) + { + m_sSavePath = xFolderPicker->getDirectory(); + OUString sPath; + osl::FileBase::getSystemPathFromFileURL(m_sSavePath, sPath); + m_xSaveDirED->set_text(sPath); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/fntctrl.cxx b/svx/source/dialog/fntctrl.cxx new file mode 100644 index 0000000000..69cc901719 --- /dev/null +++ b/svx/source/dialog/fntctrl.cxx @@ -0,0 +1,1085 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sfx2/dialoghelper.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/printer.hxx> +#include <vcl/metric.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <com/sun/star/i18n/ScriptType.hpp> + +#include <vector> +#include <deque> +#include <optional> +#include <svtools/colorcfg.hxx> +#include <svtools/sampletext.hxx> + +#include <svx/fntctrl.hxx> +#include <svx/svxids.hrc> + +// Item set includes +#include <svl/itemset.hxx> +#include <svl/itempool.hxx> +#include <svl/stritem.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/ctloptions.hxx> + +#include <editeng/editeng.hxx> +#include <editeng/colritem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/editids.hrc> +#include <editeng/postitem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> +#include <editeng/contouritem.hxx> +#include <editeng/wghtitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/shdditem.hxx> +#include <editeng/escapementitem.hxx> +#include <editeng/wrlmitem.hxx> +#include <editeng/cmapitem.hxx> +#include <editeng/kernitem.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/emphasismarkitem.hxx> +#include <editeng/charreliefitem.hxx> +#include <editeng/charscaleitem.hxx> +#include <editeng/langitem.hxx> + +//TODO: remove this and calculate off the actual size of text, not +//an arbitrary number of characters +#define TEXT_WIDTH 80 + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + + +// small helper functions to set fonts + +namespace +{ +void scaleFontWidth(vcl::Font& rFont, vcl::RenderContext const & rRenderContext,tools::Long& n100PercentFont) +{ + rFont.SetAverageFontWidth(0); + n100PercentFont = rRenderContext.GetFontMetric(rFont).GetAverageFontWidth(); +} + +void initFont(vcl::Font& rFont) +{ + rFont.SetTransparent(true); + rFont.SetAlignment(ALIGN_BASELINE); +} + +void setFontSize(vcl::Font& rFont) +{ + Size aSize(rFont.GetFontSize()); + aSize.setHeight( (aSize.Height() * 3) / 5 ); + aSize.setWidth( (aSize.Width() * 3) / 5 ); + rFont.SetFontSize(aSize); +} + +void calcFontHeightAnyAscent(vcl::RenderContext& rRenderContext, const vcl::Font& rFont, tools::Long& nHeight, tools::Long& nAscent) +{ + if (!nHeight) + { + rRenderContext.SetFont(rFont); + FontMetric aMetric(rRenderContext.GetFontMetric()); + nHeight = aMetric.GetLineHeight(); + nAscent = aMetric.GetAscent(); + } +} + +void setFont(const SvxFont& rNewFont, SvxFont& rImplFont) +{ + rImplFont = rNewFont; + rImplFont.SetTransparent(true); + rImplFont.SetAlignment(ALIGN_BASELINE); +} + +/* + * removes line feeds and carriage returns from string + * returns if param is empty + */ +OUString removeCRLF(const OUString& rText) +{ + return rText.replace(0xa, ' ').replace(0xd, ' ').trim(); +} + +struct ScriptInfo +{ + tools::Long textWidth; + SvtScriptType scriptType; + sal_Int32 changePos; + ScriptInfo(SvtScriptType scrptType, sal_Int32 position) + : textWidth(0) + , scriptType(scrptType) + , changePos(position) + { + } +}; + +} // end anonymous namespace + +class FontPrevWin_Impl +{ + friend class SvxFontPrevWindow; + + SvxFont maFont; + VclPtr<Printer> mpPrinter; + bool mbDelPrinter; + + std::vector<ScriptInfo> maScriptChanges; + SvxFont maCJKFont; + SvxFont maCTLFont; + OUString maText; + OUString maScriptText; + std::optional<Color> mxColor; + std::optional<Color> mxBackColor; + std::optional<Color> mxTextLineColor; + std::optional<Color> mxOverlineColor; + tools::Long mnAscent; + sal_Unicode mcStartBracket; + sal_Unicode mcEndBracket; + + tools::Long mn100PercentFontWidth; // initial -1 -> not set yet + tools::Long mn100PercentFontWidthCJK; + tools::Long mn100PercentFontWidthCTL; + sal_uInt16 mnFontWidthScale; + + bool mbSelection : 1; + bool mbGetSelection : 1; + bool mbTwoLines : 1; + bool mbUseFontNameAsText : 1; + bool mbTextInited : 1; + + bool m_bCJKEnabled; + bool m_bCTLEnabled; + + +public: + FontPrevWin_Impl() : + mpPrinter(nullptr), + mbDelPrinter(false), + mnAscent(0), + mcStartBracket(0), + mcEndBracket(0), + mnFontWidthScale(100), + mbSelection(false), + mbGetSelection(false), + mbTwoLines(false), + mbUseFontNameAsText(false), + mbTextInited(false) + { + m_bCJKEnabled = SvtCJKOptions::IsAnyEnabled(); + m_bCTLEnabled = SvtCTLOptions::IsCTLFontEnabled(); + mxBackColor = svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; + Invalidate100PercentFontWidth(); + } + + ~FontPrevWin_Impl() + { + if (mbDelPrinter) + mpPrinter.disposeAndClear(); + } + + void CheckScript(); + Size CalcTextSize(vcl::RenderContext& rRenderContext, OutputDevice const * pPrinter, const SvxFont& rFont); + void DrawPrev(vcl::RenderContext& rRenderContext, Printer* pPrinter, Point& rPt, const SvxFont& rFont); + + bool SetFontWidthScale(sal_uInt16 nScaleInPercent); + inline void Invalidate100PercentFontWidth(); + inline bool Is100PercentFontWidthValid() const; + void ScaleFontWidth(vcl::RenderContext const & rRenderContext); + // scales rNonCJKFont and aCJKFont depending on nFontWidthScale and + // sets the 100%-Font-Widths +}; + +inline void FontPrevWin_Impl::Invalidate100PercentFontWidth() +{ + mn100PercentFontWidth = mn100PercentFontWidthCJK = mn100PercentFontWidthCTL = -1; +} + +inline bool FontPrevWin_Impl::Is100PercentFontWidthValid() const +{ + DBG_ASSERT( ( mn100PercentFontWidth == -1 && mn100PercentFontWidthCJK == -1 ) || + ( mn100PercentFontWidth != -1 && mn100PercentFontWidthCJK != -1 ) || + ( mn100PercentFontWidth == -1 && mn100PercentFontWidthCTL == -1 ) || + ( mn100PercentFontWidth != -1 && mn100PercentFontWidthCTL != -1 ), + "*FontPrevWin_Impl::Is100PercentFontWidthValid(): 100PercentFontWidth's not synchronous" ); + return mn100PercentFontWidth != -1; +} + +/* + * evaluates the scripttypes of the actual string. + * Afterwards the positions of script change are notified in aScriptChg, + * the scripttypes in aScriptType. + * The aTextWidth array will be filled with zero. + */ +void FontPrevWin_Impl::CheckScript() +{ + assert(!maText.isEmpty()); // must have a preview text here! + if (maText == maScriptText) + { + return; // already initialized + } + + maScriptText = maText; + maScriptChanges.clear(); + + auto aEditEngine = EditEngine(nullptr); + aEditEngine.SetText(maScriptText); + + auto aScript = aEditEngine.GetScriptType({ 0, 0, 0, 0 }); + for (sal_Int32 i = 1; i <= maScriptText.getLength(); i++) + { + auto aNextScript = aEditEngine.GetScriptType({ 0, i, 0, i }); + if (aNextScript != aScript) + maScriptChanges.emplace_back(aScript, i - 1); + if (i == maScriptText.getLength()) + maScriptChanges.emplace_back(aScript, i); + aScript = aNextScript; + } +} + +/* + * Size FontPrevWin_Impl::CalcTextSize(..) + * fills the aTextWidth array with the text width of every part + * of the actual string without a script change inside. + * For Latin parts the given rFont will be used, + * for Asian parts the aCJKFont. + * The returned size contains the whole string. + * The member nAscent is calculated to the maximal ascent of all used fonts. + */ + +Size FontPrevWin_Impl::CalcTextSize(vcl::RenderContext& rRenderContext, OutputDevice const * _pPrinter, const SvxFont& rInFont) +{ + SvtScriptType aScript; + sal_uInt16 nIdx = 0; + sal_Int32 nStart = 0; + sal_Int32 nEnd; + size_t nCnt = maScriptChanges.size(); + + if (nCnt) + { + nEnd = maScriptChanges[nIdx].changePos; + aScript = maScriptChanges[nIdx].scriptType; + } + else + { + nEnd = maText.getLength(); + aScript = SvtScriptType::LATIN; + } + tools::Long nTxtWidth = 0; + tools::Long nCJKHeight = 0; + tools::Long nCTLHeight = 0; + tools::Long nHeight = 0; + mnAscent = 0; + tools::Long nCJKAscent = 0; + tools::Long nCTLAscent = 0; + + do + { + const SvxFont& rFont = (aScript == SvtScriptType::ASIAN) ? + maCJKFont : + ((aScript == SvtScriptType::COMPLEX) ? + maCTLFont : + rInFont); + tools::Long nWidth = rFont.GetTextSize(*_pPrinter, maText, nStart, nEnd - nStart).Width(); + if (nIdx >= maScriptChanges.size()) + break; + + maScriptChanges[nIdx++].textWidth = nWidth; + nTxtWidth += nWidth; + + switch (aScript) + { + case SvtScriptType::ASIAN: + calcFontHeightAnyAscent(rRenderContext, maCJKFont, nCJKHeight, nCJKAscent); + break; + case SvtScriptType::COMPLEX: + calcFontHeightAnyAscent(rRenderContext, maCTLFont, nCTLHeight, nCTLAscent); + break; + default: + calcFontHeightAnyAscent(rRenderContext, rFont, nHeight, mnAscent); + } + + if (nEnd < maText.getLength() && nIdx < nCnt) + { + nStart = nEnd; + nEnd = maScriptChanges[nIdx].changePos; + aScript = maScriptChanges[nIdx].scriptType; + } + else + break; + } + while(true); + + nHeight -= mnAscent; + nCJKHeight -= nCJKAscent; + nCTLHeight -= nCTLAscent; + + if (nHeight < nCJKHeight) + nHeight = nCJKHeight; + + if (mnAscent < nCJKAscent) + mnAscent = nCJKAscent; + + if (nHeight < nCTLHeight) + nHeight = nCTLHeight; + + if (mnAscent < nCTLAscent) + mnAscent = nCTLAscent; + + nHeight += mnAscent; + + Size aTxtSize(nTxtWidth, nHeight); + return aTxtSize; +} + +/* + * void FontPrevWin_Impl::DrawPrev(..) + * calls SvxFont::DrawPrev(..) for every part of the string without a script + * change inside, for Asian parts the aCJKFont will be used, otherwise the + * given rFont. + */ + +void FontPrevWin_Impl::DrawPrev(vcl::RenderContext& rRenderContext, Printer* _pPrinter, Point &rPt, const SvxFont& rInFont) +{ + vcl::Font aOldFont = _pPrinter->GetFont(); + SvtScriptType aScript; + sal_uInt16 nIdx = 0; + sal_Int32 nStart = 0; + sal_Int32 nEnd; + size_t nCnt = maScriptChanges.size(); + + if (nCnt) + { + nEnd = maScriptChanges[nIdx].changePos; + aScript = maScriptChanges[nIdx].scriptType; + } + else + { + nEnd = maText.getLength(); + aScript = SvtScriptType::LATIN; + } + do + { + const SvxFont& rFont = (aScript == SvtScriptType::ASIAN) + ? maCJKFont + : ((aScript == SvtScriptType::COMPLEX) + ? maCTLFont + : rInFont); + _pPrinter->SetFont(rFont); + + rFont.DrawPrev(&rRenderContext, _pPrinter, rPt, maText, nStart, nEnd - nStart); + + rPt.AdjustX(maScriptChanges[nIdx++].textWidth); + if (nEnd < maText.getLength() && nIdx < nCnt) + { + nStart = nEnd; + nEnd = maScriptChanges[nIdx].changePos; + aScript = maScriptChanges[nIdx].scriptType; + } + else + break; + } + while(true); + _pPrinter->SetFont(aOldFont); +} + + +bool FontPrevWin_Impl::SetFontWidthScale(sal_uInt16 nScale) +{ + if (mnFontWidthScale != nScale) + { + mnFontWidthScale = nScale; + return true; + } + + return false; +} + +void FontPrevWin_Impl::ScaleFontWidth(vcl::RenderContext const & rOutDev) +{ + if (!Is100PercentFontWidthValid()) + { + scaleFontWidth(maFont, rOutDev, mn100PercentFontWidth); + scaleFontWidth(maCJKFont, rOutDev, mn100PercentFontWidthCJK); + scaleFontWidth(maCTLFont, rOutDev, mn100PercentFontWidthCTL); + } + + maFont.SetAverageFontWidth(mn100PercentFontWidth * mnFontWidthScale / 100); + maCJKFont.SetAverageFontWidth(mn100PercentFontWidthCJK * mnFontWidthScale / 100); + maCTLFont.SetAverageFontWidth(mn100PercentFontWidthCTL * mnFontWidthScale / 100); +} + +static bool GetWhich (const SfxItemSet& rSet, sal_uInt16 nSlot, sal_uInt16& rWhich) +{ + rWhich = rSet.GetPool()->GetWhich(nSlot); + return rSet.GetItemState(rWhich) >= SfxItemState::DEFAULT; +} + +static void SetPrevFont(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont) +{ + sal_uInt16 nWhich; + if (GetWhich(rSet, nSlot, nWhich)) + { + const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>(rSet.Get(nWhich)); + rFont.SetFamily(rFontItem.GetFamily()); + rFont.SetFamilyName(rFontItem.GetFamilyName()); + rFont.SetPitch(rFontItem.GetPitch()); + rFont.SetCharSet(rFontItem.GetCharSet()); + rFont.SetStyleName(rFontItem.GetStyleName()); + } +} + +static void SetPrevFontStyle( const SfxItemSet& rSet, sal_uInt16 nPosture, sal_uInt16 nWeight, SvxFont& rFont ) +{ + sal_uInt16 nWhich; + if( GetWhich( rSet, nPosture, nWhich ) ) + { + const SvxPostureItem& rItem = static_cast<const SvxPostureItem&>( rSet.Get( nWhich ) ); + rFont.SetItalic( rItem.GetValue() != ITALIC_NONE ? ITALIC_NORMAL : ITALIC_NONE ); + } + + if( GetWhich( rSet, nWeight, nWhich ) ) + { + const SvxWeightItem& rItem = static_cast<const SvxWeightItem&>( rSet.Get( nWhich ) ); + rFont.SetWeight( rItem.GetValue() != WEIGHT_NORMAL ? WEIGHT_BOLD : WEIGHT_NORMAL ); + } +} + +static void SetPrevFontEscapement(SvxFont& rFont, sal_uInt8 nProp, sal_uInt8 nEscProp, short nEsc) +{ + rFont.SetPropr(nProp); + rFont.SetProprRel(nEscProp); + rFont.SetEscapement(nEsc); +} + +void SvxFontPrevWindow::ApplySettings(vcl::RenderContext& rRenderContext) +{ + Color aBgColor = svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; + Color aFgColor = svtools::ColorConfig().GetColorValue(svtools::FONTCOLOR, false).nColor; + if (aFgColor == COL_AUTO) + aFgColor = aBgColor.IsDark() ? COL_WHITE : COL_BLACK; + rRenderContext.SetBackground(aBgColor); + rRenderContext.SetTextColor(aFgColor); +} + +void SvxFontPrevWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aPrefSize(getPreviewStripSize(pDrawingArea->get_ref_device())); + pDrawingArea->set_size_request(aPrefSize.Width(), aPrefSize.Height()); + + pImpl.reset(new FontPrevWin_Impl); + SfxViewShell* pSh = SfxViewShell::Current(); + + if (pSh) + pImpl->mpPrinter = pSh->GetPrinter(); + + if (!pImpl->mpPrinter) + { + pImpl->mpPrinter = VclPtr<Printer>::Create(); + pImpl->mbDelPrinter = true; + } + initFont(pImpl->maFont); + initFont(pImpl->maCJKFont); + initFont(pImpl->maCTLFont); + + Invalidate(); +} + +SvxFontPrevWindow::SvxFontPrevWindow() +{ +} + +SvxFontPrevWindow::~SvxFontPrevWindow() +{ +} + +SvxFont& SvxFontPrevWindow::GetCTLFont() +{ + return pImpl->maCTLFont; +} + +SvxFont& SvxFontPrevWindow::GetCJKFont() +{ + return pImpl->maCJKFont; +} + +SvxFont& SvxFontPrevWindow::GetFont() +{ + pImpl->Invalidate100PercentFontWidth(); // because the user might change the size + return pImpl->maFont; +} + +const SvxFont& SvxFontPrevWindow::GetFont() const +{ + return pImpl->maFont; +} + +void SvxFontPrevWindow::SetPreviewText( const OUString& rString ) +{ + pImpl->maText = rString; + pImpl->mbTextInited = true; +} + +void SvxFontPrevWindow::SetFontNameAsPreviewText() +{ + pImpl->mbUseFontNameAsText = true; +} + +void SvxFontPrevWindow::SetFont( const SvxFont& rNormalOutFont, const SvxFont& rCJKOutFont, const SvxFont& rCTLFont ) +{ + setFont(rNormalOutFont, pImpl->maFont); + setFont(rCJKOutFont, pImpl->maCJKFont); + setFont(rCTLFont, pImpl->maCTLFont); + + pImpl->Invalidate100PercentFontWidth(); + Invalidate(); +} + +void SvxFontPrevWindow::SetColor(const Color &rColor) +{ + pImpl->mxColor = rColor; + Invalidate(); +} + +void SvxFontPrevWindow::ResetColor() +{ + pImpl->mxColor.reset(); + Invalidate(); +} + +void SvxFontPrevWindow::SetTextLineColor(const Color &rColor) +{ + pImpl->mxTextLineColor = rColor; + Invalidate(); +} + +void SvxFontPrevWindow::SetOverlineColor(const Color &rColor) +{ + pImpl->mxOverlineColor = rColor; + Invalidate(); +} + +void SvxFontPrevWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.Push(vcl::PushFlags::ALL); + rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip)); + + ApplySettings(rRenderContext); + rRenderContext.Erase(); + + Printer* pPrinter = pImpl->mpPrinter; + const SvxFont& rFont = pImpl->maFont; + const SvxFont& rCJKFont = pImpl->maCJKFont; + const SvxFont& rCTLFont = pImpl->maCTLFont; + + if (!IsEnabled()) + { + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + const Size aLogSize(rRenderContext.GetOutputSize()); + + tools::Rectangle aRect(Point(0, 0), aLogSize); + rRenderContext.SetLineColor(); + rRenderContext.SetFillColor(rStyleSettings.GetWindowColor()); + rRenderContext.DrawRect(aRect); + } + else + { + if (!pImpl->mbSelection && !pImpl->mbTextInited) + { + using namespace css::i18n::ScriptType; + + SfxViewShell* pSh = SfxViewShell::Current(); + + if (pSh && !pImpl->mbGetSelection && !pImpl->mbUseFontNameAsText) + { + pImpl->maText = removeCRLF(pSh->GetSelectionText(/*bCompleteWords*/false, /*bOnlyASample*/true)); + pImpl->mbGetSelection = true; + pImpl->mbSelection = !(pImpl->maText.isEmpty()); + } + + if (!pImpl->mbSelection || pImpl->mbUseFontNameAsText) + { + //If we're showing multiple sample texts, then they're all + //sample texts. If only showing Latin, continue to use + //the fontname as the preview + if ((pImpl->m_bCJKEnabled) || (pImpl->m_bCTLEnabled)) + pImpl->maText = makeRepresentativeTextForFont(LATIN, rFont); + else + pImpl->maText = rFont.GetFamilyName(); + + if (pImpl->m_bCJKEnabled) + { + if (!pImpl->maText.isEmpty()) + pImpl->maText += " "; + pImpl->maText += makeRepresentativeTextForFont(ASIAN, rCJKFont); + + } + if (pImpl->m_bCTLEnabled) + { + if (!pImpl->maText.isEmpty()) + pImpl->maText += " "; + pImpl->maText += makeRepresentativeTextForFont(COMPLEX, rCTLFont); + } + } + + if (pImpl->maText.isEmpty()) + { // fdo#58427: still no text? let's try that one... + pImpl->maText = makeRepresentativeTextForFont(LATIN, rFont); + } + + pImpl->maText = removeCRLF(pImpl->maText); + + if (pImpl->maText.getLength() > (TEXT_WIDTH - 1)) + { + const sal_Int32 nSpaceIdx = pImpl->maText.indexOf(" ", TEXT_WIDTH); + if (nSpaceIdx != -1) + pImpl->maText = pImpl->maText.copy(0, nSpaceIdx); + else + pImpl->maText = pImpl->maText.copy(0, (TEXT_WIDTH - 1)); + } + } + + // calculate text width scaling + pImpl->ScaleFontWidth(rRenderContext); + + pImpl->CheckScript(); + Size aTxtSize = pImpl->CalcTextSize(rRenderContext, pPrinter, rFont); + + const Size aLogSize(rRenderContext.GetOutputSize()); + + tools::Long nX = aLogSize.Width() / 2 - aTxtSize.Width() / 2; + tools::Long nY = aLogSize.Height() / 2 - aTxtSize.Height() / 2; + + if (nY + pImpl->mnAscent > aLogSize.Height()) + nY = aLogSize.Height() - pImpl->mnAscent; + + if (pImpl->mxBackColor) + { + tools::Rectangle aRect(Point(0, 0), aLogSize); + Color aLineCol = rRenderContext.GetLineColor(); + Color aFillCol = rRenderContext.GetFillColor(); + rRenderContext.SetLineColor(); + rRenderContext.SetFillColor(*pImpl->mxBackColor); + rRenderContext.DrawRect(aRect); + rRenderContext.SetLineColor(aLineCol); + rRenderContext.SetFillColor(aFillCol); + } + if (pImpl->mxColor) + { + tools::Rectangle aRect(Point(nX, nY), aTxtSize); + Color aLineCol = rRenderContext.GetLineColor(); + Color aFillCol = rRenderContext.GetFillColor(); + rRenderContext.SetLineColor(); + rRenderContext.SetFillColor(*pImpl->mxColor); + rRenderContext.DrawRect(aRect); + rRenderContext.SetLineColor(aLineCol); + rRenderContext.SetFillColor(aFillCol); + } + + if (pImpl->mxTextLineColor) + { + rRenderContext.SetTextLineColor(*pImpl->mxTextLineColor); + } + + if (pImpl->mxOverlineColor) + { + rRenderContext.SetOverlineColor(*pImpl->mxOverlineColor); + } + + tools::Long nStdAscent = pImpl->mnAscent; + nY += nStdAscent; + + if (IsTwoLines()) + { + SvxFont aSmallFont(rFont); + Size aOldSize = pImpl->maCJKFont.GetFontSize(); + setFontSize(aSmallFont); + setFontSize(pImpl->maCJKFont); + + tools::Long nStartBracketWidth = 0; + tools::Long nEndBracketWidth = 0; + tools::Long nTextWidth = 0; + if (pImpl->mcStartBracket) + { + OUString sBracket(pImpl->mcStartBracket); + nStartBracketWidth = rFont.GetTextSize(*pPrinter, sBracket).Width(); + } + if (pImpl->mcEndBracket) + { + OUString sBracket(pImpl->mcEndBracket); + nEndBracketWidth = rFont.GetTextSize(*pPrinter, sBracket).Width(); + } + nTextWidth = pImpl->CalcTextSize(rRenderContext, pPrinter, aSmallFont).Width(); + tools::Long nResultWidth = nStartBracketWidth; + nResultWidth += nEndBracketWidth; + nResultWidth += nTextWidth; + + tools::Long _nX = (aLogSize.Width() - nResultWidth) / 2; + rRenderContext.DrawLine(Point(0, nY), Point(_nX, nY)); + rRenderContext.DrawLine(Point(_nX + nResultWidth, nY), Point(aLogSize.Width(), nY)); + + tools::Long nSmallAscent = pImpl->mnAscent; + tools::Long nOffset = (nStdAscent - nSmallAscent) / 2; + + if (pImpl->mcStartBracket) + { + OUString sBracket(pImpl->mcStartBracket); + rFont.DrawPrev(&rRenderContext, pPrinter, Point(_nX, nY - nOffset - 4), sBracket); + _nX += nStartBracketWidth; + } + + Point aTmpPoint1(_nX, nY - nSmallAscent - 2); + Point aTmpPoint2(_nX, nY); + pImpl->DrawPrev(rRenderContext, pPrinter, aTmpPoint1, aSmallFont); + pImpl->DrawPrev(rRenderContext, pPrinter, aTmpPoint2, aSmallFont); + + _nX += nTextWidth; + if (pImpl->mcEndBracket) + { + Point aTmpPoint( _nX + 1, nY - nOffset - 4); + OUString sBracket(pImpl->mcEndBracket); + rFont.DrawPrev(&rRenderContext, pPrinter, aTmpPoint, sBracket); + } + pImpl->maCJKFont.SetFontSize(aOldSize); + } + else + { + + Color aLineCol = rRenderContext.GetLineColor(); + + rRenderContext.SetLineColor(rFont.GetColor()); + rRenderContext.DrawLine(Point(0, nY), Point(nX, nY)); + rRenderContext.DrawLine(Point(nX + aTxtSize.Width(), nY), Point(aLogSize.Width(), nY)); + rRenderContext.SetLineColor(aLineCol); + + Point aTmpPoint(nX, nY); + pImpl->DrawPrev(rRenderContext, pPrinter, aTmpPoint, rFont); + } + } + rRenderContext.Pop(); +} + +bool SvxFontPrevWindow::IsTwoLines() const +{ + return pImpl->mbTwoLines; +} + +void SvxFontPrevWindow::SetTwoLines(bool bSet) +{ + pImpl->mbTwoLines = bSet; +} + +void SvxFontPrevWindow::SetBrackets(sal_Unicode cStart, sal_Unicode cEnd) +{ + pImpl->mcStartBracket = cStart; + pImpl->mcEndBracket = cEnd; +} + +void SvxFontPrevWindow::SetFontWidthScale( sal_uInt16 n ) +{ + if (pImpl->SetFontWidthScale(n)) + Invalidate(); +} + +void SvxFontPrevWindow::AutoCorrectFontColor() +{ + Color aColor(COL_AUTO); + if ( pImpl->mxBackColor ) aColor = *pImpl->mxBackColor; + const bool bIsDark(aColor.IsDark()); + + aColor = pImpl->maFont.GetColor(); + if (aColor == COL_AUTO) + pImpl->maFont.SetColor( bIsDark ? COL_WHITE : COL_BLACK ); + aColor = pImpl->maCJKFont.GetColor(); + if (aColor == COL_AUTO) + pImpl->maCJKFont.SetColor( bIsDark ? COL_WHITE : COL_BLACK ); + aColor = pImpl->maCTLFont.GetColor(); + if (aColor == COL_AUTO) + pImpl->maCTLFont.SetColor( bIsDark ? COL_WHITE : COL_BLACK ); +} + +void SvxFontPrevWindow::SetFontSize( const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont ) +{ + sal_uInt16 nWhich; + tools::Long nH; + if (GetWhich(rSet, nSlot, nWhich)) + { + nH = OutputDevice::LogicToLogic(static_cast<const SvxFontHeightItem&>(rSet.Get(nWhich)).GetHeight(), + rSet.GetPool()->GetMetric(nWhich), + MapUnit::MapTwip); + } + else + nH = 240;// as default 12pt + + rFont.SetFontSize(Size(0, nH)); +} + +void SvxFontPrevWindow::SetFontLang(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont) +{ + sal_uInt16 nWhich; + LanguageType nLang; + if( GetWhich( rSet, nSlot, nWhich ) ) + nLang = static_cast<const SvxLanguageItem&>(rSet.Get(nWhich)).GetLanguage(); + else + nLang = LANGUAGE_NONE; + rFont.SetLanguage(nLang); +} + +void SvxFontPrevWindow::SetFromItemSet(const SfxItemSet &rSet, bool bPreviewBackgroundToCharacter) +{ + sal_uInt16 nWhich; + SvxFont& rFont = GetFont(); + SvxFont& rCJKFont = GetCJKFont(); + SvxFont& rCTLFont = GetCTLFont(); + + // Preview string + if( GetWhich( rSet, SID_CHAR_DLG_PREVIEW_STRING, nWhich ) ) + { + const SfxStringItem& rItem = static_cast<const SfxStringItem&>( rSet.Get( nWhich ) ); + const OUString& aString = rItem.GetValue(); + if( !aString.isEmpty() ) + SetPreviewText( aString ); + else + SetFontNameAsPreviewText(); + } + + // Underline + FontLineStyle eUnderline; + if( GetWhich( rSet, SID_ATTR_CHAR_UNDERLINE, nWhich ) ) + { + const SvxUnderlineItem& rItem = static_cast<const SvxUnderlineItem&>( rSet.Get( nWhich ) ); + eUnderline = rItem.GetValue(); + } + else + eUnderline = LINESTYLE_NONE; + + rFont.SetUnderline( eUnderline ); + rCJKFont.SetUnderline( eUnderline ); + rCTLFont.SetUnderline( eUnderline ); + + // Overline + FontLineStyle eOverline; + if( GetWhich( rSet, SID_ATTR_CHAR_OVERLINE, nWhich ) ) + { + const SvxOverlineItem& rItem = static_cast<const SvxOverlineItem&>( rSet.Get( nWhich ) ); + eOverline = rItem.GetValue(); + } + else + eOverline = LINESTYLE_NONE; + + rFont.SetOverline( eOverline ); + rCJKFont.SetOverline( eOverline ); + rCTLFont.SetOverline( eOverline ); + + // Strikeout + FontStrikeout eStrikeout; + if( GetWhich( rSet, SID_ATTR_CHAR_STRIKEOUT, nWhich ) ) + { + const SvxCrossedOutItem& rItem = static_cast<const SvxCrossedOutItem&>( rSet.Get( nWhich ) ); + eStrikeout = rItem.GetValue(); + } + else + eStrikeout = STRIKEOUT_NONE; + + rFont.SetStrikeout( eStrikeout ); + rCJKFont.SetStrikeout( eStrikeout ); + rCTLFont.SetStrikeout( eStrikeout ); + + // WordLineMode + if( GetWhich( rSet, SID_ATTR_CHAR_WORDLINEMODE, nWhich ) ) + { + const SvxWordLineModeItem& rItem = static_cast<const SvxWordLineModeItem&>( rSet.Get( nWhich ) ); + rFont.SetWordLineMode( rItem.GetValue() ); + rCJKFont.SetWordLineMode( rItem.GetValue() ); + rCTLFont.SetWordLineMode( rItem.GetValue() ); + } + + // Emphasis + if( GetWhich( rSet, SID_ATTR_CHAR_EMPHASISMARK, nWhich ) ) + { + const SvxEmphasisMarkItem& rItem = static_cast<const SvxEmphasisMarkItem&>( rSet.Get( nWhich ) ); + FontEmphasisMark eMark = rItem.GetEmphasisMark(); + rFont.SetEmphasisMark( eMark ); + rCJKFont.SetEmphasisMark( eMark ); + rCTLFont.SetEmphasisMark( eMark ); + } + + // Relief + if( GetWhich( rSet, SID_ATTR_CHAR_RELIEF, nWhich ) ) + { + const SvxCharReliefItem& rItem = static_cast<const SvxCharReliefItem&>( rSet.Get( nWhich ) ); + FontRelief eFontRelief = rItem.GetValue(); + rFont.SetRelief( eFontRelief ); + rCJKFont.SetRelief( eFontRelief ); + rCTLFont.SetRelief( eFontRelief ); + } + + // Effects + if( GetWhich( rSet, SID_ATTR_CHAR_CASEMAP, nWhich ) ) + { + const SvxCaseMapItem& rItem = static_cast<const SvxCaseMapItem&>( rSet.Get( nWhich ) ); + SvxCaseMap eCaseMap = rItem.GetValue(); + rFont.SetCaseMap( eCaseMap ); + rCJKFont.SetCaseMap( eCaseMap ); + // #i78474# small caps do not exist in CTL fonts + rCTLFont.SetCaseMap( eCaseMap == SvxCaseMap::SmallCaps ? SvxCaseMap::NotMapped : eCaseMap ); + } + + // Outline + if( GetWhich( rSet, SID_ATTR_CHAR_CONTOUR, nWhich ) ) + { + const SvxContourItem& rItem = static_cast<const SvxContourItem&>( rSet.Get( nWhich ) ); + bool bOutline = rItem.GetValue(); + rFont.SetOutline( bOutline ); + rCJKFont.SetOutline( bOutline ); + rCTLFont.SetOutline( bOutline ); + } + + // Shadow + if( GetWhich( rSet, SID_ATTR_CHAR_SHADOWED, nWhich ) ) + { + const SvxShadowedItem& rItem = static_cast<const SvxShadowedItem&>( rSet.Get( nWhich ) ); + bool bShadow = rItem.GetValue(); + rFont.SetShadow( bShadow ); + rCJKFont.SetShadow( bShadow ); + rCTLFont.SetShadow( bShadow ); + } + + // Background + bool bTransparent; + if( GetWhich( rSet, bPreviewBackgroundToCharacter ? SID_ATTR_BRUSH : SID_ATTR_BRUSH_CHAR, nWhich ) ) + { + const SvxBrushItem& rBrush = static_cast<const SvxBrushItem&>( rSet.Get( nWhich ) ); + const Color& rColor = rBrush.GetColor(); + bTransparent = rColor.IsTransparent(); + rFont.SetFillColor( rColor ); + rCJKFont.SetFillColor( rColor ); + rCTLFont.SetFillColor( rColor ); + } + else + bTransparent = true; + + rFont.SetTransparent( bTransparent ); + rCJKFont.SetTransparent( bTransparent ); + rCTLFont.SetTransparent( bTransparent ); + + if( !bPreviewBackgroundToCharacter ) + { + bool bBackColorFound = false; + if( GetWhich( rSet, SID_ATTR_BRUSH, nWhich ) ) + { + const SvxBrushItem& rBrush = static_cast<const SvxBrushItem&>( rSet.Get( nWhich ) ); + if (GPOS_NONE == rBrush.GetGraphicPos()) + { + const Color& rBrushColor = rBrush.GetColor(); + if (rBrushColor != COL_TRANSPARENT) + { + pImpl->mxBackColor = rBrush.GetColor(); + bBackColorFound = true; + } + } + } + if (!bBackColorFound) + pImpl->mxBackColor = svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; + } + + // Font + SetPrevFont( rSet, SID_ATTR_CHAR_FONT, rFont ); + SetPrevFont( rSet, SID_ATTR_CHAR_CJK_FONT, rCJKFont ); + SetPrevFont( rSet, SID_ATTR_CHAR_CTL_FONT, rCTLFont ); + + // Style + SetPrevFontStyle( rSet, SID_ATTR_CHAR_POSTURE, SID_ATTR_CHAR_WEIGHT, rFont ); + SetPrevFontStyle( rSet, SID_ATTR_CHAR_CJK_POSTURE, SID_ATTR_CHAR_CJK_WEIGHT, rCJKFont ); + SetPrevFontStyle( rSet, SID_ATTR_CHAR_CTL_POSTURE, SID_ATTR_CHAR_CTL_WEIGHT, rCTLFont ); + + // Size + SetFontSize( rSet, SID_ATTR_CHAR_FONTHEIGHT, rFont ); + SetFontSize( rSet, SID_ATTR_CHAR_CJK_FONTHEIGHT, rCJKFont ); + SetFontSize( rSet, SID_ATTR_CHAR_CTL_FONTHEIGHT, rCTLFont ); + + // Language + SetFontLang( rSet, SID_ATTR_CHAR_LANGUAGE, rFont ); + SetFontLang( rSet, SID_ATTR_CHAR_CJK_LANGUAGE, rCJKFont ); + SetFontLang( rSet, SID_ATTR_CHAR_CTL_LANGUAGE, rCTLFont ); + + // Color + if( GetWhich( rSet, SID_ATTR_CHAR_COLOR, nWhich ) ) + { + const SvxColorItem& rItem = static_cast<const SvxColorItem&>( rSet.Get( nWhich ) ); + Color aCol( rItem.GetValue() ); + rFont.SetColor( aCol ); + + rCJKFont.SetColor( aCol ); + rCTLFont.SetColor( aCol ); + + AutoCorrectFontColor(); // handle color COL_AUTO + } + + // Kerning + if( GetWhich( rSet, SID_ATTR_CHAR_KERNING, nWhich ) ) + { + const SvxKerningItem& rItem = static_cast<const SvxKerningItem&>( rSet.Get( nWhich ) ); + short nKern = static_cast<short>(OutputDevice::LogicToLogic(rItem.GetValue(), rSet.GetPool()->GetMetric(nWhich), MapUnit::MapTwip)); + rFont.SetFixKerning( nKern ); + rCJKFont.SetFixKerning( nKern ); + rCTLFont.SetFixKerning( nKern ); + } + + // Escapement + const sal_uInt8 nProp = 100; + short nEsc; + sal_uInt8 nEscProp; + if( GetWhich( rSet, SID_ATTR_CHAR_ESCAPEMENT, nWhich ) ) + { + const SvxEscapementItem& rItem = static_cast<const SvxEscapementItem&>( rSet.Get( nWhich ) ); + nEsc = rItem.GetEsc(); + nEscProp = rItem.GetProportionalHeight(); + + if( nEsc == DFLT_ESC_AUTO_SUPER ) + nEsc = DFLT_ESC_SUPER; + else if( nEsc == DFLT_ESC_AUTO_SUB ) + nEsc = DFLT_ESC_SUB; + } + else + { + nEsc = 0; + nEscProp = 100; + } + SetPrevFontEscapement( rFont, nProp, nEscProp, nEsc ); + SetPrevFontEscapement( rCJKFont, nProp, nEscProp, nEsc ); + SetPrevFontEscapement( rCTLFont, nProp, nEscProp, nEsc ); + + // Font width scale + if( GetWhich( rSet, SID_ATTR_CHAR_SCALEWIDTH, nWhich ) ) + { + const SvxCharScaleWidthItem&rItem = static_cast<const SvxCharScaleWidthItem&>( rSet.Get( nWhich ) ); + SetFontWidthScale( rItem.GetValue() ); + } + + Invalidate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/fontwork.cxx b/svx/source/dialog/fontwork.cxx new file mode 100644 index 0000000000..5f9b681b02 --- /dev/null +++ b/svx/source/dialog/fontwork.cxx @@ -0,0 +1,795 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sfx2/module.hxx> +#include <sfx2/dispatch.hxx> + +#include <svx/colorbox.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <svx/xftadit.hxx> +#include <svx/xftdiit.hxx> +#include <svx/xftstit.hxx> +#include <svx/xftmrit.hxx> +#include <svx/xftouit.hxx> +#include <svx/xftshit.hxx> +#include <svx/xftshcit.hxx> +#include <svx/xftshxy.hxx> +#include <svx/xtextit0.hxx> + +#include <svtools/unitconv.hxx> +#include <svx/svxids.hrc> +#include <bitmaps.hlst> +#include <svx/fontwork.hxx> +#include <svl/itemset.hxx> + +#define WIDTH_CHARS 10 + +SFX_IMPL_DOCKINGWINDOW_WITHID( SvxFontWorkChildWindow, SID_FONTWORK ); + +// ControllerItem for Fontwork + +SvxFontWorkControllerItem::SvxFontWorkControllerItem +( + sal_uInt16 _nId, + SvxFontWorkDialog& rDlg, + SfxBindings& rBindings +) : + + SfxControllerItem( _nId, rBindings ), + + rFontWorkDlg( rDlg ) +{ +} + +// StateChanged method for FontWork items + +void SvxFontWorkControllerItem::StateChangedAtToolBoxControl( sal_uInt16 /*nSID*/, SfxItemState /*eState*/, + const SfxPoolItem* pItem ) +{ + switch ( GetId() ) + { + case SID_FORMTEXT_STYLE: + { + const XFormTextStyleItem* pStateItem = + dynamic_cast<const XFormTextStyleItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextStyleItem expected"); + rFontWorkDlg.SetStyle_Impl(pStateItem); + break; + } + case SID_FORMTEXT_ADJUST: + { + const XFormTextAdjustItem* pStateItem = + dynamic_cast<const XFormTextAdjustItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextAdjustItem expected"); + rFontWorkDlg.SetAdjust_Impl(pStateItem); + break; + } + case SID_FORMTEXT_DISTANCE: + { + const XFormTextDistanceItem* pStateItem = + dynamic_cast<const XFormTextDistanceItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextDistanceItem expected"); + rFontWorkDlg.SetDistance_Impl(pStateItem); + break; + } + case SID_FORMTEXT_START: + { + const XFormTextStartItem* pStateItem = + dynamic_cast<const XFormTextStartItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextStartItem expected"); + rFontWorkDlg.SetStart_Impl(pStateItem); + break; + } + case SID_FORMTEXT_MIRROR: + { + const XFormTextMirrorItem* pStateItem = + dynamic_cast<const XFormTextMirrorItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextMirrorItem expected"); + rFontWorkDlg.SetMirror_Impl(pStateItem); + break; + } + case SID_FORMTEXT_HIDEFORM: + { + const XFormTextHideFormItem* pStateItem = + dynamic_cast<const XFormTextHideFormItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextHideFormItem expected"); + rFontWorkDlg.SetShowForm_Impl(pStateItem); + break; + } + case SID_FORMTEXT_OUTLINE: + { + const XFormTextOutlineItem* pStateItem = + dynamic_cast<const XFormTextOutlineItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextOutlineItem expected"); + rFontWorkDlg.SetOutline_Impl(pStateItem); + break; + } + case SID_FORMTEXT_SHADOW: + { + const XFormTextShadowItem* pStateItem = + dynamic_cast<const XFormTextShadowItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextShadowItem expected"); + rFontWorkDlg.SetShadow_Impl(pStateItem); + break; + } + case SID_FORMTEXT_SHDWCOLOR: + { + const XFormTextShadowColorItem* pStateItem = + dynamic_cast<const XFormTextShadowColorItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextShadowColorItem expected"); + rFontWorkDlg.SetShadowColor_Impl(pStateItem); + break; + } + case SID_FORMTEXT_SHDWXVAL: + { + const XFormTextShadowXValItem* pStateItem = + dynamic_cast<const XFormTextShadowXValItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextShadowXValItem expected"); + rFontWorkDlg.SetShadowXVal_Impl(pStateItem); + break; + } + case SID_FORMTEXT_SHDWYVAL: + { + const XFormTextShadowYValItem* pStateItem = + dynamic_cast<const XFormTextShadowYValItem*>( pItem ); + DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextShadowYValItem expected"); + rFontWorkDlg.SetShadowYVal_Impl(pStateItem); + break; + } + } +} + +// Derivation from SfxChildWindow as "containers" for Fontwork dialog + +SvxFontWorkChildWindow::SvxFontWorkChildWindow +( + vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo +) : + SfxChildWindow( _pParent, nId ) +{ + VclPtrInstance<SvxFontWorkDialog> pDlg(pBindings, this, _pParent); + SetWindow(pDlg); + + pDlg->Initialize( pInfo ); +} + +// Floating Window to the attribution of text effects +SvxFontWorkDialog::SvxFontWorkDialog(SfxBindings *pBindinx, + SfxChildWindow *pCW, + vcl::Window* _pParent) + : SfxDockingWindow(pBindinx, pCW, _pParent, "DockingFontwork", "svx/ui/dockingfontwork.ui") + , rBindings(*pBindinx) + , aInputIdle("SvxFontWorkDialog Input") + , nSaveShadowX(0) + , nSaveShadowY(0) + , nSaveShadowAngle(450) + , nSaveShadowSize (100) + , m_xTbxStyle(m_xBuilder->weld_toolbar("style")) + , m_xTbxAdjust(m_xBuilder->weld_toolbar("adjust")) + , m_xMtrFldDistance(m_xBuilder->weld_metric_spin_button("distance", FieldUnit::CM)) + , m_xMtrFldTextStart(m_xBuilder->weld_metric_spin_button("indent", FieldUnit::CM)) + , m_xTbxShadow(m_xBuilder->weld_toolbar("shadow")) + , m_xFbShadowX(m_xBuilder->weld_image("shadowx")) + , m_xMtrFldShadowX(m_xBuilder->weld_metric_spin_button("distancex", FieldUnit::CM)) + , m_xFbShadowY(m_xBuilder->weld_image("shadowy")) + , m_xMtrFldShadowY(m_xBuilder->weld_metric_spin_button("distancey", FieldUnit::CM)) + , m_xShadowColorLB(new ColorListBox(m_xBuilder->weld_menu_button("color"), [this]{ return GetFrameWeld(); } )) +{ + SetText(SvxResId(RID_SVXSTR_FONTWORK)); + + ApplyImageList(); + + pCtrlItems[0] = new SvxFontWorkControllerItem(SID_FORMTEXT_STYLE, *this, rBindings); + pCtrlItems[1] = new SvxFontWorkControllerItem(SID_FORMTEXT_ADJUST, *this, rBindings); + pCtrlItems[2] = new SvxFontWorkControllerItem(SID_FORMTEXT_DISTANCE, *this, rBindings); + pCtrlItems[3] = new SvxFontWorkControllerItem(SID_FORMTEXT_START, *this, rBindings); + pCtrlItems[4] = new SvxFontWorkControllerItem(SID_FORMTEXT_MIRROR, *this, rBindings); + pCtrlItems[5] = new SvxFontWorkControllerItem(SID_FORMTEXT_HIDEFORM, *this, rBindings); + pCtrlItems[6] = new SvxFontWorkControllerItem(SID_FORMTEXT_OUTLINE, *this, rBindings); + pCtrlItems[7] = new SvxFontWorkControllerItem(SID_FORMTEXT_SHADOW, *this, rBindings); + pCtrlItems[8] = new SvxFontWorkControllerItem(SID_FORMTEXT_SHDWCOLOR, *this, rBindings); + pCtrlItems[9] = new SvxFontWorkControllerItem(SID_FORMTEXT_SHDWXVAL, *this, rBindings); + pCtrlItems[10] = new SvxFontWorkControllerItem(SID_FORMTEXT_SHDWYVAL, *this, rBindings); + + m_xTbxStyle->connect_clicked(LINK(this, SvxFontWorkDialog, SelectStyleHdl_Impl)); + m_xTbxAdjust->connect_clicked(LINK(this, SvxFontWorkDialog, SelectAdjustHdl_Impl)); + m_xTbxShadow->connect_clicked(LINK(this, SvxFontWorkDialog, SelectShadowHdl_Impl)); + + Link<weld::MetricSpinButton&,void> aLink = LINK(this, SvxFontWorkDialog, ModifyInputHdl_Impl); + m_xMtrFldDistance->connect_value_changed( aLink ); + m_xMtrFldTextStart->connect_value_changed( aLink ); + m_xMtrFldShadowX->connect_value_changed( aLink ); + m_xMtrFldShadowY->connect_value_changed( aLink ); + + // Set System metric + const FieldUnit eDlgUnit = rBindings.GetDispatcher()->GetModule()->GetFieldUnit(); + SetFieldUnit(*m_xMtrFldDistance, eDlgUnit, true); + SetFieldUnit(*m_xMtrFldTextStart, eDlgUnit, true); + SetFieldUnit(*m_xMtrFldShadowX, eDlgUnit, true); + SetFieldUnit(*m_xMtrFldShadowY, eDlgUnit, true); + if( eDlgUnit == FieldUnit::MM ) + { + m_xMtrFldDistance->set_increments(50, 500, FieldUnit::NONE); + m_xMtrFldTextStart->set_increments(50, 500, FieldUnit::NONE); + m_xMtrFldShadowX->set_increments(50, 500, FieldUnit::NONE); + m_xMtrFldShadowY->set_increments(50, 500, FieldUnit::NONE); + } + else + { + m_xMtrFldDistance->set_increments(10, 100, FieldUnit::NONE); + m_xMtrFldTextStart->set_increments(10, 100, FieldUnit::NONE); + m_xMtrFldShadowX->set_increments(10, 100, FieldUnit::NONE); + m_xMtrFldShadowY->set_increments(10, 100, FieldUnit::NONE); + } + m_xMtrFldShadowX->set_width_chars(WIDTH_CHARS); + m_xMtrFldShadowY->set_width_chars(WIDTH_CHARS); + + m_xShadowColorLB->SetSelectHdl( LINK(this, SvxFontWorkDialog, ColorSelectHdl_Impl) ); + + aInputIdle.SetPriority(TaskPriority::LOWEST); + aInputIdle.SetInvokeHandler(LINK(this, SvxFontWorkDialog, InputTimeoutHdl_Impl)); +} + +SvxFontWorkDialog::~SvxFontWorkDialog() +{ + disposeOnce(); +} + +void SvxFontWorkDialog::dispose() +{ + for (SvxFontWorkControllerItem* pCtrlItem : pCtrlItems) + pCtrlItem->dispose(); + m_xTbxStyle.reset(); + m_xTbxAdjust.reset(); + m_xMtrFldDistance.reset(); + m_xMtrFldTextStart.reset(); + m_xTbxShadow.reset(); + m_xFbShadowX.reset(); + m_xMtrFldShadowX.reset(); + m_xFbShadowY.reset(); + m_xMtrFldShadowY.reset(); + m_xShadowColorLB.reset(); + SfxDockingWindow::dispose(); +} + +SfxChildAlignment SvxFontWorkDialog::CheckAlignment( SfxChildAlignment eActAlign, + SfxChildAlignment eAlign ) +{ + SfxChildAlignment eAlignment; + + switch ( eAlign ) + { + case SfxChildAlignment::TOP: + case SfxChildAlignment::HIGHESTTOP: + case SfxChildAlignment::LOWESTTOP: + case SfxChildAlignment::BOTTOM: + case SfxChildAlignment::LOWESTBOTTOM: + case SfxChildAlignment::HIGHESTBOTTOM: + { + eAlignment = eActAlign; + } + break; + + case SfxChildAlignment::LEFT: + case SfxChildAlignment::RIGHT: + case SfxChildAlignment::FIRSTLEFT: + case SfxChildAlignment::LASTLEFT: + case SfxChildAlignment::FIRSTRIGHT: + case SfxChildAlignment::LASTRIGHT: + { + eAlignment = eAlign; + } + break; + + default: + { + eAlignment = eAlign; + } + break; + } + + return eAlignment; +} + +// Set style buttons +void SvxFontWorkDialog::SetStyle_Impl(const XFormTextStyleItem* pItem) +{ + if ( pItem ) + { + OUString sId = "off"; + + switch ( pItem->GetValue() ) + { + case XFormTextStyle::Rotate : sId = "rotate"; break; + case XFormTextStyle::Upright: sId = "upright"; break; + case XFormTextStyle::SlantX : sId = "hori"; break; + case XFormTextStyle::SlantY : sId = "vert"; break; + default: ;//prevent warning + } + m_xTbxStyle->set_sensitive(true); + + // Make sure that there is always exactly one checked toolbox item. + if ( pItem->GetValue() == XFormTextStyle::NONE ) + { + m_xTbxStyle->set_item_active("rotate", false); + m_xTbxStyle->set_item_active("upright", false); + m_xTbxStyle->set_item_active("hori", false); + m_xTbxStyle->set_item_active("vert", false); + + m_xTbxStyle->set_item_active("off", true); + } + else + { + m_xTbxStyle->set_item_active("off", false); + m_xTbxStyle->set_item_active(sId, true); + } + + m_sLastStyleTbxId = sId; + } + else + m_xTbxStyle->set_sensitive(false); +} + +// Set adjust buttons + +void SvxFontWorkDialog::SetAdjust_Impl(const XFormTextAdjustItem* pItem) +{ + if ( pItem ) + { + OUString sId; + + m_xTbxAdjust->set_sensitive(true); + m_xMtrFldDistance->set_sensitive(true); + + if ( pItem->GetValue() == XFormTextAdjust::Left || pItem->GetValue() == XFormTextAdjust::Right ) + { + if (pItem->GetValue() == XFormTextAdjust::Left) + sId = "left"; + else + sId = "right"; + m_xMtrFldTextStart->set_sensitive(true); + } + else + { + if (pItem->GetValue() == XFormTextAdjust::Center) + sId = "center"; + else + sId = "autosize"; + m_xMtrFldTextStart->set_sensitive(false); + } + + if (!m_xTbxAdjust->get_item_active(sId)) + m_xTbxAdjust->set_item_active(sId, true); + + m_sLastAdjustTbxId = sId; + } + else + { + m_xTbxAdjust->set_sensitive(false); + m_xMtrFldTextStart->set_sensitive(false); + m_xMtrFldDistance->set_sensitive(false); + } +} + +// Enter Distance value in the edit field + +void SvxFontWorkDialog::SetDistance_Impl(const XFormTextDistanceItem* pItem) +{ + if (pItem && !m_xMtrFldDistance->has_focus()) + { + SetMetricValue(*m_xMtrFldDistance, pItem->GetValue(), MapUnit::Map100thMM); + } +} + +// Enter indent value in the edit field + +void SvxFontWorkDialog::SetStart_Impl(const XFormTextStartItem* pItem) +{ + if (pItem && !m_xMtrFldTextStart->has_focus()) + { + SetMetricValue(*m_xMtrFldTextStart, pItem->GetValue(), MapUnit::Map100thMM); + } +} + +// Set button for reversing the direction of text + +void SvxFontWorkDialog::SetMirror_Impl(const XFormTextMirrorItem* pItem) +{ + if ( pItem ) + m_xTbxAdjust->set_item_active("orientation", pItem->GetValue()); +} + +// Set button for contour display + +void SvxFontWorkDialog::SetShowForm_Impl(const XFormTextHideFormItem* pItem) +{ + if ( pItem ) + m_xTbxShadow->set_item_active("contour", !pItem->GetValue()); +} + +// Set button for text border + +void SvxFontWorkDialog::SetOutline_Impl(const XFormTextOutlineItem* pItem) +{ + if ( pItem ) + m_xTbxShadow->set_item_active("textcontour", pItem->GetValue()); +} + +// Set shadow buttons + +void SvxFontWorkDialog::SetShadow_Impl(const XFormTextShadowItem* pItem, + bool bRestoreValues) +{ + if ( pItem ) + { + OUString sId; + + m_xTbxShadow->set_sensitive(true); + + if ( pItem->GetValue() == XFormTextShadow::NONE ) + { + sId = "noshadow"; + m_xFbShadowX->hide(); + m_xFbShadowY->hide(); + m_xMtrFldShadowX->set_sensitive(false); + m_xMtrFldShadowY->set_sensitive(false); + m_xShadowColorLB->set_sensitive(false); + } + else + { + m_xFbShadowX->show(); + m_xFbShadowY->show(); + m_xMtrFldShadowX->set_sensitive(true); + m_xMtrFldShadowY->set_sensitive(true); + m_xShadowColorLB->set_sensitive(true); + + if ( pItem->GetValue() == XFormTextShadow::Normal ) + { + sId = "vertical"; + const FieldUnit eDlgUnit = rBindings.GetDispatcher()->GetModule()->GetFieldUnit(); + + m_xMtrFldShadowX->set_unit( eDlgUnit ); + m_xMtrFldShadowX->set_digits(2); + m_xMtrFldShadowX->set_range(INT_MIN, INT_MAX, FieldUnit::NONE); + if( eDlgUnit == FieldUnit::MM ) + m_xMtrFldShadowX->set_increments(50, 500, FieldUnit::NONE); + else + m_xMtrFldShadowX->set_increments(10, 100, FieldUnit::NONE); + m_xMtrFldShadowX->set_width_chars(WIDTH_CHARS); + + m_xMtrFldShadowY->set_unit( eDlgUnit ); + m_xMtrFldShadowY->set_digits(2); + m_xMtrFldShadowY->set_range(INT_MIN, INT_MAX, FieldUnit::NONE); + if( eDlgUnit == FieldUnit::MM ) + m_xMtrFldShadowY->set_increments(50, 500, FieldUnit::NONE); + else + m_xMtrFldShadowY->set_increments(10, 100, FieldUnit::NONE); + m_xMtrFldShadowY->set_width_chars(WIDTH_CHARS); + + if ( bRestoreValues ) + { + SetMetricValue(*m_xMtrFldShadowX, nSaveShadowX, MapUnit::Map100thMM); + SetMetricValue(*m_xMtrFldShadowY, nSaveShadowY, MapUnit::Map100thMM); + + XFormTextShadowXValItem aXItem( nSaveShadowX ); + XFormTextShadowYValItem aYItem( nSaveShadowY ); + + GetBindings().GetDispatcher()->ExecuteList( + SID_FORMTEXT_SHDWXVAL, SfxCallMode::RECORD, + { &aXItem, &aYItem }); + } + } + else + { + sId = "slant"; + + m_xMtrFldShadowX->set_unit(FieldUnit::DEGREE); + m_xMtrFldShadowX->set_digits(1); + m_xMtrFldShadowX->set_range(-1800, 1800, FieldUnit::NONE); + m_xMtrFldShadowX->set_increments(10, 100, FieldUnit::NONE); + m_xMtrFldShadowX->set_width_chars(WIDTH_CHARS); + + m_xMtrFldShadowY->set_unit(FieldUnit::PERCENT); + m_xMtrFldShadowY->set_digits(0); + m_xMtrFldShadowY->set_range(-999, 999, FieldUnit::NONE); + m_xMtrFldShadowY->set_increments(10, 100, FieldUnit::NONE); + m_xMtrFldShadowY->set_width_chars(WIDTH_CHARS); + + if ( bRestoreValues ) + { + m_xMtrFldShadowX->set_value(nSaveShadowAngle, FieldUnit::NONE); + m_xMtrFldShadowY->set_value(nSaveShadowSize, FieldUnit::NONE); + XFormTextShadowXValItem aXItem(nSaveShadowAngle); + XFormTextShadowYValItem aYItem(nSaveShadowSize); + GetBindings().GetDispatcher()->ExecuteList( + SID_FORMTEXT_SHDWXVAL, SfxCallMode::RECORD, + { &aXItem, &aYItem }); + } + } + } + + if (!m_xTbxShadow->get_item_active(sId)) + m_xTbxShadow->set_item_active(sId, true); + m_sLastShadowTbxId = sId; + + ApplyImageList(); + } + else + { + m_xTbxShadow->set_sensitive(false); + m_xMtrFldShadowX->set_sensitive(false); + m_xMtrFldShadowY->set_sensitive(false); + m_xShadowColorLB->set_sensitive(false); + } +} + +// Insert shadow color in listbox + +void SvxFontWorkDialog::SetShadowColor_Impl(const XFormTextShadowColorItem* pItem) +{ + if ( pItem ) + m_xShadowColorLB->SelectEntry(pItem->GetColorValue()); +} + +// Enter X-value for shadow in edit field +void SvxFontWorkDialog::SetShadowXVal_Impl(const XFormTextShadowXValItem* pItem) +{ + if (!pItem || m_xMtrFldShadowX->has_focus()) + return; + + // #i19251# + // sal_Int32 nValue = pItem->GetValue(); + + // #i19251# + // The two involved fields/items are used double and contain/give different + // values regarding to the access method. Thus, here we need to separate the access + // methods regarding to the kind of value accessed. + if (m_xTbxShadow->get_item_active("slant")) + { + // #i19251# + // There is no value correction necessary at all, i think this + // was only tried to be done without understanding that the two + // involved fields/items are used double and contain/give different + // values regarding to the access method. + // nValue = nValue - ( int( float( nValue ) / 360.0 ) * 360 ); + m_xMtrFldShadowX->set_value(pItem->GetValue(), FieldUnit::NONE); + } + else + { + SetMetricValue(*m_xMtrFldShadowX, pItem->GetValue(), MapUnit::Map100thMM); + } +} + +// Enter Y-value for shadow in edit field +void SvxFontWorkDialog::SetShadowYVal_Impl(const XFormTextShadowYValItem* pItem) +{ + if (!pItem || m_xMtrFldShadowY->has_focus()) + return; + + // #i19251# + // The two involved fields/items are used double and contain/give different + // values regarding to the access method. Thus, here we need to separate the access + // methods regarding to the kind of value accessed. + if (m_xTbxShadow->get_item_active("slant")) + { + m_xMtrFldShadowY->set_value(pItem->GetValue(), FieldUnit::NONE); + } + else + { + SetMetricValue(*m_xMtrFldShadowY, pItem->GetValue(), MapUnit::Map100thMM); + } +} + +IMPL_LINK(SvxFontWorkDialog, SelectStyleHdl_Impl, const OUString&, rId, void) +{ + // Execute this block when a different toolbox item has been clicked or + // when the off item has been clicked. The later is necessary to + // override the toolbox behaviour of unchecking the item after second + // click on it: One of the items has to be checked at all times (when + // enabled that is.) + if (rId != "off" && rId == m_sLastStyleTbxId) + return; + + XFormTextStyle eStyle = XFormTextStyle::NONE; + + if (rId == "rotate") + eStyle = XFormTextStyle::Rotate; + else if (rId == "upright") + eStyle = XFormTextStyle::Upright; + else if (rId == "hori") + eStyle = XFormTextStyle::SlantX; + else if (rId == "vert") + eStyle = XFormTextStyle::SlantY; + + XFormTextStyleItem aItem( eStyle ); + GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_STYLE, + SfxCallMode::RECORD, { &aItem }); + SetStyle_Impl( &aItem ); + m_sLastStyleTbxId = rId; +} + +IMPL_LINK(SvxFontWorkDialog, SelectAdjustHdl_Impl, const OUString&, rId, void) +{ + if (rId == "orientation") + { + XFormTextMirrorItem aItem(m_xTbxAdjust->get_item_active(rId)); + GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_MIRROR, + SfxCallMode::SLOT, { &aItem }); + } + else if (rId != m_sLastAdjustTbxId) + { + XFormTextAdjust eAdjust = XFormTextAdjust::AutoSize; + + if (rId == "left") + eAdjust = XFormTextAdjust::Left; + else if (rId == "center") + eAdjust = XFormTextAdjust::Center; + else if (rId == "right") + eAdjust = XFormTextAdjust::Right; + + XFormTextAdjustItem aItem(eAdjust); + GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_ADJUST, + SfxCallMode::RECORD, { &aItem }); + SetAdjust_Impl(&aItem); + m_sLastAdjustTbxId = rId; + } +} + +IMPL_LINK(SvxFontWorkDialog, SelectShadowHdl_Impl, const OUString&, rId, void) +{ + if (rId == "contour") + { + XFormTextHideFormItem aItem(!m_xTbxShadow->get_item_active(rId)); + GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_HIDEFORM, + SfxCallMode::RECORD, { &aItem }); + } + else if (rId == "textcontour") + { + XFormTextOutlineItem aItem(m_xTbxShadow->get_item_active(rId)); + GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_OUTLINE, + SfxCallMode::RECORD, { &aItem }); + } + else if (rId != m_sLastShadowTbxId) + { + XFormTextShadow eShadow = XFormTextShadow::NONE; + + if (m_sLastShadowTbxId == "vertical") + { + nSaveShadowX = GetCoreValue(*m_xMtrFldShadowX, MapUnit::Map100thMM); + nSaveShadowY = GetCoreValue(*m_xMtrFldShadowY, MapUnit::Map100thMM); + } + else if (m_sLastShadowTbxId == "slant") + { + nSaveShadowAngle = m_xMtrFldShadowX->get_value(FieldUnit::NONE); + nSaveShadowSize = m_xMtrFldShadowY->get_value(FieldUnit::NONE); + } + m_sLastShadowTbxId = rId; + + if ( rId == "vertical") eShadow = XFormTextShadow::Normal; + else if (rId == "slant") eShadow = XFormTextShadow::Slant; + + XFormTextShadowItem aItem(eShadow); + GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_SHADOW, + SfxCallMode::RECORD, { &aItem }); + SetShadow_Impl(&aItem, true); + } +} + +IMPL_LINK_NOARG(SvxFontWorkDialog, ModifyInputHdl_Impl, weld::MetricSpinButton&, void) +{ + aInputIdle.Start(); +} + +IMPL_LINK_NOARG(SvxFontWorkDialog, InputTimeoutHdl_Impl, Timer*, void) +{ + // Possibly set the Metric system again. This should be done with a + // listen, this is however not possible at the moment due to compatibility + // issues. + const FieldUnit eDlgUnit = rBindings.GetDispatcher()->GetModule()->GetFieldUnit(); + if( eDlgUnit != m_xMtrFldDistance->get_unit() ) + { + SetFieldUnit(*m_xMtrFldDistance, eDlgUnit, true); + SetFieldUnit(*m_xMtrFldTextStart, eDlgUnit, true); + if (eDlgUnit == FieldUnit::MM) + { + m_xMtrFldDistance->set_increments(50, 500, FieldUnit::NONE); + m_xMtrFldTextStart->set_increments(50, 500, FieldUnit::NONE); + } + else + { + m_xMtrFldDistance->set_increments(10, 100, FieldUnit::NONE); + m_xMtrFldTextStart->set_increments(10, 100, FieldUnit::NONE); + } + } + if( eDlgUnit != m_xMtrFldShadowX->get_unit() && + m_xTbxShadow->get_item_active("vertical") ) + { + SetFieldUnit(*m_xMtrFldShadowX, eDlgUnit, true); + SetFieldUnit(*m_xMtrFldShadowY, eDlgUnit, true); + + if (eDlgUnit == FieldUnit::MM) + { + m_xMtrFldShadowX->set_increments(50, 500, FieldUnit::NONE); + m_xMtrFldShadowY->set_increments(50, 500, FieldUnit::NONE); + } + else + { + m_xMtrFldShadowX->set_increments(10, 100, FieldUnit::NONE); + m_xMtrFldShadowY->set_increments(10, 100, FieldUnit::NONE); + } + } + + tools::Long nValue = GetCoreValue(*m_xMtrFldDistance, MapUnit::Map100thMM); + XFormTextDistanceItem aDistItem( nValue ); + nValue = GetCoreValue(*m_xMtrFldTextStart, MapUnit::Map100thMM); + XFormTextStartItem aStartItem( nValue ); + + sal_Int32 nValueX(0); + sal_Int32 nValueY(0); + + // #i19251# + // The two involved fields/items are used double and contain/give different + // values regarding to the access method. Thus, here we need to separate the access + // method regarding to the kind of value accessed. + if (m_sLastShadowTbxId == "vertical") + { + nValueX = GetCoreValue(*m_xMtrFldShadowX, MapUnit::Map100thMM); + nValueY = GetCoreValue(*m_xMtrFldShadowY, MapUnit::Map100thMM); + } + else if (m_sLastShadowTbxId == "slant") + { + nValueX = m_xMtrFldShadowX->get_value(FieldUnit::NONE); + nValueY = m_xMtrFldShadowY->get_value(FieldUnit::NONE); + } + + XFormTextShadowXValItem aShadowXItem( nValueX ); + XFormTextShadowYValItem aShadowYItem( nValueY ); + + // Slot-ID does not matter, the Exec method evaluates the entire item set + GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_DISTANCE, + SfxCallMode::RECORD, + { &aDistItem, &aStartItem, &aShadowXItem, &aShadowYItem }); +} + +IMPL_LINK_NOARG(SvxFontWorkDialog, ColorSelectHdl_Impl, ColorListBox&, void) +{ + XFormTextShadowColorItem aItem( "", m_xShadowColorLB->GetSelectEntryColor() ); + GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_SHDWCOLOR, + SfxCallMode::RECORD, { &aItem }); +} + +void SvxFontWorkDialog::ApplyImageList() +{ + if (m_sLastShadowTbxId == "slant") + { + m_xFbShadowX->set_from_icon_name(RID_SVXBMP_SHADOW_ANGLE); + m_xFbShadowY->set_from_icon_name(RID_SVXBMP_SHADOW_SIZE); + } + else + { + m_xFbShadowX->set_from_icon_name(RID_SVXBMP_SHADOW_XDIST); + m_xFbShadowY->set_from_icon_name(RID_SVXBMP_SHADOW_YDIST); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/framelink.cxx b/svx/source/dialog/framelink.cxx new file mode 100644 index 0000000000..887fc445dc --- /dev/null +++ b/svx/source/dialog/framelink.cxx @@ -0,0 +1,341 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <rtl/math.hxx> +#include <svx/framelink.hxx> + +#include <editeng/borderline.hxx> +#include <o3tl/hash_combine.hxx> + + +using namespace ::com::sun::star; +using namespace editeng; + +namespace svx::frame +{ + +Style::Style( double nP, double nD, double nS, SvxBorderLineStyle nType, double fScale ) +{ + Clear(); + mnType = nType; + mfPatternScale = fScale; + Set( nP, nD, nS ); +} + +Style::Style( const Color& rColorPrim, const Color& rColorSecn, const Color& rColorGap, bool bUseGapColor, double nP, double nD, double nS, SvxBorderLineStyle nType, double fScale ) +{ + Clear(); + mnType = nType; + mfPatternScale = fScale; + Set( rColorPrim, rColorSecn, rColorGap, bUseGapColor, nP, nD, nS ); +} + +Style::Style( const editeng::SvxBorderLine* pBorder, double fScale ) +{ + Clear(); + if(nullptr != pBorder) + { + mfPatternScale = fScale; + Set( pBorder, fScale ); + } +} + +void Style::Clear() +{ + maColorPrim = Color(); + maColorSecn = Color(); + maColorGap = Color(); + mbUseGapColor = false; + meRefMode = RefMode::Centered; + mfPrim = 0.0; + mfDist = 0.0; + mfSecn = 0.0; + mfPatternScale = 1.0; + mnType = SvxBorderLineStyle::SOLID; + mbWordTableCell = false; +} + +void Style::Set( double nP, double nD, double nS ) +{ + /* nP nD nS -> mfPrim mfDist mfSecn + -------------------------------------- + any any 0 nP 0 0 + 0 any >0 nS 0 0 + >0 0 >0 nP 0 0 + >0 >0 >0 nP nD nS + */ + mfPrim = rtl::math::round(nP ? nP : nS, 2); + mfDist = rtl::math::round((nP && nS) ? nD : 0, 2); + mfSecn = rtl::math::round((nP && nD) ? nS : 0, 2); +} + +void Style::Set( const Color& rColorPrim, const Color& rColorSecn, const Color& rColorGap, bool bUseGapColor, double nP, double nD, double nS ) +{ + maColorPrim = rColorPrim; + maColorSecn = rColorSecn; + maColorGap = rColorGap; + mbUseGapColor = bUseGapColor; + Set( nP, nD, nS ); +} + +void Style::Set( const SvxBorderLine* pBorder, double fScale, sal_uInt16 nMaxWidth ) +{ + if(nullptr == pBorder) + { + Clear(); + return; + } + + maColorPrim = pBorder->GetColorOut(); + maColorSecn = pBorder->GetColorIn(); + maColorGap = pBorder->GetColorGap(); + mbUseGapColor = pBorder->HasGapColor(); + + const sal_uInt16 nPrim(pBorder->GetOutWidth()); + const sal_uInt16 nDist(pBorder->GetDistance()); + const sal_uInt16 nSecn(pBorder->GetInWidth()); + + mnType = pBorder->GetBorderLineStyle(); + mfPatternScale = fScale; + + if( !nSecn ) // no or single frame border + { + Set( std::min<double>(nPrim * fScale, nMaxWidth), 0, 0 ); + } + else + { + Set(std::min<double>(nPrim * fScale, nMaxWidth), std::min<double>(nDist * fScale, nMaxWidth), std::min<double>(nSecn * fScale, nMaxWidth)); + // Enlarge the style if distance is too small due to rounding losses. + double nPixWidth = std::min<double>((nPrim + nDist + nSecn) * fScale, nMaxWidth); + + if( nPixWidth > GetWidth() ) + { + mfDist = nPixWidth - mfPrim - mfSecn; + } + + // Shrink the style if it is too thick for the control. + while( GetWidth() > nMaxWidth ) + { + // First decrease space between lines. + if (mfDist) + { + --mfDist; + continue; + } + + // Still too thick? Decrease the line widths. + if (mfPrim != 0.0 && rtl::math::approxEqual(mfPrim, mfSecn)) + { + // Both lines equal - decrease both to keep symmetry. + --mfPrim; + --mfSecn; + continue; + } + + // Decrease each line for itself + if (mfPrim) + --mfPrim; + + if ((GetWidth() > nMaxWidth) && mfSecn != 0.0) + --mfSecn; + } + } +} + +Style& Style::MirrorSelf() +{ + if (mfSecn) + { + std::swap( mfPrim, mfSecn ); + // also need to swap colors + std::swap( maColorPrim, maColorSecn ); + } + + if( meRefMode != RefMode::Centered ) + { + meRefMode = (meRefMode == RefMode::Begin) ? RefMode::End : RefMode::Begin; + } + + return *this; +} + +bool Style::operator==( const Style& rOther) const +{ + if (this == &rOther) + // ptr compare (same instance) + return true; + + return (Prim() == rOther.Prim() + && Dist() == rOther.Dist() + && Secn() == rOther.Secn() + && GetColorPrim() == rOther.GetColorPrim() + && GetColorSecn() == rOther.GetColorSecn() + && GetColorGap() == rOther.GetColorGap() + && GetRefMode() == rOther.GetRefMode() + && UseGapColor() == rOther.UseGapColor() + && Type() == rOther.Type()); +} + +size_t Style::hashCode() const +{ + std::size_t seed = 0; + o3tl::hash_combine(seed, Prim()); + o3tl::hash_combine(seed, Dist()); + o3tl::hash_combine(seed, Secn()); + o3tl::hash_combine(seed, static_cast<sal_Int32>(GetColorPrim())); + o3tl::hash_combine(seed, static_cast<sal_Int32>(GetColorSecn())); + o3tl::hash_combine(seed, static_cast<sal_Int32>(GetColorGap())); + o3tl::hash_combine(seed, GetRefMode()); + o3tl::hash_combine(seed, UseGapColor()); + o3tl::hash_combine(seed, Type()); + return seed; +} + + +namespace +{ +/** + * Gets the weight of rStyle, according to [MS-OI29500] v20171130, 2.1.168 Part 1 Section 17.4.66, + * tcBorders (Table Cell Borders). + */ +double GetWordTableCellBorderWeight(const Style& rStyle) +{ + double fWidth = rStyle.GetWidth(); + int nBorderNumber = 0; + + // See lcl_convertBorderStyleFromToken() in writerfilter/ and ConvertBorderStyleFromWord() in + // editeng/, this is the opposite of the combination of those functions. + switch (rStyle.Type()) + { + case SvxBorderLineStyle::NONE: + return 0.0; + case SvxBorderLineStyle::DOTTED: + case SvxBorderLineStyle::DASHED: + return 1.0; + case SvxBorderLineStyle::SOLID: + // single = 1 + // thick = 2 + // wave = 20 + nBorderNumber = 1; + break; + case SvxBorderLineStyle::DOUBLE: + case SvxBorderLineStyle::DOUBLE_THIN: + // double = 3 + // triple = 10 + // doubleWave = 21 + // dashDotStroked = 23 + nBorderNumber = 3; + break; + case SvxBorderLineStyle::DASH_DOT: + // dotDash = 8 + nBorderNumber = 8; + break; + case SvxBorderLineStyle::DASH_DOT_DOT: + // dotDotDash = 9 + nBorderNumber = 9; + break; + case SvxBorderLineStyle::THINTHICK_SMALLGAP: + // thinThickSmallGap = 11 + nBorderNumber = 11; + break; + case SvxBorderLineStyle::THICKTHIN_SMALLGAP: + // thickThinSmallGap = 12 + // thinThickThinSmallGap = 13 + nBorderNumber = 12; + break; + case SvxBorderLineStyle::THINTHICK_MEDIUMGAP: + // thinThickMediumGap = 14 + nBorderNumber = 14; + break; + case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP: + // thickThinMediumGap = 15 + // thinThickThinMediumGap = 16 + nBorderNumber = 15; + break; + case SvxBorderLineStyle::THINTHICK_LARGEGAP: + // thinThickLargeGap = 17 + nBorderNumber = 17; + break; + case SvxBorderLineStyle::THICKTHIN_LARGEGAP: + // thickThinLargeGap = 18 + // thinThickThinLargeGap = 19 + nBorderNumber = 18; + break; + case SvxBorderLineStyle::FINE_DASHED: + // dashSmallGap = 22 + nBorderNumber = 22; + break; + case SvxBorderLineStyle::EMBOSSED: + // threeDEmboss = 24 + nBorderNumber = 24; + break; + case SvxBorderLineStyle::ENGRAVED: + // threeDEngrave = 25 + nBorderNumber = 25; + break; + case SvxBorderLineStyle::OUTSET: + // outset = 26 + nBorderNumber = 25; + break; + case SvxBorderLineStyle::INSET: + // inset = 27 + nBorderNumber = 27; + break; + } + + return nBorderNumber * fWidth; +} +} + +bool Style::operator<( const Style& rOther) const +{ + if (mbWordTableCell) + { + // The below code would first compare based on the border width, Word compares based on its + // calculated weight, do that in the compat case. + double fLW = GetWordTableCellBorderWeight(*this); + double fRW = GetWordTableCellBorderWeight(rOther); + if (!rtl::math::approxEqual(fLW, fRW)) + { + return fLW < fRW; + } + } + + // different total widths -> this<rOther, if this is thinner + double nLW = GetWidth(); + double nRW = rOther.GetWidth(); + if( !rtl::math::approxEqual(nLW, nRW) ) return nLW < nRW; + + // one line double, the other single -> this<rOther, if this is single + if( (Secn() == 0) != (rOther.Secn() == 0) ) return Secn() == 0; + + // both lines double with different distances -> this<rOther, if distance of this greater + if( (Secn() && rOther.Secn()) && !rtl::math::approxEqual(Dist(), rOther.Dist()) ) return Dist() > rOther.Dist(); + + // both lines single and 1 unit thick, only one is dotted -> this<rOther, if this is dotted + if ((nLW == 1) && !Secn() && !rOther.Secn() && (Type() != rOther.Type())) return Type() > rOther.Type(); + + // seem to be equal + return false; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/framelinkarray.cxx b/svx/source/dialog/framelinkarray.cxx new file mode 100644 index 0000000000..fa58294386 --- /dev/null +++ b/svx/source/dialog/framelinkarray.cxx @@ -0,0 +1,1698 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/framelinkarray.hxx> + +#include <math.h> +#include <vector> +#include <set> +#include <unordered_set> +#include <algorithm> +#include <o3tl/hash_combine.hxx> +#include <tools/debug.hxx> +#include <tools/gen.hxx> +#include <vcl/canvastools.hxx> +#include <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> + +//#define OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL +#ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#endif + +namespace svx::frame { + +namespace { + +class Cell final +{ +private: + Style maLeft; + Style maRight; + Style maTop; + Style maBottom; + Style maTLBR; + Style maBLTR; + + basegfx::B2DHomMatrix HelperCreateB2DHomMatrixFromB2DRange( + const basegfx::B2DRange& rRange ) const; + +public: + sal_Int32 mnAddLeft; + sal_Int32 mnAddRight; + sal_Int32 mnAddTop; + sal_Int32 mnAddBottom; + + SvxRotateMode meRotMode; + double mfOrientation; + + bool mbMergeOrig; + bool mbOverlapX; + bool mbOverlapY; + +public: + explicit Cell(); + explicit Cell(const Cell&) = default; + + bool operator==( const Cell& ) const; + size_t hashCode() const; + + void SetStyleLeft(const Style& rStyle) { maLeft = rStyle; } + void SetStyleRight(const Style& rStyle) { maRight = rStyle; } + void SetStyleTop(const Style& rStyle) { maTop = rStyle; } + void SetStyleBottom(const Style& rStyle) { maBottom = rStyle; } + void SetStyleTLBR(const Style& rStyle) { maTLBR = rStyle; } + void SetStyleBLTR(const Style& rStyle) { maBLTR = rStyle; } + + const Style& GetStyleLeft() const { return maLeft; } + const Style& GetStyleRight() const { return maRight; } + const Style& GetStyleTop() const { return maTop; } + const Style& GetStyleBottom() const { return maBottom; } + const Style& GetStyleTLBR() const { return maTLBR; } + const Style& GetStyleBLTR() const { return maBLTR; } + + bool IsMerged() const { return mbMergeOrig || mbOverlapX || mbOverlapY; } + bool IsRotated() const { return mfOrientation != 0.0; } + + void MirrorSelfX(); + + basegfx::B2DHomMatrix CreateCoordinateSystemSingleCell( + const Array& rArray, sal_Int32 nCol, sal_Int32 nRow ) const; + basegfx::B2DHomMatrix CreateCoordinateSystemMergedCell( + const Array& rArray, sal_Int32 nColLeft, sal_Int32 nRowTop, sal_Int32 nColRight, sal_Int32 nRowBottom ) const; +}; + +} + +typedef std::vector< const Cell* > CellVec; + +basegfx::B2DHomMatrix Cell::HelperCreateB2DHomMatrixFromB2DRange( + const basegfx::B2DRange& rRange ) const +{ + if( rRange.isEmpty() ) + return basegfx::B2DHomMatrix(); + + basegfx::B2DPoint aOrigin(rRange.getMinimum()); + basegfx::B2DVector aX(rRange.getWidth(), 0.0); + basegfx::B2DVector aY(0.0, rRange.getHeight()); + + if (IsRotated() && SvxRotateMode::SVX_ROTATE_MODE_STANDARD != meRotMode ) + { + // tdf#143377 We need to limit applying Skew to geometry since the closer + // we get to 0.0 or PI the more sin(mfOrientation) will get to zero and the + // huger the Skew effect will be. For that, use an epsilon-radius of 1/2 + // degree around the dangerous points 0.0 and PI. + + // Snap to modulo to [0.0 .. 2PI[ to make compare easier + const double fSnapped(::basegfx::snapToZeroRange(mfOrientation, M_PI * 2.0)); + + // As a compromise, allow up to 1/2 degree + static const double fMinAng(M_PI/360.0); + + // Check if Skew makes sense or would be too huge + const bool bForbidSkew( + fSnapped < fMinAng || // range [0.0 .. fMinAng] + fSnapped > (M_PI * 2.0) - fMinAng || // range [PI-fMinAng .. 2PI[ + fabs(fSnapped - M_PI) < fMinAng); // range [PI-fMinAng .. PI+fMinAng] + + if(!bForbidSkew) + { + // when rotated, adapt values. Get Skew (cos/sin == 1/tan) + const double fSkew(aY.getY() * (cos(mfOrientation) / sin(mfOrientation))); + + switch (meRotMode) + { + case SvxRotateMode::SVX_ROTATE_MODE_TOP: + // shear Y-Axis + aY.setX(-fSkew); + break; + case SvxRotateMode::SVX_ROTATE_MODE_CENTER: + // shear origin half, Y full + aOrigin.setX(aOrigin.getX() + (fSkew * 0.5)); + aY.setX(-fSkew); + break; + case SvxRotateMode::SVX_ROTATE_MODE_BOTTOM: + // shear origin full, Y full + aOrigin.setX(aOrigin.getX() + fSkew); + aY.setX(-fSkew); + break; + default: // SvxRotateMode::SVX_ROTATE_MODE_STANDARD, already excluded above + break; + } + } + } + + // use column vectors as coordinate axes, homogen column for translation + return basegfx::utils::createCoordinateSystemTransform( aOrigin, aX, aY ); +} + +basegfx::B2DHomMatrix Cell::CreateCoordinateSystemSingleCell( + const Array& rArray, sal_Int32 nCol, sal_Int32 nRow) const +{ + const Point aPoint( rArray.GetColPosition( nCol ), rArray.GetRowPosition( nRow ) ); + const Size aSize( rArray.GetColWidth( nCol, nCol ) + 1, rArray.GetRowHeight( nRow, nRow ) + 1 ); + const basegfx::B2DRange aRange( vcl::unotools::b2DRectangleFromRectangle( tools::Rectangle( aPoint, aSize ) ) ); + + return HelperCreateB2DHomMatrixFromB2DRange( aRange ); +} + +basegfx::B2DHomMatrix Cell::CreateCoordinateSystemMergedCell( + const Array& rArray, sal_Int32 nColLeft, sal_Int32 nRowTop, sal_Int32 nColRight, sal_Int32 nRowBottom) const +{ + basegfx::B2DRange aRange( rArray.GetB2DRange( + nColLeft, nRowTop, nColRight, nRowBottom ) ); + + // adjust rectangle for partly visible merged cells + if( IsMerged() ) + { + // not *sure* what exactly this is good for, + // it is just a hard set extension at merged cells, + // probably *should* be included in the above extended + // GetColPosition/GetColWidth already. This might be + // added due to GetColPosition/GetColWidth not working + // correctly over PageChanges (if used), but not sure. + aRange.expand( + basegfx::B2DRange( + aRange.getMinX() - mnAddLeft, + aRange.getMinY() - mnAddTop, + aRange.getMaxX() + mnAddRight, + aRange.getMaxY() + mnAddBottom ) ); + } + + return HelperCreateB2DHomMatrixFromB2DRange( aRange ); +} + +Cell::Cell() : + mnAddLeft( 0 ), + mnAddRight( 0 ), + mnAddTop( 0 ), + mnAddBottom( 0 ), + meRotMode(SvxRotateMode::SVX_ROTATE_MODE_STANDARD ), + mfOrientation( 0.0 ), + mbMergeOrig( false ), + mbOverlapX( false ), + mbOverlapY( false ) +{ +} + +bool Cell::operator==(const Cell& rOther) const +{ + if (this == &rOther) + // ptr compare (same instance) + return true; + + return maLeft == rOther.maLeft + && maRight == rOther.maRight + && maTop == rOther.maTop + && maBottom == rOther.maBottom + && maTLBR == rOther.maTLBR + && maBLTR == rOther.maBLTR + && mnAddLeft == rOther.mnAddLeft + && mnAddRight == rOther.mnAddRight + && mnAddTop == rOther.mnAddTop + && mnAddBottom == rOther.mnAddBottom + && meRotMode == rOther.meRotMode + && mfOrientation == rOther.mfOrientation + && mbMergeOrig == rOther.mbMergeOrig + && mbOverlapX == rOther.mbOverlapX + && mbOverlapY == rOther.mbOverlapY; +} + +size_t Cell::hashCode() const +{ + std::size_t seed = 0; + o3tl::hash_combine(seed, maLeft.hashCode()); + o3tl::hash_combine(seed, maRight.hashCode()); + o3tl::hash_combine(seed, maTop.hashCode()); + o3tl::hash_combine(seed, maBottom.hashCode()); + o3tl::hash_combine(seed, maTLBR.hashCode()); + o3tl::hash_combine(seed, maBLTR.hashCode()); + o3tl::hash_combine(seed, mnAddLeft); + o3tl::hash_combine(seed, mnAddRight); + o3tl::hash_combine(seed, mnAddTop); + o3tl::hash_combine(seed, mnAddBottom); + o3tl::hash_combine(seed, meRotMode); + o3tl::hash_combine(seed, mfOrientation); + o3tl::hash_combine(seed, mbMergeOrig); + o3tl::hash_combine(seed, mbOverlapX); + o3tl::hash_combine(seed, mbOverlapY); + return seed; +} + +void Cell::MirrorSelfX() +{ + std::swap( maLeft, maRight ); + std::swap( mnAddLeft, mnAddRight ); + maLeft.MirrorSelf(); + maRight.MirrorSelf(); + mfOrientation = -mfOrientation; +} + + +static void lclRecalcCoordVec( std::vector<sal_Int32>& rCoords, const std::vector<sal_Int32>& rSizes ) +{ + DBG_ASSERT( rCoords.size() == rSizes.size() + 1, "lclRecalcCoordVec - inconsistent vectors" ); + auto aCIt = rCoords.begin(); + for( const auto& rSize : rSizes ) + { + *(aCIt + 1) = *aCIt + rSize; + ++aCIt; + } +} + +const Style OBJ_STYLE_NONE; +const Cell OBJ_CELL_NONE; + +/** use hashing to speed up finding duplicates */ +namespace +{ +struct RegisteredCellHash +{ + size_t operator()(Cell* const pCell) const + { + return pCell->hashCode(); + } +}; + +struct RegisteredCellEquals +{ + bool operator()(Cell* const pCell1, Cell* const pCell2) const + { + return *pCell1 == *pCell2; + } +}; +} + +struct ArrayImpl +{ + std::unordered_set<Cell*, RegisteredCellHash, RegisteredCellEquals> maRegisteredCells; + CellVec maCells; + std::vector<sal_Int32> maWidths; + std::vector<sal_Int32> maHeights; + mutable std::vector<sal_Int32> maXCoords; + mutable std::vector<sal_Int32> maYCoords; + sal_Int32 mnWidth; + sal_Int32 mnHeight; + sal_Int32 mnFirstClipCol; + sal_Int32 mnFirstClipRow; + sal_Int32 mnLastClipCol; + sal_Int32 mnLastClipRow; + mutable bool mbXCoordsDirty; + mutable bool mbYCoordsDirty; + bool mbMayHaveCellRotation; + + explicit ArrayImpl( sal_Int32 nWidth, sal_Int32 nHeight ); + ~ArrayImpl(); + + bool IsValidPos( sal_Int32 nCol, sal_Int32 nRow ) const + { return (nCol < mnWidth) && (nRow < mnHeight); } + sal_Int32 GetIndex( sal_Int32 nCol, sal_Int32 nRow ) const + { return nRow * mnWidth + nCol; } + + const Cell* GetCell( sal_Int32 nCol, sal_Int32 nRow ) const; + void PutCell( sal_Int32 nCol, sal_Int32 nRow, const Cell& ); + + sal_Int32 GetMergedFirstCol( sal_Int32 nCol, sal_Int32 nRow ) const; + sal_Int32 GetMergedFirstRow( sal_Int32 nCol, sal_Int32 nRow ) const; + sal_Int32 GetMergedLastCol( sal_Int32 nCol, sal_Int32 nRow ) const; + sal_Int32 GetMergedLastRow( sal_Int32 nCol, sal_Int32 nRow ) const; + + const Cell* GetMergedOriginCell( sal_Int32 nCol, sal_Int32 nRow ) const; + const Cell* GetMergedLastCell( sal_Int32 nCol, sal_Int32 nRow ) const; + + bool IsMergedOverlappedLeft( sal_Int32 nCol, sal_Int32 nRow ) const; + bool IsMergedOverlappedRight( sal_Int32 nCol, sal_Int32 nRow ) const; + bool IsMergedOverlappedTop( sal_Int32 nCol, sal_Int32 nRow ) const; + bool IsMergedOverlappedBottom( sal_Int32 nCol, sal_Int32 nRow ) const; + + bool IsInClipRange( sal_Int32 nCol, sal_Int32 nRow ) const; + bool IsColInClipRange( sal_Int32 nCol ) const; + bool IsRowInClipRange( sal_Int32 nRow ) const; + + bool OverlapsClipRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) const; + + sal_Int32 GetMirrorCol( sal_Int32 nCol ) const { return mnWidth - nCol - 1; } + + sal_Int32 GetColPosition( sal_Int32 nCol ) const; + sal_Int32 GetRowPosition( sal_Int32 nRow ) const; + + bool HasCellRotation() const; + + const Cell* createOrFind(const Cell& rCell); +}; + +static void lclSetMergedRange( ArrayImpl& rImpl, CellVec& rCells, sal_Int32 nWidth, sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) +{ + for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol ) + { + for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow ) + { + const Cell* pCell = rCells[ nRow * nWidth + nCol ]; + Cell aTempCell(*pCell); + aTempCell.mbMergeOrig = false; + aTempCell.mbOverlapX = nCol > nFirstCol; + aTempCell.mbOverlapY = nRow > nFirstRow; + rCells[ nRow * nWidth + nCol ] = rImpl.createOrFind(aTempCell); + } + } + Cell aTempCell(*rCells[ nFirstRow * nWidth + nFirstCol ]); + aTempCell.mbMergeOrig = true; + rCells[ nFirstRow * nWidth + nFirstCol ] = rImpl.createOrFind(aTempCell); +} + +ArrayImpl::ArrayImpl( sal_Int32 nWidth, sal_Int32 nHeight ) : + maRegisteredCells(), + mnWidth( nWidth ), + mnHeight( nHeight ), + mnFirstClipCol( 0 ), + mnFirstClipRow( 0 ), + mnLastClipCol( nWidth - 1 ), + mnLastClipRow( nHeight - 1 ), + mbXCoordsDirty( false ), + mbYCoordsDirty( false ), + mbMayHaveCellRotation( false ) +{ + const Cell* pDefaultCell = createOrFind(Cell()); + // default-construct all vectors + maCells.resize( mnWidth * mnHeight, pDefaultCell ); + maWidths.resize( mnWidth, 0 ); + maHeights.resize( mnHeight, 0 ); + maXCoords.resize( mnWidth + 1, 0 ); + maYCoords.resize( mnHeight + 1, 0 ); +} + +ArrayImpl::~ArrayImpl() +{ + for (auto* pCell : maRegisteredCells) + delete pCell; +} + +const Cell* ArrayImpl::createOrFind(const Cell& rCell) +{ + auto it = maRegisteredCells.find(const_cast<Cell*>(&rCell)); + if (it != maRegisteredCells.end()) + return *it; + + Cell* pRetval(new Cell(rCell)); + maRegisteredCells.insert(pRetval); + return pRetval; +} + +const Cell* ArrayImpl::GetCell( sal_Int32 nCol, sal_Int32 nRow ) const +{ + return IsValidPos( nCol, nRow ) ? maCells[ GetIndex( nCol, nRow ) ] : &OBJ_CELL_NONE; +} + +void ArrayImpl::PutCell( sal_Int32 nCol, sal_Int32 nRow, const Cell & rCell ) +{ + if (IsValidPos( nCol, nRow )) + maCells[ GetIndex( nCol, nRow ) ] = createOrFind(rCell); +} + +sal_Int32 ArrayImpl::GetMergedFirstCol( sal_Int32 nCol, sal_Int32 nRow ) const +{ + sal_Int32 nFirstCol = nCol; + while( (nFirstCol > 0) && GetCell( nFirstCol, nRow )->mbOverlapX ) --nFirstCol; + return nFirstCol; +} + +sal_Int32 ArrayImpl::GetMergedFirstRow( sal_Int32 nCol, sal_Int32 nRow ) const +{ + sal_Int32 nFirstRow = nRow; + while( (nFirstRow > 0) && GetCell( nCol, nFirstRow )->mbOverlapY ) --nFirstRow; + return nFirstRow; +} + +sal_Int32 ArrayImpl::GetMergedLastCol( sal_Int32 nCol, sal_Int32 nRow ) const +{ + sal_Int32 nLastCol = nCol + 1; + while( (nLastCol < mnWidth) && GetCell( nLastCol, nRow )->mbOverlapX ) ++nLastCol; + return nLastCol - 1; +} + +sal_Int32 ArrayImpl::GetMergedLastRow( sal_Int32 nCol, sal_Int32 nRow ) const +{ + sal_Int32 nLastRow = nRow + 1; + while( (nLastRow < mnHeight) && GetCell( nCol, nLastRow )->mbOverlapY ) ++nLastRow; + return nLastRow - 1; +} + +const Cell* ArrayImpl::GetMergedOriginCell( sal_Int32 nCol, sal_Int32 nRow ) const +{ + return GetCell( GetMergedFirstCol( nCol, nRow ), GetMergedFirstRow( nCol, nRow ) ); +} + +const Cell* ArrayImpl::GetMergedLastCell( sal_Int32 nCol, sal_Int32 nRow ) const +{ + return GetCell( GetMergedLastCol( nCol, nRow ), GetMergedLastRow( nCol, nRow ) ); +} + +bool ArrayImpl::IsMergedOverlappedLeft( sal_Int32 nCol, sal_Int32 nRow ) const +{ + const Cell* pCell(GetCell( nCol, nRow )); + return pCell->mbOverlapX || (pCell->mnAddLeft > 0); +} + +bool ArrayImpl::IsMergedOverlappedRight( sal_Int32 nCol, sal_Int32 nRow ) const +{ + return GetCell( nCol + 1, nRow )->mbOverlapX || (GetCell( nCol, nRow )->mnAddRight > 0); +} + +bool ArrayImpl::IsMergedOverlappedTop( sal_Int32 nCol, sal_Int32 nRow ) const +{ + const Cell* pCell(GetCell( nCol, nRow )); + return pCell->mbOverlapY || (pCell->mnAddTop > 0); +} + +bool ArrayImpl::IsMergedOverlappedBottom( sal_Int32 nCol, sal_Int32 nRow ) const +{ + return GetCell( nCol, nRow + 1 )->mbOverlapY || (GetCell( nCol, nRow )->mnAddBottom > 0); +} + +bool ArrayImpl::IsColInClipRange( sal_Int32 nCol ) const +{ + return (mnFirstClipCol <= nCol) && (nCol <= mnLastClipCol); +} + +bool ArrayImpl::IsRowInClipRange( sal_Int32 nRow ) const +{ + return (mnFirstClipRow <= nRow) && (nRow <= mnLastClipRow); +} + +bool ArrayImpl::OverlapsClipRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) const +{ + if(nLastCol < mnFirstClipCol) + return false; + + if(nFirstCol > mnLastClipCol) + return false; + + if(nLastRow < mnFirstClipRow) + return false; + + if(nFirstRow > mnLastClipRow) + return false; + + return true; +} + +bool ArrayImpl::IsInClipRange( sal_Int32 nCol, sal_Int32 nRow ) const +{ + return IsColInClipRange( nCol ) && IsRowInClipRange( nRow ); +} + +sal_Int32 ArrayImpl::GetColPosition( sal_Int32 nCol ) const +{ + if( mbXCoordsDirty ) + { + lclRecalcCoordVec( maXCoords, maWidths ); + mbXCoordsDirty = false; + } + return maXCoords[ nCol ]; +} + +sal_Int32 ArrayImpl::GetRowPosition( sal_Int32 nRow ) const +{ + if( mbYCoordsDirty ) + { + lclRecalcCoordVec( maYCoords, maHeights ); + mbYCoordsDirty = false; + } + return maYCoords[ nRow ]; +} + +bool ArrayImpl::HasCellRotation() const +{ + // check cell array + for (const auto& aCell : maCells) + { + if (aCell->IsRotated()) + { + return true; + } + } + + return false; +} + +namespace { + +class MergedCellIterator +{ +public: + explicit MergedCellIterator( const Array& rArray, sal_Int32 nCol, sal_Int32 nRow ); + + bool Is() const { return (mnCol <= mnLastCol) && (mnRow <= mnLastRow); } + sal_Int32 Col() const { return mnCol; } + sal_Int32 Row() const { return mnRow; } + + MergedCellIterator& operator++(); + +private: + sal_Int32 mnFirstCol; + sal_Int32 mnFirstRow; + sal_Int32 mnLastCol; + sal_Int32 mnLastRow; + sal_Int32 mnCol; + sal_Int32 mnRow; +}; + +} + +MergedCellIterator::MergedCellIterator( const Array& rArray, sal_Int32 nCol, sal_Int32 nRow ) +{ + rArray.GetMergedRange( mnFirstCol, mnFirstRow, mnLastCol, mnLastRow, nCol, nRow ); + mnCol = mnFirstCol; + mnRow = mnFirstRow; +} + +MergedCellIterator& MergedCellIterator::operator++() +{ + DBG_ASSERT( Is(), "svx::frame::MergedCellIterator::operator++() - already invalid" ); + if( ++mnCol > mnLastCol ) + { + mnCol = mnFirstCol; + ++mnRow; + } + return *this; +} + +#define DBG_FRAME_CHECK( cond, funcname, error ) DBG_ASSERT( cond, "svx::frame::Array::" funcname " - " error ) +#define DBG_FRAME_CHECK_COL( col, funcname ) DBG_FRAME_CHECK( (col) < GetColCount(), funcname, "invalid column index" ) +#define DBG_FRAME_CHECK_ROW( row, funcname ) DBG_FRAME_CHECK( (row) < GetRowCount(), funcname, "invalid row index" ) +#define DBG_FRAME_CHECK_COLROW( col, row, funcname ) DBG_FRAME_CHECK( ((col) < GetColCount()) && ((row) < GetRowCount()), funcname, "invalid cell index" ) +#define DBG_FRAME_CHECK_COL_1( col, funcname ) DBG_FRAME_CHECK( (col) <= GetColCount(), funcname, "invalid column index" ) +#define DBG_FRAME_CHECK_ROW_1( row, funcname ) DBG_FRAME_CHECK( (row) <= GetRowCount(), funcname, "invalid row index" ) + +Array::Array() +{ + Initialize( 0, 0 ); +} + +Array::~Array() +{ +} + +// array size and column/row indexes +void Array::Initialize( sal_Int32 nWidth, sal_Int32 nHeight ) +{ + mxImpl.reset( new ArrayImpl( nWidth, nHeight ) ); +} + +sal_Int32 Array::GetColCount() const +{ + return mxImpl->mnWidth; +} + +sal_Int32 Array::GetRowCount() const +{ + return mxImpl->mnHeight; +} + +sal_Int32 Array::GetCellCount() const +{ + return mxImpl->maCells.size(); +} + +sal_Int32 Array::GetCellIndex( sal_Int32 nCol, sal_Int32 nRow, bool bRTL ) const +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetCellIndex" ); + if (bRTL) + nCol = mxImpl->GetMirrorCol(nCol); + return mxImpl->GetIndex( nCol, nRow ); +} + +// cell border styles +void Array::SetCellStyleLeft( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleLeft" ); + const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); + if (pTempCell->GetStyleLeft() == rStyle) + return; + Cell aTempCell(*pTempCell); + aTempCell.SetStyleLeft(rStyle); + mxImpl->PutCell( nCol, nRow, aTempCell ); +} + +void Array::SetCellStyleRight( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleRight" ); + const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); + if (pTempCell->GetStyleRight() == rStyle) + return; + Cell aTempCell(*pTempCell); + aTempCell.SetStyleRight(rStyle); + mxImpl->PutCell( nCol, nRow, aTempCell ); +} + +void Array::SetCellStyleTop( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleTop" ); + const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); + if (pTempCell->GetStyleTop() == rStyle) + return; + Cell aTempCell(*pTempCell); + aTempCell.SetStyleTop(rStyle); + mxImpl->PutCell( nCol, nRow, aTempCell ); +} + +void Array::SetCellStyleBottom( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleBottom" ); + const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); + if (pTempCell->GetStyleBottom() == rStyle) + return; + Cell aTempCell(*pTempCell); + aTempCell.SetStyleBottom(rStyle); + mxImpl->PutCell( nCol, nRow, aTempCell ); +} + +void Array::SetCellStyleTLBR( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleTLBR" ); + const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); + if (pTempCell->GetStyleTLBR() == rStyle) + return; + Cell aTempCell(*pTempCell); + aTempCell.SetStyleTLBR(rStyle); + mxImpl->PutCell( nCol, nRow, aTempCell ); +} + +void Array::SetCellStyleBLTR( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleBLTR" ); + const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); + if (pTempCell->GetStyleBLTR() == rStyle) + return; + Cell aTempCell(*pTempCell); + aTempCell.SetStyleBLTR(rStyle); + mxImpl->PutCell( nCol, nRow, aTempCell ); +} + +void Array::SetCellStyleDiag( sal_Int32 nCol, sal_Int32 nRow, const Style& rTLBR, const Style& rBLTR ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleDiag" ); + const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); + if (pTempCell->GetStyleTLBR() == rTLBR && pTempCell->GetStyleBLTR() == rBLTR) + return; + Cell aTempCell(*pTempCell); + aTempCell.SetStyleTLBR(rTLBR); + aTempCell.SetStyleBLTR(rBLTR); + mxImpl->PutCell( nCol, nRow, aTempCell ); +} + +void Array::SetColumnStyleLeft( sal_Int32 nCol, const Style& rStyle ) +{ + DBG_FRAME_CHECK_COL( nCol, "SetColumnStyleLeft" ); + for( sal_Int32 nRow = 0; nRow < mxImpl->mnHeight; ++nRow ) + SetCellStyleLeft( nCol, nRow, rStyle ); +} + +void Array::SetColumnStyleRight( sal_Int32 nCol, const Style& rStyle ) +{ + DBG_FRAME_CHECK_COL( nCol, "SetColumnStyleRight" ); + for( sal_Int32 nRow = 0; nRow < mxImpl->mnHeight; ++nRow ) + SetCellStyleRight( nCol, nRow, rStyle ); +} + +void Array::SetRowStyleTop( sal_Int32 nRow, const Style& rStyle ) +{ + DBG_FRAME_CHECK_ROW( nRow, "SetRowStyleTop" ); + for( sal_Int32 nCol = 0; nCol < mxImpl->mnWidth; ++nCol ) + SetCellStyleTop( nCol, nRow, rStyle ); +} + +void Array::SetRowStyleBottom( sal_Int32 nRow, const Style& rStyle ) +{ + DBG_FRAME_CHECK_ROW( nRow, "SetRowStyleBottom" ); + for( sal_Int32 nCol = 0; nCol < mxImpl->mnWidth; ++nCol ) + SetCellStyleBottom( nCol, nRow, rStyle ); +} + +void Array::SetCellRotation(sal_Int32 nCol, sal_Int32 nRow, SvxRotateMode eRotMode, double fOrientation) +{ + DBG_FRAME_CHECK_COLROW(nCol, nRow, "SetCellRotation"); + const Cell* pTempCell(mxImpl->GetCell(nCol, nRow)); + if (pTempCell->meRotMode == eRotMode && pTempCell->mfOrientation == fOrientation) + return; + Cell aTempCell(*pTempCell); + aTempCell.meRotMode = eRotMode; + aTempCell.mfOrientation = fOrientation; + mxImpl->PutCell( nCol, nRow, aTempCell ); + + if (!mxImpl->mbMayHaveCellRotation) + { + // activate once when a cell gets actually rotated to allow fast + // answering HasCellRotation() calls + mxImpl->mbMayHaveCellRotation = aTempCell.IsRotated(); + } +} + +bool Array::HasCellRotation() const +{ + if (!mxImpl->mbMayHaveCellRotation) + { + // never set, no need to check + return false; + } + + return mxImpl->HasCellRotation(); +} + +const Style& Array::GetCellStyleLeft( sal_Int32 nCol, sal_Int32 nRow ) const +{ + // outside clipping rows or overlapped in merged cells: invisible + if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedLeft( nCol, nRow ) ) + return OBJ_STYLE_NONE; + // left clipping border: always own left style + if( nCol == mxImpl->mnFirstClipCol ) + return mxImpl->GetMergedOriginCell( nCol, nRow )->GetStyleLeft(); + // right clipping border: always right style of left neighbor cell + if( nCol == mxImpl->mnLastClipCol + 1 ) + return mxImpl->GetMergedOriginCell( nCol - 1, nRow )->GetStyleRight(); + // outside clipping columns: invisible + if( !mxImpl->IsColInClipRange( nCol ) ) + return OBJ_STYLE_NONE; + // inside clipping range: maximum of own left style and right style of left neighbor cell + return std::max( mxImpl->GetMergedOriginCell( nCol, nRow )->GetStyleLeft(), mxImpl->GetMergedOriginCell( nCol - 1, nRow )->GetStyleRight() ); +} + +const Style& Array::GetCellStyleRight( sal_Int32 nCol, sal_Int32 nRow ) const +{ + // outside clipping rows or overlapped in merged cells: invisible + if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedRight( nCol, nRow ) ) + return OBJ_STYLE_NONE; + // left clipping border: always left style of right neighbor cell + if( nCol + 1 == mxImpl->mnFirstClipCol ) + return mxImpl->GetMergedOriginCell( nCol + 1, nRow )->GetStyleLeft(); + // right clipping border: always own right style + if( nCol == mxImpl->mnLastClipCol ) + return mxImpl->GetMergedLastCell( nCol, nRow )->GetStyleRight(); + // outside clipping columns: invisible + if( !mxImpl->IsColInClipRange( nCol ) ) + return OBJ_STYLE_NONE; + // inside clipping range: maximum of own right style and left style of right neighbor cell + return std::max( mxImpl->GetMergedOriginCell( nCol, nRow )->GetStyleRight(), mxImpl->GetMergedOriginCell( nCol + 1, nRow )->GetStyleLeft() ); +} + +const Style& Array::GetCellStyleTop( sal_Int32 nCol, sal_Int32 nRow ) const +{ + // outside clipping columns or overlapped in merged cells: invisible + if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedTop( nCol, nRow ) ) + return OBJ_STYLE_NONE; + // top clipping border: always own top style + if( nRow == mxImpl->mnFirstClipRow ) + return mxImpl->GetMergedOriginCell( nCol, nRow )->GetStyleTop(); + // bottom clipping border: always bottom style of top neighbor cell + if( nRow == mxImpl->mnLastClipRow + 1 ) + return mxImpl->GetMergedOriginCell( nCol, nRow - 1 )->GetStyleBottom(); + // outside clipping rows: invisible + if( !mxImpl->IsRowInClipRange( nRow ) ) + return OBJ_STYLE_NONE; + // inside clipping range: maximum of own top style and bottom style of top neighbor cell + return std::max( mxImpl->GetMergedOriginCell( nCol, nRow )->GetStyleTop(), mxImpl->GetMergedOriginCell( nCol, nRow - 1 )->GetStyleBottom() ); +} + +const Style& Array::GetCellStyleBottom( sal_Int32 nCol, sal_Int32 nRow ) const +{ + // outside clipping columns or overlapped in merged cells: invisible + if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedBottom( nCol, nRow ) ) + return OBJ_STYLE_NONE; + // top clipping border: always top style of bottom neighbor cell + if( nRow + 1 == mxImpl->mnFirstClipRow ) + return mxImpl->GetMergedOriginCell( nCol, nRow + 1 )->GetStyleTop(); + // bottom clipping border: always own bottom style + if( nRow == mxImpl->mnLastClipRow ) + return mxImpl->GetMergedLastCell( nCol, nRow )->GetStyleBottom(); + // outside clipping rows: invisible + if( !mxImpl->IsRowInClipRange( nRow ) ) + return OBJ_STYLE_NONE; + // inside clipping range: maximum of own bottom style and top style of bottom neighbor cell + return std::max( mxImpl->GetMergedOriginCell( nCol, nRow )->GetStyleBottom(), mxImpl->GetMergedOriginCell( nCol, nRow + 1 )->GetStyleTop() ); +} + +const Style& Array::GetCellStyleTLBR( sal_Int32 nCol, sal_Int32 nRow ) const +{ + return mxImpl->GetCell( nCol, nRow )->GetStyleTLBR(); +} + +const Style& Array::GetCellStyleBLTR( sal_Int32 nCol, sal_Int32 nRow ) const +{ + return mxImpl->GetCell( nCol, nRow )->GetStyleBLTR(); +} + +const Style& Array::GetCellStyleTL( sal_Int32 nCol, sal_Int32 nRow ) const +{ + // not in clipping range: always invisible + if( !mxImpl->IsInClipRange( nCol, nRow ) ) + return OBJ_STYLE_NONE; + // return style only for top-left cell + sal_Int32 nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow ); + sal_Int32 nFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow ); + return ((nCol == nFirstCol) && (nRow == nFirstRow)) ? + mxImpl->GetCell( nFirstCol, nFirstRow )->GetStyleTLBR() : OBJ_STYLE_NONE; +} + +const Style& Array::GetCellStyleBR( sal_Int32 nCol, sal_Int32 nRow ) const +{ + // not in clipping range: always invisible + if( !mxImpl->IsInClipRange( nCol, nRow ) ) + return OBJ_STYLE_NONE; + // return style only for bottom-right cell + sal_Int32 nLastCol = mxImpl->GetMergedLastCol( nCol, nRow ); + sal_Int32 nLastRow = mxImpl->GetMergedLastRow( nCol, nRow ); + return ((nCol == nLastCol) && (nRow == nLastRow)) ? + mxImpl->GetCell( mxImpl->GetMergedFirstCol( nCol, nRow ), mxImpl->GetMergedFirstRow( nCol, nRow ) )->GetStyleTLBR() : OBJ_STYLE_NONE; +} + +const Style& Array::GetCellStyleBL( sal_Int32 nCol, sal_Int32 nRow ) const +{ + // not in clipping range: always invisible + if( !mxImpl->IsInClipRange( nCol, nRow ) ) + return OBJ_STYLE_NONE; + // return style only for bottom-left cell + sal_Int32 nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow ); + sal_Int32 nLastRow = mxImpl->GetMergedLastRow( nCol, nRow ); + return ((nCol == nFirstCol) && (nRow == nLastRow)) ? + mxImpl->GetCell( nFirstCol, mxImpl->GetMergedFirstRow( nCol, nRow ) )->GetStyleBLTR() : OBJ_STYLE_NONE; +} + +const Style& Array::GetCellStyleTR( sal_Int32 nCol, sal_Int32 nRow ) const +{ + // not in clipping range: always invisible + if( !mxImpl->IsInClipRange( nCol, nRow ) ) + return OBJ_STYLE_NONE; + // return style only for top-right cell + sal_Int32 nFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow ); + sal_Int32 nLastCol = mxImpl->GetMergedLastCol( nCol, nRow ); + return ((nCol == nLastCol) && (nRow == nFirstRow)) ? + mxImpl->GetCell( mxImpl->GetMergedFirstCol( nCol, nRow ), nFirstRow )->GetStyleBLTR() : OBJ_STYLE_NONE; +} + +// cell merging +void Array::SetMergedRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) +{ + DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "SetMergedRange" ); + DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "SetMergedRange" ); +#if OSL_DEBUG_LEVEL >= 2 + { + bool bFound = false; + for( sal_Int32 nCurrCol = nFirstCol; !bFound && (nCurrCol <= nLastCol); ++nCurrCol ) + for( sal_Int32 nCurrRow = nFirstRow; !bFound && (nCurrRow <= nLastRow); ++nCurrRow ) + bFound = mxImpl->GetCell( nCurrCol, nCurrRow )->IsMerged(); + DBG_FRAME_CHECK( !bFound, "SetMergedRange", "overlapping merged ranges" ); + } +#endif + if( mxImpl->IsValidPos( nFirstCol, nFirstRow ) && mxImpl->IsValidPos( nLastCol, nLastRow ) ) + lclSetMergedRange( *mxImpl, mxImpl->maCells, mxImpl->mnWidth, nFirstCol, nFirstRow, nLastCol, nLastRow ); +} + +void Array::SetAddMergedLeftSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedLeftSize" ); + DBG_FRAME_CHECK( mxImpl->GetMergedFirstCol( nCol, nRow ) == 0, "SetAddMergedLeftSize", "additional border inside array" ); + for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt ) + { + const Cell* pTempCell(mxImpl->GetCell(aIt.Col(), aIt.Row())); + if (pTempCell->mnAddLeft == nAddSize) + return; + Cell aTempCell(*pTempCell); + aTempCell.mnAddLeft = nAddSize; + mxImpl->PutCell( nCol, nRow, aTempCell ); + } +} + +void Array::SetAddMergedRightSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedRightSize" ); + DBG_FRAME_CHECK( mxImpl->GetMergedLastCol( nCol, nRow ) + 1 == mxImpl->mnWidth, "SetAddMergedRightSize", "additional border inside array" ); + for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt ) + { + const Cell* pTempCell(mxImpl->GetCell(aIt.Col(), aIt.Row())); + if (pTempCell->mnAddRight == nAddSize) + return; + Cell aTempCell(*pTempCell); + aTempCell.mnAddRight = nAddSize; + mxImpl->PutCell( nCol, nRow, aTempCell ); + } +} + +void Array::SetAddMergedTopSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedTopSize" ); + DBG_FRAME_CHECK( mxImpl->GetMergedFirstRow( nCol, nRow ) == 0, "SetAddMergedTopSize", "additional border inside array" ); + for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt ) + { + const Cell* pTempCell(mxImpl->GetCell(aIt.Col(), aIt.Row())); + if (pTempCell->mnAddTop == nAddSize) + return; + Cell aTempCell(*pTempCell); + aTempCell.mnAddTop = nAddSize; + mxImpl->PutCell( nCol, nRow, aTempCell ); + } +} + +void Array::SetAddMergedBottomSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize ) +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedBottomSize" ); + DBG_FRAME_CHECK( mxImpl->GetMergedLastRow( nCol, nRow ) + 1 == mxImpl->mnHeight, "SetAddMergedBottomSize", "additional border inside array" ); + for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt ) + { + const Cell* pTempCell(mxImpl->GetCell(aIt.Col(), aIt.Row())); + if (pTempCell->mnAddBottom == nAddSize) + return; + Cell aTempCell(*pTempCell); + aTempCell.mnAddBottom = nAddSize; + mxImpl->PutCell( nCol, nRow, aTempCell ); + } +} + +bool Array::IsMerged( sal_Int32 nCol, sal_Int32 nRow ) const +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMerged" ); + return mxImpl->GetCell( nCol, nRow )->IsMerged(); +} + +void Array::GetMergedOrigin( sal_Int32& rnFirstCol, sal_Int32& rnFirstRow, sal_Int32 nCol, sal_Int32 nRow ) const +{ + DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetMergedOrigin" ); + rnFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow ); + rnFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow ); +} + +void Array::GetMergedRange( sal_Int32& rnFirstCol, sal_Int32& rnFirstRow, + sal_Int32& rnLastCol, sal_Int32& rnLastRow, sal_Int32 nCol, sal_Int32 nRow ) const +{ + GetMergedOrigin( rnFirstCol, rnFirstRow, nCol, nRow ); + rnLastCol = mxImpl->GetMergedLastCol( nCol, nRow ); + rnLastRow = mxImpl->GetMergedLastRow( nCol, nRow ); +} + +// clipping +void Array::SetClipRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) +{ + DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "SetClipRange" ); + DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "SetClipRange" ); + mxImpl->mnFirstClipCol = nFirstCol; + mxImpl->mnFirstClipRow = nFirstRow; + mxImpl->mnLastClipCol = nLastCol; + mxImpl->mnLastClipRow = nLastRow; +} + +// cell coordinates +void Array::SetXOffset( sal_Int32 nXOffset ) +{ + mxImpl->maXCoords[ 0 ] = nXOffset; + mxImpl->mbXCoordsDirty = true; +} + +void Array::SetYOffset( sal_Int32 nYOffset ) +{ + mxImpl->maYCoords[ 0 ] = nYOffset; + mxImpl->mbYCoordsDirty = true; +} + +void Array::SetColWidth( sal_Int32 nCol, sal_Int32 nWidth ) +{ + DBG_FRAME_CHECK_COL( nCol, "SetColWidth" ); + mxImpl->maWidths[ nCol ] = nWidth; + mxImpl->mbXCoordsDirty = true; +} + +void Array::SetRowHeight( sal_Int32 nRow, sal_Int32 nHeight ) +{ + DBG_FRAME_CHECK_ROW( nRow, "SetRowHeight" ); + mxImpl->maHeights[ nRow ] = nHeight; + mxImpl->mbYCoordsDirty = true; +} + +void Array::SetAllColWidths( sal_Int32 nWidth ) +{ + std::fill( mxImpl->maWidths.begin(), mxImpl->maWidths.end(), nWidth ); + mxImpl->mbXCoordsDirty = true; +} + +void Array::SetAllRowHeights( sal_Int32 nHeight ) +{ + std::fill( mxImpl->maHeights.begin(), mxImpl->maHeights.end(), nHeight ); + mxImpl->mbYCoordsDirty = true; +} + +sal_Int32 Array::GetColPosition( sal_Int32 nCol ) const +{ + DBG_FRAME_CHECK_COL_1( nCol, "GetColPosition" ); + return mxImpl->GetColPosition( nCol ); +} + +sal_Int32 Array::GetRowPosition( sal_Int32 nRow ) const +{ + DBG_FRAME_CHECK_ROW_1( nRow, "GetRowPosition" ); + return mxImpl->GetRowPosition( nRow ); +} + +sal_Int32 Array::GetColWidth( sal_Int32 nFirstCol, sal_Int32 nLastCol ) const +{ + DBG_FRAME_CHECK_COL( nFirstCol, "GetColWidth" ); + DBG_FRAME_CHECK_COL( nLastCol, "GetColWidth" ); + return GetColPosition( nLastCol + 1 ) - GetColPosition( nFirstCol ); +} + +sal_Int32 Array::GetRowHeight( sal_Int32 nFirstRow, sal_Int32 nLastRow ) const +{ + DBG_FRAME_CHECK_ROW( nFirstRow, "GetRowHeight" ); + DBG_FRAME_CHECK_ROW( nLastRow, "GetRowHeight" ); + return GetRowPosition( nLastRow + 1 ) - GetRowPosition( nFirstRow ); +} + +sal_Int32 Array::GetWidth() const +{ + return GetColPosition( mxImpl->mnWidth ) - GetColPosition( 0 ); +} + +sal_Int32 Array::GetHeight() const +{ + return GetRowPosition( mxImpl->mnHeight ) - GetRowPosition( 0 ); +} + +basegfx::B2DRange Array::GetCellRange( sal_Int32 nCol, sal_Int32 nRow ) const +{ + // get the Range of the fully expanded cell (if merged) + const sal_Int32 nFirstCol(mxImpl->GetMergedFirstCol( nCol, nRow )); + const sal_Int32 nFirstRow(mxImpl->GetMergedFirstRow( nCol, nRow )); + const sal_Int32 nLastCol(mxImpl->GetMergedLastCol( nCol, nRow )); + const sal_Int32 nLastRow(mxImpl->GetMergedLastRow( nCol, nRow )); + const Point aPoint( GetColPosition( nFirstCol ), GetRowPosition( nFirstRow ) ); + const Size aSize( GetColWidth( nFirstCol, nLastCol ) + 1, GetRowHeight( nFirstRow, nLastRow ) + 1 ); + tools::Rectangle aRect(aPoint, aSize); + + // adjust rectangle for partly visible merged cells + const Cell* pCell(mxImpl->GetCell( nCol, nRow )); + + if( pCell->IsMerged() ) + { + // not *sure* what exactly this is good for, + // it is just a hard set extension at merged cells, + // probably *should* be included in the above extended + // GetColPosition/GetColWidth already. This might be + // added due to GetColPosition/GetColWidth not working + // correctly over PageChanges (if used), but not sure. + aRect.AdjustLeft( -(pCell->mnAddLeft) ); + aRect.AdjustRight(pCell->mnAddRight ); + aRect.AdjustTop( -(pCell->mnAddTop) ); + aRect.AdjustBottom(pCell->mnAddBottom ); + } + + return vcl::unotools::b2DRectangleFromRectangle(aRect); +} + +// return output range of given row/col range in logical coordinates +basegfx::B2DRange Array::GetB2DRange(sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow) const +{ + const Point aPoint( GetColPosition( nFirstCol ), GetRowPosition( nFirstRow ) ); + const Size aSize( GetColWidth( nFirstCol, nLastCol ) + 1, GetRowHeight( nFirstRow, nLastRow ) + 1 ); + + return vcl::unotools::b2DRectangleFromRectangle(tools::Rectangle(aPoint, aSize)); +} + +// mirroring +void Array::MirrorSelfX() +{ + CellVec aNewCells; + aNewCells.reserve( GetCellCount() ); + + sal_Int32 nCol, nRow; + for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow ) + { + for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol ) + { + Cell aTempCell(*mxImpl->GetCell(mxImpl->GetMirrorCol( nCol ), nRow)); + aTempCell.MirrorSelfX(); + aNewCells.push_back( mxImpl->createOrFind(aTempCell) ); + } + } + for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow ) + { + for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol ) + { + if( mxImpl->GetCell( nCol, nRow )->mbMergeOrig ) + { + sal_Int32 nLastCol = mxImpl->GetMergedLastCol( nCol, nRow ); + sal_Int32 nLastRow = mxImpl->GetMergedLastRow( nCol, nRow ); + lclSetMergedRange( *mxImpl, aNewCells, mxImpl->mnWidth, + mxImpl->GetMirrorCol( nLastCol ), nRow, + mxImpl->GetMirrorCol( nCol ), nLastRow ); + } + } + } + mxImpl->maCells.swap( aNewCells ); + + std::reverse( mxImpl->maWidths.begin(), mxImpl->maWidths.end() ); + mxImpl->mbXCoordsDirty = true; +} + +// drawing +static void HelperCreateHorizontalEntry( + const Array& rArray, + const Style& rStyle, + sal_Int32 col, + sal_Int32 row, + const basegfx::B2DPoint& rOrigin, + const basegfx::B2DVector& rX, + const basegfx::B2DVector& rY, + drawinglayer::primitive2d::SdrFrameBorderDataVector& rData, + bool bUpper, + const Color* pForceColor) +{ + // prepare SdrFrameBorderData + rData.emplace_back( + bUpper ? rOrigin : basegfx::B2DPoint(rOrigin + rY), + rX, + rStyle, + pForceColor); + drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back()); + + // get involved styles at start + const Style& rStartFromTR(rArray.GetCellStyleBL( col, row - 1 )); + const Style& rStartLFromT(rArray.GetCellStyleLeft( col, row - 1 )); + const Style& rStartLFromL(rArray.GetCellStyleTop( col - 1, row )); + const Style& rStartLFromB(rArray.GetCellStyleLeft( col, row )); + const Style& rStartFromBR(rArray.GetCellStyleTL( col, row )); + + rInstance.addSdrConnectStyleData(true, rStartFromTR, rX - rY, false); + rInstance.addSdrConnectStyleData(true, rStartLFromT, -rY, true); + rInstance.addSdrConnectStyleData(true, rStartLFromL, -rX, true); + rInstance.addSdrConnectStyleData(true, rStartLFromB, rY, false); + rInstance.addSdrConnectStyleData(true, rStartFromBR, rX + rY, false); + + // get involved styles at end + const Style& rEndFromTL(rArray.GetCellStyleBR( col, row - 1 )); + const Style& rEndRFromT(rArray.GetCellStyleRight( col, row - 1 )); + const Style& rEndRFromR(rArray.GetCellStyleTop( col + 1, row )); + const Style& rEndRFromB(rArray.GetCellStyleRight( col, row )); + const Style& rEndFromBL(rArray.GetCellStyleTR( col, row )); + + rInstance.addSdrConnectStyleData(false, rEndFromTL, -rX - rY, true); + rInstance.addSdrConnectStyleData(false, rEndRFromT, -rY, true); + rInstance.addSdrConnectStyleData(false, rEndRFromR, rX, false); + rInstance.addSdrConnectStyleData(false, rEndRFromB, rY, false); + rInstance.addSdrConnectStyleData(false, rEndFromBL, rY - rX, true); +} + +static void HelperCreateVerticalEntry( + const Array& rArray, + const Style& rStyle, + sal_Int32 col, + sal_Int32 row, + const basegfx::B2DPoint& rOrigin, + const basegfx::B2DVector& rX, + const basegfx::B2DVector& rY, + drawinglayer::primitive2d::SdrFrameBorderDataVector& rData, + bool bLeft, + const Color* pForceColor) +{ + // prepare SdrFrameBorderData + rData.emplace_back( + bLeft ? rOrigin : basegfx::B2DPoint(rOrigin + rX), + rY, + rStyle, + pForceColor); + drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back()); + + // get involved styles at start + const Style& rStartFromBL(rArray.GetCellStyleTR( col - 1, row )); + const Style& rStartTFromL(rArray.GetCellStyleTop( col - 1, row )); + const Style& rStartTFromT(rArray.GetCellStyleLeft( col, row - 1 )); + const Style& rStartTFromR(rArray.GetCellStyleTop( col, row )); + const Style& rStartFromBR(rArray.GetCellStyleTL( col, row )); + + rInstance.addSdrConnectStyleData(true, rStartFromBR, rX + rY, false); + rInstance.addSdrConnectStyleData(true, rStartTFromR, rX, false); + rInstance.addSdrConnectStyleData(true, rStartTFromT, -rY, true); + rInstance.addSdrConnectStyleData(true, rStartTFromL, -rX, true); + rInstance.addSdrConnectStyleData(true, rStartFromBL, rY - rX, true); + + // get involved styles at end + const Style& rEndFromTL(rArray.GetCellStyleBR( col - 1, row )); + const Style& rEndBFromL(rArray.GetCellStyleBottom( col - 1, row )); + const Style& rEndBFromB(rArray.GetCellStyleLeft( col, row + 1 )); + const Style& rEndBFromR(rArray.GetCellStyleBottom( col, row )); + const Style& rEndFromTR(rArray.GetCellStyleBL( col, row )); + + rInstance.addSdrConnectStyleData(false, rEndFromTR, rX - rY, false); + rInstance.addSdrConnectStyleData(false, rEndBFromR, rX, false); + rInstance.addSdrConnectStyleData(false, rEndBFromB, rY, false); + rInstance.addSdrConnectStyleData(false, rEndBFromL, -rX, true); + rInstance.addSdrConnectStyleData(false, rEndFromTL, -rY - rX, true); +} + +static void HelperClipLine( + basegfx::B2DPoint& rStart, + basegfx::B2DVector& rDirection, + const basegfx::B2DRange& rClipRange) +{ + basegfx::B2DPolygon aLine({rStart, rStart + rDirection}); + const basegfx::B2DPolyPolygon aResultPP( + basegfx::utils::clipPolygonOnRange( + aLine, + rClipRange, + true, // bInside + true)); // bStroke + + if(aResultPP.count() > 0) + { + const basegfx::B2DPolygon aResultP(aResultPP.getB2DPolygon(0)); + + if(aResultP.count() > 0) + { + const basegfx::B2DPoint aResultStart(aResultP.getB2DPoint(0)); + const basegfx::B2DPoint aResultEnd(aResultP.getB2DPoint(aResultP.count() - 1)); + + if(aResultStart != aResultEnd) + { + rStart = aResultStart; + rDirection = aResultEnd - aResultStart; + } + } + } +} + +static void HelperCreateTLBREntry( + const Array& rArray, + const Style& rStyle, + drawinglayer::primitive2d::SdrFrameBorderDataVector& rData, + const basegfx::B2DPoint& rOrigin, + const basegfx::B2DVector& rX, + const basegfx::B2DVector& rY, + sal_Int32 nColLeft, + sal_Int32 nColRight, + sal_Int32 nRowTop, + sal_Int32 nRowBottom, + const Color* pForceColor, + const basegfx::B2DRange* pClipRange) +{ + if(rStyle.IsUsed()) + { + /// prepare geometry line data + basegfx::B2DPoint aStart(rOrigin); + basegfx::B2DVector aDirection(rX + rY); + + /// check if we need to clip geometry line data and do it + if(nullptr != pClipRange) + { + HelperClipLine(aStart, aDirection, *pClipRange); + } + + /// top-left and bottom-right Style Tables + rData.emplace_back( + aStart, + aDirection, + rStyle, + pForceColor); + drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back()); + + /// Fill top-left Style Table + const Style& rTLFromRight(rArray.GetCellStyleTop(nColLeft, nRowTop)); + const Style& rTLFromBottom(rArray.GetCellStyleLeft(nColLeft, nRowTop)); + + rInstance.addSdrConnectStyleData(true, rTLFromRight, rX, false); + rInstance.addSdrConnectStyleData(true, rTLFromBottom, rY, false); + + /// Fill bottom-right Style Table + const Style& rBRFromBottom(rArray.GetCellStyleRight(nColRight, nRowBottom)); + const Style& rBRFromLeft(rArray.GetCellStyleBottom(nColRight, nRowBottom)); + + rInstance.addSdrConnectStyleData(false, rBRFromBottom, -rY, true); + rInstance.addSdrConnectStyleData(false, rBRFromLeft, -rX, true); + } +} + +static void HelperCreateBLTREntry( + const Array& rArray, + const Style& rStyle, + drawinglayer::primitive2d::SdrFrameBorderDataVector& rData, + const basegfx::B2DPoint& rOrigin, + const basegfx::B2DVector& rX, + const basegfx::B2DVector& rY, + sal_Int32 nColLeft, + sal_Int32 nColRight, + sal_Int32 nRowTop, + sal_Int32 nRowBottom, + const Color* pForceColor, + const basegfx::B2DRange* pClipRange) +{ + if(rStyle.IsUsed()) + { + /// prepare geometry line data + basegfx::B2DPoint aStart(rOrigin + rY); + basegfx::B2DVector aDirection(rX - rY); + + /// check if we need to clip geometry line data and do it + if(nullptr != pClipRange) + { + HelperClipLine(aStart, aDirection, *pClipRange); + } + + /// bottom-left and top-right Style Tables + rData.emplace_back( + aStart, + aDirection, + rStyle, + pForceColor); + drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back()); + + /// Fill bottom-left Style Table + const Style& rBLFromTop(rArray.GetCellStyleLeft(nColLeft, nRowBottom)); + const Style& rBLFromBottom(rArray.GetCellStyleBottom(nColLeft, nRowBottom)); + + rInstance.addSdrConnectStyleData(true, rBLFromTop, -rY, true); + rInstance.addSdrConnectStyleData(true, rBLFromBottom, rX, false); + + /// Fill top-right Style Table + const Style& rTRFromLeft(rArray.GetCellStyleTop(nColRight, nRowTop)); + const Style& rTRFromBottom(rArray.GetCellStyleRight(nColRight, nRowTop)); + + rInstance.addSdrConnectStyleData(false, rTRFromLeft, -rX, true); + rInstance.addSdrConnectStyleData(false, rTRFromBottom, rY, false); + } +} + +drawinglayer::primitive2d::Primitive2DContainer Array::CreateB2DPrimitiveRange( + sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow, + const Color* pForceColor ) const +{ + DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "CreateB2DPrimitiveRange" ); + DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "CreateB2DPrimitiveRange" ); + +#ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL + std::vector<basegfx::B2DRange> aClipRanges; +#endif + + // It may be necessary to extend the loop ranges by one cell to the outside, + // when possible. This is needed e.g. when there is in Calc a Cell with an + // upper CellBorder using DoubleLine and that is right/left connected upwards + // to also DoubleLine. These upper DoubleLines will be extended to meet the + // lower of the upper CellBorder and thus have graphical parts that are + // displayed one cell below and right/left of the target cell - analog to + // other examples in all other directions. + // It would be possible to explicitly test this (if possible by indices at all) + // looping and testing the styles in the outer cells to detect this, but since + // for other usages (e.g. UI) usually nFirstRow==0 and nLastRow==GetRowCount()-1 + // (and analog for Col) it is okay to just expand the range when available. + // Do *not* change nFirstRow/nLastRow due to these needed to the boolean tests + // below (!) + // Checked usages, this method is used in Calc EditView/Print/Export stuff and + // in UI (Dialog), not for Writer Tables and Draw/Impress tables. All usages + // seem okay with this change, so I will add it. + const sal_Int32 nStartRow(nFirstRow > 0 ? nFirstRow - 1 : nFirstRow); + const sal_Int32 nEndRow(nLastRow < GetRowCount() - 1 ? nLastRow + 1 : nLastRow); + const sal_Int32 nStartCol(nFirstCol > 0 ? nFirstCol - 1 : nFirstCol); + const sal_Int32 nEndCol(nLastCol < GetColCount() - 1 ? nLastCol + 1 : nLastCol); + + // prepare SdrFrameBorderDataVector + drawinglayer::primitive2d::SdrFrameBorderDataVector aData; + + // remember for which merged cells crossed lines were already created. To + // do so, hold the sal_Int32 cell index in a set for fast check + std::unordered_set< sal_Int32 > aMergedCells; + + for (sal_Int32 nRow(nStartRow); nRow <= nEndRow; ++nRow) + { + for (sal_Int32 nCol(nStartCol); nCol <= nEndCol; ++nCol) + { + // get Cell and CoordinateSystem (*only* for this Cell, do *not* expand for + // merged cells (!)), check if used (non-empty vectors) + const Cell* pCell(mxImpl->GetCell(nCol, nRow)); + basegfx::B2DHomMatrix aCoordinateSystem(pCell->CreateCoordinateSystemSingleCell(*this, nCol, nRow)); + basegfx::B2DVector aX(basegfx::utils::getColumn(aCoordinateSystem, 0)); + basegfx::B2DVector aY(basegfx::utils::getColumn(aCoordinateSystem, 1)); + + // get needed local values + basegfx::B2DPoint aOrigin(basegfx::utils::getColumn(aCoordinateSystem, 2)); + const bool bOverlapX(pCell->mbOverlapX); + const bool bFirstCol(nCol == nFirstCol); + + // handle rotation: If cell is rotated, handle lower/right edge inside + // this local geometry due to the created CoordinateSystem already representing + // the needed transformations. + const bool bRotated(pCell->IsRotated()); + + // Additionally avoid double-handling by suppressing handling when self not rotated, + // but above/left is rotated and thus already handled. Two directly connected + // rotated will paint/create both edges, they might be rotated differently. + const bool bSuppressLeft(!bRotated && nCol > nFirstCol && mxImpl->GetCell(nCol - 1, nRow)->IsRotated()); + const bool bSuppressAbove(!bRotated && nRow > nFirstRow && mxImpl->GetCell(nCol, nRow - 1)->IsRotated()); + + if(!aX.equalZero() && !aY.equalZero()) + { + // additionally needed local values + const bool bOverlapY(pCell->mbOverlapY); + const bool bLastCol(nCol == nLastCol); + const bool bFirstRow(nRow == nFirstRow); + const bool bLastRow(nRow == nLastRow); + + // create upper line for this Cell + if ((!bOverlapY // true for first line in merged cells or cells + || bFirstRow) // true for non_Calc usages of this tooling + && !bSuppressAbove) // true when above is not rotated, so edge is already handled (see bRotated) + { + // get CellStyle - method will take care to get the correct one, e.g. + // for merged cells (it uses mxImpl->GetMergedOriginCell that works with topLeft's of these) + const Style& rTop(GetCellStyleTop(nCol, nRow)); + + if(rTop.IsUsed()) + { + HelperCreateHorizontalEntry(*this, rTop, nCol, nRow, aOrigin, aX, aY, aData, true, pForceColor); + } + } + + // create lower line for this Cell + if (bLastRow // true for non_Calc usages of this tooling + || bRotated) // true if cell is rotated, handle lower edge in local geometry + { + const Style& rBottom(GetCellStyleBottom(nCol, nRow)); + + if(rBottom.IsUsed()) + { + HelperCreateHorizontalEntry(*this, rBottom, nCol, nRow + 1, aOrigin, aX, aY, aData, false, pForceColor); + } + } + + // create left line for this Cell + if ((!bOverlapX // true for first column in merged cells or cells + || bFirstCol) // true for non_Calc usages of this tooling + && !bSuppressLeft) // true when left is not rotated, so edge is already handled (see bRotated) + { + const Style& rLeft(GetCellStyleLeft(nCol, nRow)); + + if(rLeft.IsUsed()) + { + HelperCreateVerticalEntry(*this, rLeft, nCol, nRow, aOrigin, aX, aY, aData, true, pForceColor); + } + } + + // create right line for this Cell + if (bLastCol // true for non_Calc usages of this tooling + || bRotated) // true if cell is rotated, handle right edge in local geometry + { + const Style& rRight(GetCellStyleRight(nCol, nRow)); + + if(rRight.IsUsed()) + { + HelperCreateVerticalEntry(*this, rRight, nCol + 1, nRow, aOrigin, aX, aY, aData, false, pForceColor); + } + } + + // tdf#126269 check for crossed lines, these need special treatment, especially + // for merged cells (see comments in task). Separate treatment of merged and + // non-merged cells to allow better handling of both types + if(pCell->IsMerged()) + { + // first check if this merged cell was already handled. To do so, + // calculate and use the index of the TopLeft cell + sal_Int32 nColLeft(nCol), nRowTop(nRow), nColRight(nCol), nRowBottom(nRow); + GetMergedRange(nColLeft, nRowTop, nColRight, nRowBottom, nCol, nRow); + const sal_Int32 nIndexOfMergedCell(mxImpl->GetIndex(nColLeft, nRowTop)); + + auto aItInsertedPair = aMergedCells.insert(nIndexOfMergedCell); + if(aItInsertedPair.second) + { + // not found, so not yet handled. + + // Get and check if diagonal styles are used + // Note: For GetCellStyleBLTR below I tried to use nRowBottom + // as Y-value what seemed more logical, but that + // is wrong. Despite defining a line starting at + // bottom-left, the Style is defined in the cell at top-left + const Style& rTLBR(GetCellStyleTLBR(nColLeft, nRowTop)); + const Style& rBLTR(GetCellStyleBLTR(nColLeft, nRowTop)); + + if(rTLBR.IsUsed() || rBLTR.IsUsed()) + { + // test if merged cell overlaps ClipRange at all (needs visualization) + if(mxImpl->OverlapsClipRange(nColLeft, nRowTop, nColRight, nRowBottom)) + { + // when merged, get extended coordinate system and derived values + // for the full range of this merged cell. Only work with rMergedCell + // (which is the top-left single cell of the merged cell) from here on + aCoordinateSystem = mxImpl->GetCell(nColLeft, nRowTop)->CreateCoordinateSystemMergedCell( + *this, nColLeft, nRowTop, nColRight, nRowBottom); + aX = basegfx::utils::getColumn(aCoordinateSystem, 0); + aY = basegfx::utils::getColumn(aCoordinateSystem, 1); + aOrigin = basegfx::utils::getColumn(aCoordinateSystem, 2); + + // check if clip is needed + basegfx::B2DRange aClipRange; + + // first use row/col ClipTest for raw check + bool bNeedToClip( + !mxImpl->IsColInClipRange(nColLeft) || + !mxImpl->IsRowInClipRange(nRowTop) || + !mxImpl->IsColInClipRange(nColRight) || + !mxImpl->IsRowInClipRange(nRowBottom)); + + if(bNeedToClip) + { + // now get ClipRange and CellRange in logical coordinates + aClipRange = GetB2DRange( + mxImpl->mnFirstClipCol, mxImpl->mnFirstClipRow, + mxImpl->mnLastClipCol, mxImpl->mnLastClipRow); + + basegfx::B2DRange aCellRange( + GetB2DRange( + nColLeft, nRowTop, + nColRight, nRowBottom)); + + // intersect these to get the target ClipRange, ensure + // that clip is needed + aClipRange.intersect(aCellRange); + bNeedToClip = !aClipRange.isEmpty(); + +#ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL + aClipRanges.push_back(aClipRange); +#endif + } + + // create top-left to bottom-right geometry + HelperCreateTLBREntry(*this, rTLBR, aData, aOrigin, aX, aY, + nColLeft, nRowTop, nColRight, nRowBottom, pForceColor, + bNeedToClip ? &aClipRange : nullptr); + + // create bottom-left to top-right geometry + HelperCreateBLTREntry(*this, rBLTR, aData, aOrigin, aX, aY, + nColLeft, nRowTop, nColRight, nRowBottom, pForceColor, + bNeedToClip ? &aClipRange : nullptr); + } + } + } + } + else + { + // must be in clipping range: else not visible. This + // already clips completely for non-merged cells + if( mxImpl->IsInClipRange( nCol, nRow ) ) + { + // get and check if diagonal styles are used + const Style& rTLBR(GetCellStyleTLBR(nCol, nRow)); + const Style& rBLTR(GetCellStyleBLTR(nCol, nRow)); + + if(rTLBR.IsUsed() || rBLTR.IsUsed()) + { + HelperCreateTLBREntry(*this, rTLBR, aData, aOrigin, aX, aY, + nCol, nRow, nCol, nRow, pForceColor, nullptr); + + HelperCreateBLTREntry(*this, rBLTR, aData, aOrigin, aX, aY, + nCol, nRow, nCol, nRow, pForceColor, nullptr); + } + } + } + } + else if(!aY.equalZero()) + { + // cell has height, but no width. Create left vertical line for this Cell + if ((!bOverlapX // true for first column in merged cells or cells + || bFirstCol) // true for non_Calc usages of this tooling + && !bSuppressLeft) // true when left is not rotated, so edge is already handled (see bRotated) + { + const Style& rLeft(GetCellStyleLeft(nCol, nRow)); + + if (rLeft.IsUsed()) + { + HelperCreateVerticalEntry(*this, rLeft, nCol, nRow, aOrigin, aX, aY, aData, true, pForceColor); + } + } + } + else + { + // Cell has *no* size, thus no visualization + } + } + } + + // create instance of SdrFrameBorderPrimitive2D if + // SdrFrameBorderDataVector is used + drawinglayer::primitive2d::Primitive2DContainer aSequence; + + if(!aData.empty()) + { + aSequence.append( + drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D( + std::move(aData), + true))); // force visualization to minimal one discrete unit (pixel) + } + +#ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL + for(auto const& rClipRange : aClipRanges) + { + // draw ClipRange in yellow to allow simple interactive optical control in office + aSequence.append( + drawinglayer::primitive2d::Primitive2DReference( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + basegfx::utils::createPolygonFromRect(rClipRange), + basegfx::BColor(1.0, 1.0, 0.0)))); + } +#endif + + return aSequence; +} + +drawinglayer::primitive2d::Primitive2DContainer Array::CreateB2DPrimitiveArray() const +{ + drawinglayer::primitive2d::Primitive2DContainer aPrimitives; + + if (mxImpl->mnWidth && mxImpl->mnHeight) + { + aPrimitives = CreateB2DPrimitiveRange(0, 0, mxImpl->mnWidth - 1, mxImpl->mnHeight - 1, nullptr); + } + + return aPrimitives; +} + +#undef DBG_FRAME_CHECK_ROW_1 +#undef DBG_FRAME_CHECK_COL_1 +#undef DBG_FRAME_CHECK_COLROW +#undef DBG_FRAME_CHECK_ROW +#undef DBG_FRAME_CHECK_COL +#undef DBG_FRAME_CHECK + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/frmdirlbox.cxx b/svx/source/dialog/frmdirlbox.cxx new file mode 100644 index 0000000000..375a418671 --- /dev/null +++ b/svx/source/dialog/frmdirlbox.cxx @@ -0,0 +1,32 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/frmdirlbox.hxx> + +namespace svx +{ +FrameDirectionListBox::FrameDirectionListBox(std::unique_ptr<weld::ComboBox> pControl) + : m_xControl(std::move(pControl)) +{ +} + +FrameDirectionListBox::~FrameDirectionListBox() {} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/frmsel.cxx b/svx/source/dialog/frmsel.cxx new file mode 100644 index 0000000000..337013e55d --- /dev/null +++ b/svx/source/dialog/frmsel.cxx @@ -0,0 +1,1308 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <sal/config.h> + +#include <o3tl/safeint.hxx> +#include <svx/frmsel.hxx> +#include <vcl/event.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <svtools/colorcfg.hxx> + +#include <algorithm> +#include <math.h> + +#include <frmselimpl.hxx> +#include <AccessibleFrameSelector.hxx> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> + +#include <bitmaps.hlst> + +using namespace ::com::sun::star; +using namespace ::editeng; + +namespace svx { + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Any; +using ::com::sun::star::accessibility::XAccessible; +using namespace ::com::sun::star::accessibility; + +// global functions from framebordertype.hxx + +FrameBorderType GetFrameBorderTypeFromIndex( size_t nIndex ) +{ + DBG_ASSERT( nIndex < o3tl::make_unsigned(FRAMEBORDERTYPE_COUNT), + "svx::GetFrameBorderTypeFromIndex - invalid index" ); + return static_cast< FrameBorderType >( nIndex + 1 ); +} + +size_t GetIndexFromFrameBorderType( FrameBorderType eBorder ) +{ + DBG_ASSERT( eBorder != FrameBorderType::NONE, + "svx::GetIndexFromFrameBorderType - invalid frame border type" ); + return static_cast< size_t >( eBorder ) - 1; +} + +namespace +{ + +/** Space between outer control border and any graphical element of the control. */ +const tools::Long FRAMESEL_GEOM_OUTER = 2; + +/** Space between arrows and usable inner area. */ +const tools::Long FRAMESEL_GEOM_INNER = 3; + +/** Maximum width to draw a frame border style. */ +const tools::Long FRAMESEL_GEOM_WIDTH = 9; + +/** Additional margin for click area of outer lines. */ +const tools::Long FRAMESEL_GEOM_ADD_CLICK_OUTER = 5; + +/** Additional margin for click area of inner lines. */ +const tools::Long FRAMESEL_GEOM_ADD_CLICK_INNER = 2; + + +/** Returns the corresponding flag for a frame border. */ +FrameSelFlags lclGetFlagFromType( FrameBorderType eBorder ) +{ + switch( eBorder ) + { + case FrameBorderType::Left: return FrameSelFlags::Left; + case FrameBorderType::Right: return FrameSelFlags::Right; + case FrameBorderType::Top: return FrameSelFlags::Top; + case FrameBorderType::Bottom: return FrameSelFlags::Bottom; + case FrameBorderType::Horizontal: return FrameSelFlags::InnerHorizontal; + case FrameBorderType::Vertical: return FrameSelFlags::InnerVertical; + case FrameBorderType::TLBR: return FrameSelFlags::DiagonalTLBR; + case FrameBorderType::BLTR: return FrameSelFlags::DiagonalBLTR; + case FrameBorderType::NONE : break; + } + return FrameSelFlags::NONE; +} + +/** Merges the rSource polypolygon into the rDest polypolygon. */ +void lclPolyPolyUnion( tools::PolyPolygon& rDest, const tools::PolyPolygon& rSource ) +{ + const tools::PolyPolygon aTmp( rDest ); + aTmp.GetUnion( rSource, rDest ); +} + +} // namespace + +FrameBorder::FrameBorder( FrameBorderType eType ) : + meType( eType ), + meState( FrameBorderState::Hide ), + meKeyLeft( FrameBorderType::NONE ), + meKeyRight( FrameBorderType::NONE ), + meKeyTop( FrameBorderType::NONE ), + meKeyBottom( FrameBorderType::NONE ), + mbEnabled( false ), + mbSelected( false ) +{ +} + +void FrameBorder::Enable( FrameSelFlags nFlags ) +{ + mbEnabled = bool(nFlags & lclGetFlagFromType( meType )); + if( !mbEnabled ) + SetState( FrameBorderState::Hide ); +} + +void FrameBorder::SetCoreStyle( const SvxBorderLine* pStyle ) +{ + if( pStyle ) + maCoreStyle = *pStyle; + else + maCoreStyle = SvxBorderLine(); + + // from twips to points + maUIStyle.Set( &maCoreStyle, FrameBorder::GetDefaultPatternScale(), FRAMESEL_GEOM_WIDTH ); + meState = maUIStyle.IsUsed() ? FrameBorderState::Show : FrameBorderState::Hide; +} + +void FrameBorder::SetState( FrameBorderState eState ) +{ + meState = eState; + switch( meState ) + { + case FrameBorderState::Show: + SAL_WARN( "svx.dialog", "svx::FrameBorder::SetState - use SetCoreStyle to make border visible" ); + break; + case FrameBorderState::Hide: + maCoreStyle = SvxBorderLine(); + maUIStyle.Clear(); + break; + case FrameBorderState::DontCare: + maCoreStyle = SvxBorderLine(); + maUIStyle = frame::Style(3, 0, 0, SvxBorderLineStyle::SOLID, FrameBorder::GetDefaultPatternScale()); //OBJ_FRAMESTYLE_DONTCARE + break; + } +} + +void FrameBorder::AddFocusPolygon( const tools::Polygon& rFocus ) +{ + lclPolyPolyUnion( maFocusArea, tools::PolyPolygon(rFocus) ); +} + +void FrameBorder::MergeFocusToPolyPolygon( tools::PolyPolygon& rPPoly ) const +{ + lclPolyPolyUnion( rPPoly, maFocusArea ); +} + +void FrameBorder::AddClickRect( const tools::Rectangle& rRect ) +{ + lclPolyPolyUnion( maClickArea, tools::PolyPolygon( rRect ) ); +} + +bool FrameBorder::ContainsClickPoint( const Point& rPos ) const +{ + return vcl::Region( maClickArea ).Contains( rPos ); +} + +tools::Rectangle FrameBorder::GetClickBoundRect() const +{ + return maClickArea.GetBoundRect(); +} + +void FrameBorder::SetKeyboardNeighbors( + FrameBorderType eLeft, FrameBorderType eRight, FrameBorderType eTop, FrameBorderType eBottom ) +{ + meKeyLeft = eLeft; + meKeyRight = eRight; + meKeyTop = eTop; + meKeyBottom = eBottom; +} + +FrameBorderType FrameBorder::GetKeyboardNeighbor( sal_uInt16 nKeyCode ) const +{ + FrameBorderType eBorder = FrameBorderType::NONE; + switch( nKeyCode ) + { + case KEY_LEFT: eBorder = meKeyLeft; break; + case KEY_RIGHT: eBorder = meKeyRight; break; + case KEY_UP: eBorder = meKeyTop; break; + case KEY_DOWN: eBorder = meKeyBottom; break; + default: SAL_WARN( "svx.dialog", "svx::FrameBorder::GetKeyboardNeighbor - unknown key code" ); + } + return eBorder; +} + +FrameSelectorImpl::FrameSelectorImpl( FrameSelector& rFrameSel ) : + mrFrameSel( rFrameSel ), + mpVirDev( VclPtr<VirtualDevice>::Create() ), + maLeft( FrameBorderType::Left ), + maRight( FrameBorderType::Right ), + maTop( FrameBorderType::Top ), + maBottom( FrameBorderType::Bottom ), + maHor( FrameBorderType::Horizontal ), + maVer( FrameBorderType::Vertical ), + maTLBR( FrameBorderType::TLBR ), + maBLTR( FrameBorderType::BLTR ), + mnFlags( FrameSelFlags::Outer ), + mnCtrlSize( 0 ), + mnArrowSize( 0 ), + mnLine1( 0 ), + mnLine2( 0 ), + mnLine3( 0 ), + mnFocusOffs( 0 ), + mbHor( false ), + mbVer( false ), + mbTLBR( false ), + mbBLTR( false ), + mbFullRepaint( true ), + mbAutoSelect( true ), + mbHCMode( false ) +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + ,maChildVec(FRAMEBORDERTYPE_COUNT) +#endif +{ + maAllBorders.resize( FRAMEBORDERTYPE_COUNT, nullptr ); + maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Left ) ] = &maLeft; + maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Right ) ] = &maRight; + maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Top ) ] = &maTop; + maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Bottom ) ] = &maBottom; + maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Horizontal ) ] = &maHor; + maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Vertical ) ] = &maVer; + maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::TLBR ) ] = &maTLBR; + maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::BLTR ) ] = &maBLTR; +#if OSL_DEBUG_LEVEL >= 2 + { + bool bOk = true; + for( FrameBorderCIter aIt( maAllBorders ); bOk && aIt.Is(); bOk = (*aIt != 0), ++aIt ); + DBG_ASSERT( bOk, "svx::FrameSelectorImpl::FrameSelectorImpl - missing entry in maAllBorders" ); + } +#endif + // left neighbor right neighbor upper neighbor lower neighbor + maLeft.SetKeyboardNeighbors( FrameBorderType::NONE, FrameBorderType::TLBR, FrameBorderType::Top, FrameBorderType::Bottom ); + maRight.SetKeyboardNeighbors( FrameBorderType::BLTR, FrameBorderType::NONE, FrameBorderType::Top, FrameBorderType::Bottom ); + maTop.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::NONE, FrameBorderType::TLBR ); + maBottom.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::BLTR, FrameBorderType::NONE ); + maHor.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::TLBR, FrameBorderType::BLTR ); + maVer.SetKeyboardNeighbors( FrameBorderType::TLBR, FrameBorderType::BLTR, FrameBorderType::Top, FrameBorderType::Bottom ); + maTLBR.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Vertical, FrameBorderType::Top, FrameBorderType::Horizontal ); + maBLTR.SetKeyboardNeighbors( FrameBorderType::Vertical, FrameBorderType::Right, FrameBorderType::Horizontal, FrameBorderType::Bottom ); + + Initialize(mnFlags); +} + +FrameSelectorImpl::~FrameSelectorImpl() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + for( auto& rpChild : maChildVec ) + if( rpChild.is() ) + { + rpChild->Invalidate(); + rpChild->dispose(); + } +#endif +} + +// initialization +void FrameSelectorImpl::Initialize( FrameSelFlags nFlags ) +{ + mnFlags = nFlags; + + maEnabBorders.clear(); + for( FrameBorderIter aIt( maAllBorders ); aIt.Is(); ++aIt ) + { + (*aIt)->Enable( mnFlags ); + if( (*aIt)->IsEnabled() ) + maEnabBorders.push_back( *aIt ); + } + mbHor = maHor.IsEnabled(); + mbVer = maVer.IsEnabled(); + mbTLBR = maTLBR.IsEnabled(); + mbBLTR = maBLTR.IsEnabled(); + + InitVirtualDevice(); +} + +void FrameSelectorImpl::InitColors() +{ + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + svtools::ColorConfig aColorConfig; + maBackCol = aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor; + mbHCMode = rSettings.GetHighContrastMode(); + maArrowCol = aColorConfig.GetColorValue(svtools::DOCBOUNDARIES).nColor; + maMarkCol = aColorConfig.GetColorValue(svtools::TABLEBOUNDARIES).nColor; + maHCLineCol = COL_BLACK; +} + +constexpr OUString aImageIds[] = +{ + RID_SVXBMP_FRMSEL_ARROW1, + RID_SVXBMP_FRMSEL_ARROW2, + RID_SVXBMP_FRMSEL_ARROW3, + RID_SVXBMP_FRMSEL_ARROW4, + RID_SVXBMP_FRMSEL_ARROW5, + RID_SVXBMP_FRMSEL_ARROW6, + RID_SVXBMP_FRMSEL_ARROW7, + RID_SVXBMP_FRMSEL_ARROW8, + RID_SVXBMP_FRMSEL_ARROW9, + RID_SVXBMP_FRMSEL_ARROW10, + RID_SVXBMP_FRMSEL_ARROW11, + RID_SVXBMP_FRMSEL_ARROW12, + RID_SVXBMP_FRMSEL_ARROW13, + RID_SVXBMP_FRMSEL_ARROW14, + RID_SVXBMP_FRMSEL_ARROW15, + RID_SVXBMP_FRMSEL_ARROW16 +}; + +void FrameSelectorImpl::InitArrowImageList() +{ + maArrows.clear(); + + /* Build the arrow images bitmap with current colors. */ + Color pColorAry1[3]; + Color pColorAry2[3]; + pColorAry1[0] = Color( 0, 0, 0 ); + pColorAry2[0] = maArrowCol; // black -> arrow color + pColorAry1[1] = Color( 0, 255, 0 ); + pColorAry2[1] = maMarkCol; // green -> marker color + pColorAry1[2] = Color( 255, 0, 255 ); + pColorAry2[2] = maBackCol; // magenta -> background + + assert(SAL_N_ELEMENTS(aImageIds) == 16); + for (size_t i = 0; i < SAL_N_ELEMENTS(aImageIds); ++i) + { + BitmapEx aBmpEx { aImageIds[i] }; + aBmpEx.Replace(pColorAry1, pColorAry2, 3); + maArrows.emplace_back(aBmpEx); + } + assert(maArrows.size() == 16); + + mnArrowSize = maArrows[0].GetSizePixel().Height(); +} + +void FrameSelectorImpl::InitGlobalGeometry() +{ + Size aCtrlSize(mrFrameSel.GetOutputSizePixel()); + /* nMinSize is the lower of width and height (control will always be squarish). + FRAMESEL_GEOM_OUTER is the minimal distance between inner control border + and any element. */ + tools::Long nMinSize = std::min( aCtrlSize.Width(), aCtrlSize.Height() ) - 2 * FRAMESEL_GEOM_OUTER; + /* nFixedSize is the size all existing elements need in one direction: + the diag. arrow, space betw. arrow and frame border, outer frame border, + inner frame border, other outer frame border, space betw. frame border + and arrow, the other arrow. */ + tools::Long nFixedSize = 2 * mnArrowSize + 2 * FRAMESEL_GEOM_INNER + 3 * FRAMESEL_GEOM_WIDTH; + /* nBetwBordersSize contains the size between an outer and inner frame border (made odd). */ + tools::Long nBetwBordersSize = (((nMinSize - nFixedSize) / 2) - 1) | 1; + + /* The final size of the usable area. At least do not get negative */ + mnCtrlSize = 2 * nBetwBordersSize + nFixedSize; + mnCtrlSize = std::max(mnCtrlSize, static_cast<tools::Long>(0)); + mpVirDev->SetOutputSizePixel( Size( mnCtrlSize, mnCtrlSize ) ); + + /* Center the virtual device in the control. */ + maVirDevPos = Point( (aCtrlSize.Width() - mnCtrlSize) / 2, (aCtrlSize.Height() - mnCtrlSize) / 2 ); +} + +void FrameSelectorImpl::InitBorderGeometry() +{ + size_t nCol, nCols, nRow, nRows; + + // Global border geometry values + /* mnLine* is the middle point inside a frame border (i.e. mnLine1 is mid X inside left border). */ + mnLine1 = mnArrowSize + FRAMESEL_GEOM_INNER + FRAMESEL_GEOM_WIDTH / 2; + mnLine2 = mnCtrlSize / 2; + mnLine3 = 2 * mnLine2 - mnLine1; + + // Frame helper array + maArray.Initialize( mbVer ? 2 : 1, mbHor ? 2 : 1 ); + + maArray.SetXOffset( mnLine1 ); + maArray.SetAllColWidths( (mbVer ? mnLine2 : mnLine3) - mnLine1 ); + + maArray.SetYOffset( mnLine1 ); + maArray.SetAllRowHeights( (mbHor ? mnLine2 : mnLine3) - mnLine1 ); + + // Focus polygons + /* Width for focus rectangles from center of frame borders. */ + mnFocusOffs = FRAMESEL_GEOM_WIDTH / 2 + 1; + + maLeft.ClearFocusArea(); + maVer.ClearFocusArea(); + maRight.ClearFocusArea(); + maTop.ClearFocusArea(); + maHor.ClearFocusArea(); + maBottom.ClearFocusArea(); + + maLeft.AddFocusPolygon( tools::Polygon(tools::Rectangle( mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine1 + mnFocusOffs, mnLine3 + mnFocusOffs )) ); + maVer.AddFocusPolygon( tools::Polygon(tools::Rectangle( mnLine2 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine2 + mnFocusOffs, mnLine3 + mnFocusOffs )) ); + maRight.AddFocusPolygon( tools::Polygon(tools::Rectangle( mnLine3 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs )) ); + maTop.AddFocusPolygon( tools::Polygon(tools::Rectangle( mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine1 + mnFocusOffs )) ); + maHor.AddFocusPolygon( tools::Polygon(tools::Rectangle( mnLine1 - mnFocusOffs, mnLine2 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine2 + mnFocusOffs )) ); + maBottom.AddFocusPolygon( tools::Polygon(tools::Rectangle( mnLine1 - mnFocusOffs, mnLine3 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs )) ); + + maTLBR.ClearFocusArea(); + maBLTR.ClearFocusArea(); + + for( nCol = 0, nCols = maArray.GetColCount(); nCol < nCols; ++nCol ) + { + for( nRow = 0, nRows = maArray.GetRowCount(); nRow < nRows; ++nRow ) + { + const basegfx::B2DRange aCellRange(maArray.GetCellRange( nCol, nRow )); + const tools::Rectangle aRect( + basegfx::fround(aCellRange.getMinX()), basegfx::fround(aCellRange.getMinY()), + basegfx::fround(aCellRange.getMaxX()), basegfx::fround(aCellRange.getMaxY())); + const double fHorDiagAngle(atan2(fabs(aCellRange.getHeight()), fabs(aCellRange.getWidth()))); + const double fVerDiagAngle(fHorDiagAngle > 0.0 ? M_PI_2 - fHorDiagAngle : 0.0); + const tools::Long nDiagFocusOffsX(basegfx::fround(-mnFocusOffs / tan(fHorDiagAngle) + mnFocusOffs / sin(fHorDiagAngle))); + const tools::Long nDiagFocusOffsY(basegfx::fround(-mnFocusOffs / tan(fVerDiagAngle) + mnFocusOffs / sin(fVerDiagAngle))); + + std::vector< Point > aFocusVec; + aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Top() + nDiagFocusOffsY ); + aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Top() - mnFocusOffs ); + aFocusVec.emplace_back( aRect.Left() + nDiagFocusOffsX, aRect.Top() - mnFocusOffs ); + aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Bottom() - nDiagFocusOffsY ); + aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Bottom() + mnFocusOffs ); + aFocusVec.emplace_back( aRect.Right() - nDiagFocusOffsX, aRect.Bottom() + mnFocusOffs ); + maTLBR.AddFocusPolygon( tools::Polygon( static_cast< sal_uInt16 >( aFocusVec.size() ), aFocusVec.data() ) ); + + aFocusVec.clear(); + aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Top() + nDiagFocusOffsY ); + aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Top() - mnFocusOffs ); + aFocusVec.emplace_back( aRect.Right() - nDiagFocusOffsX, aRect.Top() - mnFocusOffs ); + aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Bottom() - nDiagFocusOffsY ); + aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Bottom() + mnFocusOffs ); + aFocusVec.emplace_back( aRect.Left() + nDiagFocusOffsX, aRect.Bottom() + mnFocusOffs ); + maBLTR.AddFocusPolygon( tools::Polygon( static_cast< sal_uInt16 >( aFocusVec.size() ), aFocusVec.data() ) ); + } + } + + // Click areas + for( FrameBorderIter aIt( maAllBorders ); aIt.Is(); ++aIt ) + (*aIt)->ClearClickArea(); + + /* Additional space for click area: is added to the space available to draw + the frame borders. For instance left frame border: + - To left, top, and bottom always big additional space (outer area). + - To right: Dependent on existence of inner vertical frame border + (if enabled, use less space). + */ + tools::Long nClO = FRAMESEL_GEOM_WIDTH / 2 + FRAMESEL_GEOM_ADD_CLICK_OUTER; + tools::Long nClI = (mbTLBR && mbBLTR) ? (FRAMESEL_GEOM_WIDTH / 2 + FRAMESEL_GEOM_ADD_CLICK_INNER) : nClO; + tools::Long nClH = mbHor ? nClI : nClO; // additional space dependent of horizontal inner border + tools::Long nClV = mbVer ? nClI : nClO; // additional space dependent of vertical inner border + + maLeft.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine1 - nClO, mnLine1 + nClV, mnLine3 + nClO ) ); + maVer.AddClickRect( tools::Rectangle( mnLine2 - nClI, mnLine1 - nClO, mnLine2 + nClI, mnLine3 + nClO ) ); + maRight.AddClickRect( tools::Rectangle( mnLine3 - nClV, mnLine1 - nClO, mnLine3 + nClO, mnLine3 + nClO ) ); + maTop.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine1 - nClO, mnLine3 + nClO, mnLine1 + nClH ) ); + maHor.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine2 - nClI, mnLine3 + nClO, mnLine2 + nClI ) ); + maBottom.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine3 - nClH, mnLine3 + nClO, mnLine3 + nClO ) ); + + /* Diagonal frame borders use the remaining space between outer and inner frame borders. */ + if( !(mbTLBR || mbBLTR) ) + return; + + for( nCol = 0, nCols = maArray.GetColCount(); nCol < nCols; ++nCol ) + { + for( nRow = 0, nRows = maArray.GetRowCount(); nRow < nRows; ++nRow ) + { + // the usable area between horizontal/vertical frame borders of current quadrant + const basegfx::B2DRange aCellRange(maArray.GetCellRange( nCol, nRow )); + const tools::Rectangle aRect( + basegfx::fround(aCellRange.getMinX()) + nClV + 1, basegfx::fround(aCellRange.getMinY()) + nClH + 1, + basegfx::fround(aCellRange.getMaxX()) - nClV + 1, basegfx::fround(aCellRange.getMaxY()) - nClH + 1); + + /* Both diagonal frame borders enabled. */ + if( mbTLBR && mbBLTR ) + { + // single areas + Point aMid( aRect.Center() ); + maTLBR.AddClickRect( tools::Rectangle( aRect.TopLeft(), aMid ) ); + maTLBR.AddClickRect( tools::Rectangle( aMid + Point( 1, 1 ), aRect.BottomRight() ) ); + maBLTR.AddClickRect( tools::Rectangle( aRect.Left(), aMid.Y() + 1, aMid.X(), aRect.Bottom() ) ); + maBLTR.AddClickRect( tools::Rectangle( aMid.X() + 1, aRect.Top(), aRect.Right(), aMid.Y() ) ); + // centered rectangle for both frame borders + tools::Rectangle aMidRect( aRect.TopLeft(), Size( aRect.GetWidth() / 3, aRect.GetHeight() / 3 ) ); + aMidRect.Move( (aRect.GetWidth() - aMidRect.GetWidth()) / 2, (aRect.GetHeight() - aMidRect.GetHeight()) / 2 ); + maTLBR.AddClickRect( aMidRect ); + maBLTR.AddClickRect( aMidRect ); + } + /* One of the diagonal frame borders enabled - use entire rectangle. */ + else if( mbTLBR && !mbBLTR ) // top-left to bottom-right only + maTLBR.AddClickRect( aRect ); + else if( !mbTLBR && mbBLTR ) // bottom-left to top-right only + maBLTR.AddClickRect( aRect ); + } + } +} + +void FrameSelectorImpl::InitVirtualDevice() +{ + // initialize resources + InitColors(); + InitArrowImageList(); + + sizeChanged(); +} + +void FrameSelectorImpl::sizeChanged() +{ + // initialize geometry + InitGlobalGeometry(); + InitBorderGeometry(); + + DoInvalidate( true ); +} + +// frame border access +const FrameBorder& FrameSelectorImpl::GetBorder( FrameBorderType eBorder ) const +{ + size_t nIndex = GetIndexFromFrameBorderType( eBorder ); + if( nIndex < maAllBorders.size() ) + return *maAllBorders[ nIndex ]; + SAL_WARN( "svx.dialog", "svx::FrameSelectorImpl::GetBorder - unknown border type" ); + return maTop; +} + +FrameBorder& FrameSelectorImpl::GetBorderAccess( FrameBorderType eBorder ) +{ + return const_cast< FrameBorder& >( GetBorder( eBorder ) ); +} + +// drawing +void FrameSelectorImpl::DrawBackground() +{ + // clear the area + mpVirDev->SetLineColor(); + mpVirDev->SetFillColor( maBackCol ); + mpVirDev->DrawRect( tools::Rectangle( Point( 0, 0 ), mpVirDev->GetOutputSizePixel() ) ); + + // draw the inner gray (or whatever color) rectangle + mpVirDev->SetLineColor(); + mpVirDev->SetFillColor( maMarkCol ); + mpVirDev->DrawRect( tools::Rectangle( + mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) ); + + // draw the white space for enabled frame borders + tools::PolyPolygon aPPoly; + for( FrameBorderCIter aIt( maEnabBorders ); aIt.Is(); ++aIt ) + (*aIt)->MergeFocusToPolyPolygon( aPPoly ); + aPPoly.Optimize( PolyOptimizeFlags::CLOSE ); + mpVirDev->SetLineColor( maBackCol ); + mpVirDev->SetFillColor( maBackCol ); + mpVirDev->DrawPolyPolygon( aPPoly ); +} + +void FrameSelectorImpl::DrawArrows( const FrameBorder& rBorder ) +{ + DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::DrawArrows - access to disabled border" ); + + tools::Long nLinePos = 0; + switch( rBorder.GetType() ) + { + case FrameBorderType::Left: + case FrameBorderType::Top: nLinePos = mnLine1; break; + case FrameBorderType::Vertical: + case FrameBorderType::Horizontal: nLinePos = mnLine2; break; + case FrameBorderType::Right: + case FrameBorderType::Bottom: nLinePos = mnLine3; break; + default: ; //prevent warning + } + nLinePos -= mnArrowSize / 2; + + tools::Long nTLPos = 0; + tools::Long nBRPos = mnCtrlSize - mnArrowSize; + Point aPos1, aPos2; + int nImgIndex1 = -1, nImgIndex2 = -1; + switch( rBorder.GetType() ) + { + case FrameBorderType::Left: + case FrameBorderType::Right: + case FrameBorderType::Vertical: + aPos1 = Point( nLinePos, nTLPos ); nImgIndex1 = 0; + aPos2 = Point( nLinePos, nBRPos ); nImgIndex2 = 1; + break; + + case FrameBorderType::Top: + case FrameBorderType::Bottom: + case FrameBorderType::Horizontal: + aPos1 = Point( nTLPos, nLinePos ); nImgIndex1 = 2; + aPos2 = Point( nBRPos, nLinePos ); nImgIndex2 = 3; + break; + + case FrameBorderType::TLBR: + aPos1 = Point( nTLPos, nTLPos ); nImgIndex1 = 4; + aPos2 = Point( nBRPos, nBRPos ); nImgIndex2 = 5; + break; + case FrameBorderType::BLTR: + aPos1 = Point( nTLPos, nBRPos ); nImgIndex1 = 6; + aPos2 = Point( nBRPos, nTLPos ); nImgIndex2 = 7; + break; + default: ; //prevent warning + } + + // Arrow or marker? Do not draw arrows into disabled control. + sal_uInt16 nSelectAdd = (mrFrameSel.IsEnabled() && rBorder.IsSelected()) ? 0 : 8; + if (nImgIndex1 >= 0) + mpVirDev->DrawImage(aPos1, maArrows[nImgIndex1 + nSelectAdd]); + if (nImgIndex2 >= 0) + mpVirDev->DrawImage(aPos2, maArrows[nImgIndex2 + nSelectAdd]); +} + +Color FrameSelectorImpl::GetDrawLineColor( const Color& rColor ) const +{ + Color aColor( mbHCMode ? maHCLineCol : rColor ); + if( aColor == maBackCol ) + aColor.Invert(); + return aColor; +} + +void FrameSelectorImpl::DrawAllFrameBorders() +{ + // Translate core colors to current UI colors (regards current background and HC mode). + for( FrameBorderIter aIt( maEnabBorders ); aIt.Is(); ++aIt ) + { + Color aCoreColorPrim = ((*aIt)->GetState() == FrameBorderState::DontCare) ? maMarkCol : (*aIt)->GetCoreStyle().GetColorOut(); + Color aCoreColorSecn = ((*aIt)->GetState() == FrameBorderState::DontCare) ? maMarkCol : (*aIt)->GetCoreStyle().GetColorIn(); + (*aIt)->SetUIColorPrim( GetDrawLineColor( aCoreColorPrim ) ); + (*aIt)->SetUIColorSecn( GetDrawLineColor( aCoreColorSecn ) ); + } + + // Copy all frame border styles to the helper array + maArray.SetColumnStyleLeft( 0, maLeft.GetUIStyle() ); + if( mbVer ) maArray.SetColumnStyleLeft( 1, maVer.GetUIStyle() ); + + // Invert the style for the right line + const frame::Style rRightStyle = maRight.GetUIStyle( ); + frame::Style rInvertedRight( rRightStyle.GetColorPrim(), + rRightStyle.GetColorSecn(), rRightStyle.GetColorGap(), + rRightStyle.UseGapColor(), + rRightStyle.Secn(), rRightStyle.Dist(), rRightStyle.Prim( ), + rRightStyle.Type( ), rRightStyle.PatternScale() ); + maArray.SetColumnStyleRight( mbVer ? 1 : 0, rInvertedRight ); + + maArray.SetRowStyleTop( 0, maTop.GetUIStyle() ); + if( mbHor ) + { + // Invert the style for the hor line to match the real borders + const frame::Style rHorStyle = maHor.GetUIStyle(); + frame::Style rInvertedHor( rHorStyle.GetColorPrim(), + rHorStyle.GetColorSecn(), rHorStyle.GetColorGap(), + rHorStyle.UseGapColor(), + rHorStyle.Secn(), rHorStyle.Dist(), rHorStyle.Prim( ), + rHorStyle.Type(), rHorStyle.PatternScale() ); + maArray.SetRowStyleTop( 1, rInvertedHor ); + } + + // Invert the style for the bottom line + const frame::Style rBottomStyle = maBottom.GetUIStyle( ); + frame::Style rInvertedBottom( rBottomStyle.GetColorPrim(), + rBottomStyle.GetColorSecn(), rBottomStyle.GetColorGap(), + rBottomStyle.UseGapColor(), + rBottomStyle.Secn(), rBottomStyle.Dist(), rBottomStyle.Prim( ), + rBottomStyle.Type(), rBottomStyle.PatternScale() ); + maArray.SetRowStyleBottom( mbHor ? 1 : 0, rInvertedBottom ); + + for( sal_Int32 nCol = 0; nCol < maArray.GetColCount(); ++nCol ) + for( sal_Int32 nRow = 0; nRow < maArray.GetRowCount(); ++nRow ) + maArray.SetCellStyleDiag( nCol, nRow, maTLBR.GetUIStyle(), maBLTR.GetUIStyle() ); + + // This is used in the dialog/control for 'Border' attributes. When using + // the original paint below instead of primitives, the advantage currently + // is the correct visualization of diagonal line(s) including overlaying, + // but the rest is bad. Since the edit views use primitives and the preview + // should be 'real' I opt for also changing this to primitives. I will + // keep the old solution and add a switch (above) based on a static bool so + // that interested people may test this out in the debugger. + // This is one more hint to enhance the primitive visualization further to + // support diagonals better - that's the way to go. + const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D; + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D( + drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice( + *mpVirDev, + aNewViewInformation2D)); + + pProcessor2D->process(maArray.CreateB2DPrimitiveArray()); + pProcessor2D.reset(); +} + +void FrameSelectorImpl::DrawVirtualDevice() +{ + DrawBackground(); + for(FrameBorderCIter aIt(maEnabBorders); aIt.Is(); ++aIt) + DrawArrows(**aIt); + DrawAllFrameBorders(); + mbFullRepaint = false; +} + +void FrameSelectorImpl::CopyVirDevToControl(vcl::RenderContext& rRenderContext) +{ + if (mbFullRepaint) + DrawVirtualDevice(); + rRenderContext.DrawBitmapEx(maVirDevPos, mpVirDev->GetBitmapEx(Point(0, 0), mpVirDev->GetOutputSizePixel())); +} + +void FrameSelectorImpl::DrawAllTrackingRects(vcl::RenderContext& rRenderContext) +{ + tools::PolyPolygon aPPoly; + if (mrFrameSel.IsAnyBorderSelected()) + { + for(SelFrameBorderCIter aIt( maEnabBorders ); aIt.Is(); ++aIt) + (*aIt)->MergeFocusToPolyPolygon(aPPoly); + aPPoly.Move(maVirDevPos.X(), maVirDevPos.Y()); + } + else + // no frame border selected -> draw tracking rectangle around entire control + aPPoly.Insert( tools::Polygon(tools::Rectangle(maVirDevPos, mpVirDev->GetOutputSizePixel()))); + + aPPoly.Optimize(PolyOptimizeFlags::CLOSE); + + for(sal_uInt16 nIdx = 0, nCount = aPPoly.Count(); nIdx < nCount; ++nIdx) + rRenderContext.Invert(aPPoly.GetObject(nIdx), InvertFlags::TrackFrame); +} + +Point FrameSelectorImpl::GetDevPosFromMousePos( const Point& rMousePos ) const +{ + return rMousePos - maVirDevPos; +} + +void FrameSelectorImpl::DoInvalidate( bool bFullRepaint ) +{ + mbFullRepaint |= bFullRepaint; + mrFrameSel.Invalidate(); +} + +// frame border state and style +void FrameSelectorImpl::SetBorderState( FrameBorder& rBorder, FrameBorderState eState ) +{ + DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SetBorderState - access to disabled border" ); + Any aOld; + Any aNew; + Any& rMod = eState == FrameBorderState::Show ? aNew : aOld; + rMod <<= AccessibleStateType::CHECKED; + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + rtl::Reference< a11y::AccFrameSelectorChild > xRet; + size_t nVecIdx = static_cast< size_t >( rBorder.GetType() ); + if( GetBorder(rBorder.GetType()).IsEnabled() && (1 <= nVecIdx) && (nVecIdx <= maChildVec.size()) ) + xRet = maChildVec[ --nVecIdx ].get(); +#endif + + if( eState == FrameBorderState::Show ) + SetBorderCoreStyle( rBorder, &maCurrStyle ); + else + rBorder.SetState( eState ); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (xRet.is()) + xRet->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOld, aNew ); +#endif + + DoInvalidate( true ); +} + +void FrameSelectorImpl::SetBorderCoreStyle( FrameBorder& rBorder, const SvxBorderLine* pStyle ) +{ + DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SetBorderCoreStyle - access to disabled border" ); + rBorder.SetCoreStyle( pStyle ); + DoInvalidate( true ); +} + +void FrameSelectorImpl::ToggleBorderState( FrameBorder& rBorder ) +{ + bool bDontCare = mrFrameSel.SupportsDontCareState(); + switch( rBorder.GetState() ) + { + // same order as tristate check box: visible -> don't care -> hidden + case FrameBorderState::Show: + SetBorderState( rBorder, bDontCare ? FrameBorderState::DontCare : FrameBorderState::Hide ); + break; + case FrameBorderState::Hide: + SetBorderState( rBorder, FrameBorderState::Show ); + break; + case FrameBorderState::DontCare: + SetBorderState( rBorder, FrameBorderState::Hide ); + break; + } +} + +// frame border selection +void FrameSelectorImpl::SelectBorder( FrameBorder& rBorder, bool bSelect ) +{ + DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SelectBorder - access to disabled border" ); + rBorder.Select( bSelect ); + DrawArrows( rBorder ); + DoInvalidate( false ); +} + +void FrameSelectorImpl::SilentGrabFocus() +{ + bool bOldAuto = mbAutoSelect; + mbAutoSelect = false; + mrFrameSel.GrabFocus(); + mbAutoSelect = bOldAuto; +} + +bool FrameSelectorImpl::SelectedBordersEqual() const +{ + bool bEqual = true; + SelFrameBorderCIter aIt( maEnabBorders ); + if( aIt.Is() ) + { + const SvxBorderLine& rFirstStyle = (*aIt)->GetCoreStyle(); + for( ++aIt; bEqual && aIt.Is(); ++aIt ) + bEqual = ((*aIt)->GetCoreStyle() == rFirstStyle); + } + return bEqual; +} + +FrameSelector::FrameSelector() +{ +} + +void FrameSelector::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + mxImpl.reset( new FrameSelectorImpl( *this ) ); + Size aPrefSize = pDrawingArea->get_ref_device().LogicToPixel(Size(61, 65), MapMode(MapUnit::MapAppFont)); + pDrawingArea->set_size_request(aPrefSize.Width(), aPrefSize.Height()); + EnableRTL( false ); // #107808# don't mirror the mouse handling +} + +FrameSelector::~FrameSelector() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if( mxAccess.is() ) + mxAccess->Invalidate(); +#endif +} + +void FrameSelector::Initialize( FrameSelFlags nFlags ) +{ + mxImpl->Initialize( nFlags ); + Show(); +} + +// enabled frame borders +bool FrameSelector::IsBorderEnabled( FrameBorderType eBorder ) const +{ + return mxImpl->GetBorder( eBorder ).IsEnabled(); +} + +sal_Int32 FrameSelector::GetEnabledBorderCount() const +{ + return static_cast< sal_Int32 >( mxImpl->maEnabBorders.size() ); +} + +FrameBorderType FrameSelector::GetEnabledBorderType( sal_Int32 nIndex ) const +{ + FrameBorderType eBorder = FrameBorderType::NONE; + if( nIndex >= 0 ) + { + size_t nVecIdx = static_cast< size_t >( nIndex ); + if( nVecIdx < mxImpl->maEnabBorders.size() ) + eBorder = mxImpl->maEnabBorders[ nVecIdx ]->GetType(); + } + return eBorder; +} + +// frame border state and style +bool FrameSelector::SupportsDontCareState() const +{ + return bool(mxImpl->mnFlags & FrameSelFlags::DontCare); +} + +FrameBorderState FrameSelector::GetFrameBorderState( FrameBorderType eBorder ) const +{ + return mxImpl->GetBorder( eBorder ).GetState(); +} + +const SvxBorderLine* FrameSelector::GetFrameBorderStyle( FrameBorderType eBorder ) const +{ + const SvxBorderLine& rStyle = mxImpl->GetBorder( eBorder ).GetCoreStyle(); + // rest of the world uses null pointer for invisible frame border + return rStyle.GetOutWidth() ? &rStyle : nullptr; +} + +void FrameSelector::ShowBorder( FrameBorderType eBorder, const SvxBorderLine* pStyle ) +{ + mxImpl->SetBorderCoreStyle( mxImpl->GetBorderAccess( eBorder ), pStyle ); +} + +void FrameSelector::SetBorderDontCare( FrameBorderType eBorder ) +{ + mxImpl->SetBorderState( mxImpl->GetBorderAccess( eBorder ), FrameBorderState::DontCare ); +} + +bool FrameSelector::IsAnyBorderVisible() const +{ + bool bIsSet = false; + for( FrameBorderCIter aIt( mxImpl->maEnabBorders ); !bIsSet && aIt.Is(); ++aIt ) + bIsSet = ((*aIt)->GetState() == FrameBorderState::Show); + return bIsSet; +} + +void FrameSelector::HideAllBorders() +{ + for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + mxImpl->SetBorderState( **aIt, FrameBorderState::Hide ); +} + +bool FrameSelector::GetVisibleWidth( tools::Long& rnWidth, SvxBorderLineStyle& rnStyle ) const +{ + VisFrameBorderCIter aIt( mxImpl->maEnabBorders ); + if( !aIt.Is() ) + return false; + + const SvxBorderLine& rStyle = (*aIt)->GetCoreStyle(); + bool bFound = true; + for( ++aIt; bFound && aIt.Is(); ++aIt ) + { + bFound = + (rStyle.GetWidth() == (*aIt)->GetCoreStyle().GetWidth()) && + (rStyle.GetBorderLineStyle() == + (*aIt)->GetCoreStyle().GetBorderLineStyle()); + } + + if( bFound ) + { + rnWidth = rStyle.GetWidth(); + rnStyle = rStyle.GetBorderLineStyle(); + } + return bFound; +} + +bool FrameSelector::GetVisibleColor( Color& rColor ) const +{ + VisFrameBorderCIter aIt( mxImpl->maEnabBorders ); + if( !aIt.Is() ) + return false; + + const SvxBorderLine& rStyle = (*aIt)->GetCoreStyle(); + bool bFound = true; + for( ++aIt; bFound && aIt.Is(); ++aIt ) + bFound = (rStyle.GetColor() == (*aIt)->GetCoreStyle().GetColor()); + + if( bFound ) + rColor = rStyle.GetColor(); + return bFound; +} + +// frame border selection +const Link<LinkParamNone*,void>& FrameSelector::GetSelectHdl() const +{ + return mxImpl->maSelectHdl; +} + +void FrameSelector::SetSelectHdl( const Link<LinkParamNone*,void>& rHdl ) +{ + mxImpl->maSelectHdl = rHdl; +} + +bool FrameSelector::IsBorderSelected( FrameBorderType eBorder ) const +{ + return mxImpl->GetBorder( eBorder ).IsSelected(); +} + +void FrameSelector::SelectBorder( FrameBorderType eBorder ) +{ + mxImpl->SelectBorder( mxImpl->GetBorderAccess( eBorder ), true/*bSelect*/ ); +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + // MT: bFireFox as API parameter is ugly... + // if (bFocus) + { + rtl::Reference< a11y::AccFrameSelectorChild > xRet = GetChildAccessible(eBorder); + if (xRet.is()) + { + Any aOldValue, aNewValue; + aNewValue <<= AccessibleStateType::FOCUSED; + xRet->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } +#endif +} + +bool FrameSelector::IsAnyBorderSelected() const +{ + // Construct an iterator for selected borders. If it is valid, there is a selected border. + return SelFrameBorderCIter( mxImpl->maEnabBorders ).Is(); +} + +void FrameSelector::SelectAllBorders( bool bSelect ) +{ + for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + mxImpl->SelectBorder( **aIt, bSelect ); +} + +void FrameSelector::SelectAllVisibleBorders() +{ + for( VisFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + mxImpl->SelectBorder( **aIt, true/*bSelect*/ ); +} + +void FrameSelector::SetStyleToSelection( tools::Long nWidth, SvxBorderLineStyle nStyle ) +{ + mxImpl->maCurrStyle.SetBorderLineStyle( nStyle ); + mxImpl->maCurrStyle.SetWidth( nWidth ); + for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + mxImpl->SetBorderState( **aIt, FrameBorderState::Show ); +} + +void FrameSelector::SetColorToSelection(const Color& rColor, model::ComplexColor const& rComplexColor) +{ + mxImpl->maCurrStyle.SetColor(rColor); + mxImpl->maCurrStyle.setComplexColor(rComplexColor); + + for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + mxImpl->SetBorderState( **aIt, FrameBorderState::Show ); +} + +SvxBorderLineStyle FrameSelector::getCurrentStyleLineStyle() const +{ + return mxImpl->maCurrStyle.GetBorderLineStyle(); +} + +// accessibility +Reference< XAccessible > FrameSelector::CreateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if( !mxAccess.is() ) + mxAccess = new a11y::AccFrameSelector(*this); +#endif + return mxAccess; +} + +rtl::Reference< a11y::AccFrameSelectorChild > FrameSelector::GetChildAccessible( FrameBorderType eBorder ) +{ + rtl::Reference< a11y::AccFrameSelectorChild > xRet; + size_t nVecIdx = static_cast< size_t >( eBorder ); + if( IsBorderEnabled( eBorder ) && (1 <= nVecIdx) && (nVecIdx <= mxImpl->maChildVec.size()) ) + { + --nVecIdx; + if( !mxImpl->maChildVec[ nVecIdx ].is() ) + mxImpl->maChildVec[ nVecIdx ] = new a11y::AccFrameSelectorChild( *this, eBorder ); + xRet = mxImpl->maChildVec[ nVecIdx ].get(); + } + return xRet; +} + +Reference< XAccessible > FrameSelector::GetChildAccessible( sal_Int32 nIndex ) +{ + return GetChildAccessible( GetEnabledBorderType( nIndex ) ); +} + +Reference< XAccessible > FrameSelector::GetChildAccessible( const Point& rPos ) +{ + Reference< XAccessible > xRet; + for( FrameBorderCIter aIt( mxImpl->maEnabBorders ); !xRet.is() && aIt.Is(); ++aIt ) + if( (*aIt)->ContainsClickPoint( rPos ) ) + xRet = GetChildAccessible( (*aIt)->GetType() ).get(); + return xRet; +} + +tools::Rectangle FrameSelector::GetClickBoundRect( FrameBorderType eBorder ) const +{ + tools::Rectangle aRect; + const FrameBorder& rBorder = mxImpl->GetBorder( eBorder ); + if( rBorder.IsEnabled() ) + aRect = rBorder.GetClickBoundRect(); + return aRect; +} + +// virtual functions from base class +void FrameSelector::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + mxImpl->CopyVirDevToControl(rRenderContext); + if (HasFocus()) + mxImpl->DrawAllTrackingRects(rRenderContext); +} + +bool FrameSelector::MouseButtonDown( const MouseEvent& rMEvt ) +{ + /* Mouse handling: + * Click on an unselected frame border: + Set current style/color, make frame border visible, deselect all + other frame borders. + * Click on a selected frame border: + Toggle state of the frame border (visible -> don't care -> hidden), + deselect all other frame borders. + * SHIFT+Click or CTRL+Click on an unselected frame border: + Extend selection, set current style/color to all selected frame + borders independent of the state/style/color of the borders. + * SHIFT+Click or CTRL+Click on a selected frame border: + If all frame borders have same style/color, toggle state of all + borders (see above), otherwise set current style/color to all + borders. + * Click on unused area: Do not modify selection and selected frame + borders. + */ + + // #107394# do not auto-select a frame border + mxImpl->SilentGrabFocus(); + + if( rMEvt.IsLeft() ) + { + Point aPos( mxImpl->GetDevPosFromMousePos( rMEvt.GetPosPixel() ) ); + FrameBorderPtrVec aDeselectBorders; + + bool bAnyClicked = false; // Any frame border clicked? + bool bNewSelected = false; // Any unselected frame border selected? + + /* If frame borders are set to "don't care" and the control does not + support this state, hide them on first mouse click. + DR 2004-01-30: Why are the borders set to "don't care" then?!? */ + bool bHideDontCare = !SupportsDontCareState(); + + for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + { + if( (*aIt)->ContainsClickPoint( aPos ) ) + { + // frame border is clicked + bAnyClicked = true; + if( !(*aIt)->IsSelected() ) + { + bNewSelected = true; + //mxImpl->SelectBorder( **aIt, true ); + SelectBorder((**aIt).GetType()); + } + } + else + { + // hide a "don't care" frame border only if it is not clicked + if( bHideDontCare && ((*aIt)->GetState() == FrameBorderState::DontCare) ) + mxImpl->SetBorderState( **aIt, FrameBorderState::Hide ); + + // deselect frame borders not clicked (if SHIFT or CTRL are not pressed) + if( !rMEvt.IsShift() && !rMEvt.IsMod1() ) + aDeselectBorders.push_back( *aIt ); + } + } + + if( bAnyClicked ) + { + // any valid frame border clicked? -> deselect other frame borders + for( FrameBorderIter aIt( aDeselectBorders ); aIt.Is(); ++aIt ) + mxImpl->SelectBorder( **aIt, false ); + + if( bNewSelected || !mxImpl->SelectedBordersEqual() ) + { + // new frame border selected, selection extended, or selected borders different? -> show + for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + // SetBorderState() sets current style and color to the frame border + mxImpl->SetBorderState( **aIt, FrameBorderState::Show ); + } + else + { + // all selected frame borders are equal -> toggle state + for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + mxImpl->ToggleBorderState( **aIt ); + } + + GetSelectHdl().Call( nullptr ); + } + } + + return true; +} + +bool FrameSelector::KeyInput( const KeyEvent& rKEvt ) +{ + bool bHandled = false; + vcl::KeyCode aKeyCode = rKEvt.GetKeyCode(); + if( !aKeyCode.GetModifier() ) + { + sal_uInt16 nCode = aKeyCode.GetCode(); + switch( nCode ) + { + case KEY_SPACE: + { + for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + mxImpl->ToggleBorderState( **aIt ); + bHandled = true; + } + break; + + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + { + if( !mxImpl->maEnabBorders.empty() ) + { + // start from first selected frame border + SelFrameBorderCIter aIt( mxImpl->maEnabBorders ); + FrameBorderType eBorder = aIt.Is() ? (*aIt)->GetType() : mxImpl->maEnabBorders.front()->GetType(); + + // search for next enabled frame border + do + { + eBorder = mxImpl->GetBorder( eBorder ).GetKeyboardNeighbor( nCode ); + } + while( (eBorder != FrameBorderType::NONE) && !IsBorderEnabled( eBorder ) ); + + // select the frame border + if( eBorder != FrameBorderType::NONE ) + { + DeselectAllBorders(); + SelectBorder( eBorder ); + } + bHandled = true; + } + } + break; + } + } + if (bHandled) + return true; + return CustomWidgetController::KeyInput(rKEvt); +} + +void FrameSelector::GetFocus() +{ + // auto-selection of a frame border, if focus reaches control, and nothing is selected + if( mxImpl->mbAutoSelect && !IsAnyBorderSelected() && !mxImpl->maEnabBorders.empty() ) + mxImpl->SelectBorder( *mxImpl->maEnabBorders.front(), true ); + + mxImpl->DoInvalidate( false ); + if (IsAnyBorderSelected()) + { + FrameBorderType borderType = FrameBorderType::NONE; + if (mxImpl->maLeft.IsSelected()) + borderType = FrameBorderType::Left; + else if (mxImpl->maRight.IsSelected()) + borderType = FrameBorderType::Right; + else if (mxImpl->maTop.IsSelected()) + borderType = FrameBorderType::Top; + else if (mxImpl->maBottom.IsSelected()) + borderType = FrameBorderType::Bottom; + else if (mxImpl->maHor.IsSelected()) + borderType = FrameBorderType::Horizontal; + else if (mxImpl->maVer.IsSelected()) + borderType = FrameBorderType::Vertical; + else if (mxImpl->maTLBR.IsSelected()) + borderType = FrameBorderType::TLBR; + else if (mxImpl->maBLTR.IsSelected()) + borderType = FrameBorderType::BLTR; + SelectBorder(borderType); + } + for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt ) + mxImpl->SetBorderState( **aIt, FrameBorderState::Show ); + CustomWidgetController::GetFocus(); +} + +void FrameSelector::LoseFocus() +{ + mxImpl->DoInvalidate( false ); + CustomWidgetController::LoseFocus(); +} + +void FrameSelector::StyleUpdated() +{ + mxImpl->InitVirtualDevice(); + CustomWidgetController::StyleUpdated(); +} + +void FrameSelector::Resize() +{ + CustomWidgetController::Resize(); + mxImpl->sizeChanged(); +} + +template< typename Cont, typename Iter, typename Pred > +FrameBorderIterBase< Cont, Iter, Pred >::FrameBorderIterBase( container_type& rCont ) : + maIt( rCont.begin() ), + maEnd( rCont.end() ) +{ + while( Is() && !maPred( *maIt ) ) ++maIt; +} + +template< typename Cont, typename Iter, typename Pred > +FrameBorderIterBase< Cont, Iter, Pred >& FrameBorderIterBase< Cont, Iter, Pred >::operator++() +{ + do { ++maIt; } while( Is() && !maPred( *maIt ) ); + return *this; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/graphctl.cxx b/svx/source/dialog/graphctl.cxx new file mode 100644 index 0000000000..cf127d670a --- /dev/null +++ b/svx/source/dialog/graphctl.cxx @@ -0,0 +1,850 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <svl/itempool.hxx> +#include <vcl/settings.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/svapp.hxx> + +#include <svx/graphctl.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <GraphCtlAccessibleContext.hxx> +#include <svx/svxids.hrc> +#include <svx/svdpage.hxx> +#include <svx/sdrpaintwindow.hxx> + +void GraphCtrlUserCall::Changed( const SdrObject& rObj, SdrUserCallType eType, const tools::Rectangle& /*rOldBoundRect*/ ) +{ + switch( eType ) + { + case SdrUserCallType::MoveOnly: + case SdrUserCallType::Resize: + rWin.SdrObjChanged( rObj ); + break; + + case SdrUserCallType::Inserted: + rWin.SdrObjCreated( rObj ); + break; + + default: + break; + } + rWin.QueueIdleUpdate(); +} + +GraphCtrl::GraphCtrl(weld::Dialog* pDialog) + : aUpdateIdle("svx GraphCtrl Update") + , aMap100(MapUnit::Map100thMM) + , eObjKind(SdrObjKind::NONE) + , nPolyEdit(0) + , bEditMode(false) + , mbSdrMode(false) + , mbInIdleUpdate(false) + , mpDialog(pDialog) +{ + pUserCall.reset(new GraphCtrlUserCall( *this )); + aUpdateIdle.SetPriority( TaskPriority::LOWEST ); + aUpdateIdle.SetInvokeHandler( LINK( this, GraphCtrl, UpdateHdl ) ); + aUpdateIdle.Start(); +} + +void GraphCtrl::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + EnableRTL(false); +} + +GraphCtrl::~GraphCtrl() +{ + aUpdateIdle.Stop(); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if( mpAccContext.is() ) + { + mpAccContext->disposing(); + mpAccContext.clear(); + } +#endif + + pView.reset(); + pModel.reset(); + pUserCall.reset(); +} + +void GraphCtrl::SetSdrMode(bool bSdrMode) +{ + mbSdrMode = bSdrMode; + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + rDevice.SetBackground( Wallpaper( rStyleSettings.GetWindowColor() ) ); + xVD->SetBackground( Wallpaper( rStyleSettings.GetWindowColor() ) ); + rDevice.SetMapMode( aMap100 ); + xVD->SetMapMode( aMap100 ); + + pView.reset(); + pModel.reset(); + + if ( mbSdrMode ) + InitSdrModel(); + + QueueIdleUpdate(); +} + +void GraphCtrl::InitSdrModel() +{ + SolarMutexGuard aGuard; + + rtl::Reference<SdrPage> pPage; + + // destroy old junk + pView.reset(); + pModel.reset(); + + // Creating a Model + pModel.reset(new SdrModel(nullptr, nullptr, true)); + pModel->GetItemPool().FreezeIdRanges(); + pModel->SetScaleUnit(aMap100.GetMapUnit()); + pModel->SetDefaultFontHeight( 500 ); + + pPage = new SdrPage( *pModel ); + + pPage->SetSize( aGraphSize ); + pPage->SetBorder( 0, 0, 0, 0 ); + pModel->InsertPage( pPage.get() ); + pModel->SetChanged( false ); + + // Creating a View + pView.reset(new GraphCtrlView(*pModel, this)); + pView->SetWorkArea( tools::Rectangle( Point(), aGraphSize ) ); + pView->EnableExtendedMouseEventDispatcher( true ); + pView->ShowSdrPage(pView->GetModel().GetPage(0)); + pView->SetFrameDragSingles(); + pView->SetMarkedPointsSmooth( SdrPathSmoothKind::Symmetric ); + pView->SetEditMode(); + + // #i72889# set needed flags + pView->SetPageDecorationAllowed(false); + pView->SetMasterPageVisualizationAllowed(false); + pView->SetBufferedOutputAllowed(true); + pView->SetBufferedOverlayAllowed(true); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + // Tell the accessibility object about the changes. + if (mpAccContext.is()) + mpAccContext->setModelAndView (pModel.get(), pView.get()); +#endif +} + +void GraphCtrl::SetGraphic( const Graphic& rGraphic, bool bNewModel ) +{ + aGraphic = rGraphic; + xVD->SetOutputSizePixel(Size(0, 0)); //force redraw + + if ( aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel ) + aGraphSize = Application::GetDefaultDevice()->PixelToLogic( aGraphic.GetPrefSize(), aMap100 ); + else + aGraphSize = OutputDevice::LogicToLogic( aGraphic.GetPrefSize(), aGraphic.GetPrefMapMode(), aMap100 ); + + if ( mbSdrMode && bNewModel ) + InitSdrModel(); + + aGraphSizeLink.Call( this ); + + Resize(); + + Invalidate(); + QueueIdleUpdate(); +} + +void GraphCtrl::GraphicToVD() +{ + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + xVD->SetOutputSizePixel(GetOutputSizePixel()); + xVD->SetBackground(rDevice.GetBackground()); + xVD->Erase(); + const bool bGraphicValid(GraphicType::NONE != aGraphic.GetType()); + if (bGraphicValid) + aGraphic.Draw(*xVD, Point(), aGraphSize); +} + +void GraphCtrl::Resize() +{ + weld::CustomWidgetController::Resize(); + + if (aGraphSize.Width() && aGraphSize.Height()) + { + MapMode aDisplayMap( aMap100 ); + Point aNewPos; + Size aNewSize; + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + const Size aWinSize = rDevice.PixelToLogic( GetOutputSizePixel(), aDisplayMap ); + const tools::Long nWidth = aWinSize.Width(); + const tools::Long nHeight = aWinSize.Height(); + double fGrfWH = static_cast<double>(aGraphSize.Width()) / aGraphSize.Height(); + double fWinWH = static_cast<double>(nWidth) / nHeight; + + // Adapt Bitmap to Thumb size + if ( fGrfWH < fWinWH) + { + aNewSize.setWidth( static_cast<tools::Long>( static_cast<double>(nHeight) * fGrfWH ) ); + aNewSize.setHeight( nHeight ); + } + else + { + aNewSize.setWidth( nWidth ); + aNewSize.setHeight( static_cast<tools::Long>( static_cast<double>(nWidth) / fGrfWH ) ); + } + + aNewPos.setX( ( nWidth - aNewSize.Width() ) >> 1 ); + aNewPos.setY( ( nHeight - aNewSize.Height() ) >> 1 ); + + // Implementing MapMode for Engine + aDisplayMap.SetScaleX( Fraction( aNewSize.Width(), aGraphSize.Width() ) ); + aDisplayMap.SetScaleY( Fraction( aNewSize.Height(), aGraphSize.Height() ) ); + + aDisplayMap.SetOrigin( OutputDevice::LogicToLogic( aNewPos, aMap100, aDisplayMap ) ); + rDevice.SetMapMode( aDisplayMap ); + xVD->SetMapMode( aDisplayMap ); + } + + Invalidate(); +} + +void GraphCtrl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + // #i72889# used split repaint to be able to paint an own background + // even to the buffered view + const bool bGraphicValid(GraphicType::NONE != aGraphic.GetType()); + + if (GetOutputSizePixel() != xVD->GetOutputSizePixel()) + GraphicToVD(); + + if (mbSdrMode) + { + SdrPaintWindow* pPaintWindow = pView->BeginCompleteRedraw(&rRenderContext); + pPaintWindow->SetOutputToWindow(true); + + if (bGraphicValid) + { + vcl::RenderContext& rTarget = pPaintWindow->GetTargetOutputDevice(); + + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + rTarget.SetBackground(rDevice.GetBackground()); + rTarget.Erase(); + + rTarget.DrawOutDev(Point(), xVD->GetOutputSize(), Point(), xVD->GetOutputSize(), *xVD); + } + + const vcl::Region aRepaintRegion(rRect); + pView->DoCompleteRedraw(*pPaintWindow, aRepaintRegion); + pView->EndCompleteRedraw(*pPaintWindow, true); + } + else + { + // #i73381# in non-SdrMode, paint to local directly + rRenderContext.DrawOutDev(rRect.TopLeft(), rRect.GetSize(), + rRect.TopLeft(), rRect.GetSize(), + *xVD); + } +} + +void GraphCtrl::SdrObjChanged( const SdrObject& ) +{ + QueueIdleUpdate(); +} + +void GraphCtrl::SdrObjCreated( const SdrObject& ) +{ + QueueIdleUpdate(); +} + +void GraphCtrl::MarkListHasChanged() +{ + QueueIdleUpdate(); +} + +bool GraphCtrl::KeyInput( const KeyEvent& rKEvt ) +{ + vcl::KeyCode aCode( rKEvt.GetKeyCode() ); + bool bProc = false; + + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + + switch ( aCode.GetCode() ) + { + case KEY_DELETE: + case KEY_BACKSPACE: + { + if ( mbSdrMode ) + { + pView->DeleteMarked(); + bProc = true; + } + } + break; + + case KEY_ESCAPE: + { + if ( mbSdrMode ) + { + if ( pView->IsAction() ) + { + pView->BrkAction(); + bProc = true; + } + else if ( pView->AreObjectsMarked() ) + { + pView->UnmarkAllObj(); + bProc = true; + } + } + } + break; + + case KEY_F11: + case KEY_TAB: + { + if( mbSdrMode ) + { + if( !aCode.IsMod1() && !aCode.IsMod2() ) + { + bool bForward = !aCode.IsShift(); + // select next object + if ( ! pView->MarkNextObj( bForward )) + { + // At first or last object. Cycle to the other end + // of the list. + pView->UnmarkAllObj(); + pView->MarkNextObj (bForward); + } + bProc = true; + } + else if(aCode.IsMod1()) + { + // select next handle + const SdrHdlList& rHdlList = pView->GetHdlList(); + bool bForward(!aCode.IsShift()); + + const_cast<SdrHdlList&>(rHdlList).TravelFocusHdl(bForward); + + bProc = true; + } + } + } + break; + + case KEY_END: + { + + if ( aCode.IsMod1() ) + { + // mark last object + pView->UnmarkAllObj(); + pView->MarkNextObj(); + + bProc = true; + } + } + break; + + case KEY_HOME: + { + if ( aCode.IsMod1() ) + { + pView->UnmarkAllObj(); + pView->MarkNextObj(true); + + bProc = true; + } + } + break; + + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + { + tools::Long nX = 0; + tools::Long nY = 0; + + if (aCode.GetCode() == KEY_UP) + { + // Scroll up + nX = 0; + nY =-1; + } + else if (aCode.GetCode() == KEY_DOWN) + { + // Scroll down + nX = 0; + nY = 1; + } + else if (aCode.GetCode() == KEY_LEFT) + { + // Scroll left + nX =-1; + nY = 0; + } + else if (aCode.GetCode() == KEY_RIGHT) + { + // Scroll right + nX = 1; + nY = 0; + } + + if (pView->AreObjectsMarked() && !aCode.IsMod1() ) + { + if(aCode.IsMod2()) + { + // move in 1 pixel distance + Size aLogicSizeOnePixel = rDevice.PixelToLogic(Size(1,1)); + nX *= aLogicSizeOnePixel.Width(); + nY *= aLogicSizeOnePixel.Height(); + } + else + { + // old, fixed move distance + nX *= 100; + nY *= 100; + } + + // II + const SdrHdlList& rHdlList = pView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(nullptr == pHdl) + { + // restrict movement to WorkArea + const tools::Rectangle& rWorkArea = pView->GetWorkArea(); + + if(!rWorkArea.IsEmpty()) + { + tools::Rectangle aMarkRect(pView->GetMarkedObjRect()); + aMarkRect.Move(nX, nY); + + if(!aMarkRect.Contains(rWorkArea)) + { + if(aMarkRect.Left() < rWorkArea.Left()) + { + nX += rWorkArea.Left() - aMarkRect.Left(); + } + + if(aMarkRect.Right() > rWorkArea.Right()) + { + nX -= aMarkRect.Right() - rWorkArea.Right(); + } + + if(aMarkRect.Top() < rWorkArea.Top()) + { + nY += rWorkArea.Top() - aMarkRect.Top(); + } + + if(aMarkRect.Bottom() > rWorkArea.Bottom()) + { + nY -= aMarkRect.Bottom() - rWorkArea.Bottom(); + } + } + } + + // no handle selected + if(0 != nX || 0 != nY) + { + pView->MoveAllMarked(Size(nX, nY)); + } + } + else + { + // move handle with index nHandleIndex + if (nX || nY) + { + // now move the Handle (nX, nY) + Point aStartPoint(pHdl->GetPos()); + Point aEndPoint(pHdl->GetPos() + Point(nX, nY)); + const SdrDragStat& rDragStat = pView->GetDragStat(); + + // start dragging + pView->BegDragObj(aStartPoint, nullptr, pHdl, 0); + + if(pView->IsDragObj()) + { + bool bWasNoSnap = rDragStat.IsNoSnap(); + bool bWasSnapEnabled = pView->IsSnapEnabled(); + + // switch snapping off + if(!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(); + if(bWasSnapEnabled) + pView->SetSnapEnabled(false); + + pView->MovAction(aEndPoint); + pView->EndDragObj(); + + // restore snap + if(!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(bWasNoSnap); + if(bWasSnapEnabled) + pView->SetSnapEnabled(bWasSnapEnabled); + } + } + } + + bProc = true; + } + } + break; + + case KEY_SPACE: + { + const SdrHdlList& rHdlList = pView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + if(pHdl->GetKind() == SdrHdlKind::Poly) + { + // rescue ID of point with focus + sal_uInt32 nPol(pHdl->GetPolyNum()); + sal_uInt32 nPnt(pHdl->GetPointNum()); + + if(pView->IsPointMarked(*pHdl)) + { + if(rKEvt.GetKeyCode().IsShift()) + { + pView->UnmarkPoint(*pHdl); + } + } + else + { + if(!rKEvt.GetKeyCode().IsShift()) + { + pView->UnmarkAllPoints(); + } + + pView->MarkPoint(*pHdl); + } + + if(nullptr == rHdlList.GetFocusHdl()) + { + // restore point with focus + SdrHdl* pNewOne = nullptr; + + for(size_t a = 0; !pNewOne && a < rHdlList.GetHdlCount(); ++a) + { + SdrHdl* pAct = rHdlList.GetHdl(a); + + if(pAct + && pAct->GetKind() == SdrHdlKind::Poly + && pAct->GetPolyNum() == nPol + && pAct->GetPointNum() == nPnt) + { + pNewOne = pAct; + } + } + + if(pNewOne) + { + const_cast<SdrHdlList&>(rHdlList).SetFocusHdl(pNewOne); + } + } + + bProc = true; + } + } + } + break; + + default: + break; + } + + if (bProc) + ReleaseMouse(); + + QueueIdleUpdate(); + + return bProc; +} + +bool GraphCtrl::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( mbSdrMode && ( rMEvt.GetClicks() < 2 ) ) + { + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + + const Point aLogPt( rDevice.PixelToLogic( rMEvt.GetPosPixel() ) ); + + if ( !tools::Rectangle( Point(), aGraphSize ).Contains( aLogPt ) && !pView->IsEditMode() ) + weld::CustomWidgetController::MouseButtonDown( rMEvt ); + else + { + // Get Focus for key inputs + GrabFocus(); + + if ( nPolyEdit ) + { + SdrViewEvent aVEvt; + SdrHitKind eHit = pView->PickAnything( rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt ); + + if ( nPolyEdit == SID_BEZIER_INSERT && eHit == SdrHitKind::MarkedObject ) + pView->BegInsObjPoint( aLogPt, rMEvt.IsMod1()); + else + pView->MouseButtonDown( rMEvt, &rDevice ); + } + else + pView->MouseButtonDown( rMEvt, &rDevice ); + } + + SdrObject* pCreateObj = pView->GetCreateObj(); + + // We want to realize the insert + if ( pCreateObj && !pCreateObj->GetUserCall() ) + pCreateObj->SetUserCall( pUserCall.get() ); + + SetPointer( pView->GetPreferredPointer( aLogPt, &rDevice ) ); + } + else + weld::CustomWidgetController::MouseButtonDown( rMEvt ); + + QueueIdleUpdate(); + + return false; +} + +bool GraphCtrl::MouseMove(const MouseEvent& rMEvt) +{ + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + const Point aLogPos( rDevice.PixelToLogic( rMEvt.GetPosPixel() ) ); + + if ( mbSdrMode ) + { + pView->MouseMove( rMEvt, &rDevice ); + + if( ( SID_BEZIER_INSERT == nPolyEdit ) && + !pView->PickHandle( aLogPos ) && + !pView->IsInsObjPoint() ) + { + SetPointer( PointerStyle::Cross ); + } + else + SetPointer( pView->GetPreferredPointer( aLogPos, &rDevice ) ); + } + else + weld::CustomWidgetController::MouseButtonUp( rMEvt ); + + if ( aMousePosLink.IsSet() ) + { + if ( tools::Rectangle( Point(), aGraphSize ).Contains( aLogPos ) ) + aMousePos = aLogPos; + else + aMousePos = Point(); + + aMousePosLink.Call( this ); + } + + QueueIdleUpdate(); + + return false; +} + +bool GraphCtrl::MouseButtonUp(const MouseEvent& rMEvt) +{ + if ( mbSdrMode ) + { + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + + if ( pView->IsInsObjPoint() ) + pView->EndInsObjPoint( SdrCreateCmd::ForceEnd ); + else + pView->MouseButtonUp( rMEvt, &rDevice ); + + ReleaseMouse(); + SetPointer( pView->GetPreferredPointer( rDevice.PixelToLogic( rMEvt.GetPosPixel() ), &rDevice ) ); + } + else + weld::CustomWidgetController::MouseButtonUp( rMEvt ); + + QueueIdleUpdate(); + + return false; +} + +SdrObject* GraphCtrl::GetSelectedSdrObject() const +{ + SdrObject* pSdrObj = nullptr; + + if ( mbSdrMode ) + { + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 ) + pSdrObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + } + + return pSdrObj; +} + +void GraphCtrl::SetEditMode( const bool _bEditMode ) +{ + if ( mbSdrMode ) + { + bEditMode = _bEditMode; + pView->SetEditMode( bEditMode ); + eObjKind = SdrObjKind::NONE; + pView->SetCurrentObj(eObjKind); + } + else + bEditMode = false; + + QueueIdleUpdate(); +} + +void GraphCtrl::SetPolyEditMode( const sal_uInt16 _nPolyEdit ) +{ + if ( mbSdrMode && ( _nPolyEdit != nPolyEdit ) ) + { + nPolyEdit = _nPolyEdit; + pView->SetFrameDragSingles( nPolyEdit == 0 ); + } + else + nPolyEdit = 0; + + QueueIdleUpdate(); +} + +void GraphCtrl::SetObjKind( const SdrObjKind _eObjKind ) +{ + if ( mbSdrMode ) + { + bEditMode = false; + pView->SetEditMode( bEditMode ); + eObjKind = _eObjKind; + pView->SetCurrentObj(eObjKind); + } + else + eObjKind = SdrObjKind::NONE; + + QueueIdleUpdate(); +} + +IMPL_LINK_NOARG(GraphCtrl, UpdateHdl, Timer *, void) +{ + mbInIdleUpdate = true; + aUpdateLink.Call( this ); + mbInIdleUpdate = false; +} + +void GraphCtrl::QueueIdleUpdate() +{ + if (!mbInIdleUpdate) + aUpdateIdle.Start(); +} + +namespace +{ + class WeldOverlayManager final : public sdr::overlay::OverlayManager + { + weld::CustomWidgetController& m_rGraphCtrl; + + public: + WeldOverlayManager(weld::CustomWidgetController& rGraphCtrl, OutputDevice& rDevice) + : OverlayManager(rDevice) + , m_rGraphCtrl(rGraphCtrl) + { + } + + // invalidate the given range at local OutputDevice + virtual void invalidateRange(const basegfx::B2DRange& rRange) override + { + tools::Rectangle aInvalidateRectangle(RangeToInvalidateRectangle(rRange)); + m_rGraphCtrl.Invalidate(aInvalidateRectangle); + } + }; +} + +rtl::Reference<sdr::overlay::OverlayManager> GraphCtrlView::CreateOverlayManager(OutputDevice& rDevice) const +{ + assert(&rDevice == &rGraphCtrl.GetDrawingArea()->get_ref_device()); + if (rDevice.GetOutDevType() == OUTDEV_VIRDEV) + { + rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager(new WeldOverlayManager(rGraphCtrl, rDevice)); + InitOverlayManager(xOverlayManager); + return xOverlayManager; + } + return SdrView::CreateOverlayManager(rDevice); +} + +void GraphCtrlView::InvalidateOneWin(OutputDevice& rDevice) +{ + assert(&rDevice == &rGraphCtrl.GetDrawingArea()->get_ref_device()); + if (rDevice.GetOutDevType() == OUTDEV_VIRDEV) + { + rGraphCtrl.Invalidate(); + return; + } + SdrView::InvalidateOneWin(rDevice); +} + +void GraphCtrlView::InvalidateOneWin(OutputDevice& rDevice, const tools::Rectangle& rArea) +{ + assert(&rDevice == &rGraphCtrl.GetDrawingArea()->get_ref_device()); + if (rDevice.GetOutDevType() == OUTDEV_VIRDEV) + { + rGraphCtrl.Invalidate(rArea); + return; + } + SdrView::InvalidateOneWin(rDevice, rArea); +} + +GraphCtrlView::~GraphCtrlView() +{ + // turn SetOutputToWindow back off again before + // turning back into our baseclass during dtoring + const sal_uInt32 nWindowCount(PaintWindowCount()); + for (sal_uInt32 nWinNum(0); nWinNum < nWindowCount; nWinNum++) + { + SdrPaintWindow* pPaintWindow = GetPaintWindow(nWinNum); + pPaintWindow->SetOutputToWindow(false); + } +} + +Point GraphCtrl::GetPositionInDialog() const +{ + int x, y, width, height; + if (GetDrawingArea()->get_extents_relative_to(*mpDialog, x, y, width, height)) + return Point(x, y); + return Point(); +} + +css::uno::Reference< css::accessibility::XAccessible > GraphCtrl::CreateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if(mpAccContext == nullptr ) + { + // Disable accessibility if no model/view data available + if (pView && pModel) + mpAccContext = new SvxGraphCtrlAccessibleContext(*this); + } +#endif + return mpAccContext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/grfflt.cxx b/svx/source/dialog/grfflt.cxx new file mode 100644 index 0000000000..81b9289009 --- /dev/null +++ b/svx/source/dialog/grfflt.cxx @@ -0,0 +1,294 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/BitmapSharpenFilter.hxx> +#include <vcl/BitmapMedianFilter.hxx> +#include <vcl/BitmapSobelGreyFilter.hxx> +#include <vcl/BitmapPopArtFilter.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/request.hxx> + +#include <osl/diagnose.h> +#include <svx/grfflt.hxx> +#include <svx/svxids.hrc> +#include <svx/svxdlg.hxx> + + +SvxGraphicFilterResult SvxGraphicFilter::ExecuteGrfFilterSlot( SfxRequest const & rReq, GraphicObject& rFilterObject ) +{ + const Graphic& rGraphic = rFilterObject.GetGraphic(); + SvxGraphicFilterResult nRet = SvxGraphicFilterResult::UnsupportedGraphicType; + + if( rGraphic.GetType() == GraphicType::Bitmap ) + { + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + SfxObjectShell* pShell = pViewFrame ? pViewFrame->GetObjectShell() : nullptr; + weld::Window* pFrameWeld = (pViewFrame && pViewFrame->GetViewShell()) ? pViewFrame->GetViewShell()->GetFrameWeld() : nullptr; + Graphic aGraphic; + + switch( rReq.GetSlot() ) + { + case SID_GRFFILTER_INVERT: + { + if( pShell ) + pShell->SetWaitCursor( true ); + + if( rGraphic.IsAnimated() ) + { + Animation aAnimation( rGraphic.GetAnimation() ); + + if( aAnimation.Invert() ) + aGraphic = aAnimation; + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if( aBmpEx.Invert() ) + aGraphic = aBmpEx; + } + + if( pShell ) + pShell->SetWaitCursor( false ); + } + break; + + case SID_GRFFILTER_SMOOTH: + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterSmooth(pFrameWeld, rGraphic, 0.7)); + if( aDlg->Execute() == RET_OK ) + aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 ); + } + break; + + case SID_GRFFILTER_SHARPEN: + { + if( pShell ) + pShell->SetWaitCursor( true ); + + if( rGraphic.IsAnimated() ) + { + Animation aAnimation( rGraphic.GetAnimation() ); + + if (BitmapFilter::Filter(aAnimation, BitmapSharpenFilter())) + aGraphic = aAnimation; + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapSharpenFilter())) + aGraphic = aBmpEx; + } + + if( pShell ) + pShell->SetWaitCursor( false ); + } + break; + + case SID_GRFFILTER_REMOVENOISE: + { + if( pShell ) + pShell->SetWaitCursor( true ); + + if( rGraphic.IsAnimated() ) + { + Animation aAnimation( rGraphic.GetAnimation() ); + + if (BitmapFilter::Filter(aAnimation, BitmapMedianFilter())) + aGraphic = aAnimation; + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapMedianFilter())) + aGraphic = aBmpEx; + } + + if( pShell ) + pShell->SetWaitCursor( false ); + } + break; + + case SID_GRFFILTER_SOBEL: + { + if( pShell ) + pShell->SetWaitCursor( true ); + + if( rGraphic.IsAnimated() ) + { + Animation aAnimation( rGraphic.GetAnimation() ); + + if (BitmapFilter::Filter(aAnimation, BitmapSobelGreyFilter())) + aGraphic = aAnimation; + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapSobelGreyFilter())) + aGraphic = aBmpEx; + } + + if( pShell ) + pShell->SetWaitCursor( false ); + } + break; + + case SID_GRFFILTER_MOSAIC: + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterMosaic(pFrameWeld, rGraphic)); + if( aDlg->Execute() == RET_OK ) + aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 ); + } + break; + + case SID_GRFFILTER_EMBOSS: + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterEmboss(pFrameWeld, rGraphic)); + if( aDlg->Execute() == RET_OK ) + aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 ); + } + break; + + case SID_GRFFILTER_POSTER: + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterPoster(pFrameWeld, rGraphic)); + if( aDlg->Execute() == RET_OK ) + aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 ); + } + break; + + case SID_GRFFILTER_POPART: + { + if( pShell ) + pShell->SetWaitCursor( true ); + + if( rGraphic.IsAnimated() ) + { + Animation aAnimation( rGraphic.GetAnimation() ); + + if (BitmapFilter::Filter(aAnimation, BitmapPopArtFilter())) + aGraphic = aAnimation; + } + else + { + BitmapEx aBmpEx( rGraphic.GetBitmapEx() ); + + if (BitmapFilter::Filter(aBmpEx, BitmapPopArtFilter())) + aGraphic = aBmpEx; + } + + if( pShell ) + pShell->SetWaitCursor( false ); + } + break; + + case SID_GRFFILTER_SEPIA: + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterSepia(pFrameWeld, rGraphic)); + if( aDlg->Execute() == RET_OK ) + aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 ); + } + break; + + case SID_GRFFILTER_SOLARIZE: + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterSolarize(pFrameWeld, rGraphic)); + if( aDlg->Execute() == RET_OK ) + aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 ); + } + break; + + case SID_GRFFILTER : + { + // do nothing; no error + nRet = SvxGraphicFilterResult::NONE; + break; + } + + default: + { + OSL_FAIL( "SvxGraphicFilter: selected filter slot not yet implemented" ); + nRet = SvxGraphicFilterResult::UnsupportedSlot; + } + break; + } + + if( aGraphic.GetType() != GraphicType::NONE ) + { + rFilterObject.SetGraphic( aGraphic ); + nRet = SvxGraphicFilterResult::NONE; + } + } + + return nRet; +} + + +void SvxGraphicFilter::DisableGraphicFilterSlots( SfxItemSet& rSet ) +{ + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER ) ) + rSet.DisableItem( SID_GRFFILTER ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_INVERT ) ) + rSet.DisableItem( SID_GRFFILTER_INVERT ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SMOOTH ) ) + rSet.DisableItem( SID_GRFFILTER_SMOOTH ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SHARPEN ) ) + rSet.DisableItem( SID_GRFFILTER_SHARPEN ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_REMOVENOISE ) ) + rSet.DisableItem( SID_GRFFILTER_REMOVENOISE ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SOBEL ) ) + rSet.DisableItem( SID_GRFFILTER_SOBEL ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_MOSAIC ) ) + rSet.DisableItem( SID_GRFFILTER_MOSAIC ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_EMBOSS ) ) + rSet.DisableItem( SID_GRFFILTER_EMBOSS ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_POSTER ) ) + rSet.DisableItem( SID_GRFFILTER_POSTER ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_POPART ) ) + rSet.DisableItem( SID_GRFFILTER_POPART ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SEPIA ) ) + rSet.DisableItem( SID_GRFFILTER_SEPIA ); + + if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SOLARIZE ) ) + rSet.DisableItem( SID_GRFFILTER_SOLARIZE ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/hdft.cxx b/svx/source/dialog/hdft.cxx new file mode 100644 index 0000000000..2052a072c0 --- /dev/null +++ b/svx/source/dialog/hdft.cxx @@ -0,0 +1,1046 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <o3tl/unit_conversion.hxx> +#include <svl/itemiter.hxx> +#include <sfx2/objsh.hxx> +#include <svx/svxids.hrc> + +#include <svl/intitem.hxx> +#include <svtools/unitconv.hxx> + +#include <svx/hdft.hxx> +#include <svx/pageitem.hxx> + +#include <svx/dlgutil.hxx> +#include <sfx2/htmlmode.hxx> +#include <osl/diagnose.h> + +#include <editeng/brushitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/sizeitem.hxx> +#include <editeng/boxitem.hxx> + +#include <svx/svxdlg.hxx> +#include <memory> + +#include <svx/xdef.hxx> +#include <svx/xfillit0.hxx> +#include <svx/unobrushitemhelper.hxx> + +using namespace com::sun::star; + +// Word 97 incompatibility (#i19922#) +// #i19922# - tdf#126051 see cui/source/tabpages/page.cxx and sw/source/uibase/sidebar/PageMarginControl.hxx +constexpr tools::Long MINBODY = o3tl::toTwips(1, o3tl::Length::mm); // 1mm in twips rounded + +// default distance to Header or footer +const tools::Long DEF_DIST_WRITER = 500; // 5mm (Writer) +const tools::Long DEF_DIST_CALC = 250; // 2.5mm (Calc) + +const WhichRangesContainer SvxHFPage::pRanges(svl::Items< + // Support DrawingLayer FillStyles (no real call to below GetRanges() + // detected, still do the complete transition) + XATTR_FILL_FIRST, XATTR_FILL_LAST, + + SID_ATTR_BRUSH, SID_ATTR_BRUSH, + SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER, + SID_ATTR_BORDER_OUTER, SID_ATTR_BORDER_OUTER, + SID_ATTR_BORDER_SHADOW, SID_ATTR_BORDER_SHADOW, + SID_ATTR_LRSPACE, SID_ATTR_LRSPACE, + SID_ATTR_ULSPACE, SID_ATTR_ULSPACE, + SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE, + SID_ATTR_PAGE_HEADERSET, SID_ATTR_PAGE_HEADERSET, + SID_ATTR_PAGE_FOOTERSET, SID_ATTR_PAGE_FOOTERSET, + SID_ATTR_PAGE_ON, SID_ATTR_PAGE_ON, + SID_ATTR_PAGE_DYNAMIC, SID_ATTR_PAGE_DYNAMIC, + SID_ATTR_PAGE_SHARED, SID_ATTR_PAGE_SHARED, + SID_ATTR_HDFT_DYNAMIC_SPACING, SID_ATTR_HDFT_DYNAMIC_SPACING, + SID_ATTR_PAGE_SHARED_FIRST, SID_ATTR_PAGE_SHARED_FIRST +>); + +namespace svx { + + bool ShowBorderBackgroundDlg(weld::Window* pParent, SfxItemSet* pBBSet) + { + bool bRes = false; + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSvxBorderBackgroundDlg(pParent, *pBBSet, true /*bEnableDrawingLayerFillStyles*/)); + if ( pDlg->Execute() == RET_OK && pDlg->GetOutputItemSet() ) + { + SfxItemIter aIter( *pDlg->GetOutputItemSet() ); + + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + if ( !IsInvalidItem( pItem ) ) + pBBSet->Put( *pItem ); + } + bRes = true; + } + return bRes; + } +} + +std::unique_ptr<SfxTabPage> SvxHeaderPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet ) +{ + return std::make_unique<SvxHeaderPage>( pPage, pController, *rSet ); +} + +std::unique_ptr<SfxTabPage> SvxFooterPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet ) +{ + return std::make_unique<SvxFooterPage>( pPage, pController, *rSet ); +} + +SvxHeaderPage::SvxHeaderPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttr) + : SvxHFPage( pPage, pController, rAttr, SID_ATTR_PAGE_HEADERSET ) +{ +} + +SvxFooterPage::SvxFooterPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttr) + : SvxHFPage( pPage, pController, rAttr, SID_ATTR_PAGE_FOOTERSET ) +{ +} + +SvxHFPage::SvxHFPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet, sal_uInt16 nSetId) + : SfxTabPage(pPage, pController, "svx/ui/headfootformatpage.ui", "HFFormatPage", &rSet) + , nId(nSetId) + , mbDisableQueryBox(false) + , mbEnableDrawingLayerFillStyles(false) + , m_xCntSharedBox(m_xBuilder->weld_check_button("checkSameLR")) + , m_xCntSharedFirstBox(m_xBuilder->weld_check_button("checkSameFP")) + , m_xLMLbl(m_xBuilder->weld_label("labelLeftMarg")) + , m_xLMEdit(m_xBuilder->weld_metric_spin_button("spinMargLeft", FieldUnit::CM)) + , m_xRMLbl(m_xBuilder->weld_label("labelRightMarg")) + , m_xRMEdit(m_xBuilder->weld_metric_spin_button("spinMargRight", FieldUnit::CM)) + , m_xDistFT(m_xBuilder->weld_label("labelSpacing")) + , m_xDistEdit(m_xBuilder->weld_metric_spin_button("spinSpacing", FieldUnit::CM)) + , m_xDynSpacingCB(m_xBuilder->weld_check_button("checkDynSpacing")) + , m_xHeightFT(m_xBuilder->weld_label("labelHeight")) + , m_xHeightEdit(m_xBuilder->weld_metric_spin_button("spinHeight", FieldUnit::CM)) + , m_xHeightDynBtn(m_xBuilder->weld_check_button("checkAutofit")) + , m_xBackgroundBtn(m_xBuilder->weld_button("buttonMore")) + , m_xBspWin(new weld::CustomWeld(*m_xBuilder, "drawingareaPageHF", m_aBspWin)) +{ + //swap header <-> footer in UI + if (nId == SID_ATTR_PAGE_FOOTERSET) + { + m_xContainer->set_help_id("svx/ui/headfootformatpage/FFormatPage"); + m_xPageLbl = m_xBuilder->weld_label("labelFooterFormat"); + m_xTurnOnBox = m_xBuilder->weld_check_button("checkFooterOn"); + + /* Set custom HIDs for the Footer help page (shared/01/05040400.xhp) + otherwise it would display the same extended help + on both the Header and Footer tabs */ + m_xCntSharedBox->set_help_id( "SVX_HID_FOOTER_CHECKSAMELR" ); + m_xCntSharedFirstBox->set_help_id( "SVX_HID_FOOTER_CHECKSAMEFP" ); + m_xLMEdit->set_help_id( "SVX_HID_FOOTER_SPINMARGLEFT" ); + m_xRMEdit->set_help_id( "SVX_HID_FOOTER_SPINMARGRIGHT" ); + m_xDistEdit->set_help_id( "SVX_HID_FOOTER_SPINSPACING" ); + m_xDynSpacingCB->set_help_id( "SVX_HID_FOOTER_CHECKDYNSPACING" ); + m_xHeightEdit->set_help_id( "SVX_HID_FOOTER_SPINHEIGHT" ); + m_xHeightDynBtn->set_help_id( "SVX_HID_FOOTER_CHECKAUTOFIT" ); + m_xBackgroundBtn->set_help_id( "SVX_HID_FOOTER_BUTTONMORE" ); + } + else //Header + { + m_xContainer->set_help_id("svx/ui/headfootformatpage/HFormatPage"); + m_xPageLbl = m_xBuilder->weld_label("labelHeaderFormat"); + m_xTurnOnBox = m_xBuilder->weld_check_button("checkHeaderOn"); + } + m_xTurnOnBox->show(); + m_xPageLbl->show(); + + InitHandler(); + m_aBspWin.EnableRTL(false); + + // This Page needs ExchangeSupport + SetExchangeSupport(); + + // Set metrics + FieldUnit eFUnit = GetModuleFieldUnit( rSet ); + SetFieldUnit( *m_xDistEdit, eFUnit ); + SetFieldUnit( *m_xHeightEdit, eFUnit ); + SetFieldUnit( *m_xLMEdit, eFUnit ); + SetFieldUnit( *m_xRMEdit, eFUnit ); +} + +SvxHFPage::~SvxHFPage() +{ +} + +bool SvxHFPage::FillItemSet( SfxItemSet* rSet ) +{ + const sal_uInt16 nWSize = GetWhich(SID_ATTR_PAGE_SIZE); + const sal_uInt16 nWLRSpace = GetWhich(SID_ATTR_LRSPACE); + const sal_uInt16 nWULSpace = GetWhich(SID_ATTR_ULSPACE); + const sal_uInt16 nWOn = GetWhich(SID_ATTR_PAGE_ON); + const sal_uInt16 nWDynamic = GetWhich(SID_ATTR_PAGE_DYNAMIC); + const sal_uInt16 nWDynSpacing = GetWhich(SID_ATTR_HDFT_DYNAMIC_SPACING); + const sal_uInt16 nWShared = GetWhich(SID_ATTR_PAGE_SHARED); + const sal_uInt16 nWSharedFirst = GetWhich( SID_ATTR_PAGE_SHARED_FIRST ); + const sal_uInt16 nWBrush = GetWhich(SID_ATTR_BRUSH); + const sal_uInt16 nWBox = GetWhich(SID_ATTR_BORDER_OUTER); + const sal_uInt16 nWBoxInfo = GetWhich(SID_ATTR_BORDER_INNER); + const sal_uInt16 nWShadow = GetWhich(SID_ATTR_BORDER_SHADOW); + + const SfxItemSet& rOldSet = GetItemSet(); + SfxItemPool* pPool = rOldSet.GetPool(); + DBG_ASSERT(pPool,"no pool :-("); + MapUnit eUnit = pPool->GetMetric(nWSize); + // take over DrawingLayer FillStyles + SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aSet(*pPool); + // Keep it valid + aSet.MergeRange(nWSize, nWSize); + aSet.MergeRange(nWLRSpace, nWLRSpace); + aSet.MergeRange(nWULSpace, nWULSpace); + aSet.MergeRange(nWOn, nWOn); + aSet.MergeRange(nWDynamic, nWDynamic); + aSet.MergeRange(nWShared, nWShared); + aSet.MergeRange(nWSharedFirst, nWSharedFirst); + aSet.MergeRange(nWBrush, nWBrush); + aSet.MergeRange(nWBoxInfo, nWBoxInfo); + aSet.MergeRange(nWBox, nWBox); + aSet.MergeRange(nWShadow, nWShadow); + aSet.MergeRange(nWDynSpacing, nWDynSpacing); + + if(mbEnableDrawingLayerFillStyles) + { + // When using the XATTR_FILLSTYLE DrawingLayer FillStyle definition + // extra action has to be done here since the pool default is drawing::FillStyle_SOLID + // instead of drawing::FillStyle_NONE (to have the default blue fill color at start). + aSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + + aSet.Put( SfxBoolItem( nWOn, m_xTurnOnBox->get_active() ) ); + aSet.Put( SfxBoolItem( nWDynamic, m_xHeightDynBtn->get_active() ) ); + aSet.Put( SfxBoolItem( nWShared, m_xCntSharedBox->get_active() ) ); + if(m_xCntSharedFirstBox->get_visible()) + aSet.Put(SfxBoolItem(nWSharedFirst, m_xCntSharedFirstBox->get_active())); + if (m_xDynSpacingCB->get_visible() && SfxItemPool::IsWhich(nWDynSpacing)) + { + std::unique_ptr<SfxBoolItem> pBoolItem(static_cast<SfxBoolItem*>(pPool->GetDefaultItem(nWDynSpacing).Clone())); + pBoolItem->SetValue(m_xDynSpacingCB->get_active()); + aSet.Put(std::move(pBoolItem)); + } + + // Size + SvxSizeItem aSizeItem( static_cast<const SvxSizeItem&>(rOldSet.Get( nWSize )) ); + Size aSize( aSizeItem.GetSize() ); + tools::Long nDist = GetCoreValue( *m_xDistEdit, eUnit ); + tools::Long nH = GetCoreValue( *m_xHeightEdit, eUnit ); + + nH += nDist; // add distance + aSize.setHeight( nH ); + aSizeItem.SetSize( aSize ); + aSet.Put( aSizeItem ); + + // Margins + SvxLRSpaceItem aLR( nWLRSpace ); + aLR.SetLeft( static_cast<sal_uInt16>(GetCoreValue( *m_xLMEdit, eUnit )) ); + aLR.SetRight( static_cast<sal_uInt16>(GetCoreValue( *m_xRMEdit, eUnit )) ); + aSet.Put( aLR ); + + SvxULSpaceItem aUL( nWULSpace ); + if ( nId == SID_ATTR_PAGE_HEADERSET ) + aUL.SetLower( static_cast<sal_uInt16>(nDist) ); + else + aUL.SetUpper( static_cast<sal_uInt16>(nDist) ); + aSet.Put( aUL ); + + // Background and border? + if (pBBSet) + { + aSet.Put(*pBBSet); + } + else + { + const SfxPoolItem* pItem; + + if(SfxItemState::SET == GetItemSet().GetItemState(GetWhich(nId), false, &pItem)) + { + const SfxItemSet* _pSet = &(static_cast< const SvxSetItem* >(pItem)->GetItemSet()); + + if(_pSet->GetItemState(nWBrush) == SfxItemState::SET) + { + aSet.Put(_pSet->Get(nWBrush)); + } + + if(_pSet->GetItemState(nWBoxInfo) == SfxItemState::SET) + { + aSet.Put(_pSet->Get(nWBoxInfo)); + } + + if(_pSet->GetItemState(nWBox) == SfxItemState::SET) + { + aSet.Put(_pSet->Get(nWBox)); + } + + if(_pSet->GetItemState(nWShadow) == SfxItemState::SET) + { + aSet.Put(_pSet->Get(nWShadow)); + } + + // take care of [XATTR_XATTR_FILL_FIRST .. XATTR_FILL_LAST] + for(sal_uInt16 nFillStyleId(XATTR_FILL_FIRST); nFillStyleId <= XATTR_FILL_LAST; nFillStyleId++) + { + if(_pSet->GetItemState(nFillStyleId) == SfxItemState::SET) + { + aSet.Put(_pSet->Get(nFillStyleId)); + } + } + } + } + + // Flush the SetItem + SvxSetItem aSetItem( TypedWhichId<SvxSetItem>(GetWhich( nId )), aSet ); + rSet->Put( aSetItem ); + + return true; +} + + +void SvxHFPage::Reset( const SfxItemSet* rSet ) +{ + ActivatePage( *rSet ); + ResetBackground_Impl( *rSet ); + + SfxItemPool* pPool = GetItemSet().GetPool(); + DBG_ASSERT( pPool, "Where is the pool" ); + MapUnit eUnit = pPool->GetMetric( GetWhich( SID_ATTR_PAGE_SIZE ) ); + + // Evaluate header-/footer- attributes + const SvxSetItem* pSetItem = nullptr; + + if ( SfxItemState::SET == rSet->GetItemState( GetWhich(nId), false, + reinterpret_cast<const SfxPoolItem**>(&pSetItem) ) ) + { + const SfxItemSet& rHeaderSet = pSetItem->GetItemSet(); + const SfxBoolItem& rHeaderOn = + rHeaderSet.Get(GetWhich(SID_ATTR_PAGE_ON)); + + m_xTurnOnBox->set_active(rHeaderOn.GetValue()); + + if ( rHeaderOn.GetValue() ) + { + const SfxBoolItem& rDynamic = + rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_DYNAMIC ) ); + const SfxBoolItem& rShared = + rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_SHARED ) ); + const SfxBoolItem* pSharedFirst = nullptr; + if (rHeaderSet.HasItem(GetWhich(SID_ATTR_PAGE_SHARED_FIRST))) + pSharedFirst = static_cast<const SfxBoolItem*>(&rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_SHARED_FIRST ) )); + const SvxSizeItem& rSize = + rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_SIZE ) ); + const SvxULSpaceItem& rUL = rHeaderSet.Get( GetWhich( SID_ATTR_ULSPACE ) ); + const SvxLRSpaceItem& rLR = rHeaderSet.Get( GetWhich( SID_ATTR_LRSPACE ) ); + if (m_xDynSpacingCB->get_visible()) + { + const SfxBoolItem& rDynSpacing = + static_cast<const SfxBoolItem&>(rHeaderSet.Get(GetWhich(SID_ATTR_HDFT_DYNAMIC_SPACING))); + m_xDynSpacingCB->set_active(rDynSpacing.GetValue()); + } + + + if ( nId == SID_ATTR_PAGE_HEADERSET ) + { // Header + SetMetricValue( *m_xDistEdit, rUL.GetLower(), eUnit ); + SetMetricValue( *m_xHeightEdit, rSize.GetSize().Height() - rUL.GetLower(), eUnit ); + } + else + { // Footer + SetMetricValue( *m_xDistEdit, rUL.GetUpper(), eUnit ); + SetMetricValue( *m_xHeightEdit, rSize.GetSize().Height() - rUL.GetUpper(), eUnit ); + } + + m_xHeightDynBtn->set_active(rDynamic.GetValue()); + SetMetricValue( *m_xLMEdit, rLR.GetLeft(), eUnit ); + SetMetricValue( *m_xRMEdit, rLR.GetRight(), eUnit ); + m_xCntSharedBox->set_active(rShared.GetValue()); + if (pSharedFirst) + m_xCntSharedFirstBox->set_active(pSharedFirst->GetValue()); + } + else + pSetItem = nullptr; + } + else + { + bool bIsCalc = false; + const SfxPoolItem* pExt1 = GetItem(*rSet, SID_ATTR_PAGE_EXT1); + const SfxPoolItem* pExt2 = GetItem(*rSet, SID_ATTR_PAGE_EXT2); + if (dynamic_cast<const SfxBoolItem*>(pExt1) && dynamic_cast<const SfxBoolItem*>(pExt2) ) + bIsCalc = true; + + // defaults for distance and height + tools::Long nDefaultDist = bIsCalc ? DEF_DIST_CALC : DEF_DIST_WRITER; + SetMetricValue( *m_xDistEdit, nDefaultDist, MapUnit::Map100thMM ); + SetMetricValue( *m_xHeightEdit, 500, MapUnit::Map100thMM ); + } + + if ( !pSetItem ) + { + m_xTurnOnBox->set_active(false); + m_xHeightDynBtn->set_active(true); + m_xCntSharedBox->set_active(true); + m_xCntSharedFirstBox->set_active(true); + } + + TurnOn(nullptr); + + m_xTurnOnBox->save_state(); + m_xDistEdit->save_value(); + m_xHeightEdit->save_value(); + m_xHeightDynBtn->save_state(); + m_xLMEdit->save_value(); + m_xRMEdit->save_value(); + m_xCntSharedBox->save_state(); + RangeHdl(); + + SfxObjectShell* pShell; + const SfxUInt16Item* pItem = rSet->GetItemIfSet(SID_HTML_MODE, false); + if(pItem || + ( nullptr != (pShell = SfxObjectShell::Current()) && + nullptr != (pItem = pShell->GetItem(SID_HTML_MODE)))) + { + sal_uInt16 nHtmlMode = pItem->GetValue(); + if (nHtmlMode & HTMLMODE_ON) + { + m_xCntSharedBox->hide(); + m_xBackgroundBtn->hide(); + } + } + +} + +void SvxHFPage::InitHandler() +{ + m_xTurnOnBox->connect_toggled(LINK(this, SvxHFPage, TurnOnHdl)); + m_xDistEdit->connect_value_changed(LINK(this, SvxHFPage, ValueChangeHdl)); + m_xHeightEdit->connect_value_changed(LINK(this,SvxHFPage,ValueChangeHdl)); + + m_xLMEdit->connect_value_changed(LINK(this, SvxHFPage, ValueChangeHdl)); + m_xRMEdit->connect_value_changed(LINK(this, SvxHFPage, ValueChangeHdl)); + m_xBackgroundBtn->connect_clicked(LINK(this,SvxHFPage, BackgroundHdl)); +} + +void SvxHFPage::TurnOn(const weld::Toggleable* pBox) +{ + if (m_xTurnOnBox->get_active()) + { + m_xDistFT->set_sensitive(true); + m_xDistEdit->set_sensitive(true); + m_xDynSpacingCB->set_sensitive(true); + m_xHeightFT->set_sensitive(true); + m_xHeightEdit->set_sensitive(true); + m_xHeightDynBtn->set_sensitive(true); + m_xLMLbl->set_sensitive(true); + m_xLMEdit->set_sensitive(true); + m_xRMLbl->set_sensitive(true); + m_xRMEdit->set_sensitive(true); + + SvxPageUsage nUsage = m_aBspWin.GetUsage(); + + if( nUsage == SvxPageUsage::Right || nUsage == SvxPageUsage::Left ) + m_xCntSharedBox->set_sensitive(false); + else + { + m_xCntSharedBox->set_sensitive(true); + m_xCntSharedFirstBox->set_sensitive(true); + } + m_xBackgroundBtn->set_sensitive(true); + } + else + { + bool bDelete = true; + + if (!mbDisableQueryBox && pBox && m_xTurnOnBox->get_saved_state() == TRISTATE_TRUE) + { + short nResult; + if (nId == SID_ATTR_PAGE_HEADERSET) + { + DeleteHeaderDialog aDlg(GetFrameWeld()); + nResult = aDlg.run(); + } + else + { + DeleteFooterDialog aDlg(GetFrameWeld()); + nResult = aDlg.run(); + } + bDelete = nResult == RET_YES; + } + + if ( bDelete ) + { + m_xDistFT->set_sensitive(false); + m_xDistEdit->set_sensitive(false); + m_xDynSpacingCB->set_sensitive(false); + m_xHeightFT->set_sensitive(false); + m_xHeightEdit->set_sensitive(false); + m_xHeightDynBtn->set_sensitive(false); + + m_xLMLbl->set_sensitive(false); + m_xLMEdit->set_sensitive(false); + m_xRMLbl->set_sensitive(false); + m_xRMEdit->set_sensitive(false); + + m_xCntSharedBox->set_sensitive(false); + m_xBackgroundBtn->set_sensitive(false); + m_xCntSharedFirstBox->set_sensitive(false); + } + else + m_xTurnOnBox->set_active(true); + } + UpdateExample(); +} + +IMPL_LINK(SvxHFPage, TurnOnHdl, weld::Toggleable&, rBox, void) +{ + TurnOn(&rBox); +} + +IMPL_LINK_NOARG(SvxHFPage, BackgroundHdl, weld::Button&, void) +{ + if(!pBBSet) + { + // Use only the necessary items for border and background + const sal_uInt16 nOuter(GetWhich(SID_ATTR_BORDER_OUTER)); + const sal_uInt16 nInner(GetWhich(SID_ATTR_BORDER_INNER, false)); + const sal_uInt16 nShadow(GetWhich(SID_ATTR_BORDER_SHADOW)); + + if(mbEnableDrawingLayerFillStyles) + { + pBBSet.reset(new SfxItemSetFixed + <XATTR_FILL_FIRST, XATTR_FILL_LAST, // DrawingLayer FillStyle definitions + SID_COLOR_TABLE, SID_PATTERN_LIST> // XPropertyLists for Color, Gradient, Hatch and Graphic fills + (*GetItemSet().GetPool())); + // Keep it valid + pBBSet->MergeRange(nOuter, nOuter); + pBBSet->MergeRange(nInner, nInner); + pBBSet->MergeRange(nShadow, nShadow); + + // copy items for XPropertyList entries from the DrawModel so that + // the Area TabPage can access them + static const sal_uInt16 nCopyFlags[] = { + SID_COLOR_TABLE, + SID_GRADIENT_LIST, + SID_HATCH_LIST, + SID_BITMAP_LIST, + SID_PATTERN_LIST, + 0 + }; + + for(sal_uInt16 a(0); nCopyFlags[a]; a++) + { + const SfxPoolItem* pItem = GetItemSet().GetItem(nCopyFlags[a]); + + if(pItem) + { + pBBSet->Put(*pItem); + } + else + { + OSL_ENSURE(false, "XPropertyList missing (!)"); + } + } + } + else + { + const sal_uInt16 nBrush(GetWhich(SID_ATTR_BRUSH)); + + pBBSet.reset( new SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> + (*GetItemSet().GetPool()) ); + // Keep it valid + pBBSet->MergeRange(nBrush, nBrush); + pBBSet->MergeRange(nOuter, nOuter); + pBBSet->MergeRange(nInner, nInner); + pBBSet->MergeRange(nShadow, nShadow); + } + + const SfxPoolItem* pItem; + + if(SfxItemState::SET == GetItemSet().GetItemState(GetWhich(nId), false, &pItem)) + { + // If a SfxItemSet from the SetItem for SID_ATTR_PAGE_HEADERSET or + // SID_ATTR_PAGE_FOOTERSET exists, use its content + pBBSet->Put(static_cast<const SvxSetItem*>(pItem)->GetItemSet()); + } + else + { + if(mbEnableDrawingLayerFillStyles) + { + // The style for header/footer is not yet created, need to reset + // XFillStyleItem to drawing::FillStyle_NONE which is the same as in the style + // initialization. This needs to be done since the pool default for + // XFillStyleItem is drawing::FillStyle_SOLID + pBBSet->Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + } + + if(SfxItemState::SET == GetItemSet().GetItemState(nInner, false, &pItem)) + { + // The set InfoItem is always required + pBBSet->Put(*pItem); + } + } + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + + VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSvxBorderBackgroundDlg( + GetFrameWeld(), + *pBBSet, + mbEnableDrawingLayerFillStyles)); + + pDlg->StartExecuteAsync([pDlg, this](sal_Int32 nResult) { + if (nResult == RET_OK && pDlg->GetOutputItemSet()) + { + SfxItemIter aIter(*pDlg->GetOutputItemSet()); + + for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem()) + { + if(!IsInvalidItem(pItem)) + { + pBBSet->Put(*pItem); + } + } + + { + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes; + + if (mbEnableDrawingLayerFillStyles) + { + // create FillAttributes directly from DrawingLayer FillStyle entries + aFillAttributes = + std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(*pBBSet); + } + else + { + const sal_uInt16 nWhich = GetWhich(SID_ATTR_BRUSH); + + if (pBBSet->GetItemState(nWhich) == SfxItemState::SET) + { + // create FillAttributes from SvxBrushItem + const SvxBrushItem& rItem + = static_cast<const SvxBrushItem&>(pBBSet->Get(nWhich)); + SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aTempSet(*pBBSet->GetPool()); + + setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet); + aFillAttributes = + std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aTempSet); + } + } + + if (SID_ATTR_PAGE_HEADERSET == nId) + { + //m_aBspWin.SetHdColor(rItem.GetColor()); + m_aBspWin.setHeaderFillAttributes(aFillAttributes); + } + else + { + //m_aBspWin.SetFtColor(rItem.GetColor()); + m_aBspWin.setFooterFillAttributes(aFillAttributes); + } + } + } + pDlg->disposeOnce(); + }); + + UpdateExample(); +} + +void SvxHFPage::UpdateExample() +{ + if ( nId == SID_ATTR_PAGE_HEADERSET ) + { + m_aBspWin.SetHeader( m_xTurnOnBox->get_active() ); + m_aBspWin.SetHdHeight( GetCoreValue( *m_xHeightEdit, MapUnit::MapTwip ) ); + m_aBspWin.SetHdDist( GetCoreValue( *m_xDistEdit, MapUnit::MapTwip ) ); + m_aBspWin.SetHdLeft( GetCoreValue( *m_xLMEdit, MapUnit::MapTwip ) ); + m_aBspWin.SetHdRight( GetCoreValue( *m_xRMEdit, MapUnit::MapTwip ) ); + } + else + { + m_aBspWin.SetFooter( m_xTurnOnBox->get_active() ); + m_aBspWin.SetFtHeight( GetCoreValue( *m_xHeightEdit, MapUnit::MapTwip ) ); + m_aBspWin.SetFtDist( GetCoreValue( *m_xDistEdit, MapUnit::MapTwip ) ); + m_aBspWin.SetFtLeft( GetCoreValue( *m_xLMEdit, MapUnit::MapTwip ) ); + m_aBspWin.SetFtRight( GetCoreValue( *m_xRMEdit, MapUnit::MapTwip ) ); + } + m_aBspWin.Invalidate(); +} + +void SvxHFPage::ResetBackground_Impl( const SfxItemSet& rSet ) +{ + sal_uInt16 nWhich(GetWhich(SID_ATTR_PAGE_HEADERSET)); + + if (SfxItemState::SET == rSet.GetItemState(nWhich, false)) + { + const SvxSetItem& rSetItem = static_cast< const SvxSetItem& >(rSet.Get(nWhich, false)); + const SfxItemSet& rTmpSet = rSetItem.GetItemSet(); + const SfxBoolItem& rOn = rTmpSet.Get(GetWhich(SID_ATTR_PAGE_ON)); + + if(rOn.GetValue()) + { + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aHeaderFillAttributes; + + if(mbEnableDrawingLayerFillStyles) + { + // create FillAttributes directly from DrawingLayer FillStyle entries + aHeaderFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(rTmpSet); + } + else + { + nWhich = GetWhich(SID_ATTR_BRUSH); + + if(SfxItemState::SET == rTmpSet.GetItemState(nWhich)) + { + // create FillAttributes from SvxBrushItem + const SvxBrushItem& rItem = static_cast< const SvxBrushItem& >(rTmpSet.Get(nWhich)); + SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aTempSet(*rTmpSet.GetPool()); + + setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet); + aHeaderFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aTempSet); + } + } + + m_aBspWin.setHeaderFillAttributes(aHeaderFillAttributes); + } + } + + nWhich = GetWhich(SID_ATTR_PAGE_FOOTERSET); + + if (SfxItemState::SET == rSet.GetItemState(nWhich, false)) + { + const SvxSetItem& rSetItem = static_cast< const SvxSetItem& >(rSet.Get(nWhich, false)); + const SfxItemSet& rTmpSet = rSetItem.GetItemSet(); + const SfxBoolItem& rOn = rTmpSet.Get(GetWhich(SID_ATTR_PAGE_ON)); + + if(rOn.GetValue()) + { + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFooterFillAttributes; + + if(mbEnableDrawingLayerFillStyles) + { + // create FillAttributes directly from DrawingLayer FillStyle entries + aFooterFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(rTmpSet); + } + else + { + nWhich = GetWhich(SID_ATTR_BRUSH); + + if(SfxItemState::SET == rTmpSet.GetItemState(nWhich)) + { + // create FillAttributes from SvxBrushItem + const SvxBrushItem& rItem = static_cast< const SvxBrushItem& >(rTmpSet.Get(nWhich)); + SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aTempSet(*rTmpSet.GetPool()); + + setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet); + aFooterFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aTempSet); + } + } + + m_aBspWin.setFooterFillAttributes(aFooterFillAttributes); + } + } + + drawinglayer::attribute::SdrAllFillAttributesHelperPtr aPageFillAttributes; + + if(mbEnableDrawingLayerFillStyles) + { + // create FillAttributes directly from DrawingLayer FillStyle entries + aPageFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(rSet); + } + else + { + nWhich = GetWhich(SID_ATTR_BRUSH); + + if(rSet.GetItemState(nWhich) >= SfxItemState::DEFAULT) + { + // create FillAttributes from SvxBrushItem + const SvxBrushItem& rItem = static_cast< const SvxBrushItem& >(rSet.Get(nWhich)); + SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aTempSet(*rSet.GetPool()); + + setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet); + aPageFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aTempSet); + } + } + + m_aBspWin.setPageFillAttributes(aPageFillAttributes); +} + +void SvxHFPage::ActivatePage( const SfxItemSet& rSet ) +{ + const SfxPoolItem* pItem = GetItem( rSet, SID_ATTR_LRSPACE ); + + if ( pItem ) + { + // Set left and right margins + const SvxLRSpaceItem& rLRSpace = static_cast<const SvxLRSpaceItem&>(*pItem); + + m_aBspWin.SetLeft( rLRSpace.GetLeft() ); + m_aBspWin.SetRight( rLRSpace.GetRight() ); + } + else + { + m_aBspWin.SetLeft( 0 ); + m_aBspWin.SetRight( 0 ); + } + + pItem = GetItem( rSet, SID_ATTR_ULSPACE ); + + if ( pItem ) + { + // Set top and bottom margins + const SvxULSpaceItem& rULSpace = static_cast<const SvxULSpaceItem&>(*pItem); + + m_aBspWin.SetTop( rULSpace.GetUpper() ); + m_aBspWin.SetBottom( rULSpace.GetLower() ); + } + else + { + m_aBspWin.SetTop( 0 ); + m_aBspWin.SetBottom( 0 ); + } + + SvxPageUsage nUsage = SvxPageUsage::All; + pItem = GetItem( rSet, SID_ATTR_PAGE ); + + if ( pItem ) + nUsage = static_cast<const SvxPageItem*>(pItem)->GetPageUsage(); + + m_aBspWin.SetUsage( nUsage ); + + if ( SvxPageUsage::Right == nUsage || SvxPageUsage::Left == nUsage ) + m_xCntSharedBox->set_sensitive(false); + else + { + m_xCntSharedBox->set_sensitive(true); + m_xCntSharedFirstBox->set_sensitive(true); + } + pItem = GetItem( rSet, SID_ATTR_PAGE_SIZE ); + + if ( pItem ) + { + // Orientation and Size from the PageItem + const SvxSizeItem& rSize = static_cast<const SvxSizeItem&>(*pItem); + // if the size is already swapped (Landscape) + m_aBspWin.SetSize( rSize.GetSize() ); + } + + // Evaluate Header attribute + const SvxSetItem* pSetItem = nullptr; + + if ( SfxItemState::SET == rSet.GetItemState( GetWhich( SID_ATTR_PAGE_HEADERSET ), + false, + reinterpret_cast<const SfxPoolItem**>(&pSetItem) ) ) + { + const SfxItemSet& rHeaderSet = pSetItem->GetItemSet(); + const SfxBoolItem& rHeaderOn = + rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_ON ) ); + + if ( rHeaderOn.GetValue() ) + { + const SvxSizeItem& rSize = + rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_SIZE ) ); + const SvxULSpaceItem& rUL = rHeaderSet.Get( GetWhich(SID_ATTR_ULSPACE ) ); + const SvxLRSpaceItem& rLR = rHeaderSet.Get( GetWhich( SID_ATTR_LRSPACE ) ); + tools::Long nDist = rUL.GetLower(); + + m_aBspWin.SetHdHeight( rSize.GetSize().Height() - nDist ); + m_aBspWin.SetHdDist( nDist ); + m_aBspWin.SetHdLeft( rLR.GetLeft() ); + m_aBspWin.SetHdRight( rLR.GetRight() ); + m_aBspWin.SetHeader( true ); + } + else + pSetItem = nullptr; + } + + if ( !pSetItem ) + { + m_aBspWin.SetHeader( false ); + + if ( SID_ATTR_PAGE_HEADERSET == nId ) + { + m_xCntSharedBox->set_sensitive(false); + m_xCntSharedFirstBox->set_sensitive(false); + } + } + pSetItem = nullptr; + + if ( SfxItemState::SET == rSet.GetItemState( GetWhich( SID_ATTR_PAGE_FOOTERSET ), + false, + reinterpret_cast<const SfxPoolItem**>(&pSetItem) ) ) + { + const SfxItemSet& rFooterSet = pSetItem->GetItemSet(); + const SfxBoolItem& rFooterOn = + rFooterSet.Get( GetWhich( SID_ATTR_PAGE_ON ) ); + + if ( rFooterOn.GetValue() ) + { + const SvxSizeItem& rSize = + rFooterSet.Get( GetWhich( SID_ATTR_PAGE_SIZE ) ); + const SvxULSpaceItem& rUL = rFooterSet.Get( GetWhich( SID_ATTR_ULSPACE ) ); + const SvxLRSpaceItem& rLR = rFooterSet.Get( GetWhich( SID_ATTR_LRSPACE ) ); + tools::Long nDist = rUL.GetUpper(); + + m_aBspWin.SetFtHeight( rSize.GetSize().Height() - nDist ); + m_aBspWin.SetFtDist( nDist ); + m_aBspWin.SetFtLeft( rLR.GetLeft() ); + m_aBspWin.SetFtRight( rLR.GetRight() ); + m_aBspWin.SetFooter( true ); + } + else + pSetItem = nullptr; + } + + if ( !pSetItem ) + { + m_aBspWin.SetFooter( false ); + + if ( SID_ATTR_PAGE_FOOTERSET == nId ) + { + m_xCntSharedBox->set_sensitive(false); + m_xCntSharedFirstBox->set_sensitive(false); + } + } + + pItem = GetItem( rSet, SID_ATTR_PAGE_EXT1 ); + + if ( auto pBoolItem = dynamic_cast<const SfxBoolItem*>( pItem) ) + { + m_aBspWin.SetTable( true ); + m_aBspWin.SetHorz( pBoolItem->GetValue() ); + } + + pItem = GetItem( rSet, SID_ATTR_PAGE_EXT2 ); + + if ( auto pBoolItem = dynamic_cast<const SfxBoolItem*>( pItem) ) + { + m_aBspWin.SetTable( true ); + m_aBspWin.SetVert( pBoolItem->GetValue() ); + } + ResetBackground_Impl( rSet ); + RangeHdl(); +} + +DeactivateRC SvxHFPage::DeactivatePage( SfxItemSet* _pSet ) +{ + if ( _pSet ) + FillItemSet( _pSet ); + return DeactivateRC::LeavePage; +} + +IMPL_LINK_NOARG(SvxHFPage, ValueChangeHdl, weld::MetricSpinButton&, void) +{ + UpdateExample(); + RangeHdl(); +} + +void SvxHFPage::RangeHdl() +{ + tools::Long nHHeight = m_aBspWin.GetHdHeight(); + tools::Long nHDist = m_aBspWin.GetHdDist(); + + tools::Long nFHeight = m_aBspWin.GetFtHeight(); + tools::Long nFDist = m_aBspWin.GetFtDist(); + + tools::Long nHeight = std::max(tools::Long(MINBODY), + static_cast<tools::Long>(m_xHeightEdit->denormalize(m_xHeightEdit->get_value(FieldUnit::TWIP)))); + tools::Long nDist = m_xTurnOnBox->get_active() ? + static_cast<tools::Long>(m_xDistEdit->denormalize(m_xDistEdit->get_value(FieldUnit::TWIP))) : 0; + + tools::Long nMin; + tools::Long nMax; + + if ( nId == SID_ATTR_PAGE_HEADERSET ) + { + nHHeight = nHeight; + nHDist = nDist; + } + else + { + nFHeight = nHeight; + nFDist = nDist; + } + + // Current values of the side edges + tools::Long nBT = m_aBspWin.GetTop(); + tools::Long nBB = m_aBspWin.GetBottom(); + tools::Long nBL = m_aBspWin.GetLeft(); + tools::Long nBR = m_aBspWin.GetRight(); + + tools::Long nH = m_aBspWin.GetSize().Height(); + tools::Long nW = m_aBspWin.GetSize().Width(); + + // Borders + if ( nId == SID_ATTR_PAGE_HEADERSET ) + { + // Header + nMin = ( nH - nBB - nBT ) / 5; // 20% + nMax = std::max( nH - nMin - nHDist - nFDist - nFHeight - nBB - nBT, + nMin ); + m_xHeightEdit->set_max(m_xHeightEdit->normalize(nMax), FieldUnit::TWIP); + nMin = ( nH - nBB - nBT ) / 5; // 20% + nDist = std::max( nH - nMin - nHHeight - nFDist - nFHeight - nBB - nBT, + tools::Long(0) ); + m_xDistEdit->set_max(m_xDistEdit->normalize(nDist), FieldUnit::TWIP); + } + else + { + // Footer + nMin = ( nH - nBT - nBB ) / 5; // 20% + nMax = std::max( nH - nMin - nFDist - nHDist - nHHeight - nBT - nBB, + nMin ); + m_xHeightEdit->set_max(m_xHeightEdit->normalize(nMax), FieldUnit::TWIP); + nMin = ( nH - nBT - nBB ) / 5; // 20% + nDist = std::max( nH - nMin - nFHeight - nHDist - nHHeight - nBT - nBB, + tools::Long(0) ); + m_xDistEdit->set_max(m_xDistEdit->normalize(nDist), FieldUnit::TWIP); + } + + // Limit Indentation + nMax = nW - nBL - nBR - + static_cast<tools::Long>(m_xRMEdit->denormalize(m_xRMEdit->get_value(FieldUnit::TWIP))) - MINBODY; + m_xLMEdit->set_max(m_xLMEdit->normalize(nMax), FieldUnit::TWIP); + + nMax = nW - nBL - nBR - + static_cast<tools::Long>(m_xLMEdit->denormalize(m_xLMEdit->get_value(FieldUnit::TWIP))) - MINBODY; + m_xRMEdit->set_max(m_xLMEdit->normalize(nMax), FieldUnit::TWIP); +} + +void SvxHFPage::EnableDynamicSpacing() +{ + m_xDynSpacingCB->show(); +} + +void SvxHFPage::PageCreated(const SfxAllItemSet &rSet) +{ + const SfxBoolItem* pSupportDrawingLayerFillStyleItem = rSet.GetItem<SfxBoolItem>(SID_DRAWINGLAYER_FILLSTYLES, false); + + if (pSupportDrawingLayerFillStyleItem) + { + const bool bNew(pSupportDrawingLayerFillStyleItem->GetValue()); + + mbEnableDrawingLayerFillStyles = bNew; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/hexcolorcontrol.cxx b/svx/source/dialog/hexcolorcontrol.cxx new file mode 100644 index 0000000000..fbf6b9ea66 --- /dev/null +++ b/svx/source/dialog/hexcolorcontrol.cxx @@ -0,0 +1,111 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sax/tools/converter.hxx> +#include <svx/hexcolorcontrol.hxx> +#include <rtl/character.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +namespace weld +{ +HexColorControl::HexColorControl(std::unique_ptr<weld::Entry> pEntry) + : m_xEntry(std::move(pEntry)) + , m_nAsyncModifyEvent(nullptr) +{ + m_xEntry->set_max_length(6); + m_xEntry->set_width_chars(6); + m_xEntry->connect_insert_text(LINK(this, HexColorControl, ImplProcessInputHdl)); + m_xEntry->connect_changed(LINK(this, HexColorControl, ImplProcessModifyHdl)); +} + +HexColorControl::~HexColorControl() +{ + if (m_nAsyncModifyEvent) + Application::RemoveUserEvent(m_nAsyncModifyEvent); +} + +IMPL_LINK_NOARG(HexColorControl, OnAsyncModifyHdl, void*, void) +{ + m_nAsyncModifyEvent = nullptr; + m_aModifyHdl.Call(*m_xEntry); +} + +// tdf#123291 resend it async so it arrives after ImplProcessInputHdl has been +// processed +IMPL_LINK_NOARG(HexColorControl, ImplProcessModifyHdl, weld::Entry&, void) +{ + if (m_nAsyncModifyEvent) + Application::RemoveUserEvent(m_nAsyncModifyEvent); + m_nAsyncModifyEvent = Application::PostUserEvent(LINK(this, HexColorControl, OnAsyncModifyHdl)); +} + +void HexColorControl::SetColor(Color nColor) +{ + OUStringBuffer aBuffer; + sax::Converter::convertColor(aBuffer, nColor); + OUString sColor = aBuffer.makeStringAndClear().copy(1); + if (sColor == m_xEntry->get_text()) + return; + int nStartPos, nEndPos; + m_xEntry->get_selection_bounds(nStartPos, nEndPos); + m_xEntry->set_text(sColor); + m_xEntry->select_region(nStartPos, nEndPos); +} + +Color HexColorControl::GetColor() const +{ + sal_Int32 nColor = -1; + + OUString aStr = "#" + m_xEntry->get_text(); + sal_Int32 nLen = aStr.getLength(); + + if (nLen < 7) + { + static const char* const pNullStr = "000000"; + aStr += OUString::createFromAscii(&pNullStr[nLen - 1]); + } + + sax::Converter::convertColor(nColor, aStr); + + m_xEntry->set_message_type(nColor != -1 ? weld::EntryMessageType::Normal + : weld::EntryMessageType::Error); + + return Color(ColorTransparency, nColor); +} + +IMPL_STATIC_LINK(HexColorControl, ImplProcessInputHdl, OUString&, rTest, bool) +{ + const sal_Unicode* pTest = rTest.getStr(); + sal_Int32 nLen = rTest.getLength(); + + OUStringBuffer aFilter(nLen); + for (sal_Int32 i = 0; i < nLen; ++i) + { + if (rtl::isAsciiHexDigit(*pTest)) + aFilter.append(*pTest); + ++pTest; + } + + rTest = aFilter.makeStringAndClear(); + return true; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/hyperdlg.cxx b/svx/source/dialog/hyperdlg.cxx new file mode 100644 index 0000000000..6d94d8add6 --- /dev/null +++ b/svx/source/dialog/hyperdlg.cxx @@ -0,0 +1,81 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/hyperdlg.hxx> +#include <svx/svxdlg.hxx> +#include <sfx2/app.hxx> +#include <sfx2/sfxsids.hrc> + +//# # +//# Childwindow-Wrapper-Class # +//# # +SFX_IMPL_CHILDWINDOW_WITHID(SvxHlinkDlgWrapper, SID_HYPERLINK_DIALOG) + +SvxHlinkDlgWrapper::SvxHlinkDlgWrapper( vcl::Window* _pParent, sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ) : + SfxChildWindow( _pParent, nId ), + + mpDlg( nullptr ) + +{ + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + mpDlg = pFact->CreateSvxHpLinkDlg(this, pBindings, _pParent->GetFrameWeld()); + SetController( mpDlg->GetController() ); + SetVisible_Impl(false); + + if ( !pInfo->aSize.IsEmpty() ) + { + weld::Window* pTopWindow = SfxGetpApp()->GetTopWindow(); + if (pTopWindow) + { + weld::Dialog* pDialog = GetController()->getDialog(); + + Size aParentSize(pTopWindow->get_size()); + Size aDlgSize(pDialog->get_size()); + + if( aParentSize.Width() < pInfo->aPos.X() ) + pInfo->aPos.setX( aParentSize.Width()-aDlgSize.Width() < tools::Long(0.1*aParentSize.Width()) ? + tools::Long(0.1*aParentSize.Width()) : aParentSize.Width()-aDlgSize.Width() ); + if( aParentSize.Height() < pInfo->aPos. Y() ) + pInfo->aPos.setY( aParentSize.Height()-aDlgSize.Height() < tools::Long(0.1*aParentSize.Height()) ? + tools::Long(0.1*aParentSize.Height()) : aParentSize.Height()-aDlgSize.Height() ); + + pDialog->window_move(pInfo->aPos.X(), pInfo->aPos.Y()); + } + } + SetHideNotDelete( true ); +} + +SfxChildWinInfo SvxHlinkDlgWrapper::GetInfo() const +{ + return SfxChildWindow::GetInfo(); +} + +bool SvxHlinkDlgWrapper::QueryClose() +{ + return !mpDlg || mpDlg->QueryClose(); +} + +SvxHlinkDlgWrapper::~SvxHlinkDlgWrapper() +{ + mpDlg.disposeAndClear(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/imapdlg.cxx b/svx/source/dialog/imapdlg.cxx new file mode 100644 index 0000000000..1efe3c8f4f --- /dev/null +++ b/svx/source/dialog/imapdlg.cxx @@ -0,0 +1,728 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/errinf.hxx> +#include <tools/urlobj.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <svl/eitem.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/module.hxx> +#include <sfx2/filedlghelper.hxx> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <svl/urihelper.hxx> +#include <svtools/ehdl.hxx> +#include <svtools/inettbc.hxx> +#include <svtools/sfxecode.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/docfile.hxx> +#include <unotools/localedatawrapper.hxx> +#include <vcl/weld.hxx> +#include <svx/imapdlg.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <svx/svxids.hrc> +#include "imapwnd.hxx" +#include "imapimp.hxx" +#include <svx/svdopath.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <osl/diagnose.h> +#include "dlgunit.hxx" +#include <memory> + +constexpr OUString SELF_TARGET = u"_self"_ustr; +constexpr OUString IMAP_CERN_FILTER = u"MAP - CERN"_ustr; +constexpr OUString IMAP_NCSA_FILTER = u"MAP - NCSA"_ustr; +constexpr OUString IMAP_BINARY_FILTER = u"SIP - StarView ImageMap"_ustr; +constexpr OUStringLiteral IMAP_ALL_TYPE = u"*.*"; +constexpr OUString IMAP_BINARY_TYPE = u"*.sip"_ustr; +constexpr OUString IMAP_CERN_TYPE = u"*.map"_ustr; +constexpr OUString IMAP_NCSA_TYPE = u"*.map"_ustr; + +SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID( SvxIMapDlgChildWindow, SID_IMAP ); + +// ControllerItem + +SvxIMapDlgItem::SvxIMapDlgItem( SvxIMapDlg& rIMapDlg, SfxBindings& rBindings ) : + SfxControllerItem ( SID_IMAP_EXEC, rBindings ), + rIMap ( rIMapDlg ) +{ +} + +void SvxIMapDlgItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState /*eState*/, + const SfxPoolItem* pItem ) +{ + if ( ( nSID == SID_IMAP_EXEC ) && pItem ) + { + const SfxBoolItem* pStateItem = dynamic_cast<const SfxBoolItem*>( pItem ); + assert(pStateItem); //SfxBoolItem expected + if (pStateItem) + { + // Disable Float if possible + rIMap.SetExecState( !pStateItem->GetValue() ); + } + } +} + +SvxIMapDlgChildWindow::SvxIMapDlgChildWindow(vcl::Window* _pParent, sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo const * pInfo) + : SfxChildWindow( _pParent, nId ) +{ + SetController(std::make_shared<SvxIMapDlg>(pBindings, this, _pParent->GetFrameWeld())); + SvxIMapDlg* pDlg = static_cast<SvxIMapDlg*>(GetController().get()); + pDlg->Initialize( pInfo ); +} + +void SvxIMapDlgChildWindow::UpdateIMapDlg( const Graphic& rGraphic, const ImageMap* pImageMap, + const TargetList* pTargetList, void* pEditingObj ) +{ + SvxIMapDlg* pDlg = GetIMapDlg(); + if (pDlg) + pDlg->UpdateLink(rGraphic, pImageMap, pTargetList, pEditingObj); +} + +SvxIMapDlg::SvxIMapDlg(SfxBindings *_pBindings, SfxChildWindow *pCW, weld::Window* _pParent) + : SfxModelessDialogController(_pBindings, pCW, _pParent, "svx/ui/imapdialog.ui", "ImapDialog") + , pCheckObj(nullptr) + , aIMapItem(*this, *_pBindings) + , m_xIMapWnd(new IMapWindow(_pBindings->GetActiveFrame(), m_xDialog.get())) + , m_xTbxIMapDlg1(m_xBuilder->weld_toolbar("toolbar")) + , m_xFtURL(m_xBuilder->weld_label("urlft")) + , m_xURLBox(new SvtURLBox(m_xBuilder->weld_combo_box("url"))) + , m_xFtText(m_xBuilder->weld_label("textft")) + , m_xEdtText(m_xBuilder->weld_entry("text")) + , m_xFtTarget(m_xBuilder->weld_label("targetft")) + , m_xCbbTarget(m_xBuilder->weld_combo_box("target")) + , m_xCancelBtn(m_xBuilder->weld_button("cancel")) + , m_xStbStatus1(m_xBuilder->weld_label("statusurl")) + , m_xStbStatus2(m_xBuilder->weld_label("statuspos")) + , m_xStbStatus3(m_xBuilder->weld_label("statussize")) + , m_xIMapWndWeld(new weld::CustomWeld(*m_xBuilder, "container", *m_xIMapWnd)) + +{ + m_xTbxIMapDlg1->insert_separator(4, "sep1"); + m_xTbxIMapDlg1->insert_separator(10, "sep2"); + m_xTbxIMapDlg1->insert_separator(15, "sep3"); + m_xTbxIMapDlg1->insert_separator(18, "sel4"); + + //lock this down so it doesn't jump around in size + //as entries are added later on + TargetList aTmpList; + SfxFrame::GetDefaultTargetList(aTmpList); + for (const OUString & s : aTmpList) + m_xCbbTarget->append_text(s); + Size aPrefSize(m_xCbbTarget->get_preferred_size()); + m_xCbbTarget->set_size_request(aPrefSize.Width(), -1); + m_xCbbTarget->clear(); + + m_xIMapWnd->Show(); + + pOwnData.reset(new IMapOwnData); + + m_xIMapWnd->SetInfoLink( LINK( this, SvxIMapDlg, InfoHdl ) ); + m_xIMapWnd->SetMousePosLink( LINK( this, SvxIMapDlg, MousePosHdl ) ); + m_xIMapWnd->SetGraphSizeLink( LINK( this, SvxIMapDlg, GraphSizeHdl ) ); + m_xIMapWnd->SetUpdateLink( LINK( this, SvxIMapDlg, StateHdl ) ); + + m_xURLBox->connect_changed( LINK( this, SvxIMapDlg, URLModifyHdl ) ); + m_xURLBox->connect_focus_out( LINK( this, SvxIMapDlg, URLLoseFocusHdl ) ); + m_xEdtText->connect_changed( LINK( this, SvxIMapDlg, EntryModifyHdl ) ); + m_xCbbTarget->connect_focus_out( LINK( this, SvxIMapDlg, URLLoseFocusHdl ) ); + + m_xTbxIMapDlg1->connect_clicked( LINK( this, SvxIMapDlg, TbxClickHdl ) ); + OUString sSelect("TBI_SELECT"); + m_xTbxIMapDlg1->set_item_active(sSelect, true); + TbxClickHdl(sSelect); + + m_xStbStatus1->set_size_request(120, -1); + const int nWidth = m_xStbStatus1->get_pixel_size(" 9999,99 cm / 9999,99 cm ").Width(); + m_xStbStatus2->set_size_request(nWidth, -1); + m_xStbStatus3->set_size_request(nWidth, -1); + + m_xFtURL->set_sensitive(false); + m_xURLBox->set_sensitive(false); + m_xFtText->set_sensitive(false); + m_xEdtText->set_sensitive(false); + m_xFtTarget->set_sensitive(false); + m_xCbbTarget->set_sensitive(false); + pOwnData->bExecState = false; + + pOwnData->aIdle.SetInvokeHandler( LINK( this, SvxIMapDlg, UpdateHdl ) ); + + m_xTbxIMapDlg1->set_item_sensitive("TBI_ACTIVE", false); + m_xTbxIMapDlg1->set_item_sensitive("TBI_MACRO", false ); + m_xTbxIMapDlg1->set_item_sensitive("TBI_PROPERTY", false ); + + m_xCancelBtn->connect_clicked(LINK(this, SvxIMapDlg, CancelHdl)); +} + +SvxIMapDlg::~SvxIMapDlg() +{ + m_xIMapWnd->SetUpdateLink( Link<GraphCtrl*,void>() ); + m_xIMapWnd.reset(); +} + +IMPL_LINK_NOARG(SvxIMapDlg, CancelHdl, weld::Button&, void) +{ + bool bRet = true; + + if ( m_xTbxIMapDlg1->get_item_sensitive("TBI_APPLY") ) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xDialog.get(), "svx/ui/querymodifyimagemapchangesdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QueryModifyImageMapChangesDialog")); + const tools::Long nRet = xQBox->run(); + + if( nRet == RET_YES ) + { + SfxBoolItem aBoolItem( SID_IMAP_EXEC, true ); + GetBindings().GetDispatcher()->ExecuteList(SID_IMAP_EXEC, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aBoolItem }); + } + else if( nRet == RET_CANCEL ) + bRet = false; + } + else if( m_xIMapWnd->IsChanged() ) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xDialog.get(), "svx/ui/querysaveimagemapchangesdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QuerySaveImageMapChangesDialog")); + const tools::Long nRet = xQBox->run(); + + if( nRet == RET_YES ) + bRet = DoSave(); + else if( nRet == RET_CANCEL ) + bRet = false; + } + + if (bRet) + m_xDialog->response(RET_CANCEL); +} + +// Enabled or disable all Controls + +void SvxIMapDlg::SetExecState( bool bEnable ) +{ + pOwnData->bExecState = bEnable; +} + +const ImageMap& SvxIMapDlg::GetImageMap() const +{ + return m_xIMapWnd->GetImageMap(); +} + +void SvxIMapDlg::SetTargetList( const TargetList& rTargetList ) +{ + m_xIMapWnd->SetTargetList( rTargetList ); + + m_xCbbTarget->clear(); + + for (const OUString & s : rTargetList) + m_xCbbTarget->append_text(s); +} + +void SvxIMapDlg::UpdateLink( const Graphic& rGraphic, const ImageMap* pImageMap, + const TargetList* pTargetList, void* pEditingObj ) +{ + pOwnData->aUpdateGraphic = rGraphic; + + if ( pImageMap ) + pOwnData->aUpdateImageMap = *pImageMap; + else + pOwnData->aUpdateImageMap.ClearImageMap(); + + pOwnData->pUpdateEditingObject = pEditingObj; + + // Delete UpdateTargetList, because this method can still be called several + // times before the update timer is turned on + + // TargetList must be copied, since it is owned by the caller and can be + // deleted immediately after this call the copied list will be deleted + // again in the handler + if( pTargetList ) + pOwnData->aUpdateTargetList = *pTargetList; + else + pOwnData->aUpdateTargetList.clear(); + + pOwnData->aIdle.Start(); +} + + +// Click-handler for ToolBox + +IMPL_LINK(SvxIMapDlg, TbxClickHdl, const OUString&, rNewItemId, void) +{ + if (rNewItemId == "TBI_APPLY") + { + URLLoseFocusHdl(*m_xCbbTarget); + SfxBoolItem aBoolItem( SID_IMAP_EXEC, true ); + GetBindings().GetDispatcher()->ExecuteList(SID_IMAP_EXEC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aBoolItem }); + } + else if (rNewItemId == "TBI_OPEN") + DoOpen(); + else if (rNewItemId == "TBI_SAVEAS") + DoSave(); + else if (rNewItemId == "TBI_CLOSE") + CancelHdl(*m_xCancelBtn); + else if (rNewItemId == "TBI_SELECT") + { + SetActiveTool( rNewItemId ); + m_xIMapWnd->SetEditMode( true ); + } + else if (rNewItemId == "TBI_RECT") + { + SetActiveTool( rNewItemId ); + m_xIMapWnd->SetObjKind( SdrObjKind::Rectangle ); + } + else if (rNewItemId == "TBI_CIRCLE") + { + SetActiveTool( rNewItemId ); + m_xIMapWnd->SetObjKind( SdrObjKind::CircleOrEllipse ); + } + else if (rNewItemId == "TBI_POLY") + { + SetActiveTool( rNewItemId ); + m_xIMapWnd->SetObjKind( SdrObjKind::Polygon ); + } + else if (rNewItemId == "TBI_FREEPOLY") + { + SetActiveTool( rNewItemId ); + m_xIMapWnd->SetObjKind( SdrObjKind::FreehandFill ); + } + else if (rNewItemId == "TBI_ACTIVE") + { + URLLoseFocusHdl(*m_xCbbTarget); + bool bNewState = !m_xTbxIMapDlg1->get_item_active(rNewItemId); + m_xTbxIMapDlg1->set_item_active(rNewItemId, bNewState); + m_xIMapWnd->SetCurrentObjState( !bNewState ); + } + else if (rNewItemId == "TBI_MACRO") + m_xIMapWnd->DoMacroAssign(); + else if (rNewItemId == "TBI_PROPERTY") + m_xIMapWnd->DoPropertyDialog(); + else if (rNewItemId == "TBI_POLYEDIT") + { + SetActiveTool( rNewItemId ); + m_xIMapWnd->SetPolyEditMode( m_xTbxIMapDlg1->get_item_active(rNewItemId) ? SID_BEZIER_MOVE : 0 ); + } + else if (rNewItemId == "TBI_POLYMOVE") + { + SetActiveTool( rNewItemId ); + m_xIMapWnd->SetPolyEditMode( SID_BEZIER_MOVE ); + } + else if (rNewItemId == "TBI_POLYINSERT") + { + SetActiveTool( rNewItemId ); + m_xIMapWnd->SetPolyEditMode( SID_BEZIER_INSERT ); + } + else if (rNewItemId == "TBI_POLYDELETE") + { + SetActiveTool( rNewItemId ); + m_xIMapWnd->GetSdrView()->DeleteMarkedPoints(); + } + else if (rNewItemId == "TBI_UNDO") + { + URLLoseFocusHdl(*m_xCbbTarget); + m_xIMapWnd->GetSdrModel()->Undo(); + } + else if (rNewItemId == "TBI_REDO") + { + URLLoseFocusHdl(*m_xCbbTarget); + m_xIMapWnd->GetSdrModel()->Redo(); + } +} + +void SvxIMapDlg::DoOpen() +{ + ::sfx2::FileDialogHelper aDlg( + css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::NONE, m_xDialog.get()); + + ImageMap aLoadIMap; + const OUString aFilter(SvxResId(RID_SVXSTR_IMAP_ALL_FILTER)); + + aDlg.AddFilter( aFilter, IMAP_ALL_TYPE ); + aDlg.AddFilter( IMAP_CERN_FILTER, IMAP_CERN_TYPE ); + aDlg.AddFilter( IMAP_NCSA_FILTER, IMAP_NCSA_TYPE ); + aDlg.AddFilter( IMAP_BINARY_FILTER, IMAP_BINARY_TYPE ); + + aDlg.SetCurrentFilter( aFilter ); + aDlg.SetContext(sfx2::FileDialogHelper::ImageMap); + + if( aDlg.Execute() != ERRCODE_NONE ) + return; + + INetURLObject aURL( aDlg.GetPath() ); + DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" ); + std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ )); + + if( pIStm ) + { + aLoadIMap.Read( *pIStm, IMapFormat::Detect ); + + if( pIStm->GetError() ) + { + SfxErrorContext eEC(ERRCTX_ERROR, m_xDialog.get()); + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + } + else + m_xIMapWnd->SetImageMap( aLoadIMap ); + } + + m_xIMapWnd->Invalidate(); +} + +bool SvxIMapDlg::DoSave() +{ + ::sfx2::FileDialogHelper aDlg( + css::ui::dialogs::TemplateDescription::FILESAVE_SIMPLE, + FileDialogFlags::NONE, m_xDialog.get()); + + const OUString aBinFilter( IMAP_BINARY_FILTER ); + const OUString aCERNFilter( IMAP_CERN_FILTER ); + const OUString aNCSAFilter( IMAP_NCSA_FILTER ); + SdrModel* pModel = m_xIMapWnd->GetSdrModel(); + const bool bChanged = pModel->IsChanged(); + bool bRet = false; + + aDlg.AddFilter( aCERNFilter, IMAP_CERN_TYPE ); + aDlg.AddFilter( aNCSAFilter, IMAP_NCSA_TYPE ); + aDlg.AddFilter( aBinFilter, IMAP_BINARY_TYPE ); + + aDlg.SetCurrentFilter( aCERNFilter ); + aDlg.SetContext(sfx2::FileDialogHelper::ImageMap); + + if( aDlg.Execute() == ERRCODE_NONE ) + { + const OUString aFilter( aDlg.GetCurrentFilter() ); + OUString aExt; + IMapFormat nFormat; + + if ( aFilter == aBinFilter ) + { + nFormat = IMapFormat::Binary; + aExt = "sip"; + } + else if ( aFilter == aCERNFilter ) + { + nFormat = IMapFormat::CERN; + aExt = "map"; + } + else if ( aFilter == aNCSAFilter ) + { + nFormat = IMapFormat::NCSA; + aExt = "map"; + } + else + { + return false; + } + + INetURLObject aURL( aDlg.GetPath() ); + + if( aURL.GetProtocol() == INetProtocol::NotValid ) + { + OSL_FAIL( "invalid URL" ); + } + else + { + if( aURL.getExtension().isEmpty() ) + aURL.setExtension( aExt ); + + std::unique_ptr<SvStream> pOStm(::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::TRUNC )); + if( pOStm ) + { + m_xIMapWnd->GetImageMap().Write( *pOStm, nFormat ); + + if( pOStm->GetError() ) + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + + pOStm.reset(); + pModel->SetChanged( bChanged ); + bRet = true; + } + } + } + + return bRet; +} + +void SvxIMapDlg::SetActiveTool(std::u16string_view rId) +{ + m_xTbxIMapDlg1->set_item_active("TBI_SELECT", rId == u"TBI_SELECT"); + m_xTbxIMapDlg1->set_item_active("TBI_RECT", rId == u"TBI_RECT"); + m_xTbxIMapDlg1->set_item_active("TBI_CIRCLE", rId == u"TBI_CIRCLE"); + m_xTbxIMapDlg1->set_item_active("TBI_POLY", rId == u"TBI_POLY"); + m_xTbxIMapDlg1->set_item_active("TBI_FREEPOLY", rId == u"TBI_FREEPOLY"); + + m_xTbxIMapDlg1->set_item_active("TBI_POLYINSERT", rId == u"TBI_POLYINSERT"); + m_xTbxIMapDlg1->set_item_active("TBI_POLYDELETE", false); + + bool bMove = rId == u"TBI_POLYMOVE" + || ( rId == u"TBI_POLYEDIT" + && !m_xTbxIMapDlg1->get_item_active("TBI_POLYINSERT") + && !m_xTbxIMapDlg1->get_item_active("TBI_POLYDELETE") ); + + m_xTbxIMapDlg1->set_item_active("TBI_POLYMOVE", bMove ); + + bool bEditMode = ( rId == u"TBI_POLYEDIT" ) + || ( rId == u"TBI_POLYMOVE") + || ( rId == u"TBI_POLYINSERT") + || ( rId == u"TBI_POLYDELETE" ); + + m_xTbxIMapDlg1->set_item_active("TBI_POLYEDIT", bEditMode); +} + +IMPL_LINK( SvxIMapDlg, InfoHdl, IMapWindow&, rWnd, void ) +{ + const NotifyInfo& rInfo = rWnd.GetInfo(); + + if ( rInfo.bNewObj ) + { + if (!rInfo.aMarkURL.isEmpty() && ( m_xURLBox->find_text(rInfo.aMarkURL) == -1)) + m_xURLBox->append_text(rInfo.aMarkURL); + + m_xURLBox->set_entry_text(rInfo.aMarkURL); + m_xEdtText->set_text(rInfo.aMarkAltText); + + if ( rInfo.aMarkTarget.isEmpty() ) + m_xCbbTarget->set_entry_text( SELF_TARGET ); + else + m_xCbbTarget->set_entry_text( rInfo.aMarkTarget ); + } + + if ( !rInfo.bOneMarked ) + { + m_xTbxIMapDlg1->set_item_active("TBI_ACTIVE", false); + m_xTbxIMapDlg1->set_item_sensitive("TBI_ACTIVE", false); + m_xTbxIMapDlg1->set_item_sensitive("TBI_MACRO", false); + m_xTbxIMapDlg1->set_item_sensitive("TBI_PROPERTY", false); + m_xStbStatus1->set_label(OUString()); + + m_xFtURL->set_sensitive(false); + m_xURLBox->set_sensitive(false); + m_xFtText->set_sensitive(false); + m_xEdtText->set_sensitive(false); + m_xFtTarget->set_sensitive(false); + m_xCbbTarget->set_sensitive(false); + + m_xURLBox->set_entry_text( "" ); + m_xEdtText->set_text( "" ); + } + else + { + m_xTbxIMapDlg1->set_item_sensitive("TBI_ACTIVE", true); + m_xTbxIMapDlg1->set_item_active("TBI_ACTIVE", !rInfo.bActivated ); + m_xTbxIMapDlg1->set_item_sensitive("TBI_MACRO", true); + m_xTbxIMapDlg1->set_item_sensitive("TBI_PROPERTY", true); + + m_xFtURL->set_sensitive(true); + m_xURLBox->set_sensitive(true); + m_xFtText->set_sensitive(true); + m_xEdtText->set_sensitive(true); + m_xFtTarget->set_sensitive(true); + m_xCbbTarget->set_sensitive(true); + + m_xStbStatus1->set_label(rInfo.aMarkURL); + + if ( m_xURLBox->get_active_text() != rInfo.aMarkURL ) + m_xURLBox->set_entry_text( rInfo.aMarkURL ); + + if ( m_xEdtText->get_text() != rInfo.aMarkAltText ) + m_xEdtText->set_text( rInfo.aMarkAltText ); + + if ( rInfo.aMarkTarget.isEmpty() ) + m_xCbbTarget->set_entry_text( SELF_TARGET ); + else + m_xCbbTarget->set_entry_text( rInfo.aMarkTarget ); + } +} + +IMPL_LINK( SvxIMapDlg, MousePosHdl, GraphCtrl*, pWnd, void ) +{ + const FieldUnit eFieldUnit = GetBindings().GetDispatcher()->GetModule()->GetFieldUnit(); + const Point& rMousePos = pWnd->GetMousePos(); + const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() ); + const sal_Unicode cSep = rLocaleWrapper.getNumDecimalSep()[0]; + + OUString aStr = GetUnitString( rMousePos.X(), eFieldUnit, cSep ) + + " / " + GetUnitString( rMousePos.Y(), eFieldUnit, cSep ); + + m_xStbStatus2->set_label(aStr); +} + +IMPL_LINK( SvxIMapDlg, GraphSizeHdl, GraphCtrl*, pWnd, void ) +{ + const FieldUnit eFieldUnit = GetBindings().GetDispatcher()->GetModule()->GetFieldUnit(); + const Size& rSize = pWnd->GetGraphicSize(); + const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() ); + const sal_Unicode cSep = rLocaleWrapper.getNumDecimalSep()[0]; + + OUString aStr = GetUnitString( rSize.Width(), eFieldUnit, cSep ) + + " x " + GetUnitString( rSize.Height(), eFieldUnit, cSep ); + + m_xStbStatus3->set_label(aStr); +} + +void SvxIMapDlg::URLModify() +{ + NotifyInfo aNewInfo; + + aNewInfo.aMarkURL = m_xURLBox->get_active_text(); + aNewInfo.aMarkAltText = m_xEdtText->get_text(); + aNewInfo.aMarkTarget = m_xCbbTarget->get_active_text(); + + m_xIMapWnd->ReplaceActualIMapInfo( aNewInfo ); +} + +IMPL_LINK_NOARG(SvxIMapDlg, URLModifyHdl, weld::ComboBox&, void) +{ + URLModify(); +} + +IMPL_LINK_NOARG(SvxIMapDlg, EntryModifyHdl, weld::Entry&, void) +{ + URLModify(); +} + +IMPL_LINK_NOARG(SvxIMapDlg, URLLoseFocusHdl, weld::Widget&, void) +{ + NotifyInfo aNewInfo; + const OUString aURLText( m_xURLBox->get_active_text() ); + const OUString aTargetText( m_xCbbTarget->get_active_text() ); + + if ( !aURLText.isEmpty() ) + { + OUString aBase = GetBindings().GetDispatcher()->GetFrame()->GetObjectShell()->GetMedium()->GetBaseURL(); + aNewInfo.aMarkURL = ::URIHelper::SmartRel2Abs( INetURLObject(aBase), aURLText, URIHelper::GetMaybeFileHdl(), true, false, + INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous ); + } + else + aNewInfo.aMarkURL = aURLText; + + aNewInfo.aMarkAltText = m_xEdtText->get_text(); + + if ( aTargetText.isEmpty() ) + aNewInfo.aMarkTarget = SELF_TARGET; + else + aNewInfo.aMarkTarget = aTargetText; + + m_xIMapWnd->ReplaceActualIMapInfo( aNewInfo ); +} + +IMPL_LINK_NOARG(SvxIMapDlg, UpdateHdl, Timer *, void) +{ + pOwnData->aIdle.Stop(); + + if ( pOwnData->pUpdateEditingObject != pCheckObj ) + { + if (m_xIMapWnd->IsChanged()) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xDialog.get(), "svx/ui/querysaveimagemapchangesdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QuerySaveImageMapChangesDialog")); + if (xQBox->run() == RET_YES) + { + DoSave(); + } + } + + m_xIMapWnd->SetGraphic( pOwnData->aUpdateGraphic ); + m_xIMapWnd->SetImageMap( pOwnData->aUpdateImageMap ); + SetTargetList( pOwnData->aUpdateTargetList ); + pCheckObj = pOwnData->pUpdateEditingObject; + + // After changes => default selection + m_xTbxIMapDlg1->set_item_active("TBI_SELECT", true); + m_xIMapWnd->SetEditMode( true ); + } + + // Delete the copied list again in the Update method + pOwnData->aUpdateTargetList.clear(); + + GetBindings().Invalidate( SID_IMAP_EXEC ); + m_xIMapWnd->QueueIdleUpdate(); +} + +IMPL_LINK( SvxIMapDlg, StateHdl, GraphCtrl*, pWnd, void ) +{ + const SdrObject* pObj = pWnd->GetSelectedSdrObject(); + const SdrModel* pModel = pWnd->GetSdrModel(); + const SdrView* pView = pWnd->GetSdrView(); + const bool bPolyEdit = ( pObj != nullptr ) && dynamic_cast<const SdrPathObj*>( pObj) != nullptr; + const bool bDrawEnabled = !( bPolyEdit && m_xTbxIMapDlg1->get_item_active("TBI_POLYEDIT") ); + + m_xTbxIMapDlg1->set_item_sensitive("TBI_APPLY", pOwnData->bExecState && pWnd->IsChanged() ); + + m_xTbxIMapDlg1->set_item_sensitive("TBI_SELECT", bDrawEnabled); + m_xTbxIMapDlg1->set_item_sensitive("TBI_RECT", bDrawEnabled); + m_xTbxIMapDlg1->set_item_sensitive("TBI_CIRCLE", bDrawEnabled); + m_xTbxIMapDlg1->set_item_sensitive("TBI_POLY", bDrawEnabled); + m_xTbxIMapDlg1->set_item_sensitive("TBI_FREEPOLY", bDrawEnabled); + + // BezierEditor State + m_xTbxIMapDlg1->set_item_sensitive( "TBI_POLYEDIT", bPolyEdit ); + m_xTbxIMapDlg1->set_item_sensitive( "TBI_POLYMOVE", !bDrawEnabled ); + m_xTbxIMapDlg1->set_item_sensitive( "TBI_POLYINSERT", !bDrawEnabled ); + m_xTbxIMapDlg1->set_item_sensitive( "TBI_POLYDELETE", !bDrawEnabled && pView->IsDeleteMarkedPointsPossible() ); + + // Undo/Redo + m_xTbxIMapDlg1->set_item_sensitive( "TBI_UNDO", pModel->HasUndoActions() ); + m_xTbxIMapDlg1->set_item_sensitive( "TBI_REDO", pModel->HasRedoActions() ); + + if ( bPolyEdit ) + { + switch( pWnd->GetPolyEditMode() ) + { + case SID_BEZIER_MOVE: + m_xTbxIMapDlg1->set_item_active("TBI_POLYMOVE", true); + m_xTbxIMapDlg1->set_item_active("TBI_POLYINSERT", false); + break; + case SID_BEZIER_INSERT: + m_xTbxIMapDlg1->set_item_active("TBI_POLYINSERT", true); + m_xTbxIMapDlg1->set_item_active("TBI_POLYMOVE", false); + break; + default: + break; + } + } + else + { + m_xTbxIMapDlg1->set_item_active( "TBI_POLYEDIT", false ); + m_xTbxIMapDlg1->set_item_active( "TBI_POLYMOVE", true); + m_xTbxIMapDlg1->set_item_active( "TBI_POLYINSERT", false ); + pWnd->SetPolyEditMode( 0 ); + } + + m_xIMapWnd->QueueIdleUpdate(); +} + +SvxIMapDlg* GetIMapDlg() +{ + SfxChildWindow* pWnd = nullptr; + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm && pViewFrm->HasChildWindow(SvxIMapDlgChildWindow::GetChildWindowId())) + pWnd = pViewFrm->GetChildWindow(SvxIMapDlgChildWindow::GetChildWindowId()); + return pWnd ? static_cast<SvxIMapDlg*>(pWnd->GetController().get()) : nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/imapimp.hxx b/svx/source/dialog/imapimp.hxx new file mode 100644 index 0000000000..50862f625f --- /dev/null +++ b/svx/source/dialog/imapimp.hxx @@ -0,0 +1,51 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#ifndef INCLUDED_SVX_SOURCE_DIALOG_IMAPIMP_HXX +#define INCLUDED_SVX_SOURCE_DIALOG_IMAPIMP_HXX + +#include <svx/imapdlg.hxx> +#include <vcl/graph.hxx> +#include <vcl/idle.hxx> +#include <vcl/imap.hxx> + +class IMapOwnData +{ +public: + + Idle aIdle; + Graphic aUpdateGraphic; + ImageMap aUpdateImageMap; + TargetList aUpdateTargetList; + void* pUpdateEditingObject; + bool bExecState; + + IMapOwnData() + : aIdle("svx IMapOwnData") + , pUpdateEditingObject(nullptr) + , bExecState(false) + { + } +}; + + +#endif // _IMAPIMP_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/imapwnd.cxx b/svx/source/dialog/imapwnd.cxx new file mode 100644 index 0000000000..45ad2a9e9e --- /dev/null +++ b/svx/source/dialog/imapwnd.cxx @@ -0,0 +1,738 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <tools/urlobj.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/imaprect.hxx> +#include <vcl/imapcirc.hxx> +#include <vcl/imappoly.hxx> +#include <vcl/svapp.hxx> +#include <svl/urlbmk.hxx> + +#include <svx/svxids.hrc> +#include "imapwnd.hxx" +#include <svx/svdpage.hxx> +#include <svx/svdorect.hxx> +#include <svx/svdocirc.hxx> +#include <svx/svdopath.hxx> +#include <svx/xfltrit.hxx> +#include <svx/svdpagv.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xlnclit.hxx> + +#include <sfx2/evntconf.hxx> + +#include <sot/formats.hxx> + +#include <svx/svxdlg.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <memory> + +using namespace com::sun::star; +using ::com::sun::star::frame::XFrame; +using ::com::sun::star::uno::Reference; + +#define TRANSCOL COL_WHITE + +IMapWindow::IMapWindow(const Reference< XFrame >& rxDocumentFrame, weld::Dialog* pDialog) + : GraphCtrl(pDialog) + , mxDocumentFrame(rxDocumentFrame) +{ + pIMapPool = new SfxItemPool( "IMapItemPool", + SID_ATTR_MACROITEM, SID_ATTR_MACROITEM, maItemInfos ); + pIMapPool->FreezeIdRanges(); +} + +IMapWindow::~IMapWindow() +{ +} + +void IMapWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(270, 170), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + + SetSdrMode(true); + + mxDropTargetHelper.reset(new IMapDropTargetHelper(*this)); +} + +void IMapWindow::SetImageMap( const ImageMap& rImageMap ) +{ + ReplaceImageMap( rImageMap ); +} + +void IMapWindow::ReplaceImageMap( const ImageMap& rImageMap ) +{ + SdrPage* pPage = nullptr; + aIMap = rImageMap; + + if(GetSdrModel()) + { + // try to access page + pPage = GetSdrModel()->GetPage(0); + } + + if(pPage) + { + // clear SdrObjects with broadcasting + pPage->ClearSdrObjList(); + } + + if(GetSdrView()) + { + // #i63762# reset selection at view + GetSdrView()->UnmarkAllObj(); + } + + // create new drawing objects + const sal_uInt16 nCount(rImageMap.GetIMapObjectCount()); + + for ( sal_uInt16 i(nCount); i > 0; i-- ) + { + rtl::Reference<SdrObject> pNewObj = CreateObj( rImageMap.GetIMapObject( i - 1 ) ); + + if (pNewObj && pPage) + { + pPage->InsertObject( pNewObj.get() ); + } + } +} + +void IMapWindow::ReplaceActualIMapInfo( const NotifyInfo& rNewInfo ) +{ + const SdrObject* pSdrObj = GetSelectedSdrObject(); + + if ( pSdrObj ) + { + IMapObject* pIMapObj = GetIMapObj( pSdrObj ); + if (pIMapObj) + { + pIMapObj->SetURL( rNewInfo.aMarkURL ); + pIMapObj->SetAltText( rNewInfo.aMarkAltText ); + pIMapObj->SetTarget( rNewInfo.aMarkTarget ); + pModel->SetChanged(); + UpdateInfo( false ); + } + } +} + +const ImageMap& IMapWindow::GetImageMap() +{ + if ( pModel->IsChanged() ) + { + SdrPage* pPage = pModel->GetPage( 0 ); + + if ( pPage ) + { + const size_t nCount = pPage->GetObjCount(); + + aIMap.ClearImageMap(); + + for ( size_t i = nCount; i; ) + { + --i; + aIMap.InsertIMapObject( *( static_cast<IMapUserData*>( pPage->GetObj( i )->GetUserData( 0 ) )->GetObject() ) ); + } + } + + pModel->SetChanged( false ); + } + + return aIMap; +} + +void IMapWindow::SetTargetList( const TargetList& rTargetList ) +{ + // Delete old List + // Fill with the provided list + aTargetList = rTargetList; + + pModel->SetChanged( false ); +} + +rtl::Reference<SdrObject> IMapWindow::CreateObj( const IMapObject* pIMapObj ) +{ + tools::Rectangle aClipRect( Point(), GetGraphicSize() ); + rtl::Reference<SdrObject> pSdrObj; + IMapObjectPtr pCloneIMapObj; + + switch( pIMapObj->GetType() ) + { + case IMapObjectType::Rectangle: + { + const IMapRectangleObject* pIMapRectObj = static_cast<const IMapRectangleObject*>(pIMapObj); + tools::Rectangle aDrawRect( pIMapRectObj->GetRectangle( false ) ); + + // clipped on CanvasPane + aDrawRect.Intersection( aClipRect ); + + pSdrObj = new SdrRectObj(*pModel, aDrawRect); + pCloneIMapObj.reset(static_cast<IMapObject*>(new IMapRectangleObject( *pIMapRectObj ))); + } + break; + + case IMapObjectType::Circle: + { + const IMapCircleObject* pIMapCircleObj = static_cast<const IMapCircleObject*>(pIMapObj); + const Point aCenter( pIMapCircleObj->GetCenter( false ) ); + const tools::Long nRadius = pIMapCircleObj->GetRadius( false ); + const Point aOffset( nRadius, nRadius ); + tools::Rectangle aCircle( aCenter - aOffset, aCenter + aOffset ); + + // limited to CanvasPane + aCircle.Intersection( aClipRect ); + + pSdrObj = new SdrCircObj( + *pModel, + SdrCircKind::Full, + aCircle, + 0_deg100, + 36000_deg100); + pCloneIMapObj.reset(static_cast<IMapObject*>(new IMapCircleObject( *pIMapCircleObj ))); + } + break; + + case IMapObjectType::Polygon: + { + const IMapPolygonObject* pIMapPolyObj = static_cast<const IMapPolygonObject*>(pIMapObj); + + // If it actually is an ellipse, then another ellipse is created again + if ( pIMapPolyObj->HasExtraEllipse() ) + { + tools::Rectangle aDrawRect( pIMapPolyObj->GetExtraEllipse() ); + + // clipped on CanvasPane + aDrawRect.Intersection( aClipRect ); + + pSdrObj = new SdrCircObj( + *pModel, + SdrCircKind::Full, + aDrawRect, + 0_deg100, + 36000_deg100); + } + else + { + const tools::Polygon& rPoly = pIMapPolyObj->GetPolygon( false ); + tools::Polygon aDrawPoly( rPoly ); + + // clipped on CanvasPane + aDrawPoly.Clip( aClipRect ); + + basegfx::B2DPolygon aPolygon; + aPolygon.append(aDrawPoly.getB2DPolygon()); + pSdrObj = new SdrPathObj( + *pModel, + SdrObjKind::Polygon, + basegfx::B2DPolyPolygon(aPolygon)); + } + + pCloneIMapObj.reset(static_cast<IMapObject*>(new IMapPolygonObject( *pIMapPolyObj ))); + } + break; + + default: + break; + } + + if ( pSdrObj ) + { + SfxItemSet aSet( pModel->GetItemPool() ); + + aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); + aSet.Put( XFillColorItem( "", TRANSCOL ) ); + + if ( !pIMapObj->IsActive() ) + { + aSet.Put( XFillTransparenceItem( 100 ) ); + aSet.Put( XLineColorItem( "", COL_RED ) ); + } + else + { + aSet.Put( XFillTransparenceItem( 50 ) ); + aSet.Put( XLineColorItem( "", COL_BLACK ) ); + } + + pSdrObj->SetMergedItemSetAndBroadcast(aSet); + + pSdrObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new IMapUserData( pCloneIMapObj )) ); + pSdrObj->SetUserCall( GetSdrUserCall() ); + } + + return pSdrObj; +} + +void IMapWindow::InitSdrModel() +{ + GraphCtrl::InitSdrModel(); + + SfxItemSet aSet( pModel->GetItemPool() ); + + aSet.Put( XFillColorItem( "", TRANSCOL ) ); + aSet.Put( XFillTransparenceItem( 50 ) ); + pView->SetAttributes( aSet ); + pView->SetFrameDragSingles(); +} + +void IMapWindow::SdrObjCreated( const SdrObject& rObj ) +{ + switch( rObj.GetObjIdentifier() ) + { + case SdrObjKind::Rectangle: + { + SdrRectObj* pRectObj = const_cast<SdrRectObj*>(static_cast<const SdrRectObj*>(&rObj)); + auto pObj = std::make_shared<IMapRectangleObject>( pRectObj->GetLogicRect(), "", "", "", "", "", true, false ); + + pRectObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new IMapUserData( pObj )) ); + } + break; + + case SdrObjKind::CircleOrEllipse: + { + SdrCircObj* pCircObj = const_cast<SdrCircObj*>( static_cast<const SdrCircObj*>(&rObj) ); + rtl::Reference<SdrPathObj> pPathObj = static_cast<SdrPathObj*>( pCircObj->ConvertToPolyObj( false, false ).get() ); + tools::Polygon aPoly(pPathObj->GetPathPoly().getB2DPolygon(0)); + + pPathObj.clear(); + + auto pObj = std::make_shared<IMapPolygonObject>( aPoly, "", "", "", "", "", true, false ); + pObj->SetExtraEllipse( aPoly.GetBoundRect() ); + pCircObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new IMapUserData( pObj )) ); + } + break; + + case SdrObjKind::Polygon: + case SdrObjKind::FreehandFill: + case SdrObjKind::PathPoly: + case SdrObjKind::PathFill: + { + SdrPathObj* pPathObj = const_cast<SdrPathObj*>( static_cast<const SdrPathObj*>(&rObj) ); + const basegfx::B2DPolyPolygon& rXPolyPoly = pPathObj->GetPathPoly(); + + if ( rXPolyPoly.count() ) + { + tools::Polygon aPoly(rXPolyPoly.getB2DPolygon(0)); + auto pObj = std::make_shared<IMapPolygonObject>( aPoly, "", "", "", "", "", true, false ); + pPathObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new IMapUserData( pObj )) ); + } + } + break; + + default: + break; + } +} + +void IMapWindow::SdrObjChanged( const SdrObject& rObj ) +{ + IMapUserData* pUserData = static_cast<IMapUserData*>( rObj.GetUserData( 0 ) ); + + if ( !pUserData ) + return; + + OUString aURL; + OUString aAltText; + OUString aDesc; + OUString aTarget; + IMapObjectPtr pIMapObj = pUserData->GetObject(); + bool bActive = true; + + if ( pIMapObj ) + { + aURL = pIMapObj->GetURL(); + aAltText = pIMapObj->GetAltText(); + aDesc = pIMapObj->GetDesc(); + aTarget = pIMapObj->GetTarget(); + bActive = pIMapObj->IsActive(); + } + + switch( rObj.GetObjIdentifier() ) + { + case SdrObjKind::Rectangle: + { + pUserData->ReplaceObject( std::make_shared<IMapRectangleObject>( static_cast<const SdrRectObj&>(rObj).GetLogicRect(), + aURL, aAltText, aDesc, aTarget, "", bActive, false ) ); + } + break; + + case SdrObjKind::CircleOrEllipse: + { + const SdrCircObj& rCircObj = static_cast<const SdrCircObj&>(rObj); + rtl::Reference<SdrPathObj> pPathObj = static_cast<SdrPathObj*>( rCircObj.ConvertToPolyObj( false, false ).get() ); + tools::Polygon aPoly(pPathObj->GetPathPoly().getB2DPolygon(0)); + + auto pObj = std::make_shared<IMapPolygonObject>( aPoly, aURL, aAltText, aDesc, aTarget, "", bActive, false ); + pObj->SetExtraEllipse( aPoly.GetBoundRect() ); + + pPathObj.clear(); + + pUserData->ReplaceObject( pObj ); + } + break; + + case SdrObjKind::Polygon: + case SdrObjKind::FreehandFill: + case SdrObjKind::PathPoly: + case SdrObjKind::PathFill: + { + const SdrPathObj& rPathObj = static_cast<const SdrPathObj&>(rObj); + const basegfx::B2DPolyPolygon& rXPolyPoly = rPathObj.GetPathPoly(); + + if ( rXPolyPoly.count() ) + { + tools::Polygon aPoly(rPathObj.GetPathPoly().getB2DPolygon(0)); + auto pObj = std::make_shared<IMapPolygonObject>( aPoly, aURL, aAltText, aDesc, aTarget, "", bActive, false ); + pUserData->ReplaceObject( pObj ); + } + } + break; + + default: + break; + } +} + +bool IMapWindow::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bRet = GraphCtrl::MouseButtonUp( rMEvt ); + UpdateInfo( true ); + return bRet; +} + +void IMapWindow::MarkListHasChanged() +{ + GraphCtrl::MarkListHasChanged(); + UpdateInfo( false ); +} + +SdrObject* IMapWindow::GetHitSdrObj( const Point& rPosPixel ) const +{ + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + + SdrObject* pObj = nullptr; + Point aPt = rDevice.PixelToLogic( rPosPixel ); + + if ( tools::Rectangle( Point(), GetGraphicSize() ).Contains( aPt ) ) + { + SdrPage* pPage = pModel->GetPage( 0 ); + if ( pPage ) + { + for ( size_t i = pPage->GetObjCount(); i > 0; ) + { + --i; + SdrObject* pTestObj = pPage->GetObj( i ); + IMapObject* pIMapObj = GetIMapObj( pTestObj ); + + if ( pIMapObj && pIMapObj->IsHit( aPt ) ) + { + pObj = pTestObj; + break; + } + } + } + } + + return pObj; +} + +IMapObject* IMapWindow::GetIMapObj( const SdrObject* pSdrObj ) +{ + IMapObject* pIMapObj = nullptr; + + if ( pSdrObj ) + { + IMapUserData* pUserData = static_cast<IMapUserData*>( pSdrObj->GetUserData( 0 ) ); + + if ( pUserData ) + pIMapObj = pUserData->GetObject().get(); + } + + return pIMapObj; +} + +bool IMapWindow::Command(const CommandEvent& rCEvt) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetDrawingArea(), "svx/ui/imapmenu.ui")); + mxPopupMenu = xBuilder->weld_menu("menu"); + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + const size_t nMarked = rMarkList.GetMarkCount(); + + mxPopupMenu->set_sensitive("url", false); + mxPopupMenu->set_sensitive("active", false); + mxPopupMenu->set_sensitive("macro", false); + mxPopupMenu->set_sensitive("selectall", pModel->GetPage(0)->GetObjCount() != pView->GetMarkedObjectCount()); + + if ( !nMarked ) + { + mxPopupMenu->set_sensitive("arrange", false); + mxPopupMenu->set_sensitive("delete", false); + } + else + { + if ( nMarked == 1 ) + { + SdrObject* pSdrObj = GetSelectedSdrObject(); + + mxPopupMenu->set_sensitive("url", true); + mxPopupMenu->set_sensitive("active", true); + mxPopupMenu->set_sensitive("macro", true); + mxPopupMenu->set_active("active", GetIMapObj(pSdrObj)->IsActive()); + } + + mxPopupMenu->set_sensitive("arrange", true); + mxPopupMenu->set_sensitive("delete", true); + } + + MenuSelectHdl(mxPopupMenu->popup_at_rect(GetDrawingArea(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)))); + + mxPopupMenu.reset(); + + return true; + } + return CustomWidgetController::Command(rCEvt); +} + +IMapDropTargetHelper::IMapDropTargetHelper(IMapWindow& rImapWindow) + : DropTargetHelper(rImapWindow.GetDrawingArea()->get_drop_target()) + , m_rImapWindow(rImapWindow) +{ +} + +sal_Int8 IMapDropTargetHelper::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + return m_rImapWindow.AcceptDrop(rEvt); +} + +sal_Int8 IMapDropTargetHelper::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + return m_rImapWindow.ExecuteDrop(rEvt); +} + +sal_Int8 IMapWindow::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + return( ( GetHitSdrObj( rEvt.maPosPixel ) != nullptr ) ? rEvt.mnAction : DND_ACTION_NONE ); +} + +sal_Int8 IMapWindow::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + if (mxDropTargetHelper->IsDropFormatSupported(SotClipboardFormatId::NETSCAPE_BOOKMARK)) + { + INetBookmark aBookMark( "", "" ); + SdrObject* pSdrObj = GetHitSdrObj( rEvt.maPosPixel ); + + if( pSdrObj && TransferableDataHelper( rEvt.maDropEvent.Transferable ).GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aBookMark ) ) + { + IMapObject* pIMapObj = GetIMapObj( pSdrObj ); + + pIMapObj->SetURL( aBookMark.GetURL() ); + pIMapObj->SetAltText( aBookMark.GetDescription() ); + pModel->SetChanged(); + pView->UnmarkAll(); + pView->MarkObj( pSdrObj, pView->GetSdrPageView() ); + UpdateInfo( true ); + nRet = rEvt.mnAction; + } + } + + return nRet; +} + +OUString IMapWindow::RequestHelp(tools::Rectangle& rHelpArea) +{ + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + + Point aPos = rDevice.PixelToLogic(rHelpArea.TopLeft()); + + SdrPageView* pPageView = nullptr; + SdrObject* pSdrObj = pView->PickObj(aPos, pView->getHitTolLog(), pPageView); + if (pSdrObj) + { + const IMapObject* pIMapObj = GetIMapObj( pSdrObj ); + if ( pIMapObj ) + { + OUString aStr = pIMapObj->GetURL(); + if ( !aStr.isEmpty() ) + { + rHelpArea = rDevice.LogicToPixel(tools::Rectangle( Point(), GetGraphicSize())); + return aStr; + } + } + } + + return OUString(); +} + +void IMapWindow::SetCurrentObjState( bool bActive ) +{ + SdrObject* pObj = GetSelectedSdrObject(); + + if ( !pObj ) + return; + + SfxItemSet aSet( pModel->GetItemPool() ); + + GetIMapObj( pObj )->SetActive( bActive ); + + aSet.Put( XFillColorItem( "", TRANSCOL ) ); + + if ( !bActive ) + { + aSet.Put( XFillTransparenceItem( 100 ) ); + aSet.Put( XLineColorItem( "", COL_RED ) ); + } + else + { + aSet.Put( XFillTransparenceItem( 50 ) ); + aSet.Put( XLineColorItem( "", COL_BLACK ) ); + } + + pView->SetAttributes( aSet ); +} + +void IMapWindow::UpdateInfo( bool bNewObj ) +{ + if ( !aInfoLink.IsSet() ) + return; + + const SdrObject* pSdrObj = GetSelectedSdrObject(); + const IMapObject* pIMapObj = pSdrObj ? GetIMapObj( pSdrObj ) : nullptr; + + aInfo.bNewObj = bNewObj; + + if ( pIMapObj ) + { + aInfo.bOneMarked = true; + aInfo.aMarkURL = pIMapObj->GetURL(); + aInfo.aMarkAltText = pIMapObj->GetAltText(); + aInfo.aMarkTarget = pIMapObj->GetTarget(); + aInfo.bActivated = pIMapObj->IsActive(); + aInfoLink.Call( *this ); + } + else + { + aInfo.aMarkURL.clear(); + aInfo.aMarkAltText.clear(); + aInfo.aMarkTarget.clear(); + aInfo.bOneMarked = false; + aInfo.bActivated = false; + } + + aInfoLink.Call( *this ); +} + +void IMapWindow::DoMacroAssign() +{ + SdrObject* pSdrObj = GetSelectedSdrObject(); + + if ( !pSdrObj ) + return; + + SfxItemSetFixed<SID_ATTR_MACROITEM, SID_ATTR_MACROITEM, SID_EVENTCONFIG, SID_EVENTCONFIG> + aSet(*pIMapPool); + + SfxEventNamesItem aNamesItem(SID_EVENTCONFIG); + aNamesItem.AddEvent( "MouseOver", "", SvMacroItemId::OnMouseOver ); + aNamesItem.AddEvent( "MouseOut", "", SvMacroItemId::OnMouseOut ); + aSet.Put( aNamesItem ); + + SvxMacroItem aMacroItem(SID_ATTR_MACROITEM); + IMapObject* pIMapObj = GetIMapObj( pSdrObj ); + aMacroItem.SetMacroTable( pIMapObj->GetMacroTable() ); + aSet.Put( aMacroItem ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractDialog> pMacroDlg(pFact->CreateEventConfigDialog(GetDrawingArea(), aSet, mxDocumentFrame)); + + if ( pMacroDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutSet = pMacroDlg->GetOutputItemSet(); + pIMapObj->SetMacroTable( pOutSet->Get( SID_ATTR_MACROITEM ).GetMacroTable() ); + pModel->SetChanged(); + UpdateInfo( false ); + } +} + +void IMapWindow::DoPropertyDialog() +{ + SdrObject* pSdrObj = GetSelectedSdrObject(); + + if ( !pSdrObj ) + return; + + IMapObject* pIMapObj = GetIMapObj( pSdrObj ); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractURLDlg> aDlg(pFact->CreateURLDialog(GetDrawingArea(), pIMapObj->GetURL(), pIMapObj->GetAltText(), pIMapObj->GetDesc(), + pIMapObj->GetTarget(), pIMapObj->GetName(), aTargetList)); + if ( aDlg->Execute() != RET_OK ) + return; + + const OUString aURLText( aDlg->GetURL() ); + + if ( !aURLText.isEmpty() ) + { + INetURLObject aObj( aURLText, INetProtocol::File ); + DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL" ); + pIMapObj->SetURL( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + } + else + pIMapObj->SetURL( aURLText ); + + pIMapObj->SetAltText( aDlg->GetAltText() ); + pIMapObj->SetDesc( aDlg->GetDesc() ); + pIMapObj->SetTarget( aDlg->GetTarget() ); + pIMapObj->SetName( aDlg->GetName() ); + pModel->SetChanged(); + UpdateInfo( true ); +} + +void IMapWindow::MenuSelectHdl(const OUString& rId) +{ + if (rId == "url") + DoPropertyDialog(); + else if (rId == "macro") + DoMacroAssign(); + else if (rId == "active") + { + const bool bNewState = !mxPopupMenu->get_active(rId); + SetCurrentObjState(bNewState); + UpdateInfo( false ); + } + else if (rId == "front") + pView->PutMarkedToTop(); + else if (rId == "forward") + pView->MovMarkedToTop(); + else if (rId == "backward") + pView->MovMarkedToBtm(); + else if (rId == "back") + pView->PutMarkedToBtm(); + else if (rId == "selectall") + pView->MarkAll(); + else if (rId == "delete") + pView->DeleteMarked(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/imapwnd.hxx b/svx/source/dialog/imapwnd.hxx new file mode 100644 index 0000000000..fee372a25f --- /dev/null +++ b/svx/source/dialog/imapwnd.hxx @@ -0,0 +1,142 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_SVX_SOURCE_DIALOG_IMAPWND_HXX +#define INCLUDED_SVX_SOURCE_DIALOG_IMAPWND_HXX + +#include <utility> +#include <vcl/imapobj.hxx> +#include <vcl/transfer.hxx> +#include <vcl/imap.hxx> +#include <sfx2/frame.hxx> +#include <svx/graphctl.hxx> +#include <svl/itempool.hxx> + +#include <com/sun/star/frame/XFrame.hpp> + +struct NotifyInfo +{ + OUString aMarkURL; + OUString aMarkAltText; + OUString aMarkTarget; + bool bNewObj; + bool bOneMarked; + bool bActivated; +}; + + +#define SVD_IMAP_USERDATA 0x0001 + +typedef std::shared_ptr< IMapObject > IMapObjectPtr; + +class IMapUserData : public SdrObjUserData +{ + // #i98386# use std::shared_ptr here due to cloning possibilities + IMapObjectPtr mpObj; + +public: + + explicit IMapUserData( IMapObjectPtr xIMapObj ) : + SdrObjUserData ( SdrInventor::IMap, SVD_IMAP_USERDATA ), + mpObj (std::move( xIMapObj )) {} + + IMapUserData( const IMapUserData& rIMapUserData ) : + SdrObjUserData ( SdrInventor::IMap, SVD_IMAP_USERDATA ), + mpObj ( rIMapUserData.mpObj ) {} + + virtual std::unique_ptr<SdrObjUserData> Clone( SdrObject * ) const override { return std::unique_ptr<SdrObjUserData>(new IMapUserData( *this )); } + + const IMapObjectPtr& GetObject() const { return mpObj; } + void ReplaceObject( const IMapObjectPtr& pNewIMapObject ) { mpObj = pNewIMapObject; } +}; + +class IMapWindow; + +class IMapDropTargetHelper final : public DropTargetHelper +{ + IMapWindow& m_rImapWindow; +public: + IMapDropTargetHelper(IMapWindow& rImapWindow); + + // DropTargetHelper + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; +}; + +class IMapWindow final : public GraphCtrl +{ + NotifyInfo aInfo; + ImageMap aIMap; + TargetList aTargetList; + Link<IMapWindow&,void> aInfoLink; + rtl::Reference<SfxItemPool> pIMapPool; + SfxItemInfo maItemInfos[1] = {}; + css::uno::Reference< css::frame::XFrame > + mxDocumentFrame; + std::unique_ptr<IMapDropTargetHelper> mxDropTargetHelper; + std::unique_ptr<weld::Menu> mxPopupMenu; + + void MenuSelectHdl(const OUString& rId); + + // GraphCtrl + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + virtual bool Command(const CommandEvent& rCEvt) override; + virtual OUString RequestHelp(tools::Rectangle& rHelpArea) override; + virtual void SdrObjCreated( const SdrObject& rObj ) override; + virtual void SdrObjChanged( const SdrObject& rObj ) override; + virtual void MarkListHasChanged() override; + virtual void InitSdrModel() override; + + void ReplaceImageMap( const ImageMap& rNewImageMap ); + + rtl::Reference<SdrObject> CreateObj( const IMapObject* pIMapObj ); + static IMapObject* GetIMapObj( const SdrObject* pSdrObj ); + SdrObject* GetHitSdrObj( const Point& rPosPixel ) const; + + void UpdateInfo( bool bNewObj ); + +public: + + IMapWindow(const css::uno::Reference< css::frame::XFrame >& rxDocumentFrame, + weld::Dialog* pDialog); + virtual ~IMapWindow() override; + + sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ); + sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ); + + void ReplaceActualIMapInfo( const NotifyInfo& rNewInfo ); + + void SetImageMap( const ImageMap& rImageMap ); + const ImageMap& GetImageMap(); + + void SetCurrentObjState( bool bActive ); + void DoMacroAssign(); + void DoPropertyDialog(); + + void SetInfoLink( const Link<IMapWindow&,void>& rLink ) { aInfoLink = rLink; } + + void SetTargetList( const TargetList& rTargetList ); + + const NotifyInfo& GetInfo() const { return aInfo; } +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/langbox.cxx b/svx/source/dialog/langbox.cxx new file mode 100644 index 0000000000..713bf0d34b --- /dev/null +++ b/svx/source/dialog/langbox.cxx @@ -0,0 +1,553 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/linguistic2/XAvailableLocales.hpp> +#include <com/sun/star/linguistic2/XLinguServiceManager2.hpp> +#include <com/sun/star/linguistic2/XSpellChecker1.hpp> +#include <linguistic/misc.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <unotools/localedatawrapper.hxx> +#include <tools/urlobj.hxx> +#include <svtools/langtab.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <i18nlangtag/lang.h> +#include <editeng/unolingu.hxx> +#include <svl/languageoptions.hxx> +#include <svx/langbox.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <bitmaps.hlst> + +#include <comphelper/string.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/scopeguard.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::linguistic2; +using namespace ::com::sun::star::uno; + +OUString GetDicInfoStr( std::u16string_view rName, const LanguageType nLang, bool bNeg ) +{ + INetURLObject aURLObj; + aURLObj.SetSmartProtocol( INetProtocol::File ); + aURLObj.SetSmartURL( rName, INetURLObject::EncodeMechanism::All ); + OUString aTmp( aURLObj.GetBase() + " " ); + + if ( bNeg ) + { + aTmp += " (-) "; + } + + if ( LANGUAGE_NONE == nLang ) + aTmp += SvxResId(RID_SVXSTR_LANGUAGE_ALL); + else + { + aTmp += "[" + SvtLanguageTable::GetLanguageString( nLang ) + "]"; + } + + return aTmp; +} + +// misc local helper functions +static void appendLocaleSeqToLangs(Sequence<css::lang::Locale> const& rSeq, + std::vector<LanguageType>& aLangs) +{ + sal_Int32 nCount = rSeq.getLength(); + + aLangs.reserve(aLangs.size() + nCount); + + std::transform(rSeq.begin(), rSeq.end(), std::back_inserter(aLangs), + [](const css::lang::Locale& rLocale) -> LanguageType { + return LanguageTag::convertToLanguageType(rLocale); }); +} + +static bool lcl_SeqHasLang( const Sequence< sal_Int16 > & rLangSeq, sal_Int16 nLang ) +{ + return rLangSeq.hasElements() + && std::find(rLangSeq.begin(), rLangSeq.end(), nLang) != rLangSeq.end(); +} + +namespace { + +bool lcl_isPrerequisite(LanguageType nLangType, bool requireSublang) +{ + return + nLangType != LANGUAGE_DONTKNOW && + nLangType != LANGUAGE_SYSTEM && + nLangType != LANGUAGE_NONE && + nLangType != LANGUAGE_USER_KEYID && + !MsLangId::isLegacy( nLangType) && + (!requireSublang || MsLangId::getSubLanguage( nLangType)); +} + +bool lcl_isScriptTypeRequested( LanguageType nLangType, SvxLanguageListFlags nLangList ) +{ + return + bool(nLangList & SvxLanguageListFlags::ALL) || + (bool(nLangList & SvxLanguageListFlags::WESTERN) && + (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::LATIN)) || + (bool(nLangList & SvxLanguageListFlags::CTL) && + (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::COMPLEX)) || + (bool(nLangList & SvxLanguageListFlags::CJK) && + (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::ASIAN)); +} + +} + + +LanguageType SvxLanguageBox::get_active_id() const +{ + OUString sLang = m_xControl->get_active_id(); + if (!sLang.isEmpty()) + return LanguageType(sLang.toInt32()); + else + return LANGUAGE_DONTKNOW; +} + +int SvxLanguageBox::find_id(const LanguageType eLangType) const +{ + return m_xControl->find_id(OUString::number(static_cast<sal_uInt16>(eLangType))); +} + +void SvxLanguageBox::set_id(int pos, const LanguageType eLangType) +{ + m_xControl->set_id(pos, OUString::number(static_cast<sal_uInt16>(eLangType))); +} + +LanguageType SvxLanguageBox::get_id(int pos) const +{ + return LanguageType(m_xControl->get_id(pos).toInt32()); +} + +void SvxLanguageBox::remove_id(const LanguageType eLangType) +{ + m_xControl->remove_id(OUString::number(static_cast<sal_uInt16>(eLangType))); +} + +void SvxLanguageBox::append(const LanguageType eLangType, const OUString& rStr) +{ + m_xControl->append(OUString::number(static_cast<sal_uInt16>(eLangType)), rStr); +} + +void SvxLanguageBox::set_active_id(const LanguageType eLangType) +{ + // If the core uses a LangID of an imported MS document and wants to select + // a language that is replaced, we need to select the replacement instead. + LanguageType nLang = MsLangId::getReplacementForObsoleteLanguage( eLangType); + + sal_Int32 nAt = find_id( nLang ); + + if (nAt == -1) + { + InsertLanguage( nLang ); // on-the-fly-ID + nAt = find_id( nLang ); + } + + if (nAt != -1) + m_xControl->set_active(nAt); +} + +void SvxLanguageBox::AddLanguages(const std::vector< LanguageType >& rLanguageTypes, + SvxLanguageListFlags nLangList, std::vector<weld::ComboBoxEntry>& rEntries, bool requireSublang) +{ + for ( auto const & nLangType : rLanguageTypes ) + { + if (lcl_isPrerequisite(nLangType, requireSublang)) + { + LanguageType nLang = MsLangId::getReplacementForObsoleteLanguage( nLangType ); + if (lcl_isScriptTypeRequested( nLang, nLangList)) + { + int nAt = find_id(nLang); + if (nAt != -1) + continue; + weld::ComboBoxEntry aNewEntry(BuildEntry(nLang)); + if (aNewEntry.sString.isEmpty()) + continue; + rEntries.push_back(aNewEntry); + } + } + } +} + +static void SortLanguages(std::vector<weld::ComboBoxEntry>& rEntries) +{ + auto langLess = [](const weld::ComboBoxEntry& e1, const weld::ComboBoxEntry& e2) + { + if (e1.sId == e2.sId) + return false; // shortcut + // Make sure that e.g. generic 'Spanish {es}' goes before 'Spanish (Argentina)'. + // We can't depend on MsLangId::getPrimaryLanguage/getSubLanguage, because e.g. + // for generic Bosnian {bs}, the MS-LCID is 0x781A, and getSubLanguage is not 0. + // So we have to do the expensive LanguageTag construction. + LanguageTag lt1(LanguageType(e1.sId.toInt32())), lt2(LanguageType(e2.sId.toInt32())); + if (lt1.getLanguage() == lt2.getLanguage()) + { + const bool isLangOnly1 = lt1.isIsoLocale() && lt1.getCountry().isEmpty(); + const bool isLangOnly2 = lt2.isIsoLocale() && lt2.getCountry().isEmpty(); + + if (isLangOnly1) + { + // lt1 is a generic language-only tag + if (!isLangOnly2) + return true; // lt2 is not + } + else if (isLangOnly2) + { + // lt2 is a generic language-only tag, lt1 is not + return false; + } + } + // Do a normal string comparison for other cases + static const auto aSorter = comphelper::string::NaturalStringSorter( + comphelper::getProcessComponentContext(), + Application::GetSettings().GetUILanguageTag().getLocale()); + return aSorter.compare(e1.sString, e2.sString) < 0; + }; + + std::sort(rEntries.begin(), rEntries.end(), langLess); + rEntries.erase(std::unique(rEntries.begin(), rEntries.end(), + [](const weld::ComboBoxEntry& e1, const weld::ComboBoxEntry& e2) + { return e1.sId == e2.sId; }), + rEntries.end()); +} + +void SvxLanguageBox::SetLanguageList(SvxLanguageListFlags nLangList, bool bHasLangNone, + bool bLangNoneIsLangAll, bool bCheckSpellAvail, + bool bDefaultLangExist, LanguageType eDefaultLangType, + sal_Int16 nDefaultType) +{ + m_bHasLangNone = bHasLangNone; + m_bLangNoneIsLangAll = bLangNoneIsLangAll; + m_bWithCheckmark = bCheckSpellAvail; + + m_xControl->freeze(); + comphelper::ScopeGuard aThawGuard([this]() { m_xControl->thaw(); }); + m_xControl->clear(); + + if (SvxLanguageListFlags::EMPTY == nLangList) + return; + + bool bAddSeparator = false; + + if (bHasLangNone) + { + m_xControl->append(BuildEntry(LANGUAGE_NONE)); + bAddSeparator = true; + } + + if (bDefaultLangExist) + { + m_xControl->append(BuildEntry(eDefaultLangType, nDefaultType)); + bAddSeparator = true; + } + + if (bAddSeparator) + m_xControl->append_separator(""); + + bool bAddAvailable = (!(nLangList & SvxLanguageListFlags::ONLY_KNOWN) && + ((nLangList & SvxLanguageListFlags::ALL) || + (nLangList & SvxLanguageListFlags::WESTERN) || + (nLangList & SvxLanguageListFlags::CTL) || + (nLangList & SvxLanguageListFlags::CJK))); + std::vector< LanguageType > aAvailLang; + Sequence< sal_Int16 > aSpellUsedLang; + if (bAddAvailable) + { + if (auto xAvail = LinguMgr::GetLngSvcMgr()) + { + appendLocaleSeqToLangs(xAvail->getAvailableLocales(SN_SPELLCHECKER), aAvailLang); + appendLocaleSeqToLangs(xAvail->getAvailableLocales(SN_HYPHENATOR), aAvailLang); + appendLocaleSeqToLangs(xAvail->getAvailableLocales(SN_THESAURUS), aAvailLang); + } + } + if (SvxLanguageListFlags::SPELL_USED & nLangList) + { + Reference< XSpellChecker1 > xTmp1 = LinguMgr::GetSpellChecker(); + if (xTmp1.is()) + aSpellUsedLang = xTmp1->getLanguages(); + } + + std::vector<LanguageType> aKnown; + sal_uInt32 nCount; + if ( nLangList & SvxLanguageListFlags::ONLY_KNOWN ) + { + aKnown = LocaleDataWrapper::getInstalledLanguageTypes(); + nCount = aKnown.size(); + } + else + { + nCount = SvtLanguageTable::GetLanguageEntryCount(); + } + + std::vector<weld::ComboBoxEntry> aEntries; + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + LanguageType nLangType; + if ( nLangList & SvxLanguageListFlags::ONLY_KNOWN ) + nLangType = aKnown[i]; + else + nLangType = SvtLanguageTable::GetLanguageTypeAtIndex( i ); + if ( lcl_isPrerequisite( nLangType, true ) && + (lcl_isScriptTypeRequested( nLangType, nLangList) || + (bool(nLangList & SvxLanguageListFlags::FBD_CHARS) && + MsLangId::hasForbiddenCharacters(nLangType)) || + (bool(nLangList & SvxLanguageListFlags::SPELL_USED) && + lcl_SeqHasLang(aSpellUsedLang, static_cast<sal_uInt16>(nLangType))) + ) ) + { + aEntries.push_back(BuildEntry(nLangType)); + if (aEntries.back().sString.isEmpty()) + aEntries.pop_back(); + } + } + + if (bAddAvailable) + { + // Spell checkers, hyphenators and thesauri may add language tags + // unknown so far. + AddLanguages(aAvailLang, nLangList, aEntries, true); + } + + SortLanguages(aEntries); + m_xControl->insert_vector(aEntries, true); +} + +void SvxLanguageBox::InsertLanguage(const LanguageType nLangType) +{ + if (find_id(nLangType) != -1) + return; + weld::ComboBoxEntry aEntry = BuildEntry(nLangType); + if (aEntry.sString.isEmpty()) + return; + m_xControl->append(aEntry); +} + +void SvxLanguageBox::InsertLanguages(const std::vector<LanguageType>& rLanguageTypes) +{ + std::vector<weld::ComboBoxEntry> entries; + AddLanguages(rLanguageTypes, SvxLanguageListFlags::ALL, entries, false); + SortLanguages(entries); + m_xControl->insert_vector(entries, true); +} + +weld::ComboBoxEntry SvxLanguageBox::BuildEntry(const LanguageType nLangType, sal_Int16 nType) +{ + LanguageType nLang = MsLangId::getReplacementForObsoleteLanguage(nLangType); + // For obsolete and to be replaced languages check whether an entry of the + // replacement already exists and if so don't add an entry with identical + // string as would be returned by SvtLanguageTable::GetString(). + if (nLang != nLangType) + { + int nAt = find_id( nLang ); + if (nAt != -1) + return weld::ComboBoxEntry(""); + } + + OUString aStrEntry = (LANGUAGE_NONE == nLang && m_bHasLangNone && m_bLangNoneIsLangAll) + ? SvxResId(RID_SVXSTR_LANGUAGE_ALL) + : SvtLanguageTable::GetLanguageString(nLang); + + LanguageType nRealLang = nLang; + if (nRealLang == LANGUAGE_SYSTEM) + { + nRealLang = MsLangId::resolveSystemLanguageByScriptType(nRealLang, nType); + aStrEntry += " - " + SvtLanguageTable::GetLanguageString( nRealLang ); + } + else if (nRealLang == LANGUAGE_USER_SYSTEM_CONFIG) + { + nRealLang = MsLangId::getSystemLanguage(); + // Whatever we obtained, ensure a known supported locale. + nRealLang = LanguageTag(nRealLang).makeFallback().getLanguageType(); + aStrEntry += " - " + SvtLanguageTable::GetLanguageString( nRealLang ); + } + + if (m_bWithCheckmark) + { + if (!m_xSpellUsedLang) + { + Reference<XSpellChecker1> xSpell = LinguMgr::GetSpellChecker(); + if (xSpell.is()) + m_xSpellUsedLang.reset(new Sequence<sal_Int16>(xSpell->getLanguages())); + } + + bool bFound = m_xSpellUsedLang && lcl_SeqHasLang(*m_xSpellUsedLang, static_cast<sal_uInt16>(nRealLang)); + + return weld::ComboBoxEntry(aStrEntry, OUString::number(static_cast<sal_uInt16>(nLang)), bFound ? RID_SVXBMP_CHECKED : RID_SVXBMP_NOTCHECKED); + } + else + return weld::ComboBoxEntry(aStrEntry, OUString::number(static_cast<sal_uInt16>(nLang))); +} + +IMPL_LINK(SvxLanguageBox, ChangeHdl, weld::ComboBox&, rControl, void) +{ + if (rControl.has_entry()) + { + EditedAndValid eOldState = m_eEditedAndValid; + OUString aStr(rControl.get_active_text()); + if (aStr.isEmpty()) + m_eEditedAndValid = EditedAndValid::Invalid; + else + { + const int nPos = rControl.find_text(aStr); + if (nPos != -1) + { + int nStartSelectPos, nEndSelectPos; + rControl.get_entry_selection_bounds(nStartSelectPos, nEndSelectPos); + + // Select the corresponding listbox entry if not current. This + // invalidates the Edit Selection thus has to happen between + // obtaining the Selection and setting the new Selection. + int nSelPos = m_xControl->get_active(); + bool bSetEditSelection; + if (nSelPos == nPos) + bSetEditSelection = false; + else + { + m_xControl->set_active(nPos); + bSetEditSelection = true; + } + + // If typing into the Edit control led us here, advance start of a + // full selection by one so the next character will already + // continue the string instead of having to type the same character + // again to start a new string. The selection is in reverse + // when obtained from the Edit control. + if (nEndSelectPos == 0) + { + OUString aText(m_xControl->get_active_text()); + if (nStartSelectPos == aText.getLength()) + { + ++nEndSelectPos; + bSetEditSelection = true; + } + } + + if (bSetEditSelection) + rControl.select_entry_region(nStartSelectPos, nEndSelectPos); + + m_eEditedAndValid = EditedAndValid::No; + } + else + { + OUString aCanonicalized; + bool bValid = LanguageTag::isValidBcp47( aStr, &aCanonicalized, LanguageTag::PrivateUse::ALLOW_ART_X); + m_eEditedAndValid = (bValid ? EditedAndValid::Valid : EditedAndValid::Invalid); + if (bValid && aCanonicalized != aStr) + { + m_xControl->set_entry_text(aCanonicalized); + const auto nCursorPos = aCanonicalized.getLength(); + m_xControl->select_entry_region(nCursorPos, nCursorPos); + } + } + } + if (eOldState != m_eEditedAndValid) + { + if (m_eEditedAndValid == EditedAndValid::Invalid) + rControl.set_entry_message_type(weld::EntryMessageType::Error); + else + rControl.set_entry_message_type(weld::EntryMessageType::Normal); + } + } + m_aChangeHdl.Call(rControl); +} + +SvxLanguageBox::SvxLanguageBox(std::unique_ptr<weld::ComboBox> pControl) + : m_xControl(std::move(pControl)) + , m_eSavedLanguage(LANGUAGE_DONTKNOW) + , m_eEditedAndValid(EditedAndValid::No) + , m_bHasLangNone(false) + , m_bLangNoneIsLangAll(false) + , m_bWithCheckmark(false) +{ + m_xControl->connect_changed(LINK(this, SvxLanguageBox, ChangeHdl)); +} + +SvxLanguageBox* SvxLanguageBox::SaveEditedAsEntry(SvxLanguageBox* ppBoxes[3]) +{ + if (m_eEditedAndValid != EditedAndValid::Valid) + return this; + + LanguageTag aLanguageTag(m_xControl->get_active_text()); + LanguageType nLang = aLanguageTag.getLanguageType(); + if (nLang == LANGUAGE_DONTKNOW) + { + SAL_WARN( "svx.dialog", "SvxLanguageBox::SaveEditedAsEntry: unknown tag"); + return this; + } + + for (size_t i = 0; i < 3; ++i) + { + SvxLanguageBox* pBox = ppBoxes[i]; + if (!pBox) + continue; + + const int nPos = pBox->find_id( nLang); + if (nPos != -1) + { + // Already present but with a different string or in another list. + pBox->m_xControl->set_active(nPos); + return pBox; + } + } + + if (SvtLanguageTable::HasLanguageType( nLang)) + { + // In SvtLanguageTable but not in SvxLanguageBox. On purpose? This + // may be an entry with different settings. + SAL_WARN( "svx.dialog", "SvxLanguageBox::SaveEditedAsEntry: already in SvtLanguageTable: " << + SvtLanguageTable::GetLanguageString( nLang) << ", " << nLang); + } + else + { + // Add to SvtLanguageTable first. This at an on-the-fly LanguageTag + // also sets the ScriptType needed below. + SvtLanguageTable::AddLanguageTag( aLanguageTag ); + } + + // Add to the proper list. + SvxLanguageBox* pBox = nullptr; + switch (MsLangId::getScriptType(nLang)) + { + default: + case css::i18n::ScriptType::LATIN: + pBox = ppBoxes[0]; + break; + case css::i18n::ScriptType::ASIAN: + pBox = ppBoxes[1]; + break; + case css::i18n::ScriptType::COMPLEX: + pBox = ppBoxes[2]; + break; + } + if (!pBox) + pBox = this; + pBox->InsertLanguage(nLang); + + // Select it. + const int nPos = pBox->find_id(nLang); + if (nPos != -1) + pBox->m_xControl->set_active(nPos); + + return pBox; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/linkwarn.cxx b/svx/source/dialog/linkwarn.cxx new file mode 100644 index 0000000000..852c92af10 --- /dev/null +++ b/svx/source/dialog/linkwarn.cxx @@ -0,0 +1,61 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/file.hxx> +#include <svx/linkwarn.hxx> +#include <officecfg/Office/Common.hxx> + +SvxLinkWarningDialog::SvxLinkWarningDialog(weld::Widget* pParent, const OUString& _rFileName) + : MessageDialogController(pParent, "svx/ui/linkwarndialog.ui", "LinkWarnDialog", "ask") + , m_xWarningOnBox(m_xBuilder->weld_check_button("ask")) +{ + // replace filename + OUString sInfoText = m_xDialog->get_primary_text(); + OUString aPath; + if (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(_rFileName, aPath)) + aPath = _rFileName; + sInfoText = sInfoText.replaceAll("%FILENAME", aPath); + m_xDialog->set_primary_text(sInfoText); + + // load state of "warning on" checkbox from misc options + m_xWarningOnBox->set_active(officecfg::Office::Common::Misc::ShowLinkWarningDialog::get()); + m_xWarningOnBox->set_sensitive( + !officecfg::Office::Common::Misc::ShowLinkWarningDialog::isReadOnly()); +} + +SvxLinkWarningDialog::~SvxLinkWarningDialog() +{ + try + { + // save value of "warning off" checkbox, if necessary + bool bChecked = m_xWarningOnBox->get_active(); + if (officecfg::Office::Common::Misc::ShowLinkWarningDialog::get() != bChecked) + { + std::shared_ptr<comphelper::ConfigurationChanges> xChanges( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::Misc::ShowLinkWarningDialog::set(bChecked, xChanges); + xChanges->commit(); + } + } + catch (...) + { + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/measctrl.cxx b/svx/source/dialog/measctrl.cxx new file mode 100644 index 0000000000..9539d2f68c --- /dev/null +++ b/svx/source/dialog/measctrl.cxx @@ -0,0 +1,159 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sfx2/dialoghelper.hxx> +#include <svx/svdomeas.hxx> +#include <svx/svdmodel.hxx> +#include <svx/measctrl.hxx> +#include <svx/dlgutil.hxx> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <memory> + +SvxXMeasurePreview::SvxXMeasurePreview() + : m_aMapMode(MapUnit::Map100thMM) +{ + // Scale: 1:2 + m_aMapMode.SetScaleX(Fraction(1, 2)); + m_aMapMode.SetScaleY(Fraction(1, 2)); +} + +void SvxXMeasurePreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aSize(getPreviewStripSize(pDrawingArea->get_ref_device())); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + + pModel.reset(new SdrModel(nullptr, nullptr, true)); + pMeasureObj = new SdrMeasureObj(*pModel, Point(), Point()); + + ResizeImpl(aSize); + Invalidate(); +} + +void SvxXMeasurePreview::ResizeImpl(const Size& rSize) +{ + OutputDevice& rRefDevice = GetDrawingArea()->get_ref_device(); + rRefDevice.Push(vcl::PushFlags::MAPMODE); + + rRefDevice.SetMapMode(m_aMapMode); + + Size aSize = rRefDevice.PixelToLogic(rSize); + Point aPt1(aSize.Width() / 5, static_cast<tools::Long>(aSize.Height() / 2)); + pMeasureObj->SetPoint(aPt1, 0); + Point aPt2(aSize.Width() * 4 / 5, static_cast<tools::Long>(aSize.Height() / 2)); + pMeasureObj->SetPoint(aPt2, 1); + + rRefDevice.Pop(); +} + +void SvxXMeasurePreview::Resize() +{ + CustomWidgetController::Resize(); + ResizeImpl(GetOutputSizePixel()); + Invalidate(); +} + +SvxXMeasurePreview::~SvxXMeasurePreview() {} + +void SvxXMeasurePreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.SetBackground(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor()); + rRenderContext.Erase(); + + rRenderContext.Push(vcl::PushFlags::MAPMODE); + rRenderContext.SetMapMode(m_aMapMode); + + bool bHighContrast = Application::GetSettings().GetStyleSettings().GetHighContrastMode(); + rRenderContext.SetDrawMode(bHighContrast ? OUTPUT_DRAWMODE_CONTRAST : OUTPUT_DRAWMODE_COLOR); + pMeasureObj->SingleObjectPainter(rRenderContext); + + rRenderContext.Pop(); +} + +void SvxXMeasurePreview::SetAttributes(const SfxItemSet& rInAttrs) +{ + pMeasureObj->SetMergedItemSetAndBroadcast(rInAttrs); + + Invalidate(); +} + +bool SvxXMeasurePreview::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bZoomIn = rMEvt.IsLeft() && !rMEvt.IsShift(); + bool bZoomOut = rMEvt.IsRight() || rMEvt.IsShift(); + bool bCtrl = rMEvt.IsMod1(); + + if (bZoomIn || bZoomOut) + { + Fraction aXFrac = m_aMapMode.GetScaleX(); + Fraction aYFrac = m_aMapMode.GetScaleY(); + std::unique_ptr<Fraction> pMultFrac; + + if (bZoomIn) + { + if (bCtrl) + pMultFrac.reset(new Fraction(3, 2)); + else + pMultFrac.reset(new Fraction(11, 10)); + } + else + { + if (bCtrl) + pMultFrac.reset(new Fraction(2, 3)); + else + pMultFrac.reset(new Fraction(10, 11)); + } + + aXFrac *= *pMultFrac; + aYFrac *= *pMultFrac; + + if (double(aXFrac) > 0.001 && double(aXFrac) < 1000.0 && double(aYFrac) > 0.001 + && double(aYFrac) < 1000.0) + { + m_aMapMode.SetScaleX(aXFrac); + m_aMapMode.SetScaleY(aYFrac); + + OutputDevice& rRefDevice = GetDrawingArea()->get_ref_device(); + rRefDevice.Push(vcl::PushFlags::MAPMODE); + rRefDevice.SetMapMode(m_aMapMode); + Size aOutSize(rRefDevice.PixelToLogic(GetOutputSizePixel())); + rRefDevice.Pop(); + + Point aPt(m_aMapMode.GetOrigin()); + tools::Long nX = tools::Long( + (double(aOutSize.Width()) - (double(aOutSize.Width()) * double(*pMultFrac))) / 2.0 + + 0.5); + tools::Long nY = tools::Long( + (double(aOutSize.Height()) - (double(aOutSize.Height()) * double(*pMultFrac))) / 2.0 + + 0.5); + aPt.AdjustX(nX); + aPt.AdjustY(nY); + + m_aMapMode.SetOrigin(aPt); + + Invalidate(); + } + } + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/optgrid.cxx b/svx/source/dialog/optgrid.cxx new file mode 100644 index 0000000000..2226eaa4e2 --- /dev/null +++ b/svx/source/dialog/optgrid.cxx @@ -0,0 +1,472 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svl/intitem.hxx> +#include <svtools/unitconv.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> +#include <officecfg/Office/Writer.hxx> +#include <officecfg/Office/WriterWeb.hxx> +#include <officecfg/Office/Impress.hxx> +#include <officecfg/Office/Draw.hxx> +#include <sfx2/viewfrm.hxx> +#include <vcl/commandinfoprovider.hxx> + +#include <svx/svxids.hrc> +#include <svx/optgrid.hxx> +#include <svx/dlgutil.hxx> + +// local functions +static void lcl_GetMinMax(weld::MetricSpinButton const& rField, sal_Int64& nMin, sal_Int64& nMax) +{ + rField.get_range(nMin, nMax, FieldUnit::TWIP); + nMin = rField.denormalize(nMin); + nMax = rField.denormalize(nMax); +} + +static void lcl_SetMinMax(weld::MetricSpinButton& rField, sal_Int64 nMin, sal_Int64 nMax) +{ + rField.set_range(rField.normalize(nMin), rField.normalize(nMax), FieldUnit::TWIP); +} + +static bool lcl_IsMetricSystem() +{ + SvtSysLocale aSysLocale; + MeasurementSystem eSys = aSysLocale.GetLocaleData().getMeasurementSystemEnum(); + + return (eSys == MeasurementSystem::Metric); +} + +SvxOptionsGrid::SvxOptionsGrid() : + nFldDrawX ( 100 ), + nFldDivisionX ( 0 ), + nFldDrawY ( 100 ), + nFldDivisionY ( 0 ), + bUseGridsnap ( false ), + bSynchronize ( true ), + bGridVisible ( false ), + bEqualGrid ( true ) +{ +} + +SvxGridItem* SvxGridItem::Clone( SfxItemPool* ) const +{ + return new SvxGridItem( *this ); +} + +bool SvxGridItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + + const SvxGridItem& rItem = static_cast<const SvxGridItem&>(rAttr); + + return ( bUseGridsnap == rItem.bUseGridsnap && + bSynchronize == rItem.bSynchronize && + bGridVisible == rItem.bGridVisible && + bEqualGrid == rItem.bEqualGrid && + nFldDrawX == rItem.nFldDrawX && + nFldDivisionX== rItem.nFldDivisionX&& + nFldDrawY == rItem.nFldDrawY && + nFldDivisionY== rItem.nFldDivisionY ); +} + +bool SvxGridItem::GetPresentation +( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& rText, const IntlWrapper& +) const +{ + rText = "SvxGridItem"; + return true; +} + +// TabPage Screen Settings +SvxGridTabPage::SvxGridTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet) + : SfxTabPage(pPage, pController, "svx/ui/optgridpage.ui", "OptGridPage", &rCoreSet) + , bAttrModified(false) + , m_Emode(WRITER_MODE) + , m_xCbxUseGridsnap(m_xBuilder->weld_check_button("usegridsnap")) + , m_xCbxUseGridsnapImg(m_xBuilder->weld_widget("lockusegridsnap")) + , m_xCbxGridVisible(m_xBuilder->weld_check_button("gridvisible")) + , m_xCbxGridVisibleImg(m_xBuilder->weld_widget("lockgridvisible")) + , m_xMtrFldDrawX(m_xBuilder->weld_metric_spin_button("mtrflddrawx", FieldUnit::CM)) + , m_xMtrFldDrawXImg(m_xBuilder->weld_widget("lockmtrflddrawx")) + , m_xMtrFldDrawY(m_xBuilder->weld_metric_spin_button("mtrflddrawy", FieldUnit::CM)) + , m_xMtrFldDrawYImg(m_xBuilder->weld_widget("lockmtrflddrawy")) + , m_xNumFldDivisionX(m_xBuilder->weld_spin_button("numflddivisionx")) + , m_xNumFldDivisionXImg(m_xBuilder->weld_widget("locknumflddivisionx")) + , m_xNumFldDivisionY(m_xBuilder->weld_spin_button("numflddivisiony")) + , m_xNumFldDivisionYImg(m_xBuilder->weld_widget("locknumflddivisiony")) + , m_xCbxSynchronize(m_xBuilder->weld_check_button("synchronize")) + , m_xCbxSynchronizeImg(m_xBuilder->weld_widget("locksynchronize")) + , m_xSnapFrames(m_xBuilder->weld_widget("snapframes")) + , m_xCbxSnapHelplines(m_xBuilder->weld_check_button("snaphelplines")) + , m_xCbxSnapHelplinesImg(m_xBuilder->weld_widget("locksnaphelplines")) + , m_xCbxSnapBorder(m_xBuilder->weld_check_button("snapborder")) + , m_xCbxSnapBorderImg(m_xBuilder->weld_widget("locksnapborder")) + , m_xCbxSnapFrame(m_xBuilder->weld_check_button("snapframe")) + , m_xCbxSnapFrameImg(m_xBuilder->weld_widget("locksnapframe")) + , m_xCbxSnapPoints(m_xBuilder->weld_check_button("snappoints")) + , m_xCbxSnapPointsImg(m_xBuilder->weld_widget("locksnappoints")) + , m_xMtrFldSnapArea(m_xBuilder->weld_metric_spin_button("mtrfldsnaparea", FieldUnit::PIXEL)) + , m_xMtrFldSnapAreaImg(m_xBuilder->weld_widget("lockmtrfldsnaparea")) + , m_xCbxOrtho(m_xBuilder->weld_check_button("ortho")) + , m_xCbxOrthoImg(m_xBuilder->weld_widget("lockortho")) + , m_xCbxBigOrtho(m_xBuilder->weld_check_button("bigortho")) + , m_xCbxBigOrthoImg(m_xBuilder->weld_widget("lockbigortho")) + , m_xCbxRotate(m_xBuilder->weld_check_button("rotate")) + , m_xCbxRotateImg(m_xBuilder->weld_widget("lockrotate")) + , m_xMtrFldAngle(m_xBuilder->weld_metric_spin_button("mtrfldangle", FieldUnit::DEGREE)) + , m_xMtrFldBezAngle(m_xBuilder->weld_metric_spin_button("mtrfldbezangle", FieldUnit::DEGREE)) + , m_xMtrFldBezAngleImg(m_xBuilder->weld_widget("lockmtrfldbezangle")) +{ + // This page requires exchange Support + SetExchangeSupport(); + + // Set Metrics + FieldUnit eFUnit = GetModuleFieldUnit( rCoreSet ); + sal_Int64 nMin, nMax; + + lcl_GetMinMax(*m_xMtrFldDrawX, nMin, nMax); + SetFieldUnit( *m_xMtrFldDrawX, eFUnit, true ); + lcl_SetMinMax(*m_xMtrFldDrawX, nMin, nMax); + + lcl_GetMinMax(*m_xMtrFldDrawY, nMin, nMax); + SetFieldUnit( *m_xMtrFldDrawY, eFUnit, true ); + lcl_SetMinMax(*m_xMtrFldDrawY, nMin, nMax); + + if (const SfxUInt16Item* pItem = rCoreSet.GetItemIfSet(SID_HTML_MODE, false)) + { + if (0 != (pItem->GetValue() & HTMLMODE_ON)) + m_Emode = HTML_MODE; + } + + if (m_Emode != HTML_MODE) + { + SfxViewFrame* pCurrent = SfxViewFrame::Current(); + OUString aModuleName = vcl::CommandInfoProvider::GetModuleIdentifier(pCurrent->GetFrame().GetFrameInterface()); + std::u16string_view sModulename = aModuleName.subView(aModuleName.lastIndexOf('.') + 1); + if (sModulename.starts_with(u"Text")) + m_Emode = WRITER_MODE; + else if (sModulename.starts_with(u"Spreadsheet")) + m_Emode = CALC_MODE; + else if (sModulename.starts_with(u"Presentation")) + m_Emode = IMPRESS_MODE; + else if (sModulename.starts_with(u"Drawing")) + m_Emode = DRAW_MODE; + } + + m_xCbxRotate->connect_toggled(LINK(this, SvxGridTabPage, ClickRotateHdl_Impl)); + Link<weld::Toggleable&,void> aLink = LINK(this, SvxGridTabPage, ChangeGridsnapHdl_Impl); + m_xCbxUseGridsnap->connect_toggled(aLink); + m_xCbxSynchronize->connect_toggled(aLink); + m_xCbxGridVisible->connect_toggled(aLink); + m_xMtrFldDrawX->connect_value_changed( + LINK( this, SvxGridTabPage, ChangeDrawHdl_Impl ) ); + m_xMtrFldDrawY->connect_value_changed( + LINK( this, SvxGridTabPage, ChangeDrawHdl_Impl ) ); + m_xNumFldDivisionX->connect_value_changed( + LINK( this, SvxGridTabPage, ChangeDivisionHdl_Impl ) ); + m_xNumFldDivisionY->connect_value_changed( + LINK( this, SvxGridTabPage, ChangeDivisionHdl_Impl ) ); +} + +SvxGridTabPage::~SvxGridTabPage() +{ +} + +std::unique_ptr<SfxTabPage> SvxGridTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrSet) +{ + return std::make_unique<SvxGridTabPage>(pPage, pController, rAttrSet); +} + +OUString SvxGridTabPage::GetAllStrings() +{ + OUString sAllStrings; + OUString labels[] + = { "label1", "label2", "flddrawx", "flddrawy", "label6", "label7", "label3", + "divisionx", "label4", "divisiony", "label5", "label8", "label9" }; + + for (const auto& label : labels) + { + if (const auto& pString = m_xBuilder->weld_label(label)) + sAllStrings += pString->get_label() + " "; + } + + OUString checkButton[] + = { "usegridsnap", "gridvisible", "synchronize", "snaphelplines", "snapborder", + "snapframe", "snappoints", "ortho", "bigortho", "rotate" }; + + for (const auto& check : checkButton) + { + if (const auto& pString = m_xBuilder->weld_check_button(check)) + sAllStrings += pString->get_label() + " "; + } + + return sAllStrings.replaceAll("_", ""); +} + +bool SvxGridTabPage::FillItemSet( SfxItemSet* rCoreSet ) +{ + if ( bAttrModified ) + { + SvxGridItem aGridItem( SID_ATTR_GRID_OPTIONS ); + + aGridItem.bUseGridsnap = m_xCbxUseGridsnap->get_active(); + aGridItem.bSynchronize = m_xCbxSynchronize->get_active(); + aGridItem.bGridVisible = m_xCbxGridVisible->get_active(); + + MapUnit eUnit = rCoreSet->GetPool()->GetMetric( SID_ATTR_GRID_OPTIONS ); + tools::Long nX = GetCoreValue( *m_xMtrFldDrawX, eUnit ); + tools::Long nY = GetCoreValue( *m_xMtrFldDrawY, eUnit ); + + aGridItem.nFldDrawX = static_cast<sal_uInt32>(nX); + aGridItem.nFldDrawY = static_cast<sal_uInt32>(nY); + aGridItem.nFldDivisionX = static_cast<tools::Long>(m_xNumFldDivisionX->get_value() - 1); + aGridItem.nFldDivisionY = static_cast<tools::Long>(m_xNumFldDivisionY->get_value() - 1); + + rCoreSet->Put( aGridItem ); + } + return bAttrModified; +} + +void SvxGridTabPage::Reset( const SfxItemSet* rSet ) +{ + const SvxGridItem* pGridAttr = nullptr; + + if( (pGridAttr = rSet->GetItemIfSet( SID_ATTR_GRID_OPTIONS , false )) ) + { + bool bReadOnly = false; + switch (m_Emode) + { + case WRITER_MODE: bReadOnly = officecfg::Office::Writer::Grid::Option::SnapToGrid::isReadOnly(); break; + case HTML_MODE: bReadOnly = officecfg::Office::WriterWeb::Grid::Option::SnapToGrid::isReadOnly(); break; + case IMPRESS_MODE: bReadOnly = officecfg::Office::Impress::Grid::Option::SnapToGrid::isReadOnly(); break; + case DRAW_MODE: bReadOnly = officecfg::Office::Draw::Grid::Option::SnapToGrid::isReadOnly(); break; + default: //TODO Calc + break; + } + m_xCbxUseGridsnap->set_active(pGridAttr->bUseGridsnap); + m_xCbxUseGridsnap->set_sensitive(!bReadOnly); + m_xCbxUseGridsnapImg->set_visible(bReadOnly); + + switch (m_Emode) + { + case WRITER_MODE: bReadOnly = officecfg::Office::Writer::Grid::Option::Synchronize::isReadOnly(); break; + case HTML_MODE: bReadOnly = officecfg::Office::WriterWeb::Grid::Option::Synchronize::isReadOnly(); break; + case IMPRESS_MODE: bReadOnly = officecfg::Office::Impress::Grid::Option::Synchronize::isReadOnly(); break; + case DRAW_MODE: bReadOnly = officecfg::Office::Draw::Grid::Option::Synchronize::isReadOnly(); break; + default: //TODO Calc + break; + } + m_xCbxSynchronize->set_active(pGridAttr->bSynchronize); + m_xCbxSynchronize->set_sensitive(!bReadOnly); + m_xCbxSynchronizeImg->set_visible(bReadOnly); + + switch (m_Emode) + { + case WRITER_MODE: bReadOnly = officecfg::Office::Writer::Grid::Option::VisibleGrid::isReadOnly(); break; + case HTML_MODE: bReadOnly = officecfg::Office::WriterWeb::Grid::Option::VisibleGrid::isReadOnly(); break; + case IMPRESS_MODE: bReadOnly = officecfg::Office::Impress::Grid::Option::VisibleGrid::isReadOnly(); break; + case DRAW_MODE: bReadOnly = officecfg::Office::Draw::Grid::Option::VisibleGrid::isReadOnly(); break; + default: //TODO Calc + break; + } + m_xCbxGridVisible->set_active(pGridAttr->bGridVisible); + m_xCbxGridVisible->set_sensitive(!bReadOnly); + m_xCbxGridVisibleImg->set_visible(bReadOnly); + + MapUnit eUnit = rSet->GetPool()->GetMetric( SID_ATTR_GRID_OPTIONS ); + SetMetricValue( *m_xMtrFldDrawX , pGridAttr->nFldDrawX, eUnit ); + SetMetricValue( *m_xMtrFldDrawY , pGridAttr->nFldDrawY, eUnit ); + + switch (m_Emode) + { + case WRITER_MODE: bReadOnly = officecfg::Office::Writer::Grid::Resolution::XAxis::isReadOnly(); break; + case HTML_MODE: bReadOnly = officecfg::Office::WriterWeb::Grid::Resolution::XAxis::isReadOnly(); break; + case IMPRESS_MODE: + { + if (lcl_IsMetricSystem()) + bReadOnly = officecfg::Office::Impress::Grid::Resolution::XAxis::Metric::isReadOnly(); + else + bReadOnly = officecfg::Office::Impress::Grid::Resolution::XAxis::NonMetric::isReadOnly(); + } + break; + case DRAW_MODE: + { + if (lcl_IsMetricSystem()) + bReadOnly = officecfg::Office::Draw::Grid::Resolution::XAxis::Metric::isReadOnly(); + else + bReadOnly = officecfg::Office::Draw::Grid::Resolution::XAxis::NonMetric::isReadOnly(); + } + break; + default: //TODO Calc + break; + } + m_xMtrFldDrawX->set_sensitive(!bReadOnly); + m_xMtrFldDrawXImg->set_visible(bReadOnly); + + switch (m_Emode) + { + case WRITER_MODE: bReadOnly = officecfg::Office::Writer::Grid::Resolution::YAxis::isReadOnly(); break; + case HTML_MODE: bReadOnly = officecfg::Office::WriterWeb::Grid::Resolution::YAxis::isReadOnly(); break; + case IMPRESS_MODE: + { + if (lcl_IsMetricSystem()) + bReadOnly = officecfg::Office::Impress::Grid::Resolution::YAxis::Metric::isReadOnly(); + else + bReadOnly = officecfg::Office::Impress::Grid::Resolution::YAxis::NonMetric::isReadOnly(); + } + break; + case DRAW_MODE: + { + if (lcl_IsMetricSystem()) + bReadOnly = officecfg::Office::Draw::Grid::Resolution::YAxis::Metric::isReadOnly(); + else + bReadOnly = officecfg::Office::Draw::Grid::Resolution::YAxis::NonMetric::isReadOnly(); + } + break; + default: //TODO Calc + break; + } + m_xMtrFldDrawY->set_sensitive(!bReadOnly); + m_xMtrFldDrawYImg->set_visible(bReadOnly); + + m_xNumFldDivisionX->set_value(pGridAttr->nFldDivisionX + 1); + m_xNumFldDivisionY->set_value(pGridAttr->nFldDivisionY + 1); + + switch (m_Emode) + { + case WRITER_MODE: bReadOnly = officecfg::Office::Writer::Grid::Subdivision::XAxis::isReadOnly(); break; + case HTML_MODE: bReadOnly = officecfg::Office::WriterWeb::Grid::Subdivision::XAxis::isReadOnly(); break; + case IMPRESS_MODE: bReadOnly = officecfg::Office::Impress::Grid::Subdivision::XAxis::isReadOnly(); break; + case DRAW_MODE: bReadOnly = officecfg::Office::Draw::Grid::Subdivision::XAxis::isReadOnly(); break; + default: //TODO Calc + break; + } + m_xNumFldDivisionX->set_sensitive(!bReadOnly); + m_xNumFldDivisionXImg->set_visible(bReadOnly); + + switch (m_Emode) + { + case WRITER_MODE: bReadOnly = officecfg::Office::Writer::Grid::Subdivision::YAxis::isReadOnly(); break; + case HTML_MODE: bReadOnly = officecfg::Office::WriterWeb::Grid::Subdivision::YAxis::isReadOnly(); break; + case IMPRESS_MODE: bReadOnly = officecfg::Office::Impress::Grid::Subdivision::YAxis::isReadOnly(); break; + case DRAW_MODE: bReadOnly = officecfg::Office::Draw::Grid::Subdivision::YAxis::isReadOnly(); break; + default: //TODO Calc + break; + } + m_xNumFldDivisionY->set_sensitive(!bReadOnly); + m_xNumFldDivisionYImg->set_visible(bReadOnly); + } + + ChangeGridsnapHdl_Impl(*m_xCbxUseGridsnap); + bAttrModified = false; +} + +void SvxGridTabPage::ActivatePage( const SfxItemSet& rSet ) +{ + const SvxGridItem* pGridAttr = nullptr; + if( (pGridAttr = rSet.GetItemIfSet( SID_ATTR_GRID_OPTIONS , false )) ) + { + m_xCbxUseGridsnap->set_active(pGridAttr->bUseGridsnap); + + ChangeGridsnapHdl_Impl(*m_xCbxUseGridsnap); + } + + // Metric Change if necessary (as TabPage is in the dialog, where the + // metric can be set + const SfxUInt16Item* pItem = rSet.GetItemIfSet( SID_ATTR_METRIC , false ); + if( !pItem ) + return; + + + FieldUnit eFUnit = static_cast<FieldUnit>(static_cast<tools::Long>(pItem->GetValue())); + + if (eFUnit == m_xMtrFldDrawX->get_unit()) + return; + + // Set Metrics + sal_Int64 nMin, nMax; + int nVal = m_xMtrFldDrawX->denormalize(m_xMtrFldDrawX->get_value(FieldUnit::TWIP)); + + lcl_GetMinMax(*m_xMtrFldDrawX, nMin, nMax); + SetFieldUnit(*m_xMtrFldDrawX, eFUnit, true); + lcl_SetMinMax(*m_xMtrFldDrawX, nMin, nMax); + + m_xMtrFldDrawX->set_value(m_xMtrFldDrawX->normalize(nVal), FieldUnit::TWIP); + + nVal = m_xMtrFldDrawY->denormalize(m_xMtrFldDrawY->get_value(FieldUnit::TWIP)); + lcl_GetMinMax(*m_xMtrFldDrawY, nMin, nMax); + SetFieldUnit(*m_xMtrFldDrawY, eFUnit, true); + lcl_SetMinMax(*m_xMtrFldDrawY, nMin, nMax); + m_xMtrFldDrawY->set_value(m_xMtrFldDrawY->normalize(nVal), FieldUnit::TWIP); +} + +DeactivateRC SvxGridTabPage::DeactivatePage( SfxItemSet* _pSet ) +{ + if ( _pSet ) + FillItemSet( _pSet ); + return DeactivateRC::LeavePage; +} + +IMPL_LINK(SvxGridTabPage, ChangeDrawHdl_Impl, weld::MetricSpinButton&, rField, void) +{ + bAttrModified = true; + if (m_xCbxSynchronize->get_active()) + { + if (&rField == m_xMtrFldDrawX.get()) + m_xMtrFldDrawY->set_value(m_xMtrFldDrawX->get_value(FieldUnit::NONE), FieldUnit::NONE); + else + m_xMtrFldDrawX->set_value(m_xMtrFldDrawY->get_value(FieldUnit::NONE), FieldUnit::NONE); + } +} + +IMPL_LINK_NOARG(SvxGridTabPage, ClickRotateHdl_Impl, weld::Toggleable&, void) +{ + if (m_xCbxRotate->get_active()) + { + m_xMtrFldAngle->set_sensitive(m_Emode == DRAW_MODE ? + !officecfg::Office::Draw::Snap::Position::RotatingValue::isReadOnly() : + !officecfg::Office::Impress::Snap::Position::RotatingValue::isReadOnly()); + } + else + m_xMtrFldAngle->set_sensitive(false); +} + +IMPL_LINK(SvxGridTabPage, ChangeDivisionHdl_Impl, weld::SpinButton&, rField, void) +{ + bAttrModified = true; + if (m_xCbxSynchronize->get_active()) + { + if (m_xNumFldDivisionX.get() == &rField) + m_xNumFldDivisionY->set_value(m_xNumFldDivisionX->get_value()); + else + m_xNumFldDivisionX->set_value(m_xNumFldDivisionY->get_value()); + } +} + +IMPL_LINK_NOARG(SvxGridTabPage, ChangeGridsnapHdl_Impl, weld::Toggleable&, void) +{ + bAttrModified = true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/page.hrc b/svx/source/dialog/page.hrc new file mode 100644 index 0000000000..9252e2e68b --- /dev/null +++ b/svx/source/dialog/page.hrc @@ -0,0 +1,104 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SVX_SOURCE_DIALOG_PAGE_HRC +#define INCLUDED_SVX_SOURCE_DIALOG_PAGE_HRC + +#include <i18nutil/paper.hxx> +#include <unotools/resmgr.hxx> +#include <utility> + +#define NC_(Context, String) TranslateId(Context, u8##String) + +const std::pair<TranslateId, int> RID_SVXSTRARY_PAPERSIZE_STD[] = +{ + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "A6") , PAPER_A6 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "A5") , PAPER_A5 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "A4") , PAPER_A4 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "A3") , PAPER_A3 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B6 (ISO)") , PAPER_B6_ISO }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B5 (ISO)") , PAPER_B5_ISO }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B4 (ISO)") , PAPER_B4_ISO }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Letter") , PAPER_LETTER }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Legal") , PAPER_LEGAL }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Long Bond") , PAPER_FANFOLD_LEGAL_DE }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Tabloid") , PAPER_TABLOID }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B6 (JIS)") , PAPER_B6_JIS }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B5 (JIS)") , PAPER_B5_JIS }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B4 (JIS)") , PAPER_B4_JIS }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "16 Kai") , PAPER_KAI16}, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "32 Kai") , PAPER_KAI32}, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Big 32 Kai") , PAPER_KAI32BIG}, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "User") , PAPER_USER }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "DL Envelope") , PAPER_ENV_DL }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "C6 Envelope") , PAPER_ENV_C6 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "C6/5 Envelope") , PAPER_ENV_C65 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "C5 Envelope") , PAPER_ENV_C5 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "C4 Envelope") , PAPER_ENV_C4 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#6¾ Envelope") , PAPER_ENV_PERSONAL}, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#7¾ (Monarch) Envelope") , PAPER_ENV_MONARCH}, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#9 Envelope") , PAPER_ENV_9}, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#10 Envelope") , PAPER_ENV_10}, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#11 Envelope") , PAPER_ENV_11}, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#12 Envelope") , PAPER_ENV_12}, + { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Japanese Postcard") , PAPER_POSTCARD_JP} +}; + +const std::pair<TranslateId, int> RID_SVXSTRARY_PAPERSIZE_DRAW[] = +{ + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A6") , PAPER_A6 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A5") , PAPER_A5 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A4") , PAPER_A4 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A3") , PAPER_A3 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A2") , PAPER_A2 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A1") , PAPER_A1 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A0") , PAPER_A0 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B6 (ISO)") , PAPER_B6_ISO }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B5 (ISO)") , PAPER_B5_ISO }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B4 (ISO)") , PAPER_B4_ISO }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Letter") , PAPER_LETTER }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Legal") , PAPER_LEGAL }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Long Bond") , PAPER_FANFOLD_LEGAL_DE }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Tabloid") , PAPER_TABLOID }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B6 (JIS)") , PAPER_B6_JIS }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B5 (JIS)") , PAPER_B5_JIS }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B4 (JIS)") , PAPER_B4_JIS }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "16 Kai") , PAPER_KAI16}, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "32 Kai") , PAPER_KAI32}, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Big 32 Kai") , PAPER_KAI32BIG}, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "User") , PAPER_USER }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "DL Envelope") , PAPER_ENV_DL }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "C6 Envelope") , PAPER_ENV_C6 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "C6/5 Envelope") , PAPER_ENV_C65 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "C5 Envelope") , PAPER_ENV_C5 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "C4 Envelope") , PAPER_ENV_C4 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Dia Slide") , PAPER_SLIDE_DIA }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Screen 4:3") , PAPER_SCREEN_4_3 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Screen 16:9") , PAPER_SCREEN_16_9 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Screen 16:10") , PAPER_SCREEN_16_10 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Widescreen") , PAPER_WIDESCREEN }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "On-screen Show (4:3)") , PAPER_ONSCREENSHOW_4_3 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "On-screen Show (16:9)") , PAPER_ONSCREENSHOW_16_9 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "On-screen Show (16:10)") , PAPER_ONSCREENSHOW_16_10 }, + { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Japanese Postcard") , PAPER_POSTCARD_JP} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/pagectrl.cxx b/svx/source/dialog/pagectrl.cxx new file mode 100644 index 0000000000..fe44562ff2 --- /dev/null +++ b/svx/source/dialog/pagectrl.cxx @@ -0,0 +1,397 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <editeng/frmdir.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/outdev.hxx> +#include <vcl/settings.hxx> +#include <tools/fract.hxx> +#include <svx/pageitem.hxx> +#include <svx/pagectrl.hxx> +#include <algorithm> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/geometry/viewinformation2d.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> + +#define CELL_WIDTH 1600L +#define CELL_HEIGHT 800L + +SvxPageWindow::SvxPageWindow() : + nTop(0), + nBottom(0), + nLeft(0), + nRight(0), + bResetBackground(false), + bFrameDirection(false), + nFrameDirection(SvxFrameDirection::Horizontal_LR_TB), + nHdLeft(0), + nHdRight(0), + nHdDist(0), + nHdHeight(0), + nFtLeft(0), + nFtRight(0), + nFtDist(0), + nFtHeight(0), + bFooter(false), + bHeader(false), + bTable(false), + bHorz(false), + bVert(false), + eUsage(SvxPageUsage::All) +{ +} + +SvxPageWindow::~SvxPageWindow() +{ +} + +void SvxPageWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.Push(vcl::PushFlags::MAPMODE); + rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip)); + + Fraction aXScale(aWinSize.Width(), std::max(aSize.Width() * 2 + aSize.Width() / 8, tools::Long(1))); + Fraction aYScale(aWinSize.Height(), std::max(aSize.Height(), tools::Long(1))); + MapMode aMapMode(rRenderContext.GetMapMode()); + + if(aYScale < aXScale) + { + aMapMode.SetScaleX(aYScale); + aMapMode.SetScaleY(aYScale); + } + else + { + aMapMode.SetScaleX(aXScale); + aMapMode.SetScaleY(aXScale); + } + rRenderContext.SetMapMode(aMapMode); + Size aSz(rRenderContext.PixelToLogic(GetOutputSizePixel())); + tools::Long nYPos = (aSz.Height() - aSize.Height()) / 2; + + if (eUsage == SvxPageUsage::All) + { + // all pages are equal -> draw one page + if (aSize.Width() > aSize.Height()) + { + // Draw Landscape page of the same size + Fraction aX = aMapMode.GetScaleX(); + Fraction aY = aMapMode.GetScaleY(); + Fraction a2(1.5); + aX *= a2; + aY *= a2; + aMapMode.SetScaleX(aX); + aMapMode.SetScaleY(aY); + rRenderContext.SetMapMode(aMapMode); + aSz = rRenderContext.PixelToLogic(GetOutputSizePixel()); + nYPos = (aSz.Height() - aSize.Height()) / 2; + tools::Long nXPos = (aSz.Width() - aSize.Width()) / 2; + DrawPage(rRenderContext, Point(nXPos,nYPos),true,true); + } + else + // Portrait + DrawPage(rRenderContext, Point((aSz.Width() - aSize.Width()) / 2,nYPos),true,true); + } + else + { + // Left and right page are different -> draw two pages if possible + DrawPage(rRenderContext, Point(0, nYPos), false, + eUsage == SvxPageUsage::Left || eUsage == SvxPageUsage::All || eUsage == SvxPageUsage::Mirror); + DrawPage(rRenderContext, Point(aSize.Width() + aSize.Width() / 8, nYPos), true, + eUsage == SvxPageUsage::Right || eUsage == SvxPageUsage::All || eUsage == SvxPageUsage::Mirror); + } + rRenderContext.Pop(); +} + +void SvxPageWindow::DrawPage(vcl::RenderContext& rRenderContext, const Point& rOrg, const bool bSecond, const bool bEnabled) +{ + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + const Color& rFieldColor = rStyleSettings.GetFieldColor(); + const Color& rFieldTextColor = rStyleSettings.GetFieldTextColor(); + const Color& rDisableColor = rStyleSettings.GetDisableColor(); + const Color& rDlgColor = rStyleSettings.GetDialogColor(); + + // background + if (!bSecond || bResetBackground) + { + rRenderContext.SetLineColor(COL_TRANSPARENT); + rRenderContext.SetFillColor(rDlgColor); + Size winSize(rRenderContext.GetOutputSize()); + rRenderContext.DrawRect(tools::Rectangle(Point(0,0), winSize)); + + if (bResetBackground) + bResetBackground = false; + } + rRenderContext.SetLineColor(rFieldTextColor); + + // Shadow + Size aTempSize = aSize; + + // Page + if (!bEnabled) + { + rRenderContext.SetFillColor(rDisableColor); + rRenderContext.DrawRect(tools::Rectangle(rOrg, aTempSize)); + return; + } + rRenderContext.SetFillColor(rFieldColor); + rRenderContext.DrawRect(tools::Rectangle(rOrg, aTempSize)); + + tools::Long nL = nLeft; + tools::Long nR = nRight; + + if (eUsage == SvxPageUsage::Mirror && !bSecond) + { + // turn for mirrored + nL = nRight; + nR = nLeft; + } + + tools::Rectangle aRect; + + aRect.SetLeft( rOrg.X() + nL ); + aRect.SetRight( rOrg.X() + aTempSize.Width() - nR ); + aRect.SetTop( rOrg.Y() + nTop ); + aRect.SetBottom( rOrg.Y() + aTempSize.Height() - nBottom ); + + tools::Rectangle aHdRect(aRect); + tools::Rectangle aFtRect(aRect); + + if (bHeader || bFooter) + { + // Header and/or footer used + const Color aLineColor(rRenderContext.GetLineColor()); + + // draw PageFill first and on the whole page, no outline + rRenderContext.SetLineColor(); + drawFillAttributes(rRenderContext, maPageFillAttributes, aRect, aRect); + rRenderContext.SetLineColor(aLineColor); + + if (bHeader) + { + // show headers if possible + aHdRect.AdjustLeft(nHdLeft ); + aHdRect.AdjustRight( -nHdRight ); + aHdRect.SetBottom( aRect.Top() + nHdHeight ); + aRect.AdjustTop(nHdHeight + nHdDist ); + + // draw header over PageFill, plus outline + drawFillAttributes(rRenderContext, maHeaderFillAttributes, aHdRect, aHdRect); + } + + if (bFooter) + { + // show footer if possible + aFtRect.AdjustLeft(nFtLeft ); + aFtRect.AdjustRight( -nFtRight ); + aFtRect.SetTop( aRect.Bottom() - nFtHeight ); + aRect.AdjustBottom( -(nFtHeight + nFtDist) ); + + // draw footer over PageFill, plus outline + drawFillAttributes(rRenderContext, maFooterFillAttributes, aFtRect, aFtRect); + } + + // draw page's reduced outline, only outline + drawFillAttributes(rRenderContext, drawinglayer::attribute::SdrAllFillAttributesHelperPtr(), aRect, aRect); + } + else + { + // draw PageFill and outline + drawFillAttributes(rRenderContext, maPageFillAttributes, aRect, aRect); + } + + if (bFrameDirection && !bTable) + { + Point aPos; + vcl::Font aFont(rRenderContext.GetFont()); + const Size aSaveSize = aFont.GetFontSize(); + Size aDrawSize(0,aRect.GetHeight() / 6); + aFont.SetFontSize(aDrawSize); + rRenderContext.SetFont(aFont); + OUString sText("ABC"); + Point aMove(1, rRenderContext.GetTextHeight()); + sal_Unicode cArrow = 0x2193; + tools::Long nAWidth = rRenderContext.GetTextWidth(sText.copy(0,1)); + switch (nFrameDirection) + { + case SvxFrameDirection::Horizontal_LR_TB: + aPos = aRect.TopLeft(); + aPos.AdjustX(rRenderContext.PixelToLogic(Point(1,1)).X() ); + aMove.setY( 0 ); + cArrow = 0x2192; + break; + case SvxFrameDirection::Horizontal_RL_TB: + aPos = aRect.TopRight(); + aPos.AdjustX( -nAWidth ); + aMove.setY( 0 ); + aMove.setX( aMove.X() * -1 ); + cArrow = 0x2190; + break; + case SvxFrameDirection::Vertical_LR_TB: + aPos = aRect.TopLeft(); + aPos.AdjustX(rRenderContext.PixelToLogic(Point(1,1)).X() ); + aMove.setX( 0 ); + break; + case SvxFrameDirection::Vertical_RL_TB: + aPos = aRect.TopRight(); + aPos.AdjustX( -nAWidth ); + aMove.setX( 0 ); + break; + default: break; + } + sText += OUStringChar(cArrow); + for (sal_Int32 i = 0; i < sText.getLength(); i++) + { + OUString sDraw(sText.copy(i,1)); + tools::Long nHDiff = 0; + tools::Long nCharWidth = rRenderContext.GetTextWidth(sDraw); + bool bHorizontal = 0 == aMove.Y(); + if (!bHorizontal) + { + nHDiff = (nAWidth - nCharWidth) / 2; + aPos.AdjustX(nHDiff ); + } + rRenderContext.DrawText(aPos,sDraw); + if (bHorizontal) + { + aPos.AdjustX(aMove.X() < 0 ? -nCharWidth : nCharWidth ); + } + else + { + aPos.AdjustX( -nHDiff ); + aPos.AdjustY(aMove.Y() ); + } + } + aFont.SetFontSize(aSaveSize); + rRenderContext.SetFont(aFont); + + } + if (!bTable) + return; + + // Paint Table, if necessary center it + rRenderContext.SetLineColor(COL_LIGHTGRAY); + + tools::Long nW = aRect.GetWidth(); + tools::Long nH = aRect.GetHeight(); + tools::Long const nTW = CELL_WIDTH * 3; + tools::Long const nTH = CELL_HEIGHT * 3; + tools::Long _nLeft = bHorz ? aRect.Left() + ((nW - nTW) / 2) : aRect.Left(); + tools::Long _nTop = bVert ? aRect.Top() + ((nH - nTH) / 2) : aRect.Top(); + tools::Rectangle aCellRect(Point(_nLeft, _nTop),Size(CELL_WIDTH, CELL_HEIGHT)); + + for (sal_uInt16 i = 0; i < 3; ++i) + { + aCellRect.SetLeft( _nLeft ); + aCellRect.SetRight( _nLeft + CELL_WIDTH ); + if(i > 0) + aCellRect.Move(0,CELL_HEIGHT); + + for (sal_uInt16 j = 0; j < 3; ++j) + { + if (j > 0) + aCellRect.Move(CELL_WIDTH,0); + rRenderContext.DrawRect(aCellRect); + } + } +} + +void SvxPageWindow::drawFillAttributes(vcl::RenderContext& rRenderContext, + const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes, + const tools::Rectangle& rPaintRange, + const tools::Rectangle& rDefineRange) +{ + const basegfx::B2DRange aPaintRange = vcl::unotools::b2DRectangleFromRectangle(rPaintRange); + + if(aPaintRange.isEmpty() || + basegfx::fTools::equalZero(aPaintRange.getWidth()) || + basegfx::fTools::equalZero(aPaintRange.getHeight())) + return; + + const basegfx::B2DRange aDefineRange = vcl::unotools::b2DRectangleFromRectangle(rDefineRange); + + // prepare primitive sequence + drawinglayer::primitive2d::Primitive2DContainer aSequence; + + // create fill geometry if there is something to fill + if (rFillAttributes && rFillAttributes->isUsed()) + { + aSequence = rFillAttributes->getPrimitive2DSequence(aPaintRange, aDefineRange); + } + + // create line geometry if a LineColor is set at the target device + if (rRenderContext.IsLineColor()) + { + const drawinglayer::primitive2d::Primitive2DReference xOutline( + new drawinglayer::primitive2d::PolygonHairlinePrimitive2D( + basegfx::utils::createPolygonFromRect(aPaintRange), rRenderContext.GetLineColor().getBColor())); + + aSequence.push_back(xOutline); + } + + // draw that if we have something to draw + if (aSequence.empty()) + return; + + drawinglayer::geometry::ViewInformation2D aViewInformation2D; + aViewInformation2D.setViewTransformation(rRenderContext.GetViewTransformation()); + aViewInformation2D.setViewport(aPaintRange); + + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor( + drawinglayer::processor2d::createProcessor2DFromOutputDevice(rRenderContext, aViewInformation2D)); + pProcessor->process(aSequence); +} + +void SvxPageWindow::EnableFrameDirection(bool bEnable) +{ + bFrameDirection = bEnable; +} + +void SvxPageWindow::SetFrameDirection(SvxFrameDirection nDirection) +{ + nFrameDirection = nDirection; +} + +void SvxPageWindow::ResetBackground() +{ + bResetBackground = true; +} + +void SvxPageWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + + OutputDevice& rRefDevice = pDrawingArea->get_ref_device(); + // Count in Twips by default + rRefDevice.Push(vcl::PushFlags::MAPMODE); + rRefDevice.SetMapMode(MapMode(MapUnit::MapTwip)); + aWinSize = rRefDevice.LogicToPixel(Size(75, 46), MapMode(MapUnit::MapAppFont)); + pDrawingArea->set_size_request(aWinSize.Width(), aWinSize.Height()); + + aWinSize.AdjustHeight( -4 ); + aWinSize.AdjustWidth( -4 ); + + aWinSize = rRefDevice.PixelToLogic(aWinSize); + rRefDevice.Pop(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/pagenumberlistbox.cxx b/svx/source/dialog/pagenumberlistbox.cxx new file mode 100644 index 0000000000..076da12e12 --- /dev/null +++ b/svx/source/dialog/pagenumberlistbox.cxx @@ -0,0 +1,52 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dialmgr.hxx> +#include <svx/pagenumberlistbox.hxx> +#include <editeng/numitem.hxx> +#include <com/sun/star/style/NumberingType.hpp> +#include <numberingtype.hrc> + +SvxPageNumberListBox::SvxPageNumberListBox(std::unique_ptr<weld::ComboBox> pControl) + : m_xControl(std::move(pControl)) +{ + m_xControl->set_size_request(150, -1); + + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_SVXSTRARY_NUMBERINGTYPE); ++i) + { + sal_uInt16 nData = RID_SVXSTRARY_NUMBERINGTYPE[i].second; + switch (nData) + { + // String list array is also used in Writer and contains strings + // for Bullet and Graphics, ignore those here. + case css::style::NumberingType::CHAR_SPECIAL: + case css::style::NumberingType::BITMAP: + case css::style::NumberingType::BITMAP | LINK_TOKEN: + break; + default: + { + OUString aStr = SvxResId(RID_SVXSTRARY_NUMBERINGTYPE[i].first); + m_xControl->append(OUString::number(nData), aStr); + break; + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/papersizelistbox.cxx b/svx/source/dialog/papersizelistbox.cxx new file mode 100644 index 0000000000..fc0211a8ba --- /dev/null +++ b/svx/source/dialog/papersizelistbox.cxx @@ -0,0 +1,74 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dialmgr.hxx> +#include <svx/papersizelistbox.hxx> +#include "page.hrc" + +SvxPaperSizeListBox::SvxPaperSizeListBox(std::unique_ptr<weld::ComboBox> pControl) + : m_xControl(std::move(pControl)) +{ + m_xControl->set_size_request(150, -1); +} + +void SvxPaperSizeListBox::FillPaperSizeEntries( PaperSizeApp eApp ) +{ + const std::pair<TranslateId, int>* pPaperAry = eApp == PaperSizeApp::Std ? + RID_SVXSTRARY_PAPERSIZE_STD : RID_SVXSTRARY_PAPERSIZE_DRAW; + sal_uInt32 nCnt = eApp == PaperSizeApp::Std ? + SAL_N_ELEMENTS(RID_SVXSTRARY_PAPERSIZE_STD) : SAL_N_ELEMENTS(RID_SVXSTRARY_PAPERSIZE_DRAW); + + for ( sal_uInt32 i = 0; i < nCnt; ++i ) + { + OUString aStr = SvxResId(pPaperAry[i].first); + Paper eSize = static_cast<Paper>(pPaperAry[i].second); + m_xControl->append(OUString::number(static_cast<sal_Int32>(eSize)), aStr); + } +} + +void SvxPaperSizeListBox::set_active_id( Paper ePreselectPaper ) +{ + int nEntryCount = m_xControl->get_count(); + int nSelPos = -1; + int nUserPos = -1; + + for (int i = 0; i < nEntryCount; ++i) + { + Paper eTmp = static_cast<Paper>(m_xControl->get_id(i).toInt32()); + if (eTmp == ePreselectPaper) + { + nSelPos = i; + break; + } + + if (eTmp == PAPER_USER) + nUserPos = i; + } + + // preselect current paper format - #115915#: ePaper might not be in aPaperSizeBox so use PAPER_USER instead + m_xControl->set_active((nSelPos != -1) ? nSelPos : nUserPos); +} + +Paper SvxPaperSizeListBox::get_active_id() const +{ + return static_cast<Paper>(m_xControl->get_active_id().toInt32()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/svx/source/dialog/paraprev.cxx b/svx/source/dialog/paraprev.cxx new file mode 100644 index 0000000000..27dccbfd9f --- /dev/null +++ b/svx/source/dialog/paraprev.cxx @@ -0,0 +1,214 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sfx2/dialoghelper.hxx> +#include <svx/paraprev.hxx> +#include <vcl/outdev.hxx> +#include <vcl/settings.hxx> + +SvxParaPrevWindow::SvxParaPrevWindow() : + nLeftMargin ( 0 ), + nRightMargin ( 0 ), + nFirstLineOffset ( 0 ), + nUpper ( 0 ), + nLower ( 0 ), + eAdjust ( SvxAdjust::Left ), + eLastLine ( SvxAdjust::Left ), + eLine ( SvxPrevLineSpace::N1 ) +{ + aSize = Size(11905, 16837); +} + +void SvxParaPrevWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aOptimalSize(getParagraphPreviewOptimalSize(pDrawingArea->get_ref_device())); + pDrawingArea->set_size_request(aOptimalSize.Width(), aOptimalSize.Height()); +} + +void SvxParaPrevWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + DrawParagraph(rRenderContext); +} + +#define DEF_MARGIN 120 + +void SvxParaPrevWindow::DrawParagraph(vcl::RenderContext& rRenderContext) +{ + // Count in Twips by default + rRenderContext.Push(vcl::PushFlags::MAPMODE); + rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip)); + + Size aWinSize(GetOutputSizePixel()); + aWinSize = rRenderContext.PixelToLogic(aWinSize); + Size aTmp(1, 1); + aTmp = rRenderContext.PixelToLogic(aTmp); + aWinSize.AdjustWidth( -(aTmp.Width() /2) ); + aWinSize.AdjustHeight( -(aTmp.Height() /2) ); + + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + const Color& rWinColor = rStyleSettings.GetWindowColor(); + Color aGrayColor(COL_LIGHTGRAY); + + rRenderContext.SetFillColor(rWinColor); + rRenderContext.DrawRect(tools::Rectangle(Point(), aWinSize)); + + rRenderContext.SetLineColor(); + + tools::Long nH = aWinSize.Height() / 19; + Size aLineSiz(aWinSize.Width() - DEF_MARGIN, nH); + Size aSiz = aLineSiz; + Point aPnt; + aPnt.setX( DEF_MARGIN / 2 ); + rRenderContext.SetFillColor(aGrayColor); + + for (sal_uInt16 i = 0; i < 9; ++i) + { + if (i == 3) + { + rRenderContext.SetFillColor(COL_GRAY); + auto nTop = nUpper * aLineSiz.Height() / aSize.Height(); + aPnt.AdjustY(nTop * 2 ); + } + + if (i == 6 ) + rRenderContext.SetFillColor(aGrayColor); + + if (3 <= i && 6 > i) + { + tools::Long nLeft = nLeftMargin * aLineSiz.Width() / aSize.Width(); + tools::Long nFirst = nFirstLineOffset * aLineSiz.Width() / aSize.Width(); + tools::Long nTmp = nLeft + nFirst; + + if (i == 3) + { + aPnt.AdjustX(nTmp ); + aSiz.AdjustWidth( -nTmp ); + } + else + { + aPnt.AdjustX(nLeft ); + aSiz.AdjustWidth( -nLeft ); + } + tools::Long nRight = nRightMargin * aLineSiz.Width() / aSize.Width(); + aSiz.AdjustWidth( -nRight ); + } + + if (4 == i || 5 == i || 6 == i) + { + switch (eLine) + { + case SvxPrevLineSpace::N1: + break; + case SvxPrevLineSpace::N115: + aPnt.AdjustY(nH / 6.67 ); // 1/.15 = 6.(6) + break; + case SvxPrevLineSpace::N15: + aPnt.AdjustY(nH / 2 ); + break; + case SvxPrevLineSpace::N2: + aPnt.AdjustY(nH ); + break; + case SvxPrevLineSpace::Prop: + case SvxPrevLineSpace::Min: + case SvxPrevLineSpace::Leading: + break; + } + } + + aPnt.AdjustY(nH ); + + if (3 <= i && 5 >= i) + { + tools::Long nLW = tools::Long(); + switch (i) + { + case 3: + nLW = aLineSiz.Width() * 8 / 10; + break; + case 4: + nLW = aLineSiz.Width() * 9 / 10; + break; + case 5: + nLW = aLineSiz.Width() / 2; + break; + } + + if (nLW > aSiz.Width()) + nLW = aSiz.Width(); + + switch (eAdjust) + { + case SvxAdjust::Left: + break; + case SvxAdjust::Right: + aPnt.AdjustX( aSiz.Width() - nLW ); + break; + case SvxAdjust::Center: + aPnt.AdjustX(( aSiz.Width() - nLW ) / 2 ); + break; + default: ; //prevent warning + } + if (SvxAdjust::Block == eAdjust) + { + if(5 == i) + { + switch( eLastLine ) + { + case SvxAdjust::Left: + break; + case SvxAdjust::Right: + aPnt.AdjustX( aSiz.Width() - nLW ); + break; + case SvxAdjust::Center: + aPnt.AdjustX(( aSiz.Width() - nLW ) / 2 ); + break; + case SvxAdjust::Block: + nLW = aSiz.Width(); + break; + default: ; //prevent warning + } + } + else + nLW = aSiz.Width(); + } + aSiz.setWidth( nLW ); + } + + tools::Rectangle aRect(aPnt, aSiz); + + rRenderContext.DrawRect( aRect ); + + if (5 == i) + { + auto nBottom = nLower * aLineSiz.Height() / aSize.Height(); + aPnt.AdjustY(nBottom * 2 ); + } + + aPnt.AdjustY(nH ); + // Reset, recalculate for each line + aPnt.setX( DEF_MARGIN / 2 ); + aSiz = aLineSiz; + } + rRenderContext.Pop(); +} + +#undef DEF_MARGIN + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/passwd.cxx b/svx/source/dialog/passwd.cxx new file mode 100644 index 0000000000..cb7793c3de --- /dev/null +++ b/svx/source/dialog/passwd.cxx @@ -0,0 +1,91 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svx/passwd.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> + +IMPL_LINK_NOARG(SvxPasswordDialog, ButtonHdl, weld::Button&, void) +{ + bool bOK = true; + + if (m_xNewPasswdED->get_text() != m_xRepeatPasswdED->get_text()) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + m_aRepeatPasswdErrStr)); + xBox->run(); + m_xNewPasswdED->set_text(""); + m_xRepeatPasswdED->set_text(""); + m_xNewPasswdED->grab_focus(); + bOK = false; + } + + if (bOK && m_aCheckPasswordHdl.IsSet() && !m_aCheckPasswordHdl.Call(this)) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + m_aOldPasswdErrStr)); + xBox->run(); + m_xOldPasswdED->set_text(""); + m_xOldPasswdED->grab_focus(); + bOK = false; + } + + if (bOK) + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(SvxPasswordDialog, EditModifyHdl, weld::Entry&, void) +{ + if (!m_xOKBtn->get_sensitive()) + m_xOKBtn->set_sensitive(true); +} + +SvxPasswordDialog::SvxPasswordDialog(weld::Window* pParent, bool bDisableOldPassword) + : SfxDialogController(pParent, "svx/ui/passwd.ui", "PasswordDialog") + , m_aOldPasswdErrStr(SvxResId(RID_SVXSTR_ERR_OLD_PASSWD)) + , m_aRepeatPasswdErrStr(SvxResId(RID_SVXSTR_ERR_REPEAT_PASSWD )) + , m_xOldFL(m_xBuilder->weld_label("oldpass")) + , m_xOldPasswdFT(m_xBuilder->weld_label("oldpassL")) + , m_xOldPasswdED(m_xBuilder->weld_entry("oldpassEntry")) + , m_xNewPasswdED(m_xBuilder->weld_entry("newpassEntry")) + , m_xRepeatPasswdED(m_xBuilder->weld_entry("confirmpassEntry")) + , m_xOKBtn(m_xBuilder->weld_button("ok")) +{ + m_xOKBtn->connect_clicked(LINK(this, SvxPasswordDialog, ButtonHdl)); + m_xRepeatPasswdED->connect_changed(LINK(this, SvxPasswordDialog, EditModifyHdl)); + EditModifyHdl(*m_xRepeatPasswdED); + + if (bDisableOldPassword) + { + m_xOldFL->set_sensitive(false); + m_xOldPasswdFT->set_sensitive(false); + m_xOldPasswdED->set_sensitive(false); + m_xNewPasswdED->grab_focus(); + } +} + +SvxPasswordDialog::~SvxPasswordDialog() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/relfld.cxx b/svx/source/dialog/relfld.cxx new file mode 100644 index 0000000000..3929e4fcf7 --- /dev/null +++ b/svx/source/dialog/relfld.cxx @@ -0,0 +1,103 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/relfld.hxx> + +SvxRelativeField::SvxRelativeField(std::unique_ptr<weld::MetricSpinButton> pControl) + : m_xSpinButton(std::move(pControl)) + , nRelMin(0) + , nRelMax(0) + , bRelativeMode(false) + , bRelative(false) + , bNegativeEnabled(false) + +{ + weld::SpinButton& rSpinButton = m_xSpinButton->get_widget(); + rSpinButton.connect_changed(LINK(this, SvxRelativeField, ModifyHdl)); +} + +IMPL_LINK_NOARG(SvxRelativeField, ModifyHdl, weld::Entry&, void) +{ + if (!bRelativeMode) + return; + + OUString aStr = m_xSpinButton->get_text(); + bool bNewMode = bRelative; + + if ( bRelative ) + { + const sal_Unicode* pStr = aStr.getStr(); + + while ( *pStr ) + { + if( ( ( *pStr < '0' ) || ( *pStr > '9' ) ) && + ( *pStr != '%' ) ) + { + bNewMode = false; + break; + } + pStr++; + } + } + else + { + if ( aStr.indexOf( "%" ) != -1 ) + bNewMode = true; + } + + if ( bNewMode != bRelative ) + SetRelative( bNewMode ); +} + +void SvxRelativeField::EnableRelativeMode(sal_uInt16 nMin, sal_uInt16 nMax) +{ + bRelativeMode = true; + nRelMin = nMin; + nRelMax = nMax; + m_xSpinButton->set_unit(FieldUnit::CM); +} + +void SvxRelativeField::SetRelative( bool bNewRelative ) +{ + weld::SpinButton& rSpinButton = m_xSpinButton->get_widget(); + + int nStartPos, nEndPos; + rSpinButton.get_selection_bounds(nStartPos, nEndPos); + OUString aStr = rSpinButton.get_text(); + + if ( bNewRelative ) + { + bRelative = true; + m_xSpinButton->set_digits(0); + m_xSpinButton->set_range(nRelMin, nRelMax, FieldUnit::NONE); + m_xSpinButton->set_unit(FieldUnit::PERCENT); + } + else + { + bRelative = false; + m_xSpinButton->set_digits(2); + m_xSpinButton->set_range(bNegativeEnabled ? -9999 : 0, 9999, FieldUnit::NONE); + m_xSpinButton->set_unit(FieldUnit::CM); + } + + rSpinButton.set_text(aStr); + rSpinButton.select_region(nStartPos, nEndPos); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/rlrcitem.cxx b/svx/source/dialog/rlrcitem.cxx new file mode 100644 index 0000000000..a73cd00bda --- /dev/null +++ b/svx/source/dialog/rlrcitem.cxx @@ -0,0 +1,148 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svl/rectitem.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> + +#include <svx/svxids.hrc> + +#include <svx/ruler.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/tstpitem.hxx> +#include <editeng/protitem.hxx> +#include "rlrcitem.hxx" +#include <svx/rulritem.hxx> +#include <svl/eitem.hxx> + +SvxRulerItem::SvxRulerItem(sal_uInt16 _nId, SvxRuler &rRul, SfxBindings &rBindings) +: SfxControllerItem(_nId, rBindings), + rRuler(rRul) +{ +} + + +void SvxRulerItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState) +{ + // SfxItemState::DONTCARE => pState == -1 => PTR_CAST buff + if ( eState != SfxItemState::DEFAULT ) + pState = nullptr; + + switch(nSID) + { + // Left / right margin + case SID_RULER_LR_MIN_MAX: + { + const SfxRectangleItem *pItem = dynamic_cast<const SfxRectangleItem*>( pState ); + rRuler.UpdateFrameMinMax(pItem); + break; + } + case SID_ATTR_LONG_LRSPACE: + { + const SvxLongLRSpaceItem *pItem = dynamic_cast<const SvxLongLRSpaceItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxLRSpaceItem expected"); + rRuler.UpdateFrame(pItem); + break; + } + case SID_ATTR_LONG_ULSPACE: + { + const SvxLongULSpaceItem *pItem = dynamic_cast<const SvxLongULSpaceItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxULSpaceItem expected"); + rRuler.UpdateFrame(pItem); + break; + } + case SID_ATTR_TABSTOP_VERTICAL: + case SID_ATTR_TABSTOP: + { + const SvxTabStopItem *pItem = dynamic_cast<const SvxTabStopItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxTabStopItem expected"); + rRuler.Update(pItem); + break; + } + case SID_ATTR_PARA_LRSPACE_VERTICAL: + case SID_ATTR_PARA_LRSPACE: + { + const SvxLRSpaceItem *pItem = dynamic_cast<const SvxLRSpaceItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxLRSpaceItem expected"); + rRuler.UpdatePara(pItem); + break; + } + case SID_RULER_BORDERS_VERTICAL: + case SID_RULER_BORDERS: + case SID_RULER_ROWS: + case SID_RULER_ROWS_VERTICAL: + { + const SvxColumnItem *pItem = dynamic_cast<const SvxColumnItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxColumnItem expected"); +#ifdef DBG_UTIL + if(pItem) + { + if(pItem->IsConsistent()) + rRuler.Update(pItem, nSID); + else + OSL_FAIL("Column item corrupted"); + } + else + rRuler.Update(pItem, nSID); +#else + rRuler.Update(pItem, nSID); +#endif + break; + } + case SID_RULER_PAGE_POS: + { // Position page, page width + const SvxPagePosSizeItem *pItem = dynamic_cast<const SvxPagePosSizeItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxPagePosSizeItem expected"); + rRuler.Update(pItem); + break; + } + case SID_RULER_OBJECT: + { // Object selection + const SvxObjectItem *pItem = dynamic_cast<const SvxObjectItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxObjectItem expected"); + rRuler.Update(pItem); + break; + } + case SID_RULER_PROTECT: + { + const SvxProtectItem *pItem = dynamic_cast<const SvxProtectItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxProtectItem expected"); + rRuler.Update(pItem); + break; + } + case SID_RULER_BORDER_DISTANCE: + { + const SvxLRSpaceItem *pItem = dynamic_cast<const SvxLRSpaceItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxLRSpaceItem expected"); + rRuler.UpdateBorder(pItem); + } + break; + case SID_RULER_TEXT_RIGHT_TO_LEFT : + { + const SfxBoolItem *pItem = dynamic_cast<const SfxBoolItem*>( pState ); + SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SfxBoolItem expected"); + rRuler.UpdateTextRTL(pItem); + } + break; + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/rlrcitem.hxx b/svx/source/dialog/rlrcitem.hxx new file mode 100644 index 0000000000..0fde86f0ca --- /dev/null +++ b/svx/source/dialog/rlrcitem.hxx @@ -0,0 +1,42 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_SVX_SOURCE_DIALOG_RLRCITEM_HXX +#define INCLUDED_SVX_SOURCE_DIALOG_RLRCITEM_HXX + +#include <sfx2/ctrlitem.hxx> + +class SvxRuler; + +class SvxRulerItem : public SfxControllerItem +{ +private: + SvxRuler& rRuler; + +protected: + virtual void StateChangedAtToolBoxControl( sal_uInt16, + SfxItemState, const SfxPoolItem* pState ) override; + +public: + SvxRulerItem( sal_uInt16 nId, SvxRuler&, SfxBindings& ); +}; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/rubydialog.cxx b/svx/source/dialog/rubydialog.cxx new file mode 100644 index 0000000000..83cfe3052c --- /dev/null +++ b/svx/source/dialog/rubydialog.cxx @@ -0,0 +1,873 @@ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <sal/config.h> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <svx/rubydialog.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewsh.hxx> +#include <svl/eitem.hxx> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/text/XRubySelection.hpp> +#include <com/sun/star/beans/PropertyValues.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/text/RubyAdjust.hpp> +#include <com/sun/star/view/XSelectionChangeListener.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <cppuhelper/implbase.hxx> +#include <svtools/colorcfg.hxx> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <svl/itemset.hxx> + +using namespace css::uno; +using namespace css::frame; +using namespace css::text; +using namespace css::beans; +using namespace css::style; +using namespace css::view; +using namespace css::lang; +using namespace css::container; + +SFX_IMPL_CHILDWINDOW(SvxRubyChildWindow, SID_RUBY_DIALOG); + +namespace +{ +constexpr OUString cRubyBaseText = u"RubyBaseText"_ustr; +constexpr OUString cRubyText = u"RubyText"_ustr; +constexpr OUString cRubyAdjust = u"RubyAdjust"_ustr; +constexpr OUString cRubyPosition = u"RubyPosition"_ustr; +constexpr OUString cRubyCharStyleName = u"RubyCharStyleName"_ustr; + +} // end anonymous namespace + +SvxRubyChildWindow::SvxRubyChildWindow(vcl::Window* _pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo const* pInfo) + : SfxChildWindow(_pParent, nId) +{ + auto xDlg = std::make_shared<SvxRubyDialog>(pBindings, this, _pParent->GetFrameWeld()); + SetController(xDlg); + xDlg->Initialize(pInfo); +} + +SfxChildWinInfo SvxRubyChildWindow::GetInfo() const { return SfxChildWindow::GetInfo(); } + +class SvxRubyData_Impl : public cppu::WeakImplHelper<css::view::XSelectionChangeListener> +{ + Reference<XModel> xModel; + Reference<XRubySelection> xSelection; + Sequence<PropertyValues> aRubyValues; + Reference<XController> xController; + bool bHasSelectionChanged; + bool bDisposing; + +public: + SvxRubyData_Impl(); + virtual ~SvxRubyData_Impl() override; + + void SetController(const Reference<XController>& xCtrl); + Reference<XModel> const& GetModel() + { + if (!xController.is()) + xModel = nullptr; + else + xModel = xController->getModel(); + return xModel; + } + bool HasSelectionChanged() const { return bHasSelectionChanged; } + bool IsDisposing() const { return bDisposing; } + Reference<XRubySelection> const& GetRubySelection() + { + xSelection.set(xController, UNO_QUERY); + return xSelection; + } + void UpdateRubyValues() + { + if (!xSelection.is()) + aRubyValues.realloc(0); + else + aRubyValues = xSelection->getRubyList(false); + bHasSelectionChanged = false; + } + Sequence<PropertyValues>& GetRubyValues() { return aRubyValues; } + void AssertOneEntry(); + + virtual void SAL_CALL selectionChanged(const css::lang::EventObject& aEvent) override; + virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override; +}; + +SvxRubyData_Impl::SvxRubyData_Impl() + : bHasSelectionChanged(false) + , bDisposing(false) +{ +} + +SvxRubyData_Impl::~SvxRubyData_Impl() {} + +void SvxRubyData_Impl::SetController(const Reference<XController>& xCtrl) +{ + if (xCtrl.get() == xController.get()) + return; + + try + { + Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY); + if (xSelSupp.is()) + xSelSupp->removeSelectionChangeListener(this); + + bHasSelectionChanged = true; + xController = xCtrl; + xSelSupp.set(xController, UNO_QUERY); + if (xSelSupp.is()) + xSelSupp->addSelectionChangeListener(this); + } + catch (const Exception&) + { + } +} + +void SvxRubyData_Impl::selectionChanged(const EventObject&) { bHasSelectionChanged = true; } + +void SvxRubyData_Impl::disposing(const EventObject&) +{ + try + { + Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY); + if (xSelSupp.is()) + xSelSupp->removeSelectionChangeListener(this); + } + catch (const Exception&) + { + } + xController = nullptr; + bDisposing = true; +} + +void SvxRubyData_Impl::AssertOneEntry() +{ + //create one entry + if (!aRubyValues.hasElements()) + { + aRubyValues.realloc(1); + Sequence<PropertyValue>& rValues = aRubyValues.getArray()[0]; + rValues.realloc(5); + PropertyValue* pValues = rValues.getArray(); + pValues[0].Name = cRubyBaseText; + pValues[1].Name = cRubyText; + pValues[2].Name = cRubyAdjust; + pValues[3].Name = cRubyPosition; + pValues[4].Name = cRubyCharStyleName; + } +} + +SvxRubyDialog::SvxRubyDialog(SfxBindings* pBind, SfxChildWindow* pCW, weld::Window* pParent) + : SfxModelessDialogController(pBind, pCW, pParent, "svx/ui/asianphoneticguidedialog.ui", + "AsianPhoneticGuideDialog") + , nLastPos(0) + , nCurrentEdit(0) + , bModified(false) + , pBindings(pBind) + , m_pImpl(new SvxRubyData_Impl) + , m_xLeft1ED(m_xBuilder->weld_entry("Left1ED")) + , m_xRight1ED(m_xBuilder->weld_entry("Right1ED")) + , m_xLeft2ED(m_xBuilder->weld_entry("Left2ED")) + , m_xRight2ED(m_xBuilder->weld_entry("Right2ED")) + , m_xLeft3ED(m_xBuilder->weld_entry("Left3ED")) + , m_xRight3ED(m_xBuilder->weld_entry("Right3ED")) + , m_xLeft4ED(m_xBuilder->weld_entry("Left4ED")) + , m_xRight4ED(m_xBuilder->weld_entry("Right4ED")) + , m_xScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow", true)) + , m_xAdjustLB(m_xBuilder->weld_combo_box("adjustlb")) + , m_xPositionLB(m_xBuilder->weld_combo_box("positionlb")) + , m_xCharStyleFT(m_xBuilder->weld_label("styleft")) + , m_xCharStyleLB(m_xBuilder->weld_combo_box("stylelb")) + , m_xStylistPB(m_xBuilder->weld_button("styles")) + , m_xApplyPB(m_xBuilder->weld_button("ok")) + , m_xClosePB(m_xBuilder->weld_button("close")) + , m_xContentArea(m_xDialog->weld_content_area()) + , m_xGrid(m_xBuilder->weld_widget("grid")) + , m_xPreviewWin(new RubyPreview) + , m_xPreview(new weld::CustomWeld(*m_xBuilder, "preview", *m_xPreviewWin)) +{ + m_xCharStyleLB->make_sorted(); + m_xPreviewWin->setRubyDialog(this); + m_xScrolledWindow->set_size_request(-1, m_xGrid->get_preferred_size().Height()); + m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER); + + aEditArr[0] = m_xLeft1ED.get(); + aEditArr[1] = m_xRight1ED.get(); + aEditArr[2] = m_xLeft2ED.get(); + aEditArr[3] = m_xRight2ED.get(); + aEditArr[4] = m_xLeft3ED.get(); + aEditArr[5] = m_xRight3ED.get(); + aEditArr[6] = m_xLeft4ED.get(); + aEditArr[7] = m_xRight4ED.get(); + + m_xApplyPB->connect_clicked(LINK(this, SvxRubyDialog, ApplyHdl_Impl)); + m_xClosePB->connect_clicked(LINK(this, SvxRubyDialog, CloseHdl_Impl)); + m_xStylistPB->connect_clicked(LINK(this, SvxRubyDialog, StylistHdl_Impl)); + m_xAdjustLB->connect_changed(LINK(this, SvxRubyDialog, AdjustHdl_Impl)); + m_xPositionLB->connect_changed(LINK(this, SvxRubyDialog, PositionHdl_Impl)); + m_xCharStyleLB->connect_changed(LINK(this, SvxRubyDialog, CharStyleHdl_Impl)); + + Link<weld::ScrolledWindow&, void> aScrLk(LINK(this, SvxRubyDialog, ScrollHdl_Impl)); + m_xScrolledWindow->connect_vadjustment_changed(aScrLk); + + Link<weld::Entry&, void> aEditLk(LINK(this, SvxRubyDialog, EditModifyHdl_Impl)); + Link<weld::Widget&, void> aFocusLk(LINK(this, SvxRubyDialog, EditFocusHdl_Impl)); + Link<const KeyEvent&, bool> aKeyUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownHdl_Impl)); + Link<const KeyEvent&, bool> aKeyTabUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownTabHdl_Impl)); + for (sal_uInt16 i = 0; i < 8; i++) + { + aEditArr[i]->connect_changed(aEditLk); + aEditArr[i]->connect_focus_in(aFocusLk); + if (!i || 7 == i) + aEditArr[i]->connect_key_press(aKeyTabUpDownLk); + else + aEditArr[i]->connect_key_press(aKeyUpDownLk); + } +} + +SvxRubyDialog::~SvxRubyDialog() +{ + ClearCharStyleList(); + EventObject aEvent; + m_pImpl->disposing(aEvent); +} + +void SvxRubyDialog::ClearCharStyleList() { m_xCharStyleLB->clear(); } + +void SvxRubyDialog::Close() +{ + if (IsClosing()) + return; + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + if (pViewFrame) + pViewFrame->ToggleChildWindow(SID_RUBY_DIALOG); +} + +void SvxRubyDialog::Activate() +{ + SfxModelessDialogController::Activate(); + if (m_pImpl->IsDisposing()) + { + // tdf#141967/tdf#152495 if Activate is called during tear down bail early + return; + } + + //get selection from current view frame + SfxViewFrame* pCurFrm = SfxViewFrame::Current(); + Reference<XController> xCtrl(pCurFrm ? pCurFrm->GetFrame().GetController() : nullptr); + m_pImpl->SetController(xCtrl); + if (!m_pImpl->HasSelectionChanged()) + return; + + Reference<XRubySelection> xRubySel = m_pImpl->GetRubySelection(); + m_pImpl->UpdateRubyValues(); + EnableControls(xRubySel.is()); + if (xRubySel.is()) + { + Reference<XModel> xModel = m_pImpl->GetModel(); + const OUString sCharStyleSelect = m_xCharStyleLB->get_active_text(); + ClearCharStyleList(); + Reference<XStyleFamiliesSupplier> xSupplier(xModel, UNO_QUERY); + if (xSupplier.is()) + { + try + { + Reference<XNameAccess> xFam = xSupplier->getStyleFamilies(); + Any aChar = xFam->getByName("CharacterStyles"); + Reference<XNameContainer> xChar; + aChar >>= xChar; + Reference<XIndexAccess> xCharIdx(xChar, UNO_QUERY); + if (xCharIdx.is()) + { + OUString sUIName("DisplayName"); + for (sal_Int32 nStyle = 0; nStyle < xCharIdx->getCount(); nStyle++) + { + Any aStyle = xCharIdx->getByIndex(nStyle); + Reference<XStyle> xStyle; + aStyle >>= xStyle; + Reference<XPropertySet> xPrSet(xStyle, UNO_QUERY); + OUString sName, sCoreName; + if (xPrSet.is()) + { + Reference<XPropertySetInfo> xInfo = xPrSet->getPropertySetInfo(); + if (xInfo->hasPropertyByName(sUIName)) + { + Any aName = xPrSet->getPropertyValue(sUIName); + aName >>= sName; + } + } + if (xStyle.is()) + { + sCoreName = xStyle->getName(); + if (sName.isEmpty()) + sName = sCoreName; + } + if (!sName.isEmpty()) + { + m_xCharStyleLB->append(sCoreName, sName); + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("svx.dialog", "exception in style access"); + } + if (!sCharStyleSelect.isEmpty()) + m_xCharStyleLB->set_active_text(sCharStyleSelect); + } + m_xCharStyleLB->set_sensitive(xSupplier.is()); + m_xCharStyleFT->set_sensitive(xSupplier.is()); + } + Update(); + m_xPreviewWin->Invalidate(); +} + +void SvxRubyDialog::SetRubyText(sal_Int32 nPos, weld::Entry& rLeft, weld::Entry& rRight) +{ + OUString sLeft, sRight; + const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues(); + bool bEnable = aRubyValues.getLength() > nPos; + if (bEnable) + { + const Sequence<PropertyValue> aProps = aRubyValues.getConstArray()[nPos]; + for (const PropertyValue& rProp : aProps) + { + if (rProp.Name == cRubyBaseText) + rProp.Value >>= sLeft; + else if (rProp.Name == cRubyText) + rProp.Value >>= sRight; + } + } + else if (!nPos) + { + bEnable = true; + } + rLeft.set_sensitive(bEnable); + rRight.set_sensitive(bEnable); + rLeft.set_text(sLeft); + rRight.set_text(sRight); + rLeft.save_value(); + rRight.save_value(); +} + +void SvxRubyDialog::GetRubyText() +{ + tools::Long nTempLastPos = GetLastPos(); + Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues(); + auto aRubyValuesRange = asNonConstRange(aRubyValues); + for (int i = 0; i < 8; i += 2) + { + if (aEditArr[i]->get_sensitive() + && (aEditArr[i]->get_value_changed_from_saved() + || aEditArr[i + 1]->get_value_changed_from_saved())) + { + DBG_ASSERT(aRubyValues.getLength() > (i / 2 + nTempLastPos), "wrong index"); + SetModified(true); + for (PropertyValue& propVal : asNonConstRange(aRubyValuesRange[i / 2 + nTempLastPos])) + { + if (propVal.Name == cRubyBaseText) + propVal.Value <<= aEditArr[i]->get_text(); + else if (propVal.Name == cRubyText) + propVal.Value <<= aEditArr[i + 1]->get_text(); + } + } + } +} + +void SvxRubyDialog::Update() +{ + const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues(); + sal_Int32 nLen = aRubyValues.getLength(); + m_xScrolledWindow->vadjustment_configure(0, 0, !nLen ? 1 : nLen, 1, 4, 4); + if (nLen > 4) + m_xScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS); + else + m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER); + SetLastPos(0); + SetModified(false); + + sal_Int16 nAdjust = -1; + sal_Int16 nPosition = -1; + OUString sCharStyleName, sTmp; + bool bCharStyleEqual = true; + for (sal_Int32 nRuby = 0; nRuby < nLen; nRuby++) + { + const Sequence<PropertyValue>& rProps = aRubyValues.getConstArray()[nRuby]; + for (const PropertyValue& rProp : rProps) + { + if (nAdjust > -2 && rProp.Name == cRubyAdjust) + { + sal_Int16 nTmp = sal_Int16(); + rProp.Value >>= nTmp; + if (!nRuby) + nAdjust = nTmp; + else if (nAdjust != nTmp) + nAdjust = -2; + } + if (nPosition > -2 && rProp.Name == cRubyPosition) + { + sal_Int16 nTmp = sal_Int16(); + rProp.Value >>= nTmp; + if (!nRuby) + nPosition = nTmp; + else if (nPosition != nTmp) + nPosition = -2; + } + if (bCharStyleEqual && rProp.Name == cRubyCharStyleName) + { + rProp.Value >>= sTmp; + if (!nRuby) + sCharStyleName = sTmp; + else if (sCharStyleName != sTmp) + bCharStyleEqual = false; + } + } + } + if (!nLen) + { + //enable selection if the ruby list is empty + nAdjust = 0; + nPosition = 0; + } + if (nAdjust > -1) + m_xAdjustLB->set_active(nAdjust); + else + m_xAdjustLB->set_active(-1); + if (nPosition > -1) + m_xPositionLB->set_active(nPosition); + if (!nLen || (bCharStyleEqual && sCharStyleName.isEmpty())) + sCharStyleName = "Rubies"; + if (!sCharStyleName.isEmpty()) + { + for (int i = 0, nEntryCount = m_xCharStyleLB->get_count(); i < nEntryCount; i++) + { + OUString sCoreName = m_xCharStyleLB->get_id(i); + if (sCharStyleName == sCoreName) + { + m_xCharStyleLB->set_active(i); + break; + } + } + } + else + m_xCharStyleLB->set_active(-1); + + ScrollHdl_Impl(*m_xScrolledWindow); +} + +void SvxRubyDialog::GetCurrentText(OUString& rBase, OUString& rRuby) +{ + rBase = aEditArr[nCurrentEdit * 2]->get_text(); + rRuby = aEditArr[nCurrentEdit * 2 + 1]->get_text(); +} + +IMPL_LINK(SvxRubyDialog, ScrollHdl_Impl, weld::ScrolledWindow&, rScroll, void) +{ + int nPos = rScroll.vadjustment_get_value(); + if (GetLastPos() != nPos) + { + GetRubyText(); + } + SetRubyText(nPos++, *m_xLeft1ED, *m_xRight1ED); + SetRubyText(nPos++, *m_xLeft2ED, *m_xRight2ED); + SetRubyText(nPos++, *m_xLeft3ED, *m_xRight3ED); + SetRubyText(nPos, *m_xLeft4ED, *m_xRight4ED); + SetLastPos(nPos - 3); + m_xPreviewWin->Invalidate(); +} + +IMPL_LINK_NOARG(SvxRubyDialog, ApplyHdl_Impl, weld::Button&, void) +{ + const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues(); + if (!aRubyValues.hasElements()) + { + AssertOneEntry(); + PositionHdl_Impl(*m_xPositionLB); + AdjustHdl_Impl(*m_xAdjustLB); + CharStyleHdl_Impl(*m_xCharStyleLB); + } + GetRubyText(); + //reset all edit fields - SaveValue is called + ScrollHdl_Impl(*m_xScrolledWindow); + + Reference<XRubySelection> xSelection = m_pImpl->GetRubySelection(); + if (IsModified() && xSelection.is()) + { + try + { + xSelection->setRubyList(aRubyValues, false); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("svx.dialog", ""); + } + } +} + +IMPL_LINK_NOARG(SvxRubyDialog, CloseHdl_Impl, weld::Button&, void) { Close(); } + +IMPL_LINK_NOARG(SvxRubyDialog, StylistHdl_Impl, weld::Button&, void) +{ + std::unique_ptr<SfxBoolItem> pState; + SfxItemState eState = pBindings->QueryState(SID_STYLE_DESIGNER, pState); + if (eState <= SfxItemState::SET || !pState || !pState->GetValue()) + { + pBindings->GetDispatcher()->Execute(SID_STYLE_DESIGNER, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } +} + +IMPL_LINK(SvxRubyDialog, AdjustHdl_Impl, weld::ComboBox&, rBox, void) +{ + AssertOneEntry(); + sal_Int16 nAdjust = rBox.get_active(); + for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues())) + { + for (PropertyValue& propVal : asNonConstRange(rProps)) + { + if (propVal.Name == cRubyAdjust) + propVal.Value <<= nAdjust; + } + SetModified(true); + } + m_xPreviewWin->Invalidate(); +} + +IMPL_LINK(SvxRubyDialog, PositionHdl_Impl, weld::ComboBox&, rBox, void) +{ + AssertOneEntry(); + sal_Int16 nPosition = rBox.get_active(); + for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues())) + { + for (PropertyValue& propVal : asNonConstRange(rProps)) + { + if (propVal.Name == cRubyPosition) + propVal.Value <<= nPosition; + } + SetModified(true); + } + m_xPreviewWin->Invalidate(); +} + +IMPL_LINK_NOARG(SvxRubyDialog, CharStyleHdl_Impl, weld::ComboBox&, void) +{ + AssertOneEntry(); + OUString sStyleName; + if (m_xCharStyleLB->get_active() != -1) + sStyleName = m_xCharStyleLB->get_active_id(); + for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues())) + { + for (PropertyValue& propVal : asNonConstRange(rProps)) + { + if (propVal.Name == cRubyCharStyleName) + { + propVal.Value <<= sStyleName; + } + } + SetModified(true); + } +} + +IMPL_LINK(SvxRubyDialog, EditFocusHdl_Impl, weld::Widget&, rEdit, void) +{ + for (sal_uInt16 i = 0; i < 8; i++) + { + if (&rEdit == aEditArr[i]) + { + nCurrentEdit = i / 2; + break; + } + } + m_xPreviewWin->Invalidate(); +} + +IMPL_LINK(SvxRubyDialog, EditModifyHdl_Impl, weld::Entry&, rEdit, void) +{ + EditFocusHdl_Impl(rEdit); +} + +bool SvxRubyDialog::EditScrollHdl_Impl(sal_Int32 nParam) +{ + bool bRet = false; + //scroll forward + if (nParam > 0 && (aEditArr[7]->has_focus() || aEditArr[6]->has_focus())) + { + if (m_xScrolledWindow->vadjustment_get_upper() + > m_xScrolledWindow->vadjustment_get_value() + + m_xScrolledWindow->vadjustment_get_page_size()) + { + m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value() + + 1); + aEditArr[6]->grab_focus(); + bRet = true; + } + } + //scroll backward + else if (m_xScrolledWindow->vadjustment_get_value() + && (aEditArr[0]->has_focus() || aEditArr[1]->has_focus())) + { + m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value() - 1); + aEditArr[1]->grab_focus(); + bRet = true; + } + if (bRet) + ScrollHdl_Impl(*m_xScrolledWindow); + return bRet; +} + +bool SvxRubyDialog::EditJumpHdl_Impl(sal_Int32 nParam) +{ + bool bHandled = false; + sal_uInt16 nIndex = USHRT_MAX; + for (sal_uInt16 i = 0; i < 8; i++) + { + if (aEditArr[i]->has_focus()) + nIndex = i; + } + if (nIndex < 8) + { + if (nParam > 0) + { + if (nIndex < 6) + aEditArr[nIndex + 2]->grab_focus(); + else if (EditScrollHdl_Impl(nParam)) + aEditArr[nIndex]->grab_focus(); + } + else + { + if (nIndex > 1) + aEditArr[nIndex - 2]->grab_focus(); + else if (EditScrollHdl_Impl(nParam)) + aEditArr[nIndex]->grab_focus(); + } + bHandled = true; + } + return bHandled; +} + +void SvxRubyDialog::AssertOneEntry() { m_pImpl->AssertOneEntry(); } + +void SvxRubyDialog::EnableControls(bool bEnable) +{ + m_xContentArea->set_sensitive(bEnable); + m_xApplyPB->set_sensitive(bEnable); +} + +RubyPreview::RubyPreview() + : m_pParentDlg(nullptr) +{ +} + +RubyPreview::~RubyPreview() {} + +void RubyPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/) +{ + rRenderContext.Push(vcl::PushFlags::ALL); + + rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip)); + + Size aWinSize = rRenderContext.GetOutputSize(); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + svtools::ColorConfig aColorConfig; + + Color aNewTextColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor); + Color aNewFillColor(rStyleSettings.GetWindowColor()); + + vcl::Font aFont = rRenderContext.GetFont(); + aFont.SetFontHeight(aWinSize.Height() / 4); + aFont.SetFillColor(aNewFillColor); + aFont.SetColor(aNewTextColor); + rRenderContext.SetFont(aFont); + + tools::Rectangle aRect(Point(0, 0), aWinSize); + rRenderContext.SetLineColor(); + rRenderContext.SetFillColor(aFont.GetFillColor()); + rRenderContext.DrawRect(aRect); + + OUString sBaseText, sRubyText; + m_pParentDlg->GetCurrentText(sBaseText, sRubyText); + + tools::Long nTextHeight = rRenderContext.GetTextHeight(); + tools::Long nBaseWidth = rRenderContext.GetTextWidth(sBaseText); + + vcl::Font aRubyFont(aFont); + aRubyFont.SetFontHeight(aRubyFont.GetFontHeight() * 70 / 100); + rRenderContext.SetFont(aRubyFont); + tools::Long nRubyWidth = rRenderContext.GetTextWidth(sRubyText); + rRenderContext.SetFont(aFont); + + RubyAdjust nAdjust = static_cast<RubyAdjust>(m_pParentDlg->m_xAdjustLB->get_active()); + //use center if no adjustment is available + if (nAdjust > RubyAdjust_INDENT_BLOCK) + nAdjust = RubyAdjust_CENTER; + + //which part is stretched ? + bool bRubyStretch = nBaseWidth >= nRubyWidth; + + tools::Long nCenter = aWinSize.Width() / 2; + tools::Long nHalfWidth = std::max(nBaseWidth, nRubyWidth) / 2; + tools::Long nLeftStart = nCenter - nHalfWidth; + tools::Long nRightEnd = nCenter + nHalfWidth; + + // Default values for TOP or no selection + tools::Long nYRuby = aWinSize.Height() / 4 - nTextHeight / 2; + tools::Long nYBase = aWinSize.Height() * 3 / 4 - nTextHeight / 2; + + sal_Int16 nRubyPos = m_pParentDlg->m_xPositionLB->get_active(); + if (nRubyPos == 1) // BOTTOM + std::swap(nYRuby, nYBase); + else if (nRubyPos == 2) // RIGHT ( vertically ) + { + // Align the ruby text and base text to the vertical center. + nYBase = (aWinSize.Height() - nTextHeight) / 2; + nYRuby = (aWinSize.Height() - nRubyWidth) / 2; + + // Align the ruby text at the right side of the base text + nAdjust = RubyAdjust_RIGHT; + nHalfWidth = nBaseWidth / 2; + nLeftStart = nCenter - nHalfWidth; + nRightEnd = nCenter + nHalfWidth + nRubyWidth + nTextHeight; + // Render base text first, then render ruby text on the right. + bRubyStretch = true; + + aRubyFont.SetVertical(true); + aRubyFont.SetOrientation(2700_deg10); + } + + tools::Long nYOutput; + tools::Long nOutTextWidth; + OUString sOutputText; + + if (bRubyStretch) + { + rRenderContext.DrawText(Point(nLeftStart, nYBase), sBaseText); + nYOutput = nYRuby; + sOutputText = sRubyText; + nOutTextWidth = nRubyWidth; + rRenderContext.SetFont(aRubyFont); + } + else + { + rRenderContext.SetFont(aRubyFont); + rRenderContext.DrawText(Point(nLeftStart, nYRuby), sRubyText); + nYOutput = nYBase; + sOutputText = sBaseText; + nOutTextWidth = nBaseWidth; + rRenderContext.SetFont(aFont); + } + + switch (nAdjust) + { + case RubyAdjust_LEFT: + rRenderContext.DrawText(Point(nLeftStart, nYOutput), sOutputText); + break; + case RubyAdjust_RIGHT: + rRenderContext.DrawText(Point(nRightEnd - nOutTextWidth, nYOutput), sOutputText); + break; + case RubyAdjust_INDENT_BLOCK: + { + tools::Long nCharWidth = rRenderContext.GetTextWidth("X"); + if (nOutTextWidth < (nRightEnd - nLeftStart - nCharWidth)) + { + nCharWidth /= 2; + nLeftStart += nCharWidth; + nRightEnd -= nCharWidth; + } + [[fallthrough]]; + } + case RubyAdjust_BLOCK: + { + if (sOutputText.getLength() > 1) + { + sal_Int32 nCount = sOutputText.getLength(); + tools::Long nSpace + = ((nRightEnd - nLeftStart) - rRenderContext.GetTextWidth(sOutputText)) + / (nCount - 1); + for (sal_Int32 i = 0; i < nCount; i++) + { + OUString sChar(sOutputText[i]); + rRenderContext.DrawText(Point(nLeftStart, nYOutput), sChar); + tools::Long nCharWidth = rRenderContext.GetTextWidth(sChar); + nLeftStart += nCharWidth + nSpace; + } + break; + } + [[fallthrough]]; + } + case RubyAdjust_CENTER: + rRenderContext.DrawText(Point(nCenter - nOutTextWidth / 2, nYOutput), sOutputText); + break; + default: + break; + } + rRenderContext.Pop(); +} + +void RubyPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 40, + pDrawingArea->get_text_height() * 7); + CustomWidgetController::SetDrawingArea(pDrawingArea); +} + +IMPL_LINK(SvxRubyDialog, KeyUpDownHdl_Impl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nCode = rKeyCode.GetCode(); + if (KEY_UP == nCode || KEY_DOWN == nCode) + { + sal_Int32 nParam = KEY_UP == nCode ? -1 : 1; + bHandled = EditJumpHdl_Impl(nParam); + } + return bHandled; +} + +IMPL_LINK(SvxRubyDialog, KeyUpDownTabHdl_Impl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + sal_uInt16 nMod = rKeyCode.GetModifier(); + sal_uInt16 nCode = rKeyCode.GetCode(); + if (nCode == KEY_TAB && (!nMod || KEY_SHIFT == nMod)) + { + sal_Int32 nParam = KEY_SHIFT == nMod ? -1 : 1; + if (EditScrollHdl_Impl(nParam)) + bHandled = true; + } + if (!bHandled) + bHandled = KeyUpDownHdl_Impl(rKEvt); + return bHandled; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/rulritem.cxx b/svx/source/dialog/rulritem.cxx new file mode 100644 index 0000000000..8e61bf381c --- /dev/null +++ b/svx/source/dialog/rulritem.cxx @@ -0,0 +1,735 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/svxids.hrc> +#include <svx/rulritem.hxx> +#include <svx/unomid.hxx> +#include <tools/debug.hxx> +#include <tools/mapunit.hxx> +#include <tools/UnitConversion.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/frame/status/LeftRightMargin.hpp> +#include <com/sun/star/frame/status/UpperLowerMargin.hpp> + +SfxPoolItem* SvxPagePosSizeItem::CreateDefault() { return new SvxPagePosSizeItem; } +SfxPoolItem* SvxLongLRSpaceItem::CreateDefault() { return new SvxLongLRSpaceItem; } +SfxPoolItem* SvxLongULSpaceItem::CreateDefault() { return new SvxLongULSpaceItem; } +SfxPoolItem* SvxColumnItem::CreateDefault() { return new SvxColumnItem; } +SfxPoolItem* SvxObjectItem::CreateDefault() { SAL_WARN( "svx", "No SvxObjectItem factory available"); return nullptr; } + +/* SvxLongLRSpaceItem */ + +bool SvxLongLRSpaceItem::operator==( const SfxPoolItem& rCmp) const +{ + return SfxPoolItem::operator==(rCmp) && + mlLeft == static_cast<const SvxLongLRSpaceItem &>(rCmp).mlLeft && + mlRight == static_cast<const SvxLongLRSpaceItem &>(rCmp).mlRight; +} + +bool SvxLongLRSpaceItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const +{ + bool bConvert = 0!=(nMemberId&CONVERT_TWIPS); + nMemberId &= ~CONVERT_TWIPS; + + sal_Int32 nVal; + switch( nMemberId ) + { + case 0: + { + css::frame::status::LeftRightMargin aLeftRightMargin; + aLeftRightMargin.Left = bConvert ? convertTwipToMm100( mlLeft ) : mlLeft; + aLeftRightMargin.Right = bConvert ? convertTwipToMm100( mlRight ) : mlRight; + rVal <<= aLeftRightMargin; + return true; + } + + case MID_LEFT: + nVal = mlLeft; + break; + case MID_RIGHT: + nVal = mlRight; + break; + default: + OSL_FAIL("Wrong MemberId!"); + return false; + } + + if ( bConvert ) + nVal = convertTwipToMm100( nVal ); + + rVal <<= nVal; + return true; +} + +bool SvxLongLRSpaceItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId ) +{ + bool bConvert = 0!=(nMemberId&CONVERT_TWIPS); + nMemberId &= ~CONVERT_TWIPS; + + sal_Int32 nVal = 0; + if ( nMemberId == 0 ) + { + css::frame::status::LeftRightMargin aLeftRightMargin; + if ( rVal >>= aLeftRightMargin ) + { + mlLeft = bConvert ? o3tl::toTwips(aLeftRightMargin.Left, o3tl::Length::mm100) : aLeftRightMargin.Left; + mlRight = bConvert ? o3tl::toTwips(aLeftRightMargin.Right, o3tl::Length::mm100) : aLeftRightMargin.Right; + return true; + } + } + else if ( rVal >>= nVal ) + { + if ( bConvert ) + nVal = o3tl::toTwips(nVal, o3tl::Length::mm100); + + switch( nMemberId ) + { + case MID_LEFT: + mlLeft = nVal; + break; + case MID_RIGHT: + mlRight = nVal; + break; + default: + OSL_FAIL("Wrong MemberId!"); + return false; + } + + return true; + } + + return false; +} + +bool SvxLongLRSpaceItem::GetPresentation( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rWrapper*/) const +{ + return false; +} + +SvxLongLRSpaceItem* SvxLongLRSpaceItem::Clone(SfxItemPool *) const +{ + return new SvxLongLRSpaceItem(*this); +} + +SvxLongLRSpaceItem::SvxLongLRSpaceItem(tools::Long lLeft, tools::Long lRight, TypedWhichId<SvxLongLRSpaceItem> nId) : + SfxPoolItem (nId), + mlLeft (lLeft), + mlRight (lRight) +{} + +SvxLongLRSpaceItem::SvxLongLRSpaceItem() : + SfxPoolItem (0), + mlLeft (0), + mlRight (0) +{} + +void SvxLongLRSpaceItem::SetLeft(tools::Long lArgLeft) +{ + mlLeft = lArgLeft; +} + +void SvxLongLRSpaceItem::SetRight(tools::Long lArgRight) +{ + mlRight = lArgRight; +} + +/* SvxLongULSpaceItem */ + +bool SvxLongULSpaceItem::operator==( const SfxPoolItem& rCmp) const +{ + return SfxPoolItem::operator==(rCmp) && + mlLeft == static_cast<const SvxLongULSpaceItem&>(rCmp).mlLeft && + mlRight == static_cast<const SvxLongULSpaceItem&>(rCmp).mlRight; +} + +bool SvxLongULSpaceItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const +{ + bool bConvert = 0!=(nMemberId&CONVERT_TWIPS); + nMemberId &= ~CONVERT_TWIPS; + + sal_Int32 nVal; + switch( nMemberId ) + { + case 0: + { + css::frame::status::UpperLowerMargin aUpperLowerMargin; + aUpperLowerMargin.Upper = bConvert ? convertTwipToMm100( mlLeft ) : mlLeft; + aUpperLowerMargin.Lower = bConvert ? convertTwipToMm100( mlRight ) : mlRight; + rVal <<= aUpperLowerMargin; + return true; + } + + case MID_UPPER: + nVal = mlLeft; + break; + case MID_LOWER: + nVal = mlRight; + break; + default: OSL_FAIL("Wrong MemberId!"); return false; + } + + if ( bConvert ) + nVal = convertTwipToMm100( nVal ); + + rVal <<= nVal; + return true; +} + +bool SvxLongULSpaceItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId ) +{ + bool bConvert = 0!=(nMemberId&CONVERT_TWIPS); + nMemberId &= ~CONVERT_TWIPS; + + sal_Int32 nVal = 0; + if ( nMemberId == 0 ) + { + css::frame::status::UpperLowerMargin aUpperLowerMargin; + if ( rVal >>= aUpperLowerMargin ) + { + mlLeft = bConvert ? o3tl::toTwips(aUpperLowerMargin.Upper, o3tl::Length::mm100) : aUpperLowerMargin.Upper; + mlRight = bConvert ? o3tl::toTwips(aUpperLowerMargin.Lower, o3tl::Length::mm100) : aUpperLowerMargin.Lower; + return true; + } + } + else if ( rVal >>= nVal ) + { + if ( bConvert ) + nVal = o3tl::toTwips(nVal, o3tl::Length::mm100); + + switch( nMemberId ) + { + case MID_UPPER: + mlLeft = nVal; + break; + case MID_LOWER: + mlRight = nVal; + break; + default: + OSL_FAIL("Wrong MemberId!"); + return false; + } + + return true; + } + + return false; +} + +bool SvxLongULSpaceItem::GetPresentation( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rWrapper*/ ) const +{ + return false; +} + +SvxLongULSpaceItem* SvxLongULSpaceItem::Clone(SfxItemPool *) const +{ + return new SvxLongULSpaceItem(*this); +} + +SvxLongULSpaceItem::SvxLongULSpaceItem(tools::Long lLeft, tools::Long lRight, TypedWhichId<SvxLongULSpaceItem> nId) : + SfxPoolItem (nId), + mlLeft (lLeft), + mlRight (lRight) +{} + +SvxLongULSpaceItem::SvxLongULSpaceItem() : + SfxPoolItem (0), + mlLeft (0), + mlRight (0) +{} + + +void SvxLongULSpaceItem::SetUpper(tools::Long lArgLeft) +{ + mlLeft = lArgLeft; +} + +void SvxLongULSpaceItem::SetLower(tools::Long lArgRight) +{ + mlRight = lArgRight; +} + +/* SvxPagePosSizeItem */ + +bool SvxPagePosSizeItem::operator==( const SfxPoolItem& rCmp) const +{ + return SfxPoolItem::operator==(rCmp) && + aPos == static_cast<const SvxPagePosSizeItem &>(rCmp).aPos && + lWidth == static_cast<const SvxPagePosSizeItem &>(rCmp).lWidth && + lHeight == static_cast<const SvxPagePosSizeItem &>(rCmp).lHeight; +} + +bool SvxPagePosSizeItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const +{ + nMemberId &= ~CONVERT_TWIPS; + + sal_Int32 nVal; + switch ( nMemberId ) + { + case 0 : + { + css::awt::Rectangle aPagePosSize; + aPagePosSize.X = aPos.X(); + aPagePosSize.Y = aPos.Y(); + aPagePosSize.Width = lWidth; + aPagePosSize.Height = lHeight; + rVal <<= aPagePosSize; + return true; + } + + case MID_X: nVal = aPos.X(); break; + case MID_Y: nVal = aPos.Y(); break; + case MID_WIDTH: nVal = lWidth; break; + case MID_HEIGHT: nVal = lHeight; break; + + default: OSL_FAIL("Wrong MemberId!"); return false; + } + + rVal <<= nVal; + return true; +} + +bool SvxPagePosSizeItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId ) +{ + nMemberId &= ~CONVERT_TWIPS; + + sal_Int32 nVal = 0; + if ( nMemberId == 0 ) + { + css::awt::Rectangle aPagePosSize; + if ( rVal >>= aPagePosSize ) + { + aPos.setX( aPagePosSize.X ); + aPos.setY( aPagePosSize.Y ); + lWidth = aPagePosSize.Width; + lHeight = aPagePosSize.Height; + return true; + } + else + return false; + } + else if ( rVal >>= nVal ) + { + switch ( nMemberId ) + { + case MID_X: aPos.setX( nVal ); break; + case MID_Y: aPos.setY( nVal ); break; + case MID_WIDTH: lWidth = nVal; break; + case MID_HEIGHT: lHeight = nVal; break; + + default: OSL_FAIL("Wrong MemberId!"); return false; + } + + return true; + } + + return false; +} + +bool SvxPagePosSizeItem::GetPresentation( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rWrapper*/ ) const +{ + return false; +} + +SvxPagePosSizeItem* SvxPagePosSizeItem::Clone(SfxItemPool *) const +{ + return new SvxPagePosSizeItem(*this); +} + +SvxPagePosSizeItem::SvxPagePosSizeItem(const Point &rP, tools::Long lW, tools::Long lH) : + SfxPoolItem (SID_RULER_PAGE_POS), + aPos (rP), + lWidth (lW), + lHeight (lH) +{} + +SvxPagePosSizeItem::SvxPagePosSizeItem() : + SfxPoolItem (0), + aPos (0, 0), + lWidth (0), + lHeight (0) +{} + +/* SvxColumnItem */ + +bool SvxColumnItem::operator==(const SfxPoolItem& rCmp) const +{ + if(!SfxPoolItem::operator==(rCmp) || + nActColumn != static_cast<const SvxColumnItem&>(rCmp).nActColumn || + nLeft != static_cast<const SvxColumnItem&>(rCmp).nLeft || + nRight != static_cast<const SvxColumnItem&>(rCmp).nRight || + bTable != static_cast<const SvxColumnItem&>(rCmp).bTable || + Count() != static_cast<const SvxColumnItem&>(rCmp).Count()) + return false; + + const sal_uInt16 nCount = static_cast<const SvxColumnItem&>(rCmp).Count(); + for(sal_uInt16 i = 0; i < nCount;++i) + { + if( (*this)[i] != static_cast<const SvxColumnItem&>(rCmp)[i] ) + return false; + } + return true; +} + +SvxColumnItem::SvxColumnItem( sal_uInt16 nAct ) : + SfxPoolItem (SID_RULER_BORDERS), + nLeft (0), + nRight (0), + nActColumn (nAct), + bTable (false), + bOrtho (true) + +{} + +SvxColumnItem::SvxColumnItem( sal_uInt16 nActCol, sal_uInt16 left, sal_uInt16 right ) : + SfxPoolItem (SID_RULER_BORDERS), + nLeft (left), + nRight (right), + nActColumn (nActCol), + bTable (true), + bOrtho (true) +{} + +bool SvxColumnItem::GetPresentation( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rWrapper*/ ) const +{ + return false; +} + +SvxColumnItem* SvxColumnItem::Clone(SfxItemPool* /*pPool*/) const +{ + return new SvxColumnItem(*this); +} + +bool SvxColumnItem::CalcOrtho() const +{ + const sal_uInt16 nCount = Count(); + DBG_ASSERT(nCount >= 2, "no columns"); + if(nCount < 2) + return false; + + tools::Long nColWidth = (*this)[0].GetWidth(); + for(sal_uInt16 i = 1; i < nCount; ++i) { + if( (*this)[i].GetWidth() != nColWidth) + return false; + } + //!! Wide divider + return true; +} + +bool SvxColumnItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const +{ + nMemberId &= ~CONVERT_TWIPS; + switch ( nMemberId ) + { + case 0: + // SfxDispatchController_Impl::StateChanged calls this with hardcoded 0 triggering this; + SAL_INFO("svx", "SvxColumnItem::QueryValue with nMemberId of 0"); + return false; + case MID_COLUMNARRAY: + return false; + case MID_RIGHT: + rVal <<= nRight; + break; + case MID_LEFT: + rVal <<= nLeft; + break; + case MID_ORTHO: + rVal <<= bOrtho; + break; + case MID_ACTUAL: + rVal <<= static_cast<sal_Int32>(nActColumn); + break; + case MID_TABLE: + rVal <<= bTable; + break; + default: + SAL_WARN("svx", "Wrong MemberId!"); + return false; + } + + return true; +} + +bool SvxColumnItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId ) +{ + nMemberId &= ~CONVERT_TWIPS; + sal_Int32 nVal = 0; + switch ( nMemberId ) + { + case MID_COLUMNARRAY: + { + return false; + } + case MID_RIGHT: + rVal >>= nRight; + break; + case MID_LEFT: + rVal >>= nLeft; + break; + case MID_ORTHO: + rVal >>= nVal; + bOrtho = static_cast<bool>(nVal); + break; + case MID_ACTUAL: + rVal >>= nVal; + nActColumn = static_cast<sal_uInt16>(nVal); + break; + case MID_TABLE: + rVal >>= nVal; + bTable = static_cast<bool>(nVal); + break; + default: + OSL_FAIL("Wrong MemberId!"); + return false; + } + + return true; +} + +sal_uInt16 SvxColumnItem::Count() const +{ + return aColumns.size(); +} + +SvxColumnDescription& SvxColumnItem::At(sal_uInt16 index) +{ + return aColumns[index]; +} + +SvxColumnDescription& SvxColumnItem::GetActiveColumnDescription() +{ + return aColumns[GetActColumn()]; +} + +SvxColumnDescription& SvxColumnItem::operator[](sal_uInt16 index) +{ + return aColumns[index]; +} + +const SvxColumnDescription& SvxColumnItem::operator[](sal_uInt16 index) const +{ + return aColumns[index]; +} + +void SvxColumnItem::Append(const SvxColumnDescription &rDesc) +{ + aColumns.push_back(rDesc); +} + +void SvxColumnItem::SetLeft(tools::Long left) +{ + nLeft = left; +} + +void SvxColumnItem::SetRight(tools::Long right) +{ + nRight = right; +} + + +bool SvxColumnItem::IsFirstAct() const +{ + return nActColumn == 0; +} + +bool SvxColumnItem::IsLastAct() const +{ + return nActColumn == Count() - 1; +} + +SvxColumnDescription::SvxColumnDescription(tools::Long start, tools::Long end, bool bVis) : + nStart (start), + nEnd (end), + bVisible (bVis), + nEndMin (0), + nEndMax (0) +{} + +SvxColumnDescription::SvxColumnDescription(tools::Long start, tools::Long end, tools::Long endMin, tools::Long endMax, bool bVis) : + nStart (start), + nEnd (end), + bVisible (bVis), + // fdo#85858 hack: clamp these to smaller value to prevent overflow + nEndMin(std::min<tools::Long>(endMin, std::numeric_limits<unsigned short>::max())), + nEndMax(std::min<tools::Long>(endMax, std::numeric_limits<unsigned short>::max())) +{} + +bool SvxColumnDescription::operator==(const SvxColumnDescription& rCmp) const +{ + return nStart == rCmp.nStart + && bVisible == rCmp.bVisible + && nEnd == rCmp.nEnd + && nEndMin == rCmp.nEndMin + && nEndMax == rCmp.nEndMax; +} + +bool SvxColumnDescription::operator!=(const SvxColumnDescription& rCmp) const +{ + return !operator==(rCmp); +} + +tools::Long SvxColumnDescription::GetWidth() const +{ + return nEnd - nStart; +} + +/* SvxColumnItem */ +void SvxColumnItem::SetOrtho(bool bVal) +{ + bOrtho = bVal; +} + +bool SvxColumnItem::IsConsistent() const +{ + return nActColumn < aColumns.size(); +} + +bool SvxObjectItem::operator==( const SfxPoolItem& rCmp ) const +{ + return SfxPoolItem::operator==(rCmp) && + nStartX == static_cast<const SvxObjectItem&>(rCmp).nStartX && + nEndX == static_cast<const SvxObjectItem&>(rCmp).nEndX && + nStartY == static_cast<const SvxObjectItem&>(rCmp).nStartY && + nEndY == static_cast<const SvxObjectItem&>(rCmp).nEndY && + bLimits == static_cast<const SvxObjectItem&>(rCmp).bLimits; +} + +bool SvxObjectItem::GetPresentation( + SfxItemPresentation /*ePres*/, + MapUnit /*eCoreUnit*/, + MapUnit /*ePresUnit*/, + OUString& /*rText*/, + const IntlWrapper& /*rWrapper*/ ) const +{ + return false; +} + +SvxObjectItem* SvxObjectItem::Clone(SfxItemPool *) const +{ + return new SvxObjectItem(*this); +} + +SvxObjectItem::SvxObjectItem( tools::Long nSX, tools::Long nEX, + tools::Long nSY, tools::Long nEY ) : + SfxPoolItem (SID_RULER_OBJECT), + nStartX (nSX), + nEndX (nEX), + nStartY (nSY), + nEndY (nEY), + bLimits (false) +{} + +bool SvxObjectItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const +{ + nMemberId &= ~CONVERT_TWIPS; + switch (nMemberId) + { + case MID_START_X: + rVal <<= nStartX; + break; + case MID_START_Y: + rVal <<= nStartY; + break; + case MID_END_X: + rVal <<= nEndX; + break; + case MID_END_Y: + rVal <<= nEndY; + break; + case MID_LIMIT: + rVal <<= bLimits; + break; + default: + OSL_FAIL( "Wrong MemberId" ); + return false; + } + + return true; +} + +bool SvxObjectItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId ) +{ + nMemberId &= ~CONVERT_TWIPS; + bool bRet = false; + switch (nMemberId) + { + case MID_START_X: + bRet = (rVal >>= nStartX); + break; + case MID_START_Y: + bRet = (rVal >>= nStartY); + break; + case MID_END_X: + bRet = (rVal >>= nEndX); + break; + case MID_END_Y: + bRet = (rVal >>= nEndY); + break; + case MID_LIMIT: + bRet = (rVal >>= bLimits); + break; + default: OSL_FAIL( "Wrong MemberId" ); + } + + return bRet; +} + + +void SvxObjectItem::SetStartX(tools::Long lValue) +{ + nStartX = lValue; +} + +void SvxObjectItem::SetEndX(tools::Long lValue) +{ + nEndX = lValue; +} + +void SvxObjectItem::SetStartY(tools::Long lValue) +{ + nStartY = lValue; +} + +void SvxObjectItem::SetEndY(tools::Long lValue) +{ + nEndY = lValue; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/samecontentlistbox.cxx b/svx/source/dialog/samecontentlistbox.cxx new file mode 100644 index 0000000000..66c628a1e7 --- /dev/null +++ b/svx/source/dialog/samecontentlistbox.cxx @@ -0,0 +1,39 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dialmgr.hxx> +#include <svx/samecontentlistbox.hxx> +#include <samecontent.hrc> + +namespace SameContentListBox +{ + void Fill(weld::ComboBox& rComboBox) + { + rComboBox.clear(); + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_SVXSTRARY_SAMECONTENT); ++i) + { + OUString aStr = SvxResId(RID_SVXSTRARY_SAMECONTENT[i].first); + sal_uInt32 nData = RID_SVXSTRARY_SAMECONTENT[i].second; + rComboBox.append(OUString::number(nData), aStr); + } + rComboBox.set_active(0); + rComboBox.set_size_request(150, -1); + } +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/searchcharmap.cxx b/svx/source/dialog/searchcharmap.cxx new file mode 100644 index 0000000000..2a770eac57 --- /dev/null +++ b/svx/source/dialog/searchcharmap.cxx @@ -0,0 +1,323 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> + +#include <svx/ucsubset.hxx> +#include <unordered_map> + +#include <svx/searchcharmap.hxx> + +#include <charmapacc.hxx> + +#include <rtl/ustrbuf.hxx> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + + +SvxSearchCharSet::SvxSearchCharSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow, const VclPtr<VirtualDevice>& rVirDev) + : SvxShowCharSet(std::move(pScrolledWindow), rVirDev) +{ +} + +int SvxSearchCharSet::LastInView() const +{ + int nIndex = FirstInView(); + nIndex += ROW_COUNT * COLUMN_COUNT - 1; + return std::min<int>(nIndex, getMaxCharCount() -1); +} + +bool SvxSearchCharSet::KeyInput(const KeyEvent& rKEvt) +{ + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + if (aCode.GetModifier()) + return false; + + int tmpSelected = nSelectedIndex; + + bool bRet = true; + + switch (aCode.GetCode()) + { + case KEY_RETURN: + return SvxShowCharSet::KeyInput(rKEvt); + case KEY_SPACE: + aDoubleClkHdl.Call(this); + return true; + case KEY_LEFT: + --tmpSelected; + break; + case KEY_RIGHT: + ++tmpSelected; + break; + case KEY_UP: + tmpSelected -= COLUMN_COUNT; + break; + case KEY_DOWN: + tmpSelected += COLUMN_COUNT; + break; + case KEY_PAGEUP: + tmpSelected -= ROW_COUNT * COLUMN_COUNT; + break; + case KEY_PAGEDOWN: + tmpSelected += ROW_COUNT * COLUMN_COUNT; + break; + case KEY_HOME: + tmpSelected = 0; + break; + case KEY_END: + tmpSelected = getMaxCharCount() - 1; + break; + case KEY_TAB: // some fonts have a character at these unicode control codes + case KEY_ESCAPE: + bRet = false; + tmpSelected = - 1; // mark as invalid + break; + default: + tmpSelected = -1; + bRet = false; + break; + } + + if ( tmpSelected >= 0 ) + { + SelectIndex( tmpSelected, true ); + aPreSelectHdl.Call( this ); + } + + return bRet; +} + +void SvxSearchCharSet::SelectCharacter( const Subset* sub ) +{ + if (!mxFontCharMap.is()) + RecalculateFont(*mxVirDev); + + // get next available char of current font + sal_UCS4 cChar = sub->GetRangeMin(); + int nMapIndex = 0; + + while(cChar <= sub->GetRangeMax() && nMapIndex == 0) + { + auto it = std::find_if(m_aItemList.begin(), m_aItemList.end(), + [&cChar](const std::pair<const sal_Int32, sal_UCS4>& rItem) { return rItem.second == cChar; }); + if (it != m_aItemList.end()) + nMapIndex = it->first; + cChar++; + } + + if(nMapIndex == 0) + SelectIndex( 0 ); + else + SelectIndex( nMapIndex ); + aHighHdl.Call(this); + // move selected item to top row if not in focus + //TO.DO aVscrollSB->SetThumbPos( nMapIndex / COLUMN_COUNT ); + Invalidate(); +} + +sal_UCS4 SvxSearchCharSet::GetCharFromIndex(int index) const +{ + std::unordered_map<sal_Int32, sal_UCS4>::const_iterator got = m_aItemList.find(index); + return (got != m_aItemList.end()) ? got->second : 0; +} + +void SvxSearchCharSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + InitSettings(rRenderContext); + RecalculateFont(rRenderContext); + DrawChars_Impl(rRenderContext, FirstInView(), LastInView()); +} + +sal_UCS4 SvxSearchCharSet::GetSelectCharacter() const +{ + if( nSelectedIndex >= 0 ) + { + std::unordered_map<sal_Int32,sal_UCS4>::const_iterator got = m_aItemList.find (nSelectedIndex); + + if(got == m_aItemList.end()) + return 1; + else + return got->second; + } + return 1; +} + +void SvxSearchCharSet::RecalculateFont(vcl::RenderContext& rRenderContext) +{ + if (!mbRecalculateFont) + return; + + Size aSize(GetOutputSizePixel()); + + vcl::Font aFont = rRenderContext.GetFont(); + aFont.SetWeight(WEIGHT_LIGHT); + aFont.SetAlignment(ALIGN_TOP); + int nFontHeight = (aSize.Height() - 5) * 2 / (3 * ROW_COUNT); + maFontSize = rRenderContext.PixelToLogic(Size(0, nFontHeight)); + aFont.SetFontSize(maFontSize); + aFont.SetTransparent(true); + rRenderContext.SetFont(aFont); + rRenderContext.GetFontCharMap(mxFontCharMap); + m_aItems.clear(); + getFavCharacterList(); + + nX = aSize.Width() / COLUMN_COUNT; + nY = aSize.Height() / ROW_COUNT; + + UpdateScrollRange(); + + // rearrange CharSet element in sync with nX- and nY-multiples + Size aDrawSize(nX * COLUMN_COUNT, nY * ROW_COUNT); + m_nXGap = (aSize.Width() - aDrawSize.Width()) / 2; + m_nYGap = (aSize.Height() - aDrawSize.Height()) / 2; + + mbRecalculateFont = false; +} + +void SvxSearchCharSet::UpdateScrollRange() +{ + //scrollbar settings + int nLastRow = (getMaxCharCount() - 1 + COLUMN_COUNT) / COLUMN_COUNT; + mxScrollArea->vadjustment_configure(mxScrollArea->vadjustment_get_value(), 0, nLastRow, 1, ROW_COUNT - 1, ROW_COUNT); +} + +void SvxSearchCharSet::SelectIndex(int nNewIndex, bool bFocus) +{ + if (!mxFontCharMap.is()) + RecalculateFont(*mxVirDev); + + if( nNewIndex < 0 ) + { + mxScrollArea->vadjustment_set_value(0); + nSelectedIndex = bFocus ? 0 : -1; + Invalidate(); + } + else if( nNewIndex < FirstInView() ) + { + // need to scroll up to see selected item + int nOldPos = mxScrollArea->vadjustment_get_value(); + int nDelta = (FirstInView() - nNewIndex + COLUMN_COUNT-1) / COLUMN_COUNT; + mxScrollArea->vadjustment_set_value(nOldPos - nDelta); + nSelectedIndex = nNewIndex; + Invalidate(); + } + else if( nNewIndex > LastInView() ) + { + // need to scroll down to see selected item + int nOldPos = mxScrollArea->vadjustment_get_value(); + int nDelta = (nNewIndex - LastInView() + COLUMN_COUNT) / COLUMN_COUNT; + mxScrollArea->vadjustment_set_value(nOldPos + nDelta); + + if (nNewIndex < getMaxCharCount()) + { + nSelectedIndex = nNewIndex; + Invalidate(); + } + else if (nOldPos != mxScrollArea->vadjustment_get_value()) + { + Invalidate(); + } + } + else + { + nSelectedIndex = nNewIndex; + Invalidate(); + } + + if( nSelectedIndex >= 0 ) + { +#if 0 + if( m_xAccessible.is() ) + { + svx::SvxShowCharSetItem* pItem = ImplGetItem(nSelectedIndex); + // Don't fire the focus event. + if ( bFocus ) + m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, Any(), makeAny(pItem->GetAccessible()) ); // this call assures that m_pItem is set + else + m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS, Any(), makeAny(pItem->GetAccessible()) ); // this call assures that m_pItem is set + + assert(pItem->m_xItem.is() && "No accessible created!"); + Any aOldAny, aNewAny; + aNewAny <<= AccessibleStateType::FOCUSED; + // Don't fire the focus event. + if ( bFocus ) + pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny ); + + aNewAny <<= AccessibleStateType::SELECTED; + pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny ); + } +#endif + aSelectHdl.Call(this); + } + aHighHdl.Call( this ); +} + +SvxSearchCharSet::~SvxSearchCharSet() +{ +} + +svx::SvxShowCharSetItem* SvxSearchCharSet::ImplGetItem( int _nPos ) +{ + ItemsMap::iterator aFind = m_aItems.find(_nPos); + if ( aFind == m_aItems.end() ) + { +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + OSL_ENSURE(m_xAccessible.is(), "Who wants to create a child of my table without a parent?"); +#endif + auto xItem = std::make_shared<svx::SvxShowCharSetItem>(*this, + m_xAccessible.get(), sal::static_int_cast< sal_uInt16 >(_nPos)); + aFind = m_aItems.emplace(_nPos, xItem).first; + OUStringBuffer buf; + std::unordered_map<sal_Int32,sal_UCS4>::const_iterator got = m_aItemList.find (_nPos); + if (got != m_aItemList.end()) + buf.appendUtf32(got->second); + aFind->second->maText = buf.makeStringAndClear(); + Point pix = MapIndexToPixel( _nPos ); + aFind->second->maRect = tools::Rectangle( Point( pix.X() + 1, pix.Y() + 1 ), Size(nX-1,nY-1) ); + } + + return aFind->second.get(); +} + +sal_Int32 SvxSearchCharSet::getMaxCharCount() const +{ + return m_aItemList.size(); +} + +void SvxSearchCharSet::ClearPreviousData() +{ + m_aItemList.clear(); + Invalidate(); +} + +void SvxSearchCharSet::AppendCharToList(sal_UCS4 sChar) +{ + m_aItemList.insert(std::make_pair(m_aItemList.size(), sChar)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/signaturelinehelper.cxx b/svx/source/dialog/signaturelinehelper.cxx new file mode 100644 index 0000000000..2c055e84db --- /dev/null +++ b/svx/source/dialog/signaturelinehelper.cxx @@ -0,0 +1,165 @@ +/* -*- 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 <svx/signaturelinehelper.hxx> + +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/security/DocumentDigitalSignatures.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/xmlsechelper.hxx> +#include <config_folders.h> +#include <rtl/bootstrap.hxx> +#include <sal/log.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/objsh.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <svx/svdmark.hxx> +#include <svx/svdview.hxx> +#include <tools/stream.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/streamwrap.hxx> +#include <unotools/syslocale.hxx> +#include <vcl/weld.hxx> + +using namespace com::sun::star; + +namespace svx::SignatureLineHelper +{ +OUString getSignatureImage(const OUString& rType) +{ + OUString aType = rType; + if (aType.isEmpty()) + { + aType = "signature-line.svg"; + } + OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/" + aType); + rtl::Bootstrap::expandMacros(aPath); + SvFileStream aStream(aPath, StreamMode::READ); + if (aStream.GetError() != ERRCODE_NONE) + { + SAL_WARN("cui.dialogs", "failed to open " << aType); + } + + OString const svg = read_uInt8s_ToOString(aStream, aStream.remainingSize()); + return OUString::fromUtf8(svg); +} + +uno::Reference<security::XCertificate> getSignatureCertificate(SfxObjectShell* pShell, + weld::Window* pParent) +{ + if (!pShell) + { + return {}; + } + + if (!pParent) + { + return {}; + } + + uno::Reference<security::XDocumentDigitalSignatures> xSigner; + if (pShell->GetMedium()->GetFilter()->IsAlienFormat()) + { + xSigner = security::DocumentDigitalSignatures::createDefault( + comphelper::getProcessComponentContext()); + } + else + { + OUString const aODFVersion( + comphelper::OStorageHelper::GetODFVersionFromStorage(pShell->GetStorage())); + xSigner = security::DocumentDigitalSignatures::createWithVersion( + comphelper::getProcessComponentContext(), aODFVersion); + } + xSigner->setParentWindow(pParent->GetXWindow()); + OUString aDescription; + security::CertificateKind certificateKind = security::CertificateKind_NONE; + // When signing ooxml, we only want X.509 certificates + if (pShell->GetMedium()->GetFilter()->IsAlienFormat()) + { + certificateKind = security::CertificateKind_X509; + } + uno::Reference<security::XCertificate> xSignCertificate + = xSigner->selectSigningCertificateWithType(certificateKind, aDescription); + return xSignCertificate; +} + +OUString getSignerName(const css::uno::Reference<css::security::XCertificate>& xCertificate) +{ + return comphelper::xmlsec::GetContentPart(xCertificate->getSubjectName(), + xCertificate->getCertificateKind()); +} + +OUString getLocalizedDate() +{ + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData(); + Date aDateTime(Date::SYSTEM); + return rLocaleData.getDate(aDateTime); +} + +uno::Reference<graphic::XGraphic> importSVG(std::u16string_view rSVG) +{ + SvMemoryStream aSvgStream(4096, 4096); + aSvgStream.WriteOString(OUStringToOString(rSVG, RTL_TEXTENCODING_UTF8)); + uno::Reference<io::XInputStream> xInputStream(new utl::OSeekableInputStreamWrapper(aSvgStream)); + uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + uno::Reference<graphic::XGraphicProvider> xProvider + = graphic::GraphicProvider::create(xContext); + + uno::Sequence<beans::PropertyValue> aMediaProperties{ comphelper::makePropertyValue( + "InputStream", xInputStream) }; + uno::Reference<graphic::XGraphic> xGraphic(xProvider->queryGraphic(aMediaProperties)); + return xGraphic; +} + +void setShapeCertificate(const SdrView* pView, + const css::uno::Reference<css::security::XCertificate>& xCertificate) +{ + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() < 1) + { + return; + } + + const SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pSignatureLine = pMark->GetMarkedSdrObj(); + if (!pSignatureLine) + { + return; + } + + // Remember the selected certificate. + uno::Reference<drawing::XShape> xShape = pSignatureLine->getUnoShape(); + uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); + comphelper::SequenceAsHashMap aMap(xShapeProps->getPropertyValue("InteropGrabBag")); + aMap["SignatureCertificate"] <<= xCertificate; + xShapeProps->setPropertyValue("InteropGrabBag", uno::Any(aMap.getAsConstPropertyValueList())); + + // Read svg and replace placeholder texts. + OUString aSvgImage(svx::SignatureLineHelper::getSignatureImage("signature-line-draw.svg")); + aSvgImage = aSvgImage.replaceAll("[SIGNED_BY]", SvxResId(RID_SVXSTR_SIGNATURELINE_DSIGNED_BY)); + OUString aSignerName = svx::SignatureLineHelper::getSignerName(xCertificate); + aSvgImage = aSvgImage.replaceAll("[SIGNER_NAME]", aSignerName); + OUString aDate = svx::SignatureLineHelper::getLocalizedDate(); + aDate = SvxResId(RID_SVXSTR_SIGNATURELINE_DATE).replaceFirst("%1", aDate); + aSvgImage = aSvgImage.replaceAll("[DATE]", aDate); + + uno::Reference<graphic::XGraphic> xGraphic = svx::SignatureLineHelper::importSVG(aSvgImage); + xShapeProps->setPropertyValue("Graphic", uno::Any(xGraphic)); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/spacinglistbox.cxx b/svx/source/dialog/spacinglistbox.cxx new file mode 100644 index 0000000000..90cc689ccc --- /dev/null +++ b/svx/source/dialog/spacinglistbox.cxx @@ -0,0 +1,76 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dialmgr.hxx> +#include <svx/spacinglistbox.hxx> +#include <unotools/localedatawrapper.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <spacing.hrc> + +namespace SpacingListBox +{ + void Fill(SpacingType eType, weld::ComboBox& rComboBox) + { + auto nSelected = rComboBox.get_active(); + if (nSelected == -1) + nSelected = 0; + rComboBox.clear(); + + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); + OUString sSuffix; + + const measurement* pResources; + switch (eType) + { + case SpacingType::SPACING_INCH: + pResources = RID_SVXSTRARY_SPACING_INCH; + sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::INCH); + break; + case SpacingType::MARGINS_INCH: + pResources = RID_SVXSTRARY_MARGINS_INCH; + sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::INCH); + break; + case SpacingType::SPACING_CM: + pResources = RID_SVXSTRARY_SPACING_CM; + sSuffix = " " + weld::MetricSpinButton::MetricToString(FieldUnit::CM); + break; + default: + case SpacingType::MARGINS_CM: + sSuffix = " " + weld::MetricSpinButton::MetricToString(FieldUnit::CM); + pResources = RID_SVXSTRARY_MARGINS_CM; + break; + } + + while (pResources->key) + { + OUString sMeasurement = rLocaleData.getNum(pResources->human, 2, true, false) + sSuffix; + OUString aStr = SvxResId(pResources->key).replaceFirst("%1", sMeasurement); + sal_uInt32 nData = pResources->twips; + rComboBox.append(OUString::number(nData), aStr); + ++pResources; + } + + rComboBox.set_active(nSelected); + + rComboBox.set_size_request(150, -1); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/srchctrl.cxx b/svx/source/dialog/srchctrl.cxx new file mode 100644 index 0000000000..51c8d03337 --- /dev/null +++ b/svx/source/dialog/srchctrl.cxx @@ -0,0 +1,71 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svl/intitem.hxx> +#include <sfx2/objsh.hxx> + +#include <svx/svxids.hrc> + +#include "srchctrl.hxx" +#include <svx/srchdlg.hxx> +#include <svl/srchitem.hxx> + +SvxSearchController::SvxSearchController +( + sal_uInt16 _nId, + SfxBindings& rBind, + SvxSearchDialog& rDlg +) : + SfxControllerItem( _nId, rBind ), + + rSrchDlg( rDlg ) +{ +} + + +void SvxSearchController::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState ) +{ + if ( SfxItemState::DEFAULT == eState ) + { + if ( SID_STYLE_FAMILY1 <= nSID && nSID <= SID_STYLE_FAMILY4 ) + { + SfxObjectShell* pShell = SfxObjectShell::Current(); + + if ( pShell && pShell->GetStyleSheetPool() ) + rSrchDlg.TemplatesChanged_Impl( *pShell->GetStyleSheetPool() ); + } + else if ( SID_SEARCH_OPTIONS == nSID ) + { + DBG_ASSERT( dynamic_cast<const SfxUInt16Item* >(pState) != nullptr, "wrong item type" ); + SearchOptionFlags nFlags = static_cast<SearchOptionFlags>(static_cast<const SfxUInt16Item*>(pState)->GetValue()); + rSrchDlg.EnableControls_Impl( nFlags ); + } + else if ( SID_SEARCH_ITEM == nSID ) + { + DBG_ASSERT( dynamic_cast<const SvxSearchItem*>( pState) != nullptr, "wrong item type" ); + rSrchDlg.SetItem_Impl( static_cast<const SvxSearchItem*>(pState) ); + } + } + else if ( SID_SEARCH_OPTIONS == nSID || SID_SEARCH_ITEM == nSID ) + rSrchDlg.EnableControls_Impl( SearchOptionFlags::NONE ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/srchctrl.hxx b/svx/source/dialog/srchctrl.hxx new file mode 100644 index 0000000000..0427575377 --- /dev/null +++ b/svx/source/dialog/srchctrl.hxx @@ -0,0 +1,39 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef INCLUDED_SVX_SOURCE_DIALOG_SRCHCTRL_HXX +#define INCLUDED_SVX_SOURCE_DIALOG_SRCHCTRL_HXX + +#include <sfx2/ctrlitem.hxx> +class SvxSearchDialog; + +class SvxSearchController : public SfxControllerItem +{ + SvxSearchDialog& rSrchDlg; + +protected: + virtual void StateChangedAtToolBoxControl(sal_uInt16, SfxItemState, + const SfxPoolItem* pState) override; + +public: + SvxSearchController(sal_uInt16 nId, SfxBindings& rBnd, SvxSearchDialog& rDlg); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/srchdlg.cxx b/svx/source/dialog/srchdlg.cxx new file mode 100644 index 0000000000..f06822ceaf --- /dev/null +++ b/svx/source/dialog/srchdlg.cxx @@ -0,0 +1,2475 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <osl/diagnose.h> +#include <vcl/timer.hxx> +#include <svl/slstitm.hxx> +#include <svl/itemiter.hxx> +#include <svl/style.hxx> +#include <unotools/intlwrapper.hxx> +#include <unotools/moduleoptions.hxx> +#include <unotools/searchopt.hxx> +#include <unotools/syslocale.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/module.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/basedlgs.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/cjkoptions.hxx> +#include <svl/ctloptions.hxx> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/ui/XUIElement.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/scopeguard.hxx> +#include <svl/itempool.hxx> + +#include <sfx2/app.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <svx/srchdlg.hxx> +#include <svx/strarray.hxx> + +#include <svx/strings.hrc> +#include <svx/svxids.hrc> + +#include <svl/srchitem.hxx> +#include <svx/pageitem.hxx> +#include "srchctrl.hxx" +#include <svx/dialmgr.hxx> +#include <editeng/brushitem.hxx> +#include <tools/resary.hxx> +#include <svx/svxdlg.hxx> +#include <vcl/toolbox.hxx> +#include <o3tl/typed_flags_set.hxx> +#include <comphelper/lok.hxx> + +#include <cstdlib> +#include <memory> + +#include <findtextfield.hxx> + +#include <svx/labelitemwindow.hxx> +#include <svx/xdef.hxx> +#include <officecfg/Office/Common.hxx> + +using namespace com::sun::star::i18n; +using namespace com::sun::star::uno; +using namespace com::sun::star::accessibility; +using namespace com::sun::star; +using namespace comphelper; + + +#define IS_MOBILE (comphelper::LibreOfficeKit::isActive() && SfxViewShell::Current() && SfxViewShell::Current()->isLOKMobilePhone()) + +enum class ModifyFlags { + NONE = 0x000000, + Search = 0x000001, + Replace = 0x000002, + Word = 0x000004, + Exact = 0x000008, + Backwards = 0x000010, + Selection = 0x000020, + Regexp = 0x000040, + Layout = 0x000080, + Similarity = 0x000100, + Formulas = 0x000200, + Values = 0x000400, + CalcNotes = 0x000800, + Rows = 0x001000, + Columns = 0x002000, + AllTables = 0x004000, + Notes = 0x008000, + Wildcard = 0x010000 +}; +namespace o3tl { + template<> struct typed_flags<ModifyFlags> : is_typed_flags<ModifyFlags, 0x01ffff> {}; +} + +namespace +{ + bool GetCheckBoxValue(const weld::CheckButton& rBox) + { + return rBox.get_sensitive() && rBox.get_active(); + } + + bool GetNegatedCheckBoxValue(const weld::CheckButton& rBox) + { + return rBox.get_sensitive() && !rBox.get_active(); + } +} + +struct SearchDlg_Impl +{ + bool bSaveToModule : 1, + bFocusOnSearch : 1; + WhichRangesContainer pRanges; + Timer aSelectionTimer { "svx SearchDlg_Impl aSelectionTimer" }; + + uno::Reference< frame::XDispatch > xCommand1Dispatch; + uno::Reference< frame::XDispatch > xCommand2Dispatch; + util::URL aCommand1URL; + util::URL aCommand2URL; + + SearchDlg_Impl() + : bSaveToModule(true) + , bFocusOnSearch(true) + { + aCommand1URL.Complete = aCommand1URL.Main = "vnd.sun.search:SearchViaComponent1"; + aCommand1URL.Protocol = "vnd.sun.search:"; + aCommand1URL.Path = "SearchViaComponent1"; + aCommand2URL.Complete = aCommand2URL.Main = "vnd.sun.search:SearchViaComponent2"; + aCommand2URL.Protocol = "vnd.sun.search:"; + aCommand2URL.Path = "SearchViaComponent2"; + } +}; + +static void ListToStrArr_Impl(sal_uInt16 nId, std::vector<OUString>& rStrLst, weld::ComboBox& rCBox, sal_uInt16 nRememberSize) +{ + const SfxStringListItem* pSrchItem = + static_cast<const SfxStringListItem*>(SfxGetpApp()->GetItem( nId )); + + if (!pSrchItem) + return; + + std::vector<OUString> aLst = pSrchItem->GetList(); + + if (aLst.size() > nRememberSize) + aLst.resize(nRememberSize); + + for (const OUString & s : aLst) + { + rStrLst.push_back(s); + rCBox.append_text(s); + } +} + +static void StrArrToList_Impl( TypedWhichId<SfxStringListItem> nId, const std::vector<OUString>& rStrLst ) +{ + DBG_ASSERT( !rStrLst.empty(), "check in advance"); + SfxGetpApp()->PutItem( SfxStringListItem( nId, &rStrLst ) ); +} + +SearchAttrItemList::SearchAttrItemList( SearchAttrItemList&& rList ) : + SrchAttrInfoList(std::move(rList)) +{ + for ( size_t i = 0; i < size(); ++i ) + if ( !IsInvalidItem( (*this)[i].pItemPtr ) ) + (*this)[i].pItemPtr = (*this)[i].pItemPtr->Clone(); +} + +SearchAttrItemList::SearchAttrItemList( const SearchAttrItemList& rList ) : + SrchAttrInfoList(rList) +{ + for ( size_t i = 0; i < size(); ++i ) + if ( !IsInvalidItem( (*this)[i].pItemPtr ) ) + (*this)[i].pItemPtr = (*this)[i].pItemPtr->Clone(); +} + +SearchAttrItemList::~SearchAttrItemList() +{ + Clear(); +} + +void SearchAttrItemList::Put( const SfxItemSet& rSet ) +{ + if ( !rSet.Count() ) + return; + + SfxItemPool* pPool = rSet.GetPool(); + SfxItemIter aIter( rSet ); + SearchAttrInfo aItem; + const SfxPoolItem* pItem = aIter.GetCurItem(); + sal_uInt16 nWhich; + + do + { + // only test that it is available? + if( IsInvalidItem( pItem ) ) + { + nWhich = rSet.GetWhichByOffset( aIter.GetCurPos() ); + aItem.pItemPtr = pItem; + } + else + { + nWhich = pItem->Which(); + aItem.pItemPtr = pItem->Clone(); + } + + aItem.nSlot = pPool->GetSlotId( nWhich ); + Insert( aItem ); + + pItem = aIter.NextItem(); + } while (pItem); +} + + +SfxItemSet& SearchAttrItemList::Get( SfxItemSet& rSet ) +{ + SfxItemPool* pPool = rSet.GetPool(); + + for ( size_t i = 0; i < size(); ++i ) + if ( IsInvalidItem( (*this)[i].pItemPtr ) ) + rSet.InvalidateItem( pPool->GetWhich( (*this)[i].nSlot ) ); + else + rSet.Put( *(*this)[i].pItemPtr ); + return rSet; +} + + +void SearchAttrItemList::Clear() +{ + for ( size_t i = 0; i < size(); ++i ) + if ( !IsInvalidItem( (*this)[i].pItemPtr ) ) + delete (*this)[i].pItemPtr; + SrchAttrInfoList::clear(); +} + + +// Deletes the pointer to the items +void SearchAttrItemList::Remove(size_t nPos) +{ + size_t nLen = 1; + if ( nPos + nLen > size() ) + nLen = size() - nPos; + + for ( size_t i = nPos; i < nPos + nLen; ++i ) + if ( !IsInvalidItem( (*this)[i].pItemPtr ) ) + delete (*this)[i].pItemPtr; + + SrchAttrInfoList::erase( begin() + nPos, begin() + nPos + nLen ); +} + +SvxSearchDialog::SvxSearchDialog(weld::Window* pParent, SfxChildWindow* pChildWin, SfxBindings& rBind) + : SfxModelessDialogController(&rBind, pChildWin, pParent, + IS_MOBILE ? OUString("svx/ui/findreplacedialog-mobile.ui") : OUString("svx/ui/findreplacedialog.ui"), + "FindReplaceDialog") + , rBindings(rBind) + , m_aPresentIdle("Bring SvxSearchDialog to Foreground") + , bWriter(false) + , bSearch(true) + , bFormat(false) + , bReplaceBackwards(false) + , nOptions(SearchOptionFlags::ALL) + , bSet(false) + , bConstruct(true) + , nModifyFlag(ModifyFlags::NONE) + , pReplaceList(new SearchAttrItemList) + , nTransliterationFlags(TransliterationFlags::NONE) + , m_xSearchFrame(m_xBuilder->weld_frame("searchframe")) + , m_xSearchLB(m_xBuilder->weld_combo_box("searchterm")) + , m_xSearchTmplLB(m_xBuilder->weld_combo_box("searchlist")) + , m_xSearchAttrText(m_xBuilder->weld_label("searchdesc")) + , m_xSearchLabel(m_xBuilder->weld_label("searchlabel")) + , m_xSearchIcon(m_xBuilder->weld_image("searchicon")) + , m_xSearchBox(m_xBuilder->weld_box("searchbox")) + , m_xReplaceFrame(m_xBuilder->weld_frame("replaceframe")) + , m_xReplaceLB(m_xBuilder->weld_combo_box("replaceterm")) + , m_xReplaceTmplLB(m_xBuilder->weld_combo_box("replacelist")) + , m_xReplaceAttrText(m_xBuilder->weld_label("replacedesc")) + , m_xSearchBtn(m_xBuilder->weld_button("search")) + , m_xBackSearchBtn(m_xBuilder->weld_button("backsearch")) + , m_xSearchAllBtn(m_xBuilder->weld_button("searchall")) + , m_xReplaceBtn(m_xBuilder->weld_button("replace")) + , m_xReplaceAllBtn(m_xBuilder->weld_button("replaceall")) + , m_xComponentFrame(m_xBuilder->weld_frame("componentframe")) + , m_xSearchComponent1PB(m_xBuilder->weld_button("component1")) + , m_xSearchComponent2PB(m_xBuilder->weld_button("component2")) + , m_xMatchCaseCB(m_xBuilder->weld_check_button("matchcase")) + , m_xSearchFormattedCB(m_xBuilder->weld_check_button("searchformatted")) + , m_xWordBtn(m_xBuilder->weld_check_button("wholewords")) + , m_xCloseBtn(m_xBuilder->weld_button("close")) + , m_xHelpBtn(m_xBuilder->weld_button("help")) + , m_xIncludeDiacritics(m_xBuilder->weld_check_button("includediacritics")) + , m_xIncludeKashida(m_xBuilder->weld_check_button("includekashida")) + , m_xOtherOptionsExpander(m_xBuilder->weld_expander("OptionsExpander")) + , m_xSelectionBtn(m_xBuilder->weld_check_button("selection")) + , m_xRegExpBtn(m_xBuilder->weld_check_button("regexp")) + , m_xWildcardBtn(m_xBuilder->weld_check_button("wildcard")) + , m_xSimilarityBox(m_xBuilder->weld_check_button("similarity")) + , m_xSimilarityBtn(m_xBuilder->weld_button("similaritybtn")) + , m_xLayoutBtn(m_xBuilder->weld_check_button("layout")) + , m_xNotesBtn(m_xBuilder->weld_check_button("notes")) + , m_xJapMatchFullHalfWidthCB(m_xBuilder->weld_check_button("matchcharwidth")) + , m_xJapOptionsCB(m_xBuilder->weld_check_button("soundslike")) + , m_xReplaceBackwardsCB(m_xBuilder->weld_check_button("replace_backwards")) + , m_xJapOptionsBtn(m_xBuilder->weld_button("soundslikebtn")) + , m_xAttributeBtn(m_xBuilder->weld_button("attributes")) + , m_xFormatBtn(m_xBuilder->weld_button("format")) + , m_xNoFormatBtn(m_xBuilder->weld_button("noformat")) + , m_xCalcGrid(m_xBuilder->weld_widget("calcgrid")) + , m_xCalcSearchInFT(m_xBuilder->weld_label("searchinlabel")) + , m_xCalcSearchInLB(m_xBuilder->weld_combo_box("calcsearchin")) + , m_xCalcSearchDirFT(m_xBuilder->weld_label("searchdir")) + , m_xRowsBtn(m_xBuilder->weld_radio_button("rows")) + , m_xColumnsBtn(m_xBuilder->weld_radio_button("cols")) + , m_xAllSheetsCB(m_xBuilder->weld_check_button("allsheets")) + , m_xCalcStrFT(m_xBuilder->weld_label("entirecells")) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + m_xCloseBtn->hide(); + m_xHelpBtn->hide(); + } + + m_aPresentIdle.SetTimeout(50); + m_aPresentIdle.SetInvokeHandler(LINK(this, SvxSearchDialog, PresentTimeoutHdl_Impl)); + + m_xSearchTmplLB->make_sorted(); + m_xSearchAttrText->hide(); + + m_xSearchLabel->set_font_color(Color(0x00, 0x47, 0x85)); + this->SetSearchLabel(""); // hide the message but keep the box height + m_xSearchIcon->set_size_request(24, 24); // vcl/res/infobar.png is 32x32 - too large here + + m_xReplaceTmplLB->make_sorted(); + m_xReplaceAttrText->hide(); + + aCalcStr = m_xCalcStrFT->get_label(); + + // m_xSimilarityBtn->set_height_request(m_xSimilarityBox->get_preferred_size().Height()); + // m_xJapOptionsBtn->set_height_request(m_xJapOptionsCB->get_preferred_size().Height()); + + //tdf#122322 + nRememberSize = officecfg::Office::Common::Misc::FindReplaceRememberedSearches::get(); + if (nRememberSize<1) + nRememberSize = 1; //0 crashes with no results found + + auto nTermWidth = m_xSearchLB->get_approximate_digit_width() * 28; + m_xSearchLB->set_size_request(nTermWidth, -1); + m_xSearchTmplLB->set_size_request(nTermWidth, -1); + m_xReplaceLB->set_size_request(nTermWidth, -1); + m_xReplaceTmplLB->set_size_request(nTermWidth, -1); + + Construct_Impl(); +} + +IMPL_LINK_NOARG(SvxSearchDialog, PresentTimeoutHdl_Impl, Timer*, void) +{ + getDialog()->present(); +} + +void SvxSearchDialog::Present() +{ + PresentTimeoutHdl_Impl(nullptr); + // tdf#133807 try again in a short timeout + m_aPresentIdle.Start(); +} + +void SvxSearchDialog::ChildWinDispose() +{ + rBindings.EnterRegistrations(); + pSearchController.reset(); + pOptionsController.reset(); + pFamilyController.reset(); + rBindings.LeaveRegistrations(); + SfxModelessDialogController::ChildWinDispose(); +} + +SvxSearchDialog::~SvxSearchDialog() +{ + m_aPresentIdle.Stop(); + pSearchItem.reset(); + pImpl.reset(); +} + +void SvxSearchDialog::Construct_Impl() +{ + pImpl.reset( new SearchDlg_Impl() ); + pImpl->aSelectionTimer.SetTimeout( 500 ); + pImpl->aSelectionTimer.SetInvokeHandler( + LINK( this, SvxSearchDialog, TimeoutHdl_Impl ) ); + EnableControls_Impl( SearchOptionFlags::NONE ); + + // Store old Text from m_xWordBtn + aCalcStr += "#"; + aCalcStr += m_xWordBtn->get_label(); + + aLayoutStr = SvxResId( RID_SVXSTR_SEARCH_STYLES ); + aLayoutWriterStr = SvxResId( RID_SVXSTR_WRITER_STYLES ); + aLayoutCalcStr = SvxResId( RID_SVXSTR_CALC_STYLES ); + aStylesStr = m_xLayoutBtn->get_label(); + + // Get stored search-strings from the application + ListToStrArr_Impl(SID_SEARCHDLG_SEARCHSTRINGS, + aSearchStrings, *m_xSearchLB, nRememberSize); + ListToStrArr_Impl(SID_SEARCHDLG_REPLACESTRINGS, + aReplaceStrings, *m_xReplaceLB, nRememberSize); + + InitControls_Impl(); + + // Get attribute sets only once in constructor() + const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr }; + SfxPoolItemHolder aResult(rBindings.GetDispatcher()->Execute(FID_SEARCH_SEARCHSET, SfxCallMode::SLOT, ppArgs)); + const SvxSetItem* pSrchSetItem(static_cast<const SvxSetItem*>(aResult.getItem())); + + if ( pSrchSetItem ) + InitAttrList_Impl( &pSrchSetItem->GetItemSet(), nullptr ); + + aResult = rBindings.GetDispatcher()->Execute(FID_SEARCH_REPLACESET, SfxCallMode::SLOT, ppArgs); + const SvxSetItem* pReplSetItem(static_cast<const SvxSetItem*>(aResult.getItem())); + + if ( pReplSetItem ) + InitAttrList_Impl( nullptr, &pReplSetItem->GetItemSet() ); + + // Create controller and update at once + rBindings.EnterRegistrations(); + pSearchController.reset( + new SvxSearchController( SID_SEARCH_ITEM, rBindings, *this ) ); + pOptionsController.reset( + new SvxSearchController( SID_SEARCH_OPTIONS, rBindings, *this ) ); + rBindings.LeaveRegistrations(); + rBindings.GetDispatcher()->Execute( FID_SEARCH_ON, SfxCallMode::SLOT, ppArgs ); + pImpl->aSelectionTimer.Start(); + + + if(!SvtCJKOptions::IsJapaneseFindEnabled()) + { + m_xJapOptionsCB->set_active( false ); + m_xJapOptionsCB->hide(); + m_xJapOptionsBtn->hide(); + } + if(!SvtCJKOptions::IsCJKFontEnabled()) + { + m_xJapMatchFullHalfWidthCB->hide(); + } + // Do not disable and hide the m_xIncludeDiacritics button. + // Include Diacritics == Not Ignore Diacritics => A does not match A-Umlaut (Diaeresis). + // Confusingly these have negated names (following the UI) but the actual + // transliteration is to *ignore* diacritics if "included" (sensitive) is + // _not_ checked. + if(!SvtCTLOptions::IsCTLFontEnabled()) + { + m_xIncludeDiacritics->set_active( true ); + m_xIncludeKashida->set_active( true ); + m_xIncludeKashida->hide(); + } + //component extension - show component search buttons if the commands + // vnd.sun.star::SearchViaComponent1 and 2 are supported + const uno::Reference< frame::XFrame >xFrame = rBindings.GetActiveFrame(); + const uno::Reference< frame::XDispatchProvider > xDispatchProv(xFrame, uno::UNO_QUERY); + + bool bSearchComponent1 = false; + bool bSearchComponent2 = false; + if(xDispatchProv.is()) + { + OUString sTarget("_self"); + pImpl->xCommand1Dispatch = xDispatchProv->queryDispatch(pImpl->aCommand1URL, sTarget, 0); + if (pImpl->xCommand1Dispatch.is()) + bSearchComponent1 = true; + pImpl->xCommand2Dispatch = xDispatchProv->queryDispatch(pImpl->aCommand2URL, sTarget, 0); + if (pImpl->xCommand2Dispatch.is()) + bSearchComponent2 = true; + } + + if( !(bSearchComponent1 || bSearchComponent2) ) + return; + + try + { + uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider = + configuration::theDefaultProvider::get( comphelper::getProcessComponentContext() ); + uno::Sequence< uno::Any > aArgs { + Any(OUString( "/org.openoffice.Office.Common/SearchOptions/")) }; + + uno::Reference< uno::XInterface > xIFace = xConfigurationProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationUpdateAccess", + aArgs); + uno::Reference< container::XNameAccess> xDirectAccess(xIFace, uno::UNO_QUERY); + if(xDirectAccess.is()) + { + OUString sTemp; + uno::Any aRet = xDirectAccess->getByName("ComponentSearchGroupLabel"); + aRet >>= sTemp; + m_xComponentFrame->set_label(sTemp); + aRet = xDirectAccess->getByName("ComponentSearchCommandLabel1"); + aRet >>= sTemp; + m_xSearchComponent1PB->set_label( sTemp ); + aRet = xDirectAccess->getByName("ComponentSearchCommandLabel2"); + aRet >>= sTemp; + m_xSearchComponent2PB->set_label( sTemp ); + } + } + catch(uno::Exception&){} + + if(!m_xSearchComponent1PB->get_label().isEmpty() && bSearchComponent1 ) + { + m_xComponentFrame->show(); + m_xSearchComponent1PB->show(); + } + if( !m_xSearchComponent2PB->get_label().isEmpty() ) + { + m_xComponentFrame->show(); + m_xSearchComponent2PB->show(); + } +} + +void SvxSearchDialog::Close() +{ + // remember strings + if (!aSearchStrings.empty()) + StrArrToList_Impl( SID_SEARCHDLG_SEARCHSTRINGS, aSearchStrings ); + + if (!aReplaceStrings.empty()) + StrArrToList_Impl( SID_SEARCHDLG_REPLACESTRINGS, aReplaceStrings ); + + // save settings to configuration + SvtSearchOptions aOpt; + aOpt.SetWholeWordsOnly ( m_xWordBtn->get_active() ); + aOpt.SetBackwards ( m_xReplaceBackwardsCB->get_active() ); + aOpt.SetUseRegularExpression ( m_xRegExpBtn->get_active() ); + aOpt.SetUseWildcard ( m_xWildcardBtn->get_active() ); + aOpt.SetSearchForStyles ( m_xLayoutBtn->get_active() ); + aOpt.SetSimilaritySearch ( m_xSimilarityBox->get_active() ); + aOpt.SetUseAsianOptions ( m_xJapOptionsCB->get_active() ); + aOpt.SetNotes ( m_xNotesBtn->get_active() ); + aOpt.SetIgnoreDiacritics_CTL ( !m_xIncludeDiacritics->get_active() ); + aOpt.SetIgnoreKashida_CTL ( !m_xIncludeKashida->get_active() ); + aOpt.SetSearchFormatted ( m_xSearchFormattedCB->get_active() ); + aOpt.Commit(); + + if (IsClosing()) + return; + + const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr }; + rBindings.GetDispatcher()->Execute( FID_SEARCH_OFF, SfxCallMode::SLOT, ppArgs ); + rBindings.Invalidate(SID_SEARCH_DLG); + + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + if (pViewFrame) + pViewFrame->ToggleChildWindow(SID_SEARCH_DLG); +} + +TransliterationFlags SvxSearchDialog::GetTransliterationFlags() const +{ + if (!m_xMatchCaseCB->get_active()) + nTransliterationFlags |= TransliterationFlags::IGNORE_CASE; + else + nTransliterationFlags &= ~TransliterationFlags::IGNORE_CASE; + if ( !m_xJapMatchFullHalfWidthCB->get_active()) + nTransliterationFlags |= TransliterationFlags::IGNORE_WIDTH; + else + nTransliterationFlags &= ~TransliterationFlags::IGNORE_WIDTH; + return nTransliterationFlags; +} + +void SvxSearchDialog::SetSaveToModule(bool b) +{ + pImpl->bSaveToModule = b; +} + +void SvxSearchDialog::SetSearchLabel(const OUString& rStr) +{ + m_xSearchLabel->set_label(rStr); + if (!rStr.isEmpty()) + { + m_xSearchLabel->show(); + m_xSearchIcon->show(); + m_xSearchBox->set_background(Color(0xBD, 0xE5, 0xF8)); // same as InfobarType::INFO + } + else + { + const Size aSize = m_xSearchBox->get_preferred_size(); + m_xSearchLabel->hide(); + m_xSearchIcon->hide(); + m_xSearchBox->set_size_request(-1, aSize.Height()); + m_xSearchBox->set_background(COL_TRANSPARENT); + } +} + +void SvxSearchDialog::ApplyTransliterationFlags_Impl( TransliterationFlags nSettings ) +{ + nTransliterationFlags = nSettings; + bool bVal(nSettings & TransliterationFlags::IGNORE_CASE); + m_xMatchCaseCB->set_active( !bVal ); + bVal = bool(nSettings & TransliterationFlags::IGNORE_WIDTH); + m_xJapMatchFullHalfWidthCB->set_active( !bVal ); +} + + +bool SvxSearchDialog::IsOtherOptionsExpanded() const +{ + return m_xReplaceBackwardsCB->get_active() || + m_xSelectionBtn->get_active() || + m_xRegExpBtn->get_active() || + m_xLayoutBtn->get_active() || + m_xSimilarityBox->get_active() || + m_xJapMatchFullHalfWidthCB->get_active() || + m_xJapOptionsCB->get_active() || + m_xWildcardBtn->get_active() || + m_xNotesBtn->get_active() || + m_xIncludeKashida->get_active() || + !m_xIncludeDiacritics->get_active();//tdf#138173 +} + +void SvxSearchDialog::Activate() +{ + // apply possible transliteration changes of the SvxSearchItem member + if (pSearchItem) + { + m_xMatchCaseCB->set_active( pSearchItem->GetExact() ); + m_xJapMatchFullHalfWidthCB->set_active( !pSearchItem->IsMatchFullHalfWidthForms() ); + } + + SfxModelessDialogController::Activate(); +} + +void SvxSearchDialog::InitControls_Impl() +{ + // CaseSensitives AutoComplete + m_xSearchLB->set_entry_completion( true, true ); + m_xSearchLB->show(); + m_xReplaceLB->set_entry_completion( true, true ); + m_xReplaceLB->show(); + + m_xFormatBtn->set_sensitive(false); + m_xAttributeBtn->set_sensitive(false); + + m_xSearchLB->connect_changed( LINK( this, SvxSearchDialog, ModifyHdl_Impl ) ); + m_xReplaceLB->connect_changed( LINK( this, SvxSearchDialog, ModifyHdl_Impl ) ); + + Link<weld::Widget&,void> aLink = LINK( this, SvxSearchDialog, FocusHdl_Impl ); + m_xSearchLB->connect_focus_in( aLink ); + m_xReplaceLB->connect_focus_in( aLink ); + + aLink = LINK( this, SvxSearchDialog, LoseFocusHdl_Impl ); + m_xSearchLB->connect_focus_out( aLink ); + m_xReplaceLB->connect_focus_out( aLink ); + + m_xSearchTmplLB->connect_focus_out( aLink ); + m_xReplaceTmplLB->connect_focus_out( aLink ); + + Link<weld::Button&,void> aLink2 = LINK( this, SvxSearchDialog, CommandHdl_Impl ); + m_xSearchBtn->connect_clicked( aLink2 ); + m_xBackSearchBtn->connect_clicked( aLink2 ); + m_xSearchAllBtn->connect_clicked( aLink2 ); + m_xReplaceBtn->connect_clicked( aLink2 ); + m_xReplaceAllBtn->connect_clicked( aLink2 ); + m_xCloseBtn->connect_clicked( aLink2 ); + m_xSimilarityBtn->connect_clicked( aLink2 ); + m_xJapOptionsBtn->connect_clicked( aLink2 ); + m_xSearchComponent1PB->connect_clicked( aLink2 ); + m_xSearchComponent2PB->connect_clicked( aLink2 ); + + Link<weld::Toggleable&,void> aLink3 = LINK( this, SvxSearchDialog, FlagHdl_Impl ); + m_xReplaceBackwardsCB->connect_toggled( aLink3 ); + m_xWordBtn->connect_toggled( aLink3 ); + m_xSelectionBtn->connect_toggled( aLink3 ); + m_xMatchCaseCB->connect_toggled( aLink3 ); + m_xRegExpBtn->connect_toggled( aLink3 ); + m_xWildcardBtn->connect_toggled( aLink3 ); + m_xNotesBtn->connect_toggled( aLink3 ); + m_xSimilarityBox->connect_toggled( aLink3 ); + m_xJapOptionsCB->connect_toggled( aLink3 ); + m_xJapMatchFullHalfWidthCB->connect_toggled( aLink3 ); + m_xIncludeDiacritics->connect_toggled( aLink3 ); + m_xIncludeKashida->connect_toggled( aLink3 ); + m_xLayoutBtn->connect_toggled( LINK( this, SvxSearchDialog, TemplateHdl_Impl ) ); + m_xFormatBtn->connect_clicked( LINK( this, SvxSearchDialog, FormatHdl_Impl ) ); + m_xNoFormatBtn->connect_clicked( + LINK( this, SvxSearchDialog, NoFormatHdl_Impl ) ); + m_xAttributeBtn->connect_clicked( + LINK( this, SvxSearchDialog, AttributeHdl_Impl ) ); +} + +namespace +{ + SvtModuleOptions::EFactory getModule(SfxBindings const & rBindings) + { + SvtModuleOptions::EFactory eFactory(SvtModuleOptions::EFactory::UNKNOWN_FACTORY); + try + { + const uno::Reference< frame::XFrame > xFrame = + rBindings.GetActiveFrame(); + uno::Reference< frame::XModuleManager2 > xModuleManager( + frame::ModuleManager::create(::comphelper::getProcessComponentContext())); + + OUString aModuleIdentifier = xModuleManager->identify( xFrame ); + eFactory = SvtModuleOptions::ClassifyFactoryByServiceName(aModuleIdentifier); + } + catch (const uno::Exception&) + { + } + return eFactory; + } +} + +void SvxSearchDialog::ShowOptionalControls_Impl() +{ + DBG_ASSERT( pSearchItem, "no search item" ); + + SvtModuleOptions::EFactory eFactory = getModule(rBindings); + bool bDrawApp = eFactory == SvtModuleOptions::EFactory::DRAW; + bool bWriterApp = + eFactory == SvtModuleOptions::EFactory::WRITER || + eFactory == SvtModuleOptions::EFactory::WRITERWEB || + eFactory == SvtModuleOptions::EFactory::WRITERGLOBAL; + bool bCalcApp = eFactory == SvtModuleOptions::EFactory::CALC; + + m_xLayoutBtn->set_visible(!bDrawApp); + m_xNotesBtn->set_visible(bWriterApp); + m_xRegExpBtn->set_visible(!bDrawApp); + m_xWildcardBtn->set_visible(bCalcApp); /* TODO:WILDCARD enable for other apps if hey handle it */ + m_xReplaceBackwardsCB->show(); + m_xSimilarityBox->show(); + m_xSimilarityBtn->show(); + m_xSelectionBtn->show(); + m_xIncludeDiacritics->show(); + m_xIncludeKashida->set_visible(SvtCTLOptions::IsCTLFontEnabled()); + m_xJapMatchFullHalfWidthCB->set_visible(SvtCJKOptions::IsCJKFontEnabled()); + m_xJapOptionsCB->set_visible(SvtCJKOptions::IsJapaneseFindEnabled()); + m_xJapOptionsBtn->set_visible(SvtCJKOptions::IsJapaneseFindEnabled()); + + if (bWriter) + { + m_xAttributeBtn->show(); + m_xFormatBtn->show(); + m_xNoFormatBtn->show(); + } + + if (bCalcApp) + { + m_xCalcSearchInFT->show(); + m_xCalcSearchInLB->show(); + m_xCalcSearchDirFT->show(); + m_xRowsBtn->show(); + m_xColumnsBtn->show(); + m_xAllSheetsCB->show(); + m_xSearchFormattedCB->show(); + } +} + + +namespace { + +class ToggleSaveToModule +{ +public: + ToggleSaveToModule(SvxSearchDialog& rDialog, bool bValue) : + mrDialog(rDialog), mbValue(bValue) + { + mrDialog.SetSaveToModule(mbValue); + } + + ~ToggleSaveToModule() + { + mrDialog.SetSaveToModule(!mbValue); + } +private: + SvxSearchDialog& mrDialog; + bool mbValue; +}; + +} + +void SvxSearchDialog::Init_Impl( bool bSearchPattern ) +{ + DBG_ASSERT( pSearchItem, "SearchItem == 0" ); + + // We don't want to save any intermediate state to the module while the + // dialog is being initialized. + ToggleSaveToModule aNoModuleSave(*this, false); + SvtSearchOptions aOpt; + + bWriter = ( pSearchItem->GetAppFlag() == SvxSearchApp::WRITER ); + + if ( !( nModifyFlag & ModifyFlags::Word ) ) + m_xWordBtn->set_active( pSearchItem->GetWordOnly() ); + if ( !( nModifyFlag & ModifyFlags::Exact ) ) + m_xMatchCaseCB->set_active( pSearchItem->GetExact() ); + if ( !( nModifyFlag & ModifyFlags::Backwards ) ) + m_xReplaceBackwardsCB->set_active( bReplaceBackwards ); //adjustment to replace backwards + if ( !( nModifyFlag & ModifyFlags::Notes ) ) + m_xNotesBtn->set_active( pSearchItem->GetNotes() ); + if ( !( nModifyFlag & ModifyFlags::Selection ) ) + m_xSelectionBtn->set_active( pSearchItem->GetSelection() ); + if ( !( nModifyFlag & ModifyFlags::Regexp ) ) + m_xRegExpBtn->set_active( pSearchItem->GetRegExp() ); + if ( !( nModifyFlag & ModifyFlags::Wildcard ) ) + m_xWildcardBtn->set_active( pSearchItem->GetWildcard() ); + if ( !( nModifyFlag & ModifyFlags::Layout ) ) + m_xLayoutBtn->set_active( pSearchItem->GetPattern() ); + if (m_xNotesBtn->get_active()) + m_xLayoutBtn->set_sensitive(false); + m_xSimilarityBox->set_active( pSearchItem->IsLevenshtein() ); + if ( m_xJapOptionsCB->get_visible() ) + m_xJapOptionsCB->set_active( pSearchItem->IsUseAsianOptions() ); + m_xIncludeDiacritics->set_active( !aOpt.IsIgnoreDiacritics_CTL() ); + if ( m_xIncludeKashida->get_visible() ) + m_xIncludeKashida->set_active( !aOpt.IsIgnoreKashida_CTL() ); + ApplyTransliterationFlags_Impl( pSearchItem->GetTransliterationFlags() ); + + ShowOptionalControls_Impl(); + + if ( pSearchItem->GetAppFlag() == SvxSearchApp::CALC ) + { + m_xCalcGrid->show(); + m_xSearchFormattedCB->set_active( aOpt.IsSearchFormatted() ); + Link<weld::Toggleable&,void> aLink = LINK( this, SvxSearchDialog, FlagHdl_Impl ); + m_xCalcSearchInLB->connect_changed( LINK( this, SvxSearchDialog, LBSelectHdl_Impl ) ); + m_xRowsBtn->connect_toggled( aLink ); + m_xColumnsBtn->connect_toggled( aLink ); + m_xAllSheetsCB->connect_toggled( aLink ); + m_xSearchFormattedCB->connect_toggled( aLink ); + + ModifyFlags nModifyFlagCheck; + switch ( pSearchItem->GetCellType() ) + { + case SvxSearchCellType::FORMULA: + nModifyFlagCheck = ModifyFlags::Formulas; + break; + + case SvxSearchCellType::VALUE: + nModifyFlagCheck = ModifyFlags::Values; + break; + + case SvxSearchCellType::NOTE: + nModifyFlagCheck = ModifyFlags::CalcNotes; + break; + + default: + std::abort(); // cannot happen + } + if ( !(nModifyFlag & nModifyFlagCheck) ) + m_xCalcSearchInLB->set_active( static_cast<sal_Int32>(pSearchItem->GetCellType()) ); + + m_xWordBtn->set_label( aCalcStr.getToken( 0, '#' ) ); + + if ( pSearchItem->GetRowDirection() && + !( nModifyFlag & ModifyFlags::Rows ) ) + m_xRowsBtn->set_active(true); + else if ( !pSearchItem->GetRowDirection() && + !( nModifyFlag & ModifyFlags::Columns ) ) + m_xColumnsBtn->set_active(true); + + if ( !( nModifyFlag & ModifyFlags::AllTables ) ) + m_xAllSheetsCB->set_active( pSearchItem->IsAllTables() ); + + // only look for formatting in Writer + m_xFormatBtn->hide(); + m_xNoFormatBtn->hide(); + m_xAttributeBtn->hide(); + } + else + { + m_xSearchFormattedCB->hide(); + m_xWordBtn->set_label( aCalcStr.getToken( 1, '#' ) ); + + if ( pSearchItem->GetAppFlag() == SvxSearchApp::DRAW ) + { + m_xSearchAllBtn->hide(); + + m_xRegExpBtn->hide(); + m_xWildcardBtn->hide(); + m_xLayoutBtn->hide(); + + // only look for formatting in Writer + m_xFormatBtn->hide(); + m_xNoFormatBtn->hide(); + m_xAttributeBtn->hide(); + } + else + { + m_xWildcardBtn->hide(); /* TODO:WILDCARD do not hide for other apps if they handle it */ + + if ( !pSearchList ) + { + // Get attribute sets, if it not has been done already + const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr }; + SfxPoolItemHolder aResult(rBindings.GetDispatcher()->Execute(FID_SEARCH_SEARCHSET, SfxCallMode::SLOT, ppArgs)); + const SvxSetItem* pSrchSetItem(static_cast<const SvxSetItem*>(aResult.getItem())); + + if ( pSrchSetItem ) + InitAttrList_Impl( &pSrchSetItem->GetItemSet(), nullptr ); + + aResult = rBindings.GetDispatcher()->Execute(FID_SEARCH_REPLACESET, SfxCallMode::SLOT, ppArgs); + const SvxSetItem* pReplSetItem(static_cast<const SvxSetItem*>(aResult.getItem())); + + if ( pReplSetItem ) + InitAttrList_Impl( nullptr, &pReplSetItem->GetItemSet() ); + } + } + } + + // similarity search? + if ( !( nModifyFlag & ModifyFlags::Similarity ) ) + m_xSimilarityBox->set_active( pSearchItem->IsLevenshtein() ); + bSet = true; + + FlagHdl_Impl(*m_xSimilarityBox); + FlagHdl_Impl(*m_xJapOptionsCB); + + bool bDisableSearch = false; + SfxViewShell* pViewShell = SfxViewShell::Current(); + + if ( pViewShell ) + { + bool bText = !bSearchPattern; + + if ( pViewShell->HasSelection( bText ) ) + EnableControl_Impl(*m_xSelectionBtn); + else + { + m_xSelectionBtn->set_active( false ); + m_xSelectionBtn->set_sensitive(false); + } + } + + // Pattern Search and there were no AttrSets given + if ( bSearchPattern ) + { + SfxObjectShell* pShell = SfxObjectShell::Current(); + + if ( pShell && pShell->GetStyleSheetPool() ) + { + // Templates designed + m_xSearchTmplLB->clear(); + m_xReplaceTmplLB->clear(); + SfxStyleSheetBasePool* pStylePool = pShell->GetStyleSheetPool(); + SfxStyleSheetBase* pBase = pStylePool->First(pSearchItem->GetFamily()); + + while ( pBase ) + { + if ( pBase->IsUsed() ) + m_xSearchTmplLB->append_text( pBase->GetName() ); + m_xReplaceTmplLB->append_text( pBase->GetName() ); + pBase = pStylePool->Next(); + } + m_xSearchTmplLB->set_active_text( pSearchItem->GetSearchString() ); + m_xReplaceTmplLB->set_active_text( pSearchItem->GetReplaceString() ); + + } + m_xSearchTmplLB->show(); + + if ( bConstruct ) + // Grab focus only after creating + m_xSearchTmplLB->grab_focus(); + m_xReplaceTmplLB->show(); + m_xSearchLB->hide(); + m_xReplaceLB->hide(); + + m_xWordBtn->set_sensitive(false); + m_xRegExpBtn->set_sensitive(false); + m_xWildcardBtn->set_sensitive(false); + m_xMatchCaseCB->set_sensitive(false); + + bDisableSearch = !m_xSearchTmplLB->get_count(); + } + else + { + bool bSetSearch = !( nModifyFlag & ModifyFlags::Search ); + bool bSetReplace = !( nModifyFlag & ModifyFlags::Replace ); + + if ( !(pSearchItem->GetSearchString().isEmpty()) && bSetSearch ) + m_xSearchLB->set_entry_text( pSearchItem->GetSearchString() ); + else if (!aSearchStrings.empty()) + { + bool bAttributes = + ( ( pSearchList && pSearchList->Count() ) || + ( pReplaceList && pReplaceList->Count() ) ); + + if ( bSetSearch && !bAttributes ) + m_xSearchLB->set_entry_text(aSearchStrings[0]); + + OUString aReplaceTxt = pSearchItem->GetReplaceString(); + + if (!aReplaceStrings.empty()) + aReplaceTxt = aReplaceStrings[0]; + + if ( bSetReplace && !bAttributes ) + m_xReplaceLB->set_entry_text( aReplaceTxt ); + } + m_xSearchLB->show(); + + if ( bConstruct ) + // Grab focus only after creating + m_xSearchLB->grab_focus(); + m_xReplaceLB->show(); + m_xSearchTmplLB->hide(); + m_xReplaceTmplLB->hide(); + + EnableControl_Impl(*m_xRegExpBtn); + EnableControl_Impl(*m_xWildcardBtn); + EnableControl_Impl(*m_xMatchCaseCB); + + if ( m_xRegExpBtn->get_active() ) + m_xWordBtn->set_sensitive(false); + else + EnableControl_Impl(*m_xWordBtn); + + bDisableSearch = m_xSearchLB->get_active_text().isEmpty() && + m_xSearchAttrText->get_label().isEmpty(); + } + FocusHdl_Impl(*m_xSearchLB); + + if ( bDisableSearch ) + { + m_xSearchBtn->set_sensitive(false); + m_xBackSearchBtn->set_sensitive(false); + m_xSearchAllBtn->set_sensitive(false); + m_xReplaceBtn->set_sensitive(false); + m_xReplaceAllBtn->set_sensitive(false); + m_xComponentFrame->set_sensitive(false); + } + else + { + EnableControl_Impl(*m_xSearchBtn); + EnableControl_Impl(*m_xBackSearchBtn); + EnableControl_Impl(*m_xReplaceBtn); + if (!bWriter || !m_xNotesBtn->get_active()) + { + EnableControl_Impl(*m_xSearchAllBtn); + EnableControl_Impl(*m_xReplaceAllBtn); + } + if (bWriter && pSearchItem->GetNotes()) + { + m_xSearchAllBtn->set_sensitive(false); + m_xReplaceAllBtn->set_sensitive(false); + } + } + + if (!m_xSearchAttrText->get_label().isEmpty()) + EnableControl_Impl(*m_xNoFormatBtn); + else + m_xNoFormatBtn->set_sensitive(false); + + if ( !pSearchList ) + { + m_xAttributeBtn->set_sensitive(false); + m_xFormatBtn->set_sensitive(false); + } + + if ( m_xLayoutBtn->get_active() ) + { + pImpl->bSaveToModule = false; + TemplateHdl_Impl(*m_xLayoutBtn); + pImpl->bSaveToModule = true; + } +} + + +void SvxSearchDialog::InitAttrList_Impl( const SfxItemSet* pSSet, + const SfxItemSet* pRSet ) +{ + if ( !pSSet && !pRSet ) + return; + + if ( pImpl->pRanges.empty() && pSSet ) + pImpl->pRanges = pSSet->GetRanges(); + + bool bSetOptimalLayoutSize = false; + + // See to it that are the texts of the attributes are correct + OUString aDesc; + + if ( pSSet ) + { + pSearchList.reset(new SearchAttrItemList); + + if ( pSSet->Count() ) + { + pSearchList->Put( *pSSet ); + + m_xSearchAttrText->set_label( BuildAttrText_Impl( aDesc, true ) ); + + if ( !aDesc.isEmpty() ) + { + if (!m_xSearchAttrText->get_visible()) + { + m_xSearchAttrText->show(); + bSetOptimalLayoutSize = true; + } + bFormat |= true; + } + } + } + + if ( pRSet ) + { + pReplaceList.reset(new SearchAttrItemList); + + if ( pRSet->Count() ) + { + pReplaceList->Put( *pRSet ); + + m_xReplaceAttrText->set_label( BuildAttrText_Impl( aDesc, false ) ); + + if ( !aDesc.isEmpty() ) + { + if (!m_xReplaceAttrText->get_visible()) + { + m_xReplaceAttrText->show(); + bSetOptimalLayoutSize = true; + } + bFormat |= true; + } + } + } + + if (bSetOptimalLayoutSize) + m_xDialog->resize_to_request(); +} + +IMPL_LINK( SvxSearchDialog, LBSelectHdl_Impl, weld::ComboBox&, rCtrl, void ) +{ + ClickHdl_Impl(&rCtrl); +} + +IMPL_LINK( SvxSearchDialog, FlagHdl_Impl, weld::Toggleable&, rCtrl, void ) +{ + ClickHdl_Impl(&rCtrl); +} + +void SvxSearchDialog::ClickHdl_Impl(const weld::Widget* pCtrl) +{ + if ( pCtrl && !bSet ) + SetModifyFlag_Impl(pCtrl); + else + bSet = false; + + if (pCtrl == m_xSimilarityBox.get()) + { + bool bIsChecked = m_xSimilarityBox->get_active(); + + if ( bIsChecked ) + { + m_xSimilarityBtn->set_sensitive(true); + m_xRegExpBtn->set_active( false ); + m_xRegExpBtn->set_sensitive(false); + m_xWildcardBtn->set_active( false ); + m_xWildcardBtn->set_sensitive(false); + EnableControl_Impl(*m_xWordBtn); + + if ( m_xLayoutBtn->get_active() ) + { + EnableControl_Impl(*m_xMatchCaseCB); + m_xLayoutBtn->set_active( false ); + } + m_xRegExpBtn->set_sensitive(false); + m_xWildcardBtn->set_sensitive(false); + m_xLayoutBtn->set_sensitive(false); + m_xFormatBtn->set_sensitive(false); + m_xNoFormatBtn->set_sensitive(false); + m_xAttributeBtn->set_sensitive(false); + } + else + { + EnableControl_Impl(*m_xRegExpBtn); + EnableControl_Impl(*m_xWildcardBtn); + if (!m_xNotesBtn->get_active()) + EnableControl_Impl(*m_xLayoutBtn); + EnableControl_Impl(*m_xFormatBtn); + EnableControl_Impl(*m_xAttributeBtn); + m_xSimilarityBtn->set_sensitive(false); + } + pSearchItem->SetLevenshtein( bIsChecked ); + } + else if (pCtrl == m_xNotesBtn.get()) + { + if (m_xNotesBtn->get_active()) + { + m_xLayoutBtn->set_sensitive(false); + m_xSearchAllBtn->set_sensitive(false); + m_xReplaceAllBtn->set_sensitive(false); + } + else + { + EnableControl_Impl(*m_xLayoutBtn); + ModifyHdl_Impl(*m_xSearchLB); + } + } + else + { + if ( m_xLayoutBtn->get_active() && !bFormat ) + { + m_xWordBtn->set_active( false ); + m_xWordBtn->set_sensitive(false); + m_xRegExpBtn->set_active( false ); + m_xRegExpBtn->set_sensitive(false); + m_xWildcardBtn->set_active( false ); + m_xWildcardBtn->set_sensitive(false); + m_xMatchCaseCB->set_active( false ); + m_xMatchCaseCB->set_sensitive(false); + m_xNotesBtn->set_sensitive(false); + + if ( m_xSearchTmplLB->get_count() ) + { + EnableControl_Impl(*m_xSearchBtn); + EnableControl_Impl(*m_xBackSearchBtn); + EnableControl_Impl(*m_xSearchAllBtn); + EnableControl_Impl(*m_xReplaceBtn); + EnableControl_Impl(*m_xReplaceAllBtn); + } + } + else + { + EnableControl_Impl(*m_xRegExpBtn); + EnableControl_Impl(*m_xWildcardBtn); + EnableControl_Impl(*m_xMatchCaseCB); + EnableControl_Impl(*m_xNotesBtn); + + if ( m_xRegExpBtn->get_active() ) + { + m_xWordBtn->set_active( false ); + m_xWordBtn->set_sensitive(false); + m_xWildcardBtn->set_active( false ); + m_xWildcardBtn->set_sensitive(false); + m_xSimilarityBox->set_active( false ); + m_xSimilarityBox->set_sensitive(false); + m_xSimilarityBtn->set_sensitive(false); + } + else if ( m_xWildcardBtn->get_active() ) + { + m_xRegExpBtn->set_active( false ); + m_xRegExpBtn->set_sensitive(false); + m_xSimilarityBox->set_active( false ); + m_xSimilarityBox->set_sensitive(false); + m_xSimilarityBtn->set_sensitive(false); + } + else + { + EnableControl_Impl(*m_xWordBtn); + EnableControl_Impl(*m_xSimilarityBox); + } + + // Search-string in place? then enable Buttons + bSet = true; + ModifyHdl_Impl(*m_xSearchLB); + } + } + + if (pCtrl == m_xAllSheetsCB.get()) + { + bSet = true; + ModifyHdl_Impl(*m_xSearchLB); + } + + if (pCtrl == m_xJapOptionsCB.get()) + { + bool bEnableJapOpt = m_xJapOptionsCB->get_active(); + m_xMatchCaseCB->set_sensitive(!bEnableJapOpt ); + m_xJapMatchFullHalfWidthCB->set_sensitive(!bEnableJapOpt ); + m_xJapOptionsBtn->set_sensitive( bEnableJapOpt ); + } + + if ( pImpl->bSaveToModule ) + SaveToModule_Impl(); +} + +IMPL_LINK(SvxSearchDialog, CommandHdl_Impl, weld::Button&, rBtn, void) +{ + bool bInclusive = ( m_xLayoutBtn->get_label() == aLayoutStr ); + + if ( ( &rBtn == m_xSearchBtn.get() ) || + (&rBtn == m_xBackSearchBtn.get()) || + ( &rBtn == m_xSearchAllBtn.get() )|| + ( &rBtn == m_xReplaceBtn.get() ) || + ( &rBtn == m_xReplaceAllBtn.get() ) ) + { + if ( m_xLayoutBtn->get_active() && !bInclusive ) + { + pSearchItem->SetSearchString ( m_xSearchTmplLB->get_active_text() ); + pSearchItem->SetReplaceString( m_xReplaceTmplLB->get_active_text() ); + } + else + { + pSearchItem->SetSearchString ( m_xSearchLB->get_active_text() ); + pSearchItem->SetReplaceString( m_xReplaceLB->get_active_text() ); + + if ( &rBtn == m_xReplaceBtn.get() ) + Remember_Impl( m_xReplaceLB->get_active_text(), false ); + else + { + Remember_Impl( m_xSearchLB->get_active_text(), true ); + + if ( &rBtn == m_xReplaceAllBtn.get() ) + Remember_Impl( m_xReplaceLB->get_active_text(), false ); + } + } + + pSearchItem->SetRegExp( false ); + pSearchItem->SetWildcard( false ); + pSearchItem->SetLevenshtein( false ); + if (GetCheckBoxValue(*m_xRegExpBtn)) + pSearchItem->SetRegExp( true ); + else if (GetCheckBoxValue(*m_xWildcardBtn)) + pSearchItem->SetWildcard( true ); + else if (GetCheckBoxValue(*m_xSimilarityBox)) + pSearchItem->SetLevenshtein( true ); + + pSearchItem->SetWordOnly(GetCheckBoxValue(*m_xWordBtn)); + + bool bSetBackwards = false; + if( &rBtn == m_xBackSearchBtn.get()) + { + bSetBackwards = true; + } + else if( &rBtn == m_xReplaceBtn.get()) + { + bSetBackwards = GetCheckBoxValue(*m_xReplaceBackwardsCB); + bReplaceBackwards = GetCheckBoxValue(*m_xReplaceBackwardsCB); + } + + pSearchItem->SetBackward(bSetBackwards); + + pSearchItem->SetNotes(GetCheckBoxValue(*m_xNotesBtn)); + pSearchItem->SetPattern(GetCheckBoxValue(*m_xLayoutBtn)); + pSearchItem->SetSelection(GetCheckBoxValue(*m_xSelectionBtn)); + pSearchItem->SetUseAsianOptions(GetCheckBoxValue(*m_xJapOptionsCB)); + TransliterationFlags nFlags = GetTransliterationFlags(); + if( !pSearchItem->IsUseAsianOptions()) + nFlags &= TransliterationFlags::IGNORE_CASE | + TransliterationFlags::IGNORE_WIDTH; + if (GetNegatedCheckBoxValue(*m_xIncludeDiacritics)) + nFlags |= TransliterationFlags::IGNORE_DIACRITICS_CTL; + if (GetNegatedCheckBoxValue(*m_xIncludeKashida)) + nFlags |= TransliterationFlags::IGNORE_KASHIDA_CTL; + pSearchItem->SetTransliterationFlags( nFlags ); + + if ( !bWriter ) + { + if ( m_xCalcSearchInLB->get_active() != -1) + pSearchItem->SetCellType( static_cast<SvxSearchCellType>(m_xCalcSearchInLB->get_active()) ); + + pSearchItem->SetRowDirection( m_xRowsBtn->get_active() ); + pSearchItem->SetAllTables( m_xAllSheetsCB->get_active() ); + pSearchItem->SetSearchFormatted( m_xSearchFormattedCB->get_active() ); + } + + if ((&rBtn == m_xSearchBtn.get()) || (&rBtn == m_xBackSearchBtn.get())) + pSearchItem->SetCommand( SvxSearchCmd::FIND ); + else if ( &rBtn == m_xSearchAllBtn.get() ) + pSearchItem->SetCommand( SvxSearchCmd::FIND_ALL ); + else if ( &rBtn == m_xReplaceBtn.get() ) + pSearchItem->SetCommand( SvxSearchCmd::REPLACE ); + else if ( &rBtn == m_xReplaceAllBtn.get() ) + pSearchItem->SetCommand( SvxSearchCmd::REPLACE_ALL ); + + // when looking for templates, delete format lists + if ( !bFormat && pSearchItem->GetPattern() ) + { + if ( pSearchList ) + pSearchList->Clear(); + + if ( pReplaceList ) + pReplaceList->Clear(); + } + nModifyFlag = ModifyFlags::NONE; + const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr }; + rBindings.ExecuteSynchron( FID_SEARCH_NOW, ppArgs ); + } + else if ( &rBtn == m_xCloseBtn.get() ) + { + if ( !m_xLayoutBtn->get_active() || bInclusive ) + { + OUString aStr( m_xSearchLB->get_active_text() ); + + if ( !aStr.isEmpty() ) + Remember_Impl( aStr, true ); + aStr = m_xReplaceLB->get_active_text(); + + if ( !aStr.isEmpty() ) + Remember_Impl( aStr, false ); + } + SaveToModule_Impl(); + Close(); + } + else if (&rBtn == m_xSimilarityBtn.get()) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxSearchSimilarityDialog> pDlg(pFact->CreateSvxSearchSimilarityDialog(m_xDialog.get(), + pSearchItem->IsLEVRelaxed(), + pSearchItem->GetLEVOther(), + pSearchItem->GetLEVShorter(), + pSearchItem->GetLEVLonger() )); + if ( executeSubDialog(pDlg.get()) == RET_OK ) + { + pSearchItem->SetLEVRelaxed( pDlg->IsRelaxed() ); + pSearchItem->SetLEVOther( pDlg->GetOther() ); + pSearchItem->SetLEVShorter( pDlg->GetShorter() ); + pSearchItem->SetLEVLonger( pDlg->GetLonger() ); + SaveToModule_Impl(); + } + } + else if (&rBtn == m_xJapOptionsBtn.get()) + { + SfxItemSet aSet( SfxGetpApp()->GetPool() ); + pSearchItem->SetTransliterationFlags( GetTransliterationFlags() ); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxJSearchOptionsDialog> aDlg(pFact->CreateSvxJSearchOptionsDialog(m_xDialog.get(), aSet, + pSearchItem->GetTransliterationFlags() )); + int nRet = executeSubDialog(aDlg.get()); + if (RET_OK == nRet) //! true only if FillItemSet of SvxJSearchOptionsPage returns true + { + TransliterationFlags nFlags = aDlg->GetTransliterationFlags(); + pSearchItem->SetTransliterationFlags( nFlags ); + ApplyTransliterationFlags_Impl( nFlags ); + } + } + else if (&rBtn == m_xSearchComponent1PB.get() || &rBtn == m_xSearchComponent2PB.get()) + { + uno::Sequence < beans::PropertyValue > aArgs(2); + beans::PropertyValue* pArgs = aArgs.getArray(); + pArgs[0].Name = "SearchString"; + pArgs[0].Value <<= m_xSearchLB->get_active_text(); + pArgs[1].Name = "ParentWindow"; + pArgs[1].Value <<= m_xDialog->GetXWindow(); + if (&rBtn == m_xSearchComponent1PB.get()) + { + if ( pImpl->xCommand1Dispatch.is() ) + pImpl->xCommand1Dispatch->dispatch(pImpl->aCommand1URL, aArgs); + } + else + { + if ( pImpl->xCommand2Dispatch.is() ) + pImpl->xCommand2Dispatch->dispatch(pImpl->aCommand2URL, aArgs); + } + } +} + + +IMPL_LINK( SvxSearchDialog, ModifyHdl_Impl, weld::ComboBox&, rEd, void ) +{ + if ( !bSet ) + SetModifyFlag_Impl( &rEd ); + else + bSet = false; + + // Calc allows searching for empty cells. + bool bAllowEmptySearch = (pSearchItem->GetAppFlag() == SvxSearchApp::CALC); + + if (&rEd != m_xSearchLB.get() && &rEd != m_xReplaceLB.get()) + return; + + sal_Int32 nSrchTxtLen = m_xSearchLB->get_active_text().getLength(); + sal_Int32 nReplTxtLen = 0; + if (bAllowEmptySearch) + nReplTxtLen = m_xReplaceLB->get_active_text().getLength(); + sal_Int32 nAttrTxtLen = m_xSearchAttrText->get_label().getLength(); + + if (nSrchTxtLen || nReplTxtLen || nAttrTxtLen) + { + EnableControl_Impl(*m_xSearchBtn); + EnableControl_Impl(*m_xBackSearchBtn); + EnableControl_Impl(*m_xReplaceBtn); + if (!bWriter || !m_xNotesBtn->get_active()) + { + EnableControl_Impl(*m_xSearchAllBtn); + EnableControl_Impl(*m_xReplaceAllBtn); + } + } + else + { + m_xComponentFrame->set_sensitive(false); + m_xSearchBtn->set_sensitive(false); + m_xBackSearchBtn->set_sensitive(false); + m_xSearchAllBtn->set_sensitive(false); + m_xReplaceBtn->set_sensitive(false); + m_xReplaceAllBtn->set_sensitive(false); + } +} + +IMPL_LINK_NOARG(SvxSearchDialog, TemplateHdl_Impl, weld::Toggleable&, void) +{ + if ( pImpl->bSaveToModule ) + SaveToModule_Impl(); + + if ( bFormat ) + return; + OUString sDesc; + + if ( m_xLayoutBtn->get_active() ) + { + if ( !pFamilyController ) + { + sal_uInt16 nId = 0; + + // Enable templates controller + switch ( pSearchItem->GetFamily() ) + { + case SfxStyleFamily::Char: + nId = SID_STYLE_FAMILY1; break; + + case SfxStyleFamily::Para: + nId = SID_STYLE_FAMILY2; break; + + case SfxStyleFamily::Frame: + nId = SID_STYLE_FAMILY3; break; + + case SfxStyleFamily::Page: + nId = SID_STYLE_FAMILY4; break; + + case SfxStyleFamily::All: + break; + + default: + OSL_FAIL( "StyleSheetFamily was changed?" ); + } + + rBindings.EnterRegistrations(); + pFamilyController.reset( + new SvxSearchController( nId, rBindings, *this ) ); + rBindings.LeaveRegistrations(); + m_xSearchTmplLB->clear(); + m_xReplaceTmplLB->clear(); + + m_xSearchTmplLB->show(); + m_xReplaceTmplLB->show(); + m_xSearchLB->hide(); + m_xReplaceLB->hide(); + + m_xSearchAttrText->set_label( sDesc ); + m_xReplaceAttrText->set_label( sDesc ); + + if(!sDesc.isEmpty()) + { + if (!m_xReplaceAttrText->get_visible() || !m_xReplaceAttrText->get_visible()) + { + m_xSearchAttrText->show(); + m_xReplaceAttrText->show(); + m_xDialog->resize_to_request(); + } + } + } + m_xFormatBtn->set_sensitive(false); + m_xNoFormatBtn->set_sensitive(false); + m_xAttributeBtn->set_sensitive(false); + m_xSimilarityBox->set_sensitive(false); + m_xSimilarityBtn->set_sensitive(false); + } + else + { + // Disable templates controller + rBindings.EnterRegistrations(); + pFamilyController.reset(); + rBindings.LeaveRegistrations(); + + m_xSearchLB->show(); + m_xReplaceLB->show(); + m_xSearchTmplLB->hide(); + m_xReplaceTmplLB->hide(); + + m_xSearchAttrText->set_label( BuildAttrText_Impl( sDesc, true ) ); + m_xReplaceAttrText->set_label( BuildAttrText_Impl( sDesc, false ) ); + + if(!sDesc.isEmpty()) + { + if (!m_xReplaceAttrText->get_visible() || !m_xReplaceAttrText->get_visible()) + { + m_xSearchAttrText->show(); + m_xReplaceAttrText->show(); + m_xDialog->resize_to_request(); + } + } + + EnableControl_Impl(*m_xFormatBtn); + EnableControl_Impl(*m_xAttributeBtn); + EnableControl_Impl(*m_xSimilarityBox); + + FocusHdl_Impl( bSearch ? *m_xSearchLB : *m_xReplaceLB ); + } + bSet = true; + pImpl->bSaveToModule = false; + FlagHdl_Impl(*m_xLayoutBtn); + pImpl->bSaveToModule = true; +} + +void SvxSearchDialog::Remember_Impl( const OUString &rStr, bool _bSearch ) +{ + if ( rStr.isEmpty() ) + return; + + std::vector<OUString>* pArr = _bSearch ? &aSearchStrings : &aReplaceStrings; + weld::ComboBox* pListBox = _bSearch ? m_xSearchLB.get() : m_xReplaceLB.get(); + + // tdf#154818 - rearrange the search items + const auto nPos = pListBox->find_text(rStr); + if (nPos != -1) + { + pListBox->remove(nPos); + pArr->erase(pArr->begin() + nPos); + } + else if (pListBox->get_count() >= nRememberSize) + { + // delete oldest entry at maximum occupancy (ListBox and Array) + pListBox->remove(nRememberSize - 1); + pArr->erase(pArr->begin() + nRememberSize - 1); + } + + pArr->insert(pArr->begin(), rStr); + pListBox->insert_text(0, rStr); +} + +void SvxSearchDialog::TemplatesChanged_Impl( SfxStyleSheetBasePool& rPool ) +{ + OUString aOldSrch( m_xSearchTmplLB->get_active_text() ); + OUString aOldRepl( m_xReplaceTmplLB->get_active_text() ); + m_xSearchTmplLB->clear(); + m_xReplaceTmplLB->clear(); + m_xSearchTmplLB->freeze(); + m_xReplaceTmplLB->freeze(); + SfxStyleSheetBase* pBase = rPool.First(pSearchItem->GetFamily()); + + while ( pBase ) + { + if ( pBase->IsUsed() ) + m_xSearchTmplLB->append_text( pBase->GetName() ); + m_xReplaceTmplLB->append_text( pBase->GetName() ); + pBase = rPool.Next(); + } + m_xSearchTmplLB->thaw(); + m_xReplaceTmplLB->thaw(); + m_xSearchTmplLB->set_active(0); + + if ( !aOldSrch.isEmpty() ) + m_xSearchTmplLB->set_active_text( aOldSrch ); + m_xReplaceTmplLB->set_active(0); + + if ( !aOldRepl.isEmpty() ) + m_xReplaceTmplLB->set_active_text( aOldRepl ); + + if ( m_xSearchTmplLB->get_count() ) + { + EnableControl_Impl(*m_xSearchBtn); + EnableControl_Impl(*m_xBackSearchBtn); + EnableControl_Impl(*m_xSearchAllBtn); + EnableControl_Impl(*m_xReplaceBtn); + EnableControl_Impl(*m_xReplaceAllBtn); + } +} + + +void SvxSearchDialog::EnableControls_Impl( const SearchOptionFlags nFlags ) +{ + if ( nFlags == nOptions ) + return; + else + nOptions = nFlags; + + bool bNoSearch = true; + + bool bEnableSearch = bool( SearchOptionFlags::SEARCH & nOptions ); + m_xSearchBtn->set_sensitive(bEnableSearch); + m_xBackSearchBtn->set_sensitive(bEnableSearch); + + if( bEnableSearch ) + bNoSearch = false; + + + if ( SearchOptionFlags::SEARCHALL & nOptions ) + { + m_xSearchAllBtn->set_sensitive(true); + bNoSearch = false; + } + else + m_xSearchAllBtn->set_sensitive(false); + if ( SearchOptionFlags::REPLACE & nOptions ) + { + m_xReplaceBtn->set_sensitive(true); + m_xReplaceFrame->set_sensitive(true); + m_xReplaceLB->set_sensitive(true); + m_xReplaceTmplLB->set_sensitive(true); + bNoSearch = false; + } + else + { + m_xReplaceBtn->set_sensitive(false); + m_xReplaceFrame->set_sensitive(false); + m_xReplaceLB->set_sensitive(false); + m_xReplaceTmplLB->set_sensitive(false); + } + if ( SearchOptionFlags::REPLACE_ALL & nOptions ) + { + m_xReplaceAllBtn->set_sensitive(true); + bNoSearch = false; + } + else + m_xReplaceAllBtn->set_sensitive(false); + m_xComponentFrame->set_sensitive(!bNoSearch); + m_xSearchBtn->set_sensitive( !bNoSearch ); + m_xBackSearchBtn->set_sensitive( !bNoSearch ); + m_xSearchFrame->set_sensitive( !bNoSearch ); + m_xSearchLB->set_sensitive( !bNoSearch ); + m_xNotesBtn->set_sensitive(true); + + if ( SearchOptionFlags::WHOLE_WORDS & nOptions ) + m_xWordBtn->set_sensitive(true); + else + m_xWordBtn->set_sensitive(false); + if ( SearchOptionFlags::BACKWARDS & nOptions ) + { + m_xBackSearchBtn->set_sensitive(true); + m_xReplaceBackwardsCB->set_sensitive(true); + } + else + { + m_xBackSearchBtn->set_sensitive(false); + m_xReplaceBackwardsCB->set_sensitive(false); + } + if ( SearchOptionFlags::REG_EXP & nOptions ) + m_xRegExpBtn->set_sensitive(true); + else + m_xRegExpBtn->set_sensitive(false); + if ( SearchOptionFlags::WILDCARD & nOptions ) + m_xWildcardBtn->set_sensitive(true); + else + m_xWildcardBtn->set_sensitive(false); + if ( SearchOptionFlags::EXACT & nOptions ) + m_xMatchCaseCB->set_sensitive(true); + else + m_xMatchCaseCB->set_sensitive(false); + if ( SearchOptionFlags::SELECTION & nOptions ) + m_xSelectionBtn->set_sensitive(true); + else + m_xSelectionBtn->set_sensitive(false); + if ( SearchOptionFlags::FAMILIES & nOptions ) + m_xLayoutBtn->set_sensitive(true); + else + m_xLayoutBtn->set_sensitive(false); + if ( SearchOptionFlags::FORMAT & nOptions ) + { + m_xAttributeBtn->set_sensitive(true); + m_xFormatBtn->set_sensitive(true); + m_xNoFormatBtn->set_sensitive(true); + } + else + { + m_xAttributeBtn->set_sensitive(false); + m_xFormatBtn->set_sensitive(false); + m_xNoFormatBtn->set_sensitive(false); + } + + if ( SearchOptionFlags::SIMILARITY & nOptions ) + { + m_xSimilarityBox->set_sensitive(true); + m_xSimilarityBtn->set_sensitive(true); + } + else + { + m_xSimilarityBox->set_sensitive(false); + m_xSimilarityBtn->set_sensitive(false); + } + + if ( pSearchItem ) + { + Init_Impl( pSearchItem->GetPattern() && + ( !pSearchList || !pSearchList->Count() ) ); + if (SvxSearchDialog::IsOtherOptionsExpanded()) + m_xOtherOptionsExpander->set_expanded(true); + } +} + +void SvxSearchDialog::EnableControl_Impl(const weld::Widget& rCtrl) +{ + if (m_xSearchBtn.get() == &rCtrl && ( SearchOptionFlags::SEARCH & nOptions ) ) + { + m_xComponentFrame->set_sensitive(true); + m_xSearchBtn->set_sensitive(true); + return; + } + if ( m_xSearchAllBtn.get() == &rCtrl && + ( SearchOptionFlags::SEARCHALL & nOptions ) ) + { + m_xSearchAllBtn->set_sensitive(true); + return; + } + if ( m_xReplaceBtn.get() == &rCtrl && ( SearchOptionFlags::REPLACE & nOptions ) ) + { + m_xReplaceBtn->set_sensitive(true); + return; + } + if ( m_xReplaceAllBtn.get() == &rCtrl && + ( SearchOptionFlags::REPLACE_ALL & nOptions ) ) + { + m_xReplaceAllBtn->set_sensitive(true); + return; + } + if ( m_xWordBtn.get() == &rCtrl && ( SearchOptionFlags::WHOLE_WORDS & nOptions ) ) + { + m_xWordBtn->set_sensitive(true); + return; + } + if ( SearchOptionFlags::BACKWARDS & nOptions ) + { + if( m_xBackSearchBtn.get() == &rCtrl ) + { + m_xBackSearchBtn->set_sensitive(true); + return; + } + else if ( m_xReplaceBackwardsCB.get() == &rCtrl ) + { + m_xReplaceBackwardsCB->set_sensitive(true); + return; + } + } + if (m_xNotesBtn.get() == &rCtrl) + { + m_xNotesBtn->set_sensitive(true); + return; + } + if ( m_xRegExpBtn.get() == &rCtrl && ( SearchOptionFlags::REG_EXP & nOptions ) + && !m_xSimilarityBox->get_active() && !m_xWildcardBtn->get_active()) + { + m_xRegExpBtn->set_sensitive(true); + return; + } + if ( m_xWildcardBtn.get() == &rCtrl && ( SearchOptionFlags::WILDCARD & nOptions ) + && !m_xSimilarityBox->get_active() && !m_xRegExpBtn->get_active()) + { + m_xWildcardBtn->set_sensitive(true); + return; + } + if ( m_xMatchCaseCB.get() == &rCtrl && ( SearchOptionFlags::EXACT & nOptions ) ) + { + if (!m_xJapOptionsCB->get_active()) + m_xMatchCaseCB->set_sensitive(true); + return; + } + if ( m_xSelectionBtn.get() == &rCtrl && ( SearchOptionFlags::SELECTION & nOptions ) ) + { + m_xSelectionBtn->set_sensitive(true); + return; + } + if ( m_xLayoutBtn.get() == &rCtrl && ( SearchOptionFlags::FAMILIES & nOptions ) ) + { + m_xLayoutBtn->set_sensitive(true); + return; + } + if ( m_xAttributeBtn.get() == &rCtrl + && ( SearchOptionFlags::FORMAT & nOptions ) + && pSearchList ) + { + m_xAttributeBtn->set_sensitive( pImpl->bFocusOnSearch ); + } + if ( m_xFormatBtn.get() == &rCtrl && ( SearchOptionFlags::FORMAT & nOptions ) ) + { + m_xFormatBtn->set_sensitive(true); + return; + } + if ( m_xNoFormatBtn.get() == &rCtrl && ( SearchOptionFlags::FORMAT & nOptions ) ) + { + m_xNoFormatBtn->set_sensitive(true); + return; + } + if ( m_xSimilarityBox.get() == &rCtrl && ( SearchOptionFlags::SIMILARITY & nOptions ) + && !m_xRegExpBtn->get_active() && !m_xWildcardBtn->get_active()) + { + m_xSimilarityBox->set_sensitive(true); + + if ( m_xSimilarityBox->get_active() ) + m_xSimilarityBtn->set_sensitive(true); + } +} + +void SvxSearchDialog::SetItem_Impl( const SvxSearchItem* pItem ) +{ + //TODO: save pItem and process later if m_executingSubDialog? + if ( pItem && !m_executingSubDialog ) + { + pSearchItem.reset(pItem->Clone()); + Init_Impl( pSearchItem->GetPattern() && + ( !pSearchList || !pSearchList->Count() ) ); + } +} + +IMPL_LINK(SvxSearchDialog, FocusHdl_Impl, weld::Widget&, rControl, void) +{ + sal_Int32 nTxtLen = m_xSearchAttrText->get_label().getLength(); + weld::Widget* pCtrl = &rControl; + if (pCtrl == m_xSearchLB.get()) + { + if (pCtrl->has_focus()) + pImpl->bFocusOnSearch = true; + pCtrl = m_xSearchLB.get(); + bSearch = true; + + if( nTxtLen ) + EnableControl_Impl(*m_xNoFormatBtn); + else + m_xNoFormatBtn->set_sensitive(false); + EnableControl_Impl(*m_xAttributeBtn); + } + else + { + pImpl->bFocusOnSearch = false; + pCtrl = m_xReplaceLB.get(); + bSearch = false; + + if (!m_xReplaceAttrText->get_label().isEmpty()) + EnableControl_Impl(*m_xNoFormatBtn); + else + m_xNoFormatBtn->set_sensitive(false); + m_xAttributeBtn->set_sensitive(false); + } + bSet = true; + + weld::ComboBox &rComboBox = dynamic_cast<weld::ComboBox&>(*pCtrl); + rComboBox.select_entry_region(0, -1); + ModifyHdl_Impl(rComboBox); + + if (bFormat && nTxtLen) + m_xLayoutBtn->set_label(aLayoutStr); + else + { + SvtModuleOptions::EFactory eFactory = getModule(rBindings); + bool bWriterApp = + eFactory == SvtModuleOptions::EFactory::WRITER || + eFactory == SvtModuleOptions::EFactory::WRITERWEB || + eFactory == SvtModuleOptions::EFactory::WRITERGLOBAL; + bool bCalcApp = eFactory == SvtModuleOptions::EFactory::CALC; + + if (bWriterApp) + m_xLayoutBtn->set_label(aLayoutWriterStr); + else + { + if (bCalcApp) + m_xLayoutBtn->set_label(aLayoutCalcStr); + else + m_xLayoutBtn->set_label(aStylesStr); + } + } +} + +IMPL_LINK_NOARG(SvxSearchDialog, LoseFocusHdl_Impl, weld::Widget&, void) +{ + SaveToModule_Impl(); +} + +IMPL_LINK_NOARG(SvxSearchDialog, FormatHdl_Impl, weld::Button&, void) +{ + SfxObjectShell* pSh = SfxObjectShell::Current(); + + DBG_ASSERT( pSh, "no DocShell" ); + + if ( !pSh || pImpl->pRanges.empty() ) + return; + + SfxItemPool& rPool = pSh->GetPool(); + SfxItemSet aSet(rPool, pImpl->pRanges); + + aSet.MergeRange(SID_ATTR_PARA_MODEL, SID_ATTR_PARA_MODEL); + + sal_uInt16 nBrushWhich = pSh->GetPool().GetWhich(SID_ATTR_BRUSH); + aSet.MergeRange(nBrushWhich, nBrushWhich); + + aSet.MergeRange(XATTR_FILL_FIRST, XATTR_FILL_LAST); + + OUString aTxt; + + aSet.InvalidateAllItems(); + aSet.Put(SvxBrushItem(nBrushWhich)); + + if ( bSearch ) + { + aTxt = SvxResId( RID_SVXSTR_SEARCH ); + pSearchList->Get( aSet ); + } + else + { + aTxt = SvxResId( RID_SVXSTR_REPLACE ); + pReplaceList->Get( aSet ); + } + aSet.DisableItem(SID_ATTR_PARA_MODEL); + aSet.DisableItem(rPool.GetWhich(SID_ATTR_PARA_PAGEBREAK)); + aSet.DisableItem(rPool.GetWhich(SID_ATTR_PARA_KEEP)); + + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateTabItemDialog(m_xDialog.get(), aSet)); + pDlg->SetText( aTxt ); + + if ( executeSubDialog(pDlg.get()) != RET_OK ) + return; + + DBG_ASSERT( pDlg->GetOutputItemSet(), "invalid Output-Set" ); + SfxItemSet aOutSet( *pDlg->GetOutputItemSet() ); + + SearchAttrItemList* pList = bSearch ? pSearchList.get() : pReplaceList.get(); + + const SfxPoolItem* pItem; + for( sal_uInt16 n = 0; n < pList->Count(); ++n ) + { + SearchAttrInfo* pAItem = &pList->GetObject(n); + if( !IsInvalidItem( pAItem->pItemPtr ) && + SfxItemState::SET == aOutSet.GetItemState( + pAItem->pItemPtr->Which(), false, &pItem ) ) + { + delete pAItem->pItemPtr; + pAItem->pItemPtr = pItem->Clone(); + aOutSet.ClearItem( pAItem->pItemPtr->Which() ); + } + } + + if( aOutSet.Count() ) + pList->Put( aOutSet ); + + PaintAttrText_Impl(); // Set AttributeText in GroupBox +} + +IMPL_LINK_NOARG(SvxSearchDialog, NoFormatHdl_Impl, weld::Button&, void) +{ + SvtModuleOptions::EFactory eFactory = getModule(rBindings); + bool bWriterApp = + eFactory == SvtModuleOptions::EFactory::WRITER || + eFactory == SvtModuleOptions::EFactory::WRITERWEB || + eFactory == SvtModuleOptions::EFactory::WRITERGLOBAL; + bool bCalcApp = eFactory == SvtModuleOptions::EFactory::CALC; + + if (bCalcApp) + m_xLayoutBtn->set_label( aLayoutCalcStr ); + else + { + if (bWriterApp) + m_xLayoutBtn->set_label( aLayoutWriterStr); + else + m_xLayoutBtn->set_label( aStylesStr ); + } + + bFormat = false; + m_xLayoutBtn->set_active( false ); + + bool bSetOptimalLayoutSize = false; + + if ( bSearch ) + { + pSearchList->Clear(); + m_xSearchAttrText->set_label( "" ); + if (m_xSearchAttrText->get_visible()) + { + m_xSearchAttrText->hide(); + bSetOptimalLayoutSize = true; + } + } + else + { + pReplaceList->Clear(); + m_xReplaceAttrText->set_label( "" ); + if (m_xReplaceAttrText->get_visible()) + { + m_xReplaceAttrText->hide(); + bSetOptimalLayoutSize = true; + } + } + + if (bSetOptimalLayoutSize) + m_xDialog->resize_to_request(); + + pImpl->bSaveToModule = false; + TemplateHdl_Impl(*m_xLayoutBtn); + pImpl->bSaveToModule = true; + m_xNoFormatBtn->set_sensitive(false); +} + +IMPL_LINK_NOARG(SvxSearchDialog, AttributeHdl_Impl, weld::Button&, void) +{ + if ( !pSearchList || pImpl->pRanges.empty() ) + return; + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSvxSearchAttributeDialog(m_xDialog.get(), *pSearchList, pImpl->pRanges)); + executeSubDialog(pDlg.get()); + PaintAttrText_Impl(); +} + +IMPL_LINK( SvxSearchDialog, TimeoutHdl_Impl, Timer *, pTimer, void ) +{ + SfxViewShell* pViewShell = SfxViewShell::Current(); + + if ( pViewShell ) + { + if ( pViewShell->HasSelection( m_xSearchLB->get_visible() ) ) + EnableControl_Impl(*m_xSelectionBtn); + else + { + m_xSelectionBtn->set_active( false ); + m_xSelectionBtn->set_sensitive(false); + } + } + + pTimer->Start(); +} + +OUString& SvxSearchDialog::BuildAttrText_Impl( OUString& rStr, + bool bSrchFlag ) const +{ + rStr.clear(); + + SfxObjectShell* pSh = SfxObjectShell::Current(); + DBG_ASSERT( pSh, "no DocShell" ); + + if ( !pSh ) + return rStr; + + SfxItemPool& rPool = pSh->GetPool(); + SearchAttrItemList* pList = bSrchFlag ? pSearchList.get() : pReplaceList.get(); + + if ( !pList ) + return rStr; + + // Metric query + MapUnit eMapUnit = MapUnit::MapCM; + FieldUnit eFieldUnit = pSh->GetModule()->GetFieldUnit(); + switch ( eFieldUnit ) + { + case FieldUnit::MM: eMapUnit = MapUnit::MapMM; break; + case FieldUnit::CM: + case FieldUnit::M: + case FieldUnit::KM: eMapUnit = MapUnit::MapCM; break; + case FieldUnit::TWIP: eMapUnit = MapUnit::MapTwip; break; + case FieldUnit::POINT: + case FieldUnit::PICA: eMapUnit = MapUnit::MapPoint; break; + case FieldUnit::INCH: + case FieldUnit::FOOT: + case FieldUnit::MILE: eMapUnit = MapUnit::MapInch; break; + case FieldUnit::MM_100TH: eMapUnit = MapUnit::Map100thMM; break; + default: ;//prevent warning + } + + IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag()); + for ( sal_uInt16 i = 0; i < pList->Count(); ++i ) + { + const SearchAttrInfo& rItem = pList->GetObject(i); + + if ( !rStr.isEmpty() ) + rStr += ", "; + + if ( !IsInvalidItem( rItem.pItemPtr ) ) + { + OUString aStr; + rPool.GetPresentation(*rItem.pItemPtr, eMapUnit, aStr, aIntlWrapper); + if (aStr.isEmpty()) + { + if (rStr.endsWith(", ")) + rStr = rStr.copy(0, rStr.lastIndexOf(",")); + } + else + rStr += aStr; + } + else if ( rItem.nSlot == SID_ATTR_BRUSH_CHAR ) + { + // Special treatment for text background + rStr += SvxResId( RID_SVXITEMS_BRUSH_CHAR ); + } + else + { + sal_uInt32 nId = SvxAttrNameTable::FindIndex(rItem.nSlot); + if ( RESARRAY_INDEX_NOTFOUND != nId ) + rStr += SvxAttrNameTable::GetString(nId); + } + } + return rStr; +} + + +void SvxSearchDialog::PaintAttrText_Impl() +{ + OUString aDesc; + BuildAttrText_Impl( aDesc, bSearch ); + + if ( !bFormat && !aDesc.isEmpty() ) + bFormat = true; + + bool bSetOptimalLayoutSize = false; + + if ( bSearch ) + { + m_xSearchAttrText->set_label( aDesc ); + if (!aDesc.isEmpty() && !m_xSearchAttrText->get_visible()) + { + m_xSearchAttrText->show(); + bSetOptimalLayoutSize = true; + } + + FocusHdl_Impl(*m_xSearchLB); + } + else + { + m_xReplaceAttrText->set_label( aDesc ); + if (!aDesc.isEmpty() && !m_xReplaceAttrText->get_visible()) + { + m_xReplaceAttrText->show(); + bSetOptimalLayoutSize = true; + } + + FocusHdl_Impl(*m_xReplaceLB); + } + + if (bSetOptimalLayoutSize) + m_xDialog->resize_to_request(); +} + +void SvxSearchDialog::SetModifyFlag_Impl( const weld::Widget* pCtrl ) +{ + if (m_xSearchLB.get() == pCtrl) + { + nModifyFlag |= ModifyFlags::Search; + m_xSearchLB->set_entry_message_type(weld::EntryMessageType::Normal); + if (!SvxSearchDialogWrapper::GetSearchLabel().isEmpty()) + SvxSearchDialogWrapper::SetSearchLabel(""); + } + else if ( m_xReplaceLB.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Replace; + else if ( m_xWordBtn.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Word; + else if ( m_xMatchCaseCB.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Exact; + else if ( m_xReplaceBackwardsCB.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Backwards; + else if ( m_xNotesBtn.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Notes; + else if ( m_xSelectionBtn.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Selection; + else if ( m_xRegExpBtn.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Regexp; + else if ( m_xWildcardBtn.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Wildcard; + else if ( m_xLayoutBtn.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Layout; + else if ( m_xSimilarityBox.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Similarity; + else if ( m_xCalcSearchInLB.get() == pCtrl ) + { + nModifyFlag |= ModifyFlags::Formulas; + nModifyFlag |= ModifyFlags::Values; + nModifyFlag |= ModifyFlags::CalcNotes; + } + else if ( m_xRowsBtn.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Rows; + else if ( m_xColumnsBtn.get() == pCtrl ) + nModifyFlag |= ModifyFlags::Columns; + else if ( m_xAllSheetsCB.get() == pCtrl ) + nModifyFlag |= ModifyFlags::AllTables; +} + +void SvxSearchDialog::SaveToModule_Impl() +{ + if ( !pSearchItem ) + return; + + if ( m_xLayoutBtn->get_active() ) + { + pSearchItem->SetSearchString ( m_xSearchTmplLB->get_active_text() ); + pSearchItem->SetReplaceString( m_xReplaceTmplLB->get_active_text() ); + } + else + { + pSearchItem->SetSearchString ( m_xSearchLB->get_active_text() ); + pSearchItem->SetReplaceString( m_xReplaceLB->get_active_text() ); + Remember_Impl( m_xSearchLB->get_active_text(), true ); + } + + pSearchItem->SetRegExp( false ); + pSearchItem->SetWildcard( false ); + pSearchItem->SetLevenshtein( false ); + if (GetCheckBoxValue(*m_xRegExpBtn)) + pSearchItem->SetRegExp( true ); + else if (GetCheckBoxValue(*m_xWildcardBtn)) + pSearchItem->SetWildcard( true ); + else if (GetCheckBoxValue(*m_xSimilarityBox)) + pSearchItem->SetLevenshtein( true ); + + pSearchItem->SetWordOnly(GetCheckBoxValue(*m_xWordBtn)); + pSearchItem->SetBackward(GetCheckBoxValue(*m_xReplaceBackwardsCB)); + pSearchItem->SetNotes(GetCheckBoxValue(*m_xNotesBtn)); + pSearchItem->SetPattern(GetCheckBoxValue(*m_xLayoutBtn)); + pSearchItem->SetSelection(GetCheckBoxValue(*m_xSelectionBtn)); + pSearchItem->SetUseAsianOptions(GetCheckBoxValue(*m_xJapOptionsCB)); + + SvtSearchOptions aOpt; + aOpt.SetIgnoreDiacritics_CTL(GetNegatedCheckBoxValue(*m_xIncludeDiacritics)); + aOpt.SetIgnoreKashida_CTL(GetNegatedCheckBoxValue(*m_xIncludeKashida)); + aOpt.Commit(); + + TransliterationFlags nFlags = GetTransliterationFlags(); + if( !pSearchItem->IsUseAsianOptions()) + nFlags &= TransliterationFlags::IGNORE_CASE | + TransliterationFlags::IGNORE_WIDTH; + if (GetNegatedCheckBoxValue(*m_xIncludeDiacritics)) + nFlags |= TransliterationFlags::IGNORE_DIACRITICS_CTL; + if (GetNegatedCheckBoxValue(*m_xIncludeKashida)) + nFlags |= TransliterationFlags::IGNORE_KASHIDA_CTL; + pSearchItem->SetTransliterationFlags( nFlags ); + + if ( !bWriter ) + { + if (m_xCalcSearchInLB->get_active() != -1) + pSearchItem->SetCellType( static_cast<SvxSearchCellType>(m_xCalcSearchInLB->get_active()) ); + + pSearchItem->SetRowDirection( m_xRowsBtn->get_active() ); + pSearchItem->SetAllTables( m_xAllSheetsCB->get_active() ); + pSearchItem->SetSearchFormatted( m_xSearchFormattedCB->get_active() ); + } + + pSearchItem->SetCommand( SvxSearchCmd::FIND ); + nModifyFlag = ModifyFlags::NONE; + const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr }; + rBindings.GetDispatcher()->Execute( SID_SEARCH_ITEM, SfxCallMode::SLOT, ppArgs ); +} + +short SvxSearchDialog::executeSubDialog(VclAbstractDialog * dialog) { + assert(!m_executingSubDialog); + comphelper::ScopeGuard g([this] { m_executingSubDialog = false; }); + m_executingSubDialog = true; + return dialog->Execute(); +} + +SFX_IMPL_CHILDWINDOW_WITHID(SvxSearchDialogWrapper, SID_SEARCH_DLG); + + +SvxSearchDialogWrapper::SvxSearchDialogWrapper( vcl::Window* _pParent, sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo const * pInfo ) + : SfxChildWindow( _pParent, nId ) + , dialog(std::make_shared<SvxSearchDialog>(_pParent->GetFrameWeld(), this, *pBindings)) +{ + SetController(dialog); + dialog->Initialize( pInfo ); + + pBindings->Update( SID_SEARCH_ITEM ); + pBindings->Update( SID_SEARCH_OPTIONS ); + pBindings->Update( SID_SEARCH_SEARCHSET ); + pBindings->Update( SID_SEARCH_REPLACESET ); + dialog->bConstruct = false; +} + +SvxSearchDialogWrapper::~SvxSearchDialogWrapper () +{ +} + + +SfxChildWinInfo SvxSearchDialogWrapper::GetInfo() const +{ + SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); + aInfo.bVisible = false; + return aInfo; +} + +static void lcl_SetSearchLabelWindow(const OUString& rStr, SfxViewFrame& rViewFrame) +{ + css::uno::Reference< css::beans::XPropertySet > xPropSet( + rViewFrame.GetFrame().GetFrameInterface(), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + css::uno::Reference< css::ui::XUIElement > xUIElement = + xLayoutManager->getElement("private:resource/toolbar/findbar"); + if (!xUIElement.is()) + return; + css::uno::Reference< css::awt::XWindow > xWindow( + xUIElement->getRealInterface(), css::uno::UNO_QUERY_THROW); + VclPtr< ToolBox > pToolBox = static_cast<ToolBox*>( VCLUnoHelper::GetWindow(xWindow) ); + for (ToolBox::ImplToolItems::size_type i = 0; pToolBox && i < pToolBox->GetItemCount(); ++i) + { + ToolBoxItemId id = pToolBox->GetItemId(i); + if (pToolBox->GetItemCommand(id) == ".uno:SearchLabel") + { + LabelItemWindow* pSearchLabel = dynamic_cast<LabelItemWindow*>(pToolBox->GetItemWindow(id)); + assert(pSearchLabel); + pSearchLabel->set_label(rStr, LabelItemWindowType::Info); + pSearchLabel->SetOptimalSize(); + } + } + xLayoutManager->doLayout(); + pToolBox->Resize(); +} + +OUString SvxSearchDialogWrapper::GetSearchLabel() +{ + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + if (!pViewFrame) + return OUString(); + + css::uno::Reference< css::beans::XPropertySet > xPropSet( + pViewFrame->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager; + if (!xLayoutManager.is()) + return OUString(); + css::uno::Reference< css::ui::XUIElement > xUIElement = + xLayoutManager->getElement("private:resource/toolbar/findbar"); + if (!xUIElement.is()) + return OUString(); + css::uno::Reference< css::awt::XWindow > xWindow( + xUIElement->getRealInterface(), css::uno::UNO_QUERY_THROW); + VclPtr< ToolBox > pToolBox = static_cast<ToolBox*>( VCLUnoHelper::GetWindow(xWindow) ); + for (ToolBox::ImplToolItems::size_type i = 0; pToolBox && i < pToolBox->GetItemCount(); ++i) + { + ToolBoxItemId id = pToolBox->GetItemId(i); + if (pToolBox->GetItemCommand(id) == ".uno:SearchLabel") + { + LabelItemWindow* pSearchLabel = dynamic_cast<LabelItemWindow*>(pToolBox->GetItemWindow(id)); + return pSearchLabel ? pSearchLabel->get_label() : OUString(); + } + } + return OUString(); +} + +void SvxSearchDialogWrapper::SetSearchLabel(const SearchLabel& rSL) +{ + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + if (!pViewFrame) + return; + + OUString sStr; + if (rSL == SearchLabel::End) + sStr = SvxResId(RID_SVXSTR_SEARCH_END); + else if (rSL == SearchLabel::Start) + sStr = SvxResId(RID_SVXSTR_SEARCH_START); + else if (rSL == SearchLabel::EndWrapped) + sStr = SvxResId(RID_SVXSTR_SEARCH_END_WRAPPED); + else if (rSL == SearchLabel::StartWrapped) + sStr = SvxResId(RID_SVXSTR_SEARCH_START_WRAPPED); + else if (rSL == SearchLabel::EndSheet) + sStr = SvxResId(RID_SVXSTR_SEARCH_END_SHEET); + else if (rSL == SearchLabel::NotFound) + sStr = SvxResId(RID_SVXSTR_SEARCH_NOT_FOUND); + else if (rSL == SearchLabel::NavElementNotFound) + sStr = SvxResId(RID_SVXSTR_SEARCH_NAV_ELEMENT_NOT_FOUND); + else if (rSL == SearchLabel::ReminderEndWrapped) + sStr = SvxResId(RID_SVXSTR_SEARCH_REMINDER_END_WRAPPED); + else if (rSL == SearchLabel::ReminderStartWrapped) + sStr = SvxResId(RID_SVXSTR_SEARCH_REMINDER_START_WRAPPED); + + lcl_SetSearchLabelWindow(sStr, *pViewFrame); + + if (SvxSearchDialogWrapper *pWrp = static_cast<SvxSearchDialogWrapper*>( pViewFrame-> + GetChildWindow( SvxSearchDialogWrapper::GetChildWindowId() ))) + pWrp->getDialog()->SetSearchLabel(sStr); +} + +void SvxSearchDialogWrapper::SetSearchLabel(const OUString& sStr) +{ + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + if (!pViewFrame) + return; + + lcl_SetSearchLabelWindow(sStr, *pViewFrame); + if (SvxSearchDialogWrapper *pWrp = static_cast<SvxSearchDialogWrapper*>( pViewFrame-> + GetChildWindow( SvxSearchDialogWrapper::GetChildWindowId() ))) + pWrp->getDialog()->SetSearchLabel(sStr); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/strarray.cxx b/svx/source/dialog/strarray.cxx new file mode 100644 index 0000000000..28bf16a435 --- /dev/null +++ b/svx/source/dialog/strarray.cxx @@ -0,0 +1,88 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dialmgr.hxx> +#include <svx/strarray.hxx> +#include <tools/resary.hxx> +#include <svx/svxitems.hrc> +#include <fieldunit.hrc> +#include <numberingtype.hrc> + +sal_uInt32 SvxFieldUnitTable::Count() { return std::size(RID_SVXSTR_FIELDUNIT_TABLE); } + +OUString SvxFieldUnitTable::GetString(sal_uInt32 nPos) +{ + if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count()) + return SvxResId(RID_SVXSTR_FIELDUNIT_TABLE[nPos].first); + return OUString(); +} + +FieldUnit SvxFieldUnitTable::GetValue(sal_uInt32 nPos) +{ + if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count()) + return RID_SVXSTR_FIELDUNIT_TABLE[nPos].second; + return FieldUnit::NONE; +} + +OUString SvxAttrNameTable::GetString(sal_uInt32 nPos) +{ + if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count()) + return SvxResId(RID_ATTR_NAMES[nPos].first); + return OUString(); +} + +sal_uInt32 SvxAttrNameTable::Count() { return std::size(RID_ATTR_NAMES); } + +sal_uInt32 SvxAttrNameTable::FindIndex(int nValue) +{ + for (size_t i = 0; i < std::size(RID_ATTR_NAMES); ++i) + { + if (nValue == RID_ATTR_NAMES[i].second) + return i; + } + return RESARRAY_INDEX_NOTFOUND; +} + +OUString SvxNumberingTypeTable::GetString(sal_uInt32 nPos) +{ + if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count()) + return SvxResId(RID_SVXSTRARY_NUMBERINGTYPE[nPos].first); + return OUString(); +} + +sal_uInt32 SvxNumberingTypeTable::Count() { return std::size(RID_SVXSTRARY_NUMBERINGTYPE); } + +int SvxNumberingTypeTable::GetValue(sal_uInt32 nPos) +{ + if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count()) + return RID_SVXSTRARY_NUMBERINGTYPE[nPos].second; + return 0; +} + +sal_uInt32 SvxNumberingTypeTable::FindIndex(int nValue) +{ + for (size_t i = 0; i < std::size(RID_SVXSTRARY_NUMBERINGTYPE); ++i) + { + if (nValue == RID_SVXSTRARY_NUMBERINGTYPE[i].second) + return i; + } + return RESARRAY_INDEX_NOTFOUND; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/svxbmpnumvalueset.cxx b/svx/source/dialog/svxbmpnumvalueset.cxx new file mode 100644 index 0000000000..63a5e5dce8 --- /dev/null +++ b/svx/source/dialog/svxbmpnumvalueset.cxx @@ -0,0 +1,533 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <comphelper/diagnose_ex.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <svtools/valueset.hxx> +#include <editeng/numitem.hxx> +#include <svx/gallery.hxx> +#include <vcl/event.hxx> +#include <vcl/graph.hxx> +#include <vcl/virdev.hxx> +#include <svx/numvset.hxx> +#include <com/sun/star/style/NumberingType.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/text/XNumberingFormatter.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <algorithm> + +#include <uiobject.hxx> + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::lang; +using namespace com::sun::star::text; +using namespace com::sun::star::container; +using namespace com::sun::star::style; + + +// The selection of bullets from the star symbol +const sal_Unicode aBulletTypes[] = +{ + 0x2022, + 0x25cf, + 0xe00c, + 0xe00a, + 0x2794, + 0x27a2, + 0x2717, + 0x2714 +}; + +static vcl::Font& lcl_GetDefaultBulletFont() +{ + static vcl::Font aDefBulletFont = []() + { + static vcl::Font tmp("OpenSymbol", "", Size(0, 14)); + tmp.SetCharSet( RTL_TEXTENCODING_SYMBOL ); + tmp.SetFamily( FAMILY_DONTKNOW ); + tmp.SetPitch( PITCH_DONTKNOW ); + tmp.SetWeight( WEIGHT_DONTKNOW ); + tmp.SetTransparent( true ); + return tmp; + }(); + return aDefBulletFont; +} + +static void lcl_PaintLevel(OutputDevice* pVDev, sal_Int16 nNumberingType, + const OUString& rBulletChar, const OUString& rText, const OUString& rFontName, + Point& rLeft, vcl::Font& rRuleFont, const vcl::Font& rTextFont) +{ + + if(NumberingType::CHAR_SPECIAL == nNumberingType ) + { + rRuleFont.SetStyleName(rFontName); + pVDev->SetFont(rRuleFont); + pVDev->DrawText(rLeft, rBulletChar); + rLeft.AdjustX(pVDev->GetTextWidth(rBulletChar) ); + } + else + { + pVDev->SetFont(rTextFont); + pVDev->DrawText(rLeft, rText); + rLeft.AdjustX(pVDev->GetTextWidth(rText) ); + } +} + + const TranslateId RID_SVXSTR_BULLET_DESCRIPTIONS[] = +{ + RID_SVXSTR_BULLET_DESCRIPTION_0, + RID_SVXSTR_BULLET_DESCRIPTION_1, + RID_SVXSTR_BULLET_DESCRIPTION_2, + RID_SVXSTR_BULLET_DESCRIPTION_3, + RID_SVXSTR_BULLET_DESCRIPTION_4, + RID_SVXSTR_BULLET_DESCRIPTION_5, + RID_SVXSTR_BULLET_DESCRIPTION_6, + RID_SVXSTR_BULLET_DESCRIPTION_7 +}; + +const TranslateId RID_SVXSTR_SINGLENUM_DESCRIPTIONS[] = +{ + RID_SVXSTR_SINGLENUM_DESCRIPTION_0, + RID_SVXSTR_SINGLENUM_DESCRIPTION_1, + RID_SVXSTR_SINGLENUM_DESCRIPTION_2, + RID_SVXSTR_SINGLENUM_DESCRIPTION_3, + RID_SVXSTR_SINGLENUM_DESCRIPTION_4, + RID_SVXSTR_SINGLENUM_DESCRIPTION_5, + RID_SVXSTR_SINGLENUM_DESCRIPTION_6, + RID_SVXSTR_SINGLENUM_DESCRIPTION_7 +}; + +const TranslateId RID_SVXSTR_OUTLINENUM_DESCRIPTIONS[] = +{ + RID_SVXSTR_OUTLINENUM_DESCRIPTION_0, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_1, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_2, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_3, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_4, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_5, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_6, + RID_SVXSTR_OUTLINENUM_DESCRIPTION_7 +}; + +void SvxNumValueSet::UserDraw( const UserDrawEvent& rUDEvt ) +{ + static const sal_uInt16 aLinesArr[] = + { + 15, 10, + 20, 30, + 25, 50, + 30, 70, + 35, 90, // up to here line positions + 5, 10, // character positions + 10, 30, + 15, 50, + 20, 70, + 25, 90, + }; + + const Color aBackColor(COL_WHITE); + const Color aTextColor(COL_BLACK); + + vcl::RenderContext* pDev = rUDEvt.GetRenderContext(); + tools::Rectangle aRect = rUDEvt.GetRect(); + sal_uInt16 nItemId = rUDEvt.GetItemId(); + + tools::Long nRectWidth = aRect.GetWidth(); + tools::Long nRectHeight = aRect.GetHeight(); + Size aRectSize(nRectWidth, aRect.GetHeight()); + Point aBLPos = aRect.TopLeft(); + vcl::Font aOldFont = pDev->GetFont(); + Color aOldColor = pDev->GetLineColor(); + pDev->SetLineColor(aTextColor); + vcl::Font aFont(OutputDevice::GetDefaultFont( + DefaultFontType::UI_SANS, MsLangId::getConfiguredSystemLanguage(), GetDefaultFontFlags::OnlyOne)); + + Size aSize = aFont.GetFontSize(); + + vcl::Font aRuleFont( lcl_GetDefaultBulletFont() ); + aSize.setHeight( nRectHeight/6 ); + aRuleFont.SetFontSize(aSize); + aRuleFont.SetColor(aTextColor); + aRuleFont.SetFillColor(aBackColor); + if(ePageType == NumberingPageType::BULLET) + aFont = aRuleFont; + else if(ePageType == NumberingPageType::OUTLINE) + { + aSize.setHeight( nRectHeight/8 ); + } + aFont.SetColor(aTextColor); + aFont.SetFillColor(aBackColor); + aFont.SetFontSize( aSize ); + pDev->SetFont(aFont); + + if(!pVDev) + { + // The lines are only one time in the virtual device, only the outline + // page is currently done + pVDev = VclPtr<VirtualDevice>::Create(*pDev); + pVDev->SetMapMode(pDev->GetMapMode()); + pVDev->EnableRTL( IsRTLEnabled() ); + pVDev->SetOutputSize( aRectSize ); + aOrgRect = aRect; + pVDev->SetFillColor( aBackColor ); + pVDev->SetLineColor(COL_LIGHTGRAY); + // Draw line only once + if(ePageType != NumberingPageType::OUTLINE) + { + Point aStart(aBLPos.X() + nRectWidth *25 / 100,0); + Point aEnd(aBLPos.X() + nRectWidth * 9 / 10,0); + for( sal_uInt16 i = 11; i < 100; i += 33) + { + aStart.setY( aBLPos.Y() + nRectHeight * i / 100 ); + aEnd.setY( aStart.Y() ); + pVDev->DrawLine(aStart, aEnd); + aStart.setY( aBLPos.Y() + nRectHeight * (i + 11) / 100 ); + aEnd.setY( aStart.Y() ); + pVDev->DrawLine(aStart, aEnd); + } + } + } + pDev->DrawOutDev( aRect.TopLeft(), aRectSize, + aOrgRect.TopLeft(), aRectSize, + *pVDev ); + // Now comes the text + static constexpr OUStringLiteral sValue(u"Value"); + if( NumberingPageType::SINGLENUM == ePageType || + NumberingPageType::BULLET == ePageType ) + { + Point aStart(aBLPos.X() + nRectWidth / 9,0); + for( sal_uInt16 i = 0; i < 3; i++ ) + { + sal_uInt16 nY = 11 + i * 33; + aStart.setY( aBLPos.Y() + nRectHeight * nY / 100 ); + OUString sText; + if(ePageType == NumberingPageType::BULLET) + { + sText = OUString( aBulletTypes[nItemId - 1] ); + aStart.AdjustY( -(pDev->GetTextHeight()/2) ); + aStart.setX( aBLPos.X() + 5 ); + } + else + { + if(xFormatter.is() && aNumSettings.getLength() > nItemId - 1) + { + Sequence<PropertyValue> aLevel = aNumSettings.getConstArray()[nItemId - 1]; + try + { + aLevel.realloc(aLevel.getLength() + 1); + PropertyValue& rValue = aLevel.getArray()[aLevel.getLength() - 1]; + rValue.Name = sValue; + rValue.Value <<= static_cast<sal_Int32>(i + 1); + sText = xFormatter->makeNumberingString( aLevel, aLocale ); + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("svx.dialog", ""); + } + } + // start just next to the left edge + aStart.setX( aBLPos.X() + 2 ); + aStart.AdjustY( -(pDev->GetTextHeight()/2) ); + } + pDev->DrawText(aStart, sText); + } + } + else if(NumberingPageType::OUTLINE == ePageType ) + { + // Outline numbering has to be painted into the virtual device + // to get correct lines + // has to be made again + pVDev->SetLineColor(aBackColor); + pVDev->DrawRect(aOrgRect); + tools::Long nStartX = aOrgRect.Left(); + tools::Long nStartY = aOrgRect.Top(); + + if(xFormatter.is() && aOutlineSettings.getLength() > nItemId - 1) + { + Reference<XIndexAccess> xLevel = aOutlineSettings.getArray()[nItemId - 1]; + try + { + OUString sLevelTexts[5]; + OUString sFontNames[5]; + OUString sBulletChars[5]; + sal_Int16 aNumberingTypes[5]; + OUString sPrefixes[5]; + OUString sSuffixes[5]; + sal_Int16 aParentNumberings[5]; + + sal_Int32 nLevelCount = xLevel->getCount(); + if(nLevelCount > 5) + nLevelCount = 5; + for( sal_Int32 i = 0; i < nLevelCount; i++) + { + tools::Long nTop = nStartY + nRectHeight * (aLinesArr[2 * i + 11])/100 ; + Point aLeft(nStartX + nRectWidth * (aLinesArr[2 * i + 10])/ 100, nTop ); + + Any aLevelAny = xLevel->getByIndex(i); + Sequence<PropertyValue> aLevel; + aLevelAny >>= aLevel; + aNumberingTypes[i] = 0; + aParentNumberings[i] = 0; + for(const PropertyValue& rProp : std::as_const(aLevel)) + { + if ( rProp.Name == "NumberingType" ) + rProp.Value >>= aNumberingTypes[i]; + else if ( rProp.Name == "BulletFontName" ) + rProp.Value >>= sFontNames[i]; + else if ( rProp.Name == "BulletChar" ) + rProp.Value >>= sBulletChars[i]; + else if ( rProp.Name == "Prefix" ) + rProp.Value >>= sPrefixes[i]; + else if ( rProp.Name == "Suffix" ) + rProp.Value >>= sSuffixes[i]; + else if ( rProp.Name == "ParentNumbering" ) + rProp.Value >>= aParentNumberings[i]; + } + Sequence< PropertyValue > aProperties(2); + PropertyValue* pProperties = aProperties.getArray(); + pProperties[0].Name = "NumberingType"; + pProperties[0].Value <<= aNumberingTypes[i]; + pProperties[1].Name = "Value"; + pProperties[1].Value <<= sal_Int32(1); + try + { + sLevelTexts[i] = xFormatter->makeNumberingString( aProperties, aLocale ); + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION("svx.dialog", ""); + } + + aLeft.AdjustY( -(pDev->GetTextHeight()/2) ); + if(!sPrefixes[i].isEmpty() && + sPrefixes[i] != " ") + { + pVDev->SetFont(aFont); + pVDev->DrawText(aLeft, sPrefixes[i]); + aLeft.AdjustX(pDev->GetTextWidth(sPrefixes[i]) ); + } + if(aParentNumberings[i]) + { + //insert old numberings here + sal_Int32 nStartLevel = std::min(static_cast<sal_Int32>(aParentNumberings[i]), i); + for(sal_Int32 nParentLevel = i - nStartLevel; nParentLevel < i; nParentLevel++) + { + OUString sTmp = sLevelTexts[nParentLevel] + "."; + lcl_PaintLevel(pVDev, + aNumberingTypes[nParentLevel], + sBulletChars[nParentLevel], + sTmp, + sFontNames[nParentLevel], + aLeft, + aRuleFont, + aFont); + } + } + lcl_PaintLevel(pVDev, + aNumberingTypes[i], + sBulletChars[i], + sLevelTexts[i], + sFontNames[i], + aLeft, + aRuleFont, + aFont); + if(!sSuffixes[i].isEmpty() && + !sSuffixes[i].startsWith(" ")) + { + pVDev->SetFont(aFont); + pVDev->DrawText(aLeft, sSuffixes[i]); + aLeft.AdjustX(pDev->GetTextWidth(sSuffixes[i]) ); + } + + tools::Long nLineTop = nStartY + nRectHeight * aLinesArr[2 * i + 1]/100 ; + Point aLineLeft(aLeft.X(), nLineTop ); + Point aLineRight(nStartX + nRectWidth * 90 /100, nLineTop ); + pVDev->SetLineColor(COL_LIGHTGRAY); + pVDev->DrawLine(aLineLeft, aLineRight); + } + + } +#ifdef DBG_UTIL + catch(Exception&) + { + static bool bAssert = false; + if(!bAssert) + { + TOOLS_WARN_EXCEPTION("svx.dialog", ""); + bAssert = true; + } + } +#else + catch(Exception&) + { + } +#endif + } + pDev->DrawOutDev( aRect.TopLeft(), aRectSize, + aOrgRect.TopLeft(), aRectSize, + *pVDev ); + } + + pDev->SetFont(aOldFont); + pDev->SetLineColor(aOldColor); +} + +SvxNumValueSet::SvxNumValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow) + : ValueSet(std::move(pScrolledWindow)) + , ePageType(NumberingPageType::BULLET) + , pVDev(nullptr) +{ +} + +FactoryFunction SvxNumValueSet::GetUITestFactory() const +{ + return SvxNumValueSetUIObject::create; +} + +void SvxNumValueSet::init(NumberingPageType eType) +{ + ePageType = eType; + pVDev = nullptr; + + SetColCount( 4 ); + SetLineCount( 2 ); + SetStyle( GetStyle() | WB_ITEMBORDER | WB_DOUBLEBORDER ); + if(NumberingPageType::BULLET == eType) + { + for ( sal_uInt16 i = 0; i < 8; i++ ) + { + InsertItem( i + 1, i ); + SetItemText(i + 1, SvxResId(RID_SVXSTR_BULLET_DESCRIPTIONS[i])); + } + } +} + +SvxNumValueSet::~SvxNumValueSet() +{ +} + +void SvxNumValueSet::SetNumberingSettings( + const Sequence<Sequence<PropertyValue> >& aNum, + Reference<XNumberingFormatter> const & xFormat, + const Locale& rLocale ) +{ + aNumSettings = aNum; + xFormatter = xFormat; + aLocale = rLocale; + if(aNum.getLength() > 8) + SetStyle( GetStyle()|WB_VSCROLL); + for ( sal_Int32 i = 0; i < aNum.getLength(); i++ ) + { + InsertItem( i + 1, i ); + if( i < 8 ) + SetItemText(i + 1, SvxResId(RID_SVXSTR_SINGLENUM_DESCRIPTIONS[i])); + } +} + +void SvxNumValueSet::SetOutlineNumberingSettings( + Sequence<Reference<XIndexAccess> > const & rOutline, + Reference<XNumberingFormatter> const & xFormat, + const Locale& rLocale) +{ + aOutlineSettings = rOutline; + xFormatter = xFormat; + aLocale = rLocale; + if(aOutlineSettings.getLength() > 8) + SetStyle( GetStyle() | WB_VSCROLL ); + for ( sal_Int32 i = 0; i < aOutlineSettings.getLength(); i++ ) + { + InsertItem( i + 1, i ); + if( i < 8 ) + SetItemText(i + 1, SvxResId(RID_SVXSTR_OUTLINENUM_DESCRIPTIONS[i])); + } +} + +SvxBmpNumValueSet::SvxBmpNumValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow) + : SvxNumValueSet(std::move(pScrolledWindow)) + , aFormatIdle("SvxBmpNumValueSet FormatIdle") + , bGrfNotFound(false) +{ +} + +void SvxBmpNumValueSet::init() +{ + SvxNumValueSet::init(NumberingPageType::BITMAP); + bGrfNotFound = false; + GalleryExplorer::BeginLocking(GALLERY_THEME_BULLETS); + SetStyle( GetStyle() | WB_VSCROLL ); + SetLineCount( 3 ); + aFormatIdle.SetPriority(TaskPriority::LOWEST); + aFormatIdle.SetInvokeHandler(LINK(this, SvxBmpNumValueSet, FormatHdl_Impl)); +} + + +SvxBmpNumValueSet::~SvxBmpNumValueSet() +{ + GalleryExplorer::EndLocking(GALLERY_THEME_BULLETS); + aFormatIdle.Stop(); +} + +void SvxBmpNumValueSet::UserDraw(const UserDrawEvent& rUDEvt) +{ + SvxNumValueSet::UserDraw(rUDEvt); + + tools::Rectangle aRect = rUDEvt.GetRect(); + vcl::RenderContext* pDev = rUDEvt.GetRenderContext(); + sal_uInt16 nItemId = rUDEvt.GetItemId(); + Point aBLPos = aRect.TopLeft(); + + tools::Long nRectHeight = aRect.GetHeight(); + Size aSize(nRectHeight/8, nRectHeight/8); + + Graphic aGraphic; + if(!GalleryExplorer::GetGraphicObj( GALLERY_THEME_BULLETS, nItemId - 1, + &aGraphic)) + { + bGrfNotFound = true; + } + else + { + Point aPos(aBLPos.X() + 5, 0); + for( sal_uInt16 i = 0; i < 3; i++ ) + { + sal_uInt16 nY = 11 + i * 33; + aPos.setY( aBLPos.Y() + nRectHeight * nY / 100 ); + aGraphic.Draw(*pDev, aPos, aSize); + } + } +} + +IMPL_LINK_NOARG(SvxBmpNumValueSet, FormatHdl_Impl, Timer *, void) +{ + // only when a graphics was not there, it needs to be formatted + if (bGrfNotFound) + { + SetFormat(); + bGrfNotFound = false; + } + Invalidate(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/svxdlg.cxx b/svx/source/dialog/svxdlg.cxx new file mode 100644 index 0000000000..c073fb241f --- /dev/null +++ b/svx/source/dialog/svxdlg.cxx @@ -0,0 +1,29 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/svxdlg.hxx> + +SvxAbstractDialogFactory* SvxAbstractDialogFactory::Create() +{ + return dynamic_cast<SvxAbstractDialogFactory*>(VclAbstractDialogFactory::Create()); +} + +SvxAbstractDialogFactory::~SvxAbstractDialogFactory() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/svxgraphicitem.cxx b/svx/source/dialog/svxgraphicitem.cxx new file mode 100644 index 0000000000..940941b605 --- /dev/null +++ b/svx/source/dialog/svxgraphicitem.cxx @@ -0,0 +1,40 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/svxgraphicitem.hxx> +#include <svx/svxids.hrc> +#include <utility> + +SvxGraphicItem::SvxGraphicItem( Graphic _aGraphic ) + : SfxPoolItem( SID_GRAPHIC ), aGraphic(std::move( _aGraphic )) +{ + +} + +bool SvxGraphicItem::operator==( const SfxPoolItem& rItem) const +{ + return SfxPoolItem::operator==(rItem) && static_cast<const SvxGraphicItem&>(rItem).aGraphic == aGraphic; +} + +SvxGraphicItem* SvxGraphicItem::Clone( SfxItemPool * ) const +{ + return new SvxGraphicItem( *this ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/svxruler.cxx b/svx/source/dialog/svxruler.cxx new file mode 100644 index 0000000000..6323583fd2 --- /dev/null +++ b/svx/source/dialog/svxruler.cxx @@ -0,0 +1,3582 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <cstring> +#include <climits> + +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/fieldvalues.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/virdev.hxx> +#include <vcl/weldutils.hxx> +#include <svl/eitem.hxx> +#include <svl/rectitem.hxx> +#include <svl/hint.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/strings.hrc> +#include <svx/svxids.hrc> +#include <svx/dialmgr.hxx> +#include <svx/ruler.hxx> +#include <svx/rulritem.hxx> +#include <editeng/editids.hrc> +#include <editeng/tstpitem.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/protitem.hxx> +#include <osl/diagnose.h> +#include <rtl/math.hxx> +#include <o3tl/string_view.hxx> +#include <svl/itemset.hxx> + +#include "rlrcitem.hxx" +#include <memory> + +#define CTRL_ITEM_COUNT 14 +#define GAP 10 +#define OBJECT_BORDER_COUNT 4 +#define TAB_GAP 1 +#define INDENT_GAP 2 +#define INDENT_FIRST_LINE 2 +#define INDENT_LEFT_MARGIN 3 +#define INDENT_RIGHT_MARGIN 4 +#define INDENT_COUNT 3 //without the first two old values + +struct SvxRuler_Impl { + std::unique_ptr<sal_uInt16[]> pPercBuf; + std::unique_ptr<sal_uInt16[]> pBlockBuf; + sal_uInt16 nPercSize; + tools::Long nTotalDist; + tools::Long lOldWinPos; + tools::Long lMaxLeftLogic; + tools::Long lMaxRightLogic; + tools::Long lLastLMargin; + tools::Long lLastRMargin; + std::unique_ptr<SvxProtectItem> aProtectItem; + std::unique_ptr<SfxBoolItem> pTextRTLItem; + sal_uInt16 nControllerItems; + sal_uInt16 nIdx; + sal_uInt16 nColLeftPix; + sal_uInt16 nColRightPix; // Pixel values for left / right edge + // For columns; buffered to prevent + // recalculation errors + // May be has to be widen for future values + bool bIsTableRows : 1; // mxColumnItem contains table rows instead of columns + //#i24363# tab stops relative to indent + bool bIsTabsRelativeToIndent : 1; // Tab stops relative to paragraph indent? + // false means relative to SvxRuler::GetLeftFrameMargin() + + SvxRuler_Impl() : + nPercSize(0), nTotalDist(0), + lOldWinPos(0), lMaxLeftLogic(0), lMaxRightLogic(0), + lLastLMargin(0), lLastRMargin(0), + aProtectItem(std::make_unique<SvxProtectItem>(SID_RULER_PROTECT)), + nControllerItems(0), nIdx(0), + nColLeftPix(0), nColRightPix(0), + bIsTableRows(false), + bIsTabsRelativeToIndent(true) + { + } + + void SetPercSize(sal_uInt16 nSize); + +}; + +static RulerTabData ruler_tab_svx = +{ + 0, // DPIScaleFactor to be set + 7, // ruler_tab_width + 6, // ruler_tab_height + 0, // ruler_tab_height2 + 0, // ruler_tab_width2 + 0, // ruler_tab_cwidth + 0, // ruler_tab_cwidth2 + 0, // ruler_tab_cwidth3 + 0, // ruler_tab_cwidth4 + 0, // ruler_tab_dheight + 0, // ruler_tab_dheight2 + 0, // ruler_tab_dwidth + 0, // ruler_tab_dwidth2 + 0, // ruler_tab_dwidth3 + 0, // ruler_tab_dwidth4 + 0 // ruler_tab_textoff +}; + +void SvxRuler_Impl::SetPercSize(sal_uInt16 nSize) +{ + if(nSize > nPercSize) + { + nPercSize = nSize; + pPercBuf.reset( new sal_uInt16[nPercSize] ); + pBlockBuf.reset( new sal_uInt16[nPercSize] ); + } + size_t nSize2 = sizeof(sal_uInt16) * nPercSize; + memset(pPercBuf.get(), 0, nSize2); + memset(pBlockBuf.get(), 0, nSize2); +} + +// Constructor of the ruler + +// SID_ATTR_ULSPACE, SID_ATTR_LRSPACE +// expects as parameter SvxULSpaceItem for page edge +// (either left/right or top/bottom) +// Ruler: SetMargin1, SetMargin2 + +// SID_RULER_PAGE_POS +// expects as parameter the initial value of the page and page width +// Ruler: SetPagePos + +// SID_ATTR_TABSTOP +// expects: SvxTabStopItem +// Ruler: SetTabs + +// SID_ATTR_PARA_LRSPACE +// left, right paragraph edge in H-ruler +// Ruler: SetIndents + +// SID_RULER_BORDERS +// Table borders, columns +// expects: something like SwTabCols +// Ruler: SetBorders + +constexpr tools::Long glMinFrame = 5; // minimal frame width in pixels + +SvxRuler::SvxRuler( + vcl::Window* pParent, // StarView Parent + vcl::Window* pWin, // Output window: is used for conversion + // logical units <-> pixels + SvxRulerSupportFlags flags, // Display flags, see ruler.hxx + SfxBindings &rBindings, // associated Bindings + WinBits nWinStyle) : // StarView WinBits + Ruler(pParent, nWinStyle), + pCtrlItems(CTRL_ITEM_COUNT), + pEditWin(pWin), + mxRulerImpl(new SvxRuler_Impl), + bAppSetNullOffset(false), // Is the 0-offset of the ruler set by the application? + lLogicNullOffset(0), + lAppNullOffset(LONG_MAX), + lInitialDragPos(0), + nFlags(flags), + nDragType(SvxRulerDragFlags::NONE), + nDefTabType(RULER_TAB_LEFT), + nTabCount(0), + nTabBufSize(0), + lDefTabDist(50), + lTabPos(-1), + mpBorders(1), // due to one column tables + pBindings(&rBindings), + nDragOffset(0), + nMaxLeft(0), + nMaxRight(0), + bValid(false), + bListening(false), + bActive(true), + mbCoarseSnapping(false), + mbSnapping(true) + +{ + /* Constructor; Initialize data buffer; controller items are created */ + + rBindings.EnterRegistrations(); + + // Create Supported Items + sal_uInt16 i = 0; + + // Page edges + pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_LR_MIN_MAX, *this, rBindings)); + if((nWinStyle & WB_VSCROLL) == WB_VSCROLL) + { + bHorz = false; + pCtrlItems[i++].reset(new SvxRulerItem(SID_ATTR_LONG_ULSPACE, *this, rBindings)); + } + else + { + bHorz = true; + pCtrlItems[i++].reset(new SvxRulerItem(SID_ATTR_LONG_LRSPACE, *this, rBindings)); + } + + // Page Position + pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_PAGE_POS, *this, rBindings)); + + if(nFlags & SvxRulerSupportFlags::TABS) + { + sal_uInt16 nTabStopId = bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL; + pCtrlItems[i++].reset(new SvxRulerItem(nTabStopId, *this, rBindings)); + SetExtraType(RulerExtra::Tab, nDefTabType); + } + + if(nFlags & (SvxRulerSupportFlags::PARAGRAPH_MARGINS |SvxRulerSupportFlags::PARAGRAPH_MARGINS_VERTICAL)) + { + if(bHorz) + pCtrlItems[i++].reset(new SvxRulerItem(SID_ATTR_PARA_LRSPACE, *this, rBindings)); + else + pCtrlItems[i++].reset(new SvxRulerItem(SID_ATTR_PARA_LRSPACE_VERTICAL, *this, rBindings)); + + mpIndents.resize(5 + INDENT_GAP); + + for(RulerIndent & rIndent : mpIndents) + { + rIndent.nPos = 0; + rIndent.nStyle = RulerIndentStyle::Top; + } + + mpIndents[0].nStyle = RulerIndentStyle::Top; + mpIndents[1].nStyle = RulerIndentStyle::Top; + mpIndents[INDENT_FIRST_LINE].nStyle = RulerIndentStyle::Top; + mpIndents[INDENT_LEFT_MARGIN].nStyle = RulerIndentStyle::Bottom; + mpIndents[INDENT_RIGHT_MARGIN].nStyle = RulerIndentStyle::Bottom; + } + + if( (nFlags & SvxRulerSupportFlags::BORDERS) == SvxRulerSupportFlags::BORDERS ) + { + pCtrlItems[i++].reset(new SvxRulerItem(bHorz ? SID_RULER_BORDERS : SID_RULER_BORDERS_VERTICAL, *this, rBindings)); + pCtrlItems[i++].reset(new SvxRulerItem(bHorz ? SID_RULER_ROWS : SID_RULER_ROWS_VERTICAL, *this, rBindings)); + } + + pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_TEXT_RIGHT_TO_LEFT, *this, rBindings)); + + if( (nFlags & SvxRulerSupportFlags::OBJECT) == SvxRulerSupportFlags::OBJECT ) + { + pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_OBJECT, *this, rBindings)); + mpObjectBorders.resize(OBJECT_BORDER_COUNT); + for(sal_uInt16 nBorder = 0; nBorder < OBJECT_BORDER_COUNT; ++nBorder) + { + mpObjectBorders[nBorder].nPos = 0; + mpObjectBorders[nBorder].nWidth = 0; + mpObjectBorders[nBorder].nStyle = RulerBorderStyle::Moveable; + } + } + + pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_PROTECT, *this, rBindings)); + pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_BORDER_DISTANCE, *this, rBindings)); + mxRulerImpl->nControllerItems=i; + + if( (nFlags & SvxRulerSupportFlags::SET_NULLOFFSET) == SvxRulerSupportFlags::SET_NULLOFFSET ) + SetExtraType(RulerExtra::NullOffset); + + rBindings.LeaveRegistrations(); + + ruler_tab_svx.DPIScaleFactor = pParent->GetDPIScaleFactor(); + ruler_tab_svx.height *= ruler_tab_svx.DPIScaleFactor; + ruler_tab_svx.width *= ruler_tab_svx.DPIScaleFactor; + +} + +SvxRuler::~SvxRuler() +{ + disposeOnce(); +} + +void SvxRuler::dispose() +{ + /* Destructor ruler; release internal buffer */ + if(bListening) + EndListening(*pBindings); + + pBindings->EnterRegistrations(); + + pCtrlItems.clear(); + + pBindings->LeaveRegistrations(); + + pEditWin.clear(); + Ruler::dispose(); +} + +tools::Long SvxRuler::MakePositionSticky(tools::Long aPosition, tools::Long aPointOfReference, bool aSnapToFrameMargin) const +{ + tools::Long aPointOfReferencePixel = ConvertHPosPixel(aPointOfReference); + tools::Long aLeftFramePosition = ConvertHPosPixel(GetLeftFrameMargin()); + tools::Long aRightFramePosition = ConvertHPosPixel(GetRightFrameMargin()); + + double aTick = GetCurrentRulerUnit().nTick1; + + if (mbCoarseSnapping) + aTick = GetCurrentRulerUnit().nTick2; + + tools::Long aTickPixel = pEditWin->LogicToPixel(Size(aTick, 0), GetCurrentMapMode()).Width(); + + double aHalfTick = aTick / 2.0; + double aHalfTickPixel = aTickPixel / 2.0; + + if (aSnapToFrameMargin) + { + if (aPosition > aLeftFramePosition - aHalfTickPixel && aPosition < aLeftFramePosition + aHalfTickPixel) + return aLeftFramePosition; + + if (aPosition > aRightFramePosition - aHalfTickPixel && aPosition < aRightFramePosition + aHalfTickPixel) + return aRightFramePosition; + } + + if (!mbSnapping) + return aPosition; + + // Move "coordinate system" to frame position so ticks are calculated correctly + tools::Long aTranslatedPosition = aPosition - aPointOfReferencePixel; + // Convert position to current selected map mode + tools::Long aPositionLogic = pEditWin->PixelToLogic(Size(aTranslatedPosition, 0), GetCurrentMapMode()).Width(); + // Normalize -- snap to nearest tick + aPositionLogic = rtl::math::round((aPositionLogic + aHalfTick) / aTick) * aTick; + // Convert back to pixels + aPosition = pEditWin->LogicToPixel(Size(aPositionLogic, 0), GetCurrentMapMode()).Width(); + // Move "coordinate system" back to original position + return aPosition + aPointOfReferencePixel; +} + +tools::Long SvxRuler::ConvertHPosPixel(tools::Long nVal) const +{ + return pEditWin->LogicToPixel(Size(nVal, 0)).Width(); +} + +tools::Long SvxRuler::ConvertVPosPixel(tools::Long nVal) const +{ + return pEditWin->LogicToPixel(Size(0, nVal)).Height(); +} + +tools::Long SvxRuler::ConvertHSizePixel(tools::Long nVal) const +{ + return pEditWin->LogicToPixel(Size(nVal, 0)).Width(); +} + +tools::Long SvxRuler::ConvertVSizePixel(tools::Long nVal) const +{ + return pEditWin->LogicToPixel(Size(0, nVal)).Height(); +} + +tools::Long SvxRuler::ConvertPosPixel(tools::Long nVal) const +{ + return bHorz ? ConvertHPosPixel(nVal): ConvertVPosPixel(nVal); +} + +tools::Long SvxRuler::ConvertSizePixel(tools::Long nVal) const +{ + return bHorz? ConvertHSizePixel(nVal): ConvertVSizePixel(nVal); +} + +inline tools::Long SvxRuler::ConvertHPosLogic(tools::Long nVal) const +{ + return pEditWin->PixelToLogic(Size(nVal, 0)).Width(); +} + +inline tools::Long SvxRuler::ConvertVPosLogic(tools::Long nVal) const +{ + return pEditWin->PixelToLogic(Size(0, nVal)).Height(); +} + +inline tools::Long SvxRuler::ConvertHSizeLogic(tools::Long nVal) const +{ + return pEditWin->PixelToLogic(Size(nVal, 0)).Width(); +} + +inline tools::Long SvxRuler::ConvertVSizeLogic(tools::Long nVal) const +{ + return pEditWin->PixelToLogic(Size(0, nVal)).Height(); +} + +inline tools::Long SvxRuler::ConvertPosLogic(tools::Long nVal) const +{ + return bHorz? ConvertHPosLogic(nVal): ConvertVPosLogic(nVal); +} + +inline tools::Long SvxRuler::ConvertSizeLogic(tools::Long nVal) const +{ + return bHorz? ConvertHSizeLogic(nVal): ConvertVSizeLogic(nVal); +} + +tools::Long SvxRuler::PixelHAdjust(tools::Long nVal, tools::Long nValOld) const +{ + if(ConvertHSizePixel(nVal) != ConvertHSizePixel(nValOld)) + return nVal; + else + return nValOld; +} + +tools::Long SvxRuler::PixelVAdjust(tools::Long nVal, tools::Long nValOld) const +{ + if(ConvertVSizePixel(nVal) != ConvertVSizePixel(nValOld)) + return nVal; + else + return nValOld; +} + +tools::Long SvxRuler::PixelAdjust(tools::Long nVal, tools::Long nValOld) const +{ + if(ConvertSizePixel(nVal) != ConvertSizePixel(nValOld)) + return nVal; + else + return nValOld; +} + +inline sal_uInt16 SvxRuler::GetObjectBordersOff(sal_uInt16 nIdx) const +{ + return bHorz ? nIdx : nIdx + 2; +} + +/* + Update Upper Left edge. + Items are translated into the representation of the ruler. +*/ +void SvxRuler::UpdateFrame() +{ + const RulerMarginStyle nMarginStyle = + ( mxRulerImpl->aProtectItem->IsSizeProtected() || + mxRulerImpl->aProtectItem->IsPosProtected() ) ? + RulerMarginStyle::NONE : RulerMarginStyle::Sizeable; + + if(mxLRSpaceItem && mxPagePosItem) + { + // if no initialization by default app behavior + const tools::Long nOld = lLogicNullOffset; + lLogicNullOffset = mxColumnItem ? mxColumnItem->GetLeft() : mxLRSpaceItem->GetLeft(); + + if(bAppSetNullOffset) + { + lAppNullOffset += lLogicNullOffset - nOld; + } + + if(!bAppSetNullOffset || lAppNullOffset == LONG_MAX) + { + Ruler::SetNullOffset(ConvertHPosPixel(lLogicNullOffset)); + SetMargin1(0, nMarginStyle); + lAppNullOffset = 0; + } + else + { + SetMargin1(ConvertHPosPixel(lAppNullOffset), nMarginStyle); + } + + tools::Long lRight = 0; + + // evaluate the table right edge of the table + if(mxColumnItem && mxColumnItem->IsTable()) + lRight = mxColumnItem->GetRight(); + else + lRight = mxLRSpaceItem->GetRight(); + + tools::Long aWidth = mxPagePosItem->GetWidth() - lRight - lLogicNullOffset + lAppNullOffset; + tools::Long aWidthPixel = ConvertHPosPixel(aWidth); + + SetMargin2(aWidthPixel, nMarginStyle); + } + else if(mxULSpaceItem && mxPagePosItem) + { + // relative the upper edge of the surrounding frame + const tools::Long nOld = lLogicNullOffset; + lLogicNullOffset = mxColumnItem ? mxColumnItem->GetLeft() : mxULSpaceItem->GetUpper(); + + if(bAppSetNullOffset) + { + lAppNullOffset += lLogicNullOffset - nOld; + } + + if(!bAppSetNullOffset || lAppNullOffset == LONG_MAX) + { + Ruler::SetNullOffset(ConvertVPosPixel(lLogicNullOffset)); + lAppNullOffset = 0; + SetMargin1(0, nMarginStyle); + } + else + { + SetMargin1(ConvertVPosPixel(lAppNullOffset), nMarginStyle); + } + + tools::Long lLower = mxColumnItem ? mxColumnItem->GetRight() : mxULSpaceItem->GetLower(); + tools::Long nMargin2 = mxPagePosItem->GetHeight() - lLower - lLogicNullOffset + lAppNullOffset; + tools::Long nMargin2Pixel = ConvertVPosPixel(nMargin2); + + SetMargin2(nMargin2Pixel, nMarginStyle); + } + else + { + // turns off the view + SetMargin1(); + SetMargin2(); + } + + if (mxColumnItem) + { + mxRulerImpl->nColLeftPix = static_cast<sal_uInt16>(ConvertSizePixel(mxColumnItem->GetLeft())); + mxRulerImpl->nColRightPix = static_cast<sal_uInt16>(ConvertSizePixel(mxColumnItem->GetRight())); + } +} + +void SvxRuler::MouseMove( const MouseEvent& rMEvt ) +{ + if( bActive ) + { + pBindings->Update( SID_RULER_LR_MIN_MAX ); + pBindings->Update( SID_ATTR_LONG_ULSPACE ); + pBindings->Update( SID_ATTR_LONG_LRSPACE ); + pBindings->Update( SID_RULER_PAGE_POS ); + pBindings->Update( bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL); + pBindings->Update( bHorz ? SID_ATTR_PARA_LRSPACE : SID_ATTR_PARA_LRSPACE_VERTICAL); + pBindings->Update( bHorz ? SID_RULER_BORDERS : SID_RULER_BORDERS_VERTICAL); + pBindings->Update( bHorz ? SID_RULER_ROWS : SID_RULER_ROWS_VERTICAL); + pBindings->Update( SID_RULER_OBJECT ); + pBindings->Update( SID_RULER_PROTECT ); + } + + Ruler::MouseMove( rMEvt ); + + RulerSelection aSelection = GetHoverSelection(); + + if (aSelection.eType == RulerType::DontKnow) + { + SetQuickHelpText(""); + return; + } + + RulerUnitData aUnitData = GetCurrentRulerUnit(); + double aRoundingFactor = aUnitData.nTickUnit / aUnitData.nTick1; + sal_Int32 aNoDecimalPlaces = 1 + std::ceil(std::log10(aRoundingFactor)); + OUString sUnit = OUString::createFromAscii(aUnitData.aUnitStr); + + switch (aSelection.eType) + { + case RulerType::Indent: + { + if (!mxParaItem) + break; + + tools::Long nIndex = aSelection.nAryPos + INDENT_GAP; + + tools::Long nIndentValue = 0.0; + if (nIndex == INDENT_LEFT_MARGIN) + nIndentValue = mxParaItem->GetTextLeft(); + else if (nIndex == INDENT_FIRST_LINE) + nIndentValue = mxParaItem->GetTextFirstLineOffset(); + else if (nIndex == INDENT_RIGHT_MARGIN) + nIndentValue = mxParaItem->GetRight(); + + double fValue = OutputDevice::LogicToLogic(Size(nIndentValue, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width(); + fValue = rtl::math::round(fValue / aUnitData.nTickUnit, aNoDecimalPlaces); + + SetQuickHelpText(OUString::number(fValue) + " " + sUnit); + break; + } + case RulerType::Border: + { + if (mxColumnItem == nullptr) + break; + + SvxColumnItem& aColumnItem = *mxColumnItem; + + if (aSelection.nAryPos + 1 >= aColumnItem.Count()) + break; + + double fStart = OutputDevice::LogicToLogic(Size(aColumnItem[aSelection.nAryPos].nEnd, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width(); + fStart = rtl::math::round(fStart / aUnitData.nTickUnit, aNoDecimalPlaces); + double fEnd = OutputDevice::LogicToLogic(Size(aColumnItem[aSelection.nAryPos + 1].nStart, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width(); + fEnd = rtl::math::round(fEnd / aUnitData.nTickUnit, aNoDecimalPlaces); + + SetQuickHelpText( + OUString::number(fStart) + " " + sUnit + " - " + + OUString::number(fEnd) + " " + sUnit ); + break; + } + case RulerType::Margin1: + { + tools::Long nLeft = 0.0; + if (mxLRSpaceItem) + nLeft = mxLRSpaceItem->GetLeft(); + else if (mxULSpaceItem) + nLeft = mxULSpaceItem->GetUpper(); + else + break; + + double fValue = OutputDevice::LogicToLogic(Size(nLeft, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width(); + fValue = rtl::math::round(fValue / aUnitData.nTickUnit, aNoDecimalPlaces); + SetQuickHelpText(OUString::number(fValue) + " " + sUnit); + + break; + } + case RulerType::Margin2: + { + tools::Long nRight = 0.0; + if (mxLRSpaceItem) + nRight = mxLRSpaceItem->GetRight(); + else if (mxULSpaceItem) + nRight = mxULSpaceItem->GetLower(); + else + break; + + double fValue = OutputDevice::LogicToLogic(Size(nRight, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width(); + fValue = rtl::math::round(fValue / aUnitData.nTickUnit, aNoDecimalPlaces); + SetQuickHelpText(OUString::number(fValue) + " " + sUnit); + + break; + } + default: + { + SetQuickHelpText(""); + break; + } + } +} + +void SvxRuler::StartListening_Impl() +{ + if(!bListening) + { + bValid = false; + StartListening(*pBindings); + bListening = true; + } +} + +void SvxRuler::UpdateFrame(const SvxLongLRSpaceItem *pItem) // new value LRSpace +{ + /* Store new value LRSpace; delete old ones if possible */ + if(bActive) + { + if(pItem) + mxLRSpaceItem.reset(new SvxLongLRSpaceItem(*pItem)); + else + mxLRSpaceItem.reset(); + StartListening_Impl(); + } +} + +void SvxRuler::UpdateFrameMinMax(const SfxRectangleItem *pItem) // value for MinMax +{ + /* Set new value for MinMax; delete old ones if possible */ + if(bActive) + { + if(pItem) + mxMinMaxItem.reset(new SfxRectangleItem(*pItem)); + else + mxMinMaxItem.reset(); + } +} + + +void SvxRuler::UpdateFrame(const SvxLongULSpaceItem *pItem) // new value +{ + /* Update Right/bottom margin */ + if(bActive && !bHorz) + { + if(pItem) + mxULSpaceItem.reset(new SvxLongULSpaceItem(*pItem)); + else + mxULSpaceItem.reset(); + StartListening_Impl(); + } +} + +void SvxRuler::Update( const SvxProtectItem* pItem ) +{ + if( pItem ) + mxRulerImpl->aProtectItem.reset(pItem->Clone()); +} + +void SvxRuler::UpdateTextRTL(const SfxBoolItem* pItem) +{ + if(bActive && bHorz) + { + mxRulerImpl->pTextRTLItem.reset(); + if(pItem) + mxRulerImpl->pTextRTLItem.reset(new SfxBoolItem(*pItem)); + SetTextRTL(mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue()); + StartListening_Impl(); + } +} + +void SvxRuler::Update( + const SvxColumnItem *pItem, // new value + sal_uInt16 nSID) //Slot Id to identify NULL items +{ + /* Set new value for column view */ + if(!bActive) + return; + + if(pItem) + { + mxColumnItem.reset(new SvxColumnItem(*pItem)); + mxRulerImpl->bIsTableRows = (pItem->Which() == SID_RULER_ROWS || pItem->Which() == SID_RULER_ROWS_VERTICAL); + if(!bHorz && !mxRulerImpl->bIsTableRows) + mxColumnItem->SetWhich(SID_RULER_BORDERS_VERTICAL); + } + else if(mxColumnItem && mxColumnItem->Which() == nSID) + //there are two groups of column items table/frame columns and table rows + //both can occur in vertical or horizontal mode + //the horizontal ruler handles the SID_RULER_BORDERS and SID_RULER_ROWS_VERTICAL + //and the vertical handles SID_RULER_BORDERS_VERTICAL and SID_RULER_ROWS + //if mxColumnItem is already set with one of the ids then a NULL pItem argument + //must not delete it + { + mxColumnItem.reset(); + mxRulerImpl->bIsTableRows = false; + } + StartListening_Impl(); +} + + +void SvxRuler::UpdateColumns() +{ + /* Update column view */ + if(mxColumnItem && mxColumnItem->Count() > 1) + { + mpBorders.resize(mxColumnItem->Count()); + + RulerBorderStyle nStyleFlags = RulerBorderStyle::Variable; + + bool bProtectColumns = + mxRulerImpl->aProtectItem->IsSizeProtected() || + mxRulerImpl->aProtectItem->IsPosProtected(); + + if( !bProtectColumns ) + { + nStyleFlags |= RulerBorderStyle::Moveable; + if( !mxColumnItem->IsTable() ) + nStyleFlags |= RulerBorderStyle::Sizeable; + } + + sal_uInt16 nBorders = mxColumnItem->Count(); + + if(!mxRulerImpl->bIsTableRows) + --nBorders; + + for(sal_uInt16 i = 0; i < nBorders; ++i) + { + mpBorders[i].nStyle = nStyleFlags; + if(!mxColumnItem->At(i).bVisible) + mpBorders[i].nStyle |= RulerBorderStyle::Invisible; + + mpBorders[i].nPos = ConvertPosPixel(mxColumnItem->At(i).nEnd + lAppNullOffset); + + if(mxColumnItem->Count() == i + 1) + { + //with table rows the end of the table is contained in the + //column item but it has no width! + mpBorders[i].nWidth = 0; + } + else + { + mpBorders[i].nWidth = ConvertSizePixel(mxColumnItem->At(i + 1).nStart - mxColumnItem->At(i).nEnd); + } + mpBorders[i].nMinPos = ConvertPosPixel(mxColumnItem->At(i).nEndMin + lAppNullOffset); + mpBorders[i].nMaxPos = ConvertPosPixel(mxColumnItem->At(i).nEndMax + lAppNullOffset); + } + SetBorders(mxColumnItem->Count() - 1, mpBorders.data()); + } + else + { + SetBorders(); + } +} + +void SvxRuler::UpdateObject() +{ + /* Update view of object representation */ + if (mxObjectItem) + { + DBG_ASSERT(!mpObjectBorders.empty(), "no Buffer"); + // !! to the page margin + tools::Long nMargin = mxLRSpaceItem ? mxLRSpaceItem->GetLeft() : 0; + mpObjectBorders[0].nPos = + ConvertPosPixel(mxObjectItem->GetStartX() - + nMargin + lAppNullOffset); + mpObjectBorders[1].nPos = + ConvertPosPixel(mxObjectItem->GetEndX() - nMargin + lAppNullOffset); + nMargin = mxULSpaceItem ? mxULSpaceItem->GetUpper() : 0; + mpObjectBorders[2].nPos = + ConvertPosPixel(mxObjectItem->GetStartY() - + nMargin + lAppNullOffset); + mpObjectBorders[3].nPos = + ConvertPosPixel(mxObjectItem->GetEndY() - nMargin + lAppNullOffset); + + const sal_uInt16 nOffset = GetObjectBordersOff(0); + SetBorders(2, mpObjectBorders.data() + nOffset); + } + else + { + SetBorders(); + } +} + +void SvxRuler::UpdatePara() +{ + + /* Update the view for paragraph indents: + Left margin, first line indent, right margin paragraph update + mpIndents[0] = Buffer for old intent + mpIndents[1] = Buffer for old intent + mpIndents[INDENT_FIRST_LINE] = first line indent + mpIndents[INDENT_LEFT_MARGIN] = left margin + mpIndents[INDENT_RIGHT_MARGIN] = right margin + */ + + // Dependence on PagePosItem + if (mxParaItem && mxPagePosItem && !mxObjectItem) + { + bool bRTLText = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue(); + // First-line indent is negative to the left paragraph margin + tools::Long nLeftFrameMargin = GetLeftFrameMargin(); + tools::Long nRightFrameMargin = GetRightFrameMargin(); + SetLeftFrameMargin(ConvertHPosPixel(nLeftFrameMargin)); + SetRightFrameMargin(ConvertHPosPixel(nRightFrameMargin)); + + tools::Long leftMargin; + tools::Long leftFirstLine; + tools::Long rightMargin; + + if(bRTLText) + { + leftMargin = nRightFrameMargin - mxParaItem->GetTextLeft() + lAppNullOffset; + leftFirstLine = leftMargin - mxParaItem->GetTextFirstLineOffset(); + rightMargin = nLeftFrameMargin + mxParaItem->GetRight() + lAppNullOffset; + } + else + { + leftMargin = nLeftFrameMargin + mxParaItem->GetTextLeft() + lAppNullOffset; + leftFirstLine = leftMargin + mxParaItem->GetTextFirstLineOffset(); + rightMargin = nRightFrameMargin - mxParaItem->GetRight() + lAppNullOffset; + } + + mpIndents[INDENT_LEFT_MARGIN].nPos = ConvertHPosPixel(leftMargin); + mpIndents[INDENT_FIRST_LINE].nPos = ConvertHPosPixel(leftFirstLine); + mpIndents[INDENT_RIGHT_MARGIN].nPos = ConvertHPosPixel(rightMargin); + + mpIndents[INDENT_FIRST_LINE].bInvisible = mxParaItem->IsAutoFirst(); + + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + } + else + { + if(!mpIndents.empty()) + { + mpIndents[INDENT_FIRST_LINE].nPos = 0; + mpIndents[INDENT_LEFT_MARGIN].nPos = 0; + mpIndents[INDENT_RIGHT_MARGIN].nPos = 0; + } + SetIndents(); // turn off + } +} + +void SvxRuler::UpdatePara(const SvxLRSpaceItem *pItem) // new value of paragraph indents +{ + /* Store new value of paragraph indents */ + if(bActive) + { + if(pItem) + mxParaItem.reset(new SvxLRSpaceItem(*pItem)); + else + mxParaItem.reset(); + StartListening_Impl(); + } +} + +void SvxRuler::UpdateBorder(const SvxLRSpaceItem * pItem) +{ + /* Border distance */ + if(bActive) + { + if (pItem) + mxBorderItem.reset(new SvxLRSpaceItem(*pItem)); + else + mxBorderItem.reset(); + + StartListening_Impl(); + } +} + +void SvxRuler::UpdatePage() +{ + /* Update view of position and width of page */ + if (mxPagePosItem) + { + // all objects are automatically adjusted + if(bHorz) + { + SetPagePos( + pEditWin->LogicToPixel(mxPagePosItem->GetPos()).X(), + pEditWin->LogicToPixel(Size(mxPagePosItem->GetWidth(), 0)). + Width()); + } + else + { + SetPagePos( + pEditWin->LogicToPixel(mxPagePosItem->GetPos()).Y(), + pEditWin->LogicToPixel(Size(0, mxPagePosItem->GetHeight())). + Height()); + } + if(bAppSetNullOffset) + SetNullOffset(ConvertSizePixel(-lAppNullOffset + lLogicNullOffset)); + } + else + { + SetPagePos(); + } + + tools::Long lPos = 0; + Point aOwnPos = GetPosPixel(); + Point aEdtWinPos = pEditWin->GetPosPixel(); + if( AllSettings::GetLayoutRTL() && bHorz ) + { + //#i73321# in RTL the window and the ruler is not mirrored but the + // influence of the vertical ruler is inverted + Size aOwnSize = GetSizePixel(); + Size aEdtWinSize = pEditWin->GetSizePixel(); + lPos = aOwnSize.Width() - aEdtWinSize.Width(); + lPos -= (aEdtWinPos - aOwnPos).X(); + } + else + { + Point aPos(aEdtWinPos - aOwnPos); + lPos = bHorz ? aPos.X() : aPos.Y(); + } + + // Unfortunately, we get the offset of the edit window to the ruler never + // through a status message. So we set it ourselves if necessary. + if(lPos != mxRulerImpl->lOldWinPos) + { + mxRulerImpl->lOldWinPos=lPos; + SetWinPos(lPos); + } +} + +void SvxRuler::Update(const SvxPagePosSizeItem *pItem) // new value of page attributes +{ + /* Store new value of page attributes */ + if(bActive) + { + if(pItem) + mxPagePosItem.reset(new SvxPagePosSizeItem(*pItem)); + else + mxPagePosItem.reset(); + StartListening_Impl(); + } +} + +void SvxRuler::SetDefTabDist(tools::Long inDefTabDist) // New distance for DefaultTabs in App-Metrics +{ + if (lAppNullOffset == LONG_MAX) + UpdateFrame(); // hack: try to get lAppNullOffset initialized + /* New distance is set for DefaultTabs */ + lDefTabDist = inDefTabDist; + if( !lDefTabDist ) + lDefTabDist = 1; + + UpdateTabs(); +} + +static sal_uInt16 ToSvTab_Impl(SvxTabAdjust eAdj) +{ + /* Internal conversion routine between SV-Tab.-Enum and Svx */ + switch(eAdj) { + case SvxTabAdjust::Left: return RULER_TAB_LEFT; + case SvxTabAdjust::Right: return RULER_TAB_RIGHT; + case SvxTabAdjust::Decimal: return RULER_TAB_DECIMAL; + case SvxTabAdjust::Center: return RULER_TAB_CENTER; + case SvxTabAdjust::Default: return RULER_TAB_DEFAULT; + default: ; //prevent warning + } + return 0; +} + +static SvxTabAdjust ToAttrTab_Impl(sal_uInt16 eAdj) +{ + switch(eAdj) { + case RULER_TAB_LEFT: return SvxTabAdjust::Left ; + case RULER_TAB_RIGHT: return SvxTabAdjust::Right ; + case RULER_TAB_DECIMAL: return SvxTabAdjust::Decimal ; + case RULER_TAB_CENTER: return SvxTabAdjust::Center ; + case RULER_TAB_DEFAULT: return SvxTabAdjust::Default ; + } + return SvxTabAdjust::Left; +} + +void SvxRuler::UpdateTabs() +{ + if(IsDrag()) + return; + + if (mxPagePosItem && mxParaItem && mxTabStopItem && !mxObjectItem) + { + // buffer for DefaultTabStop + // Distance last Tab <-> Right paragraph margin / DefaultTabDist + bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue(); + + const tools::Long nLeftFrameMargin = GetLeftFrameMargin(); + const tools::Long nRightFrameMargin = GetRightFrameMargin(); + + //#i24363# tab stops relative to indent + const tools::Long nParaItemTxtLeft = mxParaItem->GetTextLeft(); + + const tools::Long lParaIndent = nLeftFrameMargin + nParaItemTxtLeft; + const tools::Long lRightMargin = nRightFrameMargin - nParaItemTxtLeft; + + const tools::Long lLastTab = mxTabStopItem->Count() + ? ConvertHPosPixel(mxTabStopItem->At(mxTabStopItem->Count() - 1).GetTabPos()) + : 0; + const tools::Long lPosPixel = ConvertHPosPixel(lParaIndent) + lLastTab; + const tools::Long lRightIndent = ConvertHPosPixel(nRightFrameMargin - mxParaItem->GetRight()); + + tools::Long lCurrentDefTabDist = lDefTabDist; + if(mxTabStopItem->GetDefaultDistance()) + lCurrentDefTabDist = mxTabStopItem->GetDefaultDistance(); + tools::Long nDefTabDist = ConvertHPosPixel(lCurrentDefTabDist); + + const sal_uInt16 nDefTabBuf = lPosPixel > lRightIndent || lLastTab > lRightIndent + ? 0 + : static_cast<sal_uInt16>( (lRightIndent - lPosPixel) / nDefTabDist ); + + if(mxTabStopItem->Count() + TAB_GAP + nDefTabBuf > nTabBufSize) + { + // 10 (GAP) in stock + nTabBufSize = mxTabStopItem->Count() + TAB_GAP + nDefTabBuf + GAP; + mpTabs.resize(nTabBufSize); + } + + nTabCount = 0; + sal_uInt16 j; + + const tools::Long lParaIndentPix = ConvertSizePixel(lParaIndent); + + tools::Long lTabStartLogic = (mxRulerImpl->bIsTabsRelativeToIndent ? lParaIndent : nLeftFrameMargin) + + lAppNullOffset; + if (bRTL) + { + lTabStartLogic = lParaIndent + lRightMargin - lTabStartLogic; + } + tools::Long lLastTabOffsetLogic = 0; + for(j = 0; j < mxTabStopItem->Count(); ++j) + { + const SvxTabStop* pTab = &mxTabStopItem->At(j); + lLastTabOffsetLogic = pTab->GetTabPos(); + tools::Long lPos = lTabStartLogic + (bRTL ? -lLastTabOffsetLogic : lLastTabOffsetLogic); + mpTabs[nTabCount + TAB_GAP].nPos = ConvertHPosPixel(lPos); + mpTabs[nTabCount + TAB_GAP].nStyle = ToSvTab_Impl(pTab->GetAdjustment()); + ++nTabCount; + } + + // Adjust to previous-to-first default tab stop + lLastTabOffsetLogic -= lLastTabOffsetLogic % lCurrentDefTabDist; + + // fill the rest with default Tabs + for (j = 0; j < nDefTabBuf; ++j) + { + //simply add the default distance to the last position + lLastTabOffsetLogic += lCurrentDefTabDist; + if (bRTL) + { + mpTabs[nTabCount + TAB_GAP].nPos = + ConvertHPosPixel(lTabStartLogic - lLastTabOffsetLogic); + if (mpTabs[nTabCount + TAB_GAP].nPos <= lParaIndentPix) + break; + } + else + { + mpTabs[nTabCount + TAB_GAP].nPos = + ConvertHPosPixel(lTabStartLogic + lLastTabOffsetLogic); + if (mpTabs[nTabCount + TAB_GAP].nPos >= lRightIndent) + break; + } + + mpTabs[nTabCount + TAB_GAP].nStyle = RULER_TAB_DEFAULT; + ++nTabCount; + } + SetTabs(nTabCount, mpTabs.data() + TAB_GAP); + DBG_ASSERT(nTabCount + TAB_GAP <= nTabBufSize, "BufferSize too small"); + } + else + { + SetTabs(); + } +} + +void SvxRuler::Update(const SvxTabStopItem *pItem) // new value for tabs +{ + /* Store new value for tabs; delete old ones if possible */ + if(!bActive) + return; + + if(pItem) + { + mxTabStopItem.reset(new SvxTabStopItem(*pItem)); + if(!bHorz) + mxTabStopItem->SetWhich(SID_ATTR_TABSTOP_VERTICAL); + } + else + { + mxTabStopItem.reset(); + } + StartListening_Impl(); +} + +void SvxRuler::Update(const SvxObjectItem *pItem) // new value for objects +{ + /* Store new value for objects */ + if(bActive) + { + if(pItem) + mxObjectItem.reset(new SvxObjectItem(*pItem)); + else + mxObjectItem.reset(); + StartListening_Impl(); + } +} + +void SvxRuler::SetNullOffsetLogic(tools::Long lVal) // Setting of the logic NullOffsets +{ + lAppNullOffset = lLogicNullOffset - lVal; + bAppSetNullOffset = true; + Ruler::SetNullOffset(ConvertSizePixel(lVal)); + Update(); +} + +void SvxRuler::Update() +{ + /* Perform update of view */ + if(IsDrag()) + return; + + UpdatePage(); + UpdateFrame(); + if(nFlags & SvxRulerSupportFlags::OBJECT) + UpdateObject(); + else + UpdateColumns(); + + if(nFlags & (SvxRulerSupportFlags::PARAGRAPH_MARGINS | SvxRulerSupportFlags::PARAGRAPH_MARGINS_VERTICAL)) + UpdatePara(); + + if(nFlags & SvxRulerSupportFlags::TABS) + UpdateTabs(); +} + +tools::Long SvxRuler::GetPageWidth() const +{ + if (!mxPagePosItem) + return 0; + return bHorz ? mxPagePosItem->GetWidth() : mxPagePosItem->GetHeight(); +} + +inline tools::Long SvxRuler::GetFrameLeft() const +{ + /* Get Left margin in Pixels */ + return bAppSetNullOffset ? + GetMargin1() + ConvertSizePixel(lLogicNullOffset) : + Ruler::GetNullOffset(); +} + +tools::Long SvxRuler::GetFirstLineIndent() const +{ + /* Get First-line indent in pixels */ + return mxParaItem ? mpIndents[INDENT_FIRST_LINE].nPos : GetMargin1(); +} + +tools::Long SvxRuler::GetLeftIndent() const +{ + /* Get Left paragraph margin in Pixels */ + return mxParaItem ? mpIndents[INDENT_LEFT_MARGIN].nPos : GetMargin1(); +} + +tools::Long SvxRuler::GetRightIndent() const +{ + /* Get Right paragraph margin in Pixels */ + return mxParaItem ? mpIndents[INDENT_RIGHT_MARGIN].nPos : GetMargin2(); +} + +tools::Long SvxRuler::GetLogicRightIndent() const +{ + /* Get Right paragraph margin in Logic */ + return mxParaItem ? GetRightFrameMargin() - mxParaItem->GetRight() : GetRightFrameMargin(); +} + +// Left margin in App values, is either the margin (= 0) or the left edge of +// the column that is set in the column attribute as current column. +tools::Long SvxRuler::GetLeftFrameMargin() const +{ + // #126721# for some unknown reason the current column is set to 0xffff + DBG_ASSERT(!mxColumnItem || mxColumnItem->GetActColumn() < mxColumnItem->Count(), + "issue #126721# - invalid current column!"); + tools::Long nLeft = 0; + if (mxColumnItem && + mxColumnItem->Count() && + mxColumnItem->IsConsistent()) + { + nLeft = mxColumnItem->GetActiveColumnDescription().nStart; + } + + if (mxBorderItem && (!mxColumnItem || mxColumnItem->IsTable())) + nLeft += mxBorderItem->GetLeft(); + + return nLeft; +} + +inline tools::Long SvxRuler::GetLeftMin() const +{ + DBG_ASSERT(mxMinMaxItem, "no MinMax value set"); + if (mxMinMaxItem) + { + if (bHorz) + return mxMinMaxItem->GetValue().Left(); + else + return mxMinMaxItem->GetValue().Top(); + } + return 0; +} + +inline tools::Long SvxRuler::GetRightMax() const +{ + DBG_ASSERT(mxMinMaxItem, "no MinMax value set"); + if (mxMinMaxItem) + { + if (bHorz) + return mxMinMaxItem->GetValue().Right(); + else + return mxMinMaxItem->GetValue().Bottom(); + } + return 0; +} + + +tools::Long SvxRuler::GetRightFrameMargin() const +{ + /* Get right frame margin (in logical units) */ + if (mxColumnItem) + { + if (!IsActLastColumn(true)) + { + return mxColumnItem->At(GetActRightColumn(true)).nEnd; + } + } + + tools::Long lResult = lLogicNullOffset; + + // If possible deduct right table entry + if(mxColumnItem && mxColumnItem->IsTable()) + lResult += mxColumnItem->GetRight(); + else if(bHorz && mxLRSpaceItem) + lResult += mxLRSpaceItem->GetRight(); + else if(!bHorz && mxULSpaceItem) + lResult += mxULSpaceItem->GetLower(); + + if (bHorz && mxBorderItem && (!mxColumnItem || mxColumnItem->IsTable())) + lResult += mxBorderItem->GetRight(); + + if(bHorz) + lResult = mxPagePosItem->GetWidth() - lResult; + else + lResult = mxPagePosItem->GetHeight() - lResult; + + return lResult; +} + +#define NEG_FLAG ( (nFlags & SvxRulerSupportFlags::NEGATIVE_MARGINS) == \ + SvxRulerSupportFlags::NEGATIVE_MARGINS ) +#define TAB_FLAG ( mxColumnItem && mxColumnItem->IsTable() ) + +tools::Long SvxRuler::GetCorrectedDragPos( bool bLeft, bool bRight ) +{ + /* + Corrects the position within the calculated limits. The limit values are in + pixels relative to the page edge. + */ + + const tools::Long lNullPix = Ruler::GetNullOffset(); + tools::Long lDragPos = GetDragPos() + lNullPix; + bool bHoriRows = bHorz && mxRulerImpl->bIsTableRows; + if((bLeft || bHoriRows) && lDragPos < nMaxLeft) + lDragPos = nMaxLeft; + else if((bRight||bHoriRows) && lDragPos > nMaxRight) + lDragPos = nMaxRight; + return lDragPos - lNullPix; +} + +static void ModifyTabs_Impl( sal_uInt16 nCount, // Number of Tabs + RulerTab* pTabs, // Tab buffer + tools::Long lDiff) // difference to be added +{ + /* Helper function, move all the tabs by a fixed value */ + if( pTabs ) + { + for(sal_uInt16 i = 0; i < nCount; ++i) + { + pTabs[i].nPos += lDiff; + } + } +} + +void SvxRuler::DragMargin1() +{ + /* Dragging the left edge of frame */ + tools::Long aDragPosition = GetCorrectedDragPos( !TAB_FLAG || !NEG_FLAG ); + + aDragPosition = MakePositionSticky(aDragPosition, GetRightFrameMargin(), false); + + // Check if position changed + if (aDragPosition == 0) + return; + + DrawLine_Impl(lTabPos, ( TAB_FLAG && NEG_FLAG ) ? 3 : 7, bHorz); + if (mxColumnItem && (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)) + DragBorders(); + AdjustMargin1(aDragPosition); +} + +void SvxRuler::AdjustMargin1(tools::Long lInputDiff) +{ + const tools::Long nOld = bAppSetNullOffset? GetMargin1(): GetNullOffset(); + const tools::Long lDragPos = lInputDiff; + + bool bProtectColumns = + mxRulerImpl->aProtectItem->IsSizeProtected() || + mxRulerImpl->aProtectItem->IsPosProtected(); + + const RulerMarginStyle nMarginStyle = + bProtectColumns ? RulerMarginStyle::NONE : RulerMarginStyle::Sizeable; + + if(!bAppSetNullOffset) + { + tools::Long lDiff = lDragPos; + SetNullOffset(nOld + lDiff); + if (!mxColumnItem || !(nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR)) + { + SetMargin2( GetMargin2() - lDiff, nMarginStyle ); + + if (!mxColumnItem && !mxObjectItem && mxParaItem) + { + // Right indent of the old position + mpIndents[INDENT_RIGHT_MARGIN].nPos -= lDiff; + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + } + if (mxObjectItem) + { + mpObjectBorders[GetObjectBordersOff(0)].nPos -= lDiff; + mpObjectBorders[GetObjectBordersOff(1)].nPos -= lDiff; + SetBorders(2, mpObjectBorders.data() + GetObjectBordersOff(0)); + } + if (mxColumnItem) + { + for(sal_uInt16 i = 0; i < mxColumnItem->Count()-1; ++i) + mpBorders[i].nPos -= lDiff; + SetBorders(mxColumnItem->Count()-1, mpBorders.data()); + if(mxColumnItem->IsFirstAct()) + { + // Right indent of the old position + if (mxParaItem) + { + mpIndents[INDENT_RIGHT_MARGIN].nPos -= lDiff; + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + } + } + else + { + if (mxParaItem) + { + mpIndents[INDENT_FIRST_LINE].nPos -= lDiff; + mpIndents[INDENT_LEFT_MARGIN].nPos -= lDiff; + mpIndents[INDENT_RIGHT_MARGIN].nPos -= lDiff; + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + } + } + if(mxTabStopItem && (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL) + &&!IsActFirstColumn()) + { + ModifyTabs_Impl(nTabCount + TAB_GAP, mpTabs.data(), -lDiff); + SetTabs(nTabCount, mpTabs.data() + TAB_GAP); + } + } + } + } + else + { + tools::Long lDiff = lDragPos - nOld; + SetMargin1(nOld + lDiff, nMarginStyle); + + if (!mxColumnItem + || !(nDragType + & (SvxRulerDragFlags::OBJECT_SIZE_LINEAR + | SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL))) + { + if (!mxColumnItem && !mxObjectItem && mxParaItem) + { + // Left indent of the old position + mpIndents[INDENT_FIRST_LINE].nPos += lDiff; + mpIndents[INDENT_LEFT_MARGIN].nPos += lDiff; + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + } + + if (mxColumnItem) + { + for(sal_uInt16 i = 0; i < mxColumnItem->Count() - 1; ++i) + mpBorders[i].nPos += lDiff; + SetBorders(mxColumnItem->Count() - 1, mpBorders.data()); + if (mxColumnItem->IsFirstAct()) + { + // Left indent of the old position + if (mxParaItem) + { + mpIndents[INDENT_FIRST_LINE].nPos += lDiff; + mpIndents[INDENT_LEFT_MARGIN].nPos += lDiff; + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + } + } + else + { + if (mxParaItem) + { + mpIndents[INDENT_FIRST_LINE].nPos += lDiff; + mpIndents[INDENT_LEFT_MARGIN].nPos += lDiff; + mpIndents[INDENT_RIGHT_MARGIN].nPos += lDiff; + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + } + } + } + if (mxTabStopItem) + { + ModifyTabs_Impl(nTabCount + TAB_GAP, mpTabs.data(), lDiff); + SetTabs(nTabCount, mpTabs.data() + TAB_GAP); + } + } + } +} + +void SvxRuler::DragMargin2() +{ + /* Dragging the right edge of frame */ + tools::Long aDragPosition = GetCorrectedDragPos( true, !TAB_FLAG || !NEG_FLAG); + aDragPosition = MakePositionSticky(aDragPosition, GetLeftFrameMargin(), false); + tools::Long lDiff = aDragPosition - GetMargin2(); + + // Check if position changed + if (lDiff == 0) + return; + + if( mxRulerImpl->bIsTableRows && + !bHorz && + mxColumnItem && + (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)) + { + DragBorders(); + } + + bool bProtectColumns = + mxRulerImpl->aProtectItem->IsSizeProtected() || + mxRulerImpl->aProtectItem->IsPosProtected(); + + const RulerMarginStyle nMarginStyle = bProtectColumns ? RulerMarginStyle::NONE : RulerMarginStyle::Sizeable; + + SetMargin2( aDragPosition, nMarginStyle ); + + // Right indent of the old position + if ((!mxColumnItem || IsActLastColumn()) && mxParaItem) + { + mpIndents[INDENT_FIRST_LINE].nPos += lDiff; + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + } + + DrawLine_Impl(lTabPos, ( TAB_FLAG && NEG_FLAG ) ? 5 : 7, bHorz); +} + +void SvxRuler::DragIndents() +{ + /* Dragging the paragraph indents */ + tools::Long aDragPosition = NEG_FLAG ? GetDragPos() : GetCorrectedDragPos(); + const sal_uInt16 nIndex = GetDragAryPos() + INDENT_GAP; + + bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue(); + + if(nIndex == INDENT_RIGHT_MARGIN) + aDragPosition = MakePositionSticky(aDragPosition, bRTL ? GetLeftFrameMargin() : GetRightFrameMargin()); + else + aDragPosition = MakePositionSticky(aDragPosition, bRTL ? GetRightFrameMargin() : GetLeftFrameMargin()); + + const tools::Long lDiff = mpIndents[nIndex].nPos - aDragPosition; + + // Check if position changed + if (lDiff == 0) + return; + + if((nIndex == INDENT_FIRST_LINE || nIndex == INDENT_LEFT_MARGIN ) && + !(nDragType & SvxRulerDragFlags::OBJECT_LEFT_INDENT_ONLY)) + { + mpIndents[INDENT_FIRST_LINE].nPos -= lDiff; + } + + mpIndents[nIndex].nPos = aDragPosition; + + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + DrawLine_Impl(lTabPos, 1, bHorz); +} + +void SvxRuler::DrawLine_Impl(tools::Long& lTabPosition, int nNew, bool bHorizontal) +{ + /* + Output routine for the ledger line when moving tabs, tables and other + columns + */ + if(bHorizontal) + { + const tools::Long nHeight = pEditWin->GetOutDev()->GetOutputSize().Height(); + Point aZero = pEditWin->GetMapMode().GetOrigin(); + if(lTabPosition != -1) + { + pEditWin->InvertTracking( + tools::Rectangle( Point(lTabPosition, -aZero.Y()), + Point(lTabPosition, -aZero.Y() + nHeight)), + ShowTrackFlags::Split | ShowTrackFlags::Clip ); + } + if( nNew & 1 ) + { + tools::Long nDrapPosition = GetCorrectedDragPos( ( nNew & 4 ) != 0, ( nNew & 2 ) != 0 ); + nDrapPosition = MakePositionSticky(nDrapPosition, GetLeftFrameMargin()); + lTabPosition = ConvertHSizeLogic( nDrapPosition + GetNullOffset() ); + if (mxPagePosItem) + lTabPosition += mxPagePosItem->GetPos().X(); + pEditWin->InvertTracking( + tools::Rectangle( Point(lTabPosition, -aZero.Y()), + Point(lTabPosition, -aZero.Y() + nHeight) ), + ShowTrackFlags::Clip | ShowTrackFlags::Split ); + } + } + else + { + const tools::Long nWidth = pEditWin->GetOutDev()->GetOutputSize().Width(); + Point aZero = pEditWin->GetMapMode().GetOrigin(); + if(lTabPosition != -1) + { + pEditWin->InvertTracking( + tools::Rectangle( Point(-aZero.X(), lTabPosition), + Point(-aZero.X() + nWidth, lTabPosition)), + ShowTrackFlags::Split | ShowTrackFlags::Clip ); + } + + if(nNew & 1) + { + tools::Long nDrapPosition = GetCorrectedDragPos(); + nDrapPosition = MakePositionSticky(nDrapPosition, GetLeftFrameMargin()); + lTabPosition = ConvertVSizeLogic(nDrapPosition + GetNullOffset()); + if (mxPagePosItem) + lTabPosition += mxPagePosItem->GetPos().Y(); + pEditWin->InvertTracking( + tools::Rectangle( Point(-aZero.X(), lTabPosition), + Point(-aZero.X()+nWidth, lTabPosition)), + ShowTrackFlags::Clip | ShowTrackFlags::Split ); + } + } +} + +void SvxRuler::DragTabs() +{ + /* Dragging of Tabs */ + tools::Long aDragPosition = GetCorrectedDragPos(true, false); + aDragPosition = MakePositionSticky(aDragPosition, GetLeftFrameMargin()); + + sal_uInt16 nIdx = GetDragAryPos() + TAB_GAP; + tools::Long nDiff = aDragPosition - mpTabs[nIdx].nPos; + if (nDiff == 0) + return; + + DrawLine_Impl(lTabPos, 7, bHorz); + + if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR) + { + + for(sal_uInt16 i = nIdx; i < nTabCount; ++i) + { + mpTabs[i].nPos += nDiff; + // limit on maximum + if(mpTabs[i].nPos > GetMargin2()) + mpTabs[nIdx].nStyle |= RULER_STYLE_INVISIBLE; + else + mpTabs[nIdx].nStyle &= ~RULER_STYLE_INVISIBLE; + } + } + else if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL) + { + mxRulerImpl->nTotalDist -= nDiff; + mpTabs[nIdx].nPos = aDragPosition; + for(sal_uInt16 i = nIdx+1; i < nTabCount; ++i) + { + if(mpTabs[i].nStyle & RULER_TAB_DEFAULT) + // can be canceled at the DefaultTabs + break; + tools::Long nDelta = mxRulerImpl->nTotalDist * mxRulerImpl->pPercBuf[i]; + nDelta /= 1000; + mpTabs[i].nPos = mpTabs[nIdx].nPos + nDelta; + if(mpTabs[i].nPos + GetNullOffset() > nMaxRight) + mpTabs[i].nStyle |= RULER_STYLE_INVISIBLE; + else + mpTabs[i].nStyle &= ~RULER_STYLE_INVISIBLE; + } + } + else + { + mpTabs[nIdx].nPos = aDragPosition; + } + + if(IsDragDelete()) + mpTabs[nIdx].nStyle |= RULER_STYLE_INVISIBLE; + else + mpTabs[nIdx].nStyle &= ~RULER_STYLE_INVISIBLE; + SetTabs(nTabCount, mpTabs.data() + TAB_GAP); +} + +void SvxRuler::SetActive(bool bOn) +{ + if(bOn) + { + Activate(); + } + else + Deactivate(); + if(bActive!=bOn) + { + pBindings->EnterRegistrations(); + if(bOn) + for(sal_uInt16 i=0;i<mxRulerImpl->nControllerItems;i++) + pCtrlItems[i]->ReBind(); + else + for(sal_uInt16 j=0;j<mxRulerImpl->nControllerItems;j++) + pCtrlItems[j]->UnBind(); + pBindings->LeaveRegistrations(); + } + bActive = bOn; +} + +void SvxRuler::UpdateParaContents_Impl( + tools::Long lDifference, + UpdateType eType) // Art (all, left or right) +{ + /* Helper function; carry Tabs and Paragraph Margins */ + switch(eType) + { + case UpdateType::MoveRight: + mpIndents[INDENT_RIGHT_MARGIN].nPos += lDifference; + break; + case UpdateType::MoveLeft: + { + mpIndents[INDENT_FIRST_LINE].nPos += lDifference; + mpIndents[INDENT_LEFT_MARGIN].nPos += lDifference; + if (!mpTabs.empty()) + { + for(sal_uInt16 i = 0; i < nTabCount+TAB_GAP; ++i) + { + mpTabs[i].nPos += lDifference; + } + SetTabs(nTabCount, mpTabs.data() + TAB_GAP); + } + break; + } + } + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); +} + +void SvxRuler::DragBorders() +{ + /* Dragging of Borders (Tables and other columns) */ + bool bLeftIndentsCorrected = false; + bool bRightIndentsCorrected = false; + int nIndex; + + if(GetDragType() == RulerType::Border) + { + DrawLine_Impl(lTabPos, 7, bHorz); + nIndex = GetDragAryPos(); + } + else + { + nIndex = 0; + } + + RulerDragSize nDragSize = GetDragSize(); + tools::Long lDiff = 0; + + // the drag position has to be corrected to be able to prevent borders from passing each other + tools::Long lPos = MakePositionSticky(GetCorrectedDragPos(), GetLeftFrameMargin()); + + switch(nDragSize) + { + case RulerDragSize::Move: + { + if(GetDragType() == RulerType::Border) + lDiff = lPos - nDragOffset - mpBorders[nIndex].nPos; + else + lDiff = GetDragType() == RulerType::Margin1 ? lPos - mxRulerImpl->lLastLMargin : lPos - mxRulerImpl->lLastRMargin; + + if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR) + { + tools::Long nRight = GetMargin2() - glMinFrame; // Right limiters + for(int i = mpBorders.size() - 2; i >= nIndex; --i) + { + tools::Long l = mpBorders[i].nPos; + mpBorders[i].nPos += lDiff; + mpBorders[i].nPos = std::min(mpBorders[i].nPos, nRight - mpBorders[i].nWidth); + nRight = mpBorders[i].nPos - glMinFrame; + // RR update the column + if(i == GetActRightColumn()) + { + UpdateParaContents_Impl(mpBorders[i].nPos - l, UpdateType::MoveRight); + bRightIndentsCorrected = true; + } + // LAR, EZE update the column + else if(i == GetActLeftColumn()) + { + UpdateParaContents_Impl(mpBorders[i].nPos - l, UpdateType::MoveLeft); + bLeftIndentsCorrected = true; + } + } + } + else if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL) + { + int nLimit; + tools::Long lLeft; + int nStartLimit = mpBorders.size() - 2; + switch(GetDragType()) + { + default: ;//prevent warning + OSL_FAIL("svx::SvxRuler::DragBorders(), unknown drag type!" ); + [[fallthrough]]; + case RulerType::Border: + if(mxRulerImpl->bIsTableRows) + { + mpBorders[nIndex].nPos += lDiff; + if(bHorz) + { + lLeft = mpBorders[nIndex].nPos; + mxRulerImpl->nTotalDist -= lDiff; + nLimit = nIndex + 1; + } + else + { + lLeft = 0; + nStartLimit = nIndex - 1; + mxRulerImpl->nTotalDist += lDiff; + nLimit = 0; + } + } + else + { + nLimit = nIndex + 1; + mpBorders[nIndex].nPos += lDiff; + lLeft = mpBorders[nIndex].nPos; + mxRulerImpl->nTotalDist -= lDiff; + } + break; + case RulerType::Margin1: + nLimit = 0; + lLeft = mxRulerImpl->lLastLMargin + lDiff; + mxRulerImpl->nTotalDist -= lDiff; + break; + case RulerType::Margin2: + nLimit = 0; + lLeft= 0; + nStartLimit = mpBorders.size() - 2; + mxRulerImpl->nTotalDist += lDiff; + break; + } + + for(int i = nStartLimit; i >= nLimit; --i) + { + + tools::Long l = mpBorders[i].nPos; + mpBorders[i].nPos = + lLeft + + (mxRulerImpl->nTotalDist * mxRulerImpl->pPercBuf[i]) / 1000 + + mxRulerImpl->pBlockBuf[i]; + + // RR update the column + if(!mxRulerImpl->bIsTableRows) + { + if(i == GetActRightColumn()) + { + UpdateParaContents_Impl(mpBorders[i].nPos - l, UpdateType::MoveRight); + bRightIndentsCorrected = true; + } + // LAR, EZE update the column + else if(i == GetActLeftColumn()) + { + UpdateParaContents_Impl(mpBorders[i].nPos - l, UpdateType::MoveLeft); + bLeftIndentsCorrected = true; + } + } + } + if(mxRulerImpl->bIsTableRows) + { + //in vertical tables the left borders have to be moved + if(bHorz) + { + for(int i = 0; i < nIndex; ++i) + mpBorders[i].nPos += lDiff; + AdjustMargin1(lDiff); + } + else + { + //otherwise the right borders are moved + for(int i = mxColumnItem->Count() - 1; i > nIndex; --i) + mpBorders[i].nPos += lDiff; + SetMargin2( GetMargin2() + lDiff, RulerMarginStyle::NONE ); + } + } + } + else if(mxRulerImpl->bIsTableRows) + { + //moving rows: if a row is resized all following rows + //have to be moved by the same amount. + //This includes the left border when the table is not limited + //to a lower frame border. + int nLimit; + if(GetDragType()==RulerType::Border) + { + nLimit = nIndex + 1; + mpBorders[nIndex].nPos += lDiff; + } + else + { + nLimit=0; + } + //in vertical tables the left borders have to be moved + if(bHorz) + { + for(int i = 0; i < nIndex; ++i) + { + mpBorders[i].nPos += lDiff; + } + AdjustMargin1(lDiff); + } + else + { + //otherwise the right borders are moved + for(int i = mpBorders.size() - 2; i >= nLimit; --i) + { + mpBorders[i].nPos += lDiff; + } + SetMargin2( GetMargin2() + lDiff, RulerMarginStyle::NONE ); + } + } + else + mpBorders[nIndex].nPos += lDiff; + break; + } + case RulerDragSize::N1: + { + lDiff = lPos - mpBorders[nIndex].nPos; + mpBorders[nIndex].nWidth += mpBorders[nIndex].nPos - lPos; + mpBorders[nIndex].nPos = lPos; + break; + } + case RulerDragSize::N2: + { + const tools::Long nOld = mpBorders[nIndex].nWidth; + mpBorders[nIndex].nWidth = lPos - mpBorders[nIndex].nPos; + lDiff = mpBorders[nIndex].nWidth - nOld; + break; + } + } + if(!bRightIndentsCorrected && + GetActRightColumn() == nIndex && + nDragSize != RulerDragSize::N2 && + !mpIndents.empty() && + !mxRulerImpl->bIsTableRows) + { + UpdateParaContents_Impl(lDiff, UpdateType::MoveRight); + } + else if(!bLeftIndentsCorrected && + GetActLeftColumn() == nIndex && + nDragSize != RulerDragSize::N1 && + !mpIndents.empty()) + { + UpdateParaContents_Impl(lDiff, UpdateType::MoveLeft); + } + SetBorders(mxColumnItem->Count() - 1, mpBorders.data()); +} + +void SvxRuler::DragObjectBorder() +{ + /* Dragging of object edges */ + if(RulerDragSize::Move == GetDragSize()) + { + const tools::Long lPosition = MakePositionSticky(GetCorrectedDragPos(), GetLeftFrameMargin()); + + const sal_uInt16 nIdx = GetDragAryPos(); + mpObjectBorders[GetObjectBordersOff(nIdx)].nPos = lPosition; + SetBorders(2, mpObjectBorders.data() + GetObjectBordersOff(0)); + DrawLine_Impl(lTabPos, 7, bHorz); + + } +} + +void SvxRuler::ApplyMargins() +{ + /* Applying margins; changed by dragging. */ + const SfxPoolItem* pItem = nullptr; + sal_uInt16 nId = SID_ATTR_LONG_LRSPACE; + + if(bHorz) + { + const tools::Long lOldNull = lLogicNullOffset; + if(mxRulerImpl->lMaxLeftLogic != -1 && nMaxLeft == GetMargin1() + Ruler::GetNullOffset()) + { + lLogicNullOffset = mxRulerImpl->lMaxLeftLogic; + mxLRSpaceItem->SetLeft(lLogicNullOffset); + } + else + { + lLogicNullOffset = ConvertHPosLogic(GetFrameLeft()) - lAppNullOffset; + mxLRSpaceItem->SetLeft(PixelHAdjust(lLogicNullOffset, mxLRSpaceItem->GetLeft())); + } + + if(bAppSetNullOffset) + { + lAppNullOffset += lLogicNullOffset - lOldNull; + } + + tools::Long nRight; + if(mxRulerImpl->lMaxRightLogic != -1 + && nMaxRight == GetMargin2() + Ruler::GetNullOffset()) + { + nRight = GetPageWidth() - mxRulerImpl->lMaxRightLogic; + } + else + { + nRight = std::max(tools::Long(0), + mxPagePosItem->GetWidth() - mxLRSpaceItem->GetLeft() - + (ConvertHPosLogic(GetMargin2()) - lAppNullOffset)); + + nRight = PixelHAdjust( nRight, mxLRSpaceItem->GetRight()); + } + mxLRSpaceItem->SetRight(nRight); + + pItem = mxLRSpaceItem.get(); + +#ifdef DEBUGLIN + Debug_Impl(pEditWin, *mxLRSpaceItem); +#endif // DEBUGLIN + + } + else + { + const tools::Long lOldNull = lLogicNullOffset; + lLogicNullOffset = + ConvertVPosLogic(GetFrameLeft()) - + lAppNullOffset; + mxULSpaceItem->SetUpper( + PixelVAdjust(lLogicNullOffset, mxULSpaceItem->GetUpper())); + if(bAppSetNullOffset) + { + lAppNullOffset += lLogicNullOffset - lOldNull; + } + mxULSpaceItem->SetLower( + PixelVAdjust( + std::max(tools::Long(0), mxPagePosItem->GetHeight() - + mxULSpaceItem->GetUpper() - + (ConvertVPosLogic(GetMargin2()) - + lAppNullOffset)), mxULSpaceItem->GetLower())); + pItem = mxULSpaceItem.get(); + nId = SID_ATTR_LONG_ULSPACE; + +#ifdef DEBUGLIN + Debug_Impl(pEditWin,*mxULSpaceItem); +#endif // DEBUGLIN + + } + pBindings->GetDispatcher()->ExecuteList(nId, SfxCallMode::RECORD, { pItem }); + if (mxTabStopItem) + UpdateTabs(); +} + +tools::Long SvxRuler::RoundToCurrentMapMode(tools::Long lValue) const +{ + RulerUnitData aUnitData = GetCurrentRulerUnit(); + double aRoundingFactor = aUnitData.nTickUnit / aUnitData.nTick1; + + tools::Long lNewValue = OutputDevice::LogicToLogic(Size(lValue, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width(); + lNewValue = (rtl::math::round(lNewValue / static_cast<double>(aUnitData.nTickUnit) * aRoundingFactor) / aRoundingFactor) * aUnitData.nTickUnit; + return OutputDevice::LogicToLogic(Size(lNewValue, 0), GetCurrentMapMode(), pEditWin->GetMapMode()).Width(); +} + +void SvxRuler::ApplyIndents() +{ + /* Applying paragraph settings; changed by dragging. */ + + tools::Long nLeftFrameMargin = GetLeftFrameMargin(); + + bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue(); + + tools::Long nNewTxtLeft; + tools::Long nNewFirstLineOffset; + tools::Long nNewRight; + + tools::Long nFirstLine = ConvertPosLogic(mpIndents[INDENT_FIRST_LINE].nPos); + tools::Long nLeftMargin = ConvertPosLogic(mpIndents[INDENT_LEFT_MARGIN].nPos); + tools::Long nRightMargin = ConvertPosLogic(mpIndents[INDENT_RIGHT_MARGIN].nPos); + + if(mxColumnItem && ((bRTL && !IsActLastColumn(true)) || (!bRTL && !IsActFirstColumn(true)))) + { + if(bRTL) + { + tools::Long nRightColumn = GetActRightColumn(true); + tools::Long nRightBorder = ConvertPosLogic(mpBorders[nRightColumn].nPos); + nNewTxtLeft = nRightBorder - nLeftMargin - lAppNullOffset; + } + else + { + tools::Long nLeftColumn = GetActLeftColumn(true); + tools::Long nLeftBorder = ConvertPosLogic(mpBorders[nLeftColumn].nPos + mpBorders[nLeftColumn].nWidth); + nNewTxtLeft = nLeftMargin - nLeftBorder - lAppNullOffset; + } + } + else + { + if(bRTL) + { + tools::Long nRightBorder = ConvertPosLogic(GetMargin2()); + nNewTxtLeft = nRightBorder - nLeftMargin - lAppNullOffset; + } + else + { + tools::Long nLeftBorder = ConvertPosLogic(GetMargin1()); + nNewTxtLeft = nLeftBorder + nLeftMargin - nLeftFrameMargin - lAppNullOffset; + } + } + + if(bRTL) + nNewFirstLineOffset = nLeftMargin - nFirstLine - lAppNullOffset; + else + nNewFirstLineOffset = nFirstLine - nLeftMargin - lAppNullOffset; + + if(mxColumnItem && ((!bRTL && !IsActLastColumn(true)) || (bRTL && !IsActFirstColumn(true)))) + { + if(bRTL) + { + tools::Long nLeftColumn = GetActLeftColumn(true); + tools::Long nLeftBorder = ConvertPosLogic(mpBorders[nLeftColumn].nPos + mpBorders[nLeftColumn].nWidth); + nNewRight = nRightMargin - nLeftBorder - lAppNullOffset; + } + else + { + tools::Long nRightColumn = GetActRightColumn(true); + tools::Long nRightBorder = ConvertPosLogic(mpBorders[nRightColumn].nPos); + nNewRight = nRightBorder - nRightMargin - lAppNullOffset; + } + } + else + { + if(bRTL) + { + tools::Long nLeftBorder = ConvertPosLogic(GetMargin1()); + nNewRight = nLeftBorder + nRightMargin - nLeftFrameMargin - lAppNullOffset; + } + else + { + tools::Long nRightBorder = ConvertPosLogic(GetMargin2()); + nNewRight = nRightBorder - nRightMargin - lAppNullOffset; + } + } + + if (mbSnapping) + { + nNewTxtLeft = RoundToCurrentMapMode(nNewTxtLeft); + nNewFirstLineOffset = RoundToCurrentMapMode(nNewFirstLineOffset); + nNewRight = RoundToCurrentMapMode(nNewRight); + } + + mxParaItem->SetTextFirstLineOffset(sal::static_int_cast<short>(nNewFirstLineOffset)); + mxParaItem->SetTextLeft(nNewTxtLeft); + mxParaItem->SetRight(nNewRight); + + sal_uInt16 nParagraphId = bHorz ? SID_ATTR_PARA_LRSPACE : SID_ATTR_PARA_LRSPACE_VERTICAL; + pBindings->GetDispatcher()->ExecuteList(nParagraphId, SfxCallMode::RECORD, + { mxParaItem.get() }); + UpdateTabs(); +} + +void SvxRuler::ApplyTabs() +{ + /* Apply tab settings, changed by dragging. */ + bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue(); + const sal_uInt16 nCoreIdx = GetDragAryPos(); + if(IsDragDelete()) + { + mxTabStopItem->Remove(nCoreIdx); + } + else if(SvxRulerDragFlags::OBJECT_SIZE_LINEAR & nDragType || + SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL & nDragType) + { + SvxTabStopItem *pItem = new SvxTabStopItem(mxTabStopItem->Which()); + //remove default tab stops + for ( sal_uInt16 i = 0; i < pItem->Count(); ) + { + if ( SvxTabAdjust::Default == (*pItem)[i].GetAdjustment() ) + { + pItem->Remove(i); + continue; + } + ++i; + } + + sal_uInt16 j; + for(j = 0; j < nCoreIdx; ++j) + { + pItem->Insert(mxTabStopItem->At(j)); + } + for(; j < mxTabStopItem->Count(); ++j) + { + SvxTabStop aTabStop = mxTabStopItem->At(j); + aTabStop.GetTabPos() = PixelHAdjust( + ConvertHPosLogic( + mpTabs[j + TAB_GAP].nPos - GetLeftIndent()) - lAppNullOffset, + aTabStop.GetTabPos()); + pItem->Insert(aTabStop); + } + mxTabStopItem.reset(pItem); + } + else if( mxTabStopItem->Count() == 0 ) + return; + else + { + SvxTabStop aTabStop = mxTabStopItem->At(nCoreIdx); + if( mxRulerImpl->lMaxRightLogic != -1 && + mpTabs[nCoreIdx + TAB_GAP].nPos + Ruler::GetNullOffset() == nMaxRight ) + { + // Set tab pos exactly at the right indent + tools::Long nTmpLeftIndentLogic + = lAppNullOffset + (bRTL ? GetRightFrameMargin() : GetLeftFrameMargin()); + if (mxRulerImpl->bIsTabsRelativeToIndent && mxParaItem) + { + nTmpLeftIndentLogic += bRTL ? mxParaItem->GetRight() : mxParaItem->GetTextLeft(); + } + aTabStop.GetTabPos() + = mxRulerImpl->lMaxRightLogic - lLogicNullOffset - nTmpLeftIndentLogic; + } + else + { + if(bRTL) + { + //#i24363# tab stops relative to indent + const tools::Long nTmpLeftIndent = mxRulerImpl->bIsTabsRelativeToIndent ? + GetLeftIndent() : + ConvertHPosPixel( GetRightFrameMargin() + lAppNullOffset ); + + tools::Long nNewPosition = ConvertHPosLogic(nTmpLeftIndent - mpTabs[nCoreIdx + TAB_GAP].nPos); + aTabStop.GetTabPos() = PixelHAdjust(nNewPosition - lAppNullOffset, aTabStop.GetTabPos()); + } + else + { + //#i24363# tab stops relative to indent + const tools::Long nTmpLeftIndent = mxRulerImpl->bIsTabsRelativeToIndent ? + GetLeftIndent() : + ConvertHPosPixel( GetLeftFrameMargin() + lAppNullOffset ); + + tools::Long nNewPosition = ConvertHPosLogic(mpTabs[nCoreIdx + TAB_GAP].nPos - nTmpLeftIndent); + aTabStop.GetTabPos() = PixelHAdjust(nNewPosition - lAppNullOffset, aTabStop.GetTabPos()); + } + } + mxTabStopItem->Remove(nCoreIdx); + mxTabStopItem->Insert(aTabStop); + } + sal_uInt16 nTabStopId = bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL; + pBindings->GetDispatcher()->ExecuteList(nTabStopId, SfxCallMode::RECORD, + { mxTabStopItem.get() }); + UpdateTabs(); +} + +void SvxRuler::ApplyBorders() +{ + /* Applying (table) column settings; changed by dragging. */ + if(mxColumnItem->IsTable()) + { + tools::Long lValue = GetFrameLeft(); + if(lValue != mxRulerImpl->nColLeftPix) + { + tools::Long nLeft = PixelHAdjust( + ConvertHPosLogic(lValue) - + lAppNullOffset, + mxColumnItem->GetLeft()); + mxColumnItem->SetLeft(nLeft); + } + + lValue = GetMargin2(); + + if(lValue != mxRulerImpl->nColRightPix) + { + tools::Long nWidthOrHeight = bHorz ? mxPagePosItem->GetWidth() : mxPagePosItem->GetHeight(); + tools::Long nRight = PixelHAdjust( + nWidthOrHeight - + mxColumnItem->GetLeft() - + ConvertHPosLogic(lValue) - + lAppNullOffset, + mxColumnItem->GetRight() ); + mxColumnItem->SetRight(nRight); + } + } + + for(sal_uInt16 i = 0; i < mxColumnItem->Count() - 1; ++i) + { + tools::Long& nEnd = mxColumnItem->At(i).nEnd; + nEnd = PixelHAdjust( + ConvertPosLogic(mpBorders[i].nPos), + mxColumnItem->At(i).nEnd); + tools::Long& nStart = mxColumnItem->At(i + 1).nStart; + nStart = PixelHAdjust( + ConvertSizeLogic(mpBorders[i].nPos + + mpBorders[i].nWidth) - + lAppNullOffset, + mxColumnItem->At(i + 1).nStart); + // It may be that, due to the PixelHAdjust readjustment to old values, + // the width becomes < 0. This we readjust. + if( nEnd > nStart ) + nStart = nEnd; + } + +#ifdef DEBUGLIN + Debug_Impl(pEditWin,*mxColumnItem); +#endif // DEBUGLIN + + SfxBoolItem aFlag(SID_RULER_ACT_LINE_ONLY, + bool(nDragType & SvxRulerDragFlags::OBJECT_ACTLINE_ONLY)); + + sal_uInt16 nColId = mxRulerImpl->bIsTableRows ? (bHorz ? SID_RULER_ROWS : SID_RULER_ROWS_VERTICAL) : + (bHorz ? SID_RULER_BORDERS : SID_RULER_BORDERS_VERTICAL); + + pBindings->GetDispatcher()->ExecuteList(nColId, SfxCallMode::RECORD, + { mxColumnItem.get(), &aFlag }); +} + +void SvxRuler::ApplyObject() +{ + /* Applying object settings, changed by dragging. */ + + // to the page margin + tools::Long nMargin = mxLRSpaceItem ? mxLRSpaceItem->GetLeft() : 0; + tools::Long nStartX = PixelAdjust( + ConvertPosLogic(mpObjectBorders[0].nPos) + + nMargin - + lAppNullOffset, + mxObjectItem->GetStartX()); + mxObjectItem->SetStartX(nStartX); + + tools::Long nEndX = PixelAdjust( + ConvertPosLogic(mpObjectBorders[1].nPos) + + nMargin - + lAppNullOffset, + mxObjectItem->GetEndX()); + mxObjectItem->SetEndX(nEndX); + + nMargin = mxULSpaceItem ? mxULSpaceItem->GetUpper() : 0; + tools::Long nStartY = PixelAdjust( + ConvertPosLogic(mpObjectBorders[2].nPos) + + nMargin - + lAppNullOffset, + mxObjectItem->GetStartY()); + mxObjectItem->SetStartY(nStartY); + + tools::Long nEndY = PixelAdjust( + ConvertPosLogic(mpObjectBorders[3].nPos) + + nMargin - + lAppNullOffset, + mxObjectItem->GetEndY()); + mxObjectItem->SetEndY(nEndY); + + pBindings->GetDispatcher()->ExecuteList(SID_RULER_OBJECT, + SfxCallMode::RECORD, { mxObjectItem.get() }); +} + +void SvxRuler::PrepareProportional_Impl(RulerType eType) +{ + /* + Preparation proportional dragging, and it is calculated based on the + proportional share of the total width in parts per thousand. + */ + mxRulerImpl->nTotalDist = GetMargin2(); + switch(eType) + { + case RulerType::Margin2: + case RulerType::Margin1: + case RulerType::Border: + { + DBG_ASSERT(mxColumnItem, "no ColumnItem"); + + mxRulerImpl->SetPercSize(mxColumnItem->Count()); + + tools::Long lPos; + tools::Long lWidth=0; + sal_uInt16 nStart; + sal_uInt16 nIdx=GetDragAryPos(); + tools::Long lActWidth=0; + tools::Long lActBorderSum; + tools::Long lOrigLPos; + + if(eType != RulerType::Border) + { + lOrigLPos = GetMargin1(); + nStart = 0; + lActBorderSum = 0; + } + else + { + if(mxRulerImpl->bIsTableRows &&!bHorz) + { + lOrigLPos = GetMargin1(); + nStart = 0; + } + else + { + lOrigLPos = mpBorders[nIdx].nPos + mpBorders[nIdx].nWidth; + nStart = 1; + } + lActBorderSum = mpBorders[nIdx].nWidth; + } + + //in horizontal mode the percentage value has to be + //calculated on a "current change" position base + //because the height of the table changes while dragging + if(mxRulerImpl->bIsTableRows && RulerType::Border == eType) + { + sal_uInt16 nStartBorder; + sal_uInt16 nEndBorder; + if(bHorz) + { + nStartBorder = nIdx + 1; + nEndBorder = mxColumnItem->Count() - 1; + } + else + { + nStartBorder = 0; + nEndBorder = nIdx; + } + + lWidth = mpBorders[nIdx].nPos; + if(bHorz) + lWidth = GetMargin2() - lWidth; + mxRulerImpl->nTotalDist = lWidth; + lPos = mpBorders[nIdx].nPos; + + for(sal_uInt16 i = nStartBorder; i < nEndBorder; ++i) + { + if(bHorz) + { + lActWidth += mpBorders[i].nPos - lPos; + lPos = mpBorders[i].nPos + mpBorders[i].nWidth; + } + else + lActWidth = mpBorders[i].nPos; + mxRulerImpl->pPercBuf[i] = static_cast<sal_uInt16>((lActWidth * 1000) + / mxRulerImpl->nTotalDist); + mxRulerImpl->pBlockBuf[i] = static_cast<sal_uInt16>(lActBorderSum); + lActBorderSum += mpBorders[i].nWidth; + } + } + else + { + lPos = lOrigLPos; + for(sal_uInt16 ii = nStart; ii < mxColumnItem->Count() - 1; ++ii) + { + lWidth += mpBorders[ii].nPos - lPos; + lPos = mpBorders[ii].nPos + mpBorders[ii].nWidth; + } + + lWidth += GetMargin2() - lPos; + mxRulerImpl->nTotalDist = lWidth; + lPos = lOrigLPos; + + for(sal_uInt16 i = nStart; i < mxColumnItem->Count() - 1; ++i) + { + lActWidth += mpBorders[i].nPos - lPos; + lPos = mpBorders[i].nPos + mpBorders[i].nWidth; + mxRulerImpl->pPercBuf[i] = static_cast<sal_uInt16>((lActWidth * 1000) + / mxRulerImpl->nTotalDist); + mxRulerImpl->pBlockBuf[i] = static_cast<sal_uInt16>(lActBorderSum); + lActBorderSum += mpBorders[i].nWidth; + } + } + } + break; + case RulerType::Tab: + { + const sal_uInt16 nIdx = GetDragAryPos()+TAB_GAP; + mxRulerImpl->nTotalDist -= mpTabs[nIdx].nPos; + mxRulerImpl->SetPercSize(nTabCount); + for(sal_uInt16 n=0;n<=nIdx;mxRulerImpl->pPercBuf[n++]=0) ; + for(sal_uInt16 i = nIdx+1; i < nTabCount; ++i) + { + const tools::Long nDelta = mpTabs[i].nPos - mpTabs[nIdx].nPos; + mxRulerImpl->pPercBuf[i] = static_cast<sal_uInt16>((nDelta * 1000) / mxRulerImpl->nTotalDist); + } + break; + } + default: break; + } +} + +void SvxRuler::EvalModifier() +{ + /* + Eval Drag Modifier + Shift: move linear + Control: move proportional + Shift + Control: Table: only current line + Alt: disable snapping + Alt + Shift: coarse snapping + */ + + sal_uInt16 nModifier = GetDragModifier(); + if(mxRulerImpl->bIsTableRows) + { + //rows can only be moved in one way, additionally current column is possible + if(nModifier == KEY_SHIFT) + nModifier = 0; + } + + switch(nModifier) + { + case KEY_SHIFT: + nDragType = SvxRulerDragFlags::OBJECT_SIZE_LINEAR; + break; + case KEY_MOD2 | KEY_SHIFT: + mbCoarseSnapping = true; + break; + case KEY_MOD2: + mbSnapping = false; + break; + case KEY_MOD1: + { + const RulerType eType = GetDragType(); + nDragType = SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL; + if( RulerType::Tab == eType || + ( ( RulerType::Border == eType || + RulerType::Margin1 == eType || + RulerType::Margin2 == eType ) && + mxColumnItem ) ) + { + PrepareProportional_Impl(eType); + } + } + break; + case KEY_MOD1 | KEY_SHIFT: + if( GetDragType() != RulerType::Margin1 && + GetDragType() != RulerType::Margin2 ) + { + nDragType = SvxRulerDragFlags::OBJECT_ACTLINE_ONLY; + } + break; + } +} + +void SvxRuler::Click() +{ + /* Override handler SV; sets Tab per dispatcher call */ + Ruler::Click(); + if( bActive ) + { + pBindings->Update( SID_RULER_LR_MIN_MAX ); + pBindings->Update( SID_ATTR_LONG_ULSPACE ); + pBindings->Update( SID_ATTR_LONG_LRSPACE ); + pBindings->Update( SID_RULER_PAGE_POS ); + pBindings->Update( bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL); + pBindings->Update( bHorz ? SID_ATTR_PARA_LRSPACE : SID_ATTR_PARA_LRSPACE_VERTICAL); + pBindings->Update( bHorz ? SID_RULER_BORDERS : SID_RULER_BORDERS_VERTICAL); + pBindings->Update( bHorz ? SID_RULER_ROWS : SID_RULER_ROWS_VERTICAL); + pBindings->Update( SID_RULER_OBJECT ); + pBindings->Update( SID_RULER_PROTECT ); + pBindings->Update( SID_ATTR_PARA_LRSPACE_VERTICAL ); + } + bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue(); + if(!(mxTabStopItem && + (nFlags & SvxRulerSupportFlags::TABS) == SvxRulerSupportFlags::TABS)) + return; + + bool bContentProtected = mxRulerImpl->aProtectItem->IsContentProtected(); + if( bContentProtected ) return; + const tools::Long lPos = GetClickPos(); + if(!((bRTL && lPos < std::min(GetFirstLineIndent(), GetLeftIndent()) && lPos > GetRightIndent()) || + (!bRTL && lPos > std::min(GetFirstLineIndent(), GetLeftIndent()) && lPos < GetRightIndent()))) + return; + + //convert position in left-to-right text + tools::Long nTabPos; +//#i24363# tab stops relative to indent + if(bRTL) + nTabPos = ( mxRulerImpl->bIsTabsRelativeToIndent ? + GetLeftIndent() : + ConvertHPosPixel( GetRightFrameMargin() + lAppNullOffset ) ) - + lPos; + else + nTabPos = lPos - + ( mxRulerImpl->bIsTabsRelativeToIndent ? + GetLeftIndent() : + ConvertHPosPixel( GetLeftFrameMargin() + lAppNullOffset )); + + SvxTabStop aTabStop(ConvertHPosLogic(nTabPos), + ToAttrTab_Impl(nDefTabType)); + mxTabStopItem->Insert(aTabStop); + UpdateTabs(); +} + +void SvxRuler::CalcMinMax() +{ + /* + Calculates the limits for dragging; which are in pixels relative to the + page edge + */ + bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue(); + const tools::Long lNullPix = ConvertPosPixel(lLogicNullOffset); + mxRulerImpl->lMaxLeftLogic=mxRulerImpl->lMaxRightLogic=-1; + switch(GetDragType()) + { + case RulerType::Margin1: + { // left edge of the surrounding Frame + // DragPos - NOf between left - right + mxRulerImpl->lMaxLeftLogic = GetLeftMin(); + nMaxLeft=ConvertSizePixel(mxRulerImpl->lMaxLeftLogic); + + if (!mxColumnItem || mxColumnItem->Count() == 1) + { + if(bRTL) + { + nMaxRight = lNullPix - GetRightIndent() + + std::max(GetFirstLineIndent(), GetLeftIndent()) - + glMinFrame; + } + else + { + nMaxRight = lNullPix + GetRightIndent() - + std::max(GetFirstLineIndent(), GetLeftIndent()) - + glMinFrame; + } + } + else if(mxRulerImpl->bIsTableRows) + { + //top border is not moveable when table rows are displayed + // protection of content means the margin is not moveable + if(bHorz && !mxRulerImpl->aProtectItem->IsContentProtected()) + { + nMaxLeft = mpBorders[0].nMinPos + lNullPix; + if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL) + nMaxRight = GetRightIndent() + lNullPix - + (mxColumnItem->Count() - 1 ) * glMinFrame; + else + nMaxRight = mpBorders[0].nPos - glMinFrame + lNullPix; + } + else + nMaxLeft = nMaxRight = lNullPix; + } + else + { + if (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL) + { + nMaxRight=lNullPix+CalcPropMaxRight(); + } + else if (nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR) + { + nMaxRight = ConvertPosPixel( + GetPageWidth() - ( + (mxColumnItem->IsTable() && mxLRSpaceItem) + ? mxLRSpaceItem->GetRight() : 0)) + - GetMargin2() + GetMargin1(); + } + else + { + nMaxRight = lNullPix - glMinFrame; + if (mxColumnItem->IsFirstAct()) + { + if(bRTL) + { + nMaxRight += std::min( + mpBorders[0].nPos, + std::max(GetFirstLineIndent(), GetLeftIndent()) - GetRightIndent()); + } + else + { + nMaxRight += std::min( + mpBorders[0].nPos, GetRightIndent() - + std::max(GetFirstLineIndent(), GetLeftIndent())); + } + } + else if ( mxColumnItem->Count() > 1 ) + { + nMaxRight += mpBorders[0].nPos; + } + else + { + nMaxRight += GetRightIndent() - std::max(GetFirstLineIndent(), GetLeftIndent()); + } + // Do not drag the left table edge over the edge of the page + if(mxLRSpaceItem && mxColumnItem->IsTable()) + { + tools::Long nTmp=ConvertSizePixel(mxLRSpaceItem->GetLeft()); + if(nTmp>nMaxLeft) + nMaxLeft=nTmp; + } + } + } + break; + } + case RulerType::Margin2: + { // right edge of the surrounding Frame + mxRulerImpl->lMaxRightLogic + = mxMinMaxItem ? GetPageWidth() - GetRightMax() : GetPageWidth(); + nMaxRight = ConvertSizePixel(mxRulerImpl->lMaxRightLogic); + + if (!mxColumnItem) + { + if(bRTL) + { + nMaxLeft = GetMargin2() + GetRightIndent() - + std::max(GetFirstLineIndent(),GetLeftIndent()) - GetMargin1()+ + glMinFrame + lNullPix; + } + else + { + nMaxLeft = GetMargin2() - GetRightIndent() + + std::max(GetFirstLineIndent(),GetLeftIndent()) - GetMargin1()+ + glMinFrame + lNullPix; + } + } + else if(mxRulerImpl->bIsTableRows) + { + // get the bottom move range from the last border position - only available for rows! + // protection of content means the margin is not moveable + if(bHorz || mxRulerImpl->aProtectItem->IsContentProtected()) + { + nMaxLeft = nMaxRight = mpBorders[mxColumnItem->Count() - 1].nMaxPos + lNullPix; + } + else + { + if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL) + { + nMaxLeft = (mxColumnItem->Count()) * glMinFrame + lNullPix; + } + else + { + if(mxColumnItem->Count() > 1) + nMaxLeft = mpBorders[mxColumnItem->Count() - 2].nPos + glMinFrame + lNullPix; + else + nMaxLeft = glMinFrame + lNullPix; + } + if(mxColumnItem->Count() > 1) + nMaxRight = mpBorders[mxColumnItem->Count() - 2].nMaxPos + lNullPix; + else + nMaxRight -= GetRightIndent() - lNullPix; + } + } + else + { + nMaxLeft = glMinFrame + lNullPix; + if(IsActLastColumn() || mxColumnItem->Count() < 2 ) //If last active column + { + if(bRTL) + { + nMaxLeft = glMinFrame + lNullPix + GetMargin2() + + GetRightIndent() - std::max(GetFirstLineIndent(), + GetLeftIndent()); + } + else + { + nMaxLeft = glMinFrame + lNullPix + GetMargin2() - + GetRightIndent() + std::max(GetFirstLineIndent(), + GetLeftIndent()); + } + } + if( mxColumnItem->Count() >= 2 ) + { + tools::Long nNewMaxLeft = + glMinFrame + lNullPix + + mpBorders[mxColumnItem->Count() - 2].nPos + + mpBorders[mxColumnItem->Count() - 2].nWidth; + nMaxLeft = std::max(nMaxLeft, nNewMaxLeft); + } + + } + break; + } + case RulerType::Border: + { // Table, column (Modifier) + const sal_uInt16 nIdx = GetDragAryPos(); + switch(GetDragSize()) + { + case RulerDragSize::N1 : + { + nMaxRight = mpBorders[nIdx].nPos + + mpBorders[nIdx].nWidth + lNullPix; + + if(0 == nIdx) + nMaxLeft = lNullPix; + else + nMaxLeft = mpBorders[nIdx - 1].nPos + mpBorders[nIdx - 1].nWidth + lNullPix; + if (mxColumnItem && nIdx == mxColumnItem->GetActColumn()) + { + if(bRTL) + { + nMaxLeft += mpBorders[nIdx].nPos + + GetRightIndent() - std::max(GetFirstLineIndent(), + GetLeftIndent()); + } + else + { + nMaxLeft += mpBorders[nIdx].nPos - + GetRightIndent() + std::max(GetFirstLineIndent(), + GetLeftIndent()); + } + if(0 != nIdx) + nMaxLeft -= mpBorders[nIdx-1].nPos + + mpBorders[nIdx-1].nWidth; + } + nMaxLeft += glMinFrame; + nMaxLeft += nDragOffset; + break; + } + case RulerDragSize::Move: + { + if (mxColumnItem) + { + //nIdx contains the position of the currently moved item + //next visible separator on the left + sal_uInt16 nLeftCol=GetActLeftColumn(false, nIdx); + //next visible separator on the right + sal_uInt16 nRightCol=GetActRightColumn(false, nIdx); + //next separator on the left - regardless if visible or not + sal_uInt16 nActLeftCol=GetActLeftColumn(); + //next separator on the right - regardless if visible or not + sal_uInt16 nActRightCol=GetActRightColumn(); + if(mxColumnItem->IsTable()) + { + if(nDragType & SvxRulerDragFlags::OBJECT_ACTLINE_ONLY) + { + //the current row/column should be modified only + //then the next/previous visible border position + //marks the min/max positions + nMaxLeft = nLeftCol == USHRT_MAX ? + 0 : + mpBorders[nLeftCol].nPos; + //rows can always be increased without a limit + if(mxRulerImpl->bIsTableRows) + nMaxRight = mpBorders[nIdx].nMaxPos; + else + nMaxRight = nRightCol == USHRT_MAX ? + GetMargin2(): + mpBorders[nRightCol].nPos; + nMaxLeft += lNullPix; + nMaxRight += lNullPix; + } + else + { + if(SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL & nDragType && !bHorz && mxRulerImpl->bIsTableRows) + nMaxLeft = (nIdx + 1) * glMinFrame + lNullPix; + else + nMaxLeft = mpBorders[nIdx].nMinPos + lNullPix; + if((SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL & nDragType) || + (SvxRulerDragFlags::OBJECT_SIZE_LINEAR & nDragType) ) + { + if(mxRulerImpl->bIsTableRows) + { + if(bHorz) + nMaxRight = GetRightIndent() + lNullPix - + (mxColumnItem->Count() - nIdx - 1) * glMinFrame; + else + nMaxRight = mpBorders[nIdx].nMaxPos + lNullPix; + } + else + nMaxRight=lNullPix+CalcPropMaxRight(nIdx); + } + else + nMaxRight = mpBorders[nIdx].nMaxPos + lNullPix; + } + nMaxLeft += glMinFrame; + nMaxRight -= glMinFrame; + + } + else + { + if(nLeftCol==USHRT_MAX) + nMaxLeft=lNullPix; + else + nMaxLeft = mpBorders[nLeftCol].nPos + + mpBorders[nLeftCol].nWidth + lNullPix; + + if(nActRightCol == nIdx) + { + if(bRTL) + { + nMaxLeft += mpBorders[nIdx].nPos + + GetRightIndent() - std::max(GetFirstLineIndent(), + GetLeftIndent()); + if(nActLeftCol!=USHRT_MAX) + nMaxLeft -= mpBorders[nActLeftCol].nPos + + mpBorders[nActLeftCol].nWidth; + } + else + { + nMaxLeft += mpBorders[nIdx].nPos - + GetRightIndent() + std::max(GetFirstLineIndent(), + GetLeftIndent()); + if(nActLeftCol!=USHRT_MAX) + nMaxLeft -= mpBorders[nActLeftCol].nPos + + mpBorders[nActLeftCol].nWidth; + } + } + nMaxLeft += glMinFrame; + nMaxLeft += nDragOffset; + + // nMaxRight + // linear / proportional move + if((SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL & nDragType) || + (SvxRulerDragFlags::OBJECT_SIZE_LINEAR & nDragType) ) + { + nMaxRight=lNullPix+CalcPropMaxRight(nIdx); + } + else if(SvxRulerDragFlags::OBJECT_SIZE_LINEAR & nDragType) + { + nMaxRight = lNullPix + GetMargin2() - GetMargin1() + + (mpBorders.size() - nIdx - 1) * glMinFrame; + } + else + { + if(nRightCol==USHRT_MAX) + { // last column + nMaxRight = GetMargin2() + lNullPix; + if(IsActLastColumn()) + { + if(bRTL) + { + nMaxRight -= + GetMargin2() + GetRightIndent() - + std::max(GetFirstLineIndent(), + GetLeftIndent()); + } + else + { + nMaxRight -= + GetMargin2() - GetRightIndent() + + std::max(GetFirstLineIndent(), + GetLeftIndent()); + } + nMaxRight += mpBorders[nIdx].nPos + + mpBorders[nIdx].nWidth; + } + } + else + { + nMaxRight = lNullPix + mpBorders[nRightCol].nPos; + sal_uInt16 nNotHiddenRightCol = + GetActRightColumn(true, nIdx); + + if( nActLeftCol == nIdx ) + { + tools::Long nBorder = nNotHiddenRightCol == + USHRT_MAX ? + GetMargin2() : + mpBorders[nNotHiddenRightCol].nPos; + if(bRTL) + { + nMaxRight -= nBorder + GetRightIndent() - + std::max(GetFirstLineIndent(), + GetLeftIndent()); + } + else + { + nMaxRight -= nBorder - GetRightIndent() + + std::max(GetFirstLineIndent(), + GetLeftIndent()); + } + nMaxRight += mpBorders[nIdx].nPos + + mpBorders[nIdx].nWidth; + } + } + nMaxRight -= glMinFrame; + nMaxRight -= mpBorders[nIdx].nWidth; + } + } + } + // ObjectItem + else + { + nMaxLeft = LONG_MIN; + nMaxRight = LONG_MAX; + } + break; + } + case RulerDragSize::N2: + if (mxColumnItem) + { + nMaxLeft = lNullPix + mpBorders[nIdx].nPos; + if(nIdx == mxColumnItem->Count()-2) { // last column + nMaxRight = GetMargin2() + lNullPix; + if(mxColumnItem->IsLastAct()) { + nMaxRight -= + GetMargin2() - GetRightIndent() + + std::max(GetFirstLineIndent(), + GetLeftIndent()); + nMaxRight += mpBorders[nIdx].nPos + + mpBorders[nIdx].nWidth; + } + } + else { + nMaxRight = lNullPix + mpBorders[nIdx+1].nPos; + if(mxColumnItem->GetActColumn()-1 == nIdx) { + nMaxRight -= mpBorders[nIdx+1].nPos - GetRightIndent() + + std::max(GetFirstLineIndent(), + GetLeftIndent()); + nMaxRight += mpBorders[nIdx].nPos + + mpBorders[nIdx].nWidth; + } + } + nMaxRight -= glMinFrame; + nMaxRight -= mpBorders[nIdx].nWidth; + break; + } + } + nMaxRight += nDragOffset; + break; + } + case RulerType::Indent: + { + const sal_uInt16 nIdx = GetDragAryPos(); + switch(nIdx) { + case INDENT_FIRST_LINE - INDENT_GAP: + case INDENT_LEFT_MARGIN - INDENT_GAP: + { + if(bRTL) + { + nMaxLeft = lNullPix + GetRightIndent(); + + if(mxColumnItem && !mxColumnItem->IsFirstAct()) + nMaxLeft += mpBorders[mxColumnItem->GetActColumn()-1].nPos + + mpBorders[mxColumnItem->GetActColumn()-1].nWidth; + nMaxRight = lNullPix + GetMargin2(); + + // Dragging along + if((INDENT_FIRST_LINE - INDENT_GAP) != nIdx && + !(nDragType & SvxRulerDragFlags::OBJECT_LEFT_INDENT_ONLY)) + { + if(GetLeftIndent() > GetFirstLineIndent()) + nMaxLeft += GetLeftIndent() - GetFirstLineIndent(); + else + nMaxRight -= GetFirstLineIndent() - GetLeftIndent(); + } + } + else + { + nMaxLeft = lNullPix; + + if(mxColumnItem && !mxColumnItem->IsFirstAct()) + nMaxLeft += mpBorders[mxColumnItem->GetActColumn()-1].nPos + + mpBorders[mxColumnItem->GetActColumn()-1].nWidth; + nMaxRight = lNullPix + GetRightIndent() - glMinFrame; + + // Dragging along + if((INDENT_FIRST_LINE - INDENT_GAP) != nIdx && + !(nDragType & SvxRulerDragFlags::OBJECT_LEFT_INDENT_ONLY)) + { + if(GetLeftIndent() > GetFirstLineIndent()) + nMaxLeft += GetLeftIndent() - GetFirstLineIndent(); + else + nMaxRight -= GetFirstLineIndent() - GetLeftIndent(); + } + } + } + break; + case INDENT_RIGHT_MARGIN - INDENT_GAP: + { + if(bRTL) + { + nMaxLeft = lNullPix; + nMaxRight = lNullPix + std::min(GetFirstLineIndent(), GetLeftIndent()) - glMinFrame; + if (mxColumnItem) + { + sal_uInt16 nRightCol=GetActRightColumn( true ); + if(!IsActLastColumn( true )) + nMaxRight += mpBorders[nRightCol].nPos; + else + nMaxRight += GetMargin2(); + } + else + { + nMaxLeft += GetMargin1(); + } + nMaxLeft += glMinFrame; + } + else + { + nMaxLeft = lNullPix + + std::max(GetFirstLineIndent(), GetLeftIndent()); + nMaxRight = lNullPix; + if (mxColumnItem) + { + sal_uInt16 nRightCol=GetActRightColumn( true ); + if(!IsActLastColumn( true )) + nMaxRight += mpBorders[nRightCol].nPos; + else + nMaxRight += GetMargin2(); + } + else + nMaxRight += GetMargin2(); + nMaxLeft += glMinFrame; + } + } + break; + } + break; + } + case RulerType::Tab: // Tabs (Modifier) + /* left = NOf + Max(LAR, EZ) + right = NOf + RAR */ + + if (bRTL) + nMaxLeft = lNullPix + GetRightIndent(); + else + nMaxLeft = lNullPix + std::min(GetFirstLineIndent(), GetLeftIndent()); + + mxRulerImpl->lMaxRightLogic = GetLogicRightIndent() + lLogicNullOffset; + nMaxRight = ConvertSizePixel(mxRulerImpl->lMaxRightLogic); + break; + default: ; //prevent warning + } +} + +bool SvxRuler::StartDrag() +{ + /* + Beginning of a drag operation (SV-handler) evaluates modifier and + calculated values + + [Cross-reference] + + <SvxRuler::EvalModifier()> + <SvxRuler::CalcMinMax()> + <SvxRuler::EndDrag()> + */ + bool bContentProtected = mxRulerImpl->aProtectItem->IsContentProtected(); + + if(!bValid) + return false; + + mxRulerImpl->lLastLMargin = GetMargin1(); + mxRulerImpl->lLastRMargin = GetMargin2(); + + bool bOk = true; + + lInitialDragPos = GetDragPos(); + switch(GetDragType()) + { + case RulerType::Margin1: // left edge of the surrounding Frame + case RulerType::Margin2: // right edge of the surrounding Frame + if((bHorz && mxLRSpaceItem) || (!bHorz && mxULSpaceItem)) + { + if (!mxColumnItem) + EvalModifier(); + else + nDragType = SvxRulerDragFlags::OBJECT; + } + else + { + bOk = false; + } + break; + case RulerType::Border: // Table, column (Modifier) + if (mxColumnItem) + { + nDragOffset = 0; + if (!mxColumnItem->IsTable()) + nDragOffset = GetDragPos() - mpBorders[GetDragAryPos()].nPos; + EvalModifier(); + } + else + nDragOffset = 0; + break; + case RulerType::Indent: // Paragraph indents (Modifier) + { + if( bContentProtected ) + return false; + if(INDENT_LEFT_MARGIN == GetDragAryPos() + INDENT_GAP) { // Left paragraph indent + mpIndents[0] = mpIndents[INDENT_FIRST_LINE]; + EvalModifier(); + } + else + { + nDragType = SvxRulerDragFlags::OBJECT; + } + mpIndents[1] = mpIndents[GetDragAryPos() + INDENT_GAP]; + break; + } + case RulerType::Tab: // Tabs (Modifier) + if( bContentProtected ) + return false; + EvalModifier(); + mpTabs[0] = mpTabs[GetDragAryPos() + 1]; + mpTabs[0].nStyle |= RULER_STYLE_DONTKNOW; + break; + default: + nDragType = SvxRulerDragFlags::NONE; + } + + if(bOk) + CalcMinMax(); + + return bOk; +} + +void SvxRuler::Drag() +{ + /* SV-Draghandler */ + if(IsDragCanceled()) + { + Ruler::Drag(); + return; + } + switch(GetDragType()) { + case RulerType::Margin1: // left edge of the surrounding Frame + DragMargin1(); + mxRulerImpl->lLastLMargin = GetMargin1(); + break; + case RulerType::Margin2: // right edge of the surrounding Frame + DragMargin2(); + mxRulerImpl->lLastRMargin = GetMargin2(); + break; + case RulerType::Indent: // Paragraph indents + DragIndents(); + break; + case RulerType::Border: // Table, columns + if (mxColumnItem) + DragBorders(); + else if (mxObjectItem) + DragObjectBorder(); + break; + case RulerType::Tab: // Tabs + DragTabs(); + break; + default: + break; //prevent warning + } + Ruler::Drag(); +} + +void SvxRuler::EndDrag() +{ + /* + SV-handler; is called when ending the dragging. Triggers the updating of data + on the application, by calling the respective Apply...() methods to send the + data to the application. + */ + const bool bUndo = IsDragCanceled(); + const tools::Long lPos = GetDragPos(); + DrawLine_Impl(lTabPos, 6, bHorz); + lTabPos = -1; + + if(!bUndo) + { + switch(GetDragType()) + { + case RulerType::Margin1: // upper left edge of the surrounding Frame + case RulerType::Margin2: // lower right edge of the surrounding Frame + { + if (!mxColumnItem || !mxColumnItem->IsTable()) + ApplyMargins(); + + if(mxColumnItem && + (mxColumnItem->IsTable() || + (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL))) + ApplyBorders(); + + } + break; + case RulerType::Border: // Table, columns + if(lInitialDragPos != lPos || + (mxRulerImpl->bIsTableRows && bHorz)) //special case - the null offset is changed here + { + if (mxColumnItem) + { + ApplyBorders(); + if(bHorz) + UpdateTabs(); + } + else if (mxObjectItem) + ApplyObject(); + } + break; + case RulerType::Indent: // Paragraph indents + if(lInitialDragPos != lPos) + ApplyIndents(); + SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP); + break; + case RulerType::Tab: // Tabs + { + ApplyTabs(); + mpTabs[GetDragAryPos()].nStyle &= ~RULER_STYLE_INVISIBLE; + SetTabs(nTabCount, mpTabs.data() + TAB_GAP); + } + break; + default: + break; //prevent warning + } + } + nDragType = SvxRulerDragFlags::NONE; + + mbCoarseSnapping = false; + mbSnapping = true; + + Ruler::EndDrag(); + if(bUndo) + { + for(sal_uInt16 i = 0; i < mxRulerImpl->nControllerItems; i++) + { + pCtrlItems[i]->ClearCache(); + pCtrlItems[i]->GetBindings().Invalidate(pCtrlItems[i]->GetId()); + } + } +} + +void SvxRuler::ExtraDown() +{ + /* Override SV method, sets the new type for the Default tab. */ + + // Switch Tab Type + if(mxTabStopItem && + (nFlags & SvxRulerSupportFlags::TABS) == SvxRulerSupportFlags::TABS) + { + ++nDefTabType; + if(RULER_TAB_DEFAULT == nDefTabType) + nDefTabType = RULER_TAB_LEFT; + SetExtraType(RulerExtra::Tab, nDefTabType); + } + Ruler::ExtraDown(); +} + +void SvxRuler::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + /* + Report through the bindings that the status update is completed. The ruler + updates its appearance and gets registered again in the bindings. + */ + + // start update + if (bActive && rHint.GetId() == SfxHintId::UpdateDone) + { + Update(); + EndListening(*pBindings); + bValid = true; + bListening = false; + } +} + +void SvxRuler::MenuSelect(std::u16string_view ident) +{ + if (ident.empty()) + return; + /* Handler of the context menus for switching the unit of measurement */ + SetUnit(vcl::EnglishStringToMetric(ident)); +} + +void SvxRuler::TabMenuSelect(std::u16string_view rIdent) +{ + if (rIdent.empty()) + return; + sal_Int32 nId = o3tl::toInt32(rIdent); + /* Handler of the tab menu for setting the type */ + if (mxTabStopItem && mxTabStopItem->Count() > mxRulerImpl->nIdx) + { + SvxTabStop aTabStop = mxTabStopItem->At(mxRulerImpl->nIdx); + aTabStop.GetAdjustment() = ToAttrTab_Impl(nId - 1); + mxTabStopItem->Remove(mxRulerImpl->nIdx); + mxTabStopItem->Insert(aTabStop); + sal_uInt16 nTabStopId = bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL; + pBindings->GetDispatcher()->ExecuteList(nTabStopId, + SfxCallMode::RECORD, { mxTabStopItem.get() }); + UpdateTabs(); + mxRulerImpl->nIdx = 0; + } +} + +const TranslateId RID_SVXSTR_RULER_TAB[] = +{ + RID_SVXSTR_RULER_TAB_LEFT, + RID_SVXSTR_RULER_TAB_RIGHT, + RID_SVXSTR_RULER_TAB_CENTER, + RID_SVXSTR_RULER_TAB_DECIMAL +}; + +void SvxRuler::Command( const CommandEvent& rCommandEvent ) +{ + /* Mouse context menu for switching the unit of measurement */ + if ( CommandEventId::ContextMenu == rCommandEvent.GetCommand() ) + { + CancelDrag(); + + tools::Rectangle aRect(rCommandEvent.GetMousePosPixel(), Size(1, 1)); + weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "svx/ui/rulermenu.ui")); + std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu")); + + bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue(); + if ( !mpTabs.empty() && + RulerType::Tab == + GetRulerType( rCommandEvent.GetMousePosPixel(), &mxRulerImpl->nIdx ) && + mpTabs[mxRulerImpl->nIdx + TAB_GAP].nStyle < RULER_TAB_DEFAULT ) + { + xMenu->clear(); + + const Size aSz(ruler_tab_svx.width + 2, ruler_tab_svx.height + 2); + const Point aPt(aSz.Width() / 2, aSz.Height() / 2); + + for ( sal_uInt16 i = RULER_TAB_LEFT; i < RULER_TAB_DEFAULT; ++i ) + { + ScopedVclPtr<VirtualDevice> xDev(pPopupParent->create_virtual_device()); + xDev->SetOutputSize(aSz); + + sal_uInt16 nStyle = bRTL ? i|RULER_TAB_RTL : i; + nStyle |= static_cast<sal_uInt16>(bHorz ? WB_HORZ : WB_VERT); + + Color aFillColor(xDev->GetSettings().GetStyleSettings().GetShadowColor()); + DrawTab(*xDev, aFillColor, aPt, nStyle); + + OUString sId(OUString::number(i + 1)); + xMenu->insert(-1, sId, SvxResId(RID_SVXSTR_RULER_TAB[i]), + nullptr, xDev.get(), nullptr, TRISTATE_TRUE); + xMenu->set_active(sId, i == mpTabs[mxRulerImpl->nIdx + TAB_GAP].nStyle); + } + TabMenuSelect(xMenu->popup_at_rect(pPopupParent, aRect)); + } + else + { + FieldUnit eUnit = GetUnit(); + const int nCount = xMenu->n_children(); + + bool bReduceMetric = bool(nFlags & SvxRulerSupportFlags::REDUCED_METRIC); + for ( sal_uInt16 i = nCount; i; --i ) + { + OUString sIdent = xMenu->get_id(i - 1); + FieldUnit eMenuUnit = vcl::EnglishStringToMetric(sIdent); + xMenu->set_active(sIdent, eMenuUnit == eUnit); + if( bReduceMetric ) + { + if (eMenuUnit == FieldUnit::M || + eMenuUnit == FieldUnit::KM || + eMenuUnit == FieldUnit::FOOT || + eMenuUnit == FieldUnit::MILE) + { + xMenu->remove(sIdent); + } + else if (( eMenuUnit == FieldUnit::CHAR ) && !bHorz ) + { + xMenu->remove(sIdent); + } + else if (( eMenuUnit == FieldUnit::LINE ) && bHorz ) + { + xMenu->remove(sIdent); + } + } + } + MenuSelect(xMenu->popup_at_rect(pPopupParent, aRect)); + } + } + else + { + Ruler::Command( rCommandEvent ); + } +} + +sal_uInt16 SvxRuler::GetActRightColumn( + bool bForceDontConsiderHidden, + sal_uInt16 nAct ) const +{ + if( nAct == USHRT_MAX ) + nAct = mxColumnItem->GetActColumn(); + else + nAct++; //To be able to pass on the ActDrag + + bool bConsiderHidden = !bForceDontConsiderHidden && + !(nDragType & SvxRulerDragFlags::OBJECT_ACTLINE_ONLY); + + while( nAct < mxColumnItem->Count() - 1 ) + { + if (mxColumnItem->At(nAct).bVisible || bConsiderHidden) + return nAct; + else + nAct++; + } + return USHRT_MAX; +} + +sal_uInt16 SvxRuler::GetActLeftColumn( + bool bForceDontConsiderHidden, + sal_uInt16 nAct ) const +{ + if(nAct == USHRT_MAX) + nAct = mxColumnItem->GetActColumn(); + + sal_uInt16 nLeftOffset = 1; + + bool bConsiderHidden = !bForceDontConsiderHidden && + !(nDragType & SvxRulerDragFlags::OBJECT_ACTLINE_ONLY); + + while(nAct >= nLeftOffset) + { + if (mxColumnItem->At(nAct - nLeftOffset).bVisible || bConsiderHidden) + return nAct - nLeftOffset; + else + nLeftOffset++; + } + return USHRT_MAX; +} + +bool SvxRuler::IsActLastColumn( + bool bForceDontConsiderHidden, + sal_uInt16 nAct) const +{ + return GetActRightColumn(bForceDontConsiderHidden, nAct) == USHRT_MAX; +} + +bool SvxRuler::IsActFirstColumn( + bool bForceDontConsiderHidden, + sal_uInt16 nAct) const +{ + return GetActLeftColumn(bForceDontConsiderHidden, nAct) == USHRT_MAX; +} + +tools::Long SvxRuler::CalcPropMaxRight(sal_uInt16 nCol) const +{ + + if(!(nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR)) + { + // Remove the minimum width for all affected columns + // starting from the right edge + tools::Long _nMaxRight = GetMargin2() - GetMargin1(); + + tools::Long lFences = 0; + tools::Long lMinSpace = USHRT_MAX; + tools::Long lOldPos; + tools::Long lColumns = 0; + + sal_uInt16 nStart; + if(!mxColumnItem->IsTable()) + { + if(nCol == USHRT_MAX) + { + lOldPos = GetMargin1(); + nStart = 0; + } + else + { + lOldPos = mpBorders[nCol].nPos + mpBorders[nCol].nWidth; + nStart = nCol + 1; + lFences = mpBorders[nCol].nWidth; + } + + for(size_t i = nStart; i < mpBorders.size() - 1; ++i) + { + tools::Long lWidth = mpBorders[i].nPos - lOldPos; + lColumns += lWidth; + if(lWidth < lMinSpace) + lMinSpace = lWidth; + lOldPos = mpBorders[i].nPos + mpBorders[i].nWidth; + lFences += mpBorders[i].nWidth; + } + tools::Long lWidth = GetMargin2() - lOldPos; + lColumns += lWidth; + if(lWidth < lMinSpace) + lMinSpace = lWidth; + } + else + { + sal_uInt16 nActCol; + if(nCol == USHRT_MAX) //CalcMinMax for LeftMargin + { + lOldPos = GetMargin1(); + } + else + { + lOldPos = mpBorders[nCol].nPos; + } + lColumns = GetMargin2()-lOldPos; + nActCol = nCol; + lFences = 0; + while(nActCol < mpBorders.size() || nActCol == USHRT_MAX) + { + sal_uInt16 nRight; + if(nActCol == USHRT_MAX) + { + nRight = 0; + while (!(*mxColumnItem)[nRight].bVisible) + { + nRight++; + } + } + else + { + nRight = GetActRightColumn(false, nActCol); + } + + tools::Long lWidth; + if(nRight != USHRT_MAX) + { + lWidth = mpBorders[nRight].nPos - lOldPos; + lOldPos = mpBorders[nRight].nPos; + } + else + { + lWidth=GetMargin2() - lOldPos; + } + nActCol = nRight; + if(lWidth < lMinSpace) + lMinSpace = lWidth; + if(nActCol == USHRT_MAX) + break; + } + } + + _nMaxRight -= static_cast<tools::Long>(lFences + glMinFrame / static_cast<float>(lMinSpace) * lColumns); + return _nMaxRight; + } + else + { + if(mxColumnItem->IsTable()) + { + sal_uInt16 nVisCols = 0; + for(size_t i = GetActRightColumn(false, nCol); i < mpBorders.size();) + { + if ((*mxColumnItem)[i].bVisible) + nVisCols++; + i = GetActRightColumn(false, i); + } + return GetMargin2() - GetMargin1() - (nVisCols + 1) * glMinFrame; + } + else + { + tools::Long lWidth = 0; + for(size_t i = nCol; i < mpBorders.size() - 1; i++) + { + lWidth += glMinFrame + mpBorders[i].nWidth; + } + return GetMargin2() - GetMargin1() - lWidth; + } + } +} + +// Tab stops relative to indent (#i24363#) +void SvxRuler::SetTabsRelativeToIndent( bool bRel ) +{ + mxRulerImpl->bIsTabsRelativeToIndent = bRel; +} + +void SvxRuler::SetValues(RulerChangeType type, tools::Long diffValue) +{ + if (diffValue == 0) + return; + + if (type == RulerChangeType::MARGIN1) + AdjustMargin1(diffValue); + else if (type == RulerChangeType::MARGIN2) + SetMargin2( GetMargin2() - diffValue); + ApplyMargins(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/svx/source/dialog/swframeexample.cxx b/svx/source/dialog/swframeexample.cxx new file mode 100644 index 0000000000..29475f6f39 --- /dev/null +++ b/svx/source/dialog/swframeexample.cxx @@ -0,0 +1,714 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <vcl/metric.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <svtools/colorcfg.hxx> +#include <svx/swframeexample.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/text/RelOrientation.hpp> + +using namespace ::com::sun::star::text; + +#define FLYINFLY_BORDER 3 +constexpr OUString DEMOTEXT = u"Ij"_ustr; + +namespace { + +void DrawRect_Impl(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect, + const Color &rFillColor, const Color &rLineColor) +{ + rRenderContext.SetFillColor(rFillColor); + rRenderContext.SetLineColor(rLineColor); + rRenderContext.DrawRect(rRect); +} + +} + +SwFrameExample::SwFrameExample() + : nHAlign(HoriOrientation::CENTER) + , nHRel(RelOrientation::FRAME) + , nVAlign(VertOrientation::TOP) + , nVRel(RelOrientation::PRINT_AREA) + , nWrap(WrapTextMode_NONE) + , nAnchor(RndStdIds::FLY_AT_PAGE) + , bTrans(false) + , aRelPos(Point(0,0)) +{ + InitColors_Impl(); +} + +void SwFrameExample::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 16, + pDrawingArea->get_text_height() * 12); +} + +void SwFrameExample::InitColors_Impl() +{ + const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); + m_aBgCol = rSettings.GetWindowColor(); + + bool bHC = rSettings.GetHighContrastMode(); + + m_aFrameColor = COL_LIGHTGREEN; + m_aAlignColor = COL_LIGHTRED; + m_aTransColor = COL_TRANSPARENT; + + m_aTxtCol = bHC? + svtools::ColorConfig().GetColorValue(svtools::FONTCOLOR).nColor : + COL_GRAY; + m_aPrintAreaCol = bHC? m_aTxtCol : COL_GRAY; + m_aBorderCol = m_aTxtCol; + m_aBlankCol = bHC? m_aTxtCol : COL_LIGHTGRAY; + m_aBlankFrameCol = bHC? m_aTxtCol : COL_GRAY; +} + +void SwFrameExample::StyleUpdated() +{ + InitColors_Impl(); + CustomWidgetController::StyleUpdated(); +} + +void SwFrameExample::InitAllRects_Impl(vcl::RenderContext& rRenderContext) +{ + aPage.SetSize(GetOutputSizePixel()); + + sal_uInt32 nOutWPix = aPage.GetWidth(); + sal_uInt32 nOutHPix = aPage.GetHeight(); + + // PrintArea + sal_uInt32 nLBorder; + sal_uInt32 nRBorder; + sal_uInt32 nTBorder; + sal_uInt32 nBBorder; + + sal_uInt32 nLTxtBorder; + sal_uInt32 nRTxtBorder; + sal_uInt32 nTTxtBorder; + sal_uInt32 nBTxtBorder; + + if (nAnchor != RndStdIds::FLY_AS_CHAR) + { + nLBorder = 14; + nRBorder = 10; + nTBorder = 10; + nBBorder = 15; + + nLTxtBorder = 8; + nRTxtBorder = 4; + nTTxtBorder = 2; + nBTxtBorder = 2; + } + else + { + nLBorder = 2; + nRBorder = 2; + nTBorder = 2; + nBBorder = 2; + + nLTxtBorder = 2; + nRTxtBorder = 2; + nTTxtBorder = 2; + nBTxtBorder = 2; + } + aPagePrtArea = tools::Rectangle(Point(nLBorder, nTBorder), Point((nOutWPix - 1) - nRBorder, (nOutHPix - 1) - nBBorder)); + + // Example text: Preparing for the text output + // A line of text + aTextLine = aPagePrtArea; + aTextLine.SetSize(Size(aTextLine.GetWidth(), 2)); + aTextLine.AdjustLeft(nLTxtBorder ); + aTextLine.AdjustRight( -sal_Int32(nRTxtBorder) ); + aTextLine.Move(0, nTTxtBorder); + + // Rectangle to edges including paragraph + sal_uInt16 nLines = static_cast<sal_uInt16>((aPagePrtArea.GetHeight() / 2 - nTTxtBorder - nBTxtBorder) + / (aTextLine.GetHeight() + 2)); + aPara = aPagePrtArea; + aPara.SetSize(Size(aPara.GetWidth(), + (aTextLine.GetHeight() + 2) * nLines + nTTxtBorder + nBTxtBorder)); + + // Rectangle around paragraph without borders + aParaPrtArea = aPara; + aParaPrtArea.AdjustLeft(nLTxtBorder ); + aParaPrtArea.AdjustRight( -sal_Int32(nRTxtBorder) ); + aParaPrtArea.AdjustTop(nTTxtBorder ); + aParaPrtArea.AdjustBottom( -sal_Int32(nBTxtBorder) ); + + if (nAnchor == RndStdIds::FLY_AS_CHAR || nAnchor == RndStdIds::FLY_AT_CHAR) + { + vcl::Font aFont = OutputDevice::GetDefaultFont( + DefaultFontType::LATIN_TEXT, Application::GetSettings().GetLanguageTag().getLanguageType(), + GetDefaultFontFlags::OnlyOne, &rRenderContext ); + aFont.SetColor( m_aTxtCol ); + aFont.SetFillColor( m_aBgCol ); + aFont.SetWeight(WEIGHT_NORMAL); + + if (nAnchor == RndStdIds::FLY_AS_CHAR) + { + aFont.SetFontSize(Size(0, aParaPrtArea.GetHeight() - 2)); + rRenderContext.SetFont(aFont); + aParaPrtArea.SetSize(Size(rRenderContext.GetTextWidth(DEMOTEXT), rRenderContext.GetTextHeight())); + } + else + { + aFont.SetFontSize(Size(0, aParaPrtArea.GetHeight() / 2)); + rRenderContext.SetFont(aFont); + aAutoCharFrame.SetSize(Size(rRenderContext.GetTextWidth(OUString('A')), GetTextHeight())); + aAutoCharFrame.SetPos(Point(aParaPrtArea.Left() + (aParaPrtArea.GetWidth() - aAutoCharFrame.GetWidth()) / 2, + aParaPrtArea.Top() + (aParaPrtArea.GetHeight() - aAutoCharFrame.GetHeight()) / 2)); + } + } + + // Inner Frame anchored at the Frame + aFrameAtFrame = aPara; + aFrameAtFrame.AdjustLeft(9 ); + aFrameAtFrame.AdjustRight( -5 ); + aFrameAtFrame.AdjustBottom(5 ); + aFrameAtFrame.SetPos(Point(aFrameAtFrame.Left() + 2, (aPagePrtArea.Bottom() - aFrameAtFrame.GetHeight()) / 2 + 5)); + + // Size of the frame to be positioned + if (nAnchor != RndStdIds::FLY_AS_CHAR) + { + sal_uInt32 nLFBorder = nAnchor == RndStdIds::FLY_AT_PAGE ? nLBorder : nLTxtBorder; + sal_uInt32 nRFBorder = nAnchor == RndStdIds::FLY_AT_PAGE ? nRBorder : nRTxtBorder; + + switch (nHRel) + { + case RelOrientation::PAGE_LEFT: + case RelOrientation::FRAME_LEFT: + aFrmSize = Size(nLFBorder - 4, (aTextLine.GetHeight() + 2) * 3); + break; + + case RelOrientation::PAGE_RIGHT: + case RelOrientation::FRAME_RIGHT: + aFrmSize = Size(nRFBorder - 4, (aTextLine.GetHeight() + 2) * 3); + break; + + default: + aFrmSize = Size(nLBorder - 3, (aTextLine.GetHeight() + 2) * 3); + break; + } + aFrmSize.setWidth( std::max(tools::Long(5), aFrmSize.Width()) ); + aFrmSize.setHeight( std::max(tools::Long(5), aFrmSize.Height()) ); + } + else + { + sal_uInt32 nFreeWidth = aPagePrtArea.GetWidth() - rRenderContext.GetTextWidth(DEMOTEXT); + + aFrmSize = Size(nFreeWidth / 2, (aTextLine.GetHeight() + 2) * 3); + aDrawObj.SetSize(Size(std::max(tools::Long(5), tools::Long(nFreeWidth / 3)), std::max(tools::Long(5), aFrmSize.Height() * 3))); + aDrawObj.SetPos(Point(aParaPrtArea.Right() + 1, aParaPrtArea.Bottom() / 2)); + aParaPrtArea.SetRight( aDrawObj.Right() ); + } +} + +void SwFrameExample::CalcBoundRect_Impl(const vcl::RenderContext& rRenderContext, tools::Rectangle &rRect) +{ + switch (nAnchor) + { + case RndStdIds::FLY_AT_PAGE: + { + switch (nHRel) + { + case RelOrientation::FRAME: + case RelOrientation::PAGE_FRAME: + rRect.SetLeft( aPage.Left() ); + rRect.SetRight( aPage.Right() ); + break; + + case RelOrientation::PRINT_AREA: + case RelOrientation::PAGE_PRINT_AREA: + rRect.SetLeft( aPagePrtArea.Left() ); + rRect.SetRight( aPagePrtArea.Right() ); + break; + + case RelOrientation::PAGE_LEFT: + rRect.SetLeft( aPage.Left() ); + rRect.SetRight( aPagePrtArea.Left() ); + break; + + case RelOrientation::PAGE_RIGHT: + rRect.SetLeft( aPagePrtArea.Right() ); + rRect.SetRight( aPage.Right() ); + break; + } + + switch (nVRel) + { + case RelOrientation::PRINT_AREA: + case RelOrientation::PAGE_PRINT_AREA: + rRect.SetTop( aPagePrtArea.Top() ); + rRect.SetBottom( aPagePrtArea.Bottom() ); + break; + + case RelOrientation::FRAME: + case RelOrientation::PAGE_FRAME: + rRect.SetTop( aPage.Top() ); + rRect.SetBottom( aPage.Bottom() ); + break; + } + } + break; + + case RndStdIds::FLY_AT_FLY: + { + switch (nHRel) + { + case RelOrientation::FRAME: + case RelOrientation::PAGE_FRAME: + rRect.SetLeft( aFrameAtFrame.Left() ); + rRect.SetRight( aFrameAtFrame.Right() ); + break; + + case RelOrientation::PRINT_AREA: + case RelOrientation::PAGE_PRINT_AREA: + rRect.SetLeft( aFrameAtFrame.Left() + FLYINFLY_BORDER ); + rRect.SetRight( aFrameAtFrame.Right() - FLYINFLY_BORDER ); + break; + + case RelOrientation::PAGE_LEFT: + rRect.SetLeft( aFrameAtFrame.Left() ); + rRect.SetRight( aFrameAtFrame.Left() + FLYINFLY_BORDER ); + break; + + case RelOrientation::PAGE_RIGHT: + rRect.SetLeft( aFrameAtFrame.Right() ); + rRect.SetRight( aFrameAtFrame.Right() - FLYINFLY_BORDER ); + break; + } + + switch (nVRel) + { + case RelOrientation::FRAME: + case RelOrientation::PAGE_FRAME: + rRect.SetTop( aFrameAtFrame.Top() ); + rRect.SetBottom( aFrameAtFrame.Bottom() ); + break; + + case RelOrientation::PRINT_AREA: + case RelOrientation::PAGE_PRINT_AREA: + rRect.SetTop( aFrameAtFrame.Top() + FLYINFLY_BORDER ); + rRect.SetBottom( aFrameAtFrame.Bottom() - FLYINFLY_BORDER ); + break; + } + } + break; + case RndStdIds::FLY_AT_PARA: + case RndStdIds::FLY_AT_CHAR: + { + switch (nHRel) + { + case RelOrientation::FRAME: + rRect.SetLeft( aPara.Left() ); + rRect.SetRight( aPara.Right() ); + break; + + case RelOrientation::PRINT_AREA: + rRect.SetLeft( aParaPrtArea.Left() ); + rRect.SetRight( aParaPrtArea.Right() ); + break; + + case RelOrientation::PAGE_LEFT: + rRect.SetLeft( aPage.Left() ); + rRect.SetRight( aPagePrtArea.Left() ); + break; + + case RelOrientation::PAGE_RIGHT: + rRect.SetLeft( aPagePrtArea.Right() ); + rRect.SetRight( aPage.Right() ); + break; + + case RelOrientation::PAGE_FRAME: + rRect.SetLeft( aPage.Left() ); + rRect.SetRight( aPage.Right() ); + break; + + case RelOrientation::PAGE_PRINT_AREA: + rRect.SetLeft( aPagePrtArea.Left() ); + rRect.SetRight( aPagePrtArea.Right() ); + break; + + case RelOrientation::FRAME_LEFT: + rRect.SetLeft( aPara.Left() ); + rRect.SetRight( aParaPrtArea.Left() ); + break; + + case RelOrientation::FRAME_RIGHT: + rRect.SetLeft( aParaPrtArea.Right() ); + rRect.SetRight( aPara.Right() ); + break; + + case RelOrientation::CHAR: + rRect.SetLeft( aAutoCharFrame.Left() ); + rRect.SetRight( aAutoCharFrame.Left() ); + break; + } + + switch (nVRel) + { + case RelOrientation::FRAME: + rRect.SetTop( aPara.Top() ); + rRect.SetBottom( aPara.Bottom() ); + break; + + case RelOrientation::PRINT_AREA: + rRect.SetTop( aParaPrtArea.Top() ); + rRect.SetBottom( aParaPrtArea.Bottom() ); + break; + + case RelOrientation::CHAR: + if (nVAlign != VertOrientation::NONE && + nVAlign != VertOrientation::CHAR_BOTTOM) + rRect.SetTop( aAutoCharFrame.Top() ); + else + rRect.SetTop( aAutoCharFrame.Bottom() ); + rRect.SetBottom( aAutoCharFrame.Bottom() ); + break; + // OD 12.11.2003 #i22341# + case RelOrientation::TEXT_LINE: + rRect.SetTop( aAutoCharFrame.Top() ); + rRect.SetBottom( aAutoCharFrame.Top() ); + break; + } + } + break; + + case RndStdIds::FLY_AS_CHAR: + rRect.SetLeft( aParaPrtArea.Left() ); + rRect.SetRight( aParaPrtArea.Right() ); + + switch (nVAlign) + { + case VertOrientation::NONE: + case VertOrientation::TOP: + case VertOrientation::CENTER: + case VertOrientation::BOTTOM: + { + FontMetric aMetric(rRenderContext.GetFontMetric()); + + rRect.SetTop( aParaPrtArea.Bottom() - aMetric.GetDescent() ); + rRect.SetBottom( rRect.Top() ); + } + break; + + default: + + case VertOrientation::LINE_TOP: + case VertOrientation::LINE_CENTER: + case VertOrientation::LINE_BOTTOM: + rRect.SetTop( aParaPrtArea.Top() ); + rRect.SetBottom( aDrawObj.Bottom() ); + break; + + case VertOrientation::CHAR_TOP: + case VertOrientation::CHAR_CENTER: + case VertOrientation::CHAR_BOTTOM: + rRect.SetTop( aParaPrtArea.Top() ); + rRect.SetBottom( aParaPrtArea.Bottom() ); + break; + } + break; + + default: + break; + } +} + +tools::Rectangle SwFrameExample::DrawInnerFrame_Impl(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect, + const Color &rFillColor, const Color &rBorderColor) +{ + DrawRect_Impl(rRenderContext, rRect, rFillColor, rBorderColor); + + // determine the area relative to which the positioning happens + tools::Rectangle aRect(rRect); // aPagePrtArea = Default + CalcBoundRect_Impl(rRenderContext, aRect); + + if (nAnchor == RndStdIds::FLY_AT_FLY && &rRect == &aPagePrtArea) + { + // draw text paragraph + tools::Rectangle aTxt(aTextLine); + sal_Int32 nStep = aTxt.GetHeight() + 2; + sal_uInt16 nLines = static_cast<sal_uInt16>(aParaPrtArea.GetHeight() / (aTextLine.GetHeight() + 2)); + + for (sal_uInt16 i = 0; i < nLines; i++) + { + if (i == nLines - 1) + aTxt.SetSize(Size(aTxt.GetWidth() / 2, aTxt.GetHeight())); + DrawRect_Impl(rRenderContext, aTxt, m_aTxtCol, m_aTransColor); + aTxt.Move(0, nStep); + } + } + + return aRect; +} + +void SwFrameExample::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel)); + + InitAllRects_Impl(rRenderContext); + + // Draw page + DrawRect_Impl(rRenderContext, aPage, m_aBgCol, m_aBorderCol); + + // Draw PrintArea + tools::Rectangle aRect = DrawInnerFrame_Impl(rRenderContext, aPagePrtArea, m_aTransColor, m_aPrintAreaCol); + + if (nAnchor == RndStdIds::FLY_AT_FLY) + aRect = DrawInnerFrame_Impl(rRenderContext, aFrameAtFrame, m_aBgCol, m_aBorderCol); + + tools::Long lXPos = 0; + tools::Long lYPos = 0; + + // Horizontal alignment + if (nAnchor != RndStdIds::FLY_AS_CHAR) + { + switch (nHAlign) + { + case HoriOrientation::RIGHT: + { + lXPos = aRect.Right() - aFrmSize.Width() + 1; + break; + } + case HoriOrientation::CENTER: + { + lXPos = aRect.Left() + (aRect.GetWidth() - aFrmSize.Width()) / 2; + break; + } + case HoriOrientation::NONE: + { + lXPos = aRect.Left() + aRelPos.X(); + break; + } + + default: // HoriOrientation::LEFT + lXPos = aRect.Left(); + break; + } + } + else + { + lXPos = aRect.Right() + 2; + } + + // Vertical Alignment + if (nAnchor != RndStdIds::FLY_AS_CHAR) + { + switch (nVAlign) + { + case VertOrientation::BOTTOM: + case VertOrientation::LINE_BOTTOM: + { + // #i22341# + if ( nVRel != RelOrientation::TEXT_LINE ) + { + lYPos = aRect.Bottom() - aFrmSize.Height() + 1; + } + else + { + lYPos = aRect.Top(); + } + break; + } + case VertOrientation::CENTER: + case VertOrientation::LINE_CENTER: + { + lYPos = aRect.Top() + (aRect.GetHeight() - aFrmSize.Height()) / 2; + break; + } + case VertOrientation::NONE: + { + // #i22341# + if ( nVRel != RelOrientation::CHAR && nVRel != RelOrientation::TEXT_LINE ) + lYPos = aRect.Top() + aRelPos.Y(); + else + lYPos = aRect.Top() - aRelPos.Y(); + break; + } + default: + // #i22341# + if ( nVRel != RelOrientation::TEXT_LINE ) + { + lYPos = aRect.Top(); + } + else + { + lYPos = aRect.Bottom() - aFrmSize.Height() + 1; + } + break; + } + } + else + { + switch(nVAlign) + { + case VertOrientation::CENTER: + case VertOrientation::CHAR_CENTER: + case VertOrientation::LINE_CENTER: + lYPos = aRect.Top() + (aRect.GetHeight() - aFrmSize.Height()) / 2; + break; + + case VertOrientation::TOP: + case VertOrientation::CHAR_BOTTOM: + case VertOrientation::LINE_BOTTOM: + lYPos = aRect.Bottom() - aFrmSize.Height() + 1; + break; + + default: + lYPos = aRect.Top() - aRelPos.Y(); + break; + } + } + + tools::Rectangle aFrmRect(Point(lXPos, lYPos), aFrmSize); + + tools::Rectangle* pOuterFrame = &aPage; + + if (nAnchor == RndStdIds::FLY_AT_FLY) + pOuterFrame = &aFrameAtFrame; + + if (aFrmRect.Left() < pOuterFrame->Left()) + aFrmRect.Move(pOuterFrame->Left() - aFrmRect.Left(), 0); + if (aFrmRect.Right() > pOuterFrame->Right()) + aFrmRect.Move(pOuterFrame->Right() - aFrmRect.Right(), 0); + + if (aFrmRect.Top() < pOuterFrame->Top()) + aFrmRect.Move(0, pOuterFrame->Top() - aFrmRect.Top()); + if (aFrmRect.Bottom() > pOuterFrame->Bottom()) + aFrmRect.Move(0, pOuterFrame->Bottom() - aFrmRect.Bottom()); + + // Draw Test paragraph + const tools::Long nTxtLineHeight = aTextLine.GetHeight(); + tools::Rectangle aTxt(aTextLine); + sal_Int32 nStep; + sal_uInt16 nLines; + + if (nAnchor == RndStdIds::FLY_AT_FLY) + { + aTxt.SetLeft( aFrameAtFrame.Left() + FLYINFLY_BORDER ); + aTxt.SetRight( aFrameAtFrame.Right() - FLYINFLY_BORDER ); + aTxt.SetTop( aFrameAtFrame.Top() + FLYINFLY_BORDER ); + aTxt.SetBottom( aTxt.Top() + aTextLine.GetHeight() - 1 ); + + nStep = aTxt.GetHeight() + 2; + nLines = static_cast<sal_uInt16>(((aFrameAtFrame.GetHeight() - 2 * FLYINFLY_BORDER) * 2 / 3) + / (aTxt.GetHeight() + 2)); + } + else + { + nStep = aTxt.GetHeight() + 2; + nLines = static_cast<sal_uInt16>(aParaPrtArea.GetHeight() / (aTextLine.GetHeight() + 2)); + } + + if (nAnchor != RndStdIds::FLY_AS_CHAR) + { + // Simulate text + const tools::Long nOldR = aTxt.Right(); + const tools::Long nOldL = aTxt.Left(); + + // #i22341# + const bool bIgnoreWrap = nAnchor == RndStdIds::FLY_AT_CHAR && + ( nHRel == RelOrientation::CHAR || nVRel == RelOrientation::CHAR || + nVRel == RelOrientation::TEXT_LINE ); + + for (sal_uInt16 i = 0; i < nLines; ++i) + { + if (i == (nLines - 1)) + aTxt.SetSize(Size(aTxt.GetWidth() / 2, aTxt.GetHeight())); + + if (aTxt.Overlaps(aFrmRect) && nAnchor != RndStdIds::FLY_AS_CHAR && !bIgnoreWrap) + { + switch(nWrap) + { + case WrapTextMode_NONE: + aTxt.SetTop( aFrmRect.Bottom() + nTxtLineHeight ); + aTxt.SetBottom( aTxt.Top() + nTxtLineHeight - 1 ); + break; + + case WrapTextMode_LEFT: + aTxt.SetRight( aFrmRect.Left() ); + break; + + case WrapTextMode_RIGHT: + aTxt.SetLeft( aFrmRect.Right() ); + break; + default: break; + } + } + if (pOuterFrame->Contains(aTxt)) + DrawRect_Impl(rRenderContext, aTxt, m_aTxtCol, m_aTransColor ); + + aTxt.Move(0, nStep); + aTxt.SetRight( nOldR ); + aTxt.SetLeft( nOldL ); + } + aTxt.Move(0, -nStep); + + if (nAnchor != RndStdIds::FLY_AT_FLY && aTxt.Bottom() > aParaPrtArea.Bottom()) + { + // Text has been replaced by frame, so adjust parameters height + sal_Int32 nDiff = aTxt.Bottom() - aParaPrtArea.Bottom(); + aParaPrtArea.AdjustBottom(nDiff ); + aPara.AdjustBottom(nDiff ); + + CalcBoundRect_Impl(rRenderContext, aRect); + + aParaPrtArea.AdjustBottom( -nDiff ); + aPara.AdjustBottom( -nDiff ); + } + if (nAnchor == RndStdIds::FLY_AT_CHAR && bIgnoreWrap) + rRenderContext.DrawText(aAutoCharFrame, OUString('A')); + } + else + { + rRenderContext.DrawText(aParaPrtArea, DEMOTEXT); + DrawRect_Impl(rRenderContext, aDrawObj, m_aBlankCol, m_aBlankFrameCol ); + } + + // Draw rectangle on which the frame is aligned: + DrawRect_Impl(rRenderContext, aRect, m_aTransColor, m_aAlignColor); + + // Frame View + bool bDontFill = (nAnchor == RndStdIds::FLY_AT_CHAR && aFrmRect.Overlaps(aAutoCharFrame)) || bTrans; + DrawRect_Impl(rRenderContext, aFrmRect, bDontFill? m_aTransColor : m_aBgCol, m_aFrameColor); +} + +void SwFrameExample::SetRelPos(const Point& rP) +{ + aRelPos = rP; + + if (aRelPos.X() > 0) + aRelPos.setX( 5 ); + if (aRelPos.X() < 0) + aRelPos.setX( -5 ); + + if (aRelPos.Y() > 0) + aRelPos.setY( 5 ); + if (aRelPos.Y() < 0) + aRelPos.setY( -5 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/swframeposstrings.cxx b/svx/source/dialog/swframeposstrings.cxx new file mode 100644 index 0000000000..29b9243f8c --- /dev/null +++ b/svx/source/dialog/swframeposstrings.cxx @@ -0,0 +1,38 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <cassert> + +#include <svx/swframeposstrings.hxx> +#include <svx/dialmgr.hxx> +#include <swframeposstrings.hrc> + +OUString SvxSwFramePosString::GetString(StringId eId) +{ + static_assert( + (SAL_N_ELEMENTS(RID_SVXSW_FRAMEPOSITIONS) + == SvxSwFramePosString::STR_MAX), + "RID_SVXSW_FRAMEPOSITIONS too small"); + assert(eId >= 0 && eId < STR_MAX); + return SvxResId(RID_SVXSW_FRAMEPOSITIONS[eId]); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/txencbox.cxx b/svx/source/dialog/txencbox.cxx new file mode 100644 index 0000000000..f6bc5fa729 --- /dev/null +++ b/svx/source/dialog/txencbox.cxx @@ -0,0 +1,266 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_features.h> + +#include <svx/txencbox.hxx> +#include <svx/dialmgr.hxx> +#include <svx/txenctab.hxx> +#if HAVE_FEATURE_DBCONNECTIVITY +#include <dbcharsethelper.hxx> +#endif +#include <unotools/syslocale.hxx> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> +#include <txenctab.hrc> + +namespace +{ + std::vector<rtl_TextEncoding> FillFromDbTextEncodingMap(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags) + { + std::vector<rtl_TextEncoding> aRet; +#if !HAVE_FEATURE_DBCONNECTIVITY + (void)bExcludeImportSubsets; + (void)nExcludeInfoFlags; +#else + rtl_TextEncodingInfo aInfo; + aInfo.StructSize = sizeof(rtl_TextEncodingInfo); + ::std::vector< rtl_TextEncoding > aEncs; + sal_Int32 nCount = svxform::charset_helper::getSupportedTextEncodings( aEncs ); + for ( sal_Int32 j=0; j<nCount; j++ ) + { + bool bInsert = true; + rtl_TextEncoding nEnc = rtl_TextEncoding( aEncs[j] ); + if ( nExcludeInfoFlags ) + { + if ( !rtl_getTextEncodingInfo( nEnc, &aInfo ) ) + bInsert = false; + else + { + if ( (aInfo.Flags & nExcludeInfoFlags) == 0 ) + { + if ( (nExcludeInfoFlags & RTL_TEXTENCODING_INFO_UNICODE) && + ((nEnc == RTL_TEXTENCODING_UCS2) || + nEnc == RTL_TEXTENCODING_UCS4) ) + bInsert = false; // InfoFlags don't work for Unicode :-( + } + else + bInsert = false; + } + } + if ( bInsert ) + { + if ( bExcludeImportSubsets ) + { + switch ( nEnc ) + { + // subsets of RTL_TEXTENCODING_GB_18030 + case RTL_TEXTENCODING_GB_2312 : + case RTL_TEXTENCODING_GBK : + case RTL_TEXTENCODING_MS_936 : + bInsert = false; + break; + } + } + // CharsetMap offers a RTL_TEXTENCODING_DONTKNOW for internal use, + // makes no sense here and would result in an empty string as list + // entry. + if ( bInsert && nEnc != RTL_TEXTENCODING_DONTKNOW ) + aRet.push_back(nEnc); + } + } +#endif + return aRet; + } +} + +void SvxTextEncodingBox::FillFromDbTextEncodingMap( + bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags ) +{ + m_xControl->freeze(); + auto aEncs = ::FillFromDbTextEncodingMap(bExcludeImportSubsets, nExcludeInfoFlags); + for (auto nEnc : aEncs) + InsertTextEncoding(nEnc); + m_xControl->thaw(); +} + +void SvxTextEncodingTreeView::FillFromDbTextEncodingMap( + bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags ) +{ + m_xControl->freeze(); + auto aEncs = ::FillFromDbTextEncodingMap(bExcludeImportSubsets, nExcludeInfoFlags); + for (auto nEnc : aEncs) + InsertTextEncoding(nEnc); + m_xControl->thaw(); +} + +SvxTextEncodingBox::SvxTextEncodingBox(std::unique_ptr<weld::ComboBox> pControl) + : m_xControl(std::move(pControl)) +{ + m_xControl->make_sorted(); +} + +SvxTextEncodingTreeView::SvxTextEncodingTreeView(std::unique_ptr<weld::TreeView> pControl) + : m_xControl(std::move(pControl)) +{ + m_xControl->make_sorted(); +} + +SvxTextEncodingBox::~SvxTextEncodingBox() +{ +} + +SvxTextEncodingTreeView::~SvxTextEncodingTreeView() +{ +} + +namespace +{ + std::vector<int> FillFromTextEncodingTable(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags) + { + std::vector<int> aRet; + + rtl_TextEncodingInfo aInfo; + aInfo.StructSize = sizeof(rtl_TextEncodingInfo); + const sal_uInt32 nCount = SAL_N_ELEMENTS(RID_SVXSTR_TEXTENCODING_TABLE); + for (sal_uInt32 j = 0; j < nCount; ++j) + { + bool bInsert = true; + rtl_TextEncoding nEnc = RID_SVXSTR_TEXTENCODING_TABLE[j].second; + if ( nExcludeInfoFlags ) + { + if ( !rtl_getTextEncodingInfo( nEnc, &aInfo ) ) + bInsert = false; + else + { + if ( (aInfo.Flags & nExcludeInfoFlags) == 0 ) + { + if ( (nExcludeInfoFlags & RTL_TEXTENCODING_INFO_UNICODE) && + ((nEnc == RTL_TEXTENCODING_UCS2) || + nEnc == RTL_TEXTENCODING_UCS4) ) + bInsert = false; // InfoFlags don't work for Unicode :-( + } + else + bInsert = false; + } + } + if ( bExcludeImportSubsets ) + { + switch ( nEnc ) + { + // subsets of RTL_TEXTENCODING_GB_18030 + case RTL_TEXTENCODING_GB_2312 : + case RTL_TEXTENCODING_GBK : + case RTL_TEXTENCODING_MS_936 : + bInsert = false; + break; + } + } + if ( bInsert ) + aRet.push_back(j); + } + return aRet; + } +} + +void SvxTextEncodingBox::FillFromTextEncodingTable( + bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags ) +{ + std::vector<int> aRet(::FillFromTextEncodingTable(bExcludeImportSubsets, nExcludeInfoFlags)); + m_xControl->freeze(); + for (auto j : aRet) + { + rtl_TextEncoding nEnc = RID_SVXSTR_TEXTENCODING_TABLE[j].second; + InsertTextEncoding(nEnc, SvxResId(RID_SVXSTR_TEXTENCODING_TABLE[j].first)); + } + m_xControl->thaw(); +} + +void SvxTextEncodingTreeView::FillFromTextEncodingTable( + bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags ) +{ + std::vector<int> aRet(::FillFromTextEncodingTable(bExcludeImportSubsets, nExcludeInfoFlags)); + m_xControl->freeze(); + for (auto j : aRet) + { + rtl_TextEncoding nEnc = RID_SVXSTR_TEXTENCODING_TABLE[j].second; + InsertTextEncoding(nEnc, SvxResId(RID_SVXSTR_TEXTENCODING_TABLE[j].first)); + } + m_xControl->thaw(); +} + +void SvxTextEncodingBox::InsertTextEncoding( const rtl_TextEncoding nEnc, + const OUString& rEntry ) +{ + m_xControl->append(OUString::number(nEnc), rEntry); +} + +void SvxTextEncodingTreeView::InsertTextEncoding( const rtl_TextEncoding nEnc, + const OUString& rEntry ) +{ + m_xControl->append(OUString::number(nEnc), rEntry); +} + +void SvxTextEncodingBox::InsertTextEncoding( const rtl_TextEncoding nEnc ) +{ + const OUString& rEntry = SvxTextEncodingTable::GetTextString(nEnc); + if (!rEntry.isEmpty()) + InsertTextEncoding( nEnc, rEntry ); + else + SAL_WARN( "svx.dialog", "SvxTextEncodingBox::InsertTextEncoding: no resource string for text encoding: " << static_cast<sal_Int32>( nEnc ) ); +} + +void SvxTextEncodingTreeView::InsertTextEncoding( const rtl_TextEncoding nEnc ) +{ + const OUString& rEntry = SvxTextEncodingTable::GetTextString(nEnc); + if (!rEntry.isEmpty()) + InsertTextEncoding( nEnc, rEntry ); + else + SAL_WARN( "svx.dialog", "SvxTextEncodingTreeView::InsertTextEncoding: no resource string for text encoding: " << static_cast<sal_Int32>( nEnc ) ); +} + +rtl_TextEncoding SvxTextEncodingBox::GetSelectTextEncoding() const +{ + OUString sId(m_xControl->get_active_id()); + if (!sId.isEmpty()) + return rtl_TextEncoding(sId.toInt32()); + else + return RTL_TEXTENCODING_DONTKNOW; +} + +rtl_TextEncoding SvxTextEncodingTreeView::GetSelectTextEncoding() const +{ + OUString sId(m_xControl->get_selected_id()); + if (!sId.isEmpty()) + return rtl_TextEncoding(sId.toInt32()); + else + return RTL_TEXTENCODING_DONTKNOW; +} + +void SvxTextEncodingBox::SelectTextEncoding( const rtl_TextEncoding nEnc ) +{ + m_xControl->set_active_id(OUString::number(nEnc)); +} + +void SvxTextEncodingTreeView::SelectTextEncoding( const rtl_TextEncoding nEnc ) +{ + m_xControl->select_id(OUString::number(nEnc)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/txenctab.cxx b/svx/source/dialog/txenctab.cxx new file mode 100644 index 0000000000..791b8f735f --- /dev/null +++ b/svx/source/dialog/txenctab.cxx @@ -0,0 +1,49 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/dialmgr.hxx> +#include <svx/txenctab.hxx> +#include <txenctab.hrc> + +OUString SvxTextEncodingTable::GetTextString(const rtl_TextEncoding nEnc) +{ + const size_t nCount = SAL_N_ELEMENTS(RID_SVXSTR_TEXTENCODING_TABLE); + + for (size_t i = 0; i < nCount; ++i) + { + if (RID_SVXSTR_TEXTENCODING_TABLE[i].second == nEnc) + return SvxResId(RID_SVXSTR_TEXTENCODING_TABLE[i].first); + } + + return OUString(); +} + +rtl_TextEncoding SvxTextEncodingTable::GetTextEncoding(const OUString& rStr) +{ + const size_t nCount = SAL_N_ELEMENTS(RID_SVXSTR_TEXTENCODING_TABLE); + + for (size_t i = 0; i < nCount; ++i) + { + if (SvxResId(RID_SVXSTR_TEXTENCODING_TABLE[i].first).equals(rStr)) + return RID_SVXSTR_TEXTENCODING_TABLE[i].second; + } + return RTL_TEXTENCODING_DONTKNOW; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/dialog/weldeditview.cxx b/svx/source/dialog/weldeditview.cxx new file mode 100644 index 0000000000..46ab95eb9a --- /dev/null +++ b/svx/source/dialog/weldeditview.cxx @@ -0,0 +1,1719 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_wasm_strip.h> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <drawinglayer/processor2d/baseprocessor2d.hxx> +#include <drawinglayer/processor2d/processor2dtools.hxx> +#include <comphelper/lok.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/fhgtitem.hxx> +#include <editeng/fontitem.hxx> +#include <editeng/outliner.hxx> +#include <editeng/unoedhlp.hxx> +#include <editeng/unoedsrc.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <osl/diagnose.h> +#include <svl/itempool.hxx> +#include <svl/itemset.hxx> +#include <sal/log.hxx> +#include <svx/sdr/overlay/overlayselection.hxx> +#include <svtools/optionsdrawinglayer.hxx> +#include <svx/AccessibleTextHelper.hxx> +#include <svx/weldeditview.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/canvastools.hxx> +#include <vcl/cursor.hxx> +#include <vcl/event.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/uitest/uiobject.hxx> + +void WeldEditView::SetText(const OUString& rStr) { GetEditEngine()->SetText(rStr); } + +OUString WeldEditView::GetText() const { return GetEditEngine()->GetText(); } + +void WeldEditView::SetModifyHdl(const Link<LinkParamNone*, void>& rLink) +{ + GetEditEngine()->SetModifyHdl(rLink); +} + +EditView* WeldEditView::GetEditView() const { return m_xEditView.get(); } + +EditEngine* WeldEditView::GetEditEngine() const { return m_xEditEngine.get(); } + +bool WeldEditView::HasSelection() const +{ + EditView* pEditView = GetEditView(); + return pEditView && pEditView->HasSelection(); +} + +void WeldEditView::Delete() +{ + if (EditView* pEditView = GetEditView()) + pEditView->DeleteSelected(); +} + +void WeldEditView::Cut() +{ + if (EditView* pEditView = GetEditView()) + pEditView->Cut(); +} + +void WeldEditView::Copy() +{ + if (EditView* pEditView = GetEditView()) + pEditView->Copy(); +} + +void WeldEditView::Paste() +{ + if (EditView* pEditView = GetEditView()) + pEditView->Paste(); +} + +WeldEditView::WeldEditView() + : m_bAcceptsTab(false) +{ +} + +// tdf#127033 want to use UI font so override makeEditEngine to enable that +void WeldEditView::makeEditEngine() +{ + rtl::Reference<SfxItemPool> pItemPool = EditEngine::CreatePool(); + + vcl::Font aAppFont(Application::GetSettings().GetStyleSettings().GetAppFont()); + + pItemPool->SetPoolDefaultItem(SvxFontItem(aAppFont.GetFamilyType(), aAppFont.GetFamilyName(), + "", PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, + EE_CHAR_FONTINFO)); + pItemPool->SetPoolDefaultItem(SvxFontItem(aAppFont.GetFamilyType(), aAppFont.GetFamilyName(), + "", PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, + EE_CHAR_FONTINFO_CJK)); + pItemPool->SetPoolDefaultItem(SvxFontItem(aAppFont.GetFamilyType(), aAppFont.GetFamilyName(), + "", PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, + EE_CHAR_FONTINFO_CTL)); + + pItemPool->SetPoolDefaultItem( + SvxFontHeightItem(aAppFont.GetFontHeight() * 20, 100, EE_CHAR_FONTHEIGHT)); + pItemPool->SetPoolDefaultItem( + SvxFontHeightItem(aAppFont.GetFontHeight() * 20, 100, EE_CHAR_FONTHEIGHT_CJK)); + pItemPool->SetPoolDefaultItem( + SvxFontHeightItem(aAppFont.GetFontHeight() * 20, 100, EE_CHAR_FONTHEIGHT_CTL)); + + m_xEditEngine.reset(new EditEngine(pItemPool.get())); +} + +void WeldEditView::Resize() +{ + if (EditView* pEditView = GetEditView()) + { + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + Size aOutputSize(rDevice.PixelToLogic(GetOutputSizePixel())); + // Resizes the edit engine to adjust to the size of the output area + pEditView->SetOutputArea(tools::Rectangle(Point(0, 0), aOutputSize)); + GetEditEngine()->SetPaperSize(aOutputSize); + pEditView->ShowCursor(); + + const tools::Long nMaxVisAreaStart + = pEditView->GetEditEngine()->GetTextHeight() - aOutputSize.Height(); + tools::Rectangle aVisArea(pEditView->GetVisArea()); + if (aVisArea.Top() > nMaxVisAreaStart) + { + aVisArea.SetTop(std::max<tools::Long>(nMaxVisAreaStart, 0)); + aVisArea.SetSize(aOutputSize); + pEditView->SetVisArea(aVisArea); + pEditView->ShowCursor(); + } + + EditViewScrollStateChange(); + } + weld::CustomWidgetController::Resize(); +} + +void WeldEditView::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + DoPaint(rRenderContext, rRect); +} + +void WeldEditView::DoPaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + rRenderContext.Push(vcl::PushFlags::ALL); + rRenderContext.SetClipRegion(); + + std::vector<tools::Rectangle> aLogicRects; + + if (EditView* pEditView = GetEditView()) + { + pEditView->Paint(comphelper::LibreOfficeKit::isActive() ? rRenderContext.PixelToLogic(rRect) + : rRect, + &rRenderContext); + + if (HasFocus()) + { + pEditView->ShowCursor(false); + vcl::Cursor* pCursor = pEditView->GetCursor(); + pCursor->DrawToDevice(rRenderContext); + } + + // get logic selection + pEditView->GetSelectionRectangles(aLogicRects); + } + + if (!aLogicRects.empty()) + { + std::vector<basegfx::B2DRange> aLogicRanges; + aLogicRanges.reserve(aLogicRects.size()); + + tools::Long nMinX(LONG_MAX), nMaxX(0), nMinY(LONG_MAX), nMaxY(0); + for (const auto& aRect : aLogicRects) + { + nMinX = std::min(nMinX, aRect.Left()); + nMinY = std::min(nMinY, aRect.Top()); + nMaxX = std::max(nMaxX, aRect.Right()); + nMaxY = std::max(nMaxY, aRect.Bottom()); + } + + const Size aLogicPixel(rRenderContext.PixelToLogic(Size(1, 1))); + for (const auto& aRect : aLogicRects) + { + // Extend each range by one pixel so multiple lines touch each + // other if adjacent, so the whole set is drawn with a single + // border around the lot. But keep the selection within the + // original max extents. + auto nTop = aRect.Top(); + if (nTop > nMinY) + nTop -= aLogicPixel.Height(); + auto nBottom = aRect.Bottom(); + if (nBottom < nMaxY) + nBottom += aLogicPixel.Height(); + auto nLeft = aRect.Left(); + if (nLeft > nMinX) + nLeft -= aLogicPixel.Width(); + auto nRight = aRect.Right(); + if (nRight < nMaxX) + nRight += aLogicPixel.Width(); + + aLogicRanges.emplace_back(nLeft, nTop, nRight, nBottom); + } + + // get the system's highlight color + const Color aHighlight(SvtOptionsDrawinglayer::getHilightColor()); + + sdr::overlay::OverlaySelection aCursorOverlay(sdr::overlay::OverlayType::Transparent, + aHighlight, std::move(aLogicRanges), true); + + drawinglayer::geometry::ViewInformation2D aViewInformation2D; + aViewInformation2D.setViewTransformation(rRenderContext.GetViewTransformation()); + aViewInformation2D.setViewport(vcl::unotools::b2DRectangleFromRectangle(rRect)); + + std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor( + drawinglayer::processor2d::createProcessor2DFromOutputDevice(rRenderContext, + aViewInformation2D)); + + xProcessor->process(aCursorOverlay.getOverlayObjectPrimitive2DSequence()); + } + + rRenderContext.Pop(); +} + +bool WeldEditView::MouseMove(const MouseEvent& rMEvt) +{ + EditView* pEditView = GetEditView(); + return pEditView && pEditView->MouseMove(rMEvt); +} + +bool WeldEditView::MouseButtonDown(const MouseEvent& rMEvt) +{ + if (!IsMouseCaptured()) + CaptureMouse(); + + if (!HasFocus() && CanFocus()) + GrabFocus(); + + EditView* pEditView = GetEditView(); + return pEditView && pEditView->MouseButtonDown(rMEvt); +} + +bool WeldEditView::MouseButtonUp(const MouseEvent& rMEvt) +{ + if (IsMouseCaptured()) + ReleaseMouse(); + EditView* pEditView = GetEditView(); + return pEditView && pEditView->MouseButtonUp(rMEvt); +} + +bool WeldEditView::KeyInput(const KeyEvent& rKEvt) +{ + EditView* pEditView = GetEditView(); + + sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode(); + + if (nKey == KEY_TAB && !GetAcceptsTab()) + { + return false; + } + else if (pEditView && !pEditView->PostKeyEvent(rKEvt)) + { + if (rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2()) + { + if (nKey == KEY_A) + { + EditEngine* pEditEngine = GetEditEngine(); + sal_Int32 nPar = pEditEngine->GetParagraphCount(); + if (nPar) + { + sal_Int32 nLen = pEditEngine->GetTextLen(nPar - 1); + pEditView->SetSelection(ESelection(0, 0, nPar - 1, nLen)); + } + return true; + } + } + + return false; + } + + return true; +} + +bool WeldEditView::Command(const CommandEvent& rCEvt) +{ + EditView* pEditView = GetEditView(); + if (!pEditView) + return false; + return pEditView->Command(rCEvt); +} + +Point WeldEditView::EditViewPointerPosPixel() const +{ + return GetDrawingArea()->get_pointer_position(); +} + +class WeldEditAccessible; + +namespace +{ +class WeldViewForwarder : public SvxViewForwarder +{ + WeldEditAccessible& m_rEditAcc; + + WeldViewForwarder(const WeldViewForwarder&) = delete; + WeldViewForwarder& operator=(const WeldViewForwarder&) = delete; + +public: + explicit WeldViewForwarder(WeldEditAccessible& rAcc) + : m_rEditAcc(rAcc) + { + } + + virtual bool IsValid() const override; + virtual Point LogicToPixel(const Point& rPoint, const MapMode& rMapMode) const override; + virtual Point PixelToLogic(const Point& rPoint, const MapMode& rMapMode) const override; +}; +} + +class WeldEditAccessible; + +namespace +{ +class WeldEditSource; + +/* analog to SvxEditEngineForwarder */ +class WeldTextForwarder : public SvxTextForwarder +{ + WeldEditAccessible& m_rEditAcc; + WeldEditSource& m_rEditSource; + + DECL_LINK(NotifyHdl, EENotify&, void); + + WeldTextForwarder(const WeldTextForwarder&) = delete; + WeldTextForwarder& operator=(const WeldTextForwarder&) = delete; + +public: + WeldTextForwarder(WeldEditAccessible& rAcc, WeldEditSource& rSource); + virtual ~WeldTextForwarder() override; + + virtual sal_Int32 GetParagraphCount() const override; + virtual sal_Int32 GetTextLen(sal_Int32 nParagraph) const override; + virtual OUString GetText(const ESelection& rSel) const override; + virtual SfxItemSet GetAttribs(const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib + = EditEngineAttribs::All) const override; + virtual SfxItemSet GetParaAttribs(sal_Int32 nPara) const override; + virtual void SetParaAttribs(sal_Int32 nPara, const SfxItemSet& rSet) override; + virtual void RemoveAttribs(const ESelection& rSelection) override; + virtual void GetPortions(sal_Int32 nPara, std::vector<sal_Int32>& rList) const override; + + virtual OUString GetStyleSheet(sal_Int32 nPara) const override; + virtual void SetStyleSheet(sal_Int32 nPara, const OUString& rStyleName) override; + + virtual SfxItemState GetItemState(const ESelection& rSel, sal_uInt16 nWhich) const override; + virtual SfxItemState GetItemState(sal_Int32 nPara, sal_uInt16 nWhich) const override; + + virtual void QuickInsertText(const OUString& rText, const ESelection& rSel) override; + virtual void QuickInsertField(const SvxFieldItem& rFld, const ESelection& rSel) override; + virtual void QuickSetAttribs(const SfxItemSet& rSet, const ESelection& rSel) override; + virtual void QuickInsertLineBreak(const ESelection& rSel) override; + + virtual SfxItemPool* GetPool() const override; + + virtual OUString CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, + std::optional<Color>& rpTxtColor, + std::optional<Color>& rpFldColor, + std::optional<FontLineStyle>& rpFldLineStyle) override; + virtual void FieldClicked(const SvxFieldItem&) override; + virtual bool IsValid() const override; + + virtual LanguageType GetLanguage(sal_Int32, sal_Int32) const override; + virtual sal_Int32 GetFieldCount(sal_Int32 nPara) const override; + virtual EFieldInfo GetFieldInfo(sal_Int32 nPara, sal_uInt16 nField) const override; + virtual EBulletInfo GetBulletInfo(sal_Int32 nPara) const override; + virtual tools::Rectangle GetCharBounds(sal_Int32 nPara, sal_Int32 nIndex) const override; + virtual tools::Rectangle GetParaBounds(sal_Int32 nPara) const override; + virtual MapMode GetMapMode() const override; + virtual OutputDevice* GetRefDevice() const override; + virtual bool GetIndexAtPoint(const Point&, sal_Int32& nPara, sal_Int32& nIndex) const override; + virtual bool GetWordIndices(sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart, + sal_Int32& nEnd) const override; + virtual bool GetAttributeRun(sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nPara, + sal_Int32 nIndex, bool bInCell = false) const override; + virtual sal_Int32 GetLineCount(sal_Int32 nPara) const override; + virtual sal_Int32 GetLineLen(sal_Int32 nPara, sal_Int32 nLine) const override; + virtual void GetLineBoundaries(/*out*/ sal_Int32& rStart, /*out*/ sal_Int32& rEnd, + sal_Int32 nParagraph, sal_Int32 nLine) const override; + virtual sal_Int32 GetLineNumberAtIndex(sal_Int32 nPara, sal_Int32 nLine) const override; + virtual bool Delete(const ESelection&) override; + virtual bool InsertText(const OUString&, const ESelection&) override; + virtual bool QuickFormatDoc(bool bFull = false) override; + + virtual sal_Int16 GetDepth(sal_Int32 nPara) const override; + virtual bool SetDepth(sal_Int32 nPara, sal_Int16 nNewDepth) override; + + virtual const SfxItemSet* GetEmptyItemSetPtr() override; + // implementation functions for XParagraphAppend and XTextPortionAppend + virtual void AppendParagraph() override; + virtual sal_Int32 AppendTextPortion(sal_Int32 nPara, const OUString& rText, + const SfxItemSet& rSet) override; + + virtual void CopyText(const SvxTextForwarder& rSource) override; +}; + +/* analog to SvxEditEngineViewForwarder */ +class WeldEditViewForwarder : public SvxEditViewForwarder +{ + WeldEditAccessible& m_rEditAcc; + + WeldEditViewForwarder(const WeldEditViewForwarder&) = delete; + WeldEditViewForwarder& operator=(const WeldEditViewForwarder&) = delete; + +public: + explicit WeldEditViewForwarder(WeldEditAccessible& rAcc); + + virtual bool IsValid() const override; + + virtual Point LogicToPixel(const Point& rPoint, const MapMode& rMapMode) const override; + virtual Point PixelToLogic(const Point& rPoint, const MapMode& rMapMode) const override; + + virtual bool GetSelection(ESelection& rSelection) const override; + virtual bool SetSelection(const ESelection& rSelection) override; + virtual bool Copy() override; + virtual bool Cut() override; + virtual bool Paste() override; +}; + +class WeldEditSource : public SvxEditSource +{ + SfxBroadcaster m_aBroadCaster; + WeldViewForwarder m_aViewFwd; + WeldTextForwarder m_aTextFwd; + WeldEditViewForwarder m_aEditViewFwd; + WeldEditAccessible& m_rEditAcc; + + WeldEditSource(const WeldEditSource& rSrc) + : SvxEditSource() + , m_aViewFwd(rSrc.m_rEditAcc) + , m_aTextFwd(rSrc.m_rEditAcc, *this) + , m_aEditViewFwd(rSrc.m_rEditAcc) + , m_rEditAcc(rSrc.m_rEditAcc) + { + } + + WeldEditSource& operator=(const WeldEditSource&) = delete; + +public: + WeldEditSource(WeldEditAccessible& rAcc) + : m_aViewFwd(rAcc) + , m_aTextFwd(rAcc, *this) + , m_aEditViewFwd(rAcc) + , m_rEditAcc(rAcc) + { + } + + virtual std::unique_ptr<SvxEditSource> Clone() const override + { + return std::unique_ptr<SvxEditSource>(new WeldEditSource(*this)); + } + + virtual SvxTextForwarder* GetTextForwarder() override { return &m_aTextFwd; } + + virtual SvxViewForwarder* GetViewForwarder() override { return &m_aViewFwd; } + + virtual SvxEditViewForwarder* GetEditViewForwarder(bool /*bCreate*/) override + { + return &m_aEditViewFwd; + } + + virtual void UpdateData() override + { + // would possibly only by needed if the XText interface is implemented + // and its text needs to be updated. + } + virtual SfxBroadcaster& GetBroadcaster() const override + { + return const_cast<WeldEditSource*>(this)->m_aBroadCaster; + } +}; +} + +typedef cppu::WeakImplHelper<css::lang::XServiceInfo, css::accessibility::XAccessible, + css::accessibility::XAccessibleComponent, + css::accessibility::XAccessibleContext, + css::accessibility::XAccessibleEventBroadcaster> + WeldEditAccessibleBaseClass; + +class WeldEditAccessible : public WeldEditAccessibleBaseClass +{ + weld::CustomWidgetController* m_pController; + EditEngine* m_pEditEngine; + EditView* m_pEditView; + std::unique_ptr<::accessibility::AccessibleTextHelper> m_xTextHelper; + +public: + WeldEditAccessible(weld::CustomWidgetController* pController) + : m_pController(pController) + , m_pEditEngine(nullptr) + , m_pEditView(nullptr) + { + } + + ::accessibility::AccessibleTextHelper* GetTextHelper() { return m_xTextHelper.get(); } + + void Init(EditEngine* pEditEngine, EditView* pEditView) + { + m_pEditEngine = pEditEngine; + m_pEditView = pEditView; + m_xTextHelper.reset( + new ::accessibility::AccessibleTextHelper(std::make_unique<WeldEditSource>(*this))); + m_xTextHelper->SetEventSource(this); + } + + EditEngine* GetEditEngine() { return m_pEditEngine; } + EditView* GetEditView() { return m_pEditView; } + + void ClearWin() + { + // remove handler before current object gets destroyed + // (avoid handler being called for already dead object) + m_pEditEngine->SetNotifyHdl(Link<EENotify&, void>()); + + m_pEditEngine = nullptr; + m_pEditView = nullptr; + m_pController = nullptr; // implicitly results in AccessibleStateType::DEFUNC set + + //! make TextHelper implicitly release C++ references to some core objects + m_xTextHelper->SetEditSource(::std::unique_ptr<SvxEditSource>()); + + //! make TextHelper release references + //! (e.g. the one set by the 'SetEventSource' call) + m_xTextHelper->Dispose(); + m_xTextHelper.reset(); + } + + // XAccessible + virtual css::uno::Reference<css::accessibility::XAccessibleContext> + SAL_CALL getAccessibleContext() override + { + return this; + } + + // XAccessibleComponent + virtual sal_Bool SAL_CALL containsPoint(const css::awt::Point& rPoint) override + { + //! the arguments coordinates are relative to the current window ! + //! Thus the top left-point is (0, 0) + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + Size aSz(m_pController->GetOutputSizePixel()); + return rPoint.X >= 0 && rPoint.Y >= 0 && rPoint.X < aSz.Width() && rPoint.Y < aSz.Height(); + } + + virtual css::uno::Reference<css::accessibility::XAccessible> + SAL_CALL getAccessibleAtPoint(const css::awt::Point& rPoint) override + { + SolarMutexGuard aGuard; + if (!m_xTextHelper) + throw css::uno::RuntimeException(); + + return m_xTextHelper->GetAt(rPoint); + } + + virtual css::awt::Rectangle SAL_CALL getBounds() override + { + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + const Point aOutPos; + const Size aOutSize(m_pController->GetOutputSizePixel()); + css::awt::Rectangle aRet; + + aRet.X = aOutPos.X(); + aRet.Y = aOutPos.Y(); + aRet.Width = aOutSize.Width(); + aRet.Height = aOutSize.Height(); + + return aRet; + } + + virtual css::awt::Point SAL_CALL getLocation() override + { + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + const css::awt::Rectangle aRect(getBounds()); + css::awt::Point aRet; + + aRet.X = aRect.X; + aRet.Y = aRect.Y; + + return aRet; + } + + virtual css::awt::Point SAL_CALL getLocationOnScreen() override + { + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + css::awt::Point aScreenLoc(0, 0); + + if (weld::DrawingArea* pDrawingArea = m_pController->GetDrawingArea()) + { + AbsoluteScreenPixelPoint aPos = pDrawingArea->get_accessible_location_on_screen(); + aScreenLoc.X = aPos.X(); + aScreenLoc.Y = aPos.Y(); + } + + return aScreenLoc; + } + + virtual css::awt::Size SAL_CALL getSize() override + { + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + Size aSz(m_pController->GetOutputSizePixel()); + return css::awt::Size(aSz.Width(), aSz.Height()); + } + + virtual void SAL_CALL grabFocus() override { m_pController->GrabFocus(); } + + virtual sal_Int32 SAL_CALL getForeground() override + { + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + Color nCol = m_pEditEngine->GetAutoColor(); + return static_cast<sal_Int32>(nCol); + } + + virtual sal_Int32 SAL_CALL getBackground() override + { + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + Color nCol = m_pEditEngine->GetBackgroundColor(); + return static_cast<sal_Int32>(nCol); + } + + // XAccessibleContext + virtual sal_Int64 SAL_CALL getAccessibleChildCount() override + { + if (m_xTextHelper) + return m_xTextHelper->GetChildCount(); + return 0; + } + + virtual css::uno::Reference<css::accessibility::XAccessible> + SAL_CALL getAccessibleChild(sal_Int64 i) override + { + if (m_xTextHelper) + return m_xTextHelper->GetChild(i); + throw css::lang::IndexOutOfBoundsException(); // there is no child... + } + + virtual css::uno::Reference<css::accessibility::XAccessible> + SAL_CALL getAccessibleParent() override + { + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + return m_pController->GetDrawingArea()->get_accessible_parent(); + } + + virtual sal_Int64 SAL_CALL getAccessibleIndexInParent() override + { + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + // -1 for child not found/no parent (according to specification) + sal_Int64 nRet = -1; + + css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent()); + if (!xParent) + return nRet; + + try + { + css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext( + xParent->getAccessibleContext()); + + // iterate over parent's children and search for this object + if (xParentContext.is()) + { + sal_Int64 nChildCount = xParentContext->getAccessibleChildCount(); + for (sal_Int64 nChild = 0; (nChild < nChildCount) && (-1 == nRet); ++nChild) + { + css::uno::Reference<css::accessibility::XAccessible> xChild( + xParentContext->getAccessibleChild(nChild)); + if (xChild.get() == this) + nRet = nChild; + } + } + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION("svx", "WeldEditAccessible::getAccessibleIndexInParent"); + } + + return nRet; + } + + virtual sal_Int16 SAL_CALL getAccessibleRole() override + { + return css::accessibility::AccessibleRole::TEXT_FRAME; + } + + virtual OUString SAL_CALL getAccessibleDescription() override + { + SolarMutexGuard aGuard; + + OUString aRet; + + if (m_pController) + { + aRet = m_pController->GetAccessibleDescription(); + } + + return aRet; + } + + virtual OUString SAL_CALL getAccessibleName() override + { + SolarMutexGuard aGuard; + + OUString aRet; + + if (m_pController) + { + aRet = m_pController->GetAccessibleName(); + } + + return aRet; + } + + virtual css::uno::Reference<css::accessibility::XAccessibleRelationSet> + SAL_CALL getAccessibleRelationSet() override + { + SolarMutexGuard aGuard; + if (!m_pController) + throw css::uno::RuntimeException(); + + return m_pController->GetDrawingArea()->get_accessible_relation_set(); + } + + virtual sal_Int64 SAL_CALL getAccessibleStateSet() override + { + SolarMutexGuard aGuard; + sal_Int64 nStateSet = 0; + + if (!m_pController || !m_xTextHelper) + nStateSet |= css::accessibility::AccessibleStateType::DEFUNC; + else + { + nStateSet |= css::accessibility::AccessibleStateType::MULTI_LINE; + nStateSet |= css::accessibility::AccessibleStateType::ENABLED; + nStateSet |= css::accessibility::AccessibleStateType::EDITABLE; + nStateSet |= css::accessibility::AccessibleStateType::FOCUSABLE; + nStateSet |= css::accessibility::AccessibleStateType::SELECTABLE; + if (m_pController->HasFocus()) + nStateSet |= css::accessibility::AccessibleStateType::FOCUSED; + if (m_pController->IsActive()) + nStateSet |= css::accessibility::AccessibleStateType::ACTIVE; + if (m_pController->IsVisible()) + nStateSet |= css::accessibility::AccessibleStateType::SHOWING; + if (m_pController->IsReallyVisible()) + nStateSet |= css::accessibility::AccessibleStateType::VISIBLE; + if (COL_TRANSPARENT != m_pEditEngine->GetBackgroundColor()) + nStateSet |= css::accessibility::AccessibleStateType::OPAQUE; + } + + return nStateSet; + } + + virtual css::lang::Locale SAL_CALL getLocale() override + { + SolarMutexGuard aGuard; + return LanguageTag(m_pEditEngine->GetDefaultLanguage()).getLocale(); + } + + // XAccessibleEventBroadcaster + virtual void SAL_CALL addAccessibleEventListener( + const css::uno::Reference<css::accessibility::XAccessibleEventListener>& rListener) override + { + if (!m_xTextHelper) // not disposing (about to destroy view shell) + return; + m_xTextHelper->AddEventListener(rListener); + } + + virtual void SAL_CALL removeAccessibleEventListener( + const css::uno::Reference<css::accessibility::XAccessibleEventListener>& rListener) override + { + if (!m_xTextHelper) // not disposing (about to destroy view shell) + return; + m_xTextHelper->RemoveEventListener(rListener); + } + + virtual OUString SAL_CALL getImplementationName() override { return "WeldEditAccessible"; } + + virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override + { + return cppu::supportsService(this, rServiceName); + } + + virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return { "css::accessibility::Accessible", "css::accessibility::AccessibleComponent", + "css::accessibility::AccessibleContext" }; + } +}; + +css::uno::Reference<css::accessibility::XAccessible> WeldEditView::CreateAccessible() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (!m_xAccessible.is()) + m_xAccessible.set(new WeldEditAccessible(this)); +#endif + return m_xAccessible; +} + +WeldEditView::~WeldEditView() +{ +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (m_xAccessible.is()) + { + m_xAccessible->ClearWin(); // make Accessible nonfunctional + m_xAccessible.clear(); + } +#endif +} + +bool WeldViewForwarder::IsValid() const { return m_rEditAcc.GetEditView() != nullptr; } + +Point WeldViewForwarder::LogicToPixel(const Point& rPoint, const MapMode& rMapMode) const +{ + EditView* pEditView = m_rEditAcc.GetEditView(); + if (!pEditView) + return Point(); + OutputDevice& rOutDev = pEditView->GetOutputDevice(); + MapMode aMapMode(rOutDev.GetMapMode()); + Point aPoint(OutputDevice::LogicToLogic(rPoint, rMapMode, MapMode(aMapMode.GetMapUnit()))); + aMapMode.SetOrigin(Point()); + return rOutDev.LogicToPixel(aPoint, aMapMode); +} + +Point WeldViewForwarder::PixelToLogic(const Point& rPoint, const MapMode& rMapMode) const +{ + EditView* pEditView = m_rEditAcc.GetEditView(); + if (!pEditView) + return Point(); + OutputDevice& rOutDev = pEditView->GetOutputDevice(); + MapMode aMapMode(rOutDev.GetMapMode()); + aMapMode.SetOrigin(Point()); + Point aPoint(rOutDev.PixelToLogic(rPoint, aMapMode)); + return OutputDevice::LogicToLogic(aPoint, MapMode(aMapMode.GetMapUnit()), rMapMode); +} + +WeldTextForwarder::WeldTextForwarder(WeldEditAccessible& rAcc, WeldEditSource& rSource) + : m_rEditAcc(rAcc) + , m_rEditSource(rSource) +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->SetNotifyHdl(LINK(this, WeldTextForwarder, NotifyHdl)); +} + +WeldTextForwarder::~WeldTextForwarder() +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->SetNotifyHdl(Link<EENotify&, void>()); +} + +IMPL_LINK(WeldTextForwarder, NotifyHdl, EENotify&, rNotify, void) +{ + if (EditEngine* pEditEngine = m_rEditAcc.GetEditEngine()) + { + if (rNotify.eNotificationType == EE_NOTIFY_PROCESSNOTIFICATIONS + && !pEditEngine->IsUpdateLayout()) + { + // tdf#143088 an UpdateMode of false will just to on to cause + // AccessibleTextHelper_Impl::GetTextForwarder to throw an + // exception as a Frozen EditEngine is considered Invalid so return + // early instead + return; + } + } + + ::std::unique_ptr<SfxHint> aHint = SvxEditSourceHelper::EENotification2Hint(&rNotify); + if (aHint) + m_rEditSource.GetBroadcaster().Broadcast(*aHint); +} + +sal_Int32 WeldTextForwarder::GetParagraphCount() const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetParagraphCount() : 0; +} + +sal_Int32 WeldTextForwarder::GetTextLen(sal_Int32 nParagraph) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetTextLen(nParagraph) : 0; +} + +OUString WeldTextForwarder::GetText(const ESelection& rSel) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + OUString aRet; + if (pEditEngine) + aRet = pEditEngine->GetText(rSel); + return convertLineEnd(aRet, GetSystemLineEnd()); +} + +SfxItemSet WeldTextForwarder::GetAttribs(const ESelection& rSel, + EditEngineAttribs nOnlyHardAttrib) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + assert(pEditEngine && "EditEngine missing"); + if (rSel.nStartPara == rSel.nEndPara) + { + GetAttribsFlags nFlags = GetAttribsFlags::NONE; + switch (nOnlyHardAttrib) + { + case EditEngineAttribs::All: + nFlags = GetAttribsFlags::ALL; + break; + case EditEngineAttribs::OnlyHard: + nFlags = GetAttribsFlags::CHARATTRIBS; + break; + default: + SAL_WARN("svx", "unknown flags for WeldTextForwarder::GetAttribs"); + } + + return pEditEngine->GetAttribs(rSel.nStartPara, rSel.nStartPos, rSel.nEndPos, nFlags); + } + else + { + return pEditEngine->GetAttribs(rSel, nOnlyHardAttrib); + } +} + +SfxItemSet WeldTextForwarder::GetParaAttribs(sal_Int32 nPara) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + assert(pEditEngine && "EditEngine missing"); + + SfxItemSet aSet(pEditEngine->GetParaAttribs(nPara)); + + sal_uInt16 nWhich = EE_PARA_START; + while (nWhich <= EE_PARA_END) + { + if (aSet.GetItemState(nWhich) != SfxItemState::SET) + { + if (pEditEngine->HasParaAttrib(nPara, nWhich)) + aSet.Put(pEditEngine->GetParaAttrib(nPara, nWhich)); + } + nWhich++; + } + + return aSet; +} + +void WeldTextForwarder::SetParaAttribs(sal_Int32 nPara, const SfxItemSet& rSet) +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->SetParaAttribs(nPara, rSet); +} + +SfxItemPool* WeldTextForwarder::GetPool() const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetEmptyItemSet().GetPool() : nullptr; +} + +void WeldTextForwarder::RemoveAttribs(const ESelection& rSelection) +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->RemoveAttribs(rSelection, false /*bRemoveParaAttribs*/, 0); +} + +void WeldTextForwarder::GetPortions(sal_Int32 nPara, std::vector<sal_Int32>& rList) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->GetPortions(nPara, rList); +} + +OUString WeldTextForwarder::GetStyleSheet(sal_Int32 nPara) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (auto pStyle = pEditEngine ? pEditEngine->GetStyleSheet(nPara) : nullptr) + return pStyle->GetName(); + return OUString(); +} + +void WeldTextForwarder::SetStyleSheet(sal_Int32 nPara, const OUString& rStyleName) +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + auto pStyleSheetPool = pEditEngine ? pEditEngine->GetStyleSheetPool() : nullptr; + if (auto pStyle + = pStyleSheetPool ? pStyleSheetPool->Find(rStyleName, SfxStyleFamily::Para) : nullptr) + pEditEngine->SetStyleSheet(nPara, static_cast<SfxStyleSheet*>(pStyle)); +} + +void WeldTextForwarder::QuickInsertText(const OUString& rText, const ESelection& rSel) +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->QuickInsertText(rText, rSel); +} + +void WeldTextForwarder::QuickInsertLineBreak(const ESelection& rSel) +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->QuickInsertLineBreak(rSel); +} + +void WeldTextForwarder::QuickInsertField(const SvxFieldItem& rFld, const ESelection& rSel) +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->QuickInsertField(rFld, rSel); +} + +void WeldTextForwarder::QuickSetAttribs(const SfxItemSet& rSet, const ESelection& rSel) +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->QuickSetAttribs(rSet, rSel); +} + +bool WeldTextForwarder::IsValid() const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + // cannot reliably query EditEngine state + // while in the middle of an update + return pEditEngine && pEditEngine->IsUpdateLayout(); +} + +OUString WeldTextForwarder::CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, + sal_Int32 nPos, std::optional<Color>& rpTxtColor, + std::optional<Color>& rpFldColor, + std::optional<FontLineStyle>& rpFldLineStyle) +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->CalcFieldValue(rField, nPara, nPos, rpTxtColor, rpFldColor, + rpFldLineStyle) + : OUString(); +} + +void WeldTextForwarder::FieldClicked(const SvxFieldItem&) {} + +static SfxItemState GetSvxEditEngineItemState(EditEngine const& rEditEngine, const ESelection& rSel, + sal_uInt16 nWhich) +{ + std::vector<EECharAttrib> aAttribs; + + const SfxPoolItem* pLastItem = nullptr; + + SfxItemState eState = SfxItemState::DEFAULT; + + // check all paragraphs inside the selection + for (sal_Int32 nPara = rSel.nStartPara; nPara <= rSel.nEndPara; nPara++) + { + SfxItemState eParaState = SfxItemState::DEFAULT; + + // calculate start and endpos for this paragraph + sal_Int32 nPos = 0; + if (rSel.nStartPara == nPara) + nPos = rSel.nStartPos; + + sal_Int32 nEndPos = rSel.nEndPos; + if (rSel.nEndPara != nPara) + nEndPos = rEditEngine.GetTextLen(nPara); + + // get list of char attribs + rEditEngine.GetCharAttribs(nPara, aAttribs); + + bool bEmpty = true; // we found no item inside the selection of this paragraph + bool bGaps = false; // we found items but there are gaps between them + sal_Int32 nLastEnd = nPos; + + const SfxPoolItem* pParaItem = nullptr; + + for (const auto& rAttrib : aAttribs) + { + OSL_ENSURE(rAttrib.pAttr, "GetCharAttribs gives corrupt data"); + + const bool bEmptyPortion = (rAttrib.nStart == rAttrib.nEnd); + if ((!bEmptyPortion && (rAttrib.nStart >= nEndPos)) + || (bEmptyPortion && (rAttrib.nStart > nEndPos))) + break; // break if we are already behind our selection + + if ((!bEmptyPortion && (rAttrib.nEnd <= nPos)) + || (bEmptyPortion && (rAttrib.nEnd < nPos))) + continue; // or if the attribute ends before our selection + + if (rAttrib.pAttr->Which() != nWhich) + continue; // skip if is not the searched item + + // if we already found an item + if (pParaItem) + { + // ... and its different to this one than the state is don't care + if (*pParaItem != *(rAttrib.pAttr)) + return SfxItemState::DONTCARE; + } + else + { + pParaItem = rAttrib.pAttr; + } + + if (bEmpty) + bEmpty = false; + + if (!bGaps && rAttrib.nStart > nLastEnd) + bGaps = true; + + nLastEnd = rAttrib.nEnd; + } + + if (!bEmpty && !bGaps && nLastEnd < (nEndPos - 1)) + bGaps = true; + if (bEmpty) + eParaState = SfxItemState::DEFAULT; + else if (bGaps) + eParaState = SfxItemState::DONTCARE; + else + eParaState = SfxItemState::SET; + + // if we already found an item check if we found the same + if (pLastItem) + { + if ((pParaItem == nullptr) || (*pLastItem != *pParaItem)) + return SfxItemState::DONTCARE; + } + else + { + pLastItem = pParaItem; + eState = eParaState; + } + } + + return eState; +} + +SfxItemState WeldTextForwarder::GetItemState(const ESelection& rSel, sal_uInt16 nWhich) const +{ + SfxItemState nState = SfxItemState::DISABLED; + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + nState = GetSvxEditEngineItemState(*pEditEngine, rSel, nWhich); + return nState; +} + +SfxItemState WeldTextForwarder::GetItemState(sal_Int32 nPara, sal_uInt16 nWhich) const +{ + SfxItemState nState = SfxItemState::DISABLED; + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + { + const SfxItemSet& rSet = pEditEngine->GetParaAttribs(nPara); + nState = rSet.GetItemState(nWhich); + } + return nState; +} + +LanguageType WeldTextForwarder::GetLanguage(sal_Int32 nPara, sal_Int32 nIndex) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetLanguage(nPara, nIndex).nLang : LANGUAGE_NONE; +} + +sal_Int32 WeldTextForwarder::GetFieldCount(sal_Int32 nPara) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetFieldCount(nPara) : 0; +} + +EFieldInfo WeldTextForwarder::GetFieldInfo(sal_Int32 nPara, sal_uInt16 nField) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetFieldInfo(nPara, nField) : EFieldInfo(); +} + +EBulletInfo WeldTextForwarder::GetBulletInfo(sal_Int32 /*nPara*/) const { return EBulletInfo(); } + +tools::Rectangle WeldTextForwarder::GetCharBounds(sal_Int32 nPara, sal_Int32 nIndex) const +{ + tools::Rectangle aRect(0, 0, 0, 0); + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + + if (pEditEngine) + { + // Handle virtual position one-past-the end of the string + if (nIndex >= pEditEngine->GetTextLen(nPara)) + { + if (nIndex) + aRect = pEditEngine->GetCharacterBounds(EPosition(nPara, nIndex - 1)); + + aRect.Move(aRect.Right() - aRect.Left(), 0); + aRect.SetSize(Size(1, pEditEngine->GetTextHeight())); + } + else + { + aRect = pEditEngine->GetCharacterBounds(EPosition(nPara, nIndex)); + } + } + return aRect; +} + +tools::Rectangle WeldTextForwarder::GetParaBounds(sal_Int32 nPara) const +{ + tools::Rectangle aRect(0, 0, 0, 0); + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + + if (pEditEngine) + { + const Point aPnt = pEditEngine->GetDocPosTopLeft(nPara); + const sal_Int32 nWidth = pEditEngine->CalcTextWidth(); + const sal_Int32 nHeight = pEditEngine->GetTextHeight(nPara); + aRect = tools::Rectangle(aPnt.X(), aPnt.Y(), aPnt.X() + nWidth, aPnt.Y() + nHeight); + } + + return aRect; +} + +MapMode WeldTextForwarder::GetMapMode() const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetRefMapMode() : MapMode(MapUnit::Map100thMM); +} + +OutputDevice* WeldTextForwarder::GetRefDevice() const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetRefDevice() : nullptr; +} + +bool WeldTextForwarder::GetIndexAtPoint(const Point& rPos, sal_Int32& nPara, + sal_Int32& nIndex) const +{ + bool bRes = false; + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + { + EPosition aDocPos = pEditEngine->FindDocPosition(rPos); + nPara = aDocPos.nPara; + nIndex = aDocPos.nIndex; + bRes = true; + } + return bRes; +} + +bool WeldTextForwarder::GetWordIndices(sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart, + sal_Int32& nEnd) const +{ + bool bRes = false; + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + { + ESelection aRes = pEditEngine->GetWord(ESelection(nPara, nIndex, nPara, nIndex), + css::i18n::WordType::DICTIONARY_WORD); + + if (aRes.nStartPara == nPara && aRes.nStartPara == aRes.nEndPara) + { + nStart = aRes.nStartPos; + nEnd = aRes.nEndPos; + + bRes = true; + } + } + + return bRes; +} + +bool WeldTextForwarder::GetAttributeRun(sal_Int32& nStartIndex, sal_Int32& nEndIndex, + sal_Int32 nPara, sal_Int32 nIndex, bool bInCell) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (!pEditEngine) + return false; + SvxEditSourceHelper::GetAttributeRun(nStartIndex, nEndIndex, *pEditEngine, nPara, nIndex, + bInCell); + return true; +} + +sal_Int32 WeldTextForwarder::GetLineCount(sal_Int32 nPara) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetLineCount(nPara) : 0; +} + +sal_Int32 WeldTextForwarder::GetLineLen(sal_Int32 nPara, sal_Int32 nLine) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetLineLen(nPara, nLine) : 0; +} + +void WeldTextForwarder::GetLineBoundaries(/*out*/ sal_Int32& rStart, /*out*/ sal_Int32& rEnd, + sal_Int32 nPara, sal_Int32 nLine) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->GetLineBoundaries(rStart, rEnd, nPara, nLine); + else + rStart = rEnd = 0; +} + +sal_Int32 WeldTextForwarder::GetLineNumberAtIndex(sal_Int32 nPara, sal_Int32 nIndex) const +{ + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetLineNumberAtIndex(nPara, nIndex) : 0; +} + +bool WeldTextForwarder::QuickFormatDoc(bool /*bFull*/) +{ + bool bRes = false; + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + { + pEditEngine->QuickFormatDoc(); + bRes = true; + } + return bRes; +} + +sal_Int16 WeldTextForwarder::GetDepth(sal_Int32 /*nPara*/) const +{ + // math has no outliner... + return -1; +} + +bool WeldTextForwarder::SetDepth(sal_Int32 /*nPara*/, sal_Int16 nNewDepth) +{ + // math has no outliner... + return -1 == nNewDepth; // is it the value from 'GetDepth' ? +} + +bool WeldTextForwarder::Delete(const ESelection& rSelection) +{ + bool bRes = false; + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + { + pEditEngine->QuickDelete(rSelection); + pEditEngine->QuickFormatDoc(); + bRes = true; + } + return bRes; +} + +bool WeldTextForwarder::InsertText(const OUString& rStr, const ESelection& rSelection) +{ + bool bRes = false; + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + { + pEditEngine->QuickInsertText(rStr, rSelection); + pEditEngine->QuickFormatDoc(); + bRes = true; + } + return bRes; +} + +const SfxItemSet* WeldTextForwarder::GetEmptyItemSetPtr() +{ + const SfxItemSet* pItemSet = nullptr; + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + { + pItemSet = &pEditEngine->GetEmptyItemSet(); + } + return pItemSet; +} + +void WeldTextForwarder::AppendParagraph() +{ + // append an empty paragraph + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine) + { + sal_Int32 nParaCount = pEditEngine->GetParagraphCount(); + pEditEngine->InsertParagraph(nParaCount, OUString()); + } +} + +sal_Int32 WeldTextForwarder::AppendTextPortion(sal_Int32 nPara, const OUString& rText, + const SfxItemSet& rSet) +{ + sal_uInt16 nRes = 0; + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine && nPara < pEditEngine->GetParagraphCount()) + { + // append text + ESelection aSel(nPara, pEditEngine->GetTextLen(nPara)); + pEditEngine->QuickInsertText(rText, aSel); + + // set attributes for new appended text + nRes = aSel.nEndPos = pEditEngine->GetTextLen(nPara); + pEditEngine->QuickSetAttribs(rSet, aSel); + } + return nRes; +} + +void WeldTextForwarder::CopyText(const SvxTextForwarder& rSource) +{ + const WeldTextForwarder* pSourceForwarder = dynamic_cast<const WeldTextForwarder*>(&rSource); + if (!pSourceForwarder) + return; + EditEngine* pSourceEditEngine = pSourceForwarder->m_rEditAcc.GetEditEngine(); + EditEngine* pEditEngine = m_rEditAcc.GetEditEngine(); + if (pEditEngine && pSourceEditEngine) + { + std::unique_ptr<EditTextObject> pNewTextObject = pSourceEditEngine->CreateTextObject(); + pEditEngine->SetText(*pNewTextObject); + } +} + +WeldEditViewForwarder::WeldEditViewForwarder(WeldEditAccessible& rAcc) + : m_rEditAcc(rAcc) +{ +} + +bool WeldEditViewForwarder::IsValid() const { return m_rEditAcc.GetEditView() != nullptr; } + +Point WeldEditViewForwarder::LogicToPixel(const Point& rPoint, const MapMode& rMapMode) const +{ + EditView* pEditView = m_rEditAcc.GetEditView(); + if (!pEditView) + return Point(); + OutputDevice& rOutDev = pEditView->GetOutputDevice(); + MapMode aMapMode(rOutDev.GetMapMode()); + Point aPoint(OutputDevice::LogicToLogic(rPoint, rMapMode, MapMode(aMapMode.GetMapUnit()))); + aMapMode.SetOrigin(Point()); + return rOutDev.LogicToPixel(aPoint, aMapMode); +} + +Point WeldEditViewForwarder::PixelToLogic(const Point& rPoint, const MapMode& rMapMode) const +{ + EditView* pEditView = m_rEditAcc.GetEditView(); + if (!pEditView) + return Point(); + OutputDevice& rOutDev = pEditView->GetOutputDevice(); + MapMode aMapMode(rOutDev.GetMapMode()); + aMapMode.SetOrigin(Point()); + Point aPoint(rOutDev.PixelToLogic(rPoint, aMapMode)); + return OutputDevice::LogicToLogic(aPoint, MapMode(aMapMode.GetMapUnit()), rMapMode); +} + +bool WeldEditViewForwarder::GetSelection(ESelection& rSelection) const +{ + bool bRes = false; + EditView* pEditView = m_rEditAcc.GetEditView(); + if (pEditView) + { + rSelection = pEditView->GetSelection(); + bRes = true; + } + return bRes; +} + +bool WeldEditViewForwarder::SetSelection(const ESelection& rSelection) +{ + bool bRes = false; + EditView* pEditView = m_rEditAcc.GetEditView(); + if (pEditView) + { + pEditView->SetSelection(rSelection); + bRes = true; + } + return bRes; +} + +bool WeldEditViewForwarder::Copy() +{ + bool bRes = false; + EditView* pEditView = m_rEditAcc.GetEditView(); + if (pEditView) + { + pEditView->Copy(); + bRes = true; + } + return bRes; +} + +bool WeldEditViewForwarder::Cut() +{ + bool bRes = false; + EditView* pEditView = m_rEditAcc.GetEditView(); + if (pEditView) + { + pEditView->Cut(); + bRes = true; + } + return bRes; +} + +bool WeldEditViewForwarder::Paste() +{ + bool bRes = false; + EditView* pEditView = m_rEditAcc.GetEditView(); + if (pEditView) + { + pEditView->Paste(); + bRes = true; + } + return bRes; +} + +void WeldEditView::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aSize(pDrawingArea->get_size_request()); + if (aSize.Width() == -1) + aSize.setWidth(500); + if (aSize.Height() == -1) + aSize.setHeight(100); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + + SetOutputSizePixel(aSize); + + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + + EnableRTL(false); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + Color aBgColor = rStyleSettings.GetWindowColor(); + + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + + rDevice.SetMapMode(MapMode(MapUnit::MapTwip)); + rDevice.SetBackground(aBgColor); + + Size aOutputSize(rDevice.PixelToLogic(aSize)); + + makeEditEngine(); + m_xEditEngine->SetPaperSize(aOutputSize); + m_xEditEngine->SetRefDevice(&rDevice); + + m_xEditEngine->SetControlWord(m_xEditEngine->GetControlWord() | EEControlBits::MARKFIELDS); + + m_xEditView.reset(new EditView(m_xEditEngine.get(), nullptr)); + m_xEditView->setEditViewCallbacks(this); + m_xEditView->SetOutputArea(tools::Rectangle(Point(0, 0), aOutputSize)); + + m_xEditView->SetBackgroundColor(aBgColor); + m_xEditEngine->SetBackgroundColor(aBgColor); + m_xEditEngine->InsertView(m_xEditView.get()); + + pDrawingArea->set_cursor(PointerStyle::Text); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + InitAccessible(); +#endif +} + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY +void WeldEditView::InitAccessible() +{ + if (m_xAccessible.is()) + m_xAccessible->Init(GetEditEngine(), GetEditView()); +} +#endif + +int WeldEditView::GetSurroundingText(OUString& rSurrounding) +{ + EditView* pEditView = GetEditView(); + if (!pEditView) + return -1; + rSurrounding = pEditView->GetSurroundingText(); + return pEditView->GetSurroundingTextSelection().Min(); +} + +bool WeldEditView::DeleteSurroundingText(const Selection& rRange) +{ + EditView* pEditView = GetEditView(); + if (!pEditView) + return false; + return pEditView->DeleteSurroundingText(rRange); +} + +void WeldEditView::GetFocus() +{ + EditView* pEditView = GetEditView(); + if (pEditView) + { + pEditView->ShowCursor(false); + Invalidate(); // redraw with cursor + } + + weld::CustomWidgetController::GetFocus(); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (m_xAccessible.is()) + { + // Note: will implicitly send the AccessibleStateType::FOCUSED event + ::accessibility::AccessibleTextHelper* pHelper = m_xAccessible->GetTextHelper(); + if (pHelper) + pHelper->SetFocus(); + } +#endif +} + +void WeldEditView::LoseFocus() +{ + weld::CustomWidgetController::LoseFocus(); + Invalidate(); // redraw without cursor + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (m_xAccessible.is()) + { + // Note: will implicitly send the AccessibleStateType::FOCUSED event + ::accessibility::AccessibleTextHelper* pHelper = m_xAccessible->GetTextHelper(); + if (pHelper) + pHelper->SetFocus(false); + } +#endif +} + +bool WeldEditView::CanFocus() const { return true; } + +css::uno::Reference<css::datatransfer::dnd::XDropTarget> WeldEditView::GetDropTarget() +{ + if (!m_xDropTarget) + m_xDropTarget = weld::CustomWidgetController::GetDropTarget(); + return m_xDropTarget; +} + +css::uno::Reference<css::datatransfer::clipboard::XClipboard> WeldEditView::GetClipboard() const +{ + return weld::CustomWidgetController::GetClipboard(); +} + +void WeldEditView::EditViewSelectionChange() +{ + Invalidate(); + +#if !ENABLE_WASM_STRIP_ACCESSIBILITY + if (m_xAccessible.is()) + { + ::accessibility::AccessibleTextHelper* pHelper = m_xAccessible->GetTextHelper(); + if (pHelper) + pHelper->UpdateSelection(); + } +#endif +} + +namespace +{ +class WeldEditViewUIObject final : public DrawingAreaUIObject +{ +private: + WeldEditView* mpEditView; + +public: + WeldEditViewUIObject(const VclPtr<vcl::Window>& rDrawingArea) + : DrawingAreaUIObject(rDrawingArea) + , mpEditView(static_cast<WeldEditView*>(mpController)) + { + } + + static std::unique_ptr<UIObject> create(vcl::Window* pWindow) + { + return std::unique_ptr<UIObject>(new WeldEditViewUIObject(pWindow)); + } + + virtual StringMap get_state() override + { + StringMap aMap = WindowUIObject::get_state(); + aMap["Text"] = mpEditView->GetText(); + return aMap; + } + +private: + virtual OUString get_name() const override { return "WeldEditViewUIObject"; } +}; +} + +FactoryFunction WeldEditView::GetUITestFactory() const { return WeldEditViewUIObject::create; } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |