cui/source/dialogs/screenshotannotationdlg.cxx |   83 ++++++++++++++++++++++++-
 cui/uiconfig/ui/screenshotannotationdialog.ui  |   76 +++++++++++++---------
 2 files changed, 125 insertions(+), 34 deletions(-)

New commits:
commit 70c7f6ba49cfc506fc47dac32af98976ecf33cf8
Author:     Olivier Hallot <[email protected]>
AuthorDate: Fri Sep 19 09:11:51 2025 -0300
Commit:     Olivier Hallot <[email protected]>
CommitDate: Mon Sep 22 12:57:45 2025 +0200

    tdf#166391 Add Copy Image button to screenshot dialog.
    
    This patch is the continuation/restoring of
    
    https://gerrit.libreoffice.org/c/core/+/184907
    
    Big thank you to Noel Grandin for the help.
    
    Noel's suggestions were used to invoke CoPilot LLM
    to create the BitmapTransferable class under VSCode IDE.
    Some adjustment on the mime type were necessary.
    LLM was aware of the Dev Guide at [1]. (lines 205-239)
    
    The copyButtonHandler method was manually created and had
    support from LLM/code completion. (lines 330-353)
    
    The patch is open to further suggestions for simplification
    and code improvement or refactoring.
    
    Some lessons learned: LLM cannot guess solutions for wide
    and open demands. Narrowing the query to code fragments
    produces acceptable and easily debuggable code.
    
    PS1: Added namespace{} to silence clang plugin
    PS2: Fix clang issue
    PS3: Fix clang issue
    PS4: Add M. Kaganski suggestions, fix clang issue
    
    [1]
    
https://wiki.documentfoundation.org/Documentation/DevGuide/Office_Development#Copying_Data
    
    Change-Id: Ia5c38978e9186f4c1171fafb6a415bf33e81d123
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191201
    Tested-by: Jenkins
    Reviewed-by: Olivier Hallot <[email protected]>

diff --git a/cui/source/dialogs/screenshotannotationdlg.cxx 
b/cui/source/dialogs/screenshotannotationdlg.cxx
index 9a83237821c1..ffe0402e7770 100644
--- a/cui/source/dialogs/screenshotannotationdlg.cxx
+++ b/cui/source/dialogs/screenshotannotationdlg.cxx
@@ -26,8 +26,14 @@
 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/datatransfer/DataFlavor.hpp>
+#include <com/sun/star/uno/Any.hxx>
 
 #include <comphelper/random.hxx>
+#include <comphelper/processfactory.hxx>
+
 #include <basegfx/polygon/b2dpolygontools.hxx>
 #include <sfx2/filedlghelper.hxx>
 #include <tools/stream.hxx>
@@ -40,10 +46,14 @@
 #include <vcl/salgtype.hxx>
 #include <vcl/virdev.hxx>
 #include <vcl/weld.hxx>
+
 #include <svtools/optionsdrawinglayer.hxx>
 #include <basegfx/matrix/b2dhommatrix.hxx>
 #include <set>
 #include <string_view>
+#include <sot/formats.hxx>
+#include <vcl/transfer.hxx>
+#include <cppuhelper/implbase.hxx>
 
 using namespace com::sun::star;
 
@@ -142,6 +152,9 @@ private:
     // Handler for click on save
     DECL_LINK(saveButtonHandler, weld::Button&, void);
 
+    // Handler for click on copy
+    DECL_LINK(copyButtonHandler, weld::Button&, void);
+
     // helper methods
     weld::ScreenShotEntry* CheckHit(const basegfx::B2IPoint& rPosition);
     void PaintScreenShotEntry(
@@ -178,6 +191,7 @@ private:
     std::unique_ptr<weld::CustomWeld> mxPicture;
     std::unique_ptr<weld::TextView> mxText;
     std::unique_ptr<weld::Button> mxSave;
+    std::unique_ptr<weld::Button> mxCopy;
 
     // save as text
     OUString                    maSaveAsText;
@@ -190,6 +204,39 @@ public:
     bool MouseMove(const MouseEvent& rMouseEvent);
     bool MouseButtonUp();
 };
+namespace
+{
+class BitmapTransferable : public TransferableHelper
+{
+private:
+    Bitmap maBitmap;
+
+protected:
+    // Add supported formats for the Bitmap
+    virtual void AddSupportedFormats() override
+    {
+        AddFormat(SotClipboardFormatId::BITMAP);
+    }
+    // Provide the Bitmap data for the requested DataFlavor
+    virtual bool GetData(const css::datatransfer::DataFlavor& rFlavor, const 
OUString& ) override
+    {
+        if (SotExchange::GetFormat(rFlavor) == SotClipboardFormatId::BITMAP)
+        {
+            SetBitmap(maBitmap, rFlavor);
+            return true;
+        }
+        return false;
+    }
+
+public:
+    // Constructor to initialize the Bitmap
+    explicit BitmapTransferable(const Bitmap& rBitmap)
+        : maBitmap(rBitmap)
+    {
+    }
+};
+}
+
 
 OUString ScreenshotAnnotationDlg_Impl::maLastFolderURL = OUString();
 
@@ -221,6 +268,9 @@ ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl(
     assert(mxText);
     mxSave = rParentBuilder.weld_button(u"save"_ustr);
     assert(mxSave);
+    mxCopy = rParentBuilder.weld_button(u"copy"_ustr);
+    assert(mxCopy);
+
 
     // set screenshot image at DrawingArea, resize, set event listener
     if (mxPicture)
@@ -237,7 +287,7 @@ ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl(
         mxVirtualBufferDevice->SetFillColor(COL_TRANSPARENT);
 
         // initially set image for picture control
-        mxVirtualBufferDevice->DrawBitmapEx(Point(0, 0), maDimmedDialogBitmap);
+        mxVirtualBufferDevice->DrawBitmap(Point(0, 0), maDimmedDialogBitmap);
 
         // set size for picture control, this will re-layout so that
         // the picture control shows the whole dialog
@@ -264,6 +314,10 @@ ScreenshotAnnotationDlg_Impl::ScreenshotAnnotationDlg_Impl(
     {
         mxSave->connect_clicked(LINK(this, ScreenshotAnnotationDlg_Impl, 
saveButtonHandler));
     }
+    if(mxCopy)
+    {
+        mxCopy->connect_clicked(LINK(this, ScreenshotAnnotationDlg_Impl, 
copyButtonHandler));
+    }
 }
 
 ScreenshotAnnotationDlg_Impl::~ScreenshotAnnotationDlg_Impl()
@@ -271,6 +325,31 @@ 
ScreenshotAnnotationDlg_Impl::~ScreenshotAnnotationDlg_Impl()
     mxVirtualBufferDevice.disposeAndClear();
 }
 
+IMPL_LINK_NOARG(ScreenshotAnnotationDlg_Impl, copyButtonHandler, 
weld::Button&, void)
+{
+    // Repaint the buffer to get the latest Bitmap
+    RepaintToBuffer();
+
+    // Extract the Bitmap
+    const Bitmap aTargetBitmap(
+        mxVirtualBufferDevice->GetBitmap(
+            Point(0, 0),
+            mxVirtualBufferDevice->GetOutputSizePixel()));
+
+    // Create a BitmapTransferable
+    rtl::Reference<TransferableHelper> xClipCntnr = new 
BitmapTransferable(aTargetBitmap);
+    // Get the system clipboard
+    css::uno::Reference<css::datatransfer::clipboard::XClipboard> xClipboard =
+        css::datatransfer::clipboard::SystemClipboard::create(
+            comphelper::getProcessComponentContext());
+
+    if (xClipboard.is())
+    {
+        // Copy the BitmapTransferable to the clipboard
+        xClipboard->setContents(xClipCntnr, nullptr);
+    }
+}
+
 IMPL_LINK_NOARG(ScreenshotAnnotationDlg_Impl, saveButtonHandler, 
weld::Button&, void)
 {
     // 'save screenshot...' pressed, offer to save maParentDialogBitmap
@@ -433,7 +512,7 @@ void ScreenshotAnnotationDlg_Impl::RepaintToBuffer(
         return;
 
     // reset with original screenshot bitmap
-    mxVirtualBufferDevice->DrawBitmapEx(
+    mxVirtualBufferDevice->DrawBitmap(
         Point(0, 0),
         bUseDimmed ? maDimmedDialogBitmap : maParentDialogBitmap);
 
diff --git a/cui/uiconfig/ui/screenshotannotationdialog.ui 
b/cui/uiconfig/ui/screenshotannotationdialog.ui
index 346af2f59288..a6cde9a09973 100644
--- a/cui/uiconfig/ui/screenshotannotationdialog.ui
+++ b/cui/uiconfig/ui/screenshotannotationdialog.ui
@@ -1,40 +1,52 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.1 -->
+<!-- Generated with glade 3.40.0 -->
 <interface domain="cui">
   <requires lib="gtk+" version="3.20"/>
   <object class="GtkDialog" id="ScreenshotAnnotationDialog">
-    <property name="can_focus">False</property>
-    <property name="border_width">6</property>
+    <property name="can-focus">False</property>
+    <property name="border-width">6</property>
     <property name="title" translatable="yes" 
context="screenshotannotationdialog|ScreenshotAnnotationDialog">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>
-      <placeholder/>
-    </child>
+    <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="can-focus">False</property>
         <property name="orientation">vertical</property>
         <property name="spacing">12</property>
         <child internal-child="action_area">
           <object class="GtkButtonBox" id="dialog-action_area1">
-            <property name="can_focus">False</property>
-            <property name="layout_style">end</property>
+            <property name="can-focus">False</property>
+            <property name="layout-style">end</property>
+            <child>
+              <object class="GtkButton" id="copy">
+                <property name="label" translatable="yes" 
context="screenshotannotationdialog|copy">C_opy Image</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="receives-default">True</property>
+                <property name="use-underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
             <child>
               <object class="GtkButton" id="cancel">
                 <property name="label" translatable="yes" 
context="stock">_Cancel</property>
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="has_focus">True</property>
-                <property name="receives_default">True</property>
+                <property name="can-focus">True</property>
+                <property name="has-focus">True</property>
+                <property name="receives-default">True</property>
                 <property name="use-underline">True</property>
               </object>
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">True</property>
-                <property name="position">1</property>
+                <property name="position">2</property>
               </packing>
             </child>
             <child>
@@ -42,15 +54,15 @@
                 <property name="label" translatable="yes" 
context="screenshotannotationdialog|save">Save Screenshot...</property>
                 <property name="name">save</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="can-focus">True</property>
+                <property name="can-default">True</property>
+                <property name="has-default">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="position">3</property>
                 <property name="secondary">True</property>
               </packing>
             </child>
@@ -58,18 +70,18 @@
           <packing>
             <property name="expand">False</property>
             <property name="fill">True</property>
-            <property name="pack_type">end</property>
+            <property name="pack-type">end</property>
             <property name="position">0</property>
           </packing>
         </child>
         <child>
           <object class="GtkLabel" id="label2">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
+            <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>
+            <property name="use-underline">True</property>
+            <property name="mnemonic-widget">picture</property>
           </object>
           <packing>
             <property name="expand">False</property>
@@ -81,7 +93,7 @@
           <object class="GtkDrawingArea" id="picture">
             <property name="name">image</property>
             <property name="visible">True</property>
-            <property name="can_focus">False</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>
@@ -93,11 +105,11 @@
         <child>
           <object class="GtkLabel" id="label1">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
+            <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="use-underline">True</property>
+            <property name="mnemonic-widget">text</property>
             <property name="ellipsize">end</property>
           </object>
           <packing>
@@ -109,14 +121,14 @@
         <child>
           <object class="GtkScrolledWindow" id="scrolledwindow1">
             <property name="visible">True</property>
-            <property name="can_focus">True</property>
+            <property name="can-focus">True</property>
             <property name="hexpand">True</property>
             <property name="vexpand">True</property>
-            <property name="shadow_type">in</property>
+            <property name="shadow-type">in</property>
             <child>
               <object class="GtkTextView" id="text">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
+                <property name="can-focus">True</property>
                 <property name="hexpand">True</property>
                 <property name="vexpand">True</property>
               </object>

Reply via email to