diff options
-rw-r--r-- | cui/AllLangResTarget_cui.mk | 1 | ||||
-rw-r--r-- | cui/Library_cui.mk | 1 | ||||
-rw-r--r-- | cui/UIConfig_cui.mk | 1 | ||||
-rw-r--r-- | cui/source/dialogs/screenshotannotationdlg.cxx | 572 | ||||
-rw-r--r-- | cui/source/dialogs/screenshotannotationdlg.src | 27 | ||||
-rw-r--r-- | cui/source/factory/cuiexp.cxx | 1 | ||||
-rw-r--r-- | cui/source/factory/dlgfact.cxx | 9 | ||||
-rw-r--r-- | cui/source/factory/dlgfact.hxx | 10 | ||||
-rw-r--r-- | cui/source/inc/cuires.hrc | 1 | ||||
-rw-r--r-- | cui/source/inc/screenshotannotationdlg.hxx | 44 | ||||
-rw-r--r-- | cui/uiconfig/ui/screenshotannotationdialog.ui (renamed from vcl/uiconfig/ui/screenshotannotationdialog.ui) | 0 | ||||
-rw-r--r-- | include/vcl/abstdlg.hxx | 9 | ||||
-rw-r--r-- | vcl/UIConfig_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/source/window/layout.cxx | 470 |
14 files changed, 688 insertions, 459 deletions
diff --git a/cui/AllLangResTarget_cui.mk b/cui/AllLangResTarget_cui.mk index 7cb854c0690a..b9d2700d1229 100644 --- a/cui/AllLangResTarget_cui.mk +++ b/cui/AllLangResTarget_cui.mk @@ -32,6 +32,7 @@ $(eval $(call gb_SrsTarget_add_files,cui/res,\ cui/source/dialogs/hyperdlg.src \ cui/source/dialogs/multipat.src \ cui/source/dialogs/passwdomdlg.src \ + cui/source/dialogs/screenshotannotationdlg.src \ cui/source/dialogs/scriptdlg.src \ cui/source/dialogs/svuidlg.src \ cui/source/options/connpooloptions.src \ diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk index cc3b3144d76f..ea46629fbfed 100644 --- a/cui/Library_cui.mk +++ b/cui/Library_cui.mk @@ -115,6 +115,7 @@ $(eval $(call gb_Library_add_exception_objects,cui,\ cui/source/dialogs/multipat \ cui/source/dialogs/newtabledlg \ cui/source/dialogs/passwdomdlg \ + cui/source/dialogs/screenshotannotationdlg \ cui/source/dialogs/pastedlg \ cui/source/dialogs/postdlg \ cui/source/dialogs/scriptdlg \ diff --git a/cui/UIConfig_cui.mk b/cui/UIConfig_cui.mk index df494ceac71b..19c387144c09 100644 --- a/cui/UIConfig_cui.mk +++ b/cui/UIConfig_cui.mk @@ -144,6 +144,7 @@ $(eval $(call gb_UIConfig_add_uifiles,cui,\ cui/uiconfig/ui/paraindentspacing \ cui/uiconfig/ui/paratabspage \ cui/uiconfig/ui/password \ + cui/uiconfig/ui/screenshotannotationdialog \ cui/uiconfig/ui/pastespecial \ cui/uiconfig/ui/patterntabpage \ cui/uiconfig/ui/percentdialog \ diff --git a/cui/source/dialogs/screenshotannotationdlg.cxx b/cui/source/dialogs/screenshotannotationdlg.cxx new file mode 100644 index 000000000000..aad891f48aa6 --- /dev/null +++ b/cui/source/dialogs/screenshotannotationdlg.cxx @@ -0,0 +1,572 @@ +/* -*- 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 "screenshotannotationdlg.hxx" + +#include "cuires.hrc" +#include "dialmgr.hxx" + +#include <basegfx/range/b2irange.hxx> +#include <cppuhelper/bootstrap.hxx> +#include <com/sun/star/ui/dialogs/FilePicker.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <vcl/pngwrite.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> + +using namespace com::sun::star; + +class ControlDataEntry +{ +public: + ControlDataEntry( + const vcl::Window& rControl, + const basegfx::B2IRange& rB2IRange) + : mrControl(rControl), + maB2IRange(rB2IRange) + { + } + + const vcl::Window& getControl() const + { + return mrControl; + } + + const basegfx::B2IRange& getB2IRange() const + { + return maB2IRange; + } + +private: + const vcl::Window& mrControl; + basegfx::B2IRange maB2IRange; +}; + +typedef ::std::vector< ControlDataEntry > ControlDataCollection; +typedef ::std::set< ControlDataEntry* > ControlDataSet; + +class ScreenshotAnnotationDlg_Impl // : public ModalDialog +{ +public: + ScreenshotAnnotationDlg_Impl( + ScreenshotAnnotationDlg& rParent, + Dialog& rParentDialog); + ~ScreenshotAnnotationDlg_Impl(); + +private: + // Handler for click on save + DECL_LINK_TYPED(saveButtonHandler, Button*, void); + + // Handler for clicks on picture frame + DECL_LINK_TYPED(pictureFrameListener, VclWindowEvent&, void); + + // helper methods + void CollectChildren( + const vcl::Window& rCurrent, + const basegfx::B2IPoint& rTopLeft, + ControlDataCollection& rControlDataCollection); + ControlDataEntry* CheckHit(const basegfx::B2IPoint& rPosition); + void PaintControlDataEntry( + const ControlDataEntry& rEntry, + const Color& rColor, + double fLineWidth, + double fTransparency = 0.0); + void RepaintToBuffer( + bool bUseDimmed = false, + bool bPaintHilight = false); + void RepaintPictureElement(); + Point GetOffsetInPicture() const; + + // local variables + ScreenshotAnnotationDlg& mrParent; + Dialog& mrParentDialog; + Bitmap maParentDialogBitmap; + Bitmap maDimmedDialogBitmap; + Size maParentDialogSize; + + // VirtualDevice for buffered interation paints + VclPtr<VirtualDevice> mpVirtualBufferDevice; + + // all detected children + ControlDataCollection maAllChildren; + + // hilighted/selected children + ControlDataEntry* mpHilighted; + ControlDataSet maSelected; + + // list of detected controls + VclPtr<FixedImage> mpPicture; + VclPtr<VclMultiLineEdit> mpText; + VclPtr<PushButton> mpSave; + + // save as text + OUString maSaveAsText; + + // folder URL + static OUString maLastFolderURL; +}; + +OUString ScreenshotAnnotationDlg_Impl::maLastFolderURL = OUString(); + +ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl( + ScreenshotAnnotationDlg& rParent, + Dialog& rParentDialog) +: mrParent(rParent), + mrParentDialog(rParentDialog), + maParentDialogBitmap(rParentDialog.createScreenshot()), + maDimmedDialogBitmap(maParentDialogBitmap), + maParentDialogSize(maParentDialogBitmap.GetSizePixel()), + mpVirtualBufferDevice(nullptr), + maAllChildren(), + mpHilighted(nullptr), + maSelected(), + mpPicture(nullptr), + mpText(nullptr), + mpSave(nullptr), + maSaveAsText(CUI_RES(RID_SVXSTR_SAVE_SCREENSHOT_AS)) +{ + // image ain't empty + assert(!maParentDialogBitmap.IsEmpty()); + assert(0 != maParentDialogBitmap.GetSizePixel().Width()); + assert(0 != maParentDialogBitmap.GetSizePixel().Height()); + + // get needed widgets + mrParent.get(mpPicture, "picture"); + assert(mpPicture.get()); + mrParent.get(mpText, "text"); + assert(mpText.get()); + mrParent.get(mpSave, "save"); + assert(mpSave.get()); + + // set screenshot image at FixedImage, resize, set event listener + if (mpPicture) + { + // colelct all children. Choose start pos to be negative + // of target dialog's position to get all positions relative to (0,0) + const Point aParentPos(mrParentDialog.GetPosPixel()); + const basegfx::B2IPoint aTopLeft(-aParentPos.X(), -aParentPos.Y()); + + CollectChildren( + mrParentDialog, + aTopLeft, + maAllChildren); + + // to make clear that maParentDialogBitmap is a background image, adjust + // luminance a bit for maDimmedDialogBitmap - other methods may be applied + maDimmedDialogBitmap.Adjust(-15); + + // init paint buffering VirtualDevice + mpVirtualBufferDevice = new VirtualDevice(*Application::GetDefaultDevice(), DeviceFormat::DEFAULT, DeviceFormat::BITMASK); + mpVirtualBufferDevice->SetOutputSizePixel(maParentDialogSize); + mpVirtualBufferDevice->SetFillColor(COL_TRANSPARENT); + + // initially set image for picture control + mpPicture->SetImage(Image(maDimmedDialogBitmap)); + + // set size for picture control, this will re-layout so that + // the picture control shows the whole dialog + mpPicture->set_width_request(maParentDialogSize.Width()); + mpPicture->set_height_request(maParentDialogSize.Height()); + + // add local event listener to allow interactions with mouse + mpPicture->AddEventListener(LINK(this, ScreenshotAnnotationDlg_Impl, pictureFrameListener)); + + // avoid image scaling, this is needed for images smaller than the + // minimal dialog size + const WinBits aWinBits(mpPicture->GetStyle()); + mpPicture->SetStyle(aWinBits & (!WinBits(WB_SCALE))); + } + + // set some test text at VclMultiLineEdit and make read-only - only + // copying content to clipboard is allowed + if (mpText) + { + mpText->SetText("The quick brown fox jumps over the lazy dog :)"); + mpText->SetReadOnly(true); + } + + // set click handler for save button + if (mpSave) + { + mpSave->SetClickHdl(LINK(this, ScreenshotAnnotationDlg_Impl, saveButtonHandler)); + } +} + +void ScreenshotAnnotationDlg_Impl::CollectChildren( + const vcl::Window& rCurrent, + const basegfx::B2IPoint& rTopLeft, + ControlDataCollection& rControlDataCollection) +{ + if (rCurrent.IsVisible()) + { + const Point aCurrentPos(rCurrent.GetPosPixel()); + const Size aCurrentSize(rCurrent.GetSizePixel()); + const basegfx::B2IPoint aCurrentTopLeft(rTopLeft.getX() + aCurrentPos.X(), rTopLeft.getY() + aCurrentPos.Y()); + const basegfx::B2IRange aCurrentRange(aCurrentTopLeft, aCurrentTopLeft + basegfx::B2IPoint(aCurrentSize.Width(), aCurrentSize.Height())); + + if (!aCurrentRange.isEmpty()) + { + rControlDataCollection.push_back( + ControlDataEntry( + rCurrent, + aCurrentRange)); + } + + for (sal_uInt16 a(0); a < rCurrent.GetChildCount(); a++) + { + vcl::Window* pChild = rCurrent.GetChild(a); + + if (nullptr != pChild) + { + CollectChildren(*pChild, aCurrentTopLeft, rControlDataCollection); + } + } + } +} + +ScreenshotAnnotationDlg_Impl::~ScreenshotAnnotationDlg_Impl() +{ + mpVirtualBufferDevice.disposeAndClear(); +} + +IMPL_LINK_TYPED(ScreenshotAnnotationDlg_Impl, saveButtonHandler, Button*, pButton, void) +{ + // suppress compiler warning + (*pButton); + + // 'save screenshot...' pressed, offer to save maParentDialogBitmap + // as PNG image, use *.id file name as screenshot file name offering + OString aDerivedFileName; + + // get a suggestion for the filename from ui file name + { + const OString& rUIFileName = mrParentDialog.getUIFile(); + sal_Int32 nIndex(0); + + do + { + const OString aToken(rUIFileName.getToken(0, '/', nIndex)); + + if (!aToken.isEmpty()) + { + aDerivedFileName = aToken; + } + } while (nIndex >= 0); + } + + uno::Reference< uno::XComponentContext > xContext = cppu::defaultBootstrap_InitialComponentContext(); + const uno::Reference< ui::dialogs::XFilePicker3 > xFilePicker = + ui::dialogs::FilePicker::createWithMode(xContext, ui::dialogs::TemplateDescription::FILESAVE_SIMPLE); + + xFilePicker->setTitle(maSaveAsText); + + if (!maLastFolderURL.isEmpty()) + { + xFilePicker->setDisplayDirectory(maLastFolderURL); + } + + xFilePicker->appendFilter("*.png", "*.PNG"); + xFilePicker->setCurrentFilter("*.png"); + xFilePicker->setDefaultName(OStringToOUString(aDerivedFileName, RTL_TEXTENCODING_UTF8)); // +".png"); + + if (xFilePicker->execute() == ui::dialogs::ExecutableDialogResults::OK) + { + maLastFolderURL = xFilePicker->getDisplayDirectory(); + const uno::Sequence< OUString > files(xFilePicker->getSelectedFiles()); + + if (files.getLength()) + { + OUString aConfirmedName = files[0]; + + if (!aConfirmedName.isEmpty()) + { + INetURLObject aConfirmedURL(aConfirmedName); + OUString aCurrentExtension(aConfirmedURL.getExtension()); + + if (!aCurrentExtension.isEmpty() && 0 != aCurrentExtension.compareTo("png")) + { + aConfirmedURL.removeExtension(); + aCurrentExtension.clear(); + } + + if (aCurrentExtension.isEmpty()) + { + aConfirmedURL.setExtension("png"); + } + + // open stream + SvFileStream aNew(aConfirmedURL.PathToFileName(), StreamMode::WRITE | StreamMode::TRUNC); + + if (aNew.IsOpen()) + { + // prepare bitmap to save - do use the original screenshot here, + // not the dimmed one + RepaintToBuffer(); + + // extract Bitmap + const Bitmap aTargetBitmap( + mpVirtualBufferDevice->GetBitmap( + Point(0, 0), + mpVirtualBufferDevice->GetOutputSizePixel())); + + // write as PNG + vcl::PNGWriter aPNGWriter(aTargetBitmap); + aPNGWriter.Write(aNew); + } + } + } + } +} + +ControlDataEntry* ScreenshotAnnotationDlg_Impl::CheckHit(const basegfx::B2IPoint& rPosition) +{ + ControlDataEntry* pRetval = nullptr; + + for (auto aCandidate = maAllChildren.begin(); aCandidate != maAllChildren.end(); aCandidate++) + { + ControlDataEntry& rCandidate = *aCandidate; + + if (rCandidate.getB2IRange().isInside(rPosition)) + { + if (pRetval) + { + if (pRetval->getB2IRange().isInside(rCandidate.getB2IRange().getMinimum()) + && pRetval->getB2IRange().isInside(rCandidate.getB2IRange().getMaximum())) + { + pRetval = &rCandidate; + } + } + else + { + pRetval = &rCandidate; + } + } + } + + return pRetval; +} + +void ScreenshotAnnotationDlg_Impl::PaintControlDataEntry( + const ControlDataEntry& rEntry, + const Color& rColor, + double fLineWidth, + double fTransparency) +{ + if (mpPicture && mpVirtualBufferDevice) + { + const basegfx::B2IRange& rRange = rEntry.getB2IRange(); + static double fRelativeEdgeRadius(0.1); + basegfx::B2DRange aB2DRange(rRange); + + // grow one pixel to be a little bit outside + aB2DRange.grow(1); + + const basegfx::B2DPolygon aPolygon( + basegfx::tools::createPolygonFromRect( + aB2DRange, + fRelativeEdgeRadius, + fRelativeEdgeRadius)); + mpVirtualBufferDevice->SetLineColor(rColor); + + if (!mpVirtualBufferDevice->DrawPolyLineDirect( + aPolygon, + fLineWidth, + fTransparency, + basegfx::B2DLineJoin::Round)) + { + mpVirtualBufferDevice->DrawPolyLine( + aPolygon, + fLineWidth, + basegfx::B2DLineJoin::Round); + } + } +} + +Point ScreenshotAnnotationDlg_Impl::GetOffsetInPicture() const +{ + if (!mpPicture) + { + return Point(0, 0); + } + + const Size aPixelSizeTarget(mpPicture->GetOutputSizePixel()); + + return Point( + aPixelSizeTarget.Width() > maParentDialogSize.Width() ? (aPixelSizeTarget.Width() - maParentDialogSize.Width()) >> 1 : 0, + aPixelSizeTarget.Height() > maParentDialogSize.Height() ? (aPixelSizeTarget.Height() - maParentDialogSize.Height()) >> 1 : 0); +} + +void ScreenshotAnnotationDlg_Impl::RepaintToBuffer( + bool bUseDimmed, + bool bPaintHilight) +{ + if (mpVirtualBufferDevice) + { + // reset with original screenshot bitmap + mpVirtualBufferDevice->DrawBitmap( + Point(0, 0), + bUseDimmed ? maDimmedDialogBitmap : maParentDialogBitmap); + + // get various options - sorry, no SvtOptionsDrawinglayer in vcl + const Color aHilightColor(Application::GetSettings().GetStyleSettings().GetHighlightColor()); + const bool bIsAntiAliasing(true); + const double fTransparence(0.4); + const AntialiasingFlags nOldAA(mpVirtualBufferDevice->GetAntialiasing()); + + if (bIsAntiAliasing) + { + mpVirtualBufferDevice->SetAntialiasing(AntialiasingFlags::EnableB2dDraw); + } + + // paint selected entries + for (auto candidate = maSelected.begin(); candidate != maSelected.end(); candidate++) + { + PaintControlDataEntry(**candidate, Color(COL_LIGHTRED), 3.0); + } + + // paint hilighted entry + if (mpHilighted && bPaintHilight) + { + PaintControlDataEntry(*mpHilighted, aHilightColor, 5.0, fTransparence); + } + + if (bIsAntiAliasing) + { + mpVirtualBufferDevice->SetAntialiasing(nOldAA); + } + } +} + +void ScreenshotAnnotationDlg_Impl::RepaintPictureElement() +{ + if (mpPicture && mpVirtualBufferDevice) + { + // reset image in buffer, use dimmed version and allow hilight + RepaintToBuffer(true, true); + + // copy new content to picture control (hard paint) + mpPicture->DrawOutDev( + GetOffsetInPicture(), + maParentDialogSize, + Point(0, 0), + maParentDialogSize, + *mpVirtualBufferDevice); + + // also set image to get repaints right, but trigger no repaint + mpPicture->SetImage( + Image( + mpVirtualBufferDevice->GetBitmap( + Point(0, 0), + mpVirtualBufferDevice->GetOutputSizePixel()))); + mpPicture->Validate(); + } +} + +IMPL_LINK_TYPED(ScreenshotAnnotationDlg_Impl, pictureFrameListener, VclWindowEvent&, rEvent, void) +{ + // event in picture frame + bool bRepaint(false); + + switch (rEvent.GetId()) + { + case VCLEVENT_WINDOW_MOUSEMOVE: + case VCLEVENT_WINDOW_MOUSEBUTTONUP: + { + MouseEvent* pMouseEvent = static_cast< MouseEvent* >(rEvent.GetData()); + + if (pMouseEvent) + { + switch (rEvent.GetId()) + { + case VCLEVENT_WINDOW_MOUSEMOVE: + { + if (mpPicture->IsMouseOver()) + { + const ControlDataEntry* pOldHit = mpHilighted; + const Point aOffset(GetOffsetInPicture()); + const basegfx::B2IPoint aMousePos( + pMouseEvent->GetPosPixel().X() - aOffset.X(), + pMouseEvent->GetPosPixel().Y() - aOffset.Y()); + const ControlDataEntry* pHit = CheckHit(aMousePos); + + if (pHit && pOldHit != pHit) + { + mpHilighted = const_cast< ControlDataEntry* >(pHit); + bRepaint = true; + } + } + else if (mpHilighted) + { + mpHilighted = nullptr; + bRepaint = true; + } + break; + } + case VCLEVENT_WINDOW_MOUSEBUTTONUP: + { + if (mpPicture->IsMouseOver() && mpHilighted) + { + if (maSelected.find(mpHilighted) != maSelected.end()) + { + maSelected.erase(mpHilighted); + } + else + { + maSelected.insert(mpHilighted); + } + + bRepaint = true; + } + break; + } + default: + { + break; + } + } + } + break; + } + default: + { + break; + } + } + + if (bRepaint) + { + RepaintPictureElement(); + } +} + +ScreenshotAnnotationDlg::ScreenshotAnnotationDlg( + vcl::Window* pParent, + Dialog& rParentDialog) +: SfxModalDialog(pParent, "ScreenshotAnnotationDialog", "cui/ui/screenshotannotationdialog.ui") +{ + m_pImpl.reset(new ScreenshotAnnotationDlg_Impl(*this, rParentDialog)); +} + + +ScreenshotAnnotationDlg::~ScreenshotAnnotationDlg() +{ + disposeOnce(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/dialogs/screenshotannotationdlg.src b/cui/source/dialogs/screenshotannotationdlg.src new file mode 100644 index 000000000000..62d0ac2ec89e --- /dev/null +++ b/cui/source/dialogs/screenshotannotationdlg.src @@ -0,0 +1,27 @@ +/* -*- 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 <cuires.hrc> + +String RID_SVXSTR_SAVE_SCREENSHOT_AS +{ + Text [ en-US ] = "Save Screenshot As..."; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/factory/cuiexp.cxx b/cui/source/factory/cuiexp.cxx index 9ac7c9a61b30..ac53518218e5 100644 --- a/cui/source/factory/cuiexp.cxx +++ b/cui/source/factory/cuiexp.cxx @@ -37,6 +37,7 @@ #include "linkdlg.hxx" #include "postdlg.hxx" #include "passwdomdlg.hxx" +#include "screenshotannotationdlg.hxx" #include "cuihyperdlg.hxx" #include "selector.hxx" #include "SpellDialog.hxx" diff --git a/cui/source/factory/dlgfact.cxx b/cui/source/factory/dlgfact.cxx index 39ec6d2ab749..f45c32eeddc3 100644 --- a/cui/source/factory/dlgfact.cxx +++ b/cui/source/factory/dlgfact.cxx @@ -82,6 +82,7 @@ #include "acccfg.hxx" #include "insrc.hxx" #include "passwdomdlg.hxx" +#include "screenshotannotationdlg.hxx" #include "hyphen.hxx" #include "thesdlg.hxx" #include "about.hxx" @@ -133,6 +134,7 @@ IMPL_ABSTDLG_BASE(AbstractLinksDialog_Impl); IMPL_ABSTDLG_BASE(AbstractSpellDialog_Impl); IMPL_ABSTDLG_BASE(AbstractSvxPostItDialog_Impl); IMPL_ABSTDLG_BASE(AbstractPasswordToOpenModifyDialog_Impl); +IMPL_ABSTDLG_BASE(AbstractScreenshotAnnotationDlg_Impl); // VclAbstractDialog2_Impl @@ -1594,5 +1596,12 @@ AbstractPasswordToOpenModifyDialog * AbstractDialogFactory_Impl::CreatePasswordT return new AbstractPasswordToOpenModifyDialog_Impl( pDlg ); } +AbstractScreenshotAnnotationDlg* AbstractDialogFactory_Impl::CreateScreenshotAnnotationDlg( + vcl::Window * pParent, + Dialog& rParentDialog) +{ + VclPtrInstance<ScreenshotAnnotationDlg> pDlg(pParent, rParentDialog); + return new AbstractScreenshotAnnotationDlg_Impl(pDlg); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/cui/source/factory/dlgfact.hxx b/cui/source/factory/dlgfact.hxx index 4790b8054400..57355f660a95 100644 --- a/cui/source/factory/dlgfact.hxx +++ b/cui/source/factory/dlgfact.hxx @@ -444,6 +444,12 @@ class AbstractPasswordToOpenModifyDialog_Impl : public AbstractPasswordToOpenMod virtual bool IsRecommendToOpenReadonly() const override; }; +class ScreenshotAnnotationDlg; +class AbstractScreenshotAnnotationDlg_Impl : public AbstractScreenshotAnnotationDlg +{ + DECL_ABSTDLG_BASE(AbstractScreenshotAnnotationDlg_Impl, ScreenshotAnnotationDlg) +}; + //AbstractDialogFactory_Impl implementations class AbstractDialogFactory_Impl : public SvxAbstractDialogFactory { @@ -622,7 +628,9 @@ public: virtual SvxAbstractInsRowColDlg* CreateSvxInsRowColDlg( vcl::Window* pParent, bool bCol, const OString& sHelpId ) override; - virtual AbstractPasswordToOpenModifyDialog * CreatePasswordToOpenModifyDialog( vcl::Window * pParent, sal_uInt16 nMinPasswdLen, sal_uInt16 nMaxPasswdLen, bool bIsPasswordToModify ) override; + virtual AbstractPasswordToOpenModifyDialog * CreatePasswordToOpenModifyDialog(vcl::Window * pParent, sal_uInt16 nMinPasswdLen, sal_uInt16 nMaxPasswdLen, bool bIsPasswordToModify) override; + + virtual AbstractScreenshotAnnotationDlg* CreateScreenshotAnnotationDlg(vcl::Window * pParent, Dialog& rParentDialog) override; }; #endif diff --git a/cui/source/inc/cuires.hrc b/cui/source/inc/cuires.hrc index 8b452f216af6..efe0d202c0dc 100644 --- a/cui/source/inc/cuires.hrc +++ b/cui/source/inc/cuires.hrc @@ -418,6 +418,7 @@ #define RID_SVXSTR_LOAD_ERROR (RID_SVX_START + 1265) #define RID_SVXSTR_EDITHINT (RID_SVX_START + 1266) +#define RID_SVXSTR_SAVE_SCREENSHOT_AS (RID_SVX_START + 1267) // Hangul/Hanja Dialog #define RID_SVXSTR_HANGUL (RID_SVX_START + 1270) diff --git a/cui/source/inc/screenshotannotationdlg.hxx b/cui/source/inc/screenshotannotationdlg.hxx new file mode 100644 index 000000000000..f2412cec9a05 --- /dev/null +++ b/cui/source/inc/screenshotannotationdlg.hxx @@ -0,0 +1,44 @@ +/* -*- 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_CUI_SOURCE_INC_SCREENSHANNDLG_HXX +#define INCLUDED_CUI_SOURCE_INC_SCREENSHANNDLG_HXX + +#include <sfx2/basedlgs.hxx> +#include <memory> + +class ScreenshotAnnotationDlg_Impl; + +class ScreenshotAnnotationDlg : public SfxModalDialog +{ +private: + std::unique_ptr< ScreenshotAnnotationDlg_Impl > m_pImpl; + + ScreenshotAnnotationDlg(const ScreenshotAnnotationDlg &) = delete; + ScreenshotAnnotationDlg& operator=(const ScreenshotAnnotationDlg &) = delete; + +public: + ScreenshotAnnotationDlg( + vcl::Window* pParent, + Dialog& rParentDialog); + virtual ~ScreenshotAnnotationDlg(); +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/uiconfig/ui/screenshotannotationdialog.ui b/cui/uiconfig/ui/screenshotannotationdialog.ui index 6ab343679118..6ab343679118 100644 --- a/vcl/uiconfig/ui/screenshotannotationdialog.ui +++ b/cui/uiconfig/ui/screenshotannotationdialog.ui diff --git a/include/vcl/abstdlg.hxx b/include/vcl/abstdlg.hxx index c18f1188e236..d0638076523c 100644 --- a/include/vcl/abstdlg.hxx +++ b/include/vcl/abstdlg.hxx @@ -71,6 +71,10 @@ public: virtual bool IsRecommendToOpenReadonly() const = 0; }; +class VCL_DLLPUBLIC AbstractScreenshotAnnotationDlg : public VclAbstractDialog +{ +}; + class VCL_DLLPUBLIC VclAbstractDialogFactory { public: @@ -82,6 +86,11 @@ public: // creates instance of PasswordToOpenModifyDialog from cui virtual AbstractPasswordToOpenModifyDialog* CreatePasswordToOpenModifyDialog( vcl::Window * pParent, sal_uInt16 nMinPasswdLen, sal_uInt16 nMaxPasswdLen, bool bIsPasswordToModify ) = 0; + + // creates instance of ScreenshotAnnotationDlg from cui + virtual AbstractScreenshotAnnotationDlg* CreateScreenshotAnnotationDlg( + vcl::Window* pParent, + Dialog& rParentDialog) = 0; }; #endif diff --git a/vcl/UIConfig_vcl.mk b/vcl/UIConfig_vcl.mk index 888737ff82e1..a81b30ed497a 100644 --- a/vcl/UIConfig_vcl.mk +++ b/vcl/UIConfig_vcl.mk @@ -19,7 +19,6 @@ $(eval $(call gb_UIConfig_add_uifiles,vcl,\ vcl/uiconfig/ui/printerpropertiesdialog \ vcl/uiconfig/ui/printprogressdialog \ vcl/uiconfig/ui/querydialog \ - vcl/uiconfig/ui/screenshotannotationdialog \ )) # vim: set noet sw=4 ts=4: diff --git a/vcl/source/window/layout.cxx b/vcl/source/window/layout.cxx index 79d4efd89d09..bc548ec283d3 100644 --- a/vcl/source/window/layout.cxx +++ b/vcl/source/window/layout.cxx @@ -19,6 +19,7 @@ #include "window.h" #include <boost/multi_array.hpp> #include <officecfg/Office/Common.hxx> +#include <vcl/abstdlg.hxx> VclContainer::VclContainer(vcl::Window *pParent, WinBits nStyle) : Window(WINDOW_CONTAINER) @@ -169,448 +170,6 @@ void VclContainer::queue_resize(StateChangedType eReason) Window::queue_resize(eReason); } -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -#include <comphelper/random.hxx> -#include <basegfx/range/b2irange.hxx> - -class ControlDataEntry -{ -public: - ControlDataEntry( - const vcl::Window& rControl, - const basegfx::B2IRange& rB2IRange) - : mrControl(rControl), - maB2IRange(rB2IRange) - { - } - - const vcl::Window& getControl() const - { - return mrControl; - } - - const basegfx::B2IRange& getB2IRange() const - { - return maB2IRange; - } - -private: - const vcl::Window& mrControl; - basegfx::B2IRange maB2IRange; -}; - -typedef ::std::vector< ControlDataEntry > ControlDataCollection; -typedef ::std::set< ControlDataEntry* > ControlDataSet; - -class ScreenshotAnnotationDlg : public ModalDialog -{ -public: - ScreenshotAnnotationDlg( - vcl::Window* pParent, - Dialog& rParentDialog); - virtual ~ScreenshotAnnotationDlg(); - virtual void dispose() override; - -private: - // Handler for click on save - DECL_LINK_TYPED(saveButtonHandler, Button*, void); - - // Handler for clicks on picture frame - DECL_LINK_TYPED(pictureFrameListener, VclWindowEvent&, void); - - // helper methods - void CollectChildren( - const vcl::Window& rCurrent, - const basegfx::B2IPoint& rTopLeft, - ControlDataCollection& rControlDataCollection); - ControlDataEntry* CheckHit(const basegfx::B2IPoint& rPosition); - void PaintControlDataEntry( - const ControlDataEntry& rEntry, - const Color& rColor, - double fLineWidth, - double fTransparency = 0.0); - void RepaintPictureElement(); - Point GetOffsetInPicture() const; - - // local variables - Dialog& mrParentDialog; - Bitmap maParentDialogBitmap; - Size maParentDialogSize; - - // VirtualDevice for buffered interation paints - VclPtr<VirtualDevice> mpVirtualBufferDevice; - - // all detected children - ControlDataCollection maAllChildren; - - // hilighted/selected children - ControlDataEntry* mpHilighted; - ControlDataSet maSelected; - - // list of detected controls - VclPtr<FixedImage> mpPicture; - VclPtr<VclMultiLineEdit> mpText; - VclPtr<PushButton> mpSave; -}; - -void ScreenshotAnnotationDlg::CollectChildren( - const vcl::Window& rCurrent, - const basegfx::B2IPoint& rTopLeft, - ControlDataCollection& rControlDataCollection) -{ - if (rCurrent.IsVisible()) - { - const Point aCurrentPos(rCurrent.GetPosPixel()); - const Size aCurrentSize(rCurrent.GetSizePixel()); - const basegfx::B2IPoint aCurrentTopLeft(rTopLeft.getX() + aCurrentPos.X(), rTopLeft.getY() + aCurrentPos.Y()); - const basegfx::B2IRange aCurrentRange(aCurrentTopLeft, aCurrentTopLeft + basegfx::B2IPoint(aCurrentSize.Width(), aCurrentSize.Height())); - - if (!aCurrentRange.isEmpty()) - { - rControlDataCollection.push_back( - ControlDataEntry( - rCurrent, - aCurrentRange)); - } - - for (sal_uInt16 a(0); a < rCurrent.GetChildCount(); a++) - { - vcl::Window* pChild = rCurrent.GetChild(a); - - if (nullptr != pChild) - { - CollectChildren(*pChild, aCurrentTopLeft, rControlDataCollection); - } - } - } -} - -ScreenshotAnnotationDlg::ScreenshotAnnotationDlg( - vcl::Window* pParent, - Dialog& rParentDialog) - : ModalDialog(pParent, "ScreenshotAnnotationDialog", "vcl/ui/screenshotannotationdialog.ui"), - mrParentDialog(rParentDialog), - maParentDialogBitmap(rParentDialog.createScreenshot()), - maParentDialogSize(maParentDialogBitmap.GetSizePixel()), - mpVirtualBufferDevice(nullptr), - maAllChildren(), - mpHilighted(nullptr), - maSelected(), - mpPicture(nullptr), - mpText(nullptr), - mpSave(nullptr) -{ - // image ain't empty - assert(!maParentDialogBitmap.IsEmpty()); - assert(0 != maParentDialogBitmap.GetSizePixel().Width()); - assert(0 != maParentDialogBitmap.GetSizePixel().Height()); - - // get needed widgets - get(mpPicture, "picture"); - assert(mpPicture.get()); - get(mpText, "text"); - assert(mpText.get()); - get(mpSave, "save"); - assert(mpSave.get()); - - // set screenshot image at FixedImage, resize, set event listener - if (mpPicture) - { - // colelct all children. Choose start pos to be negative - // of target dialog's position to get all positions relative to (0,0) - const Point aParentPos(rParentDialog.GetPosPixel()); - const basegfx::B2IPoint aTopLeft(-aParentPos.X(), -aParentPos.Y()); - - CollectChildren( - rParentDialog, - aTopLeft, - maAllChildren); - - // to make clear that maParentDialogBitmap is a background image, adjust - // luminance a bit - other methods may be applied - maParentDialogBitmap.Adjust(-15); - - // init paint buffering VuirtualDevice - mpVirtualBufferDevice = new VirtualDevice(*Application::GetDefaultDevice(), DeviceFormat::DEFAULT, DeviceFormat::BITMASK); - mpVirtualBufferDevice->SetOutputSizePixel(maParentDialogSize); - mpVirtualBufferDevice->SetFillColor(COL_TRANSPARENT); - - // do paint all collected children for test purposes - static bool bTestPaint(true); - - if (bTestPaint) - { - mpVirtualBufferDevice->DrawBitmap(Point(0, 0), maParentDialogBitmap); - - for (auto aCandidate = maAllChildren.begin(); aCandidate != maAllChildren.end(); aCandidate++) - { - ControlDataEntry& rCandidate = *aCandidate; - - const basegfx::B2IRange& rB2IRange(rCandidate.getB2IRange()); - const Rectangle aRect(rB2IRange.getMinX(), rB2IRange.getMinY(), rB2IRange.getMaxX(), rB2IRange.getMaxY()); - const Color aRandomColor(comphelper::rng::uniform_uint_distribution(0, 255), comphelper::rng::uniform_uint_distribution(0, 255), comphelper::rng::uniform_uint_distribution(0, 255)); - - mpVirtualBufferDevice->SetLineColor(aRandomColor); - mpVirtualBufferDevice->DrawRect(aRect); - } - - maParentDialogBitmap = mpVirtualBufferDevice->GetBitmap(Point(0, 0), maParentDialogSize); - } - - // set image for picture control - mpPicture->SetImage(Image(maParentDialogBitmap)); - - // set size for picture control, this will re-layout so that - // the picture control shows the whole dialog - mpPicture->set_width_request(maParentDialogSize.Width()); - mpPicture->set_height_request(maParentDialogSize.Height()); - - // add local event listener to allow interactions with mouse - mpPicture->AddEventListener(LINK(this, ScreenshotAnnotationDlg, pictureFrameListener)); - - // avoid image scaling, this is needed for images smaller than the - // minimal dialog size - const WinBits aWinBits(mpPicture->GetStyle()); - mpPicture->SetStyle(aWinBits & (!WinBits(WB_SCALE))); - } - - // set some test text at VclMultiLineEdit and make read-only - only - // copying content to clipboard is allowed - if (mpText) - { - mpText->SetText("The quick brown fox jumps over the lazy dog :)"); - mpText->SetReadOnly(true); - } - - // set click handler for save button - if (mpSave) - { - mpSave->SetClickHdl(LINK(this, ScreenshotAnnotationDlg, saveButtonHandler)); - } -} - -ScreenshotAnnotationDlg::~ScreenshotAnnotationDlg() -{ - mpVirtualBufferDevice.disposeAndClear(); - disposeOnce(); -} - -void ScreenshotAnnotationDlg::dispose() -{ - ModalDialog::dispose(); -} - -IMPL_LINK_TYPED(ScreenshotAnnotationDlg, saveButtonHandler, Button*, pButton, void) -{ - // 'save screenshot...' pressed, offer to save maParentDialogBitmap - // as PNG image, use *.id file name as screenshot file name offering - const OString& rUIFileName = mrParentDialog.getUIFile(); - - - - - bool bBla = true; -} - -ControlDataEntry* ScreenshotAnnotationDlg::CheckHit(const basegfx::B2IPoint& rPosition) -{ - ControlDataEntry* pRetval = nullptr; - - for (auto aCandidate = maAllChildren.begin(); aCandidate != maAllChildren.end(); aCandidate++) - { - ControlDataEntry& rCandidate = *aCandidate; - - if (rCandidate.getB2IRange().isInside(rPosition)) - { - if (pRetval) - { - if (pRetval->getB2IRange().isInside(rCandidate.getB2IRange().getMinimum()) - && pRetval->getB2IRange().isInside(rCandidate.getB2IRange().getMaximum())) - { - pRetval = &rCandidate; - } - } - else - { - pRetval = &rCandidate; - } - } - } - - return pRetval; -} - -void ScreenshotAnnotationDlg::PaintControlDataEntry( - const ControlDataEntry& rEntry, - const Color& rColor, - double fLineWidth, - double fTransparency) -{ - if (mpPicture && mpVirtualBufferDevice) - { - const basegfx::B2IRange& rRange = rEntry.getB2IRange(); - const basegfx::B2DPolygon aPolygon(basegfx::tools::createPolygonFromRect(basegfx::B2DRange(rRange))); - mpVirtualBufferDevice->SetLineColor(rColor); - - if (!mpVirtualBufferDevice->DrawPolyLineDirect( - aPolygon, - fLineWidth, - fTransparency, - basegfx::B2DLineJoin::Round)) - { - mpVirtualBufferDevice->DrawPolyLine( - aPolygon, - fLineWidth, - basegfx::B2DLineJoin::Round); - } - } -} - -Point ScreenshotAnnotationDlg::GetOffsetInPicture() const -{ - if (!mpPicture) - { - return Point(0, 0); - } - - const Size aPixelSizeTarget(mpPicture->GetOutputSizePixel()); - - return Point( - aPixelSizeTarget.Width() > maParentDialogSize.Width() ? (aPixelSizeTarget.Width() - maParentDialogSize.Width()) >> 1 : 0, - aPixelSizeTarget.Height() > maParentDialogSize.Height() ? (aPixelSizeTarget.Height() - maParentDialogSize.Height()) >> 1 : 0); -} - -void ScreenshotAnnotationDlg::RepaintPictureElement() -{ - if (mpPicture && mpVirtualBufferDevice) - { - // restore with start bitmap - mpVirtualBufferDevice->DrawBitmap(Point(0, 0), maParentDialogBitmap); - - // get various options - sorry, no SvtOptionsDrawinglayer in vcl - const Color aHilightColor(Application::GetSettings().GetStyleSettings().GetHighlightColor()); - const bool bIsAntiAliasing(true); - const double fTransparence(0.4); - const AntialiasingFlags nOldAA(mpVirtualBufferDevice->GetAntialiasing()); - - if (bIsAntiAliasing) - { - mpVirtualBufferDevice->SetAntialiasing(AntialiasingFlags::EnableB2dDraw); - } - - // paint selected - for (auto candidate = maSelected.begin(); candidate != maSelected.end(); candidate++) - { - PaintControlDataEntry(**candidate, Color(COL_LIGHTRED), 3.0); - } - - // paint hilight - if (mpHilighted) - { - PaintControlDataEntry(*mpHilighted, aHilightColor, 5.0, fTransparence); - } - - if (bIsAntiAliasing) - { - mpVirtualBufferDevice->SetAntialiasing(nOldAA); - } - - // copy new content to picture control - mpPicture->DrawOutDev( - GetOffsetInPicture(), - maParentDialogSize, - Point(0, 0), - maParentDialogSize, - *mpVirtualBufferDevice); - - // also set image to get repaints right, but trigger no repaint - mpPicture->SetImage(Image(mpVirtualBufferDevice->GetBitmap(Point(0, 0), mpVirtualBufferDevice->GetOutputSizePixel()))); - mpPicture->Validate(); - - // const Color aRandomColor(comphelper::rng::uniform_uint_distribution(0, 255), comphelper::rng::uniform_uint_distribution(0, 255), comphelper::rng::uniform_uint_distribution(0, 255)); - // mpPicture->SetImage(Image(mpVirtualBufferDevice->GetBitmap(Point(0, 0), mpVirtualBufferDevice->GetOutputSizePixel()))); - } -} - -IMPL_LINK_TYPED(ScreenshotAnnotationDlg, pictureFrameListener, VclWindowEvent&, rEvent, void) -{ - // event in picture frame - bool bRepaint(false); - - switch (rEvent.GetId()) - { - case VCLEVENT_WINDOW_MOUSEMOVE: - // case VCLEVENT_WINDOW_MOUSEBUTTONDOWN: - case VCLEVENT_WINDOW_MOUSEBUTTONUP: - { - MouseEvent* pMouseEvent = static_cast< MouseEvent* >(rEvent.GetData()); - - if (pMouseEvent) - { - switch (rEvent.GetId()) - { - case VCLEVENT_WINDOW_MOUSEMOVE: - { - if (mpPicture->IsMouseOver()) - { - const ControlDataEntry* pOldHit = mpHilighted; - const Point aOffset(GetOffsetInPicture()); - const basegfx::B2IPoint aMousePos( - pMouseEvent->GetPosPixel().X() - aOffset.X(), - pMouseEvent->GetPosPixel().Y() - aOffset.Y()); - const ControlDataEntry* pHit = CheckHit(aMousePos); - - if (pHit && pOldHit != pHit) - { - mpHilighted = const_cast< ControlDataEntry* >(pHit); - bRepaint = true; - } - } - else if (mpHilighted) - { - mpHilighted = nullptr; - bRepaint = true; - } - break; - } - case VCLEVENT_WINDOW_MOUSEBUTTONUP: - { - if (mpPicture->IsMouseOver() && mpHilighted) - { - if (maSelected.find(mpHilighted) != maSelected.end()) - { - maSelected.erase(mpHilighted); - } - else - { - maSelected.insert(mpHilighted); - } - - bRepaint = true; - } - break; - } - default: - { - break; - } - } - } - break; - } - default: - { - break; - } - } - - if (bRepaint) - { - RepaintPictureElement(); - } -} Button* isVisibleButtonWithText(vcl::Window* pCandidate) { @@ -672,7 +231,7 @@ void VclContainer::Command(const CommandEvent& rCEvt) aMenu->InsertItem( nLocalID, pChild->GetText(), - MenuItemBits::NONE); // MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK); + MenuItemBits::NONE); aMenu->SetHelpText( nLocalID, pChild->GetHelpText()); @@ -696,7 +255,7 @@ void VclContainer::Command(const CommandEvent& rCEvt) aMenu->InsertItem( nLocalID, "Screenshot", - MenuItemBits::NONE); // MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK); + MenuItemBits::NONE); aMenu->SetHelpText( nLocalID, "Go into interactive screenshot annotation mode"); @@ -742,19 +301,19 @@ void VclContainer::Command(const CommandEvent& rCEvt) if (pParentDialog) { - // open annotation work dialog - VclPtr<ScreenshotAnnotationDlg> pDlg = VclPtr<ScreenshotAnnotationDlg>::Create( + // open screenshot annotation dialog + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + AbstractScreenshotAnnotationDlg* pTmp = pFact->CreateScreenshotAnnotationDlg( Application::GetDefDialogParent(), *pParentDialog); + std::unique_ptr< AbstractScreenshotAnnotationDlg > pDialog(pTmp); - if (pDlg && pDlg->Execute() == RET_OK) + if (pDialog) { - - - - - - bool bBla2 = true; + // currently just execute the dialog, no need to do + // different things for ok/cancel. This may change later, + // for that case use 'if (pDlg->Execute() == RET_OK)' + pDialog->Execute(); } } } @@ -770,13 +329,10 @@ void VclContainer::Command(const CommandEvent& rCEvt) } } + // call parent (do not consume) Window::Command(rCEvt); } -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// - void VclBox::accumulateMaxes(const Size &rChildSize, Size &rSize) const { long nSecondaryChildDimension = getSecondaryDimension(rChildSize); |