summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2020-09-30 14:42:10 +0100
committerCaolán McNamara <caolanm@redhat.com>2020-10-02 10:37:09 +0200
commite1ac2a940389f52e28264a623a6620a7a0d94d57 (patch)
treee8cf96b31091482bc628cf501ecbba4cf1096c05
parent1709a9b9a2433032b5acf262ef506272c33d5e63 (diff)
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@redhat.com>
-rw-r--r--editeng/source/editeng/impedit2.cxx11
-rw-r--r--include/editeng/editview.hxx3
-rw-r--r--include/svx/svdedxv.hxx1
-rw-r--r--include/svx/weldeditview.hxx9
-rw-r--r--include/vcl/customweld.hxx7
-rw-r--r--include/vcl/layout.hxx7
-rw-r--r--include/vcl/weld.hxx27
-rw-r--r--svx/source/dialog/weldeditview.cxx23
-rw-r--r--svx/source/svdraw/svdedxv.cxx7
-rw-r--r--vcl/inc/salvtables.hxx4
-rw-r--r--vcl/source/app/customweld.cxx6
-rw-r--r--vcl/source/app/salvtables.cxx13
-rw-r--r--vcl/source/window/layout.cxx17
-rw-r--r--vcl/unx/gtk3/gtk3gtkinst.cxx261
14 files changed, 386 insertions, 10 deletions
diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx
index 0887005cc8b1..471159845118 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 cac588af3243..ccc2be551ca9 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 3036836a999f..ce8bf4b1f480 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 65396e342b27..d54f2c9404a7 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); }
@@ -77,6 +79,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 0b79df8d1df1..8943110261bd 100644
--- a/include/vcl/customweld.hxx
+++ b/include/vcl/customweld.hxx
@@ -92,6 +92,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)
{
@@ -140,6 +146,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 519d9bf8688b..f69acab4b1b1 100644
--- a/include/vcl/layout.hxx
+++ b/include/vcl/layout.hxx
@@ -647,6 +647,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
@@ -745,6 +746,8 @@ public:
if (!m_aMouseReleaseHdl.Call(rMEvt))
Control::MouseButtonUp(rMEvt);
}
+ virtual OUString GetSurroundingText() const override;
+ virtual Selection GetSurroundingTextSelection() const override;
void SetUITestFactory(FactoryFunction pFactoryFunction, void* pUserData)
{
m_pFactoryFunction = pFactoryFunction;
@@ -794,6 +797,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 9b2950cc8b43..e12ef94eb71f 100644
--- a/include/vcl/weld.hxx
+++ b/include/vcl/weld.hxx
@@ -2038,12 +2038,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; }
@@ -2057,6 +2073,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;
@@ -2068,6 +2092,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 55dd8f2a0281..82c519dcc037 100644
--- a/svx/source/svdraw/svdedxv.cxx
+++ b/svx/source/svdraw/svdedxv.cxx
@@ -662,6 +662,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/inc/salvtables.hxx b/vcl/inc/salvtables.hxx
index 1bb1069500ff..391e61ea3c65 100644
--- a/vcl/inc/salvtables.hxx
+++ b/vcl/inc/salvtables.hxx
@@ -1125,6 +1125,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
@@ -1157,6 +1158,9 @@ public:
virtual void set_input_context(const InputContext& rInputContext) override;
+ virtual void im_context_set_cursor_location(const tools::Rectangle& rCursorRect,
+ int nExtTextInputWidth) override;
+
virtual a11yref get_accessible_parent() override;
virtual a11yrelationset get_accessible_relation_set() override;
diff --git a/vcl/source/app/customweld.cxx b/vcl/source/app/customweld.cxx
index e8ce7003532a..9a56aeb0acbd 100644
--- a/vcl/source/app/customweld.cxx
+++ b/vcl/source/app/customweld.cxx
@@ -38,6 +38,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));
}
IMPL_LINK(CustomWeld, DoResize, const Size&, rSize, void)
@@ -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 3a25bb5e432b..3670f5a1f709 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -5815,6 +5815,7 @@ SalInstanceDrawingArea::SalInstanceDrawingArea(VclDrawingArea* pDrawingArea, Sal
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));
}
@@ -5852,6 +5853,12 @@ void SalInstanceDrawingArea::set_input_context(const InputContext& rInputContext
m_xDrawingArea->SetInputContext(rInputContext);
}
+void SalInstanceDrawingArea::im_context_set_cursor_location(const tools::Rectangle& rCursorRect, int nExtTextInputWidth)
+{
+ tools::Rectangle aCursorRect = m_xDrawingArea->PixelToLogic(rCursorRect);
+ m_xDrawingArea->SetCursorRect(&aCursorRect, m_xDrawingArea->PixelToLogic(Size(nExtTextInputWidth, 0)).Width());
+}
+
a11yref SalInstanceDrawingArea::get_accessible_parent()
{
vcl::Window* pParent = m_xDrawingArea->GetParent();
@@ -5902,6 +5909,7 @@ void SalInstanceDrawingArea::enable_drag_source(rtl::Reference<TransferDataConta
SalInstanceDrawingArea::~SalInstanceDrawingArea()
{
+ 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>());
@@ -5972,6 +5980,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 8c2ff59f52d1..9e4831789f8d 100644
--- a/vcl/source/window/layout.cxx
+++ b/vcl/source/window/layout.cxx
@@ -2857,6 +2857,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 ea864fae5d8e..4282f17d5415 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -13105,7 +13105,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
{
@@ -13114,6 +13117,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;
@@ -13187,7 +13191,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)
{
@@ -13246,7 +13250,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;
}
@@ -13277,9 +13282,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
@@ -13381,6 +13395,11 @@ public:
return *m_xDevice;
}
+ bool signal_command(const CommandEvent& rCEvt)
+ {
+ return m_aCommandHdl.Call(rCEvt);
+ }
+
virtual void click(const Point& rPos) override
{
MouseEvent aEvent(rPos);
@@ -13389,6 +13408,238 @@ public:
}
};
+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 {