Rebased ref, commits from common ancestor: commit c27384c1404a3339ef6a0c3606d056e8e7336844 Author: Armin Le Grand <armin.le.gr...@cib.de> Date: Fri Aug 26 16:26:34 2016 +0200
screenshot: fixup some details for screenshot dlg Edge-rounding and linewidth for selection/cursor, default filename for save dialog. Code cosmetics. Change-Id: I8a76d68f4f3070458ec4ba51a81bb046057218ab diff --git a/cui/source/dialogs/screenshotannotationdlg.cxx b/cui/source/dialogs/screenshotannotationdlg.cxx index aad891f..58a3ab5 100644 --- a/cui/source/dialogs/screenshotannotationdlg.cxx +++ b/cui/source/dialogs/screenshotannotationdlg.cxx @@ -29,6 +29,14 @@ #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> #include <vcl/pngwrite.hxx> #include <basegfx/polygon/b2dpolygontools.hxx> +#include <tools/urlobj.hxx> +#include <vcl/fixed.hxx> +#include <vcl/svapp.hxx> +#include <vcl/salgtype.hxx> +#include <vcl/virdev.hxx> +#include <vcl/vclmedit.hxx> +#include <vcl/button.hxx> +#include <svtools/optionsdrawinglayer.hxx> using namespace com::sun::star; @@ -247,8 +255,7 @@ ScreenshotAnnotationDlg_Impl::~ScreenshotAnnotationDlg_Impl() IMPL_LINK_TYPED(ScreenshotAnnotationDlg_Impl, saveButtonHandler, Button*, pButton, void) { - // suppress compiler warning - (*pButton); + (void)pButton; // 'save screenshot...' pressed, offer to save maParentDialogBitmap // as PNG image, use *.id file name as screenshot file name offering @@ -272,7 +279,7 @@ IMPL_LINK_TYPED(ScreenshotAnnotationDlg_Impl, saveButtonHandler, Button*, pButto 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); + ui::dialogs::FilePicker::createWithMode(xContext, ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION); xFilePicker->setTitle(maSaveAsText); @@ -281,9 +288,10 @@ IMPL_LINK_TYPED(ScreenshotAnnotationDlg_Impl, saveButtonHandler, Button*, pButto xFilePicker->setDisplayDirectory(maLastFolderURL); } - xFilePicker->appendFilter("*.png", "*.PNG"); + xFilePicker->appendFilter("*.png", "*.png"); xFilePicker->setCurrentFilter("*.png"); - xFilePicker->setDefaultName(OStringToOUString(aDerivedFileName, RTL_TEXTENCODING_UTF8)); // +".png"); + xFilePicker->setDefaultName(OStringToOUString(aDerivedFileName, RTL_TEXTENCODING_UTF8)); + xFilePicker->setMultiSelectionMode(sal_False); if (xFilePicker->execute() == ui::dialogs::ExecutableDialogResults::OK) { @@ -338,7 +346,7 @@ ControlDataEntry* ScreenshotAnnotationDlg_Impl::CheckHit(const basegfx::B2IPoint { ControlDataEntry* pRetval = nullptr; - for (auto aCandidate = maAllChildren.begin(); aCandidate != maAllChildren.end(); aCandidate++) + for (auto aCandidate : maAllChildren) { ControlDataEntry& rCandidate = *aCandidate; @@ -370,26 +378,34 @@ void ScreenshotAnnotationDlg_Impl::PaintControlDataEntry( { 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); - + basegfx::B2DRange aB2DRange(rEntry.getB2IRange()); + + // grow in pixels to be a little bit 'outside'. This also + // ensures that getWidth()/getHeight() ain't 0.0 (see division below) + static double fGrowTopLeft(1.5); + static double fGrowBottomRight(0.5); + aB2DRange.expand(aB2DRange.getMinimum() - basegfx::B2DPoint(fGrowTopLeft, fGrowTopLeft)); + aB2DRange.expand(aB2DRange.getMaximum() + basegfx::B2DPoint(fGrowBottomRight, fGrowBottomRight)); + + // edge rounding in pixel. Need to convert, value for + // createPolygonFromRect is relative [0.0 .. 1.0] + static double fEdgeRoundPixel(8.0); const basegfx::B2DPolygon aPolygon( basegfx::tools::createPolygonFromRect( aB2DRange, - fRelativeEdgeRadius, - fRelativeEdgeRadius)); + fEdgeRoundPixel / aB2DRange.getWidth(), + fEdgeRoundPixel / aB2DRange.getHeight())); + mpVirtualBufferDevice->SetLineColor(rColor); + // try to use transparency if (!mpVirtualBufferDevice->DrawPolyLineDirect( aPolygon, fLineWidth, fTransparency, basegfx::B2DLineJoin::Round)) { + // no transparency, draw without mpVirtualBufferDevice->DrawPolyLine( aPolygon, fLineWidth, @@ -423,10 +439,11 @@ void ScreenshotAnnotationDlg_Impl::RepaintToBuffer( 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); + // get various options + const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer; + const Color aHilightColor(aSvtOptionsDrawinglayer.getHilightColor()); + const double fTransparence(aSvtOptionsDrawinglayer.GetTransparentSelectionPercent() * 0.01); + const bool bIsAntiAliasing(aSvtOptionsDrawinglayer.IsAntiAliasing()); const AntialiasingFlags nOldAA(mpVirtualBufferDevice->GetAntialiasing()); if (bIsAntiAliasing) @@ -435,15 +452,17 @@ void ScreenshotAnnotationDlg_Impl::RepaintToBuffer( } // paint selected entries - for (auto candidate = maSelected.begin(); candidate != maSelected.end(); candidate++) + for (auto candidate : maSelected) { - PaintControlDataEntry(**candidate, Color(COL_LIGHTRED), 3.0); + static double fLineWidthEntries(5.0); + PaintControlDataEntry(**candidate, Color(COL_LIGHTRED), fLineWidthEntries, fTransparence * 0.2); } // paint hilighted entry if (mpHilighted && bPaintHilight) { - PaintControlDataEntry(*mpHilighted, aHilightColor, 5.0, fTransparence); + static double fLineWidthHilight(7.0); + PaintControlDataEntry(*mpHilighted, aHilightColor, fLineWidthHilight, fTransparence); } if (bIsAntiAliasing) commit b6d76bf469d5e356cb606ad0c5eb4bfe804d92b4 Author: Armin Le Grand <armin.le.gr...@cib.de> Date: Fri Aug 26 14:48:17 2016 +0200 screenshot: added functionality to dialog Moved code to cui, added abstraction for AbstractDialog, adapted rendering, changed selection, added better selection visualization, added save ressources and dialog, creating pure screenshot with annotation display Change-Id: I533d9f559ee17cd46105b934bcf4beef87b96168 diff --git a/cui/AllLangResTarget_cui.mk b/cui/AllLangResTarget_cui.mk index 7cb854c..b9d2700 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 cc3b314..ea46629 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 df494ce..19c3871 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 0000000..aad891f --- /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 0000000..62d0ac2 --- /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 9ac7c9a..ac53518 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 39ec6d2..f45c32e 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 4790b80..57355f6 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 8b452f2..efe0d20 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 0000000..f2412ce --- /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 similarity index 100% rename from vcl/uiconfig/ui/screenshotannotationdialog.ui rename to cui/uiconfig/ui/screenshotannotationdialog.ui diff --git a/include/vcl/abstdlg.hxx b/include/vcl/abstdlg.hxx index c18f118..d063807 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 888737f..a81b30e 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 79d4efd..bc548ec 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); commit 2bac66dbe9c38a39dcab315c5ae48968e2c29316 Author: Armin Le Grand <armin.le.gr...@cib.de> Date: Thu Aug 25 16:50:41 2016 +0200 screenshot: develop interactive screenshot dialog Implemented a first version of an interactive screenshot dialog that supports annotations, text excerpts for these and allows to create a screenshot file How to use: * enable experimental features in Tools->Options * open any random dialog * open context menu - there's now a 'Screenshot' entry * click that, screenshot dialog pops open, mouse over the grayed-out screenshot to highlight controls * click once to highlight, click a 2nd time to unselect Change-Id: I3bcd76b96ad023e11421e4fcfac866ebf4f5ff78 diff --git a/include/vcl/layout.hxx b/include/vcl/layout.hxx index eb9d94f..1715a52 100644 --- a/include/vcl/layout.hxx +++ b/include/vcl/layout.hxx @@ -53,6 +53,10 @@ protected: virtual void setAllocation(const Size &rAllocation) = 0; virtual sal_uInt16 getDefaultAccessibleRole() const override; + + // evtl. support for screenshot context menu + virtual void Command(const CommandEvent& rCEvt) override; + public: //you don't want to override these virtual Size GetOptimalSize() const override; diff --git a/l10ntools/source/gRun.sh b/l10ntools/source/gRun.sh index 8a83dc6..b235e34 100755 --- a/l10ntools/source/gRun.sh +++ b/l10ntools/source/gRun.sh @@ -728,7 +728,7 @@ ${MYCMD} --files tubes/uiconfig/ui/contacts.ui ${MYCMD} --base uui/uiconfig/ui --files authfallback.ui filterselect.ui logindialog.ui macrowarnmedium.ui masterpassworddlg.ui password.ui setmasterpassworddlg.ui simplenameclash.ui sslwarndialog.ui unknownauthdialog.ui -${MYCMD} --base vcl/uiconfig/ui --files cupspassworddialog.ui errornocontentdialog.ui errornoprinterdialog.ui notebookbar.ui printdialog.ui printerdevicepage.ui printerpaperpage.ui printerpropertiesdialog.ui printprogressdialog.ui querydialog.ui +${MYCMD} --base vcl/uiconfig/ui --files cupspassworddialog.ui errornocontentdialog.ui errornoprinterdialog.ui notebookbar.ui printdialog.ui printerdevicepage.ui printerpaperpage.ui printerpropertiesdialog.ui printprogressdialog.ui querydialog.ui screenshotannotationdialog.ui ${MYCMD} --base xmlsecurity/uiconfig/ui --files certdetails.ui certgeneral.ui certpage.ui digitalsignaturesdialog.ui macrosecuritydialog.ui securitylevelpage.ui securitytrustpage.ui selectcertificatedialog.ui viewcertdialog.ui diff --git a/vcl/UIConfig_vcl.mk b/vcl/UIConfig_vcl.mk index a81b30e..888737f 100644 --- a/vcl/UIConfig_vcl.mk +++ b/vcl/UIConfig_vcl.mk @@ -19,6 +19,7 @@ $(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 dd776a5..79d4efd 100644 --- a/vcl/source/window/layout.cxx +++ b/vcl/source/window/layout.cxx @@ -18,6 +18,7 @@ #include <vcl/settings.hxx> #include "window.h" #include <boost/multi_array.hpp> +#include <officecfg/Office/Common.hxx> VclContainer::VclContainer(vcl::Window *pParent, WinBits nStyle) : Window(WINDOW_CONTAINER) @@ -168,6 +169,614 @@ 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) +{ + if (!pCandidate) + return nullptr; + + if (!pCandidate->IsVisible()) + return nullptr; + + if (pCandidate->GetText().isEmpty()) + return nullptr; + + return dynamic_cast<Button*>(pCandidate); +} + +// evtl. support for screenshot context menu +void VclContainer::Command(const CommandEvent& rCEvt) +{ + if (rCEvt.IsMouseEvent() && CommandEventId::ContextMenu == rCEvt.GetCommand()) + { + const bool bIsExperimentalMode(officecfg::Office::Common::Misc::ExperimentalMode::get()); + + if (bIsExperimentalMode) + { + bool bVisibleChildren(false); + vcl::Window* pChild(nullptr); + + for (pChild = GetWindow(GetWindowType::FirstChild); !bVisibleChildren && pChild; pChild = pChild->GetWindow(GetWindowType::Next)) + { + Button* pCandidate = isVisibleButtonWithText(pChild); + + if (nullptr == pCandidate) + continue; + + bVisibleChildren = true; + } + + if (bVisibleChildren) + { + static bool bAddButtonsToMenu(true); + static bool bAddScreenshotButtonToMenu(true); + + if (bAddButtonsToMenu || bAddScreenshotButtonToMenu) + { + const Point aMenuPos(rCEvt.GetMousePosPixel()); + ScopedVclPtrInstance<PopupMenu> aMenu; + sal_uInt16 nLocalID(1); + sal_uInt16 nScreenshotButtonID(0); + + if (bAddButtonsToMenu) + { + for (pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) + { + Button* pCandidate = isVisibleButtonWithText(pChild); + + if (nullptr == pCandidate) + continue; + + aMenu->InsertItem( + nLocalID, + pChild->GetText(), + MenuItemBits::NONE); // MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK); + aMenu->SetHelpText( + nLocalID, + pChild->GetHelpText()); + aMenu->SetHelpId( + nLocalID, + pChild->GetHelpId()); + aMenu->EnableItem( + nLocalID, + pChild->IsEnabled()); + nLocalID++; + } + } + + if (bAddScreenshotButtonToMenu) + { + if (nLocalID > 1) + { + aMenu->InsertSeparator(); + } + + aMenu->InsertItem( + nLocalID, + "Screenshot", + MenuItemBits::NONE); // MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK); + aMenu->SetHelpText( + nLocalID, + "Go into interactive screenshot annotation mode"); + aMenu->SetHelpId( + nLocalID, + "InteractiveScreenshotMode"); + aMenu->EnableItem( + nLocalID, + true); + nScreenshotButtonID = nLocalID; + } + + const sal_uInt16 nId(aMenu->Execute(this, aMenuPos)); + + // 0 == no selection (so not usable as ID) + if (0 != nId) + { + if (bAddButtonsToMenu && nId < nLocalID) + { + nLocalID = 1; + + for (pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next)) + { + Button* pCandidate = isVisibleButtonWithText(pChild); + + if (nullptr == pCandidate) + continue; + + if (nLocalID++ == nId) + { + // pCandidate is the selected button, trigger it + pCandidate->Click(); + break; + } + } + } + + if (bAddScreenshotButtonToMenu && nId == nScreenshotButtonID) + { + // screenshot was selected, access parent dialog (needed for + // screenshot and other data access) + Dialog* pParentDialog = GetParentDialog(); + + if (pParentDialog) + { + // open annotation work dialog + VclPtr<ScreenshotAnnotationDlg> pDlg = VclPtr<ScreenshotAnnotationDlg>::Create( + Application::GetDefDialogParent(), + *pParentDialog); + + if (pDlg && pDlg->Execute() == RET_OK) + { + + + + + + bool bBla2 = true; + } + } + } + } + + // consume event when: + // - CommandEventId::ContextMenu + // - bIsExperimentalMode + // - bVisibleChildren + return; + } + } + } + } + + Window::Command(rCEvt); +} + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// + void VclBox::accumulateMaxes(const Size &rChildSize, Size &rSize) const { long nSecondaryChildDimension = getSecondaryDimension(rChildSize); diff --git a/vcl/uiconfig/ui/screenshotannotationdialog.ui b/vcl/uiconfig/ui/screenshotannotationdialog.ui new file mode 100644 index 0000000..6ab3436 --- /dev/null +++ b/vcl/uiconfig/ui/screenshotannotationdialog.ui @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.16.1 --> +<interface> + <requires lib="gtk+" version="3.0"/> + <object class="GtkDialog" id="ScreenshotAnnotationDialog"> + <property name="can_focus">False</property> + <property name="border_width">6</property> + <property name="title" translatable="yes">Interactive Screenshot Annotation</property> + <property name="modal">True</property> + <property name="default_width">600</property> + <property name="default_height">460</property> + <property name="type_hint">normal</property> + <child internal-child="vbox"> + <object class="GtkBox" id="dialog-vbox1"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">12</property> + <child> + <object class="GtkImage" id="picture"> + <property name="name">picture</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="stock">gtk-missing-image</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog-action_area1"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="ok"> + <property name="label">gtk-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_stock">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">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">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="save"> + <property name="label" translatable="yes">Save Screenshot...</property> + <property name="name">save</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">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="GtkTextView" id="text"> + <property name="name">text</property> + <property name="height_request">80</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> +</interface> _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits