[Libreoffice-commits] core.git: cui/source cui/uiconfig include/vcl sc/source solenv/sanitizers test/source vcl/source vcl/unx

Caolán McNamara (via logerrit) logerrit at kemper.freedesktop.org
Tue Oct 8 14:32:49 UTC 2019


 cui/source/dialogs/screenshotannotationdlg.cxx |  115 ++++----------
 cui/source/factory/dlgfact.cxx                 |    6 
 cui/source/factory/dlgfact.hxx                 |    2 
 cui/source/inc/screenshotannotationdlg.hxx     |    4 
 cui/uiconfig/ui/screenshotannotationdialog.ui  |    5 
 include/vcl/abstdlg.hxx                        |    2 
 include/vcl/dialog.hxx                         |    5 
 include/vcl/layout.hxx                         |    2 
 include/vcl/weld.hxx                           |   26 +++
 sc/source/ui/attrdlg/scdlgfact.hxx             |    7 
 solenv/sanitizers/ui/cui.suppr                 |    4 
 test/source/screenshot_test.cxx                |    5 
 vcl/source/app/salvtables.cxx                  |   97 +++++++++++
 vcl/source/window/dialog.cxx                   |   22 ++
 vcl/source/window/layout.cxx                   |  158 -------------------
 vcl/unx/gtk3/gtk3gtkinst.cxx                   |  203 +++++++++++++++++++++++++
 16 files changed, 411 insertions(+), 252 deletions(-)

New commits:
commit 64fc5986607eaba5db627c546cb1321f00abc501
Author:     Caolán McNamara <caolanm at redhat.com>
AuthorDate: Mon Oct 7 16:43:59 2019 +0100
Commit:     Caolán McNamara <caolanm at redhat.com>
CommitDate: Tue Oct 8 16:31:21 2019 +0200

    implement gtk dialog screenshotting
    
    Change-Id: If4e570f775bd1e29dfb75cb7e5dd9d9dfc35e654
    Reviewed-on: https://gerrit.libreoffice.org/80416
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>

diff --git a/cui/source/dialogs/screenshotannotationdlg.cxx b/cui/source/dialogs/screenshotannotationdlg.cxx
index b99a87e9689d..582e6cf1c474 100644
--- a/cui/source/dialogs/screenshotannotationdlg.cxx
+++ b/cui/source/dialogs/screenshotannotationdlg.cxx
@@ -103,31 +103,6 @@ namespace
     }
 }
 
-class ControlDataEntry
-{
-public:
-    ControlDataEntry(
-        const vcl::Window& rControl,
-        const basegfx::B2IRange& rB2IRange)
-        : mrControl(rControl),
-        maB2IRange(rB2IRange)
-    {
-    }
-
-    const basegfx::B2IRange& getB2IRange() const
-    {
-        return maB2IRange;
-    }
-
-    OString const & GetHelpId() const { return mrControl.GetHelpId(); }
-
-private:
-    const vcl::Window&  mrControl;
-    basegfx::B2IRange   maB2IRange;
-};
-
-typedef std::vector< ControlDataEntry > ControlDataCollection;
-
 class Picture : public weld::CustomWidgetController
 {
 private:
@@ -156,7 +131,7 @@ public:
     ScreenshotAnnotationDlg_Impl(
         weld::Window* pParent,
         weld::Builder& rParent,
-        Dialog& rParentDialog);
+        weld::Dialog& rParentDialog);
     ~ScreenshotAnnotationDlg_Impl();
 
 private:
@@ -167,10 +142,10 @@ private:
     void CollectChildren(
         const vcl::Window& rCurrent,
         const basegfx::B2IPoint& rTopLeft,
-        ControlDataCollection& rControlDataCollection);
-    ControlDataEntry* CheckHit(const basegfx::B2IPoint& rPosition);
-    void PaintControlDataEntry(
-        const ControlDataEntry& rEntry,
+        weld::ScreenShotCollection& rControlDataCollection);
+    weld::ScreenShotEntry* CheckHit(const basegfx::B2IPoint& rPosition);
+    void PaintScreenShotEntry(
+        const weld::ScreenShotEntry& rEntry,
         const Color& rColor,
         double fLineWidth,
         double fTransparency);
@@ -182,7 +157,7 @@ private:
 
     // local variables
     weld::Window*               mpParentWindow;
-    Dialog&                     mrParentDialog;
+    weld::Dialog&               mrParentDialog;
     BitmapEx                    maParentDialogBitmap;
     BitmapEx                    maDimmedDialogBitmap;
     Size                        maParentDialogSize;
@@ -191,11 +166,11 @@ private:
     VclPtr<VirtualDevice>       mxVirtualBufferDevice;
 
     // all detected children
-    ControlDataCollection       maAllChildren;
+    weld::ScreenShotCollection  maAllChildren;
 
     // highlighted/selected children
-    ControlDataEntry*           mpHilighted;
-    std::set< ControlDataEntry* >
+    weld::ScreenShotEntry*           mpHilighted;
+    std::set< weld::ScreenShotEntry* >
                                 maSelected;
 
     // list of detected controls
@@ -221,12 +196,9 @@ OUString ScreenshotAnnotationDlg_Impl::maLastFolderURL = OUString();
 ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl(
     weld::Window* pParent,
     weld::Builder& rParentBuilder,
-    Dialog& rParentDialog)
+    weld::Dialog& rParentDialog)
 :   mpParentWindow(pParent),
     mrParentDialog(rParentDialog),
-    maParentDialogBitmap(rParentDialog.createScreenshot()),
-    maDimmedDialogBitmap(maParentDialogBitmap),
-    maParentDialogSize(maParentDialogBitmap.GetSizePixel()),
     mxVirtualBufferDevice(nullptr),
     maAllChildren(),
     mpHilighted(nullptr),
@@ -234,6 +206,12 @@ ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl(
     maPicture(this),
     maSaveAsText(CuiResId(RID_SVXSTR_SAVE_SCREENSHOT_AS))
 {
+    VclPtr<VirtualDevice> xParentDialogSurface(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
+    rParentDialog.draw(*xParentDialogSurface);
+    maParentDialogSize = xParentDialogSurface->GetOutputSizePixel();
+    maParentDialogBitmap = xParentDialogSurface->GetBitmapEx(Point(), maParentDialogSize);
+    maDimmedDialogBitmap = maParentDialogBitmap;
+
     // image ain't empty
     assert(!maParentDialogBitmap.IsEmpty());
     assert(0 != maParentDialogBitmap.GetSizePixel().Width());
@@ -247,18 +225,10 @@ ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl(
     mxSave = rParentBuilder.weld_button("save");
     assert(mxSave.get());
 
-    // set screenshot image at FixedImage, resize, set event listener
+    // set screenshot image at DrawingArea, resize, set event listener
     if (mxPicture)
     {
-        // collect 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);
+        maAllChildren = mrParentDialog.collect_screenshot_data();
 
         // to make clear that maParentDialogBitmap is a background image, adjust
         // luminance a bit for maDimmedDialogBitmap - other methods may be applied
@@ -285,8 +255,8 @@ ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl(
     if (mxText)
     {
         mxText->set_size_request(400, mxText->get_height_rows(10));
-        OUString aHelpId = OStringToOUString( mrParentDialog.GetHelpId(), RTL_TEXTENCODING_UTF8 );
-        Size aSizeCm = mrParentDialog.PixelToLogic(maParentDialogSize, MapMode(MapUnit::MapCM));
+        OUString aHelpId = OStringToOUString( mrParentDialog.get_help_id(), RTL_TEXTENCODING_UTF8 );
+        Size aSizeCm = Application::GetDefaultDevice()->PixelToLogic(maParentDialogSize, MapMode(MapUnit::MapCM));
         maMainMarkupText = lcl_ParagraphWithImage( aHelpId, aSizeCm );
         mxText->set_text( maMainMarkupText );
         mxText->set_editable(false);
@@ -302,7 +272,7 @@ ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl(
 void ScreenshotAnnotationDlg_Impl::CollectChildren(
     const vcl::Window& rCurrent,
     const basegfx::B2IPoint& rTopLeft,
-    ControlDataCollection& rControlDataCollection)
+    weld::ScreenShotCollection& rControlDataCollection)
 {
     if (rCurrent.IsVisible())
     {
@@ -313,7 +283,7 @@ void ScreenshotAnnotationDlg_Impl::CollectChildren(
 
         if (!aCurrentRange.isEmpty())
         {
-            rControlDataCollection.emplace_back(rCurrent, aCurrentRange);
+            rControlDataCollection.emplace_back(rCurrent.GetHelpId(), aCurrentRange);
         }
 
         for (sal_uInt16 a(0); a < rCurrent.GetChildCount(); a++)
@@ -337,23 +307,8 @@ IMPL_LINK_NOARG(ScreenshotAnnotationDlg_Impl, saveButtonHandler, weld::Button&,
 {
     // '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);
-    }
+    // get a suggestion for the filename from buildable name
+    OString aDerivedFileName = mrParentDialog.get_buildable_name();
 
     auto xFileDlg = std::make_unique<sfx2::FileDialogHelper>(ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION,
                                                              FileDialogFlags::NONE, mpParentWindow);
@@ -421,9 +376,9 @@ IMPL_LINK_NOARG(ScreenshotAnnotationDlg_Impl, saveButtonHandler, weld::Button&,
     }
 }
 
-ControlDataEntry* ScreenshotAnnotationDlg_Impl::CheckHit(const basegfx::B2IPoint& rPosition)
+weld::ScreenShotEntry* ScreenshotAnnotationDlg_Impl::CheckHit(const basegfx::B2IPoint& rPosition)
 {
-    ControlDataEntry* pRetval = nullptr;
+    weld::ScreenShotEntry* pRetval = nullptr;
 
     for (auto&& rCandidate : maAllChildren)
     {
@@ -447,8 +402,8 @@ ControlDataEntry* ScreenshotAnnotationDlg_Impl::CheckHit(const basegfx::B2IPoint
     return pRetval;
 }
 
-void ScreenshotAnnotationDlg_Impl::PaintControlDataEntry(
-    const ControlDataEntry& rEntry,
+void ScreenshotAnnotationDlg_Impl::PaintScreenShotEntry(
+    const weld::ScreenShotEntry& rEntry,
     const Color& rColor,
     double fLineWidth,
     double fTransparency)
@@ -527,14 +482,14 @@ void ScreenshotAnnotationDlg_Impl::RepaintToBuffer(
         for (auto&& rCandidate : maSelected)
         {
             static const double fLineWidthEntries(5.0);
-            PaintControlDataEntry(*rCandidate, COL_LIGHTRED, fLineWidthEntries, fTransparence * 0.2);
+            PaintScreenShotEntry(*rCandidate, COL_LIGHTRED, fLineWidthEntries, fTransparence * 0.2);
         }
 
         // paint highlighted entry
         if (mpHilighted && bPaintHilight)
         {
             static const double fLineWidthHilight(7.0);
-            PaintControlDataEntry(*mpHilighted, aHilightColor, fLineWidthHilight, fTransparence);
+            PaintScreenShotEntry(*mpHilighted, aHilightColor, fLineWidthHilight, fTransparence);
         }
 
         if (bIsAntiAliasing)
@@ -572,16 +527,16 @@ bool ScreenshotAnnotationDlg_Impl::MouseMove(const MouseEvent& rMouseEvent)
 
     if (maPicture.IsMouseOver())
     {
-        const ControlDataEntry* pOldHit = mpHilighted;
+        const weld::ScreenShotEntry* pOldHit = mpHilighted;
         const Point aOffset(GetOffsetInPicture());
         const basegfx::B2IPoint aMousePos(
             rMouseEvent.GetPosPixel().X() - aOffset.X(),
             rMouseEvent.GetPosPixel().Y() - aOffset.Y());
-        const ControlDataEntry* pHit = CheckHit(aMousePos);
+        const weld::ScreenShotEntry* pHit = CheckHit(aMousePos);
 
         if (pHit && pOldHit != pHit)
         {
-            mpHilighted = const_cast<ControlDataEntry*>(pHit);
+            mpHilighted = const_cast<weld::ScreenShotEntry*>(pHit);
             bRepaint = true;
         }
     }
@@ -644,8 +599,8 @@ bool Picture::MouseButtonUp(const MouseEvent&)
     return m_pDialog->MouseButtonUp();
 }
 
-ScreenshotAnnotationDlg::ScreenshotAnnotationDlg(weld::Window* pParent, Dialog& rParentDialog)
-    : GenericDialogController(pParent, "cui/ui/screenshotannotationdialog.ui", "ScreenshotAnnotationDialog")
+ScreenshotAnnotationDlg::ScreenshotAnnotationDlg(weld::Dialog& rParentDialog)
+    : GenericDialogController(&rParentDialog, "cui/ui/screenshotannotationdialog.ui", "ScreenshotAnnotationDialog")
 {
     m_pImpl.reset(new ScreenshotAnnotationDlg_Impl(m_xDialog.get(), *m_xBuilder, rParentDialog));
 }
diff --git a/cui/source/factory/dlgfact.cxx b/cui/source/factory/dlgfact.cxx
index 0a81690478ca..e08cd60cfd7d 100644
--- a/cui/source/factory/dlgfact.cxx
+++ b/cui/source/factory/dlgfact.cxx
@@ -1608,11 +1608,9 @@ VclPtr<AbstractPasswordToOpenModifyDialog> AbstractDialogFactory_Impl::CreatePas
     return VclPtr<AbstractPasswordToOpenModifyDialog_Impl>::Create(std::make_unique<PasswordToOpenModifyDialog>(pParent, nMaxPasswdLen, bIsPasswordToModify));
 }
 
-VclPtr<AbstractScreenshotAnnotationDlg> AbstractDialogFactory_Impl::CreateScreenshotAnnotationDlg(
-    weld::Window* pParent,
-    Dialog& rParentDialog)
+VclPtr<AbstractScreenshotAnnotationDlg> AbstractDialogFactory_Impl::CreateScreenshotAnnotationDlg(weld::Dialog& rParentDialog)
 {
-    return VclPtr<AbstractScreenshotAnnotationDlg_Impl>::Create(std::make_unique<ScreenshotAnnotationDlg>(pParent, rParentDialog));
+    return VclPtr<AbstractScreenshotAnnotationDlg_Impl>::Create(std::make_unique<ScreenshotAnnotationDlg>(rParentDialog));
 }
 
 VclPtr<AbstractSignatureLineDialog> AbstractDialogFactory_Impl::CreateSignatureLineDialog(
diff --git a/cui/source/factory/dlgfact.hxx b/cui/source/factory/dlgfact.hxx
index b3ada6ca80e9..3bc6e90cc377 100644
--- a/cui/source/factory/dlgfact.hxx
+++ b/cui/source/factory/dlgfact.hxx
@@ -930,7 +930,7 @@ public:
 
     virtual VclPtr<AbstractPasswordToOpenModifyDialog> CreatePasswordToOpenModifyDialog(weld::Window* pParent, sal_uInt16 nMaxPasswdLen, bool bIsPasswordToModify) override;
 
-    virtual VclPtr<AbstractScreenshotAnnotationDlg> CreateScreenshotAnnotationDlg(weld::Window* pParent, Dialog& rParentDialog) override;
+    virtual VclPtr<AbstractScreenshotAnnotationDlg> CreateScreenshotAnnotationDlg(weld::Dialog& rParentDialog) override;
 
     virtual VclPtr<AbstractSignatureLineDialog>
     CreateSignatureLineDialog(weld::Window* pParent,
diff --git a/cui/source/inc/screenshotannotationdlg.hxx b/cui/source/inc/screenshotannotationdlg.hxx
index 53df8d4f7ea5..6730ef0514fe 100644
--- a/cui/source/inc/screenshotannotationdlg.hxx
+++ b/cui/source/inc/screenshotannotationdlg.hxx
@@ -34,9 +34,7 @@ private:
     ScreenshotAnnotationDlg& operator=(const ScreenshotAnnotationDlg &) = delete;
 
 public:
-    ScreenshotAnnotationDlg(
-        weld::Window* pParent,
-        Dialog& rParentDialog);
+    ScreenshotAnnotationDlg(weld::Dialog& rParentDialog);
     virtual ~ScreenshotAnnotationDlg() override;
 };
 
diff --git a/cui/uiconfig/ui/screenshotannotationdialog.ui b/cui/uiconfig/ui/screenshotannotationdialog.ui
index e7e3e78fa3d0..45d8b51b385a 100644
--- a/cui/uiconfig/ui/screenshotannotationdialog.ui
+++ b/cui/uiconfig/ui/screenshotannotationdialog.ui
@@ -68,6 +68,8 @@
             <property name="can_focus">False</property>
             <property name="halign">start</property>
             <property name="label" translatable="yes" context="screenshotannotationdialog|label2">Click the widgets to add annotation:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">picture</property>
           </object>
           <packing>
             <property name="expand">False</property>
@@ -80,6 +82,7 @@
             <property name="name">image</property>
             <property name="visible">True</property>
             <property name="can_focus">False</property>
+            <property name="events">GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
           </object>
           <packing>
             <property name="expand">True</property>
@@ -93,6 +96,8 @@
             <property name="can_focus">False</property>
             <property name="halign">start</property>
             <property name="label" translatable="yes" context="screenshotannotationdialog|label1">Paste the following markup into the help file:</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">text</property>
             <property name="ellipsize">end</property>
           </object>
           <packing>
diff --git a/include/vcl/abstdlg.hxx b/include/vcl/abstdlg.hxx
index d9a5058c7e42..ccbc64a753a0 100644
--- a/include/vcl/abstdlg.hxx
+++ b/include/vcl/abstdlg.hxx
@@ -172,7 +172,7 @@ public:
 
     // creates instance of ScreenshotAnnotationDlg from cui
     virtual VclPtr<AbstractScreenshotAnnotationDlg> CreateScreenshotAnnotationDlg(
-        weld::Window* pParent, Dialog& rParentDialog) = 0;
+        weld::Dialog& rParentDialog) = 0;
 
     // create info dialog to show tip-of-the-day
     virtual VclPtr<AbstractTipOfTheDayDialog>
diff --git a/include/vcl/dialog.hxx b/include/vcl/dialog.hxx
index e2ddea74be78..cc5355d58132 100644
--- a/include/vcl/dialog.hxx
+++ b/include/vcl/dialog.hxx
@@ -110,6 +110,7 @@ public:
     virtual bool    EventNotify( NotifyEvent& rNEvt ) override;
     virtual void    StateChanged( StateChangedType nStateChange ) override;
     virtual void    DataChanged( const DataChangedEvent& rDCEvt ) override;
+    virtual void    Command( const CommandEvent& rCEvt ) override;
 
     virtual void queue_resize(StateChangedType eReason = StateChangedType::Layout) override;
     virtual bool set_property(const OString &rKey, const OUString &rValue) override;
@@ -127,7 +128,7 @@ public:
     // Screenshot interface
     virtual std::vector<OString> getAllPageUIXMLDescriptions() const;
     virtual bool selectPageByUIXMLDescription(const OString& rUIXMLDescription);
-    BitmapEx createScreenshot();
+    void createScreenshot(VirtualDevice& rOutput);
 
     virtual short   Execute();
     bool            IsInExecute() const { return mbInExecute; }
@@ -174,7 +175,7 @@ public:
 
     void            Activate() override;
 
-
+    void            SetPopupMenuHdl(const Link<const CommandEvent&, bool>& rLink);
     void            SetInstallLOKNotifierHdl(const Link<void*, vcl::ILibreOfficeKitNotifier*>& rLink);
 
     void            add_button(PushButton* pButton, int nResponse, bool bTransferOwnership);
diff --git a/include/vcl/layout.hxx b/include/vcl/layout.hxx
index 0164b2b09df0..494cb708f292 100644
--- a/include/vcl/layout.hxx
+++ b/include/vcl/layout.hxx
@@ -50,7 +50,7 @@ protected:
 
     virtual sal_uInt16 getDefaultAccessibleRole() const override;
 
-    // evtl. support for screenshot context menu
+    // support for screenshot context menu
     virtual void Command(const CommandEvent& rCEvt) override;
 
 public:
diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx
index 5559833799fc..20c8d9e5751e 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -10,6 +10,7 @@
 #ifndef INCLUDED_VCL_WELD_HXX
 #define INCLUDED_VCL_WELD_HXX
 
+#include <basegfx/range/b2irange.hxx>
 #include <rtl/ustring.hxx>
 #include <tools/color.hxx>
 #include <tools/date.hxx>
@@ -419,6 +420,26 @@ public:
 
 class Button;
 
+class VCL_DLLPUBLIC ScreenShotEntry
+{
+public:
+    ScreenShotEntry(const OString& rHelpId, const basegfx::B2IRange& rB2IRange)
+        : msHelpId(rHelpId)
+        , maB2IRange(rB2IRange)
+    {
+    }
+
+    const basegfx::B2IRange& getB2IRange() const { return maB2IRange; }
+
+    const OString& GetHelpId() const { return msHelpId; }
+
+private:
+    OString msHelpId;
+    basegfx::B2IRange maB2IRange;
+};
+
+typedef std::vector<ScreenShotEntry> ScreenShotCollection;
+
 class VCL_DLLPUBLIC Dialog : virtual public Window
 {
 private:
@@ -446,6 +467,11 @@ public:
     // undo previous dialog collapse
     virtual void undo_collapse() = 0;
 
+    // render the dialog for a screenshot
+    virtual void draw(VirtualDevice& rOutput) = 0;
+    // collect positions of widgets and their help ids for screenshot purposes
+    virtual ScreenShotCollection collect_screenshot_data() = 0;
+
     virtual void SetInstallLOKNotifierHdl(const Link<void*, vcl::ILibreOfficeKitNotifier*>& rLink)
         = 0;
 };
diff --git a/sc/source/ui/attrdlg/scdlgfact.hxx b/sc/source/ui/attrdlg/scdlgfact.hxx
index e087ad7dade6..8655e6bc9ad4 100644
--- a/sc/source/ui/attrdlg/scdlgfact.hxx
+++ b/sc/source/ui/attrdlg/scdlgfact.hxx
@@ -21,6 +21,7 @@
 
 #include <scabstdlg.hxx>
 #include <sfx2/sfxdlg.hxx>
+#include <vcl/virdev.hxx>
 
 #include <corodlg.hxx>
 #include <condformatmgr.hxx>
@@ -87,9 +88,11 @@ bool Class::selectPageByUIXMLDescription(const OString& rUIXMLDescription) \
 {                                                   \
    return pDlg->selectPageByUIXMLDescription(rUIXMLDescription);  \
 }                                                   \
-BitmapEx Class::createScreenshot() const              \
+BitmapEx Class::createScreenshot() const            \
 {                                                   \
-    return pDlg->createScreenshot();                \
+    VclPtr<VirtualDevice> xDialogSurface(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT)); \
+    pDlg->createScreenshot(*xDialogSurface);        \
+    return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); \
 }                                                   \
 OString Class::GetScreenshotId() const              \
 {                                                   \
diff --git a/solenv/sanitizers/ui/cui.suppr b/solenv/sanitizers/ui/cui.suppr
index 1fb9cf484716..d417663db12c 100644
--- a/solenv/sanitizers/ui/cui.suppr
+++ b/solenv/sanitizers/ui/cui.suppr
@@ -248,10 +248,6 @@ cui/uiconfig/ui/paraindentspacing.ui://GtkLabel[@id='labelFT_LINEDIST'] orphan-l
 cui/uiconfig/ui/paraindentspacing.ui://GtkSpinButton[@id='spinED_LINEDISTPERCENT'] no-labelled-by
 cui/uiconfig/ui/paraindentspacing.ui://GtkSpinButton[@id='spinED_LINEDISTMETRIC'] no-labelled-by
 cui/uiconfig/ui/password.ui://GtkLabel[@id='label1'] orphan-label
-cui/uiconfig/ui/screenshotannotationdialog.ui://GtkLabel[@id='label2'] orphan-label
-cui/uiconfig/ui/screenshotannotationdialog.ui://GtkImage[@id='picture'] no-labelled-by
-cui/uiconfig/ui/screenshotannotationdialog.ui://GtkLabel[@id='label1'] orphan-label
-cui/uiconfig/ui/screenshotannotationdialog.ui://GtkTextView[@id='text:border'] no-labelled-by
 cui/uiconfig/ui/pastespecial.ui://GtkLabel[@id='label2'] orphan-label
 cui/uiconfig/ui/pastespecial.ui://GtkLabel[@id='source'] orphan-label
 cui/uiconfig/ui/paratabspage.ui://GtkSpinButton[@id='SP_TABPOS'] no-labelled-by
diff --git a/test/source/screenshot_test.cxx b/test/source/screenshot_test.cxx
index b2173b57bf25..e849bae672ce 100644
--- a/test/source/screenshot_test.cxx
+++ b/test/source/screenshot_test.cxx
@@ -19,6 +19,7 @@
 #include <vcl/abstdlg.hxx>
 #include <vcl/pngwrite.hxx>
 #include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
 #include <unotools/configmgr.hxx>
 #include <tools/stream.hxx>
 
@@ -109,7 +110,9 @@ void ScreenshotTest::saveScreenshot(VclAbstractDialog const & rDialog)
 
 void ScreenshotTest::saveScreenshot(Dialog& rDialog)
 {
-    const BitmapEx aScreenshot(rDialog.createScreenshot());
+    VclPtr<VirtualDevice> xDialogSurface(VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT));
+    rDialog.createScreenshot(*xDialogSurface);
+    const BitmapEx aScreenshot(xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()));
 
     if (!aScreenshot.IsEmpty())
     {
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index ac51b15cbc2d..416f81071e93 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -19,6 +19,7 @@
 
 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
 #include <com/sun/star/awt/XWindow.hpp>
+#include <officecfg/Office/Common.hxx>
 #include <salframe.hxx>
 #include <salinst.hxx>
 #include <salvd.hxx>
@@ -29,12 +30,14 @@
 #include <salbmp.hxx>
 #include <salobj.hxx>
 #include <salmenu.hxx>
+#include <strings.hrc>
 #include <svdata.hxx>
 #include <messagedialog.hxx>
 #include <treeglue.hxx>
 #include <unotools/accessiblerelationsethelper.hxx>
 #include <utility>
 #include <tools/helpers.hxx>
+#include <vcl/abstdlg.hxx>
 #include <vcl/builder.hxx>
 #include <vcl/calendar.hxx>
 #include <vcl/combobox.hxx>
@@ -1255,6 +1258,34 @@ namespace
     }
 }
 
+namespace
+{
+    void CollectChildren(const vcl::Window& rCurrent, const basegfx::B2IPoint& rTopLeft, weld::ScreenShotCollection& 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.emplace_back(rCurrent.GetHelpId(), aCurrentRange);
+            }
+
+            for (sal_uInt16 a(0); a < rCurrent.GetChildCount(); a++)
+            {
+                vcl::Window* pChild = rCurrent.GetChild(a);
+                if (nullptr != pChild)
+                {
+                    CollectChildren(*pChild, aCurrentTopLeft, rControlDataCollection);
+                }
+            }
+        }
+    }
+}
+
 class SalInstanceDialog : public SalInstanceWindow, public virtual weld::Dialog
 {
 private:
@@ -1266,6 +1297,8 @@ private:
     long m_nOldEditWidthReq; // Original width request of the input field
     sal_Int32 m_nOldBorderWidth; // border width for expanded dialog
 
+    DECL_LINK(PopupScreenShotMenuHdl, const CommandEvent&, bool);
+
 public:
     SalInstanceDialog(::Dialog* pDialog, SalInstanceBuilder* pBuilder, bool bTakeOwnership)
         : SalInstanceWindow(pDialog, pBuilder, bTakeOwnership)
@@ -1273,6 +1306,11 @@ public:
         , m_nOldEditWidthReq(0)
         , m_nOldBorderWidth(0)
     {
+        const bool bScreenshotMode(officecfg::Office::Common::Misc::ScreenshotMode::get());
+        if (bScreenshotMode)
+        {
+            m_xDialog->SetPopupMenuHdl(LINK(this, SalInstanceDialog, PopupScreenShotMenuHdl));
+        }
     }
 
     virtual bool runAsync(std::shared_ptr<weld::DialogController> aOwner, const std::function<void(sal_Int32)> &rEndDialogFn) override
@@ -1435,8 +1473,67 @@ public:
     {
         return new SalInstanceContainer(m_xDialog->get_content_area(), m_pBuilder, false);
     }
+
+    virtual void draw(VirtualDevice& rOutput) override
+    {
+        m_xDialog->createScreenshot(rOutput);
+    }
+
+    virtual weld::ScreenShotCollection collect_screenshot_data() override
+    {
+        weld::ScreenShotCollection aRet;
+
+        // collect all children. Choose start pos to be negative
+        // of target dialog's position to get all positions relative to (0,0)
+        const Point aParentPos(m_xDialog->GetPosPixel());
+        const basegfx::B2IPoint aTopLeft(-aParentPos.X(), -aParentPos.Y());
+        CollectChildren(*m_xDialog, aTopLeft, aRet);
+
+        return aRet;
+    }
 };
 
+IMPL_LINK(SalInstanceDialog, PopupScreenShotMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+    if (CommandEventId::ContextMenu == rCEvt.GetCommand())
+    {
+        const Point aMenuPos(rCEvt.GetMousePosPixel());
+        ScopedVclPtrInstance<PopupMenu> aMenu;
+        sal_uInt16 nLocalID(1);
+
+        aMenu->InsertItem(nLocalID, VclResId(SV_BUTTONTEXT_SCREENSHOT));
+        aMenu->SetHelpText(nLocalID, VclResId(SV_HELPTEXT_SCREENSHOT));
+        aMenu->SetHelpId(nLocalID, "InteractiveScreenshotMode");
+        aMenu->EnableItem(nLocalID);
+
+        const sal_uInt16 nId(aMenu->Execute(m_xDialog, aMenuPos));
+
+        // 0 == no selection (so not usable as ID)
+        if (0 != nId)
+        {
+            // open screenshot annotation dialog
+            VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+            VclPtr<AbstractScreenshotAnnotationDlg> pTmp = pFact->CreateScreenshotAnnotationDlg(*this);
+            ScopedVclPtr<AbstractScreenshotAnnotationDlg> pDialog(pTmp);
+
+            if (pDialog)
+            {
+                // 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();
+            }
+        }
+
+        // consume event when:
+        // - CommandEventId::ContextMenu
+        // - bScreenshotMode
+        return true;
+    }
+
+    return false;
+}
+
 class SalInstanceMessageDialog : public SalInstanceDialog, public virtual weld::MessageDialog
 {
 private:
diff --git a/vcl/source/window/dialog.cxx b/vcl/source/window/dialog.cxx
index 5acc2bb8e757..679403c9efbc 100644
--- a/vcl/source/window/dialog.cxx
+++ b/vcl/source/window/dialog.cxx
@@ -51,6 +51,7 @@
 #include <vcl/mnemonic.hxx>
 #include <vcl/dialog.hxx>
 #include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
 #include <vcl/weld.hxx>
 #include <vcl/uitest/uiobject.hxx>
 #include <vcl/uitest/logger.hxx>
@@ -350,6 +351,7 @@ struct DialogImpl
     long    mnResult;
     bool    mbStartedModal;
     VclAbstractDialog::AsyncContext maEndCtx;
+    Link<const CommandEvent&, bool> m_aPopupMenuHdl;
     Link<void*, vcl::ILibreOfficeKitNotifier*> m_aInstallLOKNotifierHdl;
 
     DialogImpl() : mnResult( -1 ), mbStartedModal( false ) {}
@@ -733,6 +735,11 @@ Size bestmaxFrameSizeForScreenSize(const Size &rScreenSize)
 #endif
 }
 
+void Dialog::SetPopupMenuHdl(const Link<const CommandEvent&, bool>& rLink)
+{
+    mpDialogImpl->m_aPopupMenuHdl = rLink;
+}
+
 void Dialog::SetInstallLOKNotifierHdl(const Link<void*, vcl::ILibreOfficeKitNotifier*>& rLink)
 {
     mpDialogImpl->m_aInstallLOKNotifierHdl = rLink;
@@ -1042,7 +1049,7 @@ void Dialog::ensureRepaint()
     }
 }
 
-BitmapEx Dialog::createScreenshot()
+void Dialog::createScreenshot(VirtualDevice& rOutput)
 {
     // same prerequisites as in Execute()
     setDeferredProperties();
@@ -1051,7 +1058,11 @@ BitmapEx Dialog::createScreenshot()
     ToTop();
     ensureRepaint();
 
-    return GetBitmapEx(Point(), GetOutputSizePixel());
+    Point aPos;
+    Size aSize(GetOutputSizePixel());
+
+    rOutput.SetOutputSizePixel(aSize);
+    rOutput.DrawOutDev(aPos, aSize, aPos, aSize, *this);
 }
 
 short Dialog::Execute()
@@ -1591,6 +1602,13 @@ void Dialog::Activate()
     SystemWindow::Activate();
 }
 
+void Dialog::Command(const CommandEvent& rCEvt)
+{
+    if (mpDialogImpl && mpDialogImpl->m_aPopupMenuHdl.Call(rCEvt))
+        return;
+    SystemWindow::Command(rCEvt);
+}
+
 void TopLevelWindowLocker::incBusy(const weld::Widget* pIgnore)
 {
     // lock any toplevel windows from being closed until busy is over
diff --git a/vcl/source/window/layout.cxx b/vcl/source/window/layout.cxx
index 6863a8ea4b66..609a7ca5d66b 100644
--- a/vcl/source/window/layout.cxx
+++ b/vcl/source/window/layout.cxx
@@ -19,8 +19,6 @@
 #include <messagedialog.hxx>
 #include <window.h>
 #include <boost/multi_array.hpp>
-#include <officecfg/Office/Common.hxx>
-#include <vcl/abstdlg.hxx>
 #include <vcl/vclmedit.hxx>
 #include <sal/log.hxx>
 
@@ -183,159 +181,17 @@ void VclContainer::queue_resize(StateChangedType eReason)
     Window::queue_resize(eReason);
 }
 
-
-static 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
+// support for screenshot context menu
 void VclContainer::Command(const CommandEvent& rCEvt)
 {
-    if (rCEvt.IsMouseEvent() && CommandEventId::ContextMenu == rCEvt.GetCommand())
+    if (CommandEventId::ContextMenu == rCEvt.GetCommand())
     {
-        const bool bScreenshotMode(officecfg::Office::Common::Misc::ScreenshotMode::get());
-
-        if (bScreenshotMode)
+        auto pParent = GetParent();
+        if (pParent)
         {
-            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); // loplugin:constvars:ignore
-                static bool bAddScreenshotButtonToMenu(true); // loplugin:constvars:ignore
-
-                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());
-                            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,
-                            VclResId(SV_BUTTONTEXT_SCREENSHOT));
-                        aMenu->SetHelpText(
-                            nLocalID,
-                            VclResId(SV_HELPTEXT_SCREENSHOT));
-                        aMenu->SetHelpId(
-                            nLocalID,
-                            "InteractiveScreenshotMode");
-                        aMenu->EnableItem(
-                            nLocalID);
-                        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 screenshot annotation dialog
-                                VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
-                                VclPtr<AbstractScreenshotAnnotationDlg> pTmp = pFact->CreateScreenshotAnnotationDlg(
-                                    pParentDialog->GetFrameWeld(),
-                                    *pParentDialog);
-                                ScopedVclPtr<AbstractScreenshotAnnotationDlg> pDialog(pTmp);
-
-                                if (pDialog)
-                                {
-                                    // 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();
-                                }
-                            }
-                        }
-                    }
-
-                    // consume event when:
-                    // - CommandEventId::ContextMenu
-                    // - bScreenshotMode
-                    // - bVisibleChildren
-                    return;
-                }
-            }
+            CommandEvent aCEvt(rCEvt.GetMousePosPixel() + GetPosPixel(), rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData());
+            pParent->Command(aCEvt);
+            return;
         }
     }
 
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index 848d3671ed3b..bc5a7047915b 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -55,6 +55,7 @@
 #include <comphelper/string.hxx>
 #include <cppuhelper/implbase.hxx>
 #include <cppuhelper/supportsservice.hxx>
+#include <officecfg/Office/Common.hxx>
 #include <rtl/bootstrap.hxx>
 #include <svl/zforlist.hxx>
 #include <svl/zformat.hxx>
@@ -64,6 +65,7 @@
 #include <unotools/resmgr.hxx>
 #include <unx/gstsink.hxx>
 #include <vcl/ImageTree.hxx>
+#include <vcl/abstdlg.hxx>
 #include <vcl/button.hxx>
 #include <vcl/event.hxx>
 #include <vcl/i18nhelp.hxx>
@@ -75,6 +77,7 @@
 #include <vcl/virdev.hxx>
 #include <vcl/weld.hxx>
 #include <vcl/wrkwin.hxx>
+#include <strings.hrc>
 #include <window.h>
 #include <numeric>
 
@@ -1809,6 +1812,8 @@ private:
     gulong m_nSizeAllocateSignalId;
     gulong m_nButtonPressSignalId;
     gulong m_nMotionSignalId;
+    gulong m_nLeaveSignalId;
+    gulong m_nEnterSignalId;
     gulong m_nButtonReleaseSignalId;
     gulong m_nDragMotionSignalId;
     gulong m_nDragDropSignalId;
@@ -1950,6 +1955,31 @@ private:
         return true;
     }
 
+    static gboolean signalCrossing(GtkWidget*, GdkEventCrossing* pEvent, gpointer widget)
+    {
+        GtkInstanceWidget* pThis = static_cast<GtkInstanceWidget*>(widget);
+        SolarMutexGuard aGuard;
+        return pThis->signal_crossing(pEvent);
+    }
+
+    bool signal_crossing(const GdkEventCrossing* pEvent)
+    {
+        if (!m_aMouseMotionHdl.IsSet())
+            return false;
+
+        Point aPos(pEvent->x, pEvent->y);
+        if (AllSettings::GetLayoutRTL())
+            aPos.setX(gtk_widget_get_allocated_width(m_pWidget) - 1 - aPos.X());
+        sal_uInt32 nModCode = GtkSalFrame::GetMouseModCode(pEvent->state);
+        sal_uInt16 nCode = m_nLastMouseButton | (nModCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2));
+        MouseEventModifiers eModifiers = ImplGetMouseMoveMode(nModCode);
+        eModifiers = eModifiers | (pEvent->type == GDK_ENTER_NOTIFY ? MouseEventModifiers::ENTERWINDOW : MouseEventModifiers::LEAVEWINDOW);
+        MouseEvent aMEvt(aPos, 0, eModifiers, nCode, nCode);
+
+        m_aMouseMotionHdl.Call(aMEvt);
+        return true;
+    }
+
     virtual void drag_started()
     {
     }
