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
This commit is contained in:
Armin Le Grand
2016-08-26 14:48:17 +02:00
committed by Thorsten Behrens
parent f9238c34f0
commit e2d26860f9
14 changed files with 688 additions and 459 deletions

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -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 \

View File

@@ -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: */

View File

@@ -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: */

View File

@@ -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"

View File

@@ -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: */

View File

@@ -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

View File

@@ -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)

View File

@@ -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: */

View File

@@ -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

View File

@@ -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:

View File

@@ -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);