Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/unx/gtk3/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 223 kB image not shown  

Quelle  gtkframe.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <config_version.h>

#include <unx/gtk/gtkframe.hxx>
#include <unx/gtk/gtkdata.hxx>
#include <unx/gtk/gtkinst.hxx>
#include <unx/gtk/gtkgdi.hxx>
#include <unx/gtk/gtksalmenu.hxx>
#include <unx/gtk/hudawareness.h>
#include <vcl/event.hxx>
#include <vcl/i18nhelp.hxx>
#include <vcl/keycodes.hxx>
#include <unx/geninst.h>
#include <headless/svpgdi.hxx>
#include <sal/log.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <vcl/toolkit/floatwin.hxx>
#include <vcl/toolkit/unowrap.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <vcl/window.hxx>
#include <vcl/settings.hxx>

#include <gtk/gtk.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <unx/gtk/gtkbackend.hxx>

#include <strings.hrc>
#include <window.h>

#include <basegfx/vector/b2ivector.hxx>
#include <officecfg/Office/Common.hxx>

#include <dlfcn.h>

#include <algorithm>

#if OSL_DEBUG_LEVEL > 1
#  include <cstdio>
#endif

#include <i18nlangtag/mslangid.hxx>

#include <cstdlib>
#include <cmath>

#include <com/sun/star/awt/MouseButton.hpp>
#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/util/XModifiable.hpp>

#if !GTK_CHECK_VERSION(4, 0, 0)
#   define GDK_ALT_MASK GDK_MOD1_MASK
#   define GDK_TOPLEVEL_STATE_MAXIMIZED GDK_WINDOW_STATE_MAXIMIZED
#   define GDK_TOPLEVEL_STATE_MINIMIZED GDK_WINDOW_STATE_ICONIFIED
#   define gdk_wayland_surface_get_wl_surface gdk_wayland_window_get_wl_surface
#   define gdk_x11_surface_get_xid gdk_x11_window_get_xid
#endif

using namespace com::sun::star;

int GtkSalFrame::m_nFloats = 0;

static GDBusConnection* pSessionBus = nullptr;

static void EnsureSessionBus()
{
    if (!pSessionBus)
        pSessionBus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
}

sal_uInt16 GtkSalFrame::GetKeyModCode( guint state )
{
    sal_uInt16 nCode = 0;
    if( state & GDK_SHIFT_MASK )
        nCode |= KEY_SHIFT;
    if( state & GDK_CONTROL_MASK )
        nCode |= KEY_MOD1;
    if (state & GDK_ALT_MASK)
        nCode |= KEY_MOD2;
    if( state & GDK_SUPER_MASK )
        nCode |= KEY_MOD3;
    return nCode;
}

sal_uInt16 GtkSalFrame::GetMouseModCode( guint state )
{
    sal_uInt16 nCode = GetKeyModCode( state );
    if( state & GDK_BUTTON1_MASK )
        nCode |= MOUSE_LEFT;
    if( state & GDK_BUTTON2_MASK )
        nCode |= MOUSE_MIDDLE;
    if( state & GDK_BUTTON3_MASK )
        nCode |= MOUSE_RIGHT;

    return nCode;
}

// KEY_F26 is the last function key known to keycodes.hxx
static bool IsFunctionKeyVal(guint keyval)
{
    return keyval >= GDK_KEY_F1 && keyval <= GDK_KEY_F26;
}

sal_uInt16 GtkSalFrame::GetKeyCode(guint keyval)
{
    sal_uInt16 nCode = 0;
    if( keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9 )
        nCode = KEY_0 + (keyval-GDK_KEY_0);
    else if( keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9 )
        nCode = KEY_0 + (keyval-GDK_KEY_KP_0);
    else if( keyval >= GDK_KEY_A && keyval <= GDK_KEY_Z )
        nCode = KEY_A + (keyval-GDK_KEY_A );
    else if( keyval >= GDK_KEY_a && keyval <= GDK_KEY_z )
        nCode = KEY_A + (keyval-GDK_KEY_a );
    else if (IsFunctionKeyVal(keyval))
    {
        switch( keyval )
        {
            // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx
            // although GDK_KEY_F1 ... GDK_KEY_L10 are known to
            // gdk/gdkkeysyms.h, they are unlikely to be generated
            // except possibly by Solaris systems
            // this whole section needs review
            case GDK_KEY_L2:
                    nCode = KEY_F12;
                break;
            case GDK_KEY_L3:            nCode = KEY_PROPERTIES; break;
            case GDK_KEY_L4:            nCode = KEY_UNDO;       break;
            case GDK_KEY_L6:            nCode = KEY_COPY;       break// KEY_F16
            case GDK_KEY_L8:            nCode = KEY_PASTE;      break// KEY_F18
            case GDK_KEY_L10:           nCode = KEY_CUT;        break// KEY_F20
            default:
                nCode = KEY_F1 + (keyval-GDK_KEY_F1);           break;
        }
    }
    else
    {
        switch( keyval )
        {
            case GDK_KEY_KP_Down:
            case GDK_KEY_Down:          nCode = KEY_DOWN;       break;
            case GDK_KEY_KP_Up:
            case GDK_KEY_Up:            nCode = KEY_UP;         break;
            case GDK_KEY_KP_Left:
            case GDK_KEY_Left:          nCode = KEY_LEFT;       break;
            case GDK_KEY_KP_Right:
            case GDK_KEY_Right:         nCode = KEY_RIGHT;      break;
            case GDK_KEY_KP_Begin:
            case GDK_KEY_KP_Home:
            case GDK_KEY_Begin:
            case GDK_KEY_Home:          nCode = KEY_HOME;       break;
            case GDK_KEY_KP_End:
            case GDK_KEY_End:           nCode = KEY_END;        break;
            case GDK_KEY_KP_Page_Up:
            case GDK_KEY_Page_Up:       nCode = KEY_PAGEUP;     break;
            case GDK_KEY_KP_Page_Down:
            case GDK_KEY_Page_Down:     nCode = KEY_PAGEDOWN;   break;
            case GDK_KEY_KP_Enter:
            case GDK_KEY_Return:        nCode = KEY_RETURN;     break;
            case GDK_KEY_Escape:        nCode = KEY_ESCAPE;     break;
            case GDK_KEY_ISO_Left_Tab:
            case GDK_KEY_KP_Tab:
            case GDK_KEY_Tab:           nCode = KEY_TAB;        break;
            case GDK_KEY_BackSpace:     nCode = KEY_BACKSPACE;  break;
            case GDK_KEY_KP_Space:
            case GDK_KEY_space:         nCode = KEY_SPACE;      break;
            case GDK_KEY_KP_Insert:
            case GDK_KEY_Insert:        nCode = KEY_INSERT;     break;
            case GDK_KEY_KP_Delete:
            case GDK_KEY_Delete:        nCode = KEY_DELETE;     break;
            case GDK_KEY_plus:
            case GDK_KEY_KP_Add:        nCode = KEY_ADD;        break;
            case GDK_KEY_minus:
            case GDK_KEY_KP_Subtract:   nCode = KEY_SUBTRACT;   break;
            case GDK_KEY_asterisk:
            case GDK_KEY_KP_Multiply:   nCode = KEY_MULTIPLY;   break;
            case GDK_KEY_slash:
            case GDK_KEY_KP_Divide:     nCode = KEY_DIVIDE;     break;
            case GDK_KEY_period:        nCode = KEY_POINT;      break;
            case GDK_KEY_decimalpoint:  nCode = KEY_POINT;      break;
            case GDK_KEY_comma:         nCode = KEY_COMMA;      break;
            case GDK_KEY_less:          nCode = KEY_LESS;       break;
            case GDK_KEY_greater:       nCode = KEY_GREATER;    break;
            case GDK_KEY_KP_Equal:
            case GDK_KEY_equal:         nCode = KEY_EQUAL;      break;
            case GDK_KEY_Find:          nCode = KEY_FIND;       break;
            case GDK_KEY_Menu:          nCode = KEY_CONTEXTMENU;break;
            case GDK_KEY_Help:          nCode = KEY_HELP;       break;
            case GDK_KEY_Undo:          nCode = KEY_UNDO;       break;
            case GDK_KEY_Redo:          nCode = KEY_REPEAT;     break;
            // on a sun keyboard this actually is usually SunXK_Stop = 0x0000FF69 (XK_Cancel),
            // but VCL doesn't have a key definition for that
            case GDK_KEY_Cancel:        nCode = KEY_F11;        break;
            case GDK_KEY_KP_Decimal:
            case GDK_KEY_KP_Separator:  nCode = KEY_DECIMAL;    break;
            case GDK_KEY_asciitilde:    nCode = KEY_TILDE;      break;
            case GDK_KEY_leftsinglequotemark:
            case GDK_KEY_quoteleft:    nCode = KEY_QUOTELEFT;    break;
            case GDK_KEY_bracketleft:  nCode = KEY_BRACKETLEFT;  break;
            case GDK_KEY_bracketright: nCode = KEY_BRACKETRIGHT; break;
            case GDK_KEY_semicolon:    nCode = KEY_SEMICOLON;    break;
            case GDK_KEY_quoteright:   nCode = KEY_QUOTERIGHT;   break;
            case GDK_KEY_braceright:   nCode = KEY_RIGHTCURLYBRACKET;   break;
            case GDK_KEY_numbersign:   nCode = KEY_NUMBERSIGN; break;
            case GDK_KEY_Forward:      nCode = KEY_XF86FORWARD; break;
            case GDK_KEY_Back:         nCode = KEY_XF86BACK; break;
            case GDK_KEY_colon:    nCode = KEY_COLON;    break;
            // some special cases, also see saldisp.cxx
            // - - - - - - - - - - - - -  Apollo - - - - - - - - - - - - - 0x1000
            // These can be found in ap_keysym.h
            case 0x1000FF02: // apXK_Copy
                nCode = KEY_COPY;
                break;
            case 0x1000FF03: // apXK_Cut
                nCode = KEY_CUT;
                break;
            case 0x1000FF04: // apXK_Paste
                nCode = KEY_PASTE;
                break;
            case 0x1000FF14: // apXK_Repeat
                nCode = KEY_REPEAT;
                break;
            // Exit, Save
            // - - - - - - - - - - - - - - D E C - - - - - - - - - - - - - 0x1000
            // These can be found in DECkeysym.h
            case 0x1000FF00:
                nCode = KEY_DELETE;
                break;
            // - - - - - - - - - - - - - -  H P  - - - - - - - - - - - - - 0x1000
            // These can be found in HPkeysym.h
            case 0x1000FF73: // hpXK_DeleteChar
                nCode = KEY_DELETE;
                break;
            case 0x1000FF74: // hpXK_BackTab
            case 0x1000FF75: // hpXK_KP_BackTab
                nCode = KEY_TAB;
                break;
            // - - - - - - - - - - - - - - I B M - - - - - - - - - - - - -
            // - - - - - - - - - - - - - - O S F - - - - - - - - - - - - - 0x1004
            // These also can be found in HPkeysym.h
            case 0x1004FF02: // osfXK_Copy
                nCode = KEY_COPY;
                break;
            case 0x1004FF03: // osfXK_Cut
                nCode = KEY_CUT;
                break;
            case 0x1004FF04: // osfXK_Paste
                nCode = KEY_PASTE;
                break;
            case 0x1004FF07: // osfXK_BackTab
                nCode = KEY_TAB;
                break;
            case 0x1004FF08: // osfXK_BackSpace
                nCode = KEY_BACKSPACE;
                break;
            case 0x1004FF1B: // osfXK_Escape
                nCode = KEY_ESCAPE;
                break;
            // Up, Down, Left, Right, PageUp, PageDown
            // - - - - - - - - - - - - - - S C O - - - - - - - - - - - - -
            // - - - - - - - - - - - - - - S G I - - - - - - - - - - - - - 0x1007
            // - - - - - - - - - - - - - - S N I - - - - - - - - - - - - -
            // - - - - - - - - - - - - - - S U N - - - - - - - - - - - - - 0x1005
            // These can be found in Sunkeysym.h
            case 0x1005FF10: // SunXK_F36
                nCode = KEY_F11;
                break;
            case 0x1005FF11: // SunXK_F37
                nCode = KEY_F12;
                break;
            case 0x1005FF70: // SunXK_Props
                nCode = KEY_PROPERTIES;
                break;
            case 0x1005FF71: // SunXK_Front
                nCode = KEY_FRONT;
                break;
            case 0x1005FF72: // SunXK_Copy
                nCode = KEY_COPY;
                break;
            case 0x1005FF73: // SunXK_Open
                nCode = KEY_OPEN;
                break;
            case 0x1005FF74: // SunXK_Paste
                nCode = KEY_PASTE;
                break;
            case 0x1005FF75: // SunXK_Cut
                nCode = KEY_CUT;
                break;
            // - - - - - - - - - - - - - X F 8 6 - - - - - - - - - - - - - 0x1008
            // These can be found in XF86keysym.h
            // but more importantly they are also available GTK/Gdk version 3
            // and hence are already provided in gdk/gdkkeysyms.h, and hence
            // in gdk/gdk.h
            case GDK_KEY_Copy:          nCode = KEY_COPY;  break// 0x1008ff57
            case GDK_KEY_Cut:           nCode = KEY_CUT;   break// 0x1008ff58
            case GDK_KEY_Open:          nCode = KEY_OPEN;  break// 0x1008ff6b
            case GDK_KEY_Paste:         nCode = KEY_PASTE; break// 0x1008ff6d
        }
    }

    return nCode;
}

#if !GTK_CHECK_VERSION(4, 0, 0)
guint GtkSalFrame::GetKeyValFor(GdkKeymap* pKeyMap, guint16 hardware_keycode, guint8 group)
{
    guint updated_keyval = 0;
    gdk_keymap_translate_keyboard_state(pKeyMap, hardware_keycode,
        GdkModifierType(0), group, &updated_keyval, nullptr, nullptr, nullptr);
    return updated_keyval;
}
#endif

namespace {

// F10 means either KEY_F10 or KEY_MENU, which has to be decided
// in the independent part.
struct KeyAlternate
{
    sal_uInt16          nKeyCode;
    sal_Unicode     nCharCode;
    KeyAlternate() : nKeyCode( 0 ), nCharCode( 0 ) {}
    KeyAlternate( sal_uInt16 nKey, sal_Unicode nChar = 0 ) : nKeyCode( nKey ), nCharCode( nChar ) {}
};

}

static KeyAlternate
GetAlternateKeyCode( const sal_uInt16 nKeyCode )
{
    KeyAlternate aAlternate;

    switch( nKeyCode )
    {
        case KEY_F10: aAlternate = KeyAlternate( KEY_MENU );break;
        case KEY_F24: aAlternate = KeyAlternate( KEY_SUBTRACT, '-' );break;
    }

    return aAlternate;
}

#if OSL_DEBUG_LEVEL > 0
static bool dumpframes = false;
#endif

bool GtkSalFrame::doKeyCallback( guint state,
                                 guint keyval,
                                 guint16 hardware_keycode,
                                 guint8 group,
                                 sal_Unicode aOrigCode,
                                 bool bDown,
                                 bool bSendRelease
                                 )
{
    SalKeyEvent aEvent;

    aEvent.mnCharCode       = aOrigCode;
    aEvent.mnRepeat         = 0;

    vcl::DeletionListener aDel( this );

#if OSL_DEBUG_LEVEL > 0
    const char* pKeyDebug = getenv("VCL_GTK3_PAINTDEBUG");

    if (pKeyDebug && *pKeyDebug == '1')
    {
        if (bDown)
        {
            // shift-zero forces a re-draw and event is swallowed
            if (keyval == GDK_KEY_0)
            {
                SAL_INFO("vcl.gtk3""force widget_queue_draw.");
                gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea));
                return false;
            }
            else if (keyval == GDK_KEY_1)
            {
                SAL_INFO("vcl.gtk3""force repaint all.");
                TriggerPaintEvent();
                return false;
            }
            else if (keyval == GDK_KEY_2)
            {
                dumpframes = !dumpframes;
                SAL_INFO("vcl.gtk3""toggle dump frames to " << dumpframes);
                return false;
            }
        }
    }
#endif

    /*
     *  #i42122# translate all keys with Ctrl and/or Alt to group 0 else
     *  shortcuts (e.g. Ctrl-o) will not work but be inserted by the
     *  application
     *
     *  #i52338# do this for all keys that the independent part has no key code
     *  for
     *
     *  fdo#41169 rather than use group 0, detect if there is a group which can
     *  be used to input Latin text and use that if possible
     */

    aEvent.mnCode = GetKeyCode( keyval );
#if !GTK_CHECK_VERSION(4, 0, 0)
    if( aEvent.mnCode == 0 )
    {
        gint best_group = SAL_MAX_INT32;

        // Try and find Latin layout
        GdkKeymap* keymap = gdk_keymap_get_default();
        GdkKeymapKey *keys;
        gint n_keys;
        if (gdk_keymap_get_entries_for_keyval(keymap, GDK_KEY_A, &keys, &n_keys))
        {
            // Find the lowest group that supports Latin layout
            for (gint i = 0; i < n_keys; ++i)
            {
                if (keys[i].level != 0 && keys[i].level != 1)
                    continue;
                best_group = std::min(best_group, keys[i].group);
                if (best_group == 0)
                    break;
            }
            g_free(keys);
        }

        //Unavailable, go with original group then I suppose
        if (best_group == SAL_MAX_INT32)
            best_group = group;

        guint updated_keyval = GetKeyValFor(keymap, hardware_keycode, best_group);
        aEvent.mnCode = GetKeyCode(updated_keyval);
    }
#else
    (void)hardware_keycode;
    (void)group;
#endif

    aEvent.mnCode   |= GetKeyModCode( state );

    bool bStopProcessingKey;
    if (bDown)
    {
        // tdf#152404 Commit uncommitted text before dispatching key shortcuts. In
        // certain cases such as pressing Control-Alt-C in a Writer document while
        // there is uncommitted text will call GtkSalFrame::EndExtTextInput() which
        // will dispatch a SalEvent::EndExtTextInput event. Writer's handler for that
        // event will delete the uncommitted text and then insert the committed text
        // but LibreOffice will crash when deleting the uncommitted text because
        // deletion of the text also removes and deletes the newly inserted comment.
        if (m_pIMHandler && !m_pIMHandler->m_aInputEvent.maText.isEmpty() && (aEvent.mnCode & (KEY_MOD1 | KEY_MOD2)))
            m_pIMHandler->doCallEndExtTextInput();

        bStopProcessingKey = CallCallbackExc(SalEvent::KeyInput, &aEvent);
        // #i46889# copy AlternateKeyCode handling from generic plugin
        if (!bStopProcessingKey)
        {
            KeyAlternate aAlternate = GetAlternateKeyCode( aEvent.mnCode );
            if( aAlternate.nKeyCode )
            {
                aEvent.mnCode = aAlternate.nKeyCode;
                if( aAlternate.nCharCode )
                    aEvent.mnCharCode = aAlternate.nCharCode;
                bStopProcessingKey = CallCallbackExc(SalEvent::KeyInput, &aEvent);
            }
        }
        if( bSendRelease && ! aDel.isDeleted() )
        {
            CallCallbackExc(SalEvent::KeyUp, &aEvent);
        }
    }
    else
        bStopProcessingKey = CallCallbackExc(SalEvent::KeyUp, &aEvent);
    return bStopProcessingKey;
}

GtkSalFrame::GtkSalFrame( SalFrame* pParent, SalFrameStyleFlags nStyle )
    : m_nXScreen( getDisplay()->GetDefaultXScreen() )
    , m_pHeaderBar(nullptr)
    , m_bGraphics(false)
    , m_nSetFocusSignalId(0)
#if !GTK_CHECK_VERSION(4, 0, 0)
    , m_aSmoothScrollIdle("GtkSalFrame m_aSmoothScrollIdle")
#endif
{
    getDisplay()->registerFrame( this );
    m_bDefaultPos       = true;
    m_bDefaultSize      = ( (nStyle & SalFrameStyleFlags::SIZEABLE) && ! pParent );
    Init( pParent, nStyle );
}

GtkSalFrame::GtkSalFrame( SystemParentData* pSysData )
    : m_nXScreen( getDisplay()->GetDefaultXScreen() )
    , m_pHeaderBar(nullptr)
    , m_bGraphics(false)
    , m_nSetFocusSignalId(0)
#if !GTK_CHECK_VERSION(4, 0, 0)
    , m_aSmoothScrollIdle("GtkSalFrame m_aSmoothScrollIdle")
#endif
{
    getDisplay()->registerFrame( this );
    // permanently ignore errors from our unruly children ...
    GetGenericUnixSalData()->ErrorTrapPush();
    m_bDefaultPos       = true;
    m_bDefaultSize      = true;
    Init( pSysData );
}

// AppMenu watch functions.

static void ObjectDestroyedNotify( gpointer data )
{
    if ( data ) {
        g_object_unref( data );
    }
}

#if !GTK_CHECK_VERSION(4,0,0)
static void hud_activated( gboolean hud_active, gpointer user_data )
{
    if ( hud_active )
    {
        SolarMutexGuard aGuard;
        GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data );
        GtkSalMenu* pSalMenu = reinterpret_cast< GtkSalMenu* >( pSalFrame->GetMenu() );

        if ( pSalMenu )
            pSalMenu->UpdateFull();
    }
}
#endif

static void attach_menu_model(GtkSalFrame* pSalFrame)
{
    GtkWidget* pWidget = pSalFrame->getWindow();
    GdkSurface* gdkWindow = widget_get_surface(pWidget);

    if ( gdkWindow == nullptr || g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) != nullptr )
        return;

    // Create menu model and action group attached to this frame.
    GMenuModel* pMenuModel = G_MENU_MODEL( g_lo_menu_new() );
    GActionGroup* pActionGroup = reinterpret_cast<GActionGroup*>(g_lo_action_group_new());

    // Set window properties.
    g_object_set_data_full( G_OBJECT( gdkWindow ), "g-lo-menubar", pMenuModel, ObjectDestroyedNotify );
    g_object_set_data_full( G_OBJECT( gdkWindow ), "g-lo-action-group", pActionGroup, ObjectDestroyedNotify );

#if !GTK_CHECK_VERSION(4,0,0)
    // Get a DBus session connection.
    EnsureSessionBus();
    if (!pSessionBus)
        return;

    // Generate menu paths.
    sal_uIntPtr windowId = GtkSalFrame::GetNativeWindowHandle(pWidget);
    gchar* aDBusWindowPath = g_strdup_printf( "/org/libreoffice/window/%lu", windowId );
    gchar* aDBusMenubarPath = g_strdup_printf( "/org/libreoffice/window/%lu/menus/menubar", windowId );

    GdkDisplay *pDisplay = GtkSalFrame::getGdkDisplay();
#if defined(GDK_WINDOWING_X11)
    if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay))
    {
        gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_ID""org.libreoffice" );
        gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_MENUBAR_OBJECT_PATH", aDBusMenubarPath );
        gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_WINDOW_OBJECT_PATH", aDBusWindowPath );
        gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_OBJECT_PATH""/org/libreoffice" );
        gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_UNIQUE_BUS_NAME", g_dbus_connection_get_unique_name( pSessionBus ) );
    }
#endif
#if defined(GDK_WINDOWING_WAYLAND)
    if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay))
    {
        gdk_wayland_window_set_dbus_properties_libgtk_only(gdkWindow, "org.libreoffice",
                                                           nullptr,
                                                           aDBusMenubarPath,
                                                           aDBusWindowPath,
                                                           "/org/libreoffice",
                                                           g_dbus_connection_get_unique_name( pSessionBus ));
    }
#endif
    // Publish the menu model and the action group.
    SAL_INFO("vcl.unity""exporting menu model at " << pMenuModel << " for window " << windowId);
    pSalFrame->m_nMenuExportId = g_dbus_connection_export_menu_model (pSessionBus, aDBusMenubarPath, pMenuModel, nullptr);
    SAL_INFO("vcl.unity""exporting action group at " << pActionGroup << " for window " << windowId);
    pSalFrame->m_nActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, aDBusWindowPath, pActionGroup, nullptr);
    pSalFrame->m_nHudAwarenessId = hud_awareness_register( pSessionBus, aDBusMenubarPath, hud_activated, pSalFrame, nullptr, nullptr );

    g_free( aDBusWindowPath );
    g_free( aDBusMenubarPath );
#endif
}

void on_registrar_available( GDBusConnection * /*connection*/,
                             const gchar     * /*name*/,
                             const gchar     * /*name_owner*/,
                             gpointer         user_data )
{
    SolarMutexGuard aGuard;

    GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data );

    SalMenu* pSalMenu = pSalFrame->GetMenu();

    if ( pSalMenu != nullptr )
    {
        GtkSalMenu* pGtkSalMenu = static_cast<GtkSalMenu*>(pSalMenu);
        pGtkSalMenu->EnableUnity(true);
    }
}

// This is called when the registrar becomes unavailable. It shows the menubar.
void on_registrar_unavailable( GDBusConnection * /*connection*/,
                               const gchar     * /*name*/,
                               gpointer         user_data )
{
    SolarMutexGuard aGuard;

    SAL_INFO("vcl.unity""on_registrar_unavailable");

    GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data );

    SalMenu* pSalMenu = pSalFrame->GetMenu();

    if ( pSalMenu ) {
        GtkSalMenu* pGtkSalMenu = static_cast< GtkSalMenu* >( pSalMenu );
        pGtkSalMenu->EnableUnity(false);
    }
}

void GtkSalFrame::EnsureAppMenuWatch()
{
    if ( m_nWatcherId )
        return;

    // Get a DBus session connection.
    EnsureSessionBus();
    if (!pSessionBus)
        return;

    // Publish the menu only if AppMenu registrar is available.
    m_nWatcherId = g_bus_watch_name_on_connection( pSessionBus,
                                                   "com.canonical.AppMenu.Registrar",
                                                   G_BUS_NAME_WATCHER_FLAGS_NONE,
                                                   on_registrar_available,
                                                   on_registrar_unavailable,
                                                   this,
                                                   nullptr );
}

void GtkSalFrame::InvalidateGraphics()
{
    if( m_pGraphics )
    {
        m_bGraphics = false;
    }
}

GtkSalFrame::~GtkSalFrame()
{
#if !GTK_CHECK_VERSION(4,0,0)
    m_aSmoothScrollIdle.Stop();
    m_aSmoothScrollIdle.ClearInvokeHandler();
#endif

    if (m_pDropTarget)
    {
        m_pDropTarget->deinitialize();
        m_pDropTarget = nullptr;
    }

    if (m_pDragSource)
    {
        m_pDragSource->deinitialize();
        m_pDragSource= nullptr;
    }

    InvalidateGraphics();

    if (m_pParent)
    {
        m_pParent->m_aChildren.remove( this );
    }

    getDisplay()->deregisterFrame( this );

    if( m_pRegion )
    {
        cairo_region_destroy( m_pRegion );
    }

    m_pIMHandler.reset();

    //tdf#108705 remove grabs on event widget before
    //destroying event widget
    while (m_nGrabLevel)
        removeGrabLevel();

    {
        SolarMutexGuard aGuard;

        if (m_nWatcherId)
            g_bus_unwatch_name(m_nWatcherId);

        if (m_nPortalSettingChangedSignalId)
            g_signal_handler_disconnect(m_pSettingsPortal, m_nPortalSettingChangedSignalId);

        if (m_pSettingsPortal)
            g_object_unref(m_pSettingsPortal);

        if (m_nSessionClientSignalId)
            g_signal_handler_disconnect(m_pSessionClient, m_nSessionClientSignalId);

        if (m_pSessionClient)
            g_object_unref(m_pSessionClient);

        if (m_pSessionManager)
            g_object_unref(m_pSessionManager);
    }

    GtkWidget *pEventWidget = getMouseEventWidget();
    for (auto handler_id : m_aMouseSignalIds)
        g_signal_handler_disconnect(G_OBJECT(pEventWidget), handler_id);

#if !GTK_CHECK_VERSION(4, 0, 0)
    if( m_pFixedContainer )
        gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer ) );
    if( m_pEventBox )
        gtk_widget_destroy( GTK_WIDGET(m_pEventBox) );
    if( m_pTopLevelGrid )
        gtk_widget_destroy( GTK_WIDGET(m_pTopLevelGrid) );
#else
    g_signal_handler_disconnect(G_OBJECT(gtk_widget_get_display(pEventWidget)), m_nSettingChangedSignalId);
#endif
    {
        SolarMutexGuard aGuard;

        if( m_pWindow )
        {
            g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", nullptr );

            if ( pSessionBus )
            {
                if ( m_nHudAwarenessId )
                    hud_awareness_unregister( pSessionBus, m_nHudAwarenessId );
                if ( m_nMenuExportId )
                    g_dbus_connection_unexport_menu_model( pSessionBus, m_nMenuExportId );
                if ( m_nActionGroupExportId )
                    g_dbus_connection_unexport_action_group( pSessionBus, m_nActionGroupExportId );
            }
            m_xFrameWeld.reset();
#if !GTK_CHECK_VERSION(4,0,0)
            gtk_widget_destroy( m_pWindow );
#else
            if (GTK_IS_WINDOW(m_pWindow))
                gtk_window_destroy(GTK_WINDOW(m_pWindow));
            else
                g_clear_pointer(&m_pWindow, gtk_widget_unparent);
#endif
        }
    }

#if !GTK_CHECK_VERSION(4,0,0)
    if( m_pForeignParent )
        g_object_unref( G_OBJECT( m_pForeignParent ) );
    if( m_pForeignTopLevel )
        g_object_unref( G_OBJECT( m_pForeignTopLevel) );
#endif

    m_pGraphics.reset();

    if (m_pSurface)
        cairo_surface_destroy(m_pSurface);
}

void GtkSalFrame::moveWindow( tools::Long nX, tools::Long nY )
{
    if( isChild( false ) )
    {
        GtkWidget* pParent = m_pParent ? gtk_widget_get_parent(m_pWindow) : nullptr;
        // tdf#130414 it's possible that we were reparented and are no longer inside
        // our original GtkFixed parent
        if (pParent && GTK_IS_FIXED(pParent))
        {
            gtk_fixed_move( GTK_FIXED(pParent),
                            m_pWindow,
                            nX - m_pParent->maGeometry.x(), nY - m_pParent->maGeometry.y() );
        }
        return;
    }
#if GTK_CHECK_VERSION(4,0,0)
    if (GTK_IS_POPOVER(m_pWindow))
    {
        GdkRectangle aRect;
        aRect.x = nX;
        aRect.y = nY;
        aRect.width = 1;
        aRect.height = 1;
        gtk_popover_set_pointing_to(GTK_POPOVER(m_pWindow), &aRect);
        return;
    }
#else
    gtk_window_move( GTK_WINDOW(m_pWindow), nX, nY );
#endif
}

void GtkSalFrame::widget_set_size_request(tools::Long nWidth, tools::Long nHeight)
{
    gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer), nWidth, nHeight );
#if GTK_CHECK_VERSION(4,0,0)
    gtk_widget_set_size_request(GTK_WIDGET(m_pDrawingArea), nWidth, nHeight );
#endif
}

void GtkSalFrame::window_resize(tools::Long nWidth, tools::Long nHeight)
{
    m_nWidthRequest = nWidth;
    m_nHeightRequest = nHeight;
    if (!GTK_IS_WINDOW(m_pWindow))
    {
#if GTK_CHECK_VERSION(4,0,0)
        gtk_widget_set_size_request(GTK_WIDGET(m_pDrawingArea), nWidth, nHeight);
#endif
        return;
    }
    gtk_window_set_default_size(GTK_WINDOW(m_pWindow), nWidth, nHeight);
#if !GTK_CHECK_VERSION(4,0,0)
    gtk_window_resize(GTK_WINDOW(m_pWindow), nWidth, nHeight);
#endif
}

void GtkSalFrame::resizeWindow( tools::Long nWidth, tools::Long nHeight )
{
    if( isChild( false ) )
    {
        widget_set_size_request(nWidth, nHeight);
    }
    else if( ! isChild( truefalse ) )
        window_resize(nWidth, nHeight);
}

#if !GTK_CHECK_VERSION(4,0,0)
// tdf#124694 GtkFixed takes the max size of all its children as its
// preferred size, causing it to not clip its child, but grow instead.

static void
ooo_fixed_get_preferred_height(GtkWidget*, gint *minimum, gint *natural)
{
    *minimum = 0;
    *natural = 0;
}

static void
ooo_fixed_get_preferred_width(GtkWidget*, gint *minimum, gint *natural)
{
    *minimum = 0;
    *natural = 0;
}

static void
ooo_fixed_class_init(gpointer klass_, gpointer)
{
    auto const klass = static_cast<GtkFixedClass *>(klass_);
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    widget_class->get_accessible = ooo_fixed_get_accessible;
    widget_class->get_preferred_height = ooo_fixed_get_preferred_height;
    widget_class->get_preferred_width = ooo_fixed_get_preferred_width;
}

/*
 * Always use a sub-class of GtkFixed we can tag for a11y. This allows us to
 * utilize GAIL for the toplevel window and toolkit implementation incl.
 * key event listener support ..
 */


GType
ooo_fixed_get_type()
{
    static GType type = 0;

    if (!type) {
        static const GTypeInfo tinfo =
        {
            sizeof (GtkFixedClass),
            nullptr,      /* base init */
            nullptr,  /* base finalize */
            ooo_fixed_class_init, /* class init */
            nullptr, /* class finalize */
            nullptr,                      /* class data */
            sizeof (GtkFixed),         /* instance size */
            0,                         /* nb preallocs */
            nullptr,  /* instance init */
            nullptr                       /* value table */
        };

        type = g_type_register_static( GTK_TYPE_FIXED, "OOoFixed",
                                       &tinfo, GTypeFlags(0));
    }

    return type;
}

#endif

void GtkSalFrame::updateScreenNumber()
{
#if !GTK_CHECK_VERSION(4,0,0)
    int nScreen = 0;
    GdkScreen *pScreen = gtk_widget_get_screen( m_pWindow );
    if( pScreen )
        nScreen = getDisplay()->getSystem()->getScreenMonitorIdx( pScreen, maGeometry.x(), maGeometry.y() );
    maGeometry.setScreen(nScreen);
#endif
}

GtkWidget *GtkSalFrame::getMouseEventWidget() const
{
#if !GTK_CHECK_VERSION(4,0,0)
    return GTK_WIDGET(m_pEventBox);
#else
    return GTK_WIDGET(m_pFixedContainer);
#endif
}

static void damaged(void *handle,
                    sal_Int32 nExtentsX, sal_Int32 nExtentsY,
                    sal_Int32 nExtentsWidth, sal_Int32 nExtentsHeight)
{
    GtkSalFrame* pThis = static_cast<GtkSalFrame*>(handle);
    pThis->damaged(nExtentsX, nExtentsY, nExtentsWidth, nExtentsHeight);
}

#if !GTK_CHECK_VERSION(4,0,0)
static void notifyUnref(gpointer data, GObject *) { g_object_unref(data); }
#endif

void GtkSalFrame::InitCommon()
{
    m_pSurface = nullptr;
    m_nGrabLevel = 0;
    m_bSalObjectSetPosSize = false;
    m_nPortalSettingChangedSignalId = 0;
    m_nSessionClientSignalId = 0;
    m_pSettingsPortal = nullptr;
    m_pSessionManager = nullptr;
    m_pSessionClient = nullptr;

    m_aDamageHandler.handle = this;
    m_aDamageHandler.damaged = ::damaged;

#if !GTK_CHECK_VERSION(4,0,0)
    m_aSmoothScrollIdle.SetInvokeHandler(LINK(this, GtkSalFrame, AsyncScroll));
#endif

    m_pTopLevelGrid = GTK_GRID(gtk_grid_new());
    container_add(m_pWindow, GTK_WIDGET(m_pTopLevelGrid));

#if !GTK_CHECK_VERSION(4,0,0)
    m_pEventBox = GTK_EVENT_BOX(gtk_event_box_new());
    gtk_widget_add_events( GTK_WIDGET(m_pEventBox),
                           GDK_ALL_EVENTS_MASK );
    gtk_widget_set_vexpand(GTK_WIDGET(m_pEventBox), true);
    gtk_widget_set_hexpand(GTK_WIDGET(m_pEventBox), true);
    gtk_grid_attach(m_pTopLevelGrid, GTK_WIDGET(m_pEventBox), 0, 0, 1, 1);
#endif

    // add the fixed container child,
    // fixed is needed since we have to position plugin windows
#if !GTK_CHECK_VERSION(4,0,0)
    m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), nullptr ));
    m_pDrawingArea = m_pFixedContainer;
#else
    m_pOverlay = GTK_OVERLAY(gtk_overlay_new());
    m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), nullptr ));
    m_pDrawingArea = GTK_DRAWING_AREA(gtk_drawing_area_new());
#endif
    if (GTK_IS_WINDOW(m_pWindow))
    {
        Size aDefWindowSize = calcDefaultSize();
        gtk_window_set_default_size(GTK_WINDOW(m_pWindow), aDefWindowSize.Width(), aDefWindowSize.Height());
    }
    gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), true);
    gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer), 1, 1);
#if !GTK_CHECK_VERSION(4,0,0)
    gtk_container_add( GTK_CONTAINER(m_pEventBox), GTK_WIDGET(m_pFixedContainer) );
#else
    gtk_widget_set_vexpand(GTK_WIDGET(m_pOverlay), true);
    gtk_widget_set_hexpand(GTK_WIDGET(m_pOverlay), true);
    gtk_grid_attach(m_pTopLevelGrid, GTK_WIDGET(m_pOverlay), 0, 0, 1, 1);
    gtk_overlay_set_child(m_pOverlay, GTK_WIDGET(m_pDrawingArea));
    gtk_overlay_add_overlay(m_pOverlay, GTK_WIDGET(m_pFixedContainer));
#endif

    GtkWidget *pEventWidget = getMouseEventWidget();
#if !GTK_CHECK_VERSION(4,0,0)
    gtk_widget_set_app_paintable(GTK_WIDGET(m_pFixedContainer), true);
    gtk_widget_set_redraw_on_allocate(GTK_WIDGET(m_pFixedContainer), false);
#endif

#if GTK_CHECK_VERSION(4,0,0)
    m_nSettingChangedSignalId = g_signal_connect(G_OBJECT(gtk_widget_get_display(pEventWidget)), "setting-changed", G_CALLBACK(signalStyleUpdated), this);
#else
    // use pEventWidget instead of m_pWindow to avoid infinite event loop under Linux Mint Mate 18.3
    g_signal_connect(G_OBJECT(pEventWidget), "style-updated", G_CALLBACK(signalStyleUpdated), this);
#endif
    gtk_widget_set_has_tooltip(pEventWidget, true);
    // connect signals
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "query-tooltip", G_CALLBACK(signalTooltipQuery), this ));
#if !GTK_CHECK_VERSION(4,0,0)
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-press-event", G_CALLBACK(signalButton), this ));
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-release-event", G_CALLBACK(signalButton), this ));

    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "motion-notify-event", G_CALLBACK(signalMotion), this ));
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "leave-notify-event", G_CALLBACK(signalCrossing), this ));
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "enter-notify-event", G_CALLBACK(signalCrossing), this ));

    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "scroll-event", G_CALLBACK(signalScroll), this ));
#else
    GtkGesture *pClick = gtk_gesture_click_new();
    gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(pClick), 0);
    // use GTK_PHASE_TARGET instead of default GTK_PHASE_BUBBLE because I don't
    // want click events in gtk widgets inside the overlay, e.g. the font size
    // combobox GtkEntry in the toolbar, to be propagated down to this widget
    gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(pClick), GTK_PHASE_TARGET);
    gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pClick));
    g_signal_connect(pClick, "pressed", G_CALLBACK(gesturePressed), this);
    g_signal_connect(pClick, "released", G_CALLBACK(gestureReleased), this);

    GtkEventController* pMotionController = gtk_event_controller_motion_new();
    g_signal_connect(pMotionController, "motion", G_CALLBACK(signalMotion), this);
    g_signal_connect(pMotionController, "enter", G_CALLBACK(signalEnter), this);
    g_signal_connect(pMotionController, "leave", G_CALLBACK(signalLeave), this);
    gtk_widget_add_controller(pEventWidget, pMotionController);

    GtkEventController* pScrollController = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES);
    g_signal_connect(pScrollController, "scroll", G_CALLBACK(signalScroll), this);
    gtk_widget_add_controller(pEventWidget, pScrollController);
#endif

#if GTK_CHECK_VERSION(4,0,0)
    GtkGesture* pZoomGesture = gtk_gesture_zoom_new();
    gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pZoomGesture));
#else
    GtkGesture* pZoomGesture = gtk_gesture_zoom_new(GTK_WIDGET(pEventWidget));
    g_object_weak_ref(G_OBJECT(pEventWidget), notifyUnref, pZoomGesture);
#endif
    gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(pZoomGesture),
                                               GTK_PHASE_TARGET);
    // Note that the default zoom gesture signal handler needs to run first to setup correct
    // scale delta. Otherwise the first "begin" event will always contain scale delta of infinity.
    g_signal_connect_after(pZoomGesture, "begin", G_CALLBACK(signalZoomBegin), this);
    g_signal_connect_after(pZoomGesture, "update", G_CALLBACK(signalZoomUpdate), this);
    g_signal_connect_after(pZoomGesture, "end", G_CALLBACK(signalZoomEnd), this);

#if GTK_CHECK_VERSION(4,0,0)
    GtkGesture* pRotateGesture = gtk_gesture_rotate_new();
    gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pRotateGesture));
#else
    GtkGesture* pRotateGesture = gtk_gesture_rotate_new(GTK_WIDGET(pEventWidget));
    g_object_weak_ref(G_OBJECT(pEventWidget), notifyUnref, pRotateGesture);
#endif
    gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(pRotateGesture),
                                               GTK_PHASE_TARGET);
    g_signal_connect(pRotateGesture, "begin", G_CALLBACK(signalRotateBegin), this);
    g_signal_connect(pRotateGesture, "update", G_CALLBACK(signalRotateUpdate), this);
    g_signal_connect(pRotateGesture, "end", G_CALLBACK(signalRotateEnd), this);

    //Drop Target Stuff
#if GTK_CHECK_VERSION(4,0,0)
    GtkDropTargetAsync* pDropTarget = gtk_drop_target_async_new(nullptr, GdkDragAction(GDK_ACTION_ALL));
    g_signal_connect(G_OBJECT(pDropTarget), "drag-enter", G_CALLBACK(signalDragMotion)this);
    g_signal_connect(G_OBJECT(pDropTarget), "drag-motion", G_CALLBACK(signalDragMotion), this);
    g_signal_connect(G_OBJECT(pDropTarget), "drag-leave", G_CALLBACK(signalDragLeave), this);
    g_signal_connect(G_OBJECT(pDropTarget), "drop", G_CALLBACK(signalDragDrop), this);
    gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pDropTarget));
#else
    gtk_drag_dest_set(GTK_WIDGET(pEventWidget), GtkDestDefaults(0), nullptr, 0, GdkDragAction(0));
    gtk_drag_dest_set_track_motion(GTK_WIDGET(pEventWidget), true);
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-motion", G_CALLBACK(signalDragMotion), this ));
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-drop"G_CALLBACK(signalDragDrop), this ));
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-data-received", G_CALLBACK(signalDragDropReceived), this ));
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-leave", G_CALLBACK(signalDragLeave), this ));
#endif

#if !GTK_CHECK_VERSION(4,0,0)
    //Drag Source Stuff
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-end", G_CALLBACK(signalDragEnd), this ));
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-failed", G_CALLBACK(signalDragFailed), this ));
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-data-delete", G_CALLBACK(signalDragDelete), this ));
    m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-data-get", G_CALLBACK(signalDragDataGet), this ));
#endif

#if !GTK_CHECK_VERSION(4,0,0)
    g_signal_connect( G_OBJECT(m_pFixedContainer), "draw", G_CALLBACK(signalDraw), this );
    g_signal_connect( G_OBJECT(m_pFixedContainer), "size-allocate", G_CALLBACK(sizeAllocated), this );
#else
    gtk_drawing_area_set_draw_func(m_pDrawingArea, signalDraw, this, nullptr);
    g_signal_connect(G_OBJECT(m_pDrawingArea), "resize", G_CALLBACK(sizeAllocated), this);
#endif

    g_signal_connect(G_OBJECT(m_pFixedContainer), "realize", G_CALLBACK(signalRealize)this);

#if !GTK_CHECK_VERSION(4,0,0)
    GtkGesture *pSwipe = gtk_gesture_swipe_new(pEventWidget);
    g_object_weak_ref(G_OBJECT(pEventWidget), notifyUnref, pSwipe);
#else
    GtkGesture *pSwipe = gtk_gesture_swipe_new();
    gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pSwipe));
#endif
    g_signal_connect(pSwipe, "swipe", G_CALLBACK(gestureSwipe), this);
    gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pSwipe), GTK_PHASE_TARGET);

#if !GTK_CHECK_VERSION(4,0,0)
    GtkGesture *pLongPress = gtk_gesture_long_press_new(pEventWidget);
    g_object_weak_ref(G_OBJECT(pEventWidget), notifyUnref, pLongPress);
#else
    GtkGesture *pLongPress = gtk_gesture_long_press_new();
    gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pLongPress));
#endif
    g_signal_connect(pLongPress, "pressed", G_CALLBACK(gestureLongPress), this);
    gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pLongPress), GTK_PHASE_TARGET);

#if !GTK_CHECK_VERSION(4,0,0)
    g_signal_connect_after( G_OBJECT(m_pWindow), "focus-in-event", G_CALLBACK(signalFocus), this );
    g_signal_connect_after( G_OBJECT(m_pWindow), "focus-out-event", G_CALLBACK(signalFocus), this );
    if (GTK_IS_WINDOW(m_pWindow)) // i.e. not if it's a GtkEventBox which doesn't have the signal
        m_nSetFocusSignalId = g_signal_connect( G_OBJECT(m_pWindow), "set-focus", G_CALLBACK(signalSetFocus), this );
#else
    GtkEventController* pFocusController = gtk_event_controller_focus_new();
    g_signal_connect(pFocusController, "enter", G_CALLBACK(signalFocusEnter), this);
    g_signal_connect(pFocusController, "leave", G_CALLBACK(signalFocusLeave), this);
    gtk_widget_set_focusable(pEventWidget, true);
    gtk_widget_add_controller(pEventWidget, pFocusController);
    if (GTK_IS_WINDOW(m_pWindow)) // i.e. not if it's a GtkEventBox which doesn't have the property (presumably?)
        m_nSetFocusSignalId = g_signal_connect( G_OBJECT(m_pWindow), "notify::focus-widget"G_CALLBACK(signalSetFocus), this );
#endif
#if !GTK_CHECK_VERSION(4,0,0)
    g_signal_connect( G_OBJECT(m_pWindow), "map-event", G_CALLBACK(signalMap), this );
    g_signal_connect( G_OBJECT(m_pWindow), "unmap-event", G_CALLBACK(signalUnmap), this );
    g_signal_connect( G_OBJECT(m_pWindow), "delete-event", G_CALLBACK(signalDelete), this );
#else
    g_signal_connect( G_OBJECT(m_pWindow), "map", G_CALLBACK(signalMap), this );
    g_signal_connect( G_OBJECT(m_pWindow), "unmap", G_CALLBACK(signalUnmap), this );
    if (GTK_IS_WINDOW(m_pWindow))
        g_signal_connect( G_OBJECT(m_pWindow), "close-request", G_CALLBACK(signalDelete), this );
#endif
#if !GTK_CHECK_VERSION(4,0,0)
    g_signal_connect( G_OBJECT(m_pWindow), "configure-event", G_CALLBACK(signalConfigure), this );
#endif

#if !GTK_CHECK_VERSION(4,0,0)
    g_signal_connect( G_OBJECT(m_pWindow), "key-press-event", G_CALLBACK(signalKey), this );
    g_signal_connect( G_OBJECT(m_pWindow), "key-release-event", G_CALLBACK(signalKey), this );
#else
    m_pKeyController = GTK_EVENT_CONTROLLER_KEY(gtk_event_controller_key_new());
    g_signal_connect(m_pKeyController, "key-pressed", G_CALLBACK(signalKeyPressed), this);
    g_signal_connect(m_pKeyController, "key-released", G_CALLBACK(signalKeyReleased), this);
    gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(m_pKeyController));

#endif
    g_signal_connect( G_OBJECT(m_pWindow), "destroy", G_CALLBACK(signalDestroy), this );

    // init members
    m_nKeyModifiers     = ModKeyFlags::NONE;
    m_bFullscreen       = false;
#if GTK_CHECK_VERSION(4,0,0)
    m_nState            = static_cast<GdkToplevelState>(0);
#else
    m_nState            = GDK_WINDOW_STATE_WITHDRAWN;
#endif
    m_pIMHandler        = nullptr;
    m_pRegion           = nullptr;
    m_pDropTarget       = nullptr;
    m_pDragSource       = nullptr;
    m_bGeometryIsProvisional = false;
    m_bIconSetWhileUnmapped = false;
    m_bTooltipBlocked   = false;
    m_ePointerStyle     = static_cast<PointerStyle>(0xffff);
    m_pSalMenu          = nullptr;
    m_nWatcherId        = 0;
    m_nMenuExportId     = 0;
    m_nActionGroupExportId = 0;
    m_nHudAwarenessId   = 0;

#if !GTK_CHECK_VERSION(4,0,0)
    gtk_widget_add_events( m_pWindow,
                           GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
                           GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
                           GDK_SCROLL_MASK | GDK_TOUCHPAD_GESTURE_MASK
                           );
#endif

    // show the widgets
#if !GTK_CHECK_VERSION(4,0,0)
    gtk_widget_show_all(GTK_WIDGET(m_pTopLevelGrid));
#else
    gtk_widget_set_visible(GTK_WIDGET(m_pTopLevelGrid), true);
#endif

    // realize the window, we need an XWindow id
    gtk_widget_realize( m_pWindow );

    if (GTK_IS_WINDOW(m_pWindow))
    {
#if !GTK_CHECK_VERSION(4,0,0)
        g_signal_connect(G_OBJECT(m_pWindow), "window-state-event", G_CALLBACK(signalWindowState), this);
#else
        GdkSurface* gdkWindow = widget_get_surface(m_pWindow);
        g_signal_connect(G_OBJECT(gdkWindow), "notify::state", G_CALLBACK(signalWindowState), this);
#endif
    }

    //system data
    m_aSystemData.SetWindowHandle(GetNativeWindowHandle(m_pWindow));
    m_aSystemData.pSalFrame     = this;
    m_aSystemData.pWidget       = m_pWindow;
    m_aSystemData.nScreen       = m_nXScreen.getXScreen();
    m_aSystemData.toolkit       = SystemEnvData::Toolkit::Gtk;

#if defined(GDK_WINDOWING_X11)
    GdkDisplay *pDisplay = getGdkDisplay();
    if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay))
    {
        m_aSystemData.pDisplay = gdk_x11_display_get_xdisplay(pDisplay);
        m_aSystemData.platform = SystemEnvData::Platform::Xcb;
#if !GTK_CHECK_VERSION(4,0,0)
        GdkScreen* pScreen = gtk_widget_get_screen(m_pWindow);
        GdkVisual* pVisual = gdk_screen_get_system_visual(pScreen);
        m_aSystemData.pVisual = gdk_x11_visual_get_xvisual(pVisual);
#endif
    }
#endif
#if defined(GDK_WINDOWING_WAYLAND)
    if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay))
    {
        m_aSystemData.pDisplay = gdk_wayland_display_get_wl_display(pDisplay);
        m_aSystemData.platform = SystemEnvData::Platform::Wayland;
    }
#endif

    m_bGraphics = false;
    m_pGraphics = nullptr;

    m_nFloatFlags = FloatWinPopupFlags::NONE;
    m_bFloatPositioned = false;

    m_nWidthRequest = 0;
    m_nHeightRequest = 0;

    // fake an initial geometry, gets updated via configure event or SetPosSize
    if (m_bDefaultPos || m_bDefaultSize)
    {
        Size aDefSize = calcDefaultSize();
        maGeometry.setPosSize({ -1, -1 }, aDefSize);
        maGeometry.setDecorations(0, 0, 0, 0);
    }
    updateScreenNumber();

    SetIcon(SV_ICON_ID_OFFICE);
}

GtkSalFrame *GtkSalFrame::getFromWindow( GtkWidget *pWindow )
{
    return static_cast<GtkSalFrame *>(g_object_get_data( G_OBJECT( pWindow ), "SalFrame" ));
}

void GtkSalFrame::DisallowCycleFocusOut()
{
    if (!m_nSetFocusSignalId)
        return;
    // don't enable/disable can-focus as control enters and leaves
    // embedded native gtk widgets
    g_signal_handler_disconnect(G_OBJECT(m_pWindow), m_nSetFocusSignalId);
    m_nSetFocusSignalId = 0;

#if !GTK_CHECK_VERSION(4, 0, 0)
    // gtk3: set container without can-focus and focus will tab between
    // the native embedded widgets using the default gtk handling for
    // that
    // gtk4: no need because the native widgets are the only
    // thing in the overlay and the drawing widget is underneath so
    // the natural focus cycle is sufficient
    gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), false);
#endif
}

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

void GtkSalFrame::AllowCycleFocusOut()
{
    if (m_nSetFocusSignalId)
        return;
#if !GTK_CHECK_VERSION(4,0,0)
    // 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);
#else
    m_nSetFocusSignalId = g_signal_connect(G_OBJECT(m_pWindow), "notify::focus-widget"G_CALLBACK(signalSetFocus), this);
#endif

#if !GTK_CHECK_VERSION(4, 0, 0)
    // set container without can-focus and focus will tab between
    // the native embedded widgets using the default gtk handling for
    // that
    // gtk4: no need because the native widgets are the only
    // thing in the overlay and the drawing widget is underneath so
    // the natural focus cycle is sufficient
    gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), true);
#endif
}

namespace
{
    enum ColorScheme
    {
        DEFAULT,
        PREFER_DARK,
        PREFER_LIGHT
    };

    void ReadColorScheme(GDBusProxy* proxy, GVariant** out)
    {
        g_autoptr (GVariant) ret =
            g_dbus_proxy_call_sync(proxy, "Read",
                                   g_variant_new ("(ss)""org.freedesktop.appearance""color-scheme"),
                                   G_DBUS_CALL_FLAGS_NONE, G_MAXINT, nullptr, nullptr);
        if (!ret)
            return;

        g_autoptr (GVariant) child = nullptr;
        g_variant_get(ret, "(v)", &child);
        g_variant_get(child, "v", out);

        return;
    }
}

void GtkSalFrame::SetColorScheme(GVariant* variant)
{
    if (!m_pWindow)
        return;

    guint32 color_scheme;

    switch (MiscSettings::GetAppColorMode())
    {
        default:
        case AppearanceMode::AUTO:
        {
            if (variant)
            {
                color_scheme = g_variant_get_uint32(variant);
                if (color_scheme > PREFER_LIGHT)
                    color_scheme = DEFAULT;
            }
            else
                color_scheme = DEFAULT;
            break;
        }
        case AppearanceMode::LIGHT:
            color_scheme = PREFER_LIGHT;
            break;
        case AppearanceMode::DARK:
            color_scheme = PREFER_DARK;
            break;
    }

    bool bDarkIconTheme(color_scheme == PREFER_DARK);
    GtkSettings* pSettings = gtk_widget_get_settings(m_pWindow);
    g_object_set(pSettings, "gtk-application-prefer-dark-theme", bDarkIconTheme, nullptr);
}

bool GtkSalFrame::GetUseDarkMode() const
{
    if (!m_pWindow)
        return false;
    GtkSettings* pSettings = gtk_widget_get_settings(m_pWindow);
    gboolean bDarkIconTheme = false;
    g_object_get(pSettings, "gtk-application-prefer-dark-theme", &bDarkIconTheme, nullptr);
    return bDarkIconTheme;
}

bool GtkSalFrame::GetUseReducedAnimation() const
{
    if (!m_pWindow)
        return false;
    GtkSettings* pSettings = gtk_widget_get_settings(m_pWindow);
    gboolean bAnimations;
    g_object_get(pSettings, "gtk-enable-animations", &bAnimations, nullptr);
    return !bAnimations;
}

static void settings_portal_changed_cb(GDBusProxy*, const char*, const char* signal_name,
                                       GVariant* parameters, gpointer frame)
{
    if (g_strcmp0(signal_name, "SettingChanged"))
        return;

    g_autoptr (GVariant) value = nullptr;
    const char *name_space;
    const char *name;
    g_variant_get(parameters, "(&s&sv)", &name_space, &name, &value);

    if (g_strcmp0(name_space, "org.freedesktop.appearance") ||
        g_strcmp0(name, "color-scheme"))
      return;

    GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
    pThis->SetColorScheme(value);
}

void GtkSalFrame::ListenPortalSettings()
{
    EnsureSessionBus();

    if (!pSessionBus)
        return;

    m_pSettingsPortal = g_dbus_proxy_new_sync(pSessionBus,
                                              G_DBUS_PROXY_FLAGS_NONE,
                                              nullptr,
                                              "org.freedesktop.portal.Desktop",
                                              "/org/freedesktop/portal/desktop",
                                              "org.freedesktop.portal.Settings",
                                              nullptr,
                                              nullptr);

    UpdateDarkMode();

    if (!m_pSettingsPortal)
        return;

    m_nPortalSettingChangedSignalId = g_signal_connect(m_pSettingsPortal, "g-signal", G_CALLBACK(settings_portal_changed_cb), this);
}

static void session_client_response(GDBusProxy* client_proxy)
{
    g_dbus_proxy_call(client_proxy,
                      "EndSessionResponse",
                      g_variant_new ("(bs)"true""),
                      G_DBUS_CALL_FLAGS_NONE,
                      G_MAXINT,
                      nullptr, nullptr, nullptr);
}

// unset documents "modify" flag so they won't veto closing
static void clear_modify_and_terminate()
{
    const css::uno::Reference<css::uno::XComponentContext>& xContext = ::comphelper::getProcessComponentContext();
    uno::Reference<frame::XDesktop> xDesktop(frame::Desktop::create(xContext));
    uno::Reference<css::container::XEnumeration> xComponents = xDesktop->getComponents()->createEnumeration();
    while (xComponents->hasMoreElements())
    {
        css::uno::Reference<css::util::XModifiable> xModifiable(xComponents->nextElement(), css::uno::UNO_QUERY);
        if (xModifiable)
            xModifiable->setModified(false);
    }
    xDesktop->terminate();
}

static void session_client_signal(GDBusProxy* client_proxy, const char*, const char* signal_name,
                                  GVariant* /*parameters*/, gpointer frame)
{
    GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);

    if (g_str_equal (signal_name, "QueryEndSession"))
    {
        const css::uno::Reference<css::uno::XComponentContext>& xContext = ::comphelper::getProcessComponentContext();
        uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(xContext));

        bool bModified = false;

        // find the XModifiable for this GtkSalFrame
        if (UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper(false))
        {
            VclPtr<vcl::Window> xThisWindow = pThis->GetWindow();
            css::uno::Reference<css::container::XIndexAccess> xList = xDesktop->getFrames();
            sal_Int32 nFrameCount = xList->getCount();
            for (sal_Int32 i = 0; i < nFrameCount; ++i)
            {
                css::uno::Reference<css::frame::XFrame> xFrame;
                xList->getByIndex(i) >>= xFrame;
                if (!xFrame)
                    continue;
                VclPtr<vcl::Window> xWin = pWrapper->GetWindow(xFrame->getContainerWindow());
                if (!xWin)
                   continue;
                if (xWin->GetFrameWindow() != xThisWindow)
                    continue;
                css::uno::Reference<css::frame::XController> xController = xFrame->getController();
                if (!xController)
                    break;
                css::uno::Reference<css::util::XModifiable> xModifiable(xController->getModel(), css::uno::UNO_QUERY);
                if (!xModifiable)
                    break;
                bModified = xModifiable->isModified();
                break;
            }
        }

        pThis->SessionManagerInhibit(bModified, APPLICATION_INHIBIT_LOGOUT, VclResId(STR_UNSAVED_DOCUMENTS),
                                     gtk_window_get_icon_name(GTK_WINDOW(pThis->getWindow())));

        session_client_response(client_proxy);
    }
    else if (g_str_equal (signal_name, "CancelEndSession"))
    {
        // restore back to uninhibited (to set again if queried), so frames
        // that go away before the next logout don't affect that logout
        pThis->SessionManagerInhibit(false, APPLICATION_INHIBIT_LOGOUT, VclResId(STR_UNSAVED_DOCUMENTS),
                                     gtk_window_get_icon_name(GTK_WINDOW(pThis->getWindow())));
    }
    else if (g_str_equal (signal_name, "EndSession"))
    {
        session_client_response(client_proxy);
        clear_modify_and_terminate();
    }
    else if (g_str_equal (signal_name, "Stop"))
    {
        clear_modify_and_terminate();
    }
}

void GtkSalFrame::ListenSessionManager()
{
    EnsureSessionBus();

    if (!pSessionBus)
        return;

    m_pSessionManager = g_dbus_proxy_new_sync(pSessionBus,
                                              G_DBUS_PROXY_FLAGS_NONE,
                                              nullptr,
                                              "org.gnome.SessionManager",
                                              "/org/gnome/SessionManager",
                                              "org.gnome.SessionManager",
                                              nullptr,
                                              nullptr);

    if (!m_pSessionManager)
        return;

    GVariant* res = g_dbus_proxy_call_sync(m_pSessionManager,
                                 "RegisterClient",
                                 g_variant_new ("(ss)""org.libreoffice"""),
                                 G_DBUS_CALL_FLAGS_NONE,
                                 G_MAXINT,
                                 nullptr,
                                 nullptr);

    if (!res)
        return;

    gchar* client_path;
    g_variant_get(res, "(o)", &client_path);
    g_variant_unref(res);

    m_pSessionClient = g_dbus_proxy_new_sync(pSessionBus,
                                             G_DBUS_PROXY_FLAGS_NONE,
                                             nullptr,
                                             "org.gnome.SessionManager",
                                             client_path,
                                             "org.gnome.SessionManager.ClientPrivate",
                                             nullptr,
                                             nullptr);

    g_free(client_path);

    if (!m_pSessionClient)
        return;

    m_nSessionClientSignalId = g_signal_connect(m_pSessionClient, "g-signal", G_CALLBACK(session_client_signal), this);
}

void GtkSalFrame::UpdateDarkMode()
{
    g_autoptr (GVariant) value = nullptr;
    if (m_pSettingsPortal)
        ReadColorScheme(m_pSettingsPortal, &value);
    SetColorScheme(value);
}

#if GTK_CHECK_VERSION(4,0,0)
static void PopoverClosed(GtkPopover*, GtkSalFrame* pThis)
{
    SolarMutexGuard aGuard;
    pThis->closePopup();
}
#endif

void GtkSalFrame::Init( SalFrame* pParent, SalFrameStyleFlags nStyle )
{
    if( nStyle & SalFrameStyleFlags::DEFAULT ) // ensure default style
    {
        nStyle |= SalFrameStyleFlags::MOVEABLE | SalFrameStyleFlags::SIZEABLE | SalFrameStyleFlags::CLOSEABLE;
        nStyle &= ~SalFrameStyleFlags::FLOAT;
    }

    m_pParent = static_cast<GtkSalFrame*>(pParent);
#if !GTK_CHECK_VERSION(4,0,0)
    m_pForeignParent = nullptr;
    m_aForeignParentWindow = None;
    m_pForeignTopLevel = nullptr;
    m_aForeignTopLevelWindow = None;
#endif
    m_nStyle = nStyle;

    bool bPopup = ((nStyle & SalFrameStyleFlags::FLOAT) &&
                   !(nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION));

    if( nStyle & SalFrameStyleFlags::SYSTEMCHILD )
    {
#if !GTK_CHECK_VERSION(4,0,0)
        m_pWindow = gtk_event_box_new();
#else
        m_pWindow = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
#endif
        if( m_pParent )
        {
            // insert into container
            gtk_fixed_put( m_pParent->getFixedContainer(),
                           m_pWindow, 0, 0 );
        }
    }
    else
    {
#if !GTK_CHECK_VERSION(4,0,0)
        m_pWindow = gtk_window_new(bPopup ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL);
#else
        if (!bPopup)
            m_pWindow = gtk_window_new();
        else
        {
            m_pWindow = gtk_popover_new();
            gtk_popover_set_has_arrow(GTK_POPOVER(m_pWindow), false);
            g_signal_connect(m_pWindow, "closed", G_CALLBACK(PopoverClosed), this);
        }
#endif

#if !GTK_CHECK_VERSION(4,0,0)
        // hook up F1 to show help for embedded native gtk widgets
        GtkAccelGroup *pGroup = gtk_accel_group_new();
        GClosure* closure = g_cclosure_new(G_CALLBACK(GtkSalFrame::NativeWidgetHelpPressed), GTK_WINDOW(m_pWindow), nullptr);
        gtk_accel_group_connect(pGroup, GDK_KEY_F1, static_cast<GdkModifierType>(0), GTK_ACCEL_LOCKED, closure);
        gtk_window_add_accel_group(GTK_WINDOW(m_pWindow), pGroup);
#endif
    }

    g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame"this );
    g_object_set_data( G_OBJECT( m_pWindow ), "libo-version"const_cast<char *>(LIBO_VERSION_DOTTED));

    // force wm class hint
    if (!isChild())
    {
        if (m_pParent)
            m_sWMClass = m_pParent->m_sWMClass;
        updateWMClass();
    }

    if (GTK_IS_WINDOW(m_pWindow))
    {
        if (m_pParent)
        {
            GtkWidget* pTopLevel = widget_get_toplevel(m_pParent->m_pWindow);
#if !GTK_CHECK_VERSION(4,0,0)
            if (!isChild())
                gtk_window_set_screen(GTK_WINDOW(m_pWindow), gtk_widget_get_screen(pTopLevel));
#endif

            if (!(m_pParent->m_nStyle & SalFrameStyleFlags::PLUG))
                gtk_window_set_transient_for(GTK_WINDOW(m_pWindow), GTK_WINDOW(pTopLevel));
            m_pParent->m_aChildren.push_back( this );
            gtk_window_group_add_window(gtk_window_get_group(GTK_WINDOW(pTopLevel)), GTK_WINDOW(m_pWindow));
        }
        else
        {
            gtk_window_group_add_window(gtk_window_group_new(), GTK_WINDOW(m_pWindow));
            g_object_unref(gtk_window_get_group(GTK_WINDOW(m_pWindow)));
        }
    }
    else if (GTK_IS_POPOVER(m_pWindow))
    {
        assert(m_pParent);
        gtk_widget_set_parent(m_pWindow, m_pParent->getMouseEventWidget());
    }

    // set window type
    bool bDecoHandling =
        ! isChild() &&
        ( ! (nStyle & SalFrameStyleFlags::FLOAT) ||
          (nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) );

    if( bDecoHandling )
    {
#if !GTK_CHECK_VERSION(4,0,0)
        GdkWindowTypeHint eType = GDK_WINDOW_TYPE_HINT_NORMAL;
        if( (nStyle & SalFrameStyleFlags::DIALOG) && m_pParent != nullptr )
            eType = GDK_WINDOW_TYPE_HINT_DIALOG;
#endif
        if( nStyle & SalFrameStyleFlags::INTRO )
        {
#if !GTK_CHECK_VERSION(4,0,0)
            gtk_window_set_role( GTK_WINDOW(m_pWindow), "splashscreen" );
            eType = GDK_WINDOW_TYPE_HINT_SPLASHSCREEN;
#endif
        }
        else if( nStyle & SalFrameStyleFlags::TOOLWINDOW )
        {
#if !GTK_CHECK_VERSION(4,0,0)
            eType = GDK_WINDOW_TYPE_HINT_DIALOG;
            gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow), true );
#endif
        }
        else if( nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION )
        {
#if !GTK_CHECK_VERSION(4,0,0)
            eType = GDK_WINDOW_TYPE_HINT_TOOLBAR;
            gtk_window_set_focus_on_map(GTK_WINDOW(m_pWindow), false);
#endif
            gtk_window_set_decorated(GTK_WINDOW(m_pWindow), false);
        }
#if !GTK_CHECK_VERSION(4,0,0)
        gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), eType );
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=97 G=93

¤ Dauer der Verarbeitung: 0.18 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.