@@ -2033,6 +2063,8 @@ public:
         , m_nSizeAllocateSignalId(0)
         , m_nButtonPressSignalId(0)
         , m_nMotionSignalId(0)
+        , m_nLeaveSignalId(0)
+        , m_nEnterSignalId(0)
         , m_nButtonReleaseSignalId(0)
         , m_nDragMotionSignalId(0)
         , m_nDragDropSignalId(0)
@@ -2063,6 +2095,8 @@ public:
     {
         ensureEventWidget();
         m_nMotionSignalId = g_signal_connect(m_pMouseEventBox, "motion-notify-event", G_CALLBACK(signalMotion), this);
+        m_nLeaveSignalId = g_signal_connect(m_pMouseEventBox, "leave-notify-event", G_CALLBACK(signalCrossing), this);
+        m_nEnterSignalId = g_signal_connect(m_pMouseEventBox, "enter-notify-event", G_CALLBACK(signalCrossing), this);
         weld::Widget::connect_mouse_move(rLink);
     }
 
@@ -2584,6 +2618,10 @@ public:
             g_signal_handler_disconnect(m_pMouseEventBox, m_nButtonPressSignalId);
         if (m_nMotionSignalId)
             g_signal_handler_disconnect(m_pMouseEventBox, m_nMotionSignalId);
+        if (m_nLeaveSignalId)
+            g_signal_handler_disconnect(m_pMouseEventBox, m_nLeaveSignalId);
+        if (m_nEnterSignalId)
+            g_signal_handler_disconnect(m_pMouseEventBox, m_nEnterSignalId);
         if (m_nButtonReleaseSignalId)
             g_signal_handler_disconnect(m_pMouseEventBox, m_nButtonReleaseSignalId);
         if (m_nFocusInSignalId)
@@ -3742,6 +3780,135 @@ private:
 
     void asyncresponse(gint ret);
 
+    static void signalActivate(GtkMenuItem*, gpointer data)
+    {
+        bool* pActivate = static_cast<bool*>(data);
+        *pActivate = true;
+    }
+
+    bool signal_screenshot_popup_menu(GdkEventButton* pEvent)
+    {
+        GtkWidget *pMenu = gtk_menu_new();
+
+        GtkWidget* pMenuItem = gtk_menu_item_new_with_mnemonic(MapToGtkAccelerator(VclResId(SV_BUTTONTEXT_SCREENSHOT)).getStr());
+        gtk_menu_shell_append(GTK_MENU_SHELL(pMenu), pMenuItem);
+        bool bActivate(false);
+        g_signal_connect(pMenuItem, "activate", G_CALLBACK(signalActivate), &bActivate);
+        gtk_widget_show(pMenuItem);
+
+        int button, event_time;
+        if (pEvent)
+        {
+            button = pEvent->button;
+            event_time = pEvent->time;
+        }
+        else
+        {
+            button = 0;
+            event_time = gtk_get_current_event_time();
+        }
+
+        gtk_menu_attach_to_widget(GTK_MENU(pMenu), GTK_WIDGET(m_pDialog), nullptr);
+
+        GMainLoop* pLoop = g_main_loop_new(nullptr, true);
+        gulong nSignalId = g_signal_connect_swapped(G_OBJECT(pMenu), "deactivate", G_CALLBACK(g_main_loop_quit), pLoop);
+
+        gtk_menu_popup(GTK_MENU(pMenu), nullptr, nullptr, nullptr, nullptr, button, event_time);
+
+        if (g_main_loop_is_running(pLoop))
+        {
+            gdk_threads_leave();
+            g_main_loop_run(pLoop);
+            gdk_threads_enter();
+        }
+
+        g_main_loop_unref(pLoop);
+        g_signal_handler_disconnect(pMenu, nSignalId);
+        gtk_menu_detach(GTK_MENU(pMenu));
+
+        if (bActivate)
+        {
+            // open screenshot annotation dialog
+            VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+            VclPtr<AbstractScreenshotAnnotationDlg> xTmp = pFact->CreateScreenshotAnnotationDlg(*this);
+            ScopedVclPtr<AbstractScreenshotAnnotationDlg> xDialog(xTmp);
+            xDialog->Execute();
+        }
+
+        return false;
+    }
+
+    static gboolean signalScreenshotPopupMenu(GtkWidget*, gpointer widget)
+    {
+        GtkInstanceDialog* pThis = static_cast<GtkInstanceDialog*>(widget);
+        return pThis->signal_screenshot_popup_menu(nullptr);
+    }
+
+    static gboolean signalScreenshotButton(GtkWidget*, GdkEventButton* pEvent, gpointer widget)
+    {
+        GtkInstanceDialog* pThis = static_cast<GtkInstanceDialog*>(widget);
+        SolarMutexGuard aGuard;
+        return pThis->signal_screenshot_button(pEvent);
+    }
+
+    bool signal_screenshot_button(GdkEventButton* pEvent)
+    {
+        if (gdk_event_triggers_context_menu(reinterpret_cast<GdkEvent*>(pEvent)) && pEvent->type == GDK_BUTTON_PRESS)
+        {
+            //if handled for context menu, stop processing
+            return signal_screenshot_popup_menu(pEvent);
+        }
+        return false;
+    }
+
+    static Point get_csd_offset(GtkWidget* pTopLevel)
+    {
+        // try and omit drawing CSD under wayland
+        GList* pChildren = gtk_container_get_children(GTK_CONTAINER(pTopLevel));
+        GList* pChild = g_list_first(pChildren);
+
+        int x, y;
+        gtk_widget_translate_coordinates(GTK_WIDGET(pChild->data),
+                                         GTK_WIDGET(pTopLevel),
+                                         0, 0, &x, &y);
+
+        int innerborder = gtk_container_get_border_width(GTK_CONTAINER(pChild->data));
+        g_list_free(pChildren);
+
+        int outerborder = gtk_container_get_border_width(GTK_CONTAINER(pTopLevel));
+        int totalborder = outerborder + innerborder;
+        x -= totalborder;
+        y -= totalborder;
+
+        return Point(x, y);
+    }
+
+    static void do_collect_screenshot_data(GtkWidget* pItem, gpointer data)
+    {
+        GtkWidget* pTopLevel = gtk_widget_get_toplevel(pItem);
+
+        int x, y;
+        gtk_widget_translate_coordinates(pItem, pTopLevel, 0, 0, &x, &y);
+
+        Point aOffset = get_csd_offset(pTopLevel);
+
+        GtkAllocation alloc;
+        gtk_widget_get_allocation(pItem, &alloc);
+
+        const basegfx::B2IPoint aCurrentTopLeft(x - aOffset.X(), y - aOffset.Y());
+        const basegfx::B2IRange aCurrentRange(aCurrentTopLeft, aCurrentTopLeft + basegfx::B2IPoint(alloc.width, alloc.height));
+
+        if (!aCurrentRange.isEmpty())
+        {
+            weld::ScreenShotCollection* pCollection = static_cast<weld::ScreenShotCollection*>(data);
+            pCollection->emplace_back(::get_help_id(pItem), aCurrentRange);
+        }
+
+        if (GTK_IS_CONTAINER(pItem))
+            gtk_container_forall(GTK_CONTAINER(pItem), do_collect_screenshot_data, data);
+    }
+
+
 public:
     GtkInstanceDialog(GtkWindow* pDialog, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
         : GtkInstanceWindow(pDialog, pBuilder, bTakeOwnership)
@@ -3756,6 +3923,12 @@ public:
         , m_nOldEditWidthReq(0)
         , m_nOldBorderWidth(0)
     {
+        const bool bScreenshotMode(officecfg::Office::Common::Misc::ScreenshotMode::get());
+        if (bScreenshotMode)
+        {
+            g_signal_connect(m_pDialog, "popup-menu", G_CALLBACK(signalScreenshotPopupMenu), this);
+            g_signal_connect(m_pDialog, "button-press-event", G_CALLBACK(signalScreenshotButton), this);
+        }
     }
 
     virtual bool runAsync(std::shared_ptr<weld::DialogController> rDialogController, const std::function<void(sal_Int32)>& func) override
@@ -3949,6 +4122,36 @@ public:
         //not implemented for the gtk variant
     }
 
+    virtual void draw(VirtualDevice& rOutput) override
+    {
+        rOutput.SetOutputSizePixel(get_size());
+        cairo_surface_t* pSurface = get_underlying_cairo_surface(rOutput);
+        cairo_t* cr = cairo_create(pSurface);
+
+        Point aOffset = get_csd_offset(GTK_WIDGET(m_pDialog));
+
+#if defined(GDK_WINDOWING_X11)
+        GdkDisplay *pDisplay = gtk_widget_get_display(GTK_WIDGET(m_pDialog));
+        if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay))
+            assert(aOffset.X() == 0 && aOffset.Y() == 0 && "expected offset of 0 under X");
+#endif
+
+        cairo_translate(cr, -aOffset.X(), -aOffset.Y());
+
+        gtk_widget_draw(GTK_WIDGET(m_pDialog), cr);
+
+        cairo_destroy(cr);
+    }
+
+    virtual weld::ScreenShotCollection collect_screenshot_data() override
+    {
+        weld::ScreenShotCollection aRet;
+
+        gtk_container_foreach(GTK_CONTAINER(m_pDialog), do_collect_screenshot_data, &aRet);
+
+        return aRet;
+    }
+
     virtual ~GtkInstanceDialog() override
     {
         if (!m_aHiddenWidgets.empty())


More information about the Libreoffice-commits mailing list