[Libreoffice-commits] core.git: Branch 'libreoffice-7-0' - editeng/source include/editeng include/svx include/vcl svx/source vcl/source vcl/unx

Caolán McNamara (via logerrit) logerrit at kemper.freedesktop.org
Tue Oct 6 10:44:02 UTC 2020


 editeng/source/editeng/impedit2.cxx |   11 -
 include/editeng/editview.hxx        |    3 
 include/svx/svdedxv.hxx             |    1 
 include/svx/weldeditview.hxx        |    9 +
 include/vcl/customweld.hxx          |    7 
 include/vcl/layout.hxx              |    7 
 include/vcl/weld.hxx                |   27 +++
 svx/source/dialog/weldeditview.cxx  |   23 +++
 svx/source/svdraw/svdedxv.cxx       |    7 
 vcl/source/app/customweld.cxx       |    6 
 vcl/source/app/salvtables.cxx       |   14 +
 vcl/source/window/layout.cxx        |   17 ++
 vcl/unx/gtk3/gtk3gtkinst.cxx        |  261 +++++++++++++++++++++++++++++++++++-
 13 files changed, 383 insertions(+), 10 deletions(-)

New commits:
commit 046a43559ba3c5ff53c364a69c99e70357c22e60
Author:     Caolán McNamara <caolanm at redhat.com>
AuthorDate: Wed Sep 30 14:42:10 2020 +0100
Commit:     Miklos Vajna <vmiklos at collabora.com>
CommitDate: Tue Oct 6 12:43:27 2020 +0200

    tdf#134566 gtk IM support for custom widgets
    
    Change-Id: I5c731161768d09d021db5c353de816e173159096
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103764
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103991
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>

diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx
index eb9ef5eca2c3..0cf19653e0d1 100644
--- a/editeng/source/editeng/impedit2.cxx
+++ b/editeng/source/editeng/impedit2.cxx
@@ -478,11 +478,12 @@ void ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView )
                 if ( nInputEnd > rLine.GetEnd() )
                     nInputEnd = rLine.GetEnd();
                 tools::Rectangle aR2 = PaMtoEditCursor( EditPaM( aPaM.GetNode(), nInputEnd ), GetCursorFlags::EndOfLine );
-                if (vcl::Window* pWindow = pView->GetWindow())
-                {
-                    tools::Rectangle aRect = pView->GetImpEditView()->GetWindowPos( aR1 );
-                    pWindow->SetCursorRect( &aRect, aR2.Left()-aR1.Right() );
-                }
+                tools::Rectangle aRect = pView->GetImpEditView()->GetWindowPos( aR1 );
+                auto nExtTextInputWidth = aR2.Left() - aR1.Right();
+                if (EditViewCallbacks* pEditViewCallbacks = pView->getEditViewCallbacks())
+                    pEditViewCallbacks->EditViewCursorRect(aRect, nExtTextInputWidth);
+                else if (vcl::Window* pWindow = pView->GetWindow())
+                    pWindow->SetCursorRect(&aRect, nExtTextInputWidth);
             }
         }
         else
diff --git a/include/editeng/editview.hxx b/include/editeng/editview.hxx
index 41d707127258..c750dcef9254 100644
--- a/include/editeng/editview.hxx
+++ b/include/editeng/editview.hxx
@@ -111,6 +111,9 @@ public:
     // Triggered to update InputEngine context information
     virtual void EditViewInputContext(const InputContext& rInputContext) = 0;
 
+    // Triggered to update InputEngine cursor position
+    virtual void EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth) = 0;
+
     // implemented if drag and drop support is wanted
     virtual css::uno::Reference<css::datatransfer::dnd::XDropTarget> GetDropTarget()
     {
diff --git a/include/svx/svdedxv.hxx b/include/svx/svdedxv.hxx
index feb27e347057..4684cd4318c5 100644
--- a/include/svx/svdedxv.hxx
+++ b/include/svx/svdedxv.hxx
@@ -69,6 +69,7 @@ class SVXCORE_DLLPUBLIC SdrObjEditView : public SdrGlueEditView, public EditView
     virtual void EditViewSelectionChange() override;
     virtual OutputDevice& EditViewOutputDevice() const override;
     virtual void EditViewInputContext(const InputContext& rInputContext) override;
+    virtual void EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth) override;
 
     // The OverlayObjects used for visualizing active TextEdit (currently
     // using TextEditOverlayObject, but not limited to it
diff --git a/include/svx/weldeditview.hxx b/include/svx/weldeditview.hxx
index 3a96014628c4..4edda521b9b1 100644
--- a/include/svx/weldeditview.hxx
+++ b/include/svx/weldeditview.hxx
@@ -25,6 +25,8 @@ class SVX_DLLPUBLIC WeldEditView : public weld::CustomWidgetController, public E
 public:
     WeldEditView();
     virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+    virtual int GetSurroundingText(OUString& rSurrounding) override;
+    virtual bool DeleteSurroundingText(const Selection& rRange) override;
 
     void SetText(const OUString& rStr) { m_xEditEngine->SetText(rStr); }
 
@@ -71,6 +73,13 @@ protected:
     {
         SetInputContext(rInputContext);
     }
+
+    virtual void EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth) override
+    {
+        OutputDevice& rRefDevice = EditViewOutputDevice();
+        SetCursorRect(rRefDevice.LogicToPixel(rRect),
+                      rRefDevice.LogicToPixel(Size(nExtTextInputWidth, 0)).Width());
+    }
 };
 
 #endif // INCLUDED_SVX_WELDEDITVIEW_HXX
diff --git a/include/vcl/customweld.hxx b/include/vcl/customweld.hxx
index 0c3bd0a142b8..3efcc51d2a90 100644
--- a/include/vcl/customweld.hxx
+++ b/include/vcl/customweld.hxx
@@ -88,6 +88,12 @@ public:
     {
         m_pDrawingArea->set_input_context(rInputContext);
     }
+    void SetCursorRect(const tools::Rectangle& rCursorRect, int nExtTextInputWidth)
+    {
+        m_pDrawingArea->im_context_set_cursor_location(rCursorRect, nExtTextInputWidth);
+    }
+    virtual int GetSurroundingText(OUString& /*rSurrounding*/) { return -1; }
+    virtual bool DeleteSurroundingText(const Selection& /*rRange*/) { return false; }
     void SetDragDataTransferrable(rtl::Reference<TransferDataContainer>& rTransferrable,
                                   sal_uInt8 eDNDConstants)
     {
@@ -136,6 +142,7 @@ private:
     DECL_LINK(DoCommand, const CommandEvent&, bool);
     DECL_LINK(DoStyleUpdated, weld::Widget&, void);
     DECL_LINK(DoRequestHelp, tools::Rectangle&, OUString);
+    DECL_LINK(DoGetSurrounding, OUString&, int);
 
 public:
     CustomWeld(weld::Builder& rBuilder, const OString& rDrawingId,
diff --git a/include/vcl/layout.hxx b/include/vcl/layout.hxx
index 8a5b881644e4..179a0342150a 100644
--- a/include/vcl/layout.hxx
+++ b/include/vcl/layout.hxx
@@ -643,6 +643,7 @@ private:
     Link<VclDrawingArea&, void> m_aStyleUpdatedHdl;
     Link<const CommandEvent&, bool> m_aCommandHdl;
     Link<tools::Rectangle&, OUString> m_aQueryTooltipHdl;
+    Link<OUString&, int> m_aGetSurroundingHdl;
     Link<VclDrawingArea*, bool> m_aStartDragHdl;
 
     virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override
@@ -741,6 +742,8 @@ public:
     {
         SetBackground();
     }
+    virtual OUString GetSurroundingText() const override;
+    virtual Selection GetSurroundingTextSelection() const override;
     void SetUITestFactory(FactoryFunction pFactoryFunction, void* pUserData)
     {
         m_pFactoryFunction = pFactoryFunction;
@@ -790,6 +793,10 @@ public:
     {
         m_aQueryTooltipHdl = rLink;
     }
+    void SetGetSurroundingHdl(const Link<OUString&, int>& rLink)
+    {
+        m_aGetSurroundingHdl = rLink;
+    }
     void SetStartDragHdl(const Link<VclDrawingArea*, bool>& rLink)
     {
         m_aStartDragHdl = rLink;
diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx
index 458eb13b83f6..0d58711d1dc7 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -2028,12 +2028,28 @@ protected:
     Link<tools::Rectangle&, OUString> m_aQueryTooltipHdl;
     // if handler returns true, drag is disallowed
     Link<DrawingArea&, bool> m_aDragBeginHdl;
+    // return position of cursor, fill OUString& with surrounding text
+    Link<OUString&, int> m_aGetSurroundingHdl;
+    // attempt to delete the range, return true if successful
+    Link<const Selection&, bool> m_aDeleteSurroundingHdl;
 
     OUString signal_query_tooltip(tools::Rectangle& rHelpArea)
     {
         return m_aQueryTooltipHdl.Call(rHelpArea);
     }
 
+    int signal_im_context_get_surrounding(OUString& rSurroundingText)
+    {
+        if (!m_aGetSurroundingHdl.IsSet())
+            return -1;
+        return m_aGetSurroundingHdl.Call(rSurroundingText);
+    }
+
+    bool signal_im_context_delete_surrounding(const Selection& rRange)
+    {
+        return m_aDeleteSurroundingHdl.Call(rRange);
+    }
+
 public:
     void connect_draw(const Link<draw_args, void>& rLink) { m_aDrawHdl = rLink; }
     void connect_style_updated(const Link<Widget&, void>& rLink) { m_aStyleUpdatedHdl = rLink; }
@@ -2047,6 +2063,14 @@ public:
         m_aQueryTooltipHdl = rLink;
     }
     void connect_drag_begin(const Link<DrawingArea&, bool>& rLink) { m_aDragBeginHdl = rLink; }
+    void connect_im_context_get_surrounding(const Link<OUString&, int>& rLink)
+    {
+        m_aGetSurroundingHdl = rLink;
+    }
+    void connect_im_context_delete_surrounding(const Link<const Selection&, bool>& rLink)
+    {
+        m_aDeleteSurroundingHdl = rLink;
+    }
     virtual void queue_draw() = 0;
     virtual void queue_draw_area(int x, int y, int width, int height) = 0;
     virtual void queue_resize() = 0;
@@ -2058,6 +2082,9 @@ public:
     virtual void set_cursor(PointerStyle ePointerStyle) = 0;
 
     virtual void set_input_context(const InputContext& rInputContext) = 0;
+    virtual void im_context_set_cursor_location(const tools::Rectangle& rCursorRect,
+                                                int nExtTextInputWidth)
+        = 0;
 
     // use return here just to generate matching VirtualDevices
     virtual OutputDevice& get_ref_device() = 0;
diff --git a/svx/source/dialog/weldeditview.cxx b/svx/source/dialog/weldeditview.cxx
index 1ac95622af42..12c6a94cd740 100644
--- a/svx/source/dialog/weldeditview.cxx
+++ b/svx/source/dialog/weldeditview.cxx
@@ -1427,6 +1427,29 @@ void WeldEditView::SetDrawingArea(weld::DrawingArea* pDrawingArea)
         m_xAccessible->Init(m_xEditEngine.get(), m_xEditView.get());
 }
 
+int WeldEditView::GetSurroundingText(OUString& rSurrounding)
+{
+    rSurrounding = m_xEditView->GetSurroundingText();
+    return m_xEditView->GetSurroundingTextSelection().Min();
+}
+
+bool WeldEditView::DeleteSurroundingText(const Selection& rRange)
+{
+    bool bRes(false);
+    EditEngine* pEditEngine = m_xEditView->GetEditEngine();
+    if (pEditEngine)
+    {
+        ESelection aSel(m_xEditView->GetSelection());
+        aSel.nEndPara = aSel.nStartPara;
+        aSel.nStartPos = rRange.Min();
+        aSel.nEndPos = rRange.Max();
+        pEditEngine->QuickDelete(aSel);
+        pEditEngine->QuickFormatDoc();
+        bRes = true;
+    }
+    return bRes;
+}
+
 void WeldEditView::GetFocus()
 {
     m_xEditView->ShowCursor();
diff --git a/svx/source/svdraw/svdedxv.cxx b/svx/source/svdraw/svdedxv.cxx
index 851a73043535..647fe0470511 100644
--- a/svx/source/svdraw/svdedxv.cxx
+++ b/svx/source/svdraw/svdedxv.cxx
@@ -669,6 +669,13 @@ void SdrObjEditView::EditViewInputContext(const InputContext& rInputContext)
     pTextEditWin->SetInputContext(rInputContext);
 }
 
+void SdrObjEditView::EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth)
+{
+    if (!pTextEditWin)
+        return;
+    pTextEditWin->SetCursorRect(&rRect, nExtTextInputWidth);
+}
+
 void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow)
 {
     if (!comphelper::LibreOfficeKit::isActive())
diff --git a/vcl/source/app/customweld.cxx b/vcl/source/app/customweld.cxx
index 07dc7935b198..c08a38208312 100644
--- a/vcl/source/app/customweld.cxx
+++ b/vcl/source/app/customweld.cxx
@@ -37,6 +37,7 @@ CustomWeld::CustomWeld(weld::Builder& rBuilder, const OString& rDrawingId,
     m_xDrawingArea->connect_style_updated(LINK(this, CustomWeld, DoStyleUpdated));
     m_xDrawingArea->connect_command(LINK(this, CustomWeld, DoCommand));
     m_xDrawingArea->connect_query_tooltip(LINK(this, CustomWeld, DoRequestHelp));
+    m_xDrawingArea->connect_im_context_get_surrounding(LINK(this, CustomWeld, DoGetSurrounding));
     m_rWidgetController.SetDrawingArea(m_xDrawingArea.get());
 }
 
@@ -94,6 +95,11 @@ IMPL_LINK(CustomWeld, DoRequestHelp, tools::Rectangle&, rHelpArea, OUString)
 {
     return m_rWidgetController.RequestHelp(rHelpArea);
 }
+
+IMPL_LINK(CustomWeld, DoGetSurrounding, OUString&, rSurrounding, int)
+{
+    return m_rWidgetController.GetSurroundingText(rSurrounding);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index 7e80bc2b053d..3108ff5a9440 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -5501,6 +5501,7 @@ private:
     DECL_LINK(StyleUpdatedHdl, VclDrawingArea&, void);
     DECL_LINK(CommandHdl, const CommandEvent&, bool);
     DECL_LINK(QueryTooltipHdl, tools::Rectangle&, OUString);
+    DECL_LINK(GetSurroundingHdl, OUString&, int);
     DECL_LINK(StartDragHdl, VclDrawingArea*, bool);
 
     // SalInstanceWidget has a generic listener for all these
@@ -5545,6 +5546,7 @@ public:
         m_xDrawingArea->SetStyleUpdatedHdl(LINK(this, SalInstanceDrawingArea, StyleUpdatedHdl));
         m_xDrawingArea->SetCommandHdl(LINK(this, SalInstanceDrawingArea, CommandHdl));
         m_xDrawingArea->SetQueryTooltipHdl(LINK(this, SalInstanceDrawingArea, QueryTooltipHdl));
+        m_xDrawingArea->SetGetSurroundingHdl(LINK(this, SalInstanceDrawingArea, GetSurroundingHdl));
         m_xDrawingArea->SetStartDragHdl(LINK(this, SalInstanceDrawingArea, StartDragHdl));
     }
 
@@ -5582,6 +5584,12 @@ public:
         m_xDrawingArea->SetInputContext(rInputContext);
     }
 
+    virtual void im_context_set_cursor_location(const tools::Rectangle& rCursorRect, int nExtTextInputWidth) override
+    {
+        tools::Rectangle aCursorRect = m_xDrawingArea->PixelToLogic(rCursorRect);
+        m_xDrawingArea->SetCursorRect(&aCursorRect, m_xDrawingArea->PixelToLogic(Size(nExtTextInputWidth, 0)).Width());
+    }
+
     virtual a11yref get_accessible_parent() override
     {
         vcl::Window* pParent = m_xDrawingArea->GetParent();
@@ -5632,6 +5640,7 @@ public:
 
     virtual ~SalInstanceDrawingArea() override
     {
+        m_xDrawingArea->SetGetSurroundingHdl(Link<OUString&, int>());
         m_xDrawingArea->SetQueryTooltipHdl(Link<tools::Rectangle&, OUString>());
         m_xDrawingArea->SetCommandHdl(Link<const CommandEvent&, bool>());
         m_xDrawingArea->SetStyleUpdatedHdl(Link<VclDrawingArea&, void>());
@@ -5698,6 +5707,11 @@ IMPL_LINK(SalInstanceDrawingArea, CommandHdl, const CommandEvent&, rEvent, bool)
     return m_aCommandHdl.Call(rEvent);
 }
 
+IMPL_LINK(SalInstanceDrawingArea, GetSurroundingHdl, OUString&, rSurrounding, int)
+{
+    return m_aGetSurroundingHdl.Call(rSurrounding);
+}
+
 IMPL_LINK(SalInstanceDrawingArea, QueryTooltipHdl, tools::Rectangle&, rHelpArea, OUString)
 {
     return m_aQueryTooltipHdl.Call(rHelpArea);
diff --git a/vcl/source/window/layout.cxx b/vcl/source/window/layout.cxx
index 77009a9b3cc8..080acd5f9901 100644
--- a/vcl/source/window/layout.cxx
+++ b/vcl/source/window/layout.cxx
@@ -2844,6 +2844,23 @@ void VclDrawingArea::StartDrag(sal_Int8, const Point&)
     xContainer->StartDrag(this, m_nDragAction);
 }
 
+OUString VclDrawingArea::GetSurroundingText() const
+{
+    OUString sSurroundingText;
+    if (m_aGetSurroundingHdl.Call(sSurroundingText) != -1)
+        return sSurroundingText;
+    return Control::GetSurroundingText();
+}
+
+Selection VclDrawingArea::GetSurroundingTextSelection() const
+{
+    OUString sSurroundingText;
+    int nCursor = m_aGetSurroundingHdl.Call(sSurroundingText);
+    if (nCursor != -1)
+        return Selection(nCursor, nCursor);
+    return Control::GetSurroundingTextSelection();
+}
+
 VclHPaned::~VclHPaned()
 {
 }
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index 77af86f2e3f2..792b9381d7d5 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -12461,7 +12461,10 @@ public:
     }
 };
 
-    AtkObject* (*default_drawing_area_get_accessible)(GtkWidget *widget);
+// IMHandler
+class IMHandler;
+
+AtkObject* (*default_drawing_area_get_accessible)(GtkWidget *widget);
 
 class GtkInstanceDrawingArea : public GtkInstanceWidget, public virtual weld::DrawingArea
 {
@@ -12470,6 +12473,7 @@ private:
     a11yref m_xAccessible;
     AtkObject *m_pAccessible;
     ScopedVclPtrInstance<VirtualDevice> m_xDevice;
+    std::unique_ptr<IMHandler> m_xIMHandler;
     cairo_surface_t* m_pSurface;
     gulong m_nDrawSignalId;
     gulong m_nStyleUpdatedSignalId;
@@ -12543,7 +12547,7 @@ private:
     }
     virtual bool signal_popup_menu(const CommandEvent& rCEvt) override
     {
-        return m_aCommandHdl.Call(rCEvt);
+        return signal_command(rCEvt);
     }
     bool signal_scroll(GdkEventScroll* pEvent)
     {
@@ -12602,7 +12606,8 @@ public:
         {
             GtkWidget* pParent = gtk_widget_get_parent(m_pWidget);
             m_pAccessible = atk_object_wrapper_new(m_xAccessible, gtk_widget_get_accessible(pParent), pDefaultAccessible);
-            g_object_ref(m_pAccessible);
+            if (m_pAccessible)
+                g_object_ref(m_pAccessible);
         }
         return m_pAccessible;
     }
@@ -12621,9 +12626,18 @@ public:
         gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(m_pDrawingArea)), pCursor);
     }
 
-    virtual void set_input_context(const InputContext& /*rInputContext*/) override
+    virtual void set_input_context(const InputContext& rInputContext) override;
+
+    virtual void im_context_set_cursor_location(const tools::Rectangle& rCursorRect, int nExtTextInputWidth) override;
+
+    int im_context_get_surrounding(OUString& rSurroundingText)
+    {
+        return signal_im_context_get_surrounding(rSurroundingText);
+    }
+
+    bool im_context_delete_surrounding(const Selection& rRange)
     {
-        // TODO follow up for the gtk case
+        return signal_im_context_delete_surrounding(rRange);
     }
 
     virtual void queue_draw() override
@@ -12724,8 +12738,245 @@ public:
     {
         return *m_xDevice;
     }
+
+    bool signal_command(const CommandEvent& rCEvt)
+    {
+        return m_aCommandHdl.Call(rCEvt);
+    }
 };
 
+class IMHandler
+{
+private:
+    GtkInstanceDrawingArea* m_pArea;
+    GtkIMContext* m_pIMContext;
+    OUString m_sPreeditText;
+    gulong m_nFocusInSignalId;
+    gulong m_nFocusOutSignalId;
+    bool m_bExtTextInput;
+
+public:
+    IMHandler(GtkInstanceDrawingArea* pArea)
+        : m_pArea(pArea)
+        , m_pIMContext(gtk_im_multicontext_new())
+        , m_nFocusInSignalId(g_signal_connect(m_pArea->getWidget(), "focus-in-event", G_CALLBACK(signalFocusIn), this))
+        , m_nFocusOutSignalId(g_signal_connect(m_pArea->getWidget(), "focus-out-event", G_CALLBACK(signalFocusOut), this))
+        , m_bExtTextInput(false)
+    {
+        g_signal_connect(m_pIMContext, "preedit-start", G_CALLBACK(signalIMPreeditStart), this);
+        g_signal_connect(m_pIMContext, "preedit-end", G_CALLBACK(signalIMPreeditEnd), this);
+        g_signal_connect(m_pIMContext, "commit", G_CALLBACK(signalIMCommit), this);
+        g_signal_connect(m_pIMContext, "preedit-changed", G_CALLBACK(signalIMPreeditChanged), this);
+        g_signal_connect(m_pIMContext, "retrieve-surrounding", G_CALLBACK(signalIMRetrieveSurrounding), this);
+        g_signal_connect(m_pIMContext, "delete-surrounding", G_CALLBACK(signalIMDeleteSurrounding), this);
+
+        GtkWidget* pWidget = m_pArea->getWidget();
+        if (!gtk_widget_get_realized(pWidget))
+            gtk_widget_realize(pWidget);
+        GdkWindow* pWin = gtk_widget_get_window(pWidget);
+        gtk_im_context_set_client_window(m_pIMContext, pWin);
+        gtk_im_context_focus_in(m_pIMContext);
+    }
+
+    void signalFocus(bool bIn)
+    {
+        if (bIn)
+            gtk_im_context_focus_in(m_pIMContext);
+        else
+            gtk_im_context_focus_out(m_pIMContext);
+    }
+
+    static gboolean signalFocusIn(GtkWidget*, GdkEvent*, gpointer im_handler)
+    {
+        IMHandler* pThis = static_cast<IMHandler*>(im_handler);
+        pThis->signalFocus(true);
+        return false;
+    }
+
+    static gboolean signalFocusOut(GtkWidget*, GdkEvent*, gpointer im_handler)
+    {
+        IMHandler* pThis = static_cast<IMHandler*>(im_handler);
+        pThis->signalFocus(false);
+        return false;
+    }
+
+    ~IMHandler()
+    {
+        EndExtTextInput();
+
+        g_signal_handler_disconnect(m_pArea->getWidget(), m_nFocusOutSignalId);
+        g_signal_handler_disconnect(m_pArea->getWidget(), m_nFocusInSignalId);
+
+        // first give IC a chance to deinitialize
+        gtk_im_context_set_client_window(m_pIMContext, nullptr);
+        // destroy old IC
+        g_object_unref(m_pIMContext);
+    }
+
+    void updateIMSpotLocation()
+    {
+        CommandEvent aCEvt(Point(), CommandEventId::CursorPos);
+        // we expect set_cursor_location to get triggered by this
+        m_pArea->signal_command(aCEvt);
+    }
+
+    void set_cursor_location(const tools::Rectangle& rRect)
+    {
+        GdkRectangle aArea{static_cast<int>(rRect.Left()), static_cast<int>(rRect.Top()),
+                           static_cast<int>(rRect.GetWidth()), static_cast<int>(rRect.GetHeight())};
+        gtk_im_context_set_cursor_location(m_pIMContext, &aArea);
+    }
+
+    static void signalIMCommit(GtkIMContext* /*pContext*/, gchar* pText, gpointer im_handler)
+    {
+        IMHandler* pThis = static_cast<IMHandler*>(im_handler);
+
+        // at least editeng expects to have seen a start before accepting a commit
+        pThis->StartExtTextInput();
+
+        OUString sText(pText, strlen(pText), RTL_TEXTENCODING_UTF8);
+        CommandExtTextInputData aData(sText, nullptr, sText.getLength(), 0, false);
+        CommandEvent aCEvt(Point(), CommandEventId::ExtTextInput, false, &aData);
+        pThis->m_pArea->signal_command(aCEvt);
+
+        pThis->updateIMSpotLocation();
+
+        pThis->EndExtTextInput();
+
+        pThis->m_sPreeditText.clear();
+    }
+
+    static void signalIMPreeditChanged(GtkIMContext* pIMContext, gpointer im_handler)
+    {
+        IMHandler* pThis = static_cast<IMHandler*>(im_handler);
+
+        sal_Int32 nCursorPos(0);
+        sal_uInt8 nCursorFlags(0);
+        std::vector<ExtTextInputAttr> aInputFlags;
+        OUString sText = GtkSalFrame::GetPreeditDetails(pIMContext, aInputFlags, nCursorPos, nCursorFlags);
+
+        // change from nothing to nothing -> do not start preedit e.g. this
+        // will activate input into a calc cell without user input
+        if (sText.isEmpty() && pThis->m_sPreeditText.isEmpty())
+            return;
+
+        pThis->m_sPreeditText = sText;
+
+        CommandExtTextInputData aData(sText, aInputFlags.data(), nCursorPos, nCursorFlags, false);
+        CommandEvent aCEvt(Point(), CommandEventId::ExtTextInput, false, &aData);
+        pThis->m_pArea->signal_command(aCEvt);
+
+        pThis->updateIMSpotLocation();
+    }
+
+    static gboolean signalIMRetrieveSurrounding(GtkIMContext* pContext, gpointer im_handler)
+    {
+        IMHandler* pThis = static_cast<IMHandler*>(im_handler);
+
+        OUString sSurroundingText;
+        int nCursorIndex = pThis->m_pArea->im_context_get_surrounding(sSurroundingText);
+
+        if (nCursorIndex != -1)
+        {
+            OString sUTF = OUStringToOString(sSurroundingText, RTL_TEXTENCODING_UTF8);
+            OUString sCursorText(sSurroundingText.copy(0, nCursorIndex));
+            gtk_im_context_set_surrounding(pContext, sUTF.getStr(), sUTF.getLength(),
+                OUStringToOString(sCursorText, RTL_TEXTENCODING_UTF8).getLength());
+        }
+
+        return true;
+    }
+
+    static gboolean signalIMDeleteSurrounding(GtkIMContext*, gint nOffset, gint nChars,
+        gpointer im_handler)
+    {
+        bool bRet = false;
+
+        IMHandler* pThis = static_cast<IMHandler*>(im_handler);
+
+        OUString sSurroundingText;
+        sal_Int32 nCursorIndex = pThis->m_pArea->im_context_get_surrounding(sSurroundingText);
+
+        if (nCursorIndex != -1)
+        {
+            // Note that offset and n_chars are in characters not in bytes
+            // which differs from the usage other places in GtkIMContext
+
+            if (nOffset > 0)
+            {
+                while (nCursorIndex < sSurroundingText.getLength())
+                    sSurroundingText.iterateCodePoints(&nCursorIndex, 1);
+            }
+            else if (nOffset < 0)
+            {
+                while (nCursorIndex > 0)
+                    sSurroundingText.iterateCodePoints(&nCursorIndex, -1);
+            }
+
+            sal_Int32 nCursorEndIndex(nCursorIndex);
+            sal_Int32 nCount(0);
+            while (nCount < nChars && nCursorEndIndex < sSurroundingText.getLength())
+                ++nCount;
+
+            bRet = pThis->m_pArea->im_context_delete_surrounding(Selection(nCursorIndex, nCursorEndIndex));
+        }
+
+        return bRet;
+    }
+
+    void StartExtTextInput()
+    {
+        if (m_bExtTextInput)
+            return;
+        CommandEvent aCEvt(Point(), CommandEventId::StartExtTextInput);
+        m_pArea->signal_command(aCEvt);
+        m_bExtTextInput = true;
+    }
+
+    static void signalIMPreeditStart(GtkIMContext*, gpointer im_handler)
+    {
+        IMHandler* pThis = static_cast<IMHandler*>(im_handler);
+        pThis->StartExtTextInput();
+        pThis->updateIMSpotLocation();
+    }
+
+    void EndExtTextInput()
+    {
+        if (!m_bExtTextInput)
+            return;
+        CommandEvent aCEvt(Point(), CommandEventId::EndExtTextInput);
+        m_pArea->signal_command(aCEvt);
+        m_bExtTextInput = false;
+    }
+
+    static void signalIMPreeditEnd(GtkIMContext*, gpointer im_handler)
+    {
+        IMHandler* pThis = static_cast<IMHandler*>(im_handler);
+        pThis->updateIMSpotLocation();
+        pThis->EndExtTextInput();
+    }
+};
+
+void GtkInstanceDrawingArea::set_input_context(const InputContext& rInputContext)
+{
+    bool bUseIm(rInputContext.GetOptions() & InputContextFlags::Text);
+    if (!bUseIm)
+    {
+        m_xIMHandler.reset();
+        return;
+    }
+    // create a new im context
+    if (!m_xIMHandler)
+        m_xIMHandler.reset(new IMHandler(this));
+}
+
+void GtkInstanceDrawingArea::im_context_set_cursor_location(const tools::Rectangle& rCursorRect, int /*nExtTextInputWidth*/)
+{
+    if (!m_xIMHandler)
+        return;
+    m_xIMHandler->set_cursor_location(rCursorRect);
+}
+
 }
 
 namespace {


More information about the Libreoffice-commits mailing list