diff options
author | Caolán McNamara <caolanm@redhat.com> | 2020-09-30 14:42:10 +0100 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2020-10-02 10:37:09 +0200 |
commit | e1ac2a940389f52e28264a623a6620a7a0d94d57 (patch) | |
tree | e8cf96b31091482bc628cf501ecbba4cf1096c05 | |
parent | 1709a9b9a2433032b5acf262ef506272c33d5e63 (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.cxx | 11 | ||||
-rw-r--r-- | include/editeng/editview.hxx | 3 | ||||
-rw-r--r-- | include/svx/svdedxv.hxx | 1 | ||||
-rw-r--r-- | include/svx/weldeditview.hxx | 9 | ||||
-rw-r--r-- | include/vcl/customweld.hxx | 7 | ||||
-rw-r--r-- | include/vcl/layout.hxx | 7 | ||||
-rw-r--r-- | include/vcl/weld.hxx | 27 | ||||
-rw-r--r-- | svx/source/dialog/weldeditview.cxx | 23 | ||||
-rw-r--r-- | svx/source/svdraw/svdedxv.cxx | 7 | ||||
-rw-r--r-- | vcl/inc/salvtables.hxx | 4 | ||||
-rw-r--r-- | vcl/source/app/customweld.cxx | 6 | ||||
-rw-r--r-- | vcl/source/app/salvtables.cxx | 13 | ||||
-rw-r--r-- | vcl/source/window/layout.cxx | 17 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 261 |
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 { |