diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2021-12-07 23:25:54 +0100 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2021-12-20 06:21:34 +0100 |
commit | 046e6cfa544d2ffd67fd29ba7dde41b495744618 (patch) | |
tree | b32fa69e90091ab53c311a0735edb49fa62730d0 | |
parent | b5c616d10bff3213840d4893d13b4493de71fa56 (diff) |
Add graphic size checker for the preferred document DPI
This change adds a graphic size checker, which checks all the
images in the document, if they largely differ (outside of 50% and
110% of the image size) from the set preferred image DPI document
setting. For all images that don't fall under this bounds, list
them in the dialog and offer the posibility to select/goto the
image and pop-up the properties dialog for the image to change
its size.
Change-Id: I06efce77c291fdb6ec3864d72c2f4d15dba9c42b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127094
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
25 files changed, 1007 insertions, 0 deletions
diff --git a/include/sfx2/sfxsids.hrc b/include/sfx2/sfxsids.hrc index d9a8b14eb514..e4bf9bbbe71b 100644 --- a/include/sfx2/sfxsids.hrc +++ b/include/sfx2/sfxsids.hrc @@ -447,6 +447,7 @@ class SvxSearchItem; // Used for redaction #define SID_SHAPE_NAME (SID_SFX_START + 808) // FREE: SID_SFX_START + 809 +#define SID_GRAPHIC_SIZE_CHECK (SID_SFX_START + 809) #define SID_ACCESSIBILITY_CHECK (SID_SFX_START + 810) #define SID_ASYNCHRON (SID_SFX_START + 811) diff --git a/include/svx/GenericCheckDialog.hxx b/include/svx/GenericCheckDialog.hxx new file mode 100644 index 000000000000..ef2a41159737 --- /dev/null +++ b/include/svx/GenericCheckDialog.hxx @@ -0,0 +1,83 @@ +/* -*- 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/. + * + */ + +#pragma once + +#include <svx/svxdllapi.h> +#include <tools/link.hxx> +#include <vcl/weld.hxx> + +namespace svx +{ +class CheckData +{ +public: + virtual ~CheckData() {} + + virtual OUString getText() = 0; + + virtual bool canMarkObject() = 0; + virtual void markObject() = 0; + + virtual bool hasProperties() = 0; + virtual void runProperties() = 0; +}; + +class CheckDataCollection +{ +protected: + std::vector<std::unique_ptr<CheckData>> m_aCollection; + +public: + virtual ~CheckDataCollection() {} + + std::vector<std::unique_ptr<CheckData>>& getCollection() { return m_aCollection; } + + virtual OUString getTitle() = 0; +}; + +class GenericCheckEntry final +{ +private: + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Container> m_xContainer; + std::unique_ptr<weld::Label> m_xLabel; + std::unique_ptr<weld::Button> m_xMarkButton; + std::unique_ptr<weld::Button> m_xPropertiesButton; + + std::unique_ptr<CheckData>& m_pCheckData; + +public: + GenericCheckEntry(weld::Container* pParent, std::unique_ptr<CheckData>& rCheckData); + + weld::Widget* get_widget() const { return m_xContainer.get(); } + + DECL_LINK(MarkButtonClicked, weld::Button&, void); + DECL_LINK(PropertiesButtonClicked, weld::Button&, void); +}; + +class SVX_DLLPUBLIC GenericCheckDialog final : public weld::GenericDialogController +{ +private: + std::vector<std::unique_ptr<GenericCheckEntry>> m_aCheckEntries; + CheckDataCollection& m_rCheckDataCollection; + + // Controls + std::unique_ptr<weld::Box> m_xCheckBox; + +public: + GenericCheckDialog(weld::Window* pParent, CheckDataCollection& rCheckDataCollection); + virtual ~GenericCheckDialog() override; + virtual short run() override; +}; + +} // end svx namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/svx/strings.hrc b/include/svx/strings.hrc index 2e00e3c4c1a7..49c170b0a4bb 100644 --- a/include/svx/strings.hrc +++ b/include/svx/strings.hrc @@ -1816,6 +1816,13 @@ #define RID_SVXSTR_HOR_PRESET_ONLYHOR NC_("RID_SVXSTR_HOR_PRESET_ONLYHOR", "Top and Bottom Borders, and All Inner Lines") #define RID_SVXSTR_VER_PRESET_ONLYVER NC_("RID_SVXSTR_VER_PRESET_ONLYVER", "Left and Right Borders, and All Inner Lines") +/*-------------------------------------------------------------------- + Description: GraphicSizeCheck strings + --------------------------------------------------------------------*/ +#define STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE NC_("STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE", "Graphic Size Check") +#define STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW NC_("STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW", "Image '%NAME%' has too few pixels for the current size (%DPIX% x %DPIY% DPI)") +#define STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH NC_("STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH", "Image '%NAME%' has too many pixels for the current size (%DPIX% x %DPIY% DPI)") + #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu b/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu index 2e3bf1f57647..a51ab13d077c 100644 --- a/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu +++ b/officecfg/registry/data/org/openoffice/Office/UI/GenericCommands.xcu @@ -7389,6 +7389,14 @@ bit 3 (0x8): #define UICOMMANDDESCRIPTION_PROPERTIES_TOGGLEBUTTON 8 <value>1</value> </prop> </node> + <node oor:name=".uno:GraphicSizeCheck" oor:op="replace"> + <prop oor:name="Label" oor:type="xs:string"> + <value xml:lang="en-US">Graphic Size Check...</value> + </prop> + <prop oor:name="Properties" oor:type="xs:int"> + <value>1</value> + </prop> + </node> <node oor:name=".uno:SidebarDeck.PropertyDeck" oor:op="replace"> <prop oor:name="Label" oor:type="xs:string"> <value xml:lang="en-US">Open the Properties Deck</value> diff --git a/sd/Library_sd.mk b/sd/Library_sd.mk index f8e77848e4d3..da11d900f564 100644 --- a/sd/Library_sd.mk +++ b/sd/Library_sd.mk @@ -401,6 +401,7 @@ $(eval $(call gb_Library_add_exception_objects,sd,\ sd/source/ui/tools/AsynchronousCall \ sd/source/ui/tools/ConfigurationAccess \ sd/source/ui/tools/EventMultiplexer \ + sd/source/ui/tools/GraphicSizeCheck \ sd/source/ui/tools/IconCache \ sd/source/ui/tools/IdleDetection \ sd/source/ui/tools/PreviewRenderer \ diff --git a/sd/sdi/_drvwsh.sdi b/sd/sdi/_drvwsh.sdi index 7060d8dd6434..73c2181d0743 100644 --- a/sd/sdi/_drvwsh.sdi +++ b/sd/sdi/_drvwsh.sdi @@ -76,6 +76,11 @@ interface DrawView ExecMethod = FuTemporary ; StateMethod = GetMenuState ; ] + SID_GRAPHIC_SIZE_CHECK + [ + ExecMethod = FuTemporary ; + StateMethod = GetMenuState ; + ] SID_EXTERNAL_EDIT [ ExecMethod = FuTemporary ; diff --git a/sd/source/ui/inc/tools/GraphicSizeCheck.hxx b/sd/source/ui/inc/tools/GraphicSizeCheck.hxx new file mode 100644 index 000000000000..9da3d569bd3a --- /dev/null +++ b/sd/source/ui/inc/tools/GraphicSizeCheck.hxx @@ -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/. + * + */ + +#pragma once + +#include <memory> +#include <drawdoc.hxx> +#include <svx/GenericCheckDialog.hxx> +#include <svx/svdograf.hxx> + +namespace sd +{ +class GraphicSizeViolation final +{ +private: + SdrGrafObj* m_pGraphicObject; + + sal_Int32 m_nLowDPILimit = 0; + sal_Int32 m_nHighDPILimit = 0; + + sal_Int32 m_nDPIX = 0; + sal_Int32 m_nDPIY = 0; + +public: + GraphicSizeViolation(sal_Int32 nDPI, SdrGrafObj* pGraphicObject); + bool check(); + + OUString getGraphicName(); + + SdrGrafObj* getObject() const { return m_pGraphicObject; } + + bool isDPITooLow() { return m_nDPIX < m_nLowDPILimit || m_nDPIY < m_nLowDPILimit; } + + bool isDPITooHigh() { return m_nDPIX > m_nHighDPILimit || m_nDPIY > m_nHighDPILimit; } + + sal_Int32 getDPIX() { return m_nDPIX; } + + sal_Int32 getDPIY() { return m_nDPIY; } +}; + +class GraphicSizeCheck final +{ +private: + SdDrawDocument* m_pDocument; + std::vector<std::unique_ptr<GraphicSizeViolation>> m_aGraphicSizeViolationList; + +public: + GraphicSizeCheck(SdDrawDocument* pDocument) + : m_pDocument(pDocument) + { + } + + void check(); + + std::vector<std::unique_ptr<GraphicSizeViolation>>& getViolationList() + { + return m_aGraphicSizeViolationList; + } +}; + +class GraphicSizeCheckGUIEntry : public svx::CheckData +{ +private: + SdDrawDocument* m_pDocument; + std::unique_ptr<GraphicSizeViolation> m_pViolation; + +public: + GraphicSizeCheckGUIEntry(SdDrawDocument* pDocument, + std::unique_ptr<GraphicSizeViolation>&& pViolation) + : m_pDocument(pDocument) + , m_pViolation(std::move(pViolation)) + { + } + + OUString getText() override; + + bool canMarkObject() override { return true; } + + void markObject() override; + + bool hasProperties() override { return true; } + + void runProperties() override; +}; + +class GraphicSizeCheckGUIResult : public svx::CheckDataCollection +{ +public: + GraphicSizeCheckGUIResult(SdDrawDocument* m_pDocument); + + OUString getTitle() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/GraphicSizeCheck.cxx b/sd/source/ui/tools/GraphicSizeCheck.cxx new file mode 100644 index 000000000000..14bfcf3353b9 --- /dev/null +++ b/sd/source/ui/tools/GraphicSizeCheck.cxx @@ -0,0 +1,213 @@ +/* -*- 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 <memory> +#include <tools/GraphicSizeCheck.hxx> +#include <svx/strings.hrc> +#include <svx/svdobj.hxx> +#include <svx/svdpage.hxx> +#include <svx/svxids.hrc> +#include <sfx2/dispatch.hxx> + +#include <sdresid.hxx> +#include <DrawViewShell.hxx> +#include <DrawDocShell.hxx> + +namespace sd +{ +namespace +{ +class ModelTraverseHandler +{ +public: + virtual ~ModelTraverseHandler() {} + + virtual void handleSdrObject(SdrObject* pObject) = 0; +}; + +class ModelTraverser +{ +private: + std::vector<std::shared_ptr<ModelTraverseHandler>> m_pNodeHandler; + SdDrawDocument* m_pDocument; + +public: + ModelTraverser(SdDrawDocument* pDocument) + : m_pDocument(pDocument) + { + } + + void traverse() + { + if (!m_pDocument) + return; + + for (sal_uInt16 nPage = 0; nPage < m_pDocument->GetPageCount(); ++nPage) + { + SdrPage* pPage = m_pDocument->GetPage(nPage); + if (pPage) + { + for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + if (pObject) + { + for (auto& pNodeHandler : m_pNodeHandler) + { + pNodeHandler->handleSdrObject(pObject); + } + } + } + } + } + } + + void addNodeHandler(std::shared_ptr<ModelTraverseHandler> pHandler) + { + m_pNodeHandler.push_back(pHandler); + } +}; +} + +GraphicSizeViolation::GraphicSizeViolation(sal_Int32 nDPI, SdrGrafObj* pGraphicObject) + : m_pGraphicObject(pGraphicObject) +{ + constexpr double fLowPercentage = 110; + constexpr double fHighPercentage = 50; + + m_nLowDPILimit = sal_Int32(100.0 / fLowPercentage * nDPI); + m_nHighDPILimit = sal_Int32(100.0 / fHighPercentage * nDPI); +} + +bool GraphicSizeViolation::check() +{ + Graphic aGraphic = m_pGraphicObject->GetGraphic(); + Size aSizePixel = aGraphic.GetSizePixel(); + Size aGraphicSize = m_pGraphicObject->GetLogicRect().GetSize(); + + double nSizeXInch + = o3tl::convert(double(aGraphicSize.Width()), o3tl::Length::mm100, o3tl::Length::in); + double nSizeYInch + = o3tl::convert(double(aGraphicSize.Height()), o3tl::Length::mm100, o3tl::Length::in); + + m_nDPIX = sal_Int32(aSizePixel.Width() / nSizeXInch); + m_nDPIY = sal_Int32(aSizePixel.Height() / nSizeYInch); + + return isDPITooLow() || isDPITooHigh(); +} + +OUString GraphicSizeViolation::getGraphicName() { return m_pGraphicObject->GetName(); } + +namespace +{ +class GraphicSizeCheckHandler : public ModelTraverseHandler +{ + sal_Int32 m_nDPI; + std::vector<std::unique_ptr<GraphicSizeViolation>>& m_rGraphicSizeViolationList; + +public: + GraphicSizeCheckHandler( + sal_Int32 nDPI, + std::vector<std::unique_ptr<GraphicSizeViolation>>& rGraphicSizeViolationList) + : m_nDPI(nDPI) + , m_rGraphicSizeViolationList(rGraphicSizeViolationList) + { + } + + void handleSdrObject(SdrObject* pObject) override + { + auto* pGraphicObject = dynamic_cast<SdrGrafObj*>(pObject); + if (!pGraphicObject) + return; + + auto pEntry = std::make_unique<GraphicSizeViolation>(m_nDPI, pGraphicObject); + if (pEntry->check()) + { + m_rGraphicSizeViolationList.push_back(std::move(pEntry)); + } + } +}; + +} // end anonymous namespace + +void GraphicSizeCheck::check() +{ + if (!m_pDocument) + return; + + sal_Int32 nDPI = m_pDocument->getImagePreferredDPI(); + if (nDPI == 0) + return; + + auto pHandler = std::make_shared<GraphicSizeCheckHandler>(nDPI, m_aGraphicSizeViolationList); + + ModelTraverser aModelTraverser(m_pDocument); + aModelTraverser.addNodeHandler(pHandler); + aModelTraverser.traverse(); +} + +OUString GraphicSizeCheckGUIEntry::getText() +{ + OUString sText; + + if (m_pViolation->isDPITooLow()) + { + sText = SdResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW); + } + else if (m_pViolation->isDPITooHigh()) + { + sText = SdResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH); + } + + sText = sText.replaceAll("%NAME%", m_pViolation->getGraphicName()); + sText = sText.replaceAll("%DPIX%", OUString::number(m_pViolation->getDPIX())); + sText = sText.replaceAll("%DPIY%", OUString::number(m_pViolation->getDPIY())); + + return sText; +} + +void GraphicSizeCheckGUIEntry::markObject() +{ + sd::ViewShell* pViewShell = m_pDocument->GetDocSh()->GetViewShell(); + SdrView* pView = pViewShell->GetView(); + pView->ShowSdrPage(m_pViolation->getObject()->getSdrPageFromSdrObject()); + pView->UnmarkAll(); + pView->MarkObj(m_pViolation->getObject(), pView->GetSdrPageView()); +} + +void GraphicSizeCheckGUIEntry::runProperties() +{ + markObject(); + sd::ViewShell* pViewShell = m_pDocument->GetDocSh()->GetViewShell(); + pViewShell->GetDispatcher()->Execute(SID_ATTR_GRAF_CROP, SfxCallMode::SYNCHRON); +} + +GraphicSizeCheckGUIResult::GraphicSizeCheckGUIResult(SdDrawDocument* pDocument) +{ + GraphicSizeCheck aCheck(pDocument); + aCheck.check(); + + auto& rCollection = getCollection(); + for (auto& rpViolation : aCheck.getViolationList()) + { + auto rGUIEntry + = std::make_unique<GraphicSizeCheckGUIEntry>(pDocument, std::move(rpViolation)); + rCollection.push_back(std::move(rGUIEntry)); + } +} + +OUString GraphicSizeCheckGUIResult::getTitle() +{ + return SdResId(STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews2.cxx b/sd/source/ui/view/drviews2.cxx index b951231f2c5b..87d1a34e2ebe 100644 --- a/sd/source/ui/view/drviews2.cxx +++ b/sd/source/ui/view/drviews2.cxx @@ -183,6 +183,7 @@ #include <SlideSorterViewShell.hxx> #include <controller/SlideSorterController.hxx> #include <controller/SlsPageSelector.hxx> +#include <tools/GraphicSizeCheck.hxx> #include <ViewShellBase.hxx> #include <memory> @@ -1462,6 +1463,17 @@ void DrawViewShell::FuTemporary(SfxRequest& rReq) } break; + case SID_GRAPHIC_SIZE_CHECK: + { + sd::GraphicSizeCheckGUIResult aResult(GetDoc()); + svx::GenericCheckDialog aDialog(GetFrameWeld(), aResult); + aDialog.run(); + + Cancel(); + rReq.Ignore(); + } + break; + case SID_ATTRIBUTES_LINE: // BASIC { SetCurrentFunction( FuLine::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); diff --git a/sd/source/ui/view/drviewsj.cxx b/sd/source/ui/view/drviewsj.cxx index 9eebdbc57c29..6bbf88338f06 100644 --- a/sd/source/ui/view/drviewsj.cxx +++ b/sd/source/ui/view/drviewsj.cxx @@ -539,6 +539,10 @@ void DrawViewShell::GetMenuStateSel( SfxItemSet &rSet ) rSet.DisableItem(SID_SAVE_GRAPHIC); rSet.DisableItem(SID_EXTERNAL_EDIT); } + if (GetDoc()->getImagePreferredDPI() <= 0) + { + rSet.DisableItem(SID_GRAPHIC_SIZE_CHECK); + } } } // end of namespace sd diff --git a/sd/uiconfig/sdraw/menubar/menubar.xml b/sd/uiconfig/sdraw/menubar/menubar.xml index fd327d6bc91b..9c5a997bcc5a 100644 --- a/sd/uiconfig/sdraw/menubar/menubar.xml +++ b/sd/uiconfig/sdraw/menubar/menubar.xml @@ -417,6 +417,7 @@ <menu:menuitem menu:id=".uno:ColorSettings"/> <menu:menuseparator/> <menu:menuitem menu:id=".uno:GrafAttrCrop"/> + <menu:menuitem menu:id=".uno:GraphicSizeCheck"/> </menu:menupopup> </menu:menu> <menu:menuseparator/> diff --git a/sd/uiconfig/simpress/menubar/menubar.xml b/sd/uiconfig/simpress/menubar/menubar.xml index fc64d6050e6e..855a26991a0b 100644 --- a/sd/uiconfig/simpress/menubar/menubar.xml +++ b/sd/uiconfig/simpress/menubar/menubar.xml @@ -448,6 +448,7 @@ <menu:menuitem menu:id=".uno:ColorSettings"/> <menu:menuseparator/> <menu:menuitem menu:id=".uno:GrafAttrCrop"/> + <menu:menuitem menu:id=".uno:GraphicSizeCheck"/> </menu:menupopup> </menu:menu> <menu:menu menu:id=".uno:FormatObjectMenu"> diff --git a/svx/Library_svx.mk b/svx/Library_svx.mk index efb5a6e1b031..a13f1cade697 100644 --- a/svx/Library_svx.mk +++ b/svx/Library_svx.mk @@ -105,6 +105,7 @@ $(eval $(call gb_Library_add_exception_objects,svx,\ svx/source/customshapes/EnhancedCustomShapeFontWork \ svx/source/customshapes/EnhancedCustomShapeHandle \ svx/source/dialog/AccessibilityCheckDialog \ + svx/source/dialog/GenericCheckDialog \ svx/source/dialog/_bmpmask \ svx/source/dialog/charmap \ svx/source/dialog/searchcharmap \ diff --git a/svx/UIConfig_svx.mk b/svx/UIConfig_svx.mk index 2186185f10c2..33bc1797f20f 100644 --- a/svx/UIConfig_svx.mk +++ b/svx/UIConfig_svx.mk @@ -78,6 +78,8 @@ $(eval $(call gb_UIConfig_add_uifiles,svx,\ svx/uiconfig/ui/functionmenu \ svx/uiconfig/ui/gallerymenu1 \ svx/uiconfig/ui/gallerymenu2 \ + svx/uiconfig/ui/genericcheckdialog \ + svx/uiconfig/ui/genericcheckentry \ svx/uiconfig/ui/grafctrlbox \ svx/uiconfig/ui/grafmodebox \ svx/uiconfig/ui/headfootformatpage \ diff --git a/svx/sdi/svx.sdi b/svx/sdi/svx.sdi index 1e08145d3644..f0314fe63360 100644 --- a/svx/sdi/svx.sdi +++ b/svx/sdi/svx.sdi @@ -12425,6 +12425,23 @@ SfxVoidItem SpellCheckApplySuggestion SID_SPELLCHECK_APPLY_SUGGESTION GroupId = SfxGroupId::Format; ] +SfxVoidItem GraphicSizeCheck SID_GRAPHIC_SIZE_CHECK +() +[ + AutoUpdate = FALSE, + FastCall = FALSE, + ReadOnlyDoc = TRUE, + Toggle = FALSE, + Container = FALSE, + RecordAbsolute = FALSE, + RecordPerSet; + + AccelConfig = FALSE, + MenuConfig = FALSE, + ToolBoxConfig = FALSE, + GroupId = SfxGroupId::Modify; +] + SfxVoidItem AccessibilityCheck SID_ACCESSIBILITY_CHECK () [ diff --git a/svx/source/dialog/GenericCheckDialog.cxx b/svx/source/dialog/GenericCheckDialog.cxx new file mode 100644 index 000000000000..09fc3d678769 --- /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/uiconfig/ui/genericcheckdialog.ui b/svx/uiconfig/ui/genericcheckdialog.ui new file mode 100644 index 000000000000..232221c0a878 --- /dev/null +++ b/svx/uiconfig/ui/genericcheckdialog.ui @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="svx"> + <requires lib="gtk+" version="3.20"/> + <object class="GtkDialog" id="GenericCheckDialog"> + <property name="width-request">850</property> + <property name="height-request">480</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="border-width">6</property> + <property name="modal">True</property> + <property name="type-hint">dialog</property> + <child internal-child="vbox"> + <object class="GtkBox" id="dialogBox1"> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialogButtons"> + <property name="can-focus">False</property> + <property name="layout-style">end</property> + <child> + <object class="GtkButton" id="ok"> + <property name="label" translatable="yes" context="stock">_OK</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="can-default">True</property> + <property name="has-default">True</property> + <property name="receives-default">True</property> + <property name="use-underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="cancel"> + <property name="label" translatable="yes" context="stock">_Cancel</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="use-underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="help"> + <property name="label" translatable="yes" context="stock">_Help</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="use-underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + <property name="secondary">True</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack-type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkScrolledWindow"> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="shadow-type">in</property> + <child> + <object class="GtkViewport"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <child> + <object class="GtkBox" id="checkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <placeholder/> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-5">ok</action-widget> + <action-widget response="-6">cancel</action-widget> + <action-widget response="-11">help</action-widget> + </action-widgets> + </object> +</interface> diff --git a/svx/uiconfig/ui/genericcheckentry.ui b/svx/uiconfig/ui/genericcheckentry.ui new file mode 100644 index 000000000000..1b02ca13eeac --- /dev/null +++ b/svx/uiconfig/ui/genericcheckentry.ui @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="svx"> + <requires lib="gtk+" version="3.20"/> + <object class="GtkBox" id="checkEntryBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="border-width">3</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="hexpand">True</property> + <property name="selectable">True</property> + <property name="xalign">0</property> + <child internal-child="accessible"> + <object class="AtkObject" id="label-atkobject"> + <property name="AtkObject::accessible-role">static</property> + </object> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="markButton"> + <property name="label" translatable="yes" context="genericcheckentry|markButton">Mark</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="propertiesButton"> + <property name="label" translatable="yes" context="genericcheckentry|propertiesButton">Properties</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + </object> +</interface> diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk index 6989bce49cea..58639859bb0e 100644 --- a/sw/Library_sw.mk +++ b/sw/Library_sw.mk @@ -312,6 +312,7 @@ $(eval $(call gb_Library_add_exception_objects,sw,\ sw/source/core/frmedt/tblsel \ sw/source/core/graphic/grfatr \ sw/source/core/graphic/ndgrf \ + sw/source/core/graphic/GraphicSizeCheck \ sw/source/core/layout/anchoreddrawobject \ sw/source/core/layout/anchoredobject \ sw/source/core/layout/atrfrm \ diff --git a/sw/sdi/_basesh.sdi b/sw/sdi/_basesh.sdi index 44d6c5338e18..dd195496d1a8 100644 --- a/sw/sdi/_basesh.sdi +++ b/sw/sdi/_basesh.sdi @@ -605,4 +605,11 @@ interface BaseTextSelection ExecMethod = ExecDlg; DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; ] + + SID_GRAPHIC_SIZE_CHECK + [ + ExecMethod = ExecDlg; + StateMethod = GetState; + DisableFlags="SfxDisableFlags::SwOnProtectedCursor"; + ] } diff --git a/sw/source/core/graphic/GraphicSizeCheck.cxx b/sw/source/core/graphic/GraphicSizeCheck.cxx new file mode 100644 index 000000000000..50b1cb70a8f2 --- /dev/null +++ b/sw/source/core/graphic/GraphicSizeCheck.cxx @@ -0,0 +1,164 @@ +/* -*- 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 <GraphicSizeCheck.hxx> +#include <svx/strings.hrc> +#include <svx/svdobj.hxx> +#include <unotools/viewoptions.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> + +#include <ModelTraverser.hxx> +#include <ndgrf.hxx> +#include <IDocumentSettingAccess.hxx> +#include <fmtfsize.hxx> +#include <wrtsh.hxx> +#include <wview.hxx> +#include <cmdid.h> + +using namespace css; + +namespace sw +{ +GraphicSizeViolation::GraphicSizeViolation(sal_Int32 nDPI, const SwGrfNode* pGraphicNode) + : m_pGraphicNode(pGraphicNode) +{ + constexpr double fLowPercentage = 110; + constexpr double fHighPercentage = 50; + + m_nLowDPILimit = sal_Int32(100.0 / fLowPercentage * nDPI); + m_nHighDPILimit = sal_Int32(100.0 / fHighPercentage * nDPI); +} + +bool GraphicSizeViolation::check() +{ + auto pFrameFormat = m_pGraphicNode->GetFlyFormat(); + Graphic aGraphic = m_pGraphicNode->GetGraphic(); + Size aSizePixel = aGraphic.GetSizePixel(); + Size aFrameSize(pFrameFormat->GetFrameSize().GetSize()); + + double nSizeXInch + = o3tl::convert(double(aFrameSize.Width()), o3tl::Length::twip, o3tl::Length::in); + double nSizeYInch + = o3tl::convert(double(aFrameSize.Height()), o3tl::Length::twip, o3tl::Length::in); + + m_nDPIX = sal_Int32(aSizePixel.Width() / nSizeXInch); + m_nDPIY = sal_Int32(aSizePixel.Height() / nSizeYInch); + + return isDPITooLow() || isDPITooHigh(); +} + +OUString GraphicSizeViolation::getGraphicName() +{ + return m_pGraphicNode->GetFlyFormat()->GetName(); +} + +namespace +{ +class GraphicSizeCheckHandler : public ModelTraverseHandler +{ +private: + sal_Int32 m_nDPI; + std::vector<std::unique_ptr<GraphicSizeViolation>>& m_rGraphicSizeViolationList; + +public: + GraphicSizeCheckHandler( + sal_Int32 nDPI, + std::vector<std::unique_ptr<GraphicSizeViolation>>& rGraphicSizeViolationList) + : m_nDPI(nDPI) + , m_rGraphicSizeViolationList(rGraphicSizeViolationList) + { + } + + void handleNode(SwNode* pNode) override + { + if (!pNode->IsGrfNode()) + return; + + auto pEntry = std::make_unique<GraphicSizeViolation>(m_nDPI, pNode->GetGrfNode()); + if (pEntry->check()) + { + m_rGraphicSizeViolationList.push_back(std::move(pEntry)); + } + } + + void handleSdrObject(SdrObject* /*pObject*/) override {} +}; + +} // end anonymous namespace + +void GraphicSizeCheck::check() +{ + sal_Int32 nDPI = m_pDocument->getIDocumentSettingAccess().getImagePreferredDPI(); + if (nDPI == 0) + return; + + auto pHandler = std::make_shared<GraphicSizeCheckHandler>(nDPI, m_aGraphicSizeViolationList); + ModelTraverser aModelTraverser(m_pDocument); + aModelTraverser.addNodeHandler(pHandler); + aModelTraverser.traverse(); +} + +OUString GraphicSizeCheckGUIEntry::getText() +{ + OUString sText; + + if (m_pViolation->isDPITooLow()) + { + sText = SwResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW); + } + else if (m_pViolation->isDPITooHigh()) + { + sText = SwResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH); + } + + sText = sText.replaceAll("%NAME%", m_pViolation->getGraphicName()); + sText = sText.replaceAll("%DPIX%", OUString::number(m_pViolation->getDPIX())); + sText = sText.replaceAll("%DPIY%", OUString::number(m_pViolation->getDPIY())); + + return sText; +} + +void GraphicSizeCheckGUIEntry::markObject() +{ + SwWrtShell* pWrtShell = m_pDocument->GetDocShell()->GetWrtShell(); + pWrtShell->GotoFly(m_pViolation->getGraphicName(), FLYCNTTYPE_ALL, true); +} + +void GraphicSizeCheckGUIEntry::runProperties() +{ + markObject(); + SwWrtShell* pWrtShell = m_pDocument->GetDocShell()->GetWrtShell(); + pWrtShell->GetView().GetViewFrame()->GetDispatcher()->Execute(FN_FORMAT_GRAFIC_DLG, + SfxCallMode::SYNCHRON); +} + +GraphicSizeCheckGUIResult::GraphicSizeCheckGUIResult(SwDoc* pDocument) +{ + GraphicSizeCheck aCheck(pDocument); + aCheck.check(); + + auto& rCollection = getCollection(); + for (auto& rpViolation : aCheck.getViolationList()) + { + auto rGUIEntry + = std::make_unique<GraphicSizeCheckGUIEntry>(pDocument, std::move(rpViolation)); + rCollection.push_back(std::move(rGUIEntry)); + } +} + +OUString GraphicSizeCheckGUIResult::getTitle() +{ + return SwResId(STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE); +} + +} // end sw namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/inc/GraphicSizeCheck.hxx b/sw/source/core/inc/GraphicSizeCheck.hxx new file mode 100644 index 000000000000..f708b0a5f4dc --- /dev/null +++ b/sw/source/core/inc/GraphicSizeCheck.hxx @@ -0,0 +1,99 @@ +/* -*- 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/. + * + */ + +#pragma once + +#include <doc.hxx> +#include "ModelTraverser.hxx" +#include <svx/GenericCheckDialog.hxx> + +namespace sw +{ +class GraphicSizeViolation final +{ +private: + const SwGrfNode* m_pGraphicNode; + + sal_Int32 m_nLowDPILimit = 0; + sal_Int32 m_nHighDPILimit = 0; + + sal_Int32 m_nDPIX = 0; + sal_Int32 m_nDPIY = 0; + +public: + GraphicSizeViolation(sal_Int32 nDPI, const SwGrfNode* pGraphicNode); + bool check(); + + OUString getGraphicName(); + + bool isDPITooLow() { return m_nDPIX < m_nLowDPILimit || m_nDPIY < m_nLowDPILimit; } + + bool isDPITooHigh() { return m_nDPIX > m_nHighDPILimit || m_nDPIY > m_nHighDPILimit; } + + sal_Int32 getDPIX() { return m_nDPIX; } + + sal_Int32 getDPIY() { return m_nDPIY; } +}; + +class GraphicSizeCheck final +{ +private: + SwDoc* m_pDocument; + std::vector<std::unique_ptr<GraphicSizeViolation>> m_aGraphicSizeViolationList; + +public: + GraphicSizeCheck(SwDoc* pDocument) + : m_pDocument(pDocument) + { + } + + void check(); + + std::vector<std::unique_ptr<GraphicSizeViolation>>& getViolationList() + { + return m_aGraphicSizeViolationList; + } +}; + +class GraphicSizeCheckGUIEntry : public svx::CheckData +{ +private: + SwDoc* m_pDocument; + std::unique_ptr<GraphicSizeViolation> m_pViolation; + +public: + GraphicSizeCheckGUIEntry(SwDoc* pDocument, std::unique_ptr<GraphicSizeViolation>&& pViolation) + : m_pDocument(pDocument) + , m_pViolation(std::move(pViolation)) + { + } + + OUString getText() override; + + bool canMarkObject() override { return true; } + + void markObject() override; + + bool hasProperties() override { return true; } + + void runProperties() override; +}; + +class GraphicSizeCheckGUIResult : public svx::CheckDataCollection +{ +public: + GraphicSizeCheckGUIResult(SwDoc* pDocument); + + OUString getTitle() override; +}; + +} // end sw namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/shells/basesh.cxx b/sw/source/uibase/shells/basesh.cxx index 05d631bd7b3e..5b4911ff70ad 100644 --- a/sw/source/uibase/shells/basesh.cxx +++ b/sw/source/uibase/shells/basesh.cxx @@ -85,6 +85,7 @@ #include <modcfg.hxx> #include <svx/fmshell.hxx> #include <SwRewriter.hxx> +#include <GraphicSizeCheck.hxx> #include <svx/galleryitem.hxx> #include <sfx2/devtools/DevelopmentToolChildWindow.hxx> #include <com/sun/star/gallery/GalleryItemType.hpp> @@ -1963,6 +1964,13 @@ void SwBaseShell::GetState( SfxItemSet &rSet ) else rSet.Put( SfxVisibilityItem( nWhich, false ) ); break; + case SID_GRAPHIC_SIZE_CHECK: + { + sal_Int32 nDPI = rSh.GetDoc()->getIDocumentSettingAccess().getImagePreferredDPI(); + if (nDPI <= 0) + rSet.DisableItem(nWhich); + } + break; } nWhich = aIter.NextWhich(); } @@ -2785,6 +2793,15 @@ void SwBaseShell::ExecDlg(SfxRequest &rReq) aDialog.run(); } break; + + case SID_GRAPHIC_SIZE_CHECK: + { + sw::GraphicSizeCheckGUIResult aResult(rSh.GetDoc()); + svx::GenericCheckDialog aDialog(pMDI, aResult); + aDialog.run(); + } + break; + default:OSL_FAIL("wrong Dispatcher (basesh.cxx)"); } if(!bDone) diff --git a/sw/uiconfig/sglobal/menubar/menubar.xml b/sw/uiconfig/sglobal/menubar/menubar.xml index b8dafe715afe..852877152a3c 100644 --- a/sw/uiconfig/sglobal/menubar/menubar.xml +++ b/sw/uiconfig/sglobal/menubar/menubar.xml @@ -479,6 +479,7 @@ <menu:menuitem menu:id=".uno:ColorSettings"/> <menu:menuseparator/> <menu:menuitem menu:id=".uno:GraphicDialog"/> + <menu:menuitem menu:id=".uno:GraphicSizeCheck"/> </menu:menupopup> </menu:menu> <menu:menu menu:id=".uno:FormatObjectMenu"> diff --git a/sw/uiconfig/swriter/menubar/menubar.xml b/sw/uiconfig/swriter/menubar/menubar.xml index e27ca8aa3cad..2fb1d51d8cc6 100644 --- a/sw/uiconfig/swriter/menubar/menubar.xml +++ b/sw/uiconfig/swriter/menubar/menubar.xml @@ -490,6 +490,7 @@ <menu:menuitem menu:id=".uno:ColorSettings"/> <menu:menuseparator/> <menu:menuitem menu:id=".uno:GraphicDialog"/> + <menu:menuitem menu:id=".uno:GraphicSizeCheck"/> </menu:menupopup> </menu:menu> <menu:menu menu:id=".uno:FormatObjectMenu"> |