for DisallowCycleFocusOut forward unused keyevents to InterimItemWindow

for ctrl+q etc

Change-Id: Ie0ad94cf0e85693960428ffee5ae4a0ecffb7c6b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/104635
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx
index 27865f2..820d796 100644
--- a/vcl/inc/unx/gtk/gtkframe.hxx
+++ b/vcl/inc/unx/gtk/gtkframe.hxx
@@ -530,6 +530,8 @@ public:
    static OUString             GetPreeditDetails(GtkIMContext* pIMContext, std::vector<ExtTextInputAttr>& rInputFlags, sal_Int32& rCursorPos, sal_uInt8& rCursorFlags);

    void DisallowCycleFocusOut();
    bool IsCycleFocusOutDisallowed() const;
    void AllowCycleFocusOut();
};

#define OOO_TYPE_FIXED ooo_fixed_get_type()
diff --git a/vcl/unx/gtk3/gtk3gtkframe.cxx b/vcl/unx/gtk3/gtk3gtkframe.cxx
index 26c34a0..eb964a7 100644
--- a/vcl/unx/gtk3/gtk3gtkframe.cxx
+++ b/vcl/unx/gtk3/gtk3gtkframe.cxx
@@ -1026,6 +1026,26 @@ void GtkSalFrame::DisallowCycleFocusOut()
    gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), false);
}

bool GtkSalFrame::IsCycleFocusOutDisallowed() const
{
    return m_nSetFocusSignalId == 0;
}

void GtkSalFrame::AllowCycleFocusOut()
{
    if (m_nSetFocusSignalId)
        return;
    // enable/disable can-focus as control enters and leaves
    // embedded native gtk widgets
    m_nSetFocusSignalId = g_signal_connect(G_OBJECT(m_pWindow), "set-focus", G_CALLBACK(signalSetFocus), this);

    // set container without can-focus and focus will tab between
    // the native embedded widgets using the default gtk handling for
    // that
    gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), true);
}


void GtkSalFrame::Init( SalFrame* pParent, SalFrameStyleFlags nStyle )
{
    if( nStyle & SalFrameStyleFlags::DEFAULT ) // ensure default style
@@ -3198,6 +3218,8 @@ gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointe

    bool bFocusInAnotherGtkWidget = false;

    VclPtr<vcl::Window> xTopLevelInterimWindow;

    if (GTK_IS_WINDOW(pThis->m_pWindow))
    {
        GtkWidget* pFocusWindow = gtk_window_get_focus(GTK_WINDOW(pThis->m_pWindow));
@@ -3216,6 +3238,24 @@ gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointe
            g_type_class_unref(pClass);
            if (bHandled)
                return true;

            // Is focus inside a full-app InterimItemWindow? In which case find
            // that InterimItemWindow and send unconsumed keystrokes to it to
            // support ctrl-q etc shortcuts
            if (pThis->IsCycleFocusOutDisallowed())
            {
                GtkWidget* pSearch = pFocusWindow;
                while (pSearch)
                {
                    void* pData = g_object_get_data(G_OBJECT(pSearch), "InterimWindowGlue");
                    if (pData)
                    {
                        xTopLevelInterimWindow = static_cast<vcl::Window*>(pData);
                        break;
                    }
                    pSearch = gtk_widget_get_parent(pSearch);
                }
            }
        }
    }

@@ -3303,6 +3343,22 @@ gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointe
    }
    else
    {
        VclPtr<vcl::Window> xOrigFocusWin;
        if (xTopLevelInterimWindow)
        {
            // Focus is inside a full-app InterimItemWindow send unconsumed
            // keystrokes to by setting it as the mpFocusWin
            VclPtr<vcl::Window> xVclWindow = pThis->GetWindow();
            ImplFrameData* pFrameData = xVclWindow->ImplGetWindowImpl()->mpFrameData;
            xOrigFocusWin = pFrameData->mpFocusWin;
            pFrameData->mpFocusWin = xTopLevelInterimWindow;
            if (pEvent->keyval == GDK_KEY_F6)
            {
                // For F6, allow the focus to leave the InterimItemWindow
                pThis->AllowCycleFocusOut();
            }
        }

        bStopProcessingKey = pThis->doKeyCallback(pEvent->state,
                                                  pEvent->keyval,
                                                  pEvent->hardware_keycode,
@@ -3310,8 +3366,27 @@ gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointe
                                                  sal_Unicode(gdk_keyval_to_unicode( pEvent->keyval )),
                                                  (pEvent->type == GDK_KEY_PRESS),
                                                  false);
        if( ! aDel.isDeleted() )
        if (!aDel.isDeleted())
        {
            pThis->m_nKeyModifiers = ModKeyFlags::NONE;

            if (xTopLevelInterimWindow)
            {
                // Focus was inside a full-app InterimItemWindow, restore the original
                // focus win, unless the focus was changed away from the InterimItemWindow
                // which should only be possible with F6
                VclPtr<vcl::Window> xVclWindow = pThis->GetWindow();
                ImplFrameData* pFrameData = xVclWindow->ImplGetWindowImpl()->mpFrameData;
                if (pFrameData->mpFocusWin == xTopLevelInterimWindow)
                    pFrameData->mpFocusWin = xOrigFocusWin;
                if (pEvent->keyval == GDK_KEY_F6)
                {
                    // undo the above AllowCycleFocusOut for F6
                    pThis->DisallowCycleFocusOut();
                }
            }
        }

    }

    if (!bFocusInAnotherGtkWidget && !aDel.isDeleted() && pThis->m_pIMHandler)
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index 6448e37..13f97ce 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -16291,6 +16291,7 @@ private:
    std::vector<GtkLabel*> m_aMnemonicLabels;

    VclPtr<SystemChildWindow> m_xInterimGlue;
    bool m_bAllowCycleFocusOut;

    void postprocess_widget(GtkWidget* pWidget)
    {
@@ -16458,12 +16459,14 @@ private:
        pThis->postprocess_widget(GTK_WIDGET(pObject));
    }
public:
    GtkInstanceBuilder(GtkWidget* pParent, const OUString& rUIRoot, const OUString& rUIFile, SystemChildWindow* pInterimGlue)
    GtkInstanceBuilder(GtkWidget* pParent, const OUString& rUIRoot, const OUString& rUIFile,
                       SystemChildWindow* pInterimGlue, bool bAllowCycleFocusOut)
        : weld::Builder()
        , m_pStringReplace(Translate::GetReadStringHook())
        , m_pParentWidget(pParent)
        , m_nNotifySignalId(0)
        , m_xInterimGlue(pInterimGlue)
        , m_bAllowCycleFocusOut(bAllowCycleFocusOut)
    {
        OUString sHelpRoot(rUIFile);
        ensure_intercept_drawing_area_accessibility();
@@ -16495,6 +16498,17 @@ public:
        {
            assert(m_pParentWidget);
            g_object_set_data(G_OBJECT(m_pParentWidget), "InterimWindowGlue", m_xInterimGlue.get());

            if (!m_bAllowCycleFocusOut)
            {
                GtkWidget* pTopLevel = gtk_widget_get_toplevel(m_pParentWidget);
                assert(pTopLevel);
                GtkSalFrame* pFrame = GtkSalFrame::getFromWindow(pTopLevel);
                assert(pFrame);
                // unhook handler and let gtk cycle its own way through this widget's
                // children because it has no non-gtk siblings
                pFrame->DisallowCycleFocusOut();
            }
        }
    }

@@ -16555,6 +16569,18 @@ public:
    {
        g_slist_free(m_pObjectList);
        g_object_unref(m_pBuilder);

        if (m_xInterimGlue && !m_bAllowCycleFocusOut)
        {
            GtkWidget* pTopLevel = gtk_widget_get_toplevel(m_pParentWidget);
            assert(pTopLevel);
            GtkSalFrame* pFrame = GtkSalFrame::getFromWindow(pTopLevel);
            assert(pFrame);
            // rehook handler and let vcl cycle its own way through this widget's
            // children
            pFrame->AllowCycleFocusOut();
        }

        m_xInterimGlue.disposeAndClear();
    }

@@ -17048,7 +17074,7 @@ weld::Builder* GtkInstance::CreateBuilder(weld::Widget* pParent, const OUString&
    if (pParent && !pParentWidget) //remove when complete
        return SalInstance::CreateBuilder(pParent, rUIRoot, rUIFile);
    GtkWidget* pBuilderParent = pParentWidget ? pParentWidget->getWidget() : nullptr;
    return new GtkInstanceBuilder(pBuilderParent, rUIRoot, rUIFile, nullptr);
    return new GtkInstanceBuilder(pBuilderParent, rUIRoot, rUIFile, nullptr, true);
}

// tdf#135965 for the case of native widgets inside a GtkSalFrame and F1 pressed, run help
@@ -17122,19 +17148,8 @@ weld::Builder* GtkInstance::CreateInterimBuilder(vcl::Window* pParent, const OUS
    GtkWidget *pWindow = static_cast<GtkWidget*>(pEnvData->pWidget);
    gtk_widget_show_all(pWindow);

    if (!bAllowCycleFocusOut)
    {
        GtkWidget* pTopLevel = gtk_widget_get_toplevel(pWindow);
        assert(pTopLevel);
        GtkSalFrame* pFrame = GtkSalFrame::getFromWindow(pTopLevel);
        assert(pFrame);
        // unhook handler and let gtk cycle its own way through this widget's
        // children because it has no non-gtk siblings
        pFrame->DisallowCycleFocusOut();
    }

    // build the widget tree as a child of the GtkEventBox GtkGrid parent
    return new GtkInstanceBuilder(pWindow, rUIRoot, rUIFile, xEmbedWindow.get());
    return new GtkInstanceBuilder(pWindow, rUIRoot, rUIFile, xEmbedWindow.get(), bAllowCycleFocusOut);
}

weld::MessageDialog* GtkInstance::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType, VclButtonsType eButtonsType, const OUString &rPrimaryMessage)