From a95302af9fabfc9a4e6b5d2983b9f75645f47438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= Date: Wed, 9 Dec 2015 15:33:06 +0000 Subject: [PATCH] move things around a little to make backporting easier Change-Id: I0fa86b4b524056f7a9fac228062401ea1ec2ca25 --- vcl/Library_vclplug_gtk.mk | 2 +- vcl/Library_vclplug_gtk3.mk | 2 +- vcl/inc/unx/gtk/gtkframe.hxx | 1 - vcl/unx/gtk/gtksalframe.cxx | 4883 ++++++++++++++++++++++++++++++++++ vcl/unx/gtk/window/gtksalframe.cxx | 4883 ---------------------------------- vcl/unx/gtk3/app/gtk3gtkinst.cxx | 641 ----- vcl/unx/gtk3/gtk3gtkframe.cxx | 3803 ++++++++++++++++++++++++++ vcl/unx/gtk3/gtk3gtkinst.cxx | 641 +++++ vcl/unx/gtk3/window/gtk3gtkframe.cxx | 12 - 9 files changed, 9329 insertions(+), 5539 deletions(-) create mode 100644 vcl/unx/gtk/gtksalframe.cxx delete mode 100644 vcl/unx/gtk/window/gtksalframe.cxx delete mode 100644 vcl/unx/gtk3/app/gtk3gtkinst.cxx create mode 100644 vcl/unx/gtk3/gtk3gtkframe.cxx create mode 100644 vcl/unx/gtk3/gtk3gtkinst.cxx delete mode 100644 vcl/unx/gtk3/window/gtk3gtkframe.cxx diff --git a/vcl/Library_vclplug_gtk.mk b/vcl/Library_vclplug_gtk.mk index ec7f2f8..6d10d5c 100644 --- a/vcl/Library_vclplug_gtk.mk +++ b/vcl/Library_vclplug_gtk.mk @@ -100,7 +100,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_gtk,\ vcl/unx/gtk/app/gtkinst \ vcl/unx/gtk/app/gtksys \ vcl/unx/gtk/gdi/salnativewidgets-gtk \ - vcl/unx/gtk/window/gtksalframe \ + vcl/unx/gtk/gtksalframe \ vcl/unx/gtk/window/gtkobject \ vcl/unx/gtk/fpicker/resourceprovider \ vcl/unx/gtk/fpicker/SalGtkPicker \ diff --git a/vcl/Library_vclplug_gtk3.mk b/vcl/Library_vclplug_gtk3.mk index 515a81c..222c9d6 100644 --- a/vcl/Library_vclplug_gtk3.mk +++ b/vcl/Library_vclplug_gtk3.mk @@ -99,7 +99,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_gtk3,\ vcl/unx/gtk3/a11y/gtk3atkwindow \ vcl/unx/gtk3/a11y/gtk3atkwrapper \ vcl/unx/gtk3/app/gtk3gtkdata \ - vcl/unx/gtk3/app/gtk3gtkinst \ + vcl/unx/gtk3/gtk3gtkinst \ vcl/unx/gtk3/app/gtk3gtksys \ vcl/unx/gtk3/app/gtk3fpicker \ vcl/unx/gtk3/gdi/cairo_gtk3_cairo \ @@ -107,7 +107,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_gtk3,\ vcl/unx/gtk3/gdi/gtk3gtkprintwrapper \ vcl/unx/gtk3/gdi/gtk3salnativewidgets-gtk \ vcl/unx/gtk3/gdi/gtk3salprn-gtk \ - vcl/unx/gtk3/window/gtk3gtkframe \ + vcl/unx/gtk3/gtk3gtkframe \ vcl/unx/gtk3/window/gtk3gtkobject \ vcl/unx/gtk3/window/gtk3gtksalmenu \ vcl/unx/gtk3/window/gtk3glomenu \ diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx index 4594249..0fb5da7 100644 --- a/vcl/inc/unx/gtk/gtkframe.hxx +++ b/vcl/inc/unx/gtk/gtkframe.hxx @@ -240,7 +240,6 @@ class GtkSalFrame : public SalFrame, public X11WindowProvider static void signalStyleSet( GtkWidget*, GtkStyle* pPrevious, gpointer ); #if GTK_CHECK_VERSION(3,0,0) static gboolean signalDraw( GtkWidget*, cairo_t *cr, gpointer ); - static void signalFlagsChanged( GtkWidget*, GtkStateFlags, gpointer ); static void sizeAllocated(GtkWidget*, GdkRectangle *pAllocation, gpointer frame); #if GTK_CHECK_VERSION(3,14,0) static void gestureSwipe(GtkGestureSwipe* gesture, gdouble velocity_x, gdouble velocity_y, gpointer frame); diff --git a/vcl/unx/gtk/gtksalframe.cxx b/vcl/unx/gtk/gtksalframe.cxx new file mode 100644 index 0000000..9e03f14 --- /dev/null +++ b/vcl/unx/gtk/gtksalframe.cxx @@ -0,0 +1,4883 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !GTK_CHECK_VERSION(3,0,0) +# include +#endif +#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) +# include +#endif +#if defined ENABLE_GMENU_INTEGRATION // defined in gtksalmenu.hxx above +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#if OSL_DEBUG_LEVEL > 1 +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if GTK_CHECK_VERSION(3,0,0) +# include +#endif + +#ifdef ENABLE_DBUS +#include + +#define GSM_DBUS_SERVICE "org.gnome.SessionManager" +#define GSM_DBUS_PATH "/org/gnome/SessionManager" +#define GSM_DBUS_INTERFACE "org.gnome.SessionManager" +#endif + +#include + +#if GTK_CHECK_VERSION(3,0,0) +#define IS_WIDGET_REALIZED gtk_widget_get_realized +#define IS_WIDGET_MAPPED gtk_widget_get_mapped +#else +#define IS_WIDGET_REALIZED GTK_WIDGET_REALIZED +#define IS_WIDGET_MAPPED GTK_WIDGET_MAPPED +#endif + +#if !GTK_CHECK_VERSION(3,0,0) +#define GDK_IS_X11_DISPLAY(foo) (true) +#endif + +using namespace com::sun::star; + +int GtkSalFrame::m_nFloats = 0; + +#if defined ENABLE_GMENU_INTEGRATION +static GDBusConnection* pSessionBus = NULL; +#endif + +static sal_uInt16 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_MOD1_MASK) ) + nCode |= KEY_MOD2; + + // Map Meta/Super keys to MOD3 modifier on all Unix systems + // except Mac OS X + if ( (state & GDK_META_MASK ) || ( state & GDK_SUPER_MASK ) ) + nCode |= KEY_MOD3; + return nCode; +} + +static sal_uInt16 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; +} + +static sal_uInt16 GetKeyCode( guint keyval ) +{ + sal_uInt16 nCode = 0; + if( keyval >= GDK_0 && keyval <= GDK_9 ) + nCode = KEY_0 + (keyval-GDK_0); + else if( keyval >= GDK_KP_0 && keyval <= GDK_KP_9 ) + nCode = KEY_0 + (keyval-GDK_KP_0); + else if( keyval >= GDK_A && keyval <= GDK_Z ) + nCode = KEY_A + (keyval-GDK_A ); + else if( keyval >= GDK_a && keyval <= GDK_z ) + nCode = KEY_A + (keyval-GDK_a ); + else if( keyval >= GDK_F1 && keyval <= GDK_F26 ) + { +#if !GTK_CHECK_VERSION(3,0,0) + if( GetGtkSalData()->GetGtkDisplay()->IsNumLockFromXS() ) + { + nCode = KEY_F1 + (keyval-GDK_F1); + } + else +#endif + { + switch( keyval ) + { + // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx + case GDK_L2: +#if !GTK_CHECK_VERSION(3,0,0) + if( GetGtkSalData()->GetGtkDisplay()->GetServerVendor() == vendor_sun ) + nCode = KEY_REPEAT; + else +#endif + nCode = KEY_F12; + break; + case GDK_L3: nCode = KEY_PROPERTIES; break; + case GDK_L4: nCode = KEY_UNDO; break; + case GDK_L6: nCode = KEY_COPY; break; // KEY_F16 + case GDK_L8: nCode = KEY_PASTE; break; // KEY_F18 + case GDK_L10: nCode = KEY_CUT; break; // KEY_F20 + default: + nCode = KEY_F1 + (keyval-GDK_F1); break; + } + } + } + else + { + switch( keyval ) + { + case GDK_KP_Down: + case GDK_Down: nCode = KEY_DOWN; break; + case GDK_KP_Up: + case GDK_Up: nCode = KEY_UP; break; + case GDK_KP_Left: + case GDK_Left: nCode = KEY_LEFT; break; + case GDK_KP_Right: + case GDK_Right: nCode = KEY_RIGHT; break; + case GDK_KP_Begin: + case GDK_KP_Home: + case GDK_Begin: + case GDK_Home: nCode = KEY_HOME; break; + case GDK_KP_End: + case GDK_End: nCode = KEY_END; break; + case GDK_KP_Page_Up: + case GDK_Page_Up: nCode = KEY_PAGEUP; break; + case GDK_KP_Page_Down: + case GDK_Page_Down: nCode = KEY_PAGEDOWN; break; + case GDK_KP_Enter: + case GDK_Return: nCode = KEY_RETURN; break; + case GDK_Escape: nCode = KEY_ESCAPE; break; + case GDK_ISO_Left_Tab: + case GDK_KP_Tab: + case GDK_Tab: nCode = KEY_TAB; break; + case GDK_BackSpace: nCode = KEY_BACKSPACE; break; + case GDK_KP_Space: + case GDK_space: nCode = KEY_SPACE; break; + case GDK_KP_Insert: + case GDK_Insert: nCode = KEY_INSERT; break; + case GDK_KP_Delete: + case GDK_Delete: nCode = KEY_DELETE; break; + case GDK_plus: + case GDK_KP_Add: nCode = KEY_ADD; break; + case GDK_minus: + case GDK_KP_Subtract: nCode = KEY_SUBTRACT; break; + case GDK_asterisk: + case GDK_KP_Multiply: nCode = KEY_MULTIPLY; break; + case GDK_slash: + case GDK_KP_Divide: nCode = KEY_DIVIDE; break; + case GDK_period: nCode = KEY_POINT; break; + case GDK_decimalpoint: nCode = KEY_POINT; break; + case GDK_comma: nCode = KEY_COMMA; break; + case GDK_less: nCode = KEY_LESS; break; + case GDK_greater: nCode = KEY_GREATER; break; + case GDK_KP_Equal: + case GDK_equal: nCode = KEY_EQUAL; break; + case GDK_Find: nCode = KEY_FIND; break; + case GDK_Menu: nCode = KEY_CONTEXTMENU;break; + case GDK_Help: nCode = KEY_HELP; break; + case GDK_Undo: nCode = KEY_UNDO; break; + case GDK_Redo: nCode = KEY_REPEAT; break; + case GDK_KP_Decimal: + case GDK_KP_Separator: nCode = KEY_DECIMAL; break; + case GDK_asciitilde: nCode = KEY_TILDE; break; + case GDK_leftsinglequotemark: + case GDK_quoteleft: nCode = KEY_QUOTELEFT; break; + case GDK_bracketleft: nCode = KEY_BRACKETLEFT; break; + case GDK_bracketright: nCode = KEY_BRACKETRIGHT; break; + case GDK_semicolon: nCode = KEY_SEMICOLON; break; + case GDK_quoteright: nCode = KEY_QUOTERIGHT; break; + // some special cases, also see saldisp.cxx + // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000 + 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 + case 0x1000FF00: + nCode = KEY_DELETE; + break; + // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000 + 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 + 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 + 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; + } + } + + return nCode; +} + +static guint 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, NULL, NULL, NULL); + return updated_keyval; +} + +// 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 ) {} +}; + +inline 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 GTK_CHECK_VERSION(3,0,0) + +namespace { +/// Decouple SalFrame lifetime from damagetracker lifetime +struct DamageTracker : public basebmp::IBitmapDeviceDamageTracker +{ + DamageTracker(GtkSalFrame& rFrame) : m_rFrame(rFrame) + {} + + virtual ~DamageTracker() {} + + virtual void damaged(const basegfx::B2IBox& rDamageRect) const SAL_OVERRIDE + { + m_rFrame.damaged(rDamageRect); + } + + GtkSalFrame& m_rFrame; +}; +} + +static bool dumpframes = false; +#endif + +void GtkSalFrame::doKeyCallback( guint state, + guint keyval, + guint16 hardware_keycode, + guint8 group, + guint32 time, + sal_Unicode aOrigCode, + bool bDown, + bool bSendRelease + ) +{ + SalKeyEvent aEvent; + + aEvent.mnTime = time; + aEvent.mnCharCode = aOrigCode; + aEvent.mnRepeat = 0; + + vcl::DeletionListener aDel( this ); + +#if GTK_CHECK_VERSION(3,0,0) +#if 0 + // shift-zero forces a re-draw and event is swallowed + if (keyval == GDK_0) + { + fprintf( stderr, "force widget_queue_draw\n"); + gtk_widget_queue_draw (m_pFixedContainer); + return; + } + else if (keyval == GDK_1) + { + fprintf( stderr, "force repaint all\n"); + TriggerPaintEvent(); + return; + } + else if (keyval == GDK_2) + { + dumpframes = !dumpframes; + fprintf(stderr, "toggle dump frames to %d\n", dumpframes); + return; + } +#endif +#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( 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_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); + } + + aEvent.mnCode |= GetKeyModCode( state ); + + if( bDown ) + { + bool bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent ); + // #i46889# copy AlternatKeyCode handling from generic plugin + if( ! bHandled ) + { + KeyAlternate aAlternate = GetAlternateKeyCode( aEvent.mnCode ); + if( aAlternate.nKeyCode ) + { + aEvent.mnCode = aAlternate.nKeyCode; + if( aAlternate.nCharCode ) + aEvent.mnCharCode = aAlternate.nCharCode; + bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent ); + } + } + if( bSendRelease && ! aDel.isDeleted() ) + { + CallCallback( SALEVENT_KEYUP, &aEvent ); + } + } + else + CallCallback( SALEVENT_KEYUP, &aEvent ); +} + +GtkSalFrame::GraphicsHolder::~GraphicsHolder() +{ + delete pGraphics; +} + +GtkSalFrame::GtkSalFrame( SalFrame* pParent, sal_uLong nStyle ) + : m_nXScreen( getDisplay()->GetDefaultXScreen() ) +{ + getDisplay()->registerFrame( this ); + m_bDefaultPos = true; + m_bDefaultSize = ( (nStyle & SAL_FRAME_STYLE_SIZEABLE) && ! pParent ); + m_bWindowIsGtkPlug = false; +#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) + m_pLastSyncedDbusMenu = NULL; +#endif + Init( pParent, nStyle ); +} + +GtkSalFrame::GtkSalFrame( SystemParentData* pSysData ) + : m_nXScreen( getDisplay()->GetDefaultXScreen() ) +{ + getDisplay()->registerFrame( this ); + // permanently ignore errors from our unruly children ... + GetGenericData()->ErrorTrapPush(); + m_bDefaultPos = true; + m_bDefaultSize = true; +#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) + m_pLastSyncedDbusMenu = NULL; +#endif + Init( pSysData ); +} + +#ifdef ENABLE_GMENU_INTEGRATION + +static void +gdk_x11_window_set_utf8_property (GdkWindow *window, + const gchar *name, + const gchar *value) +{ +#if !GTK_CHECK_VERSION(3,0,0) + GdkDisplay* display = gdk_window_get_display (window); + + if (value != NULL) + { + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_WINDOW_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, name), + gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, + PropModeReplace, reinterpret_cast(value), strlen (value)); + } + else + { + XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_WINDOW_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, name)); + } +#endif +} + +// AppMenu watch functions. + +static void ObjectDestroyedNotify( gpointer data ) +{ + if ( data ) { + g_object_unref( data ); + } +} + +#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) +void GtkSalFrame::EnsureDbusMenuSynced() +{ + GtkSalMenu* pSalMenu = static_cast(GetMenu()); + if(m_pLastSyncedDbusMenu != pSalMenu) { + m_pLastSyncedDbusMenu = pSalMenu; + static_cast(pSalMenu)->Activate(); + } +} +#endif + +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(); + } +} + +static void activate_uno(GSimpleAction *action, GVariant*, gpointer) +{ + uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + + uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext ); + + uno::Reference < css::frame::XFrame > xFrame(xDesktop->getActiveFrame()); + if (!xFrame.is()) + xFrame = uno::Reference < css::frame::XFrame >(xDesktop, uno::UNO_QUERY); + + if (!xFrame.is()) + return; + + uno::Reference< css::frame::XDispatchProvider > xDispatchProvider(xFrame, uno::UNO_QUERY); + if (!xDispatchProvider.is()) + return; + + gchar *strval = NULL; + g_object_get(action, "name", &strval, NULL); + if (!strval) + return; + + if (strcmp(strval, "New") == 0) + { + uno::Reference xModuleManager(frame::ModuleManager::create(xContext)); + OUString aModuleId(xModuleManager->identify(xFrame)); + if (aModuleId.isEmpty()) + return; + + comphelper::SequenceAsHashMap lModuleDescription(xModuleManager->getByName(aModuleId)); + OUString sFactoryService; + lModuleDescription[OUString("ooSetupFactoryEmptyDocumentURL")] >>= sFactoryService; + if (sFactoryService.isEmpty()) + return; + + uno::Sequence < css::beans::PropertyValue > args(0); + xDesktop->loadComponentFromURL(sFactoryService, OUString("_blank"), 0, args); + return; + } + + OUString sCommand(".uno:"); + sCommand += OUString(strval, strlen(strval), RTL_TEXTENCODING_UTF8); + g_free(strval); + + css::util::URL aCommand; + aCommand.Complete = sCommand; + uno::Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create(xContext); + xParser->parseStrict(aCommand); + + uno::Reference< css::frame::XDispatch > xDisp = xDispatchProvider->queryDispatch(aCommand, OUString(), 0); + + if (!xDisp.is()) + return; + + xDisp->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >()); +} + +static const GActionEntry app_entries[] = { + { "OptionsTreeDialog", activate_uno, NULL, NULL, NULL, {0} }, + { "About", activate_uno, NULL, NULL, NULL, {0} }, + { "HelpIndex", activate_uno, NULL, NULL, NULL, {0} }, + { "Quit", activate_uno, NULL, NULL, NULL, {0} }, + { "New", activate_uno, NULL, NULL, NULL, {0} } +}; + +gboolean ensure_dbus_setup( gpointer data ) +{ + GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( data ); + GdkWindow* gdkWindow = widget_get_window( pSalFrame->getWindow() ); + + if ( gdkWindow != NULL && g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) == NULL ) + { + // Get a DBus session connection. + if(!pSessionBus) + pSessionBus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + if( !pSessionBus ) + return FALSE; + + // Create menu model and action group attached to this frame. + GMenuModel* pMenuModel = G_MENU_MODEL( g_lo_menu_new() ); + GActionGroup* pActionGroup = reinterpret_cast(g_lo_action_group_new( static_cast< gpointer >( pSalFrame ) )); + + // Generate menu paths. + ::Window windowId = GDK_WINDOW_XID( gdkWindow ); + gchar* aDBusWindowPath = g_strdup_printf( "/org/libreoffice/window/%lu", windowId ); + gchar* aDBusMenubarPath = g_strdup_printf( "/org/libreoffice/window/%lu/menus/menubar", windowId ); + + // 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 ); + + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_ID", "org.libreoffice" ); + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_UNIQUE_BUS_NAME", g_dbus_connection_get_unique_name( pSessionBus ) ); + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_OBJECT_PATH", "/org/libreoffice" ); + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_WINDOW_OBJECT_PATH", aDBusWindowPath ); + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_MENUBAR_OBJECT_PATH", aDBusMenubarPath ); + + // 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, NULL); + SAL_INFO("vcl.unity", "exporting action group at " << pActionGroup << " for window " << windowId); + pSalFrame->m_nActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, aDBusWindowPath, pActionGroup, NULL); + pSalFrame->m_nHudAwarenessId = hud_awareness_register( pSessionBus, aDBusMenubarPath, hud_activated, pSalFrame, NULL, NULL ); + + // fdo#70885 we don't want app menu under Unity + bool bDesktopIsUnity = (SalGetDesktopEnvironment() == "UNITY"); + + if (!bDesktopIsUnity) + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APP_MENU_OBJECT_PATH", "/org/libreoffice/menus/appmenu" ); + + //app menu, to-do translations, block normal menus when active, honor use appmenu settings + ResMgr* pMgr = ImplGetResMgr(); + if( pMgr && !bDesktopIsUnity ) + { + GMenu *menu = g_menu_new (); + GMenuItem* item; + + GMenu *firstsubmenu = g_menu_new (); + + OString sNew(OUStringToOString(ResId(SV_BUTTONTEXT_NEW, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sNew.getStr(), "app.New"); + g_menu_append_item( firstsubmenu, item ); + g_object_unref(item); + + g_menu_append_section( menu, NULL, G_MENU_MODEL(firstsubmenu)); + g_object_unref(firstsubmenu); + + GMenu *secondsubmenu = g_menu_new (); + + OString sPreferences(OUStringToOString(ResId(SV_STDTEXT_PREFERENCES, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sPreferences.getStr(), "app.OptionsTreeDialog"); + g_menu_append_item( secondsubmenu, item ); + g_object_unref(item); + + g_menu_append_section( menu, NULL, G_MENU_MODEL(secondsubmenu)); + g_object_unref(secondsubmenu); + + GMenu *thirdsubmenu = g_menu_new (); + + OString sHelp(OUStringToOString(ResId(SV_BUTTONTEXT_HELP, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sHelp.getStr(), "app.HelpIndex"); + g_menu_append_item( thirdsubmenu, item ); + g_object_unref(item); + + OString sAbout(OUStringToOString(ResId(SV_STDTEXT_ABOUT, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sAbout.getStr(), "app.About"); + g_menu_append_item( thirdsubmenu, item ); + g_object_unref(item); + + OString sQuit(OUStringToOString(ResId(SV_MENU_MAC_QUITAPP, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sQuit.getStr(), "app.Quit"); + g_menu_append_item( thirdsubmenu, item ); + g_object_unref(item); + g_menu_append_section( menu, NULL, G_MENU_MODEL(thirdsubmenu)); + g_object_unref(thirdsubmenu); + + GSimpleActionGroup *group = g_simple_action_group_new (); +#if GLIB_CHECK_VERSION(2,38,0) // g_simple_action_group_add_entries is deprecated since 2.38 + g_action_map_add_action_entries (G_ACTION_MAP (group), app_entries, G_N_ELEMENTS (app_entries), NULL); +#else + g_simple_action_group_add_entries (group, app_entries, G_N_ELEMENTS (app_entries), NULL); +#endif + GActionGroup* pAppActionGroup = G_ACTION_GROUP(group); + + pSalFrame->m_nAppActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, "/org/libreoffice", pAppActionGroup, NULL); + g_object_unref(pAppActionGroup); + pSalFrame->m_nAppMenuExportId = g_dbus_connection_export_menu_model (pSessionBus, "/org/libreoffice/menus/appmenu", G_MENU_MODEL (menu), NULL); + g_object_unref(menu); + } + + g_free( aDBusMenubarPath ); + g_free( aDBusWindowPath ); + } + + return FALSE; +} + +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 != NULL ) + { + GtkSalMenu* pGtkSalMenu = static_cast(pSalMenu); + pGtkSalMenu->Display( true ); + pGtkSalMenu->UpdateFull(); + } +} + +// 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"); + + //pSessionBus = NULL; + GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data ); + + SalMenu* pSalMenu = pSalFrame->GetMenu(); + + if ( pSalMenu ) { + GtkSalMenu* pGtkSalMenu = static_cast< GtkSalMenu* >( pSalMenu ); + pGtkSalMenu->Display( false ); + } +} +#endif + +void GtkSalFrame::EnsureAppMenuWatch() +{ +#ifdef ENABLE_GMENU_INTEGRATION + if ( !m_nWatcherId ) + { + // Get a DBus session connection. + if ( pSessionBus == NULL ) + { + pSessionBus = g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, NULL ); + + if ( pSessionBus == NULL ) + 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, + static_cast(this), + NULL ); + } + + //ensure_dbus_setup( this ); +#else + (void) this; // loplugin:staticmethods +#endif +} + +void GtkSalFrame::InvalidateGraphics() +{ + for (unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); ++i) + { + if( !m_aGraphics[i].pGraphics ) + continue; +#if !GTK_CHECK_VERSION(3,0,0) + m_aGraphics[i].pGraphics->SetDrawable( None, m_nXScreen ); + m_aGraphics[i].pGraphics->SetWindow(NULL); +#endif + m_aGraphics[i].bInUse = false; + } +} + +GtkSalFrame::~GtkSalFrame() +{ + InvalidateGraphics(); + + if( m_pParent ) + m_pParent->m_aChildren.remove( this ); + + getDisplay()->deregisterFrame( this ); + + if( m_pRegion ) + { +#if GTK_CHECK_VERSION(3,0,0) + cairo_region_destroy( m_pRegion ); +#else + gdk_region_destroy( m_pRegion ); +#endif + } + +#if !GTK_CHECK_VERSION(3,0,0) + if( m_hBackgroundPixmap ) + { + XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(), + widget_get_xid(m_pWindow), + None ); + XFreePixmap( getDisplay()->GetDisplay(), m_hBackgroundPixmap ); + } +#endif + + if( m_pIMHandler ) + delete m_pIMHandler; + + GtkWidget *pEventWidget = getMouseEventWidget(); + for (auto handler_id : m_aMouseSignalIds) + g_signal_handler_disconnect(G_OBJECT(pEventWidget), handler_id); + if( m_pFixedContainer ) + gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer ) ); + if( m_pEventBox ) + gtk_widget_destroy( GTK_WIDGET(m_pEventBox) ); + { + SolarMutexGuard aGuard; +#if defined ENABLE_GMENU_INTEGRATION + if(m_nWatcherId) + g_bus_unwatch_name(m_nWatcherId); +#endif + if( m_pWindow ) + { + g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", NULL ); + +#if defined ENABLE_GMENU_INTEGRATION + 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_nAppMenuExportId ) + g_dbus_connection_unexport_menu_model( pSessionBus, m_nAppMenuExportId ); + if ( m_nActionGroupExportId ) + g_dbus_connection_unexport_action_group( pSessionBus, m_nActionGroupExportId ); + if ( m_nAppActionGroupExportId ) + g_dbus_connection_unexport_action_group( pSessionBus, m_nAppActionGroupExportId ); + } +#endif + gtk_widget_destroy( m_pWindow ); + } + } + if( m_pForeignParent ) + g_object_unref( G_OBJECT( m_pForeignParent ) ); + if( m_pForeignTopLevel ) + g_object_unref( G_OBJECT( m_pForeignTopLevel) ); +} + +void GtkSalFrame::moveWindow( long nX, long nY ) +{ + if( isChild( false, true ) ) + { + if( m_pParent ) + gtk_fixed_move( m_pParent->getFixedContainer(), + m_pWindow, + nX - m_pParent->maGeometry.nX, nY - m_pParent->maGeometry.nY ); + } + else + gtk_window_move( GTK_WINDOW(m_pWindow), nX, nY ); +} + +void GtkSalFrame::widget_set_size_request(long nWidth, long nHeight) +{ +#if !GTK_CHECK_VERSION(3,0,0) + gint nOrigwidth, nOrigheight; + gtk_window_get_size(GTK_WINDOW(m_pWindow), &nOrigwidth, &nOrigheight); + if (nWidth > nOrigwidth || nHeight > nOrigheight) + { + m_bPaintsBlocked = true; + } + gtk_widget_set_size_request(m_pWindow, nWidth, nHeight ); +#else + gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer), nWidth, nHeight ); +#endif +} + +void GtkSalFrame::window_resize(long nWidth, long nHeight) +{ +#if !GTK_CHECK_VERSION(3,0,0) + gint nOrigwidth, nOrigheight; + gtk_window_get_size(GTK_WINDOW(m_pWindow), &nOrigwidth, &nOrigheight); + if (nWidth > nOrigwidth || nHeight > nOrigheight) + { + m_bPaintsBlocked = true; + } +#endif + gtk_window_resize(GTK_WINDOW(m_pWindow), nWidth, nHeight); +} + +void GtkSalFrame::resizeWindow( long nWidth, long nHeight ) +{ + if( isChild( false, true ) ) + { + widget_set_size_request(nWidth, nHeight); + } + else if( ! isChild( true, false ) ) + window_resize(nWidth, nHeight); +} + +#if GTK_CHECK_VERSION(3,2,0) + +static void +ooo_fixed_class_init(GtkFixedClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + widget_class->get_accessible = ooo_fixed_get_accessible; +} + +#endif + +/* + * 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 */ +#if GTK_CHECK_VERSION(3,2,0) + reinterpret_cast(ooo_fixed_class_init), /* class init */ +#else + nullptr, /* class init */ +#endif + nullptr, /* class finalize */ + NULL, /* class data */ + sizeof (GtkFixed), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static( GTK_TYPE_FIXED, "OOoFixed", + &tinfo, (GTypeFlags) 0); + } + + return type; +} + +void GtkSalFrame::updateScreenNumber() +{ + int nScreen = 0; + GdkScreen *pScreen = gtk_widget_get_screen( m_pWindow ); + if( pScreen ) + nScreen = getDisplay()->getSystem()->getScreenMonitorIdx( pScreen, maGeometry.nX, maGeometry.nY ); + maGeometry.nDisplayScreenNumber = nScreen; +} + +GtkWidget *GtkSalFrame::getMouseEventWidget() const +{ +#if GTK_CHECK_VERSION(3,0,0) + return GTK_WIDGET(m_pEventBox); +#else + return m_pWindow; +#endif +} + +void GtkSalFrame::InitCommon() +{ +#if GTK_CHECK_VERSION(3,0,0) + m_pEventBox = GTK_EVENT_BOX(gtk_event_box_new()); + gtk_widget_add_events( GTK_WIDGET(m_pEventBox), + GDK_ALL_EVENTS_MASK ); + gtk_container_add( GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pEventBox) ); + + // add the fixed container child, + // fixed is needed since we have to position plugin windows + m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), NULL )); + gtk_container_add( GTK_CONTAINER(m_pEventBox), GTK_WIDGET(m_pFixedContainer) ); +#else + m_pEventBox = 0; + // add the fixed container child, + // fixed is needed since we have to position plugin windows + m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), NULL )); + gtk_container_add( GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pFixedContainer) ); +#endif + + GtkWidget *pEventWidget = getMouseEventWidget(); + + gtk_widget_set_app_paintable(GTK_WIDGET(m_pFixedContainer), true); + /*non-X11 displays won't show anything at all without double-buffering + enabled*/ + if (GDK_IS_X11_DISPLAY(getGdkDisplay())) + gtk_widget_set_double_buffered(GTK_WIDGET(m_pFixedContainer), false); + gtk_widget_set_redraw_on_allocate(GTK_WIDGET(m_pFixedContainer), false); + + + // connect signals + g_signal_connect( G_OBJECT(m_pWindow), "style-set", G_CALLBACK(signalStyleSet), this ); + 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), "motion-notify-event", G_CALLBACK(signalMotion), this )); + m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-release-event", G_CALLBACK(signalButton), this )); +#if GTK_CHECK_VERSION(3,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 ); +// g_signal_connect( G_OBJECT(m_pWindow), "state-flags-changed", G_CALLBACK(signalFlagsChanged), this ); +#if GTK_CHECK_VERSION(3,14,0) + GtkGesture *pSwipe = gtk_gesture_swipe_new(pEventWidget); + g_signal_connect(pSwipe, "swipe", G_CALLBACK(gestureSwipe), this); + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pSwipe), GTK_PHASE_TARGET); + g_object_weak_ref(G_OBJECT(pEventWidget), reinterpret_cast(g_object_unref), pSwipe); + + GtkGesture *pLongPress = gtk_gesture_long_press_new(pEventWidget); + g_signal_connect(pLongPress, "pressed", G_CALLBACK(gestureLongPress), this); + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pLongPress), GTK_PHASE_TARGET); + g_object_weak_ref(G_OBJECT(pEventWidget), reinterpret_cast(g_object_unref), pLongPress); + +#endif + +#else + g_signal_connect( G_OBJECT(m_pFixedContainer), "expose-event", G_CALLBACK(signalExpose), this ); +#endif + g_signal_connect( G_OBJECT(m_pWindow), "focus-in-event", G_CALLBACK(signalFocus), this ); + g_signal_connect( G_OBJECT(m_pWindow), "focus-out-event", G_CALLBACK(signalFocus), this ); + 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), "configure-event", G_CALLBACK(signalConfigure), this ); + 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 ); + g_signal_connect( G_OBJECT(m_pWindow), "delete-event", G_CALLBACK(signalDelete), this ); + g_signal_connect( G_OBJECT(m_pWindow), "window-state-event", G_CALLBACK(signalWindowState), this ); + g_signal_connect( G_OBJECT(m_pWindow), "scroll-event", G_CALLBACK(signalScroll), this ); + g_signal_connect( G_OBJECT(m_pWindow), "leave-notify-event", G_CALLBACK(signalCrossing), this ); + g_signal_connect( G_OBJECT(m_pWindow), "enter-notify-event", G_CALLBACK(signalCrossing), this ); + g_signal_connect( G_OBJECT(m_pWindow), "visibility-notify-event", G_CALLBACK(signalVisibility), this ); + g_signal_connect( G_OBJECT(m_pWindow), "destroy", G_CALLBACK(signalDestroy), this ); + + // init members + m_pCurrentCursor = NULL; + m_nKeyModifiers = 0; + m_bFullscreen = false; + m_bSpanMonitorsWhenFullscreen = false; + m_nState = GDK_WINDOW_STATE_WITHDRAWN; + m_nVisibility = GDK_VISIBILITY_FULLY_OBSCURED; +#if GTK_CHECK_VERSION(3,0,0) + m_nLastScrollEventTime = GDK_CURRENT_TIME; +#endif + m_bSendModChangeOnRelease = false; + m_pIMHandler = NULL; + m_hBackgroundPixmap = None; + m_nSavedScreenSaverTimeout = 0; + m_nGSMCookie = 0; + m_nExtStyle = 0; + m_pRegion = NULL; + m_ePointerStyle = static_cast(0xffff); + m_bSetFocusOnMap = false; + m_pSalMenu = NULL; + m_nWatcherId = 0; + m_nMenuExportId = 0; + m_nAppMenuExportId = 0; + m_nActionGroupExportId = 0; + m_nAppActionGroupExportId = 0; + m_nHudAwarenessId = 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_VISIBILITY_NOTIFY_MASK | GDK_SCROLL_MASK + ); + + // show the widgets +#if GTK_CHECK_VERSION(3,0,0) + gtk_widget_show_all( GTK_WIDGET(m_pEventBox) ); +#else + gtk_widget_show_all( GTK_WIDGET(m_pFixedContainer) ); +#endif + + // realize the window, we need an XWindow id + gtk_widget_realize( m_pWindow ); + + //system data + m_aSystemData.nSize = sizeof( SystemEnvData ); +#if !GTK_CHECK_VERSION(3,0,0) + GtkSalDisplay* pDisp = GetGtkSalData()->GetGtkDisplay(); + m_aSystemData.pDisplay = pDisp->GetDisplay(); + m_aSystemData.pVisual = pDisp->GetVisual( m_nXScreen ).GetVisual(); + m_aSystemData.nDepth = pDisp->GetVisual( m_nXScreen ).GetDepth(); + m_aSystemData.aColormap = pDisp->GetColormap( m_nXScreen ).GetXColormap(); + m_aSystemData.aWindow = widget_get_xid(m_pWindow); + m_aSystemData.aShellWindow = m_aSystemData.aWindow; +#else + static int nWindow = 0; + m_aSystemData.aWindow = nWindow; + m_aSystemData.aShellWindow = nWindow; + ++nWindow; +#endif + m_aSystemData.pSalFrame = this; + m_aSystemData.pWidget = m_pWindow; + m_aSystemData.nScreen = m_nXScreen.getXScreen(); + m_aSystemData.pAppContext = NULL; + m_aSystemData.pShellWidget = m_aSystemData.pWidget; + + // fake an initial geometry, gets updated via configure event or SetPosSize + if( m_bDefaultPos || m_bDefaultSize ) + { + Size aDefSize = calcDefaultSize(); + maGeometry.nX = -1; + maGeometry.nY = -1; + maGeometry.nWidth = aDefSize.Width(); + maGeometry.nHeight = aDefSize.Height(); + if( m_pParent ) + { + // approximation + maGeometry.nTopDecoration = m_pParent->maGeometry.nTopDecoration; + maGeometry.nBottomDecoration = m_pParent->maGeometry.nBottomDecoration; + maGeometry.nLeftDecoration = m_pParent->maGeometry.nLeftDecoration; + maGeometry.nRightDecoration = m_pParent->maGeometry.nRightDecoration; + } + else + { + maGeometry.nTopDecoration = 0; + maGeometry.nBottomDecoration = 0; + maGeometry.nLeftDecoration = 0; + maGeometry.nRightDecoration = 0; + } + } + else + { + resizeWindow( maGeometry.nWidth, maGeometry.nHeight ); + moveWindow( maGeometry.nX, maGeometry.nY ); + } + updateScreenNumber(); + + SetIcon(1); + +#if !GTK_CHECK_VERSION(3,0,0) + m_nWorkArea = pDisp->getWMAdaptor()->getCurrentWorkArea(); + /* #i64117# gtk sets a nice background pixmap + * but we actually don't really want that, so save + * some time on the Xserver as well as prevent + * some paint issues + */ + XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(), + widget_get_xid(m_pWindow), + m_hBackgroundPixmap ); +#endif +} + +/* Sadly gtk_window_set_accept_focus exists only since gtk 2.4 + * for achieving the same effect we will remove the WM_TAKE_FOCUS + * protocol from the window and set the input hint to false. + * But gtk_window_set_accept_focus needs to be called before + * window realization whereas the removal obviously can only happen + * after realization. + */ + +#if !GTK_CHECK_VERSION(3,0,0) +extern "C" { + typedef void(*setAcceptFn)( GtkWindow*, gboolean ); + static setAcceptFn p_gtk_window_set_accept_focus = NULL; + static bool bGetAcceptFocusFn = true; + + typedef void(*setUserTimeFn)( GdkWindow*, guint32 ); + static setUserTimeFn p_gdk_x11_window_set_user_time = NULL; + static bool bGetSetUserTimeFn = true; +} +#endif + +static void lcl_set_accept_focus( GtkWindow* pWindow, gboolean bAccept, bool bBeforeRealize ) +{ +#if !GTK_CHECK_VERSION(3,0,0) + if( bGetAcceptFocusFn ) + { + bGetAcceptFocusFn = false; + p_gtk_window_set_accept_focus = reinterpret_cast(osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_window_set_accept_focus" )); + } + if( p_gtk_window_set_accept_focus && bBeforeRealize ) + p_gtk_window_set_accept_focus( pWindow, bAccept ); + else if( ! bBeforeRealize ) + { + Display* pDisplay = GetGtkSalData()->GetGtkDisplay()->GetDisplay(); + ::Window aWindow = widget_get_xid(GTK_WIDGET(pWindow)); + XWMHints* pHints = XGetWMHints( pDisplay, aWindow ); + if( ! pHints ) + { + pHints = XAllocWMHints(); + pHints->flags = 0; + } + pHints->flags |= InputHint; + pHints->input = bAccept ? True : False; + XSetWMHints( pDisplay, aWindow, pHints ); + XFree( pHints ); + + if (GetGtkSalData()->GetGtkDisplay()->getWMAdaptor()->getWindowManagerName() == "compiz") + return; + + /* remove WM_TAKE_FOCUS protocol; this would usually be the + * right thing, but gtk handles it internally whereas we + * want to handle it ourselves (as to sometimes not get + * the focus) + */ + Atom* pProtocols = NULL; + int nProtocols = 0; + XGetWMProtocols( pDisplay, + aWindow, + &pProtocols, &nProtocols ); + if( pProtocols ) + { + bool bSet = false; + Atom nTakeFocus = XInternAtom( pDisplay, "WM_TAKE_FOCUS", True ); + if( nTakeFocus ) + { + for( int i = 0; i < nProtocols; i++ ) + { + if( pProtocols[i] == nTakeFocus ) + { + for( int n = i; n < nProtocols-1; n++ ) + pProtocols[n] = pProtocols[n+1]; + nProtocols--; + i--; + bSet = true; + } + } + } + if( bSet ) + XSetWMProtocols( pDisplay, aWindow, pProtocols, nProtocols ); + XFree( pProtocols ); + } + } +#else + gtk_window_set_accept_focus(pWindow, bAccept); + (void)bBeforeRealize; +#endif +} + +#if !GTK_CHECK_VERSION(3,0,0) +static void lcl_set_user_time( GtkWindow* i_pWindow, guint32 i_nTime ) +{ + if( bGetSetUserTimeFn ) + { + bGetSetUserTimeFn = false; + p_gdk_x11_window_set_user_time = reinterpret_cast(osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gdk_x11_window_set_user_time" )); + } + bool bSet = false; + if( p_gdk_x11_window_set_user_time ) + { + GdkWindow* pWin = widget_get_window(GTK_WIDGET(i_pWindow)); + if( pWin ) // only if the window is realized. + { + p_gdk_x11_window_set_user_time( pWin, i_nTime ); + bSet = true; + } + } + if( !bSet ) + { + Display* pDisplay = GetGtkSalData()->GetGtkDisplay()->GetDisplay(); + Atom nUserTime = XInternAtom( pDisplay, "_NET_WM_USER_TIME", True ); + if( nUserTime ) + { + XChangeProperty( pDisplay, widget_get_xid(GTK_WIDGET(i_pWindow)), + nUserTime, XA_CARDINAL, 32, + PropModeReplace, reinterpret_cast(&i_nTime), 1 ); + } + } +}; +#endif + +GtkSalFrame *GtkSalFrame::getFromWindow( GtkWindow *pWindow ) +{ + return static_cast(g_object_get_data( G_OBJECT( pWindow ), "SalFrame" )); +} + +void GtkSalFrame::Init( SalFrame* pParent, sal_uLong nStyle ) +{ + if( nStyle & SAL_FRAME_STYLE_DEFAULT ) // ensure default style + { + nStyle |= SAL_FRAME_STYLE_MOVEABLE | SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_CLOSEABLE; + nStyle &= ~SAL_FRAME_STYLE_FLOAT; + } + + m_pParent = static_cast(pParent); + m_pForeignParent = NULL; + m_aForeignParentWindow = None; + m_pForeignTopLevel = NULL; + m_aForeignTopLevelWindow = None; + m_nStyle = nStyle; + + GtkWindowType eWinType = ( (nStyle & SAL_FRAME_STYLE_FLOAT) && + ! (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION| + SAL_FRAME_STYLE_FLOAT_FOCUSABLE)) + ) + ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL; + + if( nStyle & SAL_FRAME_STYLE_SYSTEMCHILD ) + { + m_pWindow = gtk_event_box_new(); + if( m_pParent ) + { + // insert into container + gtk_fixed_put( m_pParent->getFixedContainer(), + m_pWindow, 0, 0 ); + + } + } + else + { + m_pWindow = gtk_widget_new( GTK_TYPE_WINDOW, "type", eWinType, + "visible", FALSE, NULL ); + } + g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", this ); + g_object_set_data( G_OBJECT( m_pWindow ), "libo-version", (gpointer)LIBO_VERSION_DOTTED); + + // force wm class hint + m_nExtStyle = ~0; + if (m_pParent) + m_sWMClass = m_pParent->m_sWMClass; + SetExtendedFrameStyle( 0 ); + + if( m_pParent && m_pParent->m_pWindow && ! isChild() ) + gtk_window_set_screen( GTK_WINDOW(m_pWindow), gtk_window_get_screen( GTK_WINDOW(m_pParent->m_pWindow) ) ); + + if (m_pParent) + { + if (!(m_pParent->m_nStyle & SAL_FRAME_STYLE_PLUG)) + gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), GTK_WINDOW(m_pParent->m_pWindow) ); + m_pParent->m_aChildren.push_back( this ); + } + + InitCommon(); + + // set window type + bool bDecoHandling = + ! isChild() && + ( ! (nStyle & SAL_FRAME_STYLE_FLOAT) || + (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) ); + + if( bDecoHandling ) + { + GdkWindowTypeHint eType = GDK_WINDOW_TYPE_HINT_NORMAL; + if( (nStyle & SAL_FRAME_STYLE_DIALOG) && m_pParent != 0 ) + eType = GDK_WINDOW_TYPE_HINT_DIALOG; + if( (nStyle & SAL_FRAME_STYLE_INTRO) ) + { + gtk_window_set_role( GTK_WINDOW(m_pWindow), "splashscreen" ); + eType = GDK_WINDOW_TYPE_HINT_SPLASHSCREEN; + } + else if( (nStyle & SAL_FRAME_STYLE_TOOLWINDOW ) ) + { + eType = GDK_WINDOW_TYPE_HINT_UTILITY; + gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow), true ); + } + else if( (nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) + { + eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; + lcl_set_accept_focus( GTK_WINDOW(m_pWindow), false, true ); + } + else if( (nStyle & SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) + { + eType = GDK_WINDOW_TYPE_HINT_UTILITY; + } +#if !GTK_CHECK_VERSION(3,0,0) + if( (nStyle & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN ) + && getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) + { + eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; + gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), true ); + } +#endif + gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), eType ); + gtk_window_set_gravity( GTK_WINDOW(m_pWindow), GDK_GRAVITY_STATIC ); + } + else if( (nStyle & SAL_FRAME_STYLE_FLOAT) ) + { + gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), GDK_WINDOW_TYPE_HINT_POPUP_MENU ); + } + +#if !GTK_CHECK_VERSION(3,0,0) + if( eWinType == GTK_WINDOW_TOPLEVEL ) + { +#ifdef ENABLE_GMENU_INTEGRATION + // Enable DBus native menu if available. + ensure_dbus_setup( this ); +#endif + + guint32 nUserTime = 0; + if( (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_TOOLWINDOW)) == 0 ) + { + nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); + } + lcl_set_user_time(GTK_WINDOW(m_pWindow), nUserTime); + } +#endif + + if( bDecoHandling ) + { + gtk_window_set_resizable( GTK_WINDOW(m_pWindow), (nStyle & SAL_FRAME_STYLE_SIZEABLE) != 0 ); + if( ( (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION)) ) ) + lcl_set_accept_focus( GTK_WINDOW(m_pWindow), false, false ); + } +} + +GdkNativeWindow GtkSalFrame::findTopLevelSystemWindow( GdkNativeWindow aWindow ) +{ +#if !GTK_CHECK_VERSION(3,0,0) + ::Window aRoot, aParent; + ::Window* pChildren; + unsigned int nChildren; + bool bBreak = false; + do + { + pChildren = NULL; + nChildren = 0; + aParent = aRoot = None; + XQueryTree( getDisplay()->GetDisplay(), aWindow, + &aRoot, &aParent, &pChildren, &nChildren ); + XFree( pChildren ); + if( aParent != aRoot ) + aWindow = aParent; + int nCount = 0; + Atom* pProps = XListProperties( getDisplay()->GetDisplay(), + aWindow, + &nCount ); + for( int i = 0; i < nCount && ! bBreak; ++i ) + bBreak = (pProps[i] == XA_WM_HINTS); + if( pProps ) + XFree( pProps ); + } while( aParent != aRoot && ! bBreak ); + + return aWindow; +#else + (void)aWindow; + //FIXME: no findToplevelSystemWindow + return 0; +#endif +} + +void GtkSalFrame::Init( SystemParentData* pSysData ) +{ + m_pParent = NULL; + m_aForeignParentWindow = (GdkNativeWindow)pSysData->aWindow; + m_pForeignParent = NULL; + m_aForeignTopLevelWindow = findTopLevelSystemWindow( (GdkNativeWindow)pSysData->aWindow ); + m_pForeignTopLevel = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignTopLevelWindow ); + gdk_window_set_events( m_pForeignTopLevel, GDK_STRUCTURE_MASK ); + + if( pSysData->nSize > sizeof(pSysData->nSize)+sizeof(pSysData->aWindow) && pSysData->bXEmbedSupport ) + { +#if GTK_CHECK_VERSION(3,0,0) + m_pWindow = gtk_plug_new_for_display( getGdkDisplay(), pSysData->aWindow ); +#else + m_pWindow = gtk_plug_new( pSysData->aWindow ); +#endif + m_bWindowIsGtkPlug = true; + widget_set_can_default( m_pWindow, true ); + widget_set_can_focus( m_pWindow, true ); + gtk_widget_set_sensitive( m_pWindow, true ); + } + else + { + m_pWindow = gtk_window_new( GTK_WINDOW_POPUP ); + m_bWindowIsGtkPlug = false; + } + m_nStyle = SAL_FRAME_STYLE_PLUG; + InitCommon(); + + m_pForeignParent = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignParentWindow ); + gdk_window_set_events( m_pForeignParent, GDK_STRUCTURE_MASK ); + +#if !GTK_CHECK_VERSION(3,0,0) + int x_ret, y_ret; + unsigned int w, h, bw, d; + ::Window aRoot; + XGetGeometry( getDisplay()->GetDisplay(), pSysData->aWindow, + &aRoot, &x_ret, &y_ret, &w, &h, &bw, &d ); + maGeometry.nWidth = w; + maGeometry.nHeight = h; + window_resize(w, h); + gtk_window_move( GTK_WINDOW(m_pWindow), 0, 0 ); + if( ! m_bWindowIsGtkPlug ) + { + XReparentWindow( getDisplay()->GetDisplay(), + widget_get_xid(m_pWindow), + (::Window)pSysData->aWindow, + 0, 0 ); + } +#else + //FIXME: Handling embedded windows, is going to be fun ... +#endif +} + +void GtkSalFrame::askForXEmbedFocus( sal_Int32 i_nTimeCode ) +{ +#if !GTK_CHECK_VERSION(3,0,0) + XEvent aEvent; + + memset( &aEvent, 0, sizeof(aEvent) ); + aEvent.xclient.window = m_aForeignParentWindow; + aEvent.xclient.type = ClientMessage; + aEvent.xclient.message_type = getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED ); + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = i_nTimeCode ? i_nTimeCode : CurrentTime; + aEvent.xclient.data.l[1] = 3; // XEMBED_REQUEST_FOCUS + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + + GetGenericData()->ErrorTrapPush(); + XSendEvent( getDisplay()->GetDisplay(), + m_aForeignParentWindow, + False, NoEventMask, &aEvent ); + GetGenericData()->ErrorTrapPop(); +#else + (void) this; // loplugin:staticmethods + (void)i_nTimeCode; + //FIXME: no askForXEmbedFocus for gtk3 yet +#endif +} + +void GtkSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle ) +{ + if( nStyle != m_nExtStyle && ! isChild() ) + { + m_nExtStyle = nStyle; + updateWMClass(); + } +} + +SalGraphics* GtkSalFrame::AcquireGraphics() +{ + if( m_pWindow ) + { + for( int i = 0; i < nMaxGraphics; i++ ) + { + if( ! m_aGraphics[i].bInUse ) + { + m_aGraphics[i].bInUse = true; + if( ! m_aGraphics[i].pGraphics ) + { +#if GTK_CHECK_VERSION(3,0,0) + m_aGraphics[i].pGraphics = new GtkSalGraphics( this, m_pWindow ); + if( !m_aFrame.get() ) + { + AllocateFrame(); + TriggerPaintEvent(); + } + m_aGraphics[i].pGraphics->setDevice( m_aFrame ); +#else // common case: + m_aGraphics[i].pGraphics = new GtkSalGraphics( this, m_pWindow, m_nXScreen ); +#endif + } + return m_aGraphics[i].pGraphics; + } + } + } + + return NULL; +} + +void GtkSalFrame::ReleaseGraphics( SalGraphics* pGraphics ) +{ + for( int i = 0; i < nMaxGraphics; i++ ) + { + if( m_aGraphics[i].pGraphics == pGraphics ) + { + m_aGraphics[i].bInUse = false; + break; + } + } +} + +bool GtkSalFrame::PostEvent( void* pData ) +{ + getDisplay()->SendInternalEvent( this, pData ); + return true; +} + +void GtkSalFrame::SetTitle( const OUString& rTitle ) +{ + m_aTitle = rTitle; + if( m_pWindow && ! isChild() ) + gtk_window_set_title( GTK_WINDOW(m_pWindow), OUStringToOString( rTitle, RTL_TEXTENCODING_UTF8 ).getStr() ); +} + +static inline sal_uInt8 * +getRow( BitmapBuffer *pBuffer, sal_uLong nRow ) +{ + if( BMP_SCANLINE_ADJUSTMENT( pBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN ) + return pBuffer->mpBits + nRow * pBuffer->mnScanlineSize; + else + return pBuffer->mpBits + ( pBuffer->mnHeight - nRow - 1 ) * pBuffer->mnScanlineSize; +} + +static GdkPixbuf * +bitmapToPixbuf( SalBitmap *pSalBitmap, SalBitmap *pSalAlpha ) +{ + g_return_val_if_fail( pSalBitmap != NULL, NULL ); + g_return_val_if_fail( pSalAlpha != NULL, NULL ); + + BitmapBuffer *pBitmap = pSalBitmap->AcquireBuffer( BITMAP_READ_ACCESS ); + g_return_val_if_fail( pBitmap != NULL, NULL ); + g_return_val_if_fail( pBitmap->mnBitCount == 24 || pBitmap->mnBitCount == 32, NULL ); + + BitmapBuffer *pAlpha = pSalAlpha->AcquireBuffer( BITMAP_READ_ACCESS ); + g_return_val_if_fail( pAlpha != NULL, NULL ); + g_return_val_if_fail( pAlpha->mnBitCount == 8, NULL ); + + Size aSize = pSalBitmap->GetSize(); + g_return_val_if_fail( pSalAlpha->GetSize() == aSize, NULL ); + + int nX, nY; + guchar *pPixbufData = static_cast(g_malloc (4 * aSize.Width() * aSize.Height() )); + guchar *pDestData = pPixbufData; + + for( nY = 0; nY < pBitmap->mnHeight; nY++ ) + { + sal_uInt8 *pData = getRow( pBitmap, nY ); + sal_uInt8 *pAlphaData = getRow( pAlpha, nY ); + + for( nX = 0; nX < pBitmap->mnWidth; nX++ ) + { + BitmapColor aColor; + if (pBitmap->mnFormat == BMP_FORMAT_24BIT_TC_BGR) + { + aColor = BitmapColor(pData[2], pData[1], pData[0]); + pData += 3; + } + else if (pBitmap->mnFormat == BMP_FORMAT_24BIT_TC_RGB) + { + aColor = BitmapColor(pData[0], pData[1], pData[2]); + pData += 3; + } + else + { + pBitmap->maColorMask.GetColorFor32Bit(aColor, pData); + pData += 4; + } + *pDestData++ = aColor.GetRed(); + *pDestData++ = aColor.GetGreen(); + *pDestData++ = aColor.GetBlue(); + *pDestData++ = 255 - *pAlphaData++; + } + } + + pSalBitmap->ReleaseBuffer( pBitmap, BITMAP_READ_ACCESS ); + pSalAlpha->ReleaseBuffer( pAlpha, BITMAP_READ_ACCESS ); + + return gdk_pixbuf_new_from_data( pPixbufData, + GDK_COLORSPACE_RGB, true, 8, + aSize.Width(), aSize.Height(), + aSize.Width() * 4, + reinterpret_cast(g_free), + NULL ); +} + +void GtkSalFrame::SetIcon( sal_uInt16 nIcon ) +{ + if( (m_nStyle & (SAL_FRAME_STYLE_PLUG|SAL_FRAME_STYLE_SYSTEMCHILD|SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_INTRO|SAL_FRAME_STYLE_OWNERDRAWDECORATION)) + || ! m_pWindow ) + return; + + if( !ImplGetResMgr() ) + return; + + GdkPixbuf *pBuf; + GList *pIcons = NULL; + + sal_uInt16 nOffsets[2] = { SV_ICON_SMALL_START, SV_ICON_LARGE_START }; + sal_uInt16 nIndex; + + for( nIndex = 0; nIndex < sizeof(nOffsets)/ sizeof(sal_uInt16); nIndex++ ) + { + // #i44723# workaround gcc temporary problem + ResId aResId( nOffsets[nIndex] + nIcon, *ImplGetResMgr() ); + BitmapEx aIcon( aResId ); + + // #i81083# convert to 24bit/8bit alpha bitmap + Bitmap aBmp = aIcon.GetBitmap(); + if( aBmp.GetBitCount() != 24 || ! aIcon.IsAlpha() ) + { + if( aBmp.GetBitCount() != 24 ) + aBmp.Convert( BMP_CONVERSION_24BIT ); + AlphaMask aMask; + if( ! aIcon.IsAlpha() ) + { + switch( aIcon.GetTransparentType() ) + { + case TRANSPARENT_NONE: + { + sal_uInt8 nTrans = 0; + aMask = AlphaMask( aBmp.GetSizePixel(), &nTrans ); + } + break; + case TRANSPARENT_COLOR: + aMask = AlphaMask( aBmp.CreateMask( aIcon.GetTransparentColor() ) ); + break; + case TRANSPARENT_BITMAP: + aMask = AlphaMask( aIcon.GetMask() ); + break; + default: + OSL_FAIL( "unhandled transparent type" ); + break; + } + } + else + aMask = aIcon.GetAlpha(); + aIcon = BitmapEx( aBmp, aMask ); + } + + ImpBitmap *pIconImpBitmap = aIcon.ImplGetBitmapImpBitmap(); + ImpBitmap *pIconImpMask = aIcon.ImplGetMaskImpBitmap(); + + if( pIconImpBitmap && pIconImpMask ) + { + SalBitmap *pIconBitmap = + pIconImpBitmap->ImplGetSalBitmap(); + SalBitmap *pIconMask = + pIconImpMask->ImplGetSalBitmap(); + + if( ( pBuf = bitmapToPixbuf( pIconBitmap, pIconMask ) ) ) + pIcons = g_list_prepend( pIcons, pBuf ); + } + } + + gtk_window_set_icon_list( GTK_WINDOW(m_pWindow), pIcons ); + + g_list_foreach( pIcons, reinterpret_cast(g_object_unref), NULL ); + g_list_free( pIcons ); +} + +void GtkSalFrame::SetMenu( SalMenu* pSalMenu ) +{ +// if(m_pSalMenu) +// { +// static_cast(m_pSalMenu)->DisconnectFrame(); +// } + m_pSalMenu = pSalMenu; +} + +SalMenu* GtkSalFrame::GetMenu() +{ + return m_pSalMenu; +} + +void GtkSalFrame::DrawMenuBar() +{ +} + +void GtkSalFrame::Center() +{ + long nX, nY; + + if( m_pParent ) + { + nX = ((long)m_pParent->maGeometry.nWidth - (long)maGeometry.nWidth)/2; + nY = ((long)m_pParent->maGeometry.nHeight - (long)maGeometry.nHeight)/2; + } + else + { + GdkScreen *pScreen = NULL; + gint px, py; + GdkModifierType nMask; + gdk_display_get_pointer( getGdkDisplay(), &pScreen, &px, &py, &nMask ); + if( !pScreen ) + pScreen = gtk_widget_get_screen( m_pWindow ); + + gint nMonitor; + nMonitor = gdk_screen_get_monitor_at_point( pScreen, px, py ); + + GdkRectangle aMonitor; + gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aMonitor ); + + nX = aMonitor.x + (aMonitor.width - (long)maGeometry.nWidth)/2; + nY = aMonitor.y + (aMonitor.height - (long)maGeometry.nHeight)/2; + } + SetPosSize( nX, nY, 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ); +} + +Size GtkSalFrame::calcDefaultSize() +{ + return bestmaxFrameSizeForScreenSize(getDisplay()->GetScreenSize(GetDisplayScreen())); +} + +void GtkSalFrame::SetDefaultSize() +{ + Size aDefSize = calcDefaultSize(); + + SetPosSize( 0, 0, aDefSize.Width(), aDefSize.Height(), + SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + + if( (m_nStyle & SAL_FRAME_STYLE_DEFAULT) && m_pWindow ) + gtk_window_maximize( GTK_WINDOW(m_pWindow) ); +} + +static void initClientId() +{ +#if !GTK_CHECK_VERSION(3,0,0) + static bool bOnce = false; + if (!bOnce) + { + bOnce = true; + const OString& rID = SessionManagerClient::getSessionID(); + if (!rID.isEmpty()) + gdk_set_sm_client_id(rID.getStr()); + } +#else + // No session management support for gtk3+ - this is now legacy. +#endif +} + +void GtkSalFrame::Show( bool bVisible, bool bNoActivate ) +{ + if( m_pWindow ) + { +#if !GTK_CHECK_VERSION(3,0,0) + if( m_pParent && (m_pParent->m_nStyle & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN) + && getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) + gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), bVisible ); +#endif + if( bVisible ) + { + initClientId(); + getDisplay()->startupNotificationCompleted(); + + if( m_bDefaultPos ) + Center(); + if( m_bDefaultSize ) + SetDefaultSize(); + setMinMaxSize(); + +#if !GTK_CHECK_VERSION(3,0,0) + // #i45160# switch to desktop where a dialog with parent will appear + if( m_pParent && m_pParent->m_nWorkArea != m_nWorkArea && IS_WIDGET_MAPPED(m_pParent->m_pWindow) ) + getDisplay()->getWMAdaptor()->switchToWorkArea( m_pParent->m_nWorkArea ); +#endif + + if( isFloatGrabWindow() && + m_pParent && + m_nFloats == 0 && + ! getDisplay()->GetCaptureFrame() ) + { + /* #i63086# + * outsmart Metacity's "focus:mouse" mode + * which insists on taking the focus from the document + * to the new float. Grab focus to parent frame BEFORE + * showing the float (cannot grab it to the float + * before show). + */ + m_pParent->grabPointer( true, true ); + } + +#if !GTK_CHECK_VERSION(3,0,0) + guint32 nUserTime = 0; + if( ! bNoActivate && (m_nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_TOOLWINDOW)) == 0 ) + nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); + + //For these floating windows we don't want the main window to lose focus, and metacity has... + // metacity-2.24.0/src/core/window.c + + // if ((focus_window != NULL) && XSERVER_TIME_IS_BEFORE (compare, focus_window->net_wm_user_time)) + // "compare" window focus prevented by other activity + + // where "compare" is this window + + // which leads to... + + // /* This happens for error dialogs or alerts; these need to remain on + // * top, but it would be confusing to have its ancestor remain + // * focused. + // */ + // if (meta_window_is_ancestor_of_transient (focus_window, window)) + // "The focus window %s is an ancestor of the newly mapped " + // "window %s which isn't being focused. Unfocusing the " + // "ancestor.\n", + + // i.e. having a time < that of the toplevel frame means that the toplevel frame gets unfocused. + // awesome. + if( nUserTime == 0 ) + { + nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); + } + lcl_set_user_time(GTK_WINDOW(m_pWindow), nUserTime ); +#endif + + if( ! bNoActivate && (m_nStyle & SAL_FRAME_STYLE_TOOLWINDOW) ) + m_bSetFocusOnMap = true; + + gtk_widget_show( m_pWindow ); + + if( isFloatGrabWindow() ) + { + m_nFloats++; + if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 1 ) + { + grabPointer(true, true); + GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this; + pKeyboardFrame->grabKeyboard(true); + } + // #i44068# reset parent's IM context + if( m_pParent ) + m_pParent->EndExtTextInput(0); + } + if( m_bWindowIsGtkPlug ) + askForXEmbedFocus( 0 ); + } + else + { + if( isFloatGrabWindow() ) + { + m_nFloats--; + if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 0) + { + GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this; + pKeyboardFrame->grabKeyboard(false); + grabPointer(false); + } + } + gtk_widget_hide( m_pWindow ); + if( m_pIMHandler ) + m_pIMHandler->focusChanged( false ); + // flush here; there may be a very seldom race between + // the display connection used for clipboard and our connection + Flush(); + } + CallCallback( SALEVENT_RESIZE, NULL ); + TriggerPaintEvent(); + } +} + +void GtkSalFrame::setMinMaxSize() +{ + /* #i34504# metacity (and possibly others) do not treat + * _NET_WM_STATE_FULLSCREEN and max_width/height independently; + * whether they should is undefined. So don't set the max size hint + * for a full screen window. + */ + if( m_pWindow && ! isChild() ) + { + GdkGeometry aGeo; + int aHints = 0; + if( m_nStyle & SAL_FRAME_STYLE_SIZEABLE ) + { + if( m_aMinSize.Width() && m_aMinSize.Height() && ! m_bFullscreen ) + { + aGeo.min_width = m_aMinSize.Width(); + aGeo.min_height = m_aMinSize.Height(); + aHints |= GDK_HINT_MIN_SIZE; + } + if( m_aMaxSize.Width() && m_aMaxSize.Height() && ! m_bFullscreen ) + { + aGeo.max_width = m_aMaxSize.Width(); + aGeo.max_height = m_aMaxSize.Height(); + aHints |= GDK_HINT_MAX_SIZE; + } + } + else + { + if( ! m_bFullscreen ) + { + aGeo.min_width = maGeometry.nWidth; + aGeo.min_height = maGeometry.nHeight; + aHints |= GDK_HINT_MIN_SIZE; + + aGeo.max_width = maGeometry.nWidth; + aGeo.max_height = maGeometry.nHeight; + aHints |= GDK_HINT_MAX_SIZE; + } + } + if( m_bFullscreen && m_aMaxSize.Width() && m_aMaxSize.Height() ) + { + aGeo.max_width = m_aMaxSize.Width(); + aGeo.max_height = m_aMaxSize.Height(); + aHints |= GDK_HINT_MAX_SIZE; + } + if( aHints ) + { + gtk_window_set_geometry_hints( GTK_WINDOW(m_pWindow), + NULL, + &aGeo, + GdkWindowHints( aHints ) ); + } + } +} + +void GtkSalFrame::SetMaxClientSize( long nWidth, long nHeight ) +{ + if( ! isChild() ) + { + m_aMaxSize = Size( nWidth, nHeight ); + // Show does a setMinMaxSize + if( IS_WIDGET_MAPPED( m_pWindow ) ) + setMinMaxSize(); + } +} +void GtkSalFrame::SetMinClientSize( long nWidth, long nHeight ) +{ + if( ! isChild() ) + { + m_aMinSize = Size( nWidth, nHeight ); + if( m_pWindow ) + { + widget_set_size_request(nWidth, nHeight ); + // Show does a setMinMaxSize + if( IS_WIDGET_MAPPED( m_pWindow ) ) + setMinMaxSize(); + } + } +} + +// FIXME: we should really be an SvpSalFrame sub-class, and +// share their AllocateFrame ! +void GtkSalFrame::AllocateFrame() +{ +#if GTK_CHECK_VERSION(3,0,0) + basegfx::B2IVector aFrameSize( maGeometry.nWidth, maGeometry.nHeight ); + if( ! m_aFrame.get() || m_aFrame->getSize() != aFrameSize ) + { + if( aFrameSize.getX() == 0 ) + aFrameSize.setX( 1 ); + if( aFrameSize.getY() == 0 ) + aFrameSize.setY( 1 ); + m_aFrame = basebmp::createBitmapDevice(aFrameSize, true, + basebmp::FORMAT_THIRTYTWO_BIT_TC_MASK_BGRX); + m_aFrame->setDamageTracker( + basebmp::IBitmapDeviceDamageTrackerSharedPtr(new DamageTracker(*this)) ); + SAL_INFO("vcl.gtk3", "allocated m_aFrame size of " << maGeometry.nWidth << " x " << maGeometry.nHeight); + +#if OSL_DEBUG_LEVEL > 0 // set background to orange + m_aFrame->clear( basebmp::Color( 255, 127, 0 ) ); +#endif + + // update device in existing graphics + for( unsigned int i = 0; i < SAL_N_ELEMENTS( m_aGraphics ); ++i ) + { + if( !m_aGraphics[i].pGraphics ) + continue; + m_aGraphics[i].pGraphics->setDevice( m_aFrame ); + } + } +#endif +} + +void GtkSalFrame::SetPosSize( long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags ) +{ + if( !m_pWindow || isChild( true, false ) ) + return; + + bool bSized = false, bMoved = false; + + if( (nFlags & ( SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT )) && + (nWidth > 0 && nHeight > 0 ) // sometimes stupid things happen + ) + { + m_bDefaultSize = false; + + if( (unsigned long)nWidth != maGeometry.nWidth || (unsigned long)nHeight != maGeometry.nHeight ) + bSized = true; + maGeometry.nWidth = nWidth; + maGeometry.nHeight = nHeight; + + if( isChild( false, true ) ) + widget_set_size_request(nWidth, nHeight); + else if( ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) ) + window_resize(nWidth, nHeight); + setMinMaxSize(); + } + else if( m_bDefaultSize ) + SetDefaultSize(); + + m_bDefaultSize = false; + + if( nFlags & ( SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ) ) + { + if( m_pParent ) + { + if( AllSettings::GetLayoutRTL() ) + nX = m_pParent->maGeometry.nWidth-maGeometry.nWidth-1-nX; + nX += m_pParent->maGeometry.nX; + nY += m_pParent->maGeometry.nY; + } + + if( nX != maGeometry.nX || nY != maGeometry.nY ) + bMoved = true; + maGeometry.nX = nX; + maGeometry.nY = nY; + + m_bDefaultPos = false; + + moveWindow( maGeometry.nX, maGeometry.nY ); + + updateScreenNumber(); + } + else if( m_bDefaultPos ) + Center(); + + m_bDefaultPos = false; + + if( bSized ) + AllocateFrame(); + + if( bSized && ! bMoved ) + CallCallback( SALEVENT_RESIZE, NULL ); + else if( bMoved && ! bSized ) + CallCallback( SALEVENT_MOVE, NULL ); + else if( bMoved && bSized ) + CallCallback( SALEVENT_MOVERESIZE, NULL ); + + if (bSized) + TriggerPaintEvent(); +} + +void GtkSalFrame::GetClientSize( long& rWidth, long& rHeight ) +{ + if( m_pWindow && !(m_nState & GDK_WINDOW_STATE_ICONIFIED) ) + { + rWidth = maGeometry.nWidth; + rHeight = maGeometry.nHeight; + } + else + rWidth = rHeight = 0; +} + +void GtkSalFrame::GetWorkArea( Rectangle& rRect ) +{ +#if !GTK_CHECK_VERSION(3,0,0) + rRect = GetGtkSalData()->GetGtkDisplay()->getWMAdaptor()->getWorkArea( 0 ); +#else + GdkScreen *pScreen = gtk_window_get_screen(GTK_WINDOW(m_pWindow)); + Rectangle aRetRect; + int max = gdk_screen_get_n_monitors (pScreen); + for (int i = 0; i < max; ++i) + { + GdkRectangle aRect; + gdk_screen_get_monitor_workarea(pScreen, i, &aRect); + Rectangle aMonitorRect(aRect.x, aRect.y, aRect.x+aRect.width, aRect.y+aRect.height); + aRetRect.Union(aMonitorRect); + } + rRect = aRetRect; +#endif +} + +SalFrame* GtkSalFrame::GetParent() const +{ + return m_pParent; +} + +void GtkSalFrame::SetWindowState( const SalFrameState* pState ) +{ + if( ! m_pWindow || ! pState || isChild( true, false ) ) + return; + + const sal_uLong nMaxGeometryMask = + WINDOWSTATE_MASK_X | WINDOWSTATE_MASK_Y | + WINDOWSTATE_MASK_WIDTH | WINDOWSTATE_MASK_HEIGHT | + WINDOWSTATE_MASK_MAXIMIZED_X | WINDOWSTATE_MASK_MAXIMIZED_Y | + WINDOWSTATE_MASK_MAXIMIZED_WIDTH | WINDOWSTATE_MASK_MAXIMIZED_HEIGHT; + + if( (pState->mnMask & WINDOWSTATE_MASK_STATE) && + ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) && + (pState->mnState & WINDOWSTATE_STATE_MAXIMIZED) && + (pState->mnMask & nMaxGeometryMask) == nMaxGeometryMask ) + { + resizeWindow( pState->mnWidth, pState->mnHeight ); + moveWindow( pState->mnX, pState->mnY ); + m_bDefaultPos = m_bDefaultSize = false; + + maGeometry.nX = pState->mnMaximizedX; + maGeometry.nY = pState->mnMaximizedY; + maGeometry.nWidth = pState->mnMaximizedWidth; + maGeometry.nHeight = pState->mnMaximizedHeight; + updateScreenNumber(); + + m_nState = GdkWindowState( m_nState | GDK_WINDOW_STATE_MAXIMIZED ); + m_aRestorePosSize = Rectangle( Point( pState->mnX, pState->mnY ), + Size( pState->mnWidth, pState->mnHeight ) ); + CallCallback( SALEVENT_RESIZE, NULL ); + } + else if( pState->mnMask & (WINDOWSTATE_MASK_X | WINDOWSTATE_MASK_Y | + WINDOWSTATE_MASK_WIDTH | WINDOWSTATE_MASK_HEIGHT ) ) + { + sal_uInt16 nPosSizeFlags = 0; + long nX = pState->mnX - (m_pParent ? m_pParent->maGeometry.nX : 0); + long nY = pState->mnY - (m_pParent ? m_pParent->maGeometry.nY : 0); + if( pState->mnMask & WINDOWSTATE_MASK_X ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_X; + else + nX = maGeometry.nX - (m_pParent ? m_pParent->maGeometry.nX : 0); + if( pState->mnMask & WINDOWSTATE_MASK_Y ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_Y; + else + nY = maGeometry.nY - (m_pParent ? m_pParent->maGeometry.nY : 0); + if( pState->mnMask & WINDOWSTATE_MASK_WIDTH ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH; + if( pState->mnMask & WINDOWSTATE_MASK_HEIGHT ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT; + SetPosSize( nX, nY, pState->mnWidth, pState->mnHeight, nPosSizeFlags ); + } + if( pState->mnMask & WINDOWSTATE_MASK_STATE && ! isChild() ) + { + if( pState->mnState & WINDOWSTATE_STATE_MAXIMIZED ) + gtk_window_maximize( GTK_WINDOW(m_pWindow) ); + else + gtk_window_unmaximize( GTK_WINDOW(m_pWindow) ); + /* #i42379# there is no rollup state in GDK; and rolled up windows are + * (probably depending on the WM) reported as iconified. If we iconify a + * window here that was e.g. a dialog, then it will be unmapped but still + * not be displayed in the task list, so it's an iconified window that + * the user cannot get out of this state. So do not set the iconified state + * on windows with a parent (that is transient frames) since these tend + * to not be represented in an icon task list. + */ + if( (pState->mnState & WINDOWSTATE_STATE_MINIMIZED) + && ! m_pParent ) + gtk_window_iconify( GTK_WINDOW(m_pWindow) ); + else + gtk_window_deiconify( GTK_WINDOW(m_pWindow) ); + } + TriggerPaintEvent(); +} + +bool GtkSalFrame::GetWindowState( SalFrameState* pState ) +{ + pState->mnState = WINDOWSTATE_STATE_NORMAL; + pState->mnMask = WINDOWSTATE_MASK_STATE; + // rollup ? gtk 2.2 does not seem to support the shaded state + if( (m_nState & GDK_WINDOW_STATE_ICONIFIED) ) + pState->mnState |= WINDOWSTATE_STATE_MINIMIZED; + if( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) + { + pState->mnState |= WINDOWSTATE_STATE_MAXIMIZED; + pState->mnX = m_aRestorePosSize.Left(); + pState->mnY = m_aRestorePosSize.Top(); + pState->mnWidth = m_aRestorePosSize.GetWidth(); + pState->mnHeight = m_aRestorePosSize.GetHeight(); + pState->mnMaximizedX = maGeometry.nX; + pState->mnMaximizedY = maGeometry.nY; + pState->mnMaximizedWidth = maGeometry.nWidth; + pState->mnMaximizedHeight = maGeometry.nHeight; + pState->mnMask |= WINDOWSTATE_MASK_MAXIMIZED_X | + WINDOWSTATE_MASK_MAXIMIZED_Y | + WINDOWSTATE_MASK_MAXIMIZED_WIDTH | + WINDOWSTATE_MASK_MAXIMIZED_HEIGHT; + } + else + { + pState->mnX = maGeometry.nX; + pState->mnY = maGeometry.nY; + pState->mnWidth = maGeometry.nWidth; + pState->mnHeight = maGeometry.nHeight; + } + pState->mnMask |= WINDOWSTATE_MASK_X | + WINDOWSTATE_MASK_Y | + WINDOWSTATE_MASK_WIDTH | + WINDOWSTATE_MASK_HEIGHT; + + return true; +} + +typedef enum { + SET_RETAIN_SIZE, + SET_FULLSCREEN, + SET_UN_FULLSCREEN +} SetType; + +void GtkSalFrame::SetScreen( unsigned int nNewScreen, int eType, Rectangle *pSize ) +{ + if( !m_pWindow ) + return; + + if (maGeometry.nDisplayScreenNumber == nNewScreen && eType == SET_RETAIN_SIZE) + return; + + GdkScreen *pScreen = NULL; + GdkRectangle aNewMonitor; + + bool bSpanAllScreens = nNewScreen == (unsigned int)-1; + m_bSpanMonitorsWhenFullscreen = bSpanAllScreens && getDisplay()->getSystem()->GetDisplayScreenCount() > 1; + + if (m_bSpanMonitorsWhenFullscreen) //span all screens + { + pScreen = gtk_widget_get_screen( m_pWindow ); + aNewMonitor.x = 0; + aNewMonitor.y = 0; + aNewMonitor.width = gdk_screen_get_width(pScreen); + aNewMonitor.height = gdk_screen_get_height(pScreen); + } + else + { + gint nMonitor; + bool bSameMonitor = false; + + if (!bSpanAllScreens) + { + pScreen = getDisplay()->getSystem()->getScreenMonitorFromIdx( nNewScreen, nMonitor ); + if (!pScreen) + { + g_warning ("Attempt to move GtkSalFrame to invalid screen %d => " + "fallback to current\n", nNewScreen); + } + } + + if (!pScreen) + { + pScreen = gtk_widget_get_screen( m_pWindow ); + bSameMonitor = true; + } + + // Heavy lifting, need to move screen ... + if( pScreen != gtk_widget_get_screen( m_pWindow )) + gtk_window_set_screen( GTK_WINDOW( m_pWindow ), pScreen ); + + gint nOldMonitor = gdk_screen_get_monitor_at_window( + pScreen, widget_get_window( m_pWindow ) ); + if (bSameMonitor) + nMonitor = nOldMonitor; + + #if OSL_DEBUG_LEVEL > 1 + if( nMonitor == nOldMonitor ) + g_warning( "An apparently pointless SetScreen - should we elide it ?" ); + #endif + + GdkRectangle aOldMonitor; + gdk_screen_get_monitor_geometry( pScreen, nOldMonitor, &aOldMonitor ); + gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aNewMonitor ); + + maGeometry.nX = aNewMonitor.x + maGeometry.nX - aOldMonitor.x; + maGeometry.nY = aNewMonitor.y + maGeometry.nY - aOldMonitor.y; + } + + bool bResize = false; + bool bVisible = IS_WIDGET_MAPPED( m_pWindow ); + if( bVisible ) + Show( false ); + + if( eType == SET_FULLSCREEN ) + { + maGeometry.nX = aNewMonitor.x; + maGeometry.nY = aNewMonitor.y; + maGeometry.nWidth = aNewMonitor.width; + maGeometry.nHeight = aNewMonitor.height; + m_nStyle |= SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; + bResize = true; + + // #i110881# for the benefit of compiz set a max size here + // else setting to fullscreen fails for unknown reasons + m_aMaxSize.Width() = aNewMonitor.width; + m_aMaxSize.Height() = aNewMonitor.height; + } + + if( pSize && eType == SET_UN_FULLSCREEN ) + { + maGeometry.nX = pSize->Left(); + maGeometry.nY = pSize->Top(); + maGeometry.nWidth = pSize->GetWidth(); + maGeometry.nHeight = pSize->GetHeight(); + m_nStyle &= ~SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; + bResize = true; + } + + if (bResize) + { + // temporarily re-sizeable + if( !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) ) + gtk_window_set_resizable( GTK_WINDOW(m_pWindow), TRUE ); + window_resize(maGeometry.nWidth, maGeometry.nHeight); + //I wonder if we should instead leave maGeometry alone and rely on + //configure-event to trigger signalConfigure and set it there + AllocateFrame(); + TriggerPaintEvent(); + } + + gtk_window_move( GTK_WINDOW( m_pWindow ), maGeometry.nX, maGeometry.nY ); + +#if !GTK_CHECK_VERSION(3,0,0) + // _NET_WM_STATE_FULLSCREEN (Metacity <-> KWin) + if( ! getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) +#endif + { +#if GTK_CHECK_VERSION(3,8,0) + gdk_window_set_fullscreen_mode( widget_get_window(m_pWindow), m_bSpanMonitorsWhenFullscreen + ? GDK_FULLSCREEN_ON_ALL_MONITORS : GDK_FULLSCREEN_ON_CURRENT_MONITOR ); +#endif + if( eType == SET_FULLSCREEN ) + gtk_window_fullscreen( GTK_WINDOW( m_pWindow ) ); + else if( eType == SET_UN_FULLSCREEN ) + gtk_window_unfullscreen( GTK_WINDOW( m_pWindow ) ); + } + + if( eType == SET_UN_FULLSCREEN && + !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) ) + gtk_window_set_resizable( GTK_WINDOW( m_pWindow ), FALSE ); + + // FIXME: we should really let gtk+ handle our widget hierarchy ... + if( m_pParent && gtk_widget_get_screen( m_pParent->m_pWindow ) != pScreen ) + SetParent( NULL ); + std::list< GtkSalFrame* > aChildren = m_aChildren; + for( std::list< GtkSalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it ) + (*it)->SetScreen( nNewScreen, SET_RETAIN_SIZE ); + + m_bDefaultPos = m_bDefaultSize = false; + updateScreenNumber(); + CallCallback( SALEVENT_MOVERESIZE, NULL ); + + if( bVisible ) + Show( true ); +} + +void GtkSalFrame::SetScreenNumber( unsigned int nNewScreen ) +{ + SetScreen( nNewScreen, SET_RETAIN_SIZE ); +} + +void GtkSalFrame::updateWMClass() +{ + OString aResClass = OUStringToOString(m_sWMClass, RTL_TEXTENCODING_ASCII_US); + const char *pResClass = !aResClass.isEmpty() ? aResClass.getStr() : + SalGenericSystem::getFrameClassName(); + Display *display; + + if (!getDisplay()->IsX11Display()) + return; + +#if GTK_CHECK_VERSION(3,0,0) + display = GDK_DISPLAY_XDISPLAY(getGdkDisplay()); +#else + display = getDisplay()->GetDisplay(); +#endif + + if( IS_WIDGET_REALIZED( m_pWindow ) ) + { + XClassHint* pClass = XAllocClassHint(); + OString aResName = SalGenericSystem::getFrameResName(); + pClass->res_name = const_cast(aResName.getStr()); + pClass->res_class = const_cast(pResClass); + XSetClassHint( display, + widget_get_xid(m_pWindow), + pClass ); + XFree( pClass ); + } +} + +void GtkSalFrame::SetApplicationID( const OUString &rWMClass ) +{ + if( rWMClass != m_sWMClass && ! isChild() ) + { + m_sWMClass = rWMClass; + updateWMClass(); + + for( std::list< GtkSalFrame* >::iterator it = m_aChildren.begin(); it != m_aChildren.end(); ++it ) + (*it)->SetApplicationID(rWMClass); + } +} + +void GtkSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nScreen ) +{ + m_bFullscreen = bFullScreen; + + if( !m_pWindow || isChild() ) + return; + + if( bFullScreen ) + { + m_aRestorePosSize = Rectangle( Point( maGeometry.nX, maGeometry.nY ), + Size( maGeometry.nWidth, maGeometry.nHeight ) ); + SetScreen( nScreen, SET_FULLSCREEN ); + } + else + { + SetScreen( nScreen, SET_UN_FULLSCREEN, + !m_aRestorePosSize.IsEmpty() ? &m_aRestorePosSize : NULL ); + m_aRestorePosSize = Rectangle(); + } +} + +/* definitions from xautolock.c (pl15) */ +#define XAUTOLOCK_DISABLE 1 +#define XAUTOLOCK_ENABLE 2 + +void GtkSalFrame::setAutoLock( bool bLock ) +{ + if( isChild() || !getDisplay()->IsX11Display() ) + return; + + GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(m_pWindow) ); + GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); + GdkWindow *pRootWin = gdk_screen_get_root_window( pScreen ); + + Atom nAtom = XInternAtom( GDK_DISPLAY_XDISPLAY( pDisplay ), + "XAUTOLOCK_MESSAGE", False ); + + int nMessage = bLock ? XAUTOLOCK_ENABLE : XAUTOLOCK_DISABLE; + + XChangeProperty( GDK_DISPLAY_XDISPLAY( pDisplay ), + GDK_WINDOW_XID( pRootWin ), + nAtom, XA_INTEGER, + 8, PropModeReplace, + reinterpret_cast(&nMessage), + sizeof( nMessage ) ); +} + +#ifdef ENABLE_DBUS +/** cookie is returned as an unsigned integer */ +static guint +dbus_inhibit_gsm (const gchar *appname, + const gchar *reason, + guint xid) +{ + gboolean res; + guint cookie; + GError *error = NULL; + DBusGProxy *proxy = NULL; + + /* get the DBUS session connection */ + DBusGConnection *session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (error != NULL) { + g_debug ("DBUS cannot connect : %s", error->message); + g_error_free (error); + return -1; + } + + /* get the proxy with gnome-session-manager */ + proxy = dbus_g_proxy_new_for_name (session_connection, + GSM_DBUS_SERVICE, + GSM_DBUS_PATH, + GSM_DBUS_INTERFACE); + if (proxy == NULL) { + g_debug ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE); + return -1; + } + + res = dbus_g_proxy_call (proxy, + "Inhibit", &error, + G_TYPE_STRING, appname, + G_TYPE_UINT, xid, + G_TYPE_STRING, reason, + G_TYPE_UINT, 8, //Inhibit the session being marked as idle + G_TYPE_INVALID, + G_TYPE_UINT, &cookie, + G_TYPE_INVALID); + + /* check the return value */ + if (! res) { + cookie = -1; + g_debug ("Inhibit method failed"); + } + + /* check the error value */ + if (error != NULL) { + g_debug ("Inhibit problem : %s", error->message); + g_error_free (error); + cookie = -1; + } + + g_object_unref (G_OBJECT (proxy)); + return cookie; +} + +static void +dbus_uninhibit_gsm (guint cookie) +{ + gboolean res; + GError *error = NULL; + DBusGProxy *proxy = NULL; + DBusGConnection *session_connection = NULL; + + if (cookie == guint(-1)) { + g_debug ("Invalid cookie"); + return; + } + + /* get the DBUS session connection */ + session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (error) { + g_debug ("DBUS cannot connect : %s", error->message); + g_error_free (error); + return; + } + + /* get the proxy with gnome-session-manager */ + proxy = dbus_g_proxy_new_for_name (session_connection, + GSM_DBUS_SERVICE, + GSM_DBUS_PATH, + GSM_DBUS_INTERFACE); + if (proxy == NULL) { + g_debug ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE); + return; + } + + res = dbus_g_proxy_call (proxy, + "Uninhibit", + &error, + G_TYPE_UINT, cookie, + G_TYPE_INVALID, + G_TYPE_INVALID); + + /* check the return value */ + if (! res) { + g_debug ("Uninhibit method failed"); + } + + /* check the error value */ + if (error != NULL) { + g_debug ("Uninhibit problem : %s", error->message); + g_error_free (error); + cookie = -1; + } + g_object_unref (G_OBJECT (proxy)); +} +#endif + +void GtkSalFrame::StartPresentation( bool bStart ) +{ + setAutoLock( !bStart ); + + if( !getDisplay()->IsX11Display() ) + return; + +#if !GTK_CHECK_VERSION(3,0,0) + Display *pDisplay = GDK_DISPLAY_XDISPLAY( getGdkDisplay() ); + + int nTimeout, nInterval, bPreferBlanking, bAllowExposures; + XGetScreenSaver( pDisplay, &nTimeout, &nInterval, + &bPreferBlanking, &bAllowExposures ); +#endif + if( bStart ) + { +#if !GTK_CHECK_VERSION(3,0,0) + if ( nTimeout ) + { + m_nSavedScreenSaverTimeout = nTimeout; + XResetScreenSaver( pDisplay ); + XSetScreenSaver( pDisplay, 0, nInterval, + bPreferBlanking, bAllowExposures ); + } +#endif +#ifdef ENABLE_DBUS + m_nGSMCookie = dbus_inhibit_gsm(g_get_application_name(), "presentation", + widget_get_xid(m_pWindow)); +#endif + } + else + { +#if !GTK_CHECK_VERSION(3,0,0) + if( m_nSavedScreenSaverTimeout ) + XSetScreenSaver( pDisplay, m_nSavedScreenSaverTimeout, + nInterval, bPreferBlanking, + bAllowExposures ); +#endif + m_nSavedScreenSaverTimeout = 0; +#ifdef ENABLE_DBUS + dbus_uninhibit_gsm(m_nGSMCookie); +#endif + } +} + +void GtkSalFrame::SetAlwaysOnTop( bool bOnTop ) +{ + if( m_pWindow ) + gtk_window_set_keep_above( GTK_WINDOW( m_pWindow ), bOnTop ); +} + +void GtkSalFrame::ToTop( sal_uInt16 nFlags ) +{ + if( m_pWindow ) + { + if( isChild( false, true ) ) + gtk_widget_grab_focus( m_pWindow ); + else if( IS_WIDGET_MAPPED( m_pWindow ) ) + { + if( ! (nFlags & SAL_FRAME_TOTOP_GRABFOCUS_ONLY) ) + gtk_window_present( GTK_WINDOW(m_pWindow) ); + else + { +#if !GTK_CHECK_VERSION(3,0,0) + guint32 nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); +#else + guint32 nUserTime = GDK_CURRENT_TIME; +#endif + gdk_window_focus( widget_get_window(m_pWindow), nUserTime ); + } +#if !GTK_CHECK_VERSION(3,0,0) + /* need to do an XSetInputFocus here because + * gdk_window_focus will ask a EWMH compliant WM to put the focus + * to our window - which it of course won't since our input hint + * is set to false. + */ + if( (m_nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_FLOAT_FOCUSABLE)) ) + { + // sad but true: this can cause an XError, we need to catch that + // to do this we need to synchronize with the XServer + GetGenericData()->ErrorTrapPush(); + XSetInputFocus( getDisplay()->GetDisplay(), widget_get_xid(m_pWindow), RevertToParent, CurrentTime ); + // fdo#46687 - an XSync should not be necessary - but for some reason it is. + XSync( getDisplay()->GetDisplay(), False ); + GetGenericData()->ErrorTrapPop(); + } +#endif + } + else + { + if( nFlags & SAL_FRAME_TOTOP_RESTOREWHENMIN ) + gtk_window_present( GTK_WINDOW(m_pWindow) ); + } + } +} + +void GtkSalFrame::SetPointer( PointerStyle ePointerStyle ) +{ + if( m_pWindow && ePointerStyle != m_ePointerStyle ) + { + m_ePointerStyle = ePointerStyle; + GdkCursor *pCursor = getDisplay()->getCursor( ePointerStyle ); + gdk_window_set_cursor( widget_get_window(m_pWindow), pCursor ); + m_pCurrentCursor = pCursor; + + // #i80791# use grabPointer the same way as CaptureMouse, respective float grab + if( getDisplay()->MouseCaptured( this ) ) + grabPointer( true, false ); + else if( m_nFloats > 0 ) + grabPointer( true, true ); + } +} + +void GtkSalFrame::grabPointer( bool bGrab, bool bOwnerEvents ) +{ + static const char* pEnv = getenv( "SAL_NO_MOUSEGRABS" ); + if (pEnv && *pEnv) + return; + + if (!m_pWindow) + return; + + const int nMask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); + +#if GTK_CHECK_VERSION(3,0,0) + GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay()); + GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager); + if (bGrab) + gdk_device_grab(pPointer, widget_get_window(getMouseEventWidget()), GDK_OWNERSHIP_NONE, bOwnerEvents, (GdkEventMask) nMask, m_pCurrentCursor, GDK_CURRENT_TIME); + else + gdk_device_ungrab(pPointer, GDK_CURRENT_TIME); +#else + if( bGrab ) + { + bool bUseGdkGrab = true; + const std::list< SalFrame* >& rFrames = getDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + const GtkSalFrame* pFrame = static_cast< const GtkSalFrame* >(*it); + if( pFrame->m_bWindowIsGtkPlug ) + { + bUseGdkGrab = false; + break; + } + } + if( bUseGdkGrab ) + { + gdk_pointer_grab( widget_get_window( m_pWindow ), bOwnerEvents, + (GdkEventMask) nMask, NULL, m_pCurrentCursor, + GDK_CURRENT_TIME ); + } + else + { + // FIXME: for some unknown reason gdk_pointer_grab does not + // really produce owner events for GtkPlug windows + // the cause is yet unknown + + // this is of course a bad hack, especially as we cannot + // set the right cursor this way + XGrabPointer( getDisplay()->GetDisplay(), + widget_get_xid( m_pWindow ), + bOwnerEvents, + PointerMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, + GrabModeAsync, + None, + None, + CurrentTime + ); + } + } + else + { + // Two GdkDisplays may be open + gdk_display_pointer_ungrab( getGdkDisplay(), GDK_CURRENT_TIME); + } +#endif +} + +void GtkSalFrame::grabKeyboard( bool bGrab ) +{ + if (!m_pWindow) + return; + +#if GTK_CHECK_VERSION(3,0,0) + GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay()); + GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager); + GdkDevice* pKeyboard = gdk_device_get_associated_device(pPointer); + if (bGrab) + { + gdk_device_grab(pKeyboard, widget_get_window(m_pWindow), GDK_OWNERSHIP_NONE, + true, (GdkEventMask)(GDK_KEY_PRESS | GDK_KEY_RELEASE), NULL, GDK_CURRENT_TIME); + } + else + { + gdk_device_ungrab(pKeyboard, GDK_CURRENT_TIME); + } +#else + if( bGrab ) + { + gdk_keyboard_grab(widget_get_window(m_pWindow), true, + GDK_CURRENT_TIME); + } + else + { + gdk_keyboard_ungrab(GDK_CURRENT_TIME); + } +#endif +} + +void GtkSalFrame::CaptureMouse( bool bCapture ) +{ + getDisplay()->CaptureMouse( bCapture ? this : NULL ); +} + +void GtkSalFrame::SetPointerPos( long nX, long nY ) +{ + GtkSalFrame* pFrame = this; + while( pFrame && pFrame->isChild( false, true ) ) + pFrame = pFrame->m_pParent; + if( ! pFrame ) + return; + + GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(pFrame->m_pWindow) ); + GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); + + /* when the application tries to center the mouse in the dialog the + * window isn't mapped already. So use coordinates relative to the root window. + */ + unsigned int nWindowLeft = maGeometry.nX + nX; + unsigned int nWindowTop = maGeometry.nY + nY; + + XWarpPointer( GDK_DISPLAY_XDISPLAY (pDisplay), None, + GDK_WINDOW_XID (gdk_screen_get_root_window( pScreen ) ), + 0, 0, 0, 0, nWindowLeft, nWindowTop); + // #i38648# ask for the next motion hint + gint x, y; + GdkModifierType mask; + gdk_window_get_pointer( widget_get_window(pFrame->m_pWindow) , &x, &y, &mask ); +} + +void GtkSalFrame::Flush() +{ +#if GTK_CHECK_VERSION(3,0,0) + gdk_display_flush( getGdkDisplay() ); +#else + XFlush (GDK_DISPLAY_XDISPLAY (getGdkDisplay())); +#endif +} + +void GtkSalFrame::Sync() +{ + gdk_display_sync( getGdkDisplay() ); +} + +#ifndef GDK_Open +#define GDK_Open 0x1008ff6b +#endif +#ifndef GDK_Paste +#define GDK_Paste 0x1008ff6d +#endif +#ifndef GDK_Copy +#define GDK_Copy 0x1008ff57 +#endif +#ifndef GDK_Cut +#define GDK_Cut 0x1008ff58 +#endif + +void GtkSalFrame::KeyCodeToGdkKey(const vcl::KeyCode& rKeyCode, + guint* pGdkKeyCode, GdkModifierType *pGdkModifiers) +{ + if ( pGdkKeyCode == NULL || pGdkModifiers == NULL ) + return; + + // Get GDK key modifiers + GdkModifierType nModifiers = (GdkModifierType) 0; + + if ( rKeyCode.IsShift() ) + nModifiers = (GdkModifierType) ( nModifiers | GDK_SHIFT_MASK ); + + if ( rKeyCode.IsMod1() ) + nModifiers = (GdkModifierType) ( nModifiers | GDK_CONTROL_MASK ); + + if ( rKeyCode.IsMod2() ) + nModifiers = (GdkModifierType) ( nModifiers | GDK_MOD1_MASK ); + + *pGdkModifiers = nModifiers; + + // Get GDK keycode. + guint nKeyCode = 0; + + guint nCode = rKeyCode.GetCode(); + + if ( nCode >= KEY_0 && nCode <= KEY_9 ) + nKeyCode = ( nCode - KEY_0 ) + GDK_0; + else if ( nCode >= KEY_A && nCode <= KEY_Z ) + nKeyCode = ( nCode - KEY_A ) + GDK_A; + else if ( nCode >= KEY_F1 && nCode <= KEY_F26 ) + nKeyCode = ( nCode - KEY_F1 ) + GDK_F1; + else + { + switch( nCode ) + { + case KEY_DOWN: nKeyCode = GDK_Down; break; + case KEY_UP: nKeyCode = GDK_Up; break; + case KEY_LEFT: nKeyCode = GDK_Left; break; + case KEY_RIGHT: nKeyCode = GDK_Right; break; + case KEY_HOME: nKeyCode = GDK_Home; break; + case KEY_END: nKeyCode = GDK_End; break; + case KEY_PAGEUP: nKeyCode = GDK_Page_Up; break; + case KEY_PAGEDOWN: nKeyCode = GDK_Page_Down; break; + case KEY_RETURN: nKeyCode = GDK_Return; break; + case KEY_ESCAPE: nKeyCode = GDK_Escape; break; + case KEY_TAB: nKeyCode = GDK_Tab; break; + case KEY_BACKSPACE: nKeyCode = GDK_BackSpace; break; + case KEY_SPACE: nKeyCode = GDK_space; break; + case KEY_INSERT: nKeyCode = GDK_Insert; break; + case KEY_DELETE: nKeyCode = GDK_Delete; break; + case KEY_ADD: nKeyCode = GDK_plus; break; + case KEY_SUBTRACT: nKeyCode = GDK_minus; break; + case KEY_MULTIPLY: nKeyCode = GDK_asterisk; break; + case KEY_DIVIDE: nKeyCode = GDK_slash; break; + case KEY_POINT: nKeyCode = GDK_period; break; + case KEY_COMMA: nKeyCode = GDK_comma; break; + case KEY_LESS: nKeyCode = GDK_less; break; + case KEY_GREATER: nKeyCode = GDK_greater; break; + case KEY_EQUAL: nKeyCode = GDK_equal; break; + case KEY_FIND: nKeyCode = GDK_Find; break; + case KEY_CONTEXTMENU: nKeyCode = GDK_Menu; break; + case KEY_HELP: nKeyCode = GDK_Help; break; + case KEY_UNDO: nKeyCode = GDK_Undo; break; + case KEY_REPEAT: nKeyCode = GDK_Redo; break; + case KEY_DECIMAL: nKeyCode = GDK_KP_Decimal; break; + case KEY_TILDE: nKeyCode = GDK_asciitilde; break; + case KEY_QUOTELEFT: nKeyCode = GDK_quoteleft; break; + case KEY_BRACKETLEFT: nKeyCode = GDK_bracketleft; break; + case KEY_BRACKETRIGHT: nKeyCode = GDK_bracketright; break; + case KEY_SEMICOLON: nKeyCode = GDK_semicolon; break; + case KEY_QUOTERIGHT: nKeyCode = GDK_quoteright; break; + + // Special cases + case KEY_COPY: nKeyCode = GDK_Copy; break; + case KEY_CUT: nKeyCode = GDK_Cut; break; + case KEY_PASTE: nKeyCode = GDK_Paste; break; + case KEY_OPEN: nKeyCode = GDK_Open; break; + } + } + + *pGdkKeyCode = nKeyCode; +} + +OUString GtkSalFrame::GetKeyName( sal_uInt16 nKeyCode ) +{ +#if !GTK_CHECK_VERSION(3,0,0) + return getDisplay()->GetKeyName( nKeyCode ); +#else + guint nGtkKeyCode; + GdkModifierType nGtkModifiers; + KeyCodeToGdkKey(nKeyCode, &nGtkKeyCode, &nGtkModifiers ); + + gchar* pName = gtk_accelerator_get_label(nGtkKeyCode, nGtkModifiers); + OUString aRet(pName, rtl_str_getLength(pName), RTL_TEXTENCODING_UTF8); + g_free(pName); + return aRet; +#endif +} + +GdkDisplay *GtkSalFrame::getGdkDisplay() +{ + return GetGtkSalData()->GetGdkDisplay(); +} + +GtkSalDisplay *GtkSalFrame::getDisplay() +{ + return GetGtkSalData()->GetGtkDisplay(); +} + +SalFrame::SalPointerState GtkSalFrame::GetPointerState() +{ + SalPointerState aState; + GdkScreen* pScreen; + gint x, y; + GdkModifierType aMask; + gdk_display_get_pointer( getGdkDisplay(), &pScreen, &x, &y, &aMask ); + aState.maPos = Point( x - maGeometry.nX, y - maGeometry.nY ); + aState.mnState = GetMouseModCode( aMask ); + return aState; +} + +KeyIndicatorState GtkSalFrame::GetIndicatorState() +{ +#if !GTK_CHECK_VERSION(3,0,0) + return GetGtkSalData()->GetGtkDisplay()->GetIndicatorState(); +#else + KeyIndicatorState nState = KeyIndicatorState::NONE; + + GdkKeymap *pKeyMap = gdk_keymap_get_for_display(getGdkDisplay()); + + if (gdk_keymap_get_caps_lock_state(pKeyMap)) + nState |= KeyIndicatorState::CAPSLOCK; + if (gdk_keymap_get_num_lock_state(pKeyMap)) + nState |= KeyIndicatorState::NUMLOCK; +#if GTK_CHECK_VERSION(3,18,0) + if (gdk_keymap_get_scroll_lock_state(pKeyMap)) + nState |= KeyIndicatorState::SCROLLLOCK; +#endif + return nState; +#endif +} + +void GtkSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode ) +{ +#if !GTK_CHECK_VERSION(3,0,0) + GetGtkSalData()->GetGtkDisplay()->SimulateKeyPress(nKeyCode); +#else + g_warning ("missing simulate keypress %d", nKeyCode); +#endif +} + +void GtkSalFrame::SetInputContext( SalInputContext* pContext ) +{ + if( ! pContext ) + return; + + if( ! (pContext->mnOptions & InputContextFlags::Text) ) + return; + + // create a new im context + if( ! m_pIMHandler ) + m_pIMHandler = new IMHandler( this ); +} + +void GtkSalFrame::EndExtTextInput( sal_uInt16 nFlags ) +{ + if( m_pIMHandler ) + m_pIMHandler->endExtTextInput( nFlags ); +} + +bool GtkSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , vcl::KeyCode& ) +{ + // not supported yet + return false; +} + +LanguageType GtkSalFrame::GetInputLanguage() +{ + return LANGUAGE_DONTKNOW; +} + +void GtkSalFrame::UpdateSettings( AllSettings& rSettings ) +{ + if( ! m_pWindow ) + return; + + GtkSalGraphics* pGraphics = static_cast(m_aGraphics[0].pGraphics); + bool bFreeGraphics = false; + if( ! pGraphics ) + { + pGraphics = static_cast(AcquireGraphics()); + if ( !pGraphics ) + { + SAL_WARN("vcl", "Could not get graphics - unable to update settings"); + return; + } + bFreeGraphics = true; + } + + pGraphics->updateSettings( rSettings ); + + if( bFreeGraphics ) + ReleaseGraphics( pGraphics ); +} + +void GtkSalFrame::Beep() +{ + gdk_display_beep( getGdkDisplay() ); +} + +const SystemEnvData* GtkSalFrame::GetSystemData() const +{ + return &m_aSystemData; +} + +void GtkSalFrame::SetParent( SalFrame* pNewParent ) +{ + if( m_pParent ) + m_pParent->m_aChildren.remove( this ); + m_pParent = static_cast(pNewParent); + if( m_pParent ) + m_pParent->m_aChildren.push_back( this ); + if( ! isChild() ) + gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), + (m_pParent && ! m_pParent->isChild(true,false)) ? GTK_WINDOW(m_pParent->m_pWindow) : NULL + ); +} + +#if !GTK_CHECK_VERSION(3,0,0) + +void GtkSalFrame::createNewWindow( ::Window aNewParent, bool bXEmbed, SalX11Screen nXScreen ) +{ + bool bWasVisible = m_pWindow && IS_WIDGET_MAPPED(m_pWindow); + if( bWasVisible ) + Show( false ); + + if( (int)nXScreen.getXScreen() >= getDisplay()->GetXScreenCount() ) + nXScreen = m_nXScreen; + + SystemParentData aParentData; + aParentData.aWindow = aNewParent; + aParentData.bXEmbedSupport = bXEmbed; + if( aNewParent == None ) + { + aNewParent = getDisplay()->GetRootWindow(nXScreen); + aParentData.aWindow = None; + aParentData.bXEmbedSupport = false; + } + else + { + // is new parent a root window ? + Display* pDisp = getDisplay()->GetDisplay(); + int nScreens = getDisplay()->GetXScreenCount(); + for( int i = 0; i < nScreens; i++ ) + { + if( aNewParent == RootWindow( pDisp, i ) ) + { + nXScreen = SalX11Screen( i ); + aParentData.aWindow = None; + aParentData.bXEmbedSupport = false; + break; + } + } + } + + // free xrender resources + for( unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); i++ ) + if( m_aGraphics[i].bInUse ) + m_aGraphics[i].pGraphics->SetDrawable( None, m_nXScreen ); + + // first deinit frame + if( m_pIMHandler ) + { + delete m_pIMHandler; + m_pIMHandler = NULL; + } + if( m_pRegion ) + { + gdk_region_destroy( m_pRegion ); + } + + GtkWidget *pEventWidget = getMouseEventWidget(); + for (auto handler_id : m_aMouseSignalIds) + g_signal_handler_disconnect(G_OBJECT(pEventWidget), handler_id); + if( m_pFixedContainer ) + gtk_widget_destroy( GTK_WIDGET(m_pFixedContainer) ); + if( m_pEventBox ) + gtk_widget_destroy( GTK_WIDGET(m_pEventBox) ); + if( m_pWindow ) + gtk_widget_destroy( m_pWindow ); + if( m_pForeignParent ) + g_object_unref( G_OBJECT( m_pForeignParent ) ); + if( m_pForeignTopLevel ) + g_object_unref( G_OBJECT( m_pForeignTopLevel ) ); + + // init new window + m_bDefaultPos = m_bDefaultSize = false; + if( aParentData.aWindow != None ) + { + m_nStyle |= SAL_FRAME_STYLE_PLUG; + Init( &aParentData ); + } + else + { + m_nStyle &= ~SAL_FRAME_STYLE_PLUG; + Init( (m_pParent && m_pParent->m_nXScreen == m_nXScreen) ? m_pParent : NULL, m_nStyle ); + } + + // update graphics + for( unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); i++ ) + { + if( m_aGraphics[i].bInUse ) + { + m_aGraphics[i].pGraphics->SetDrawable( widget_get_xid(m_pWindow), m_nXScreen ); + m_aGraphics[i].pGraphics->SetWindow( m_pWindow ); + } + } + + if( ! m_aTitle.isEmpty() ) + SetTitle( m_aTitle ); + + if( bWasVisible ) + Show( true ); + + std::list< GtkSalFrame* > aChildren = m_aChildren; + m_aChildren.clear(); + for( std::list< GtkSalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it ) + (*it)->createNewWindow( None, false, m_nXScreen ); + + // FIXME: SalObjects +} +#endif + +bool GtkSalFrame::SetPluginParent( SystemParentData* pSysParent ) +{ +#if !GTK_CHECK_VERSION(3,0,0) + GetGenericData()->ErrorTrapPush(); // permanantly ignore unruly children's errors + createNewWindow( pSysParent->aWindow, (pSysParent->nSize > sizeof(long)) && pSysParent->bXEmbedSupport, m_nXScreen ); + return true; +#else + (void)pSysParent; + //FIXME: no SetPluginParent impl. for gtk3 + return false; +#endif +} + +void GtkSalFrame::ResetClipRegion() +{ + if( m_pWindow ) + gdk_window_shape_combine_region( widget_get_window( m_pWindow ), NULL, 0, 0 ); +} + +void GtkSalFrame::BeginSetClipRegion( sal_uLong ) +{ +#if GTK_CHECK_VERSION(3,0,0) + if( m_pRegion ) + cairo_region_destroy( m_pRegion ); + m_pRegion = cairo_region_create(); +#else + if( m_pRegion ) + gdk_region_destroy( m_pRegion ); + m_pRegion = gdk_region_new(); +#endif +} + +void GtkSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + if( m_pRegion ) + { + GdkRectangle aRect; + aRect.x = nX; + aRect.y = nY; + aRect.width = nWidth; + aRect.height = nHeight; +#if GTK_CHECK_VERSION(3,0,0) + cairo_region_union_rectangle( m_pRegion, &aRect ); +#else + gdk_region_union_with_rect( m_pRegion, &aRect ); +#endif + } +} + +void GtkSalFrame::EndSetClipRegion() +{ + if( m_pWindow && m_pRegion ) + gdk_window_shape_combine_region( widget_get_window(m_pWindow), m_pRegion, 0, 0 ); +} + +#if !GTK_CHECK_VERSION(3,0,0) +bool GtkSalFrame::Dispatch( const XEvent* pEvent ) +{ + bool bContinueDispatch = true; + + if( pEvent->type == PropertyNotify ) + { + vcl_sal::WMAdaptor* pAdaptor = getDisplay()->getWMAdaptor(); + Atom nDesktopAtom = pAdaptor->getAtom( vcl_sal::WMAdaptor::NET_WM_DESKTOP ); + if( pEvent->xproperty.atom == nDesktopAtom && + pEvent->xproperty.state == PropertyNewValue ) + { + m_nWorkArea = pAdaptor->getWindowWorkArea( widget_get_xid(m_pWindow) ); + } + } + else if( pEvent->type == ConfigureNotify ) + { + if( m_pForeignParent && pEvent->xconfigure.window == m_aForeignParentWindow ) + { + bContinueDispatch = false; + gtk_window_resize( GTK_WINDOW(m_pWindow), pEvent->xconfigure.width, pEvent->xconfigure.height ); + if( ( sal::static_int_cast< int >(maGeometry.nWidth) != + pEvent->xconfigure.width ) || + ( sal::static_int_cast< int >(maGeometry.nHeight) != + pEvent->xconfigure.height ) ) + { + maGeometry.nWidth = pEvent->xconfigure.width; + maGeometry.nHeight = pEvent->xconfigure.height; + setMinMaxSize(); + getDisplay()->SendInternalEvent( this, NULL, SALEVENT_RESIZE ); + } + } + else if( m_pForeignTopLevel && pEvent->xconfigure.window == m_aForeignTopLevelWindow ) + { + bContinueDispatch = false; + // update position + int x = 0, y = 0; + ::Window aChild; + XTranslateCoordinates( getDisplay()->GetDisplay(), + widget_get_xid(m_pWindow), + getDisplay()->GetRootWindow( getDisplay()->GetDefaultXScreen() ), + 0, 0, + &x, &y, + &aChild ); + if( x != maGeometry.nX || y != maGeometry.nY ) + { + maGeometry.nX = x; + maGeometry.nY = y; + getDisplay()->SendInternalEvent( this, NULL, SALEVENT_MOVE ); + } + } + } + else if( pEvent->type == ClientMessage && + pEvent->xclient.message_type == getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED ) && + pEvent->xclient.window == widget_get_xid(m_pWindow) && + m_bWindowIsGtkPlug + ) + { + // FIXME: this should not be necessary, GtkPlug should do this + // transparently for us + if( pEvent->xclient.data.l[1] == 1 || // XEMBED_WINDOW_ACTIVATE + pEvent->xclient.data.l[1] == 2 // XEMBED_WINDOW_DEACTIVATE + ) + { + GdkEventFocus aEvent; + aEvent.type = GDK_FOCUS_CHANGE; + aEvent.window = widget_get_window( m_pWindow ); + aEvent.send_event = gint8(TRUE); + aEvent.in = gint16(pEvent->xclient.data.l[1] == 1); + signalFocus( m_pWindow, &aEvent, this ); + } + } + + return bContinueDispatch; +} +#endif + +gboolean GtkSalFrame::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + SalMouseEvent aEvent; + sal_uInt16 nEventType = 0; + switch( pEvent->type ) + { + case GDK_BUTTON_PRESS: + nEventType = SALEVENT_MOUSEBUTTONDOWN; + break; + case GDK_BUTTON_RELEASE: + nEventType = SALEVENT_MOUSEBUTTONUP; + break; + default: + return false; + } + switch( pEvent->button ) + { + case 1: aEvent.mnButton = MOUSE_LEFT; break; + case 2: aEvent.mnButton = MOUSE_MIDDLE; break; + case 3: aEvent.mnButton = MOUSE_RIGHT; break; + default: return false; + } + aEvent.mnTime = pEvent->time; + aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; + aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; + aEvent.mnCode = GetMouseModCode( pEvent->state ); + + bool bClosePopups = false; + if( pEvent->type == GDK_BUTTON_PRESS && + (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) == 0 + ) + { + if( m_nFloats > 0 ) + { + // close popups if user clicks outside our application + gint x, y; + bClosePopups = (gdk_display_get_window_at_pointer( GtkSalFrame::getGdkDisplay(), &x, &y ) == NULL); + } + /* #i30306# release implicit pointer grab if no popups are open; else + * Drag cannot grab the pointer and will fail. + */ + if( m_nFloats < 1 || bClosePopups ) + gdk_display_pointer_ungrab( GtkSalFrame::getGdkDisplay(), GDK_CURRENT_TIME ); + } + + if( pThis->m_bWindowIsGtkPlug && + pEvent->type == GDK_BUTTON_PRESS && + pEvent->button == 1 ) + { + pThis->askForXEmbedFocus( pEvent->time ); + } + + // --- RTL --- (mirror mouse pos) + if( AllSettings::GetLayoutRTL() ) + aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; + + vcl::DeletionListener aDel( pThis ); + + pThis->CallCallback( nEventType, &aEvent ); + + if( ! aDel.isDeleted() ) + { + if( bClosePopups ) + { + ImplSVData* pSVData = ImplGetSVData(); + if ( pSVData->maWinData.mpFirstFloat ) + { + static const char* pEnv = getenv( "SAL_FLOATWIN_NOAPPFOCUSCLOSE" ); + if ( !(pSVData->maWinData.mpFirstFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose) && !(pEnv && *pEnv) ) + pSVData->maWinData.mpFirstFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); + } + } + + if( ! aDel.isDeleted() ) + { + int frame_x = (int)(pEvent->x_root - pEvent->x); + int frame_y = (int)(pEvent->y_root - pEvent->y); + if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) + { + pThis->maGeometry.nX = frame_x; + pThis->maGeometry.nY = frame_y; + pThis->CallCallback( SALEVENT_MOVE, NULL ); + } + } + } + + return true; +} + +#if GTK_CHECK_VERSION(3,0,0) +void GtkSalFrame::signalFlagsChanged( GtkWidget* , GtkStateFlags state, gpointer frame ) +{ + //TO-DO: This isn't as helpful as I'd like it to be. The color selector puts the main + //windows into the backdrop, disabling everything, and the floating navigator window + //is also problematic. + GtkSalFrame* pThis = static_cast(frame); + + bool bOldBackDrop = state & GTK_STATE_FLAG_BACKDROP; + bool bNewBackDrop = (gtk_widget_get_state_flags(GTK_WIDGET(pThis->m_pWindow)) & GTK_STATE_FLAG_BACKDROP); + if (bNewBackDrop && !bOldBackDrop) + { + pThis->GetWindow()->Disable(); + } + else if (bOldBackDrop && !bNewBackDrop) + { + pThis->GetWindow()->Enable(); + } +} +#endif + +gboolean GtkSalFrame::signalScroll( GtkWidget*, GdkEvent* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + GdkEventScroll* pSEvent = reinterpret_cast(pEvent); + +#if GTK_CHECK_VERSION(3,0,0) + // gnome#726878 check for duplicate legacy scroll event + if (pSEvent->direction != GDK_SCROLL_SMOOTH && + pThis->m_nLastScrollEventTime == pSEvent->time) + { + return true; + } +#endif + + SalWheelMouseEvent aEvent; + + aEvent.mnTime = pSEvent->time; + aEvent.mnX = (sal_uLong)pSEvent->x; + aEvent.mnY = (sal_uLong)pSEvent->y; + aEvent.mnCode = GetMouseModCode( pSEvent->state ); + aEvent.mnScrollLines = 3; + + switch (pSEvent->direction) + { +#if GTK_CHECK_VERSION(3,0,0) + case GDK_SCROLL_SMOOTH: + { + double delta_x, delta_y; + gdk_event_get_scroll_deltas(pEvent, &delta_x, &delta_y); + //pick the bigger one I guess + aEvent.mbHorz = fabs(delta_x) > fabs(delta_y); + if (aEvent.mbHorz) + aEvent.mnDelta = -delta_x; + else + aEvent.mnDelta = -delta_y; + aEvent.mnScrollLines = 1; + pThis->m_nLastScrollEventTime = pSEvent->time; + break; + } +#endif + case GDK_SCROLL_UP: + aEvent.mnDelta = 120; + aEvent.mbHorz = false; + break; + case GDK_SCROLL_DOWN: + aEvent.mnDelta = -120; + aEvent.mbHorz = false; + break; + case GDK_SCROLL_LEFT: + aEvent.mbHorz = true; + aEvent.mnDelta = 120; + break; + case GDK_SCROLL_RIGHT: + aEvent.mnDelta = -120; + aEvent.mbHorz = true; + break; + }; + + aEvent.mnNotchDelta = aEvent.mnDelta < 0 ? -1 : 1; + + // --- RTL --- (mirror mouse pos) + if( AllSettings::GetLayoutRTL() ) + aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; + + pThis->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); + + return true; +} + +#if GTK_CHECK_VERSION(3,14,0) +void GtkSalFrame::gestureSwipe(GtkGestureSwipe* gesture, gdouble velocity_x, gdouble velocity_y, gpointer frame) +{ + gdouble x, y; + GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); + //I feel I want the first point of the sequence, not the last point which + //the docs say this gives, but for the moment assume we start and end + //within the same vcl window + if (gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y)) + { + GtkSalFrame* pThis = static_cast(frame); + + SalSwipeEvent aEvent; + aEvent.mnVelocityX = velocity_x; + aEvent.mnVelocityY = velocity_y; + aEvent.mnX = x; + aEvent.mnY = y; + + pThis->CallCallback(SALEVENT_SWIPE, &aEvent); + } +} + +void GtkSalFrame::gestureLongPress(GtkGestureLongPress* gesture, gpointer frame) +{ + GtkSalFrame* pThis = static_cast(frame); + + if(pThis) + { + SalLongPressEvent aEvent; + + gdouble x, y; + GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); + gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y); + aEvent.mnX = x; + aEvent.mnY = y; + + pThis->CallCallback(SALEVENT_LONGPRESS, &aEvent); + } +} + +#endif + +gboolean GtkSalFrame::signalMotion( GtkWidget*, GdkEventMotion* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + SalMouseEvent aEvent; + aEvent.mnTime = pEvent->time; + aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; + aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; + aEvent.mnCode = GetMouseModCode( pEvent->state ); + aEvent.mnButton = 0; + + // --- RTL --- (mirror mouse pos) + if( AllSettings::GetLayoutRTL() ) + aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; + + vcl::DeletionListener aDel( pThis ); + + pThis->CallCallback( SALEVENT_MOUSEMOVE, &aEvent ); + + if( ! aDel.isDeleted() ) + { + int frame_x = (int)(pEvent->x_root - pEvent->x); + int frame_y = (int)(pEvent->y_root - pEvent->y); + if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) + { + pThis->maGeometry.nX = frame_x; + pThis->maGeometry.nY = frame_y; + pThis->CallCallback( SALEVENT_MOVE, NULL ); + } + + if( ! aDel.isDeleted() ) + { + // ask for the next hint + gint x, y; + GdkModifierType mask; + gdk_window_get_pointer( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &x, &y, &mask ); + } + } + + return true; +} + +gboolean GtkSalFrame::signalCrossing( GtkWidget*, GdkEventCrossing* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + SalMouseEvent aEvent; + aEvent.mnTime = pEvent->time; + aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; + aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; + aEvent.mnCode = GetMouseModCode( pEvent->state ); + aEvent.mnButton = 0; + + pThis->CallCallback( (pEvent->type == GDK_ENTER_NOTIFY) ? SALEVENT_MOUSEMOVE : SALEVENT_MOUSELEAVE, &aEvent ); + + return true; +} + +#if GTK_CHECK_VERSION(3,0,0) + +cairo_t* GtkSalFrame::getCairoContext() const +{ + cairo_t* cr = SvpSalGraphics::createCairoContext(m_aFrame); + assert(cr); + return cr; +} + +void GtkSalFrame::damaged (const basegfx::B2IBox& rDamageRect) +{ +#if OSL_DEBUG_LEVEL > 1 + long long area = rDamageRect.getWidth() * rDamageRect.getHeight(); + if( area > 32 * 1024 ) + { + fprintf( stderr, "bitmap damaged %d %d (%dx%d) area %lld widget\n", + (int) rDamageRect.getMinX(), + (int) rDamageRect.getMinY(), + (int) rDamageRect.getWidth(), + (int) rDamageRect.getHeight(), + area ); + } +#endif + + if (dumpframes) + { + static int frame; + OString tmp("/tmp/frame" + OString::number(frame++) + ".png"); + cairo_t* cr = getCairoContext(); + cairo_surface_write_to_png(cairo_get_target(cr), tmp.getStr()); + cairo_destroy(cr); + } + + gtk_widget_queue_draw_area(GTK_WIDGET(m_pFixedContainer), + rDamageRect.getMinX(), + rDamageRect.getMinY(), + rDamageRect.getWidth(), + rDamageRect.getHeight()); +} + +// blit our backing basebmp buffer to the target cairo context cr +gboolean GtkSalFrame::signalDraw( GtkWidget*, cairo_t *cr, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + cairo_save(cr); + + cairo_t* source = pThis->getCairoContext(); + cairo_surface_t *pSurface = cairo_get_target(source); + + cairo_set_operator( cr, CAIRO_OPERATOR_OVER ); + cairo_set_source_surface(cr, pSurface, 0, 0); + cairo_paint(cr); + + cairo_destroy(source); + + cairo_restore(cr); + + cairo_surface_flush(cairo_get_target(cr)); + + return false; +} + +void GtkSalFrame::sizeAllocated(GtkWidget*, GdkRectangle *pAllocation, gpointer frame) +{ + GtkSalFrame* pThis = static_cast(frame); + + bool bSized = false; + + if( pThis->m_bFullscreen || (pThis->m_nStyle & (SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_PLUG)) == SAL_FRAME_STYLE_SIZEABLE ) + { + if( pAllocation->width != (int)pThis->maGeometry.nWidth || pAllocation->height != (int)pThis->maGeometry.nHeight ) + { + bSized = true; + pThis->maGeometry.nWidth = pAllocation->width; + pThis->maGeometry.nHeight = pAllocation->height; + } + } + + if( bSized ) + { + pThis->AllocateFrame(); + pThis->CallCallback( SALEVENT_RESIZE, nullptr ); + pThis->TriggerPaintEvent(); + } +} + +gboolean GtkSalFrame::signalConfigure(GtkWidget*, GdkEventConfigure* pEvent, gpointer frame) +{ + GtkSalFrame* pThis = static_cast(frame); + pThis->m_bPaintsBlocked = false; + + bool bMoved = false; + int x = pEvent->x, y = pEvent->y; + + /* HACK: during sizing/moving a toolbar pThis->maGeometry is actually + * already exact; even worse: due to the asynchronicity of configure + * events the borderwindow which would evaluate this event + * would size/move based on wrong data if we would actually evaluate + * this event. So let's swallow it. + */ + if( (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) && + GtkSalFrame::getDisplay()->GetCaptureFrame() == pThis ) + return false; + + /* #i31785# claims we cannot trust the x,y members of the event; + * they are e.g. not set correctly on maximize/demaximize; + * yet the gdkdisplay-x11.c code handling configure_events has + * done this XTranslateCoordinates work since the day ~zero. + */ + if( x != pThis->maGeometry.nX || y != pThis->maGeometry.nY ) + { + bMoved = true; + pThis->maGeometry.nX = x; + pThis->maGeometry.nY = y; + } + + // update decoration hints + if( ! (pThis->m_nStyle & SAL_FRAME_STYLE_PLUG) ) + { + GdkRectangle aRect; + gdk_window_get_frame_extents( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &aRect ); + pThis->maGeometry.nTopDecoration = y - aRect.y; + pThis->maGeometry.nBottomDecoration = aRect.y + aRect.height - y - pEvent->height; + pThis->maGeometry.nLeftDecoration = x - aRect.x; + pThis->maGeometry.nRightDecoration = aRect.x + aRect.width - x - pEvent->width; + } + else + { + pThis->maGeometry.nTopDecoration = + pThis->maGeometry.nBottomDecoration = + pThis->maGeometry.nLeftDecoration = + pThis->maGeometry.nRightDecoration = 0; + } + + pThis->updateScreenNumber(); + + if (bMoved) + pThis->CallCallback(SALEVENT_MOVE, nullptr); + + return false; +} +#else +gboolean GtkSalFrame::signalExpose( GtkWidget*, GdkEventExpose* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + pThis->m_bPaintsBlocked = false; + + struct SalPaintEvent aEvent( pEvent->area.x, pEvent->area.y, pEvent->area.width, pEvent->area.height ); + + pThis->CallCallback( SALEVENT_PAINT, &aEvent ); + + return false; +} + +gboolean GtkSalFrame::signalConfigure( GtkWidget*, GdkEventConfigure* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + pThis->m_bPaintsBlocked = false; + + bool bMoved = false, bSized = false; + int x = pEvent->x, y = pEvent->y; + + /* HACK: during sizing/moving a toolbar pThis->maGeometry is actually + * already exact; even worse: due to the asynchronicity of configure + * events the borderwindow which would evaluate this event + * would size/move based on wrong data if we would actually evaluate + * this event. So let's swallow it. + */ + if( (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) && + GtkSalFrame::getDisplay()->GetCaptureFrame() == pThis ) + return false; + + /* #i31785# claims we cannot trust the x,y members of the event; + * they are e.g. not set correctly on maximize/demaximize; + * yet the gdkdisplay-x11.c code handling configure_events has + * done this XTranslateCoordinates work since the day ~zero. + */ + if( x != pThis->maGeometry.nX || y != pThis->maGeometry.nY ) + { + bMoved = true; + pThis->maGeometry.nX = x; + pThis->maGeometry.nY = y; + } + /* #i86302# + * for non sizeable windows we set the min and max hint for the window manager to + * achieve correct sizing. However this is asynchronous and e.g. on Compiz + * it sometimes happens that the window gets resized to another size (some default) + * if we update the size here, subsequent setMinMaxSize will use this wrong size + * - which is not good since the window manager will now size the window back to this + * wrong size at some point. + */ + if( pThis->m_bFullscreen || (pThis->m_nStyle & (SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_PLUG)) == SAL_FRAME_STYLE_SIZEABLE ) + { + if( pEvent->width != (int)pThis->maGeometry.nWidth || pEvent->height != (int)pThis->maGeometry.nHeight ) + { + bSized = true; + pThis->maGeometry.nWidth = pEvent->width; + pThis->maGeometry.nHeight = pEvent->height; + } + } + + // update decoration hints + if( ! (pThis->m_nStyle & SAL_FRAME_STYLE_PLUG) ) + { + GdkRectangle aRect; + gdk_window_get_frame_extents( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &aRect ); + pThis->maGeometry.nTopDecoration = y - aRect.y; + pThis->maGeometry.nBottomDecoration = aRect.y + aRect.height - y - pEvent->height; + pThis->maGeometry.nLeftDecoration = x - aRect.x; + pThis->maGeometry.nRightDecoration = aRect.x + aRect.width - x - pEvent->width; + } + else + { + pThis->maGeometry.nTopDecoration = + pThis->maGeometry.nBottomDecoration = + pThis->maGeometry.nLeftDecoration = + pThis->maGeometry.nRightDecoration = 0; + } + + pThis->updateScreenNumber(); + if( bSized ) + pThis->AllocateFrame(); + + if( bMoved && bSized ) + pThis->CallCallback( SALEVENT_MOVERESIZE, nullptr ); + else if( bMoved ) + pThis->CallCallback( SALEVENT_MOVE, nullptr ); + else if( bSized ) + pThis->CallCallback( SALEVENT_RESIZE, nullptr ); + + if (bSized) + pThis->TriggerPaintEvent(); + return false; +} + +#endif // GTK_CHECK_VERSION(3,0,0) + +void GtkSalFrame::TriggerPaintEvent() +{ + //Under gtk2 we can basically paint directly into the XWindow and on + //additional "expose-event" events we can re-render the missing pieces + // + //Under gtk3 we have to keep our own buffer up to date and flush it into + //the given cairo context on "draw". So we emit a paint event on + //opportune resize trigger events to initially fill our backbuffer and then + //keep it up to date with our direct paints and tell gtk those regions + //have changed and then blit them into the provided cairo context when + //we get the "draw" + // + //The other alternative was to always paint everything on "draw", but + //that duplicates the amount of drawing and is hideously slow +#if GTK_CHECK_VERSION(3,0,0) + SAL_INFO("vcl.gtk3", "force painting" << 0 << "," << 0 << " " << maGeometry.nWidth << "x" << maGeometry.nHeight); + SalPaintEvent aPaintEvt(0, 0, maGeometry.nWidth, maGeometry.nHeight, true); + CallCallback(SALEVENT_PAINT, &aPaintEvt); + gtk_widget_queue_draw(GTK_WIDGET(m_pFixedContainer)); +#endif +} + +gboolean GtkSalFrame::signalFocus( GtkWidget*, GdkEventFocus* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + SalGenericInstance *pSalInstance = + static_cast< SalGenericInstance* >(GetSalData()->m_pInstance); + + // check if printers have changed (analogous to salframe focus handler) + pSalInstance->updatePrinterUpdate(); + + if( !pEvent->in ) + { + pThis->m_nKeyModifiers = 0; + pThis->m_bSendModChangeOnRelease = false; + } + + if( pThis->m_pIMHandler ) + pThis->m_pIMHandler->focusChanged( pEvent->in ); + + // ask for changed printers like generic implementation + if( pEvent->in && pSalInstance->isPrinterInit() ) + pSalInstance->updatePrinterUpdate(); + + // FIXME: find out who the hell steals the focus from our frame + // while we have the pointer grabbed, this should not come from + // the window manager. Is this an event that was still queued ? + // The focus does not seem to get set inside our process + + // in the meantime do not propagate focus get/lose if floats are open + if( m_nFloats == 0 ) + pThis->CallCallback( pEvent->in ? SALEVENT_GETFOCUS : SALEVENT_LOSEFOCUS, NULL ); + + return false; +} + +#if !GTK_CHECK_VERSION(3,8,0) +static OString getDisplayString() +{ + int nParams = rtl_getAppCommandArgCount(); + OUString aParam; + for( int i = 0; i < nParams; i++ ) + { + rtl_getAppCommandArg( i, &aParam.pData ); + if( i < nParams-1 && (aParam == "-display" || aParam == "--display" ) ) + { + rtl_getAppCommandArg( i+1, &aParam.pData ); + return OUStringToOString( aParam, osl_getThreadTextEncoding() ); + } + } + return OString(); +} +#endif + +gboolean GtkSalFrame::signalMap( GtkWidget *pWidget, GdkEvent*, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + +#if !GTK_CHECK_VERSION(3,8,0) + //Spawn off a helper program that will attempt to set this fullscreen + //window to span all displays. + if (pThis->m_bFullscreen && pThis->m_bSpanMonitorsWhenFullscreen) + { + GdkWindow* gdkwin = widget_get_window(pThis->m_pWindow); + if (gdkwin) + { + OUString sProgramURL( "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/xid-fullscreen-on-all-monitors"); + rtl::Bootstrap::expandMacros(sProgramURL); + OUString sProgram; + if (osl::FileBase::getSystemPathFromFileURL(sProgramURL, sProgram) == osl::File::E_None) + { + OString sFinalProgram(OUStringToOString(sProgram, osl_getThreadTextEncoding()) + + " " + OString::number((int)GDK_WINDOW_XID(gdkwin))); + OString sDisplay(getDisplayString()); + if (!sDisplay.isEmpty()) + { + sFinalProgram += "--display " + sDisplay; + } + int returnValue = system(sFinalProgram.getStr()); + (void)returnValue; + } + } + } +#endif + + bool bSetFocus = pThis->m_bSetFocusOnMap; + pThis->m_bSetFocusOnMap = false; + +#if !GTK_CHECK_VERSION(3,0,0) + if( bSetFocus ) + { + GetGenericData()->ErrorTrapPush(); + XSetInputFocus( GtkSalFrame::getDisplay()->GetDisplay(), + widget_get_xid(pWidget), + RevertToParent, CurrentTime ); + XSync( GtkSalFrame::getDisplay()->GetDisplay(), False ); + GetGenericData()->ErrorTrapPop(); + } +#else + (void)pWidget; (void)bSetFocus; + //FIXME: no set input focus ... +#endif + + pThis->CallCallback( SALEVENT_RESIZE, NULL ); + pThis->TriggerPaintEvent(); + + return false; +} + +gboolean GtkSalFrame::signalUnmap( GtkWidget*, GdkEvent*, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + pThis->CallCallback( SALEVENT_RESIZE, NULL ); + + return false; +} + +gboolean GtkSalFrame::signalKey( GtkWidget*, GdkEventKey* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + vcl::DeletionListener aDel( pThis ); + + if( pThis->m_pIMHandler ) + { + if( pThis->m_pIMHandler->handleKeyEvent( pEvent ) ) + return true; + } + + // handle modifiers + if( pEvent->keyval == GDK_Shift_L || pEvent->keyval == GDK_Shift_R || + pEvent->keyval == GDK_Control_L || pEvent->keyval == GDK_Control_R || + pEvent->keyval == GDK_Alt_L || pEvent->keyval == GDK_Alt_R || + pEvent->keyval == GDK_Meta_L || pEvent->keyval == GDK_Meta_R || + pEvent->keyval == GDK_Super_L || pEvent->keyval == GDK_Super_R ) + { + SalKeyModEvent aModEvt; + + sal_uInt16 nModCode = GetKeyModCode( pEvent->state ); + + aModEvt.mnModKeyCode = 0; // emit no MODKEYCHANGE events + if( pEvent->type == GDK_KEY_PRESS && !pThis->m_nKeyModifiers ) + pThis->m_bSendModChangeOnRelease = true; + + else if( pEvent->type == GDK_KEY_RELEASE && + pThis->m_bSendModChangeOnRelease ) + { + aModEvt.mnModKeyCode = pThis->m_nKeyModifiers; + pThis->m_nKeyModifiers = 0; + } + + sal_uInt16 nExtModMask = 0; + sal_uInt16 nModMask = 0; + // pressing just the ctrl key leads to a keysym of XK_Control but + // the event state does not contain ControlMask. In the release + // event its the other way round: it does contain the Control mask. + // The modifier mode therefore has to be adapted manually. + switch( pEvent->keyval ) + { + case GDK_Control_L: + nExtModMask = MODKEY_LMOD1; + nModMask = KEY_MOD1; + break; + case GDK_Control_R: + nExtModMask = MODKEY_RMOD1; + nModMask = KEY_MOD1; + break; + case GDK_Alt_L: + nExtModMask = MODKEY_LMOD2; + nModMask = KEY_MOD2; + break; + case GDK_Alt_R: + nExtModMask = MODKEY_RMOD2; + nModMask = KEY_MOD2; + break; + case GDK_Shift_L: + nExtModMask = MODKEY_LSHIFT; + nModMask = KEY_SHIFT; + break; + case GDK_Shift_R: + nExtModMask = MODKEY_RSHIFT; + nModMask = KEY_SHIFT; + break; + // Map Meta/Super to MOD3 modifier on all Unix systems + // except Mac OS X + case GDK_Meta_L: + case GDK_Super_L: + nExtModMask = MODKEY_LMOD3; + nModMask = KEY_MOD3; + break; + case GDK_Meta_R: + case GDK_Super_R: + nExtModMask = MODKEY_RMOD3; + nModMask = KEY_MOD3; + break; + } + if( pEvent->type == GDK_KEY_RELEASE ) + { + nModCode &= ~nModMask; + pThis->m_nKeyModifiers &= ~nExtModMask; + } + else + { + nModCode |= nModMask; + pThis->m_nKeyModifiers |= nExtModMask; + } + + aModEvt.mnCode = nModCode; + aModEvt.mnTime = pEvent->time; + + pThis->CallCallback( SALEVENT_KEYMODCHANGE, &aModEvt ); + + } + else + { + pThis->doKeyCallback( pEvent->state, + pEvent->keyval, + pEvent->hardware_keycode, + pEvent->group, + pEvent->time, + sal_Unicode(gdk_keyval_to_unicode( pEvent->keyval )), + (pEvent->type == GDK_KEY_PRESS), + false ); + if( ! aDel.isDeleted() ) + pThis->m_bSendModChangeOnRelease = false; + } + + if( !aDel.isDeleted() && pThis->m_pIMHandler ) + pThis->m_pIMHandler->updateIMSpotLocation(); + + return true; +} + +gboolean GtkSalFrame::signalDelete( GtkWidget*, GdkEvent*, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + +#if GTK_CHECK_VERSION(3,0,0) + //If we went into the backdrop we disabled the toplevel window, if we + //receive a delete here, re-enable so we can process it + bool bBackDrop = (gtk_widget_get_state_flags(GTK_WIDGET(pThis->m_pWindow)) & GTK_STATE_FLAG_BACKDROP); + if (bBackDrop) + pThis->GetWindow()->Enable(); +#endif + + pThis->CallCallback( SALEVENT_CLOSE, NULL ); + + return true; +} + +void GtkSalFrame::signalStyleSet( GtkWidget*, GtkStyle* pPrevious, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + // every frame gets an initial style set on creation + // do not post these as the whole application tends to + // redraw itself to adjust to the new style + // where there IS no new style resulting in tremendous unnecessary flickering + if( pPrevious != NULL ) + { + // signalStyleSet does NOT usually have the gdk lock + // so post user event to safely dispatch the SALEVENT_SETTINGSCHANGED + // note: settings changed for multiple frames is avoided in winproc.cxx ImplHandleSettings + GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_SETTINGSCHANGED ); + GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_FONTCHANGED ); + } + +#if !GTK_CHECK_VERSION(3,0,0) + /* #i64117# gtk sets a nice background pixmap + * but we actually don't really want that, so save + * some time on the Xserver as well as prevent + * some paint issues + */ + GdkWindow* pWin = widget_get_window(GTK_WIDGET(pThis->getWindow())); + if( pWin ) + { + ::Window aWin = GDK_WINDOW_XWINDOW(pWin); + if( aWin != None ) + XSetWindowBackgroundPixmap( GtkSalFrame::getDisplay()->GetDisplay(), + aWin, + pThis->m_hBackgroundPixmap ); + } + if( ! pThis->m_pParent ) + { + // signalize theme changed for NWF caches + // FIXME: should be called only once for a style change + GtkSalGraphics::bThemeChanged = true; + } +#endif +} + +gboolean GtkSalFrame::signalWindowState( GtkWidget*, GdkEvent* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + if( (pThis->m_nState & GDK_WINDOW_STATE_ICONIFIED) != (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_ICONIFIED ) ) + { + GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_RESIZE ); + pThis->TriggerPaintEvent(); + } + + if( (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_MAXIMIZED) && + ! (pThis->m_nState & GDK_WINDOW_STATE_MAXIMIZED) ) + { + pThis->m_aRestorePosSize = + Rectangle( Point( pThis->maGeometry.nX, pThis->maGeometry.nY ), + Size( pThis->maGeometry.nWidth, pThis->maGeometry.nHeight ) ); + } + pThis->m_nState = pEvent->window_state.new_window_state; + + #if OSL_DEBUG_LEVEL > 1 + if( (pEvent->window_state.changed_mask & GDK_WINDOW_STATE_FULLSCREEN) ) + { + fprintf( stderr, "window %p %s full screen state\n", + pThis, + (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_FULLSCREEN) ? "enters" : "leaves"); + } + #endif + + return false; +} + +gboolean GtkSalFrame::signalVisibility( GtkWidget*, GdkEventVisibility* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + pThis->m_nVisibility = pEvent->state; + return true; +} + +void GtkSalFrame::signalDestroy( GtkWidget* pObj, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + if( pObj == pThis->m_pWindow ) + { + pThis->m_pFixedContainer = NULL; + pThis->m_pEventBox = NULL; + pThis->m_pWindow = NULL; + pThis->InvalidateGraphics(); + } +} + +// GtkSalFrame::IMHandler + +GtkSalFrame::IMHandler::IMHandler( GtkSalFrame* pFrame ) +: m_pFrame(pFrame), + m_nPrevKeyPresses( 0 ), + m_pIMContext( NULL ), + m_bFocused( true ), + m_bPreeditJustChanged( false ) +{ + m_aInputEvent.mpTextAttr = NULL; + createIMContext(); +} + +GtkSalFrame::IMHandler::~IMHandler() +{ + // cancel an eventual event posted to begin preedit again + GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + deleteIMContext(); +} + +void GtkSalFrame::IMHandler::createIMContext() +{ + if( ! m_pIMContext ) + { + m_pIMContext = gtk_im_multicontext_new (); + 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 ); + g_signal_connect( m_pIMContext, "preedit_start", + G_CALLBACK (signalIMPreeditStart), this ); + g_signal_connect( m_pIMContext, "preedit_end", + G_CALLBACK (signalIMPreeditEnd), this ); + + GetGenericData()->ErrorTrapPush(); + gtk_im_context_set_client_window( m_pIMContext, widget_get_window(GTK_WIDGET(m_pFrame->m_pWindow)) ); + gtk_im_context_focus_in( m_pIMContext ); + GetGenericData()->ErrorTrapPop(); + m_bFocused = true; + } +} + +void GtkSalFrame::IMHandler::deleteIMContext() +{ + if( m_pIMContext ) + { + // first give IC a chance to deinitialize + GetGenericData()->ErrorTrapPush(); + gtk_im_context_set_client_window( m_pIMContext, NULL ); + GetGenericData()->ErrorTrapPop(); + // destroy old IC + g_object_unref( m_pIMContext ); + m_pIMContext = NULL; + } +} + +void GtkSalFrame::IMHandler::doCallEndExtTextInput() +{ + m_aInputEvent.mpTextAttr = NULL; + m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); +} + +void GtkSalFrame::IMHandler::updateIMSpotLocation() +{ + SalExtTextInputPosEvent aPosEvent; + m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent ); + GdkRectangle aArea; + aArea.x = aPosEvent.mnX; + aArea.y = aPosEvent.mnY; + aArea.width = aPosEvent.mnWidth; + aArea.height = aPosEvent.mnHeight; + GetGenericData()->ErrorTrapPush(); + gtk_im_context_set_cursor_location( m_pIMContext, &aArea ); + GetGenericData()->ErrorTrapPop(); +} + +void GtkSalFrame::IMHandler::sendEmptyCommit() +{ + vcl::DeletionListener aDel( m_pFrame ); + + SalExtTextInputEvent aEmptyEv; + aEmptyEv.mnTime = 0; + aEmptyEv.mpTextAttr = 0; + aEmptyEv.maText.clear(); + aEmptyEv.mnCursorPos = 0; + aEmptyEv.mnCursorFlags = 0; + aEmptyEv.mbOnlyCursor = False; + m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&aEmptyEv ); + if( ! aDel.isDeleted() ) + m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); +} + +void GtkSalFrame::IMHandler::endExtTextInput( sal_uInt16 /*nFlags*/ ) +{ + gtk_im_context_reset ( m_pIMContext ); + + if( m_aInputEvent.mpTextAttr ) + { + vcl::DeletionListener aDel( m_pFrame ); + // delete preedit in sal (commit an empty string) + sendEmptyCommit(); + if( ! aDel.isDeleted() ) + { + // mark previous preedit state again (will e.g. be sent at focus gain) + m_aInputEvent.mpTextAttr = &m_aInputFlags[0]; + if( m_bFocused ) + { + // begin preedit again + GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + } + } + } +} + +void GtkSalFrame::IMHandler::focusChanged( bool bFocusIn ) +{ + m_bFocused = bFocusIn; + if( bFocusIn ) + { + GetGenericData()->ErrorTrapPush(); + gtk_im_context_focus_in( m_pIMContext ); + GetGenericData()->ErrorTrapPop(); + if( m_aInputEvent.mpTextAttr ) + { + sendEmptyCommit(); + // begin preedit again + GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + } + } + else + { + GetGenericData()->ErrorTrapPush(); + gtk_im_context_focus_out( m_pIMContext ); + GetGenericData()->ErrorTrapPop(); + // cancel an eventual event posted to begin preedit again + GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + } +} + +bool GtkSalFrame::IMHandler::handleKeyEvent( GdkEventKey* pEvent ) +{ + vcl::DeletionListener aDel( m_pFrame ); + + if( pEvent->type == GDK_KEY_PRESS ) + { + // Add this key press event to the list of previous key presses + // to which we compare key release events. If a later key release + // event has a matching key press event in this list, we swallow + // the key release because some GTK Input Methods don't swallow it + // for us. + m_aPrevKeyPresses.push_back( PreviousKeyPress(pEvent) ); + m_nPrevKeyPresses++; + + // Also pop off the earliest key press event if there are more than 10 + // already. + while (m_nPrevKeyPresses > 10) + { + m_aPrevKeyPresses.pop_front(); + m_nPrevKeyPresses--; + } + + GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); + + // #i51353# update spot location on every key input since we cannot + // know which key may activate a preedit choice window + updateIMSpotLocation(); + if( aDel.isDeleted() ) + return true; + + gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); + g_object_unref( pRef ); + + if( aDel.isDeleted() ) + return true; + + m_bPreeditJustChanged = false; + + if( bResult ) + return true; + else + { + DBG_ASSERT( m_nPrevKeyPresses > 0, "key press has vanished !" ); + if( ! m_aPrevKeyPresses.empty() ) // sanity check + { + // event was not swallowed, do not filter a following + // key release event + // note: this relies on gtk_im_context_filter_keypress + // returning without calling a handler (in the "not swallowed" + // case ) which might change the previous key press list so + // we would pop the wrong event here + m_aPrevKeyPresses.pop_back(); + m_nPrevKeyPresses--; + } + } + } + + // Determine if we got an earlier key press event corresponding to this key release + if (pEvent->type == GDK_KEY_RELEASE) + { + GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); + gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); + g_object_unref( pRef ); + + if( aDel.isDeleted() ) + return true; + + m_bPreeditJustChanged = false; + + std::list::iterator iter = m_aPrevKeyPresses.begin(); + std::list::iterator iter_end = m_aPrevKeyPresses.end(); + while (iter != iter_end) + { + // If we found a corresponding previous key press event, swallow the release + // and remove the earlier key press from our list + if (*iter == pEvent) + { + m_aPrevKeyPresses.erase(iter); + m_nPrevKeyPresses--; + return true; + } + ++iter; + } + + if( bResult ) + return true; + } + + return false; +} + +/* FIXME: +* #122282# still more hacking: some IMEs never start a preedit but simply commit +* in this case we cannot commit a single character. Workaround: do not do the +* single key hack for enter or space if the unicode committed does not match +*/ + +static bool checkSingleKeyCommitHack( guint keyval, sal_Unicode cCode ) +{ + bool bRet = true; + switch( keyval ) + { + case GDK_KP_Enter: + case GDK_Return: + if( cCode != '\n' && cCode != '\r' ) + bRet = false; + break; + case GDK_space: + case GDK_KP_Space: + if( cCode != ' ' ) + bRet = false; + break; + default: + break; + } + return bRet; +} + +void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext* pContext, gchar* pText, gpointer im_handler ) +{ + GtkSalFrame::IMHandler* pThis = static_cast(im_handler); + + SolarMutexGuard aGuard; + vcl::DeletionListener aDel( pThis->m_pFrame ); + { + const bool bWasPreedit = + (pThis->m_aInputEvent.mpTextAttr != 0) || + pThis->m_bPreeditJustChanged; + + pThis->m_aInputEvent.mnTime = 0; + pThis->m_aInputEvent.mpTextAttr = 0; + pThis->m_aInputEvent.maText = OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ); + pThis->m_aInputEvent.mnCursorPos = pThis->m_aInputEvent.maText.getLength(); + pThis->m_aInputEvent.mnCursorFlags = 0; + pThis->m_aInputEvent.mbOnlyCursor = False; + + pThis->m_aInputFlags.clear(); + + /* necessary HACK: all keyboard input comes in here as soon as a IMContext is set + * which is logical and consequent. But since even simple input like + * comes through the commit signal instead of signalKey + * and all kinds of windows only implement KeyInput (e.g. PushButtons, + * RadioButtons and a lot of other Controls), will send a single + * KeyInput/KeyUp sequence instead of an ExtText event if there + * never was a preedit and the text is only one character. + * + * In this case there the last ExtText event must have been + * SALEVENT_ENDEXTTEXTINPUT, either because of a regular commit + * or because there never was a preedit. + */ + bool bSingleCommit = false; + if( ! bWasPreedit + && pThis->m_aInputEvent.maText.getLength() == 1 + && ! pThis->m_aPrevKeyPresses.empty() + ) + { + const PreviousKeyPress& rKP = pThis->m_aPrevKeyPresses.back(); + sal_Unicode aOrigCode = pThis->m_aInputEvent.maText[0]; + + if( checkSingleKeyCommitHack( rKP.keyval, aOrigCode ) ) + { + pThis->m_pFrame->doKeyCallback( rKP.state, rKP.keyval, rKP.hardware_keycode, rKP.group, rKP.time, aOrigCode, true, true ); + bSingleCommit = true; + } + } + if( ! bSingleCommit ) + { + pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent); + if( ! aDel.isDeleted() ) + pThis->doCallEndExtTextInput(); + } + if( ! aDel.isDeleted() ) + { + // reset input event + pThis->m_aInputEvent.maText.clear(); + pThis->m_aInputEvent.mnCursorPos = 0; + pThis->updateIMSpotLocation(); + } + } +#ifdef SOLARIS + // #i51356# workaround a solaris IIIMP bug + // in case of partial commits the preedit changed signal + // and commit signal come in wrong order + if( ! aDel.isDeleted() ) + signalIMPreeditChanged( pContext, im_handler ); +#else + (void) pContext; +#endif +} + +void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext*, gpointer im_handler ) +{ + GtkSalFrame::IMHandler* pThis = static_cast(im_handler); + + char* pText = NULL; + PangoAttrList* pAttrs = NULL; + gint nCursorPos = 0; + + gtk_im_context_get_preedit_string( pThis->m_pIMContext, + &pText, + &pAttrs, + &nCursorPos ); + if( pText && ! *pText ) // empty string + { + // change from nothing to nothing -> do not start preedit + // e.g. this will activate input into a calc cell without + // user input + if( pThis->m_aInputEvent.maText.getLength() == 0 ) + { + g_free( pText ); + pango_attr_list_unref( pAttrs ); + return; + } + } + + pThis->m_bPreeditJustChanged = true; + + bool bEndPreedit = (!pText || !*pText) && pThis->m_aInputEvent.mpTextAttr != NULL; + pThis->m_aInputEvent.mnTime = 0; + pThis->m_aInputEvent.maText = pText ? OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ) : OUString(); + pThis->m_aInputEvent.mnCursorPos = nCursorPos; + pThis->m_aInputEvent.mnCursorFlags = 0; + pThis->m_aInputEvent.mbOnlyCursor = False; + + pThis->m_aInputFlags = std::vector( std::max( 1, (int)pThis->m_aInputEvent.maText.getLength() ), 0 ); + + PangoAttrIterator *iter = pango_attr_list_get_iterator(pAttrs); + do + { + GSList *attr_list = NULL; + GSList *tmp_list = NULL; + gint start, end; + guint sal_attr = 0; + + pango_attr_iterator_range (iter, &start, &end); + if (end == G_MAXINT) + end = pText ? strlen (pText) : 0; + if (end == start) + continue; + + start = g_utf8_pointer_to_offset (pText, pText + start); + end = g_utf8_pointer_to_offset (pText, pText + end); + + tmp_list = attr_list = pango_attr_iterator_get_attrs (iter); + while (tmp_list) + { + PangoAttribute *pango_attr = static_cast(tmp_list->data); + + switch (pango_attr->klass->type) + { + case PANGO_ATTR_BACKGROUND: + sal_attr |= (EXTTEXTINPUT_ATTR_HIGHLIGHT | EXTTEXTINPUT_CURSOR_INVISIBLE); + break; + case PANGO_ATTR_UNDERLINE: + sal_attr |= EXTTEXTINPUT_ATTR_UNDERLINE; + break; + case PANGO_ATTR_STRIKETHROUGH: + sal_attr |= EXTTEXTINPUT_ATTR_REDTEXT; + break; + default: + break; + } + pango_attribute_destroy (pango_attr); + tmp_list = tmp_list->next; + } + if (sal_attr == 0) + sal_attr |= EXTTEXTINPUT_ATTR_UNDERLINE; + g_slist_free (attr_list); + + // Set the sal attributes on our text + for (int i = start; i < end; ++i) + { + SAL_WARN_IF(i >= static_cast(pThis->m_aInputFlags.size()), + "vcl.gtk", "pango attrib out of range. Broken range: " + << start << "," << end << " Legal range: 0," + << pThis->m_aInputFlags.size()); + if (i >= static_cast(pThis->m_aInputFlags.size())) + continue; + pThis->m_aInputFlags[i] |= sal_attr; + } + } while (pango_attr_iterator_next (iter)); + pango_attr_iterator_destroy(iter); + + pThis->m_aInputEvent.mpTextAttr = &pThis->m_aInputFlags[0]; + + g_free( pText ); + pango_attr_list_unref( pAttrs ); + + SolarMutexGuard aGuard; + vcl::DeletionListener aDel( pThis->m_pFrame ); + + pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent); + if( bEndPreedit && ! aDel.isDeleted() ) + pThis->doCallEndExtTextInput(); + if( ! aDel.isDeleted() ) + pThis->updateIMSpotLocation(); +} + +void GtkSalFrame::IMHandler::signalIMPreeditStart( GtkIMContext*, gpointer /*im_handler*/ ) +{ +} + +void GtkSalFrame::IMHandler::signalIMPreeditEnd( GtkIMContext*, gpointer im_handler ) +{ + GtkSalFrame::IMHandler* pThis = static_cast(im_handler); + + pThis->m_bPreeditJustChanged = true; + + SolarMutexGuard aGuard; + vcl::DeletionListener aDel( pThis->m_pFrame ); + pThis->doCallEndExtTextInput(); + if( ! aDel.isDeleted() ) + pThis->updateIMSpotLocation(); +} + +uno::Reference + FindFocus(uno::Reference< accessibility::XAccessibleContext > xContext) +{ + if (!xContext.is()) + uno::Reference< accessibility::XAccessibleEditableText >(); + + uno::Reference xState = xContext->getAccessibleStateSet(); + if (xState.is()) + { + if (xState->contains(accessibility::AccessibleStateType::FOCUSED)) + return uno::Reference(xContext, uno::UNO_QUERY); + } + + for (sal_Int32 i = 0; i < xContext->getAccessibleChildCount(); ++i) + { + uno::Reference< accessibility::XAccessible > xChild = xContext->getAccessibleChild(i); + if (!xChild.is()) + continue; + uno::Reference< accessibility::XAccessibleContext > xChildContext = xChild->getAccessibleContext(); + if (!xChildContext.is()) + continue; + uno::Reference< accessibility::XAccessibleEditableText > xText = FindFocus(xChildContext); + if (xText.is()) + return xText; + } + return uno::Reference< accessibility::XAccessibleEditableText >(); +} + +static uno::Reference lcl_GetxText(vcl::Window *pFocusWin) +{ + uno::Reference xText; + try + { + uno::Reference< accessibility::XAccessible > xAccessible( pFocusWin->GetAccessible( true ) ); + if (xAccessible.is()) + xText = FindFocus(xAccessible->getAccessibleContext()); + } + catch(const uno::Exception& e) + { + SAL_WARN( "vcl.gtk", "Exception in getting input method surrounding text: " << e.Message); + } + return xText; +} + +gboolean GtkSalFrame::IMHandler::signalIMRetrieveSurrounding( GtkIMContext* pContext, gpointer /*im_handler*/ ) +{ + vcl::Window *pFocusWin = Application::GetFocusWindow(); + if (!pFocusWin) + return true; + + uno::Reference xText = lcl_GetxText(pFocusWin); + if (xText.is()) + { + sal_Int32 nPosition = xText->getCaretPosition(); + OUString sAllText = xText->getText(); + OString sUTF = OUStringToOString(sAllText, RTL_TEXTENCODING_UTF8); + OUString sCursorText(sAllText.copy(0, nPosition)); + gtk_im_context_set_surrounding(pContext, sUTF.getStr(), sUTF.getLength(), + OUStringToOString(sCursorText, RTL_TEXTENCODING_UTF8).getLength()); + return true; + } + + return false; +} + +gboolean GtkSalFrame::IMHandler::signalIMDeleteSurrounding( GtkIMContext*, gint offset, gint nchars, + gpointer /*im_handler*/ ) +{ + vcl::Window *pFocusWin = Application::GetFocusWindow(); + if (!pFocusWin) + return true; + + uno::Reference xText = lcl_GetxText(pFocusWin); + if (xText.is()) + { + sal_Int32 nPosition = xText->getCaretPosition(); + // #i111768# range checking + sal_Int32 nDeletePos = nPosition + offset; + sal_Int32 nDeleteEnd = nDeletePos + nchars; + if (nDeletePos < 0) + nDeletePos = 0; + if (nDeleteEnd < 0) + nDeleteEnd = 0; + if (nDeleteEnd > xText->getCharacterCount()) + nDeleteEnd = xText->getCharacterCount(); + + xText->deleteText(nDeletePos, nDeleteEnd); + //tdf91641 adjust cursor if deleted chars shift it forward (normal case) + if (nDeletePos < nPosition) + { + if (nDeleteEnd <= nPosition) + nPosition = nPosition - (nDeleteEnd - nDeletePos); + else + nPosition = nDeletePos; + + if (xText->getCharacterCount() >= nPosition) + xText->setCaretPosition( nPosition ); + } + return true; + } + + return false; +} + +Size GtkSalDisplay::GetScreenSize( int nDisplayScreen ) +{ + Rectangle aRect = m_pSys->GetDisplayScreenPosSizePixel( nDisplayScreen ); + return Size( aRect.GetWidth(), aRect.GetHeight() ); +} + +Window GtkSalFrame::GetX11Window() +{ + return widget_get_xid(m_pWindow); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk/window/gtksalframe.cxx b/vcl/unx/gtk/window/gtksalframe.cxx deleted file mode 100644 index 9e03f14..0000000 --- a/vcl/unx/gtk/window/gtksalframe.cxx +++ /dev/null @@ -1,4883 +0,0 @@ -/* -*- 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !GTK_CHECK_VERSION(3,0,0) -# include -#endif -#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) -# include -#endif -#if defined ENABLE_GMENU_INTEGRATION // defined in gtksalmenu.hxx above -# include -#endif - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#if OSL_DEBUG_LEVEL > 1 -# include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if GTK_CHECK_VERSION(3,0,0) -# include -#endif - -#ifdef ENABLE_DBUS -#include - -#define GSM_DBUS_SERVICE "org.gnome.SessionManager" -#define GSM_DBUS_PATH "/org/gnome/SessionManager" -#define GSM_DBUS_INTERFACE "org.gnome.SessionManager" -#endif - -#include - -#if GTK_CHECK_VERSION(3,0,0) -#define IS_WIDGET_REALIZED gtk_widget_get_realized -#define IS_WIDGET_MAPPED gtk_widget_get_mapped -#else -#define IS_WIDGET_REALIZED GTK_WIDGET_REALIZED -#define IS_WIDGET_MAPPED GTK_WIDGET_MAPPED -#endif - -#if !GTK_CHECK_VERSION(3,0,0) -#define GDK_IS_X11_DISPLAY(foo) (true) -#endif - -using namespace com::sun::star; - -int GtkSalFrame::m_nFloats = 0; - -#if defined ENABLE_GMENU_INTEGRATION -static GDBusConnection* pSessionBus = NULL; -#endif - -static sal_uInt16 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_MOD1_MASK) ) - nCode |= KEY_MOD2; - - // Map Meta/Super keys to MOD3 modifier on all Unix systems - // except Mac OS X - if ( (state & GDK_META_MASK ) || ( state & GDK_SUPER_MASK ) ) - nCode |= KEY_MOD3; - return nCode; -} - -static sal_uInt16 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; -} - -static sal_uInt16 GetKeyCode( guint keyval ) -{ - sal_uInt16 nCode = 0; - if( keyval >= GDK_0 && keyval <= GDK_9 ) - nCode = KEY_0 + (keyval-GDK_0); - else if( keyval >= GDK_KP_0 && keyval <= GDK_KP_9 ) - nCode = KEY_0 + (keyval-GDK_KP_0); - else if( keyval >= GDK_A && keyval <= GDK_Z ) - nCode = KEY_A + (keyval-GDK_A ); - else if( keyval >= GDK_a && keyval <= GDK_z ) - nCode = KEY_A + (keyval-GDK_a ); - else if( keyval >= GDK_F1 && keyval <= GDK_F26 ) - { -#if !GTK_CHECK_VERSION(3,0,0) - if( GetGtkSalData()->GetGtkDisplay()->IsNumLockFromXS() ) - { - nCode = KEY_F1 + (keyval-GDK_F1); - } - else -#endif - { - switch( keyval ) - { - // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx - case GDK_L2: -#if !GTK_CHECK_VERSION(3,0,0) - if( GetGtkSalData()->GetGtkDisplay()->GetServerVendor() == vendor_sun ) - nCode = KEY_REPEAT; - else -#endif - nCode = KEY_F12; - break; - case GDK_L3: nCode = KEY_PROPERTIES; break; - case GDK_L4: nCode = KEY_UNDO; break; - case GDK_L6: nCode = KEY_COPY; break; // KEY_F16 - case GDK_L8: nCode = KEY_PASTE; break; // KEY_F18 - case GDK_L10: nCode = KEY_CUT; break; // KEY_F20 - default: - nCode = KEY_F1 + (keyval-GDK_F1); break; - } - } - } - else - { - switch( keyval ) - { - case GDK_KP_Down: - case GDK_Down: nCode = KEY_DOWN; break; - case GDK_KP_Up: - case GDK_Up: nCode = KEY_UP; break; - case GDK_KP_Left: - case GDK_Left: nCode = KEY_LEFT; break; - case GDK_KP_Right: - case GDK_Right: nCode = KEY_RIGHT; break; - case GDK_KP_Begin: - case GDK_KP_Home: - case GDK_Begin: - case GDK_Home: nCode = KEY_HOME; break; - case GDK_KP_End: - case GDK_End: nCode = KEY_END; break; - case GDK_KP_Page_Up: - case GDK_Page_Up: nCode = KEY_PAGEUP; break; - case GDK_KP_Page_Down: - case GDK_Page_Down: nCode = KEY_PAGEDOWN; break; - case GDK_KP_Enter: - case GDK_Return: nCode = KEY_RETURN; break; - case GDK_Escape: nCode = KEY_ESCAPE; break; - case GDK_ISO_Left_Tab: - case GDK_KP_Tab: - case GDK_Tab: nCode = KEY_TAB; break; - case GDK_BackSpace: nCode = KEY_BACKSPACE; break; - case GDK_KP_Space: - case GDK_space: nCode = KEY_SPACE; break; - case GDK_KP_Insert: - case GDK_Insert: nCode = KEY_INSERT; break; - case GDK_KP_Delete: - case GDK_Delete: nCode = KEY_DELETE; break; - case GDK_plus: - case GDK_KP_Add: nCode = KEY_ADD; break; - case GDK_minus: - case GDK_KP_Subtract: nCode = KEY_SUBTRACT; break; - case GDK_asterisk: - case GDK_KP_Multiply: nCode = KEY_MULTIPLY; break; - case GDK_slash: - case GDK_KP_Divide: nCode = KEY_DIVIDE; break; - case GDK_period: nCode = KEY_POINT; break; - case GDK_decimalpoint: nCode = KEY_POINT; break; - case GDK_comma: nCode = KEY_COMMA; break; - case GDK_less: nCode = KEY_LESS; break; - case GDK_greater: nCode = KEY_GREATER; break; - case GDK_KP_Equal: - case GDK_equal: nCode = KEY_EQUAL; break; - case GDK_Find: nCode = KEY_FIND; break; - case GDK_Menu: nCode = KEY_CONTEXTMENU;break; - case GDK_Help: nCode = KEY_HELP; break; - case GDK_Undo: nCode = KEY_UNDO; break; - case GDK_Redo: nCode = KEY_REPEAT; break; - case GDK_KP_Decimal: - case GDK_KP_Separator: nCode = KEY_DECIMAL; break; - case GDK_asciitilde: nCode = KEY_TILDE; break; - case GDK_leftsinglequotemark: - case GDK_quoteleft: nCode = KEY_QUOTELEFT; break; - case GDK_bracketleft: nCode = KEY_BRACKETLEFT; break; - case GDK_bracketright: nCode = KEY_BRACKETRIGHT; break; - case GDK_semicolon: nCode = KEY_SEMICOLON; break; - case GDK_quoteright: nCode = KEY_QUOTERIGHT; break; - // some special cases, also see saldisp.cxx - // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000 - 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 - case 0x1000FF00: - nCode = KEY_DELETE; - break; - // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000 - 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 - 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 - 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; - } - } - - return nCode; -} - -static guint 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, NULL, NULL, NULL); - return updated_keyval; -} - -// 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 ) {} -}; - -inline 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 GTK_CHECK_VERSION(3,0,0) - -namespace { -/// Decouple SalFrame lifetime from damagetracker lifetime -struct DamageTracker : public basebmp::IBitmapDeviceDamageTracker -{ - DamageTracker(GtkSalFrame& rFrame) : m_rFrame(rFrame) - {} - - virtual ~DamageTracker() {} - - virtual void damaged(const basegfx::B2IBox& rDamageRect) const SAL_OVERRIDE - { - m_rFrame.damaged(rDamageRect); - } - - GtkSalFrame& m_rFrame; -}; -} - -static bool dumpframes = false; -#endif - -void GtkSalFrame::doKeyCallback( guint state, - guint keyval, - guint16 hardware_keycode, - guint8 group, - guint32 time, - sal_Unicode aOrigCode, - bool bDown, - bool bSendRelease - ) -{ - SalKeyEvent aEvent; - - aEvent.mnTime = time; - aEvent.mnCharCode = aOrigCode; - aEvent.mnRepeat = 0; - - vcl::DeletionListener aDel( this ); - -#if GTK_CHECK_VERSION(3,0,0) -#if 0 - // shift-zero forces a re-draw and event is swallowed - if (keyval == GDK_0) - { - fprintf( stderr, "force widget_queue_draw\n"); - gtk_widget_queue_draw (m_pFixedContainer); - return; - } - else if (keyval == GDK_1) - { - fprintf( stderr, "force repaint all\n"); - TriggerPaintEvent(); - return; - } - else if (keyval == GDK_2) - { - dumpframes = !dumpframes; - fprintf(stderr, "toggle dump frames to %d\n", dumpframes); - return; - } -#endif -#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( 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_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); - } - - aEvent.mnCode |= GetKeyModCode( state ); - - if( bDown ) - { - bool bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent ); - // #i46889# copy AlternatKeyCode handling from generic plugin - if( ! bHandled ) - { - KeyAlternate aAlternate = GetAlternateKeyCode( aEvent.mnCode ); - if( aAlternate.nKeyCode ) - { - aEvent.mnCode = aAlternate.nKeyCode; - if( aAlternate.nCharCode ) - aEvent.mnCharCode = aAlternate.nCharCode; - bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent ); - } - } - if( bSendRelease && ! aDel.isDeleted() ) - { - CallCallback( SALEVENT_KEYUP, &aEvent ); - } - } - else - CallCallback( SALEVENT_KEYUP, &aEvent ); -} - -GtkSalFrame::GraphicsHolder::~GraphicsHolder() -{ - delete pGraphics; -} - -GtkSalFrame::GtkSalFrame( SalFrame* pParent, sal_uLong nStyle ) - : m_nXScreen( getDisplay()->GetDefaultXScreen() ) -{ - getDisplay()->registerFrame( this ); - m_bDefaultPos = true; - m_bDefaultSize = ( (nStyle & SAL_FRAME_STYLE_SIZEABLE) && ! pParent ); - m_bWindowIsGtkPlug = false; -#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) - m_pLastSyncedDbusMenu = NULL; -#endif - Init( pParent, nStyle ); -} - -GtkSalFrame::GtkSalFrame( SystemParentData* pSysData ) - : m_nXScreen( getDisplay()->GetDefaultXScreen() ) -{ - getDisplay()->registerFrame( this ); - // permanently ignore errors from our unruly children ... - GetGenericData()->ErrorTrapPush(); - m_bDefaultPos = true; - m_bDefaultSize = true; -#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) - m_pLastSyncedDbusMenu = NULL; -#endif - Init( pSysData ); -} - -#ifdef ENABLE_GMENU_INTEGRATION - -static void -gdk_x11_window_set_utf8_property (GdkWindow *window, - const gchar *name, - const gchar *value) -{ -#if !GTK_CHECK_VERSION(3,0,0) - GdkDisplay* display = gdk_window_get_display (window); - - if (value != NULL) - { - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_WINDOW_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, name), - gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, - PropModeReplace, reinterpret_cast(value), strlen (value)); - } - else - { - XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_WINDOW_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, name)); - } -#endif -} - -// AppMenu watch functions. - -static void ObjectDestroyedNotify( gpointer data ) -{ - if ( data ) { - g_object_unref( data ); - } -} - -#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) -void GtkSalFrame::EnsureDbusMenuSynced() -{ - GtkSalMenu* pSalMenu = static_cast(GetMenu()); - if(m_pLastSyncedDbusMenu != pSalMenu) { - m_pLastSyncedDbusMenu = pSalMenu; - static_cast(pSalMenu)->Activate(); - } -} -#endif - -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(); - } -} - -static void activate_uno(GSimpleAction *action, GVariant*, gpointer) -{ - uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); - - uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext ); - - uno::Reference < css::frame::XFrame > xFrame(xDesktop->getActiveFrame()); - if (!xFrame.is()) - xFrame = uno::Reference < css::frame::XFrame >(xDesktop, uno::UNO_QUERY); - - if (!xFrame.is()) - return; - - uno::Reference< css::frame::XDispatchProvider > xDispatchProvider(xFrame, uno::UNO_QUERY); - if (!xDispatchProvider.is()) - return; - - gchar *strval = NULL; - g_object_get(action, "name", &strval, NULL); - if (!strval) - return; - - if (strcmp(strval, "New") == 0) - { - uno::Reference xModuleManager(frame::ModuleManager::create(xContext)); - OUString aModuleId(xModuleManager->identify(xFrame)); - if (aModuleId.isEmpty()) - return; - - comphelper::SequenceAsHashMap lModuleDescription(xModuleManager->getByName(aModuleId)); - OUString sFactoryService; - lModuleDescription[OUString("ooSetupFactoryEmptyDocumentURL")] >>= sFactoryService; - if (sFactoryService.isEmpty()) - return; - - uno::Sequence < css::beans::PropertyValue > args(0); - xDesktop->loadComponentFromURL(sFactoryService, OUString("_blank"), 0, args); - return; - } - - OUString sCommand(".uno:"); - sCommand += OUString(strval, strlen(strval), RTL_TEXTENCODING_UTF8); - g_free(strval); - - css::util::URL aCommand; - aCommand.Complete = sCommand; - uno::Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create(xContext); - xParser->parseStrict(aCommand); - - uno::Reference< css::frame::XDispatch > xDisp = xDispatchProvider->queryDispatch(aCommand, OUString(), 0); - - if (!xDisp.is()) - return; - - xDisp->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >()); -} - -static const GActionEntry app_entries[] = { - { "OptionsTreeDialog", activate_uno, NULL, NULL, NULL, {0} }, - { "About", activate_uno, NULL, NULL, NULL, {0} }, - { "HelpIndex", activate_uno, NULL, NULL, NULL, {0} }, - { "Quit", activate_uno, NULL, NULL, NULL, {0} }, - { "New", activate_uno, NULL, NULL, NULL, {0} } -}; - -gboolean ensure_dbus_setup( gpointer data ) -{ - GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( data ); - GdkWindow* gdkWindow = widget_get_window( pSalFrame->getWindow() ); - - if ( gdkWindow != NULL && g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) == NULL ) - { - // Get a DBus session connection. - if(!pSessionBus) - pSessionBus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); - if( !pSessionBus ) - return FALSE; - - // Create menu model and action group attached to this frame. - GMenuModel* pMenuModel = G_MENU_MODEL( g_lo_menu_new() ); - GActionGroup* pActionGroup = reinterpret_cast(g_lo_action_group_new( static_cast< gpointer >( pSalFrame ) )); - - // Generate menu paths. - ::Window windowId = GDK_WINDOW_XID( gdkWindow ); - gchar* aDBusWindowPath = g_strdup_printf( "/org/libreoffice/window/%lu", windowId ); - gchar* aDBusMenubarPath = g_strdup_printf( "/org/libreoffice/window/%lu/menus/menubar", windowId ); - - // 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 ); - - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_ID", "org.libreoffice" ); - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_UNIQUE_BUS_NAME", g_dbus_connection_get_unique_name( pSessionBus ) ); - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_OBJECT_PATH", "/org/libreoffice" ); - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_WINDOW_OBJECT_PATH", aDBusWindowPath ); - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_MENUBAR_OBJECT_PATH", aDBusMenubarPath ); - - // 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, NULL); - SAL_INFO("vcl.unity", "exporting action group at " << pActionGroup << " for window " << windowId); - pSalFrame->m_nActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, aDBusWindowPath, pActionGroup, NULL); - pSalFrame->m_nHudAwarenessId = hud_awareness_register( pSessionBus, aDBusMenubarPath, hud_activated, pSalFrame, NULL, NULL ); - - // fdo#70885 we don't want app menu under Unity - bool bDesktopIsUnity = (SalGetDesktopEnvironment() == "UNITY"); - - if (!bDesktopIsUnity) - gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APP_MENU_OBJECT_PATH", "/org/libreoffice/menus/appmenu" ); - - //app menu, to-do translations, block normal menus when active, honor use appmenu settings - ResMgr* pMgr = ImplGetResMgr(); - if( pMgr && !bDesktopIsUnity ) - { - GMenu *menu = g_menu_new (); - GMenuItem* item; - - GMenu *firstsubmenu = g_menu_new (); - - OString sNew(OUStringToOString(ResId(SV_BUTTONTEXT_NEW, *pMgr).toString(), - RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); - - item = g_menu_item_new(sNew.getStr(), "app.New"); - g_menu_append_item( firstsubmenu, item ); - g_object_unref(item); - - g_menu_append_section( menu, NULL, G_MENU_MODEL(firstsubmenu)); - g_object_unref(firstsubmenu); - - GMenu *secondsubmenu = g_menu_new (); - - OString sPreferences(OUStringToOString(ResId(SV_STDTEXT_PREFERENCES, *pMgr).toString(), - RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); - - item = g_menu_item_new(sPreferences.getStr(), "app.OptionsTreeDialog"); - g_menu_append_item( secondsubmenu, item ); - g_object_unref(item); - - g_menu_append_section( menu, NULL, G_MENU_MODEL(secondsubmenu)); - g_object_unref(secondsubmenu); - - GMenu *thirdsubmenu = g_menu_new (); - - OString sHelp(OUStringToOString(ResId(SV_BUTTONTEXT_HELP, *pMgr).toString(), - RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); - - item = g_menu_item_new(sHelp.getStr(), "app.HelpIndex"); - g_menu_append_item( thirdsubmenu, item ); - g_object_unref(item); - - OString sAbout(OUStringToOString(ResId(SV_STDTEXT_ABOUT, *pMgr).toString(), - RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); - - item = g_menu_item_new(sAbout.getStr(), "app.About"); - g_menu_append_item( thirdsubmenu, item ); - g_object_unref(item); - - OString sQuit(OUStringToOString(ResId(SV_MENU_MAC_QUITAPP, *pMgr).toString(), - RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); - - item = g_menu_item_new(sQuit.getStr(), "app.Quit"); - g_menu_append_item( thirdsubmenu, item ); - g_object_unref(item); - g_menu_append_section( menu, NULL, G_MENU_MODEL(thirdsubmenu)); - g_object_unref(thirdsubmenu); - - GSimpleActionGroup *group = g_simple_action_group_new (); -#if GLIB_CHECK_VERSION(2,38,0) // g_simple_action_group_add_entries is deprecated since 2.38 - g_action_map_add_action_entries (G_ACTION_MAP (group), app_entries, G_N_ELEMENTS (app_entries), NULL); -#else - g_simple_action_group_add_entries (group, app_entries, G_N_ELEMENTS (app_entries), NULL); -#endif - GActionGroup* pAppActionGroup = G_ACTION_GROUP(group); - - pSalFrame->m_nAppActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, "/org/libreoffice", pAppActionGroup, NULL); - g_object_unref(pAppActionGroup); - pSalFrame->m_nAppMenuExportId = g_dbus_connection_export_menu_model (pSessionBus, "/org/libreoffice/menus/appmenu", G_MENU_MODEL (menu), NULL); - g_object_unref(menu); - } - - g_free( aDBusMenubarPath ); - g_free( aDBusWindowPath ); - } - - return FALSE; -} - -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 != NULL ) - { - GtkSalMenu* pGtkSalMenu = static_cast(pSalMenu); - pGtkSalMenu->Display( true ); - pGtkSalMenu->UpdateFull(); - } -} - -// 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"); - - //pSessionBus = NULL; - GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data ); - - SalMenu* pSalMenu = pSalFrame->GetMenu(); - - if ( pSalMenu ) { - GtkSalMenu* pGtkSalMenu = static_cast< GtkSalMenu* >( pSalMenu ); - pGtkSalMenu->Display( false ); - } -} -#endif - -void GtkSalFrame::EnsureAppMenuWatch() -{ -#ifdef ENABLE_GMENU_INTEGRATION - if ( !m_nWatcherId ) - { - // Get a DBus session connection. - if ( pSessionBus == NULL ) - { - pSessionBus = g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, NULL ); - - if ( pSessionBus == NULL ) - 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, - static_cast(this), - NULL ); - } - - //ensure_dbus_setup( this ); -#else - (void) this; // loplugin:staticmethods -#endif -} - -void GtkSalFrame::InvalidateGraphics() -{ - for (unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); ++i) - { - if( !m_aGraphics[i].pGraphics ) - continue; -#if !GTK_CHECK_VERSION(3,0,0) - m_aGraphics[i].pGraphics->SetDrawable( None, m_nXScreen ); - m_aGraphics[i].pGraphics->SetWindow(NULL); -#endif - m_aGraphics[i].bInUse = false; - } -} - -GtkSalFrame::~GtkSalFrame() -{ - InvalidateGraphics(); - - if( m_pParent ) - m_pParent->m_aChildren.remove( this ); - - getDisplay()->deregisterFrame( this ); - - if( m_pRegion ) - { -#if GTK_CHECK_VERSION(3,0,0) - cairo_region_destroy( m_pRegion ); -#else - gdk_region_destroy( m_pRegion ); -#endif - } - -#if !GTK_CHECK_VERSION(3,0,0) - if( m_hBackgroundPixmap ) - { - XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(), - widget_get_xid(m_pWindow), - None ); - XFreePixmap( getDisplay()->GetDisplay(), m_hBackgroundPixmap ); - } -#endif - - if( m_pIMHandler ) - delete m_pIMHandler; - - GtkWidget *pEventWidget = getMouseEventWidget(); - for (auto handler_id : m_aMouseSignalIds) - g_signal_handler_disconnect(G_OBJECT(pEventWidget), handler_id); - if( m_pFixedContainer ) - gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer ) ); - if( m_pEventBox ) - gtk_widget_destroy( GTK_WIDGET(m_pEventBox) ); - { - SolarMutexGuard aGuard; -#if defined ENABLE_GMENU_INTEGRATION - if(m_nWatcherId) - g_bus_unwatch_name(m_nWatcherId); -#endif - if( m_pWindow ) - { - g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", NULL ); - -#if defined ENABLE_GMENU_INTEGRATION - 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_nAppMenuExportId ) - g_dbus_connection_unexport_menu_model( pSessionBus, m_nAppMenuExportId ); - if ( m_nActionGroupExportId ) - g_dbus_connection_unexport_action_group( pSessionBus, m_nActionGroupExportId ); - if ( m_nAppActionGroupExportId ) - g_dbus_connection_unexport_action_group( pSessionBus, m_nAppActionGroupExportId ); - } -#endif - gtk_widget_destroy( m_pWindow ); - } - } - if( m_pForeignParent ) - g_object_unref( G_OBJECT( m_pForeignParent ) ); - if( m_pForeignTopLevel ) - g_object_unref( G_OBJECT( m_pForeignTopLevel) ); -} - -void GtkSalFrame::moveWindow( long nX, long nY ) -{ - if( isChild( false, true ) ) - { - if( m_pParent ) - gtk_fixed_move( m_pParent->getFixedContainer(), - m_pWindow, - nX - m_pParent->maGeometry.nX, nY - m_pParent->maGeometry.nY ); - } - else - gtk_window_move( GTK_WINDOW(m_pWindow), nX, nY ); -} - -void GtkSalFrame::widget_set_size_request(long nWidth, long nHeight) -{ -#if !GTK_CHECK_VERSION(3,0,0) - gint nOrigwidth, nOrigheight; - gtk_window_get_size(GTK_WINDOW(m_pWindow), &nOrigwidth, &nOrigheight); - if (nWidth > nOrigwidth || nHeight > nOrigheight) - { - m_bPaintsBlocked = true; - } - gtk_widget_set_size_request(m_pWindow, nWidth, nHeight ); -#else - gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer), nWidth, nHeight ); -#endif -} - -void GtkSalFrame::window_resize(long nWidth, long nHeight) -{ -#if !GTK_CHECK_VERSION(3,0,0) - gint nOrigwidth, nOrigheight; - gtk_window_get_size(GTK_WINDOW(m_pWindow), &nOrigwidth, &nOrigheight); - if (nWidth > nOrigwidth || nHeight > nOrigheight) - { - m_bPaintsBlocked = true; - } -#endif - gtk_window_resize(GTK_WINDOW(m_pWindow), nWidth, nHeight); -} - -void GtkSalFrame::resizeWindow( long nWidth, long nHeight ) -{ - if( isChild( false, true ) ) - { - widget_set_size_request(nWidth, nHeight); - } - else if( ! isChild( true, false ) ) - window_resize(nWidth, nHeight); -} - -#if GTK_CHECK_VERSION(3,2,0) - -static void -ooo_fixed_class_init(GtkFixedClass *klass) -{ - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); - widget_class->get_accessible = ooo_fixed_get_accessible; -} - -#endif - -/* - * 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 */ -#if GTK_CHECK_VERSION(3,2,0) - reinterpret_cast(ooo_fixed_class_init), /* class init */ -#else - nullptr, /* class init */ -#endif - nullptr, /* class finalize */ - NULL, /* class data */ - sizeof (GtkFixed), /* instance size */ - 0, /* nb preallocs */ - (GInstanceInitFunc) NULL, /* instance init */ - NULL /* value table */ - }; - - type = g_type_register_static( GTK_TYPE_FIXED, "OOoFixed", - &tinfo, (GTypeFlags) 0); - } - - return type; -} - -void GtkSalFrame::updateScreenNumber() -{ - int nScreen = 0; - GdkScreen *pScreen = gtk_widget_get_screen( m_pWindow ); - if( pScreen ) - nScreen = getDisplay()->getSystem()->getScreenMonitorIdx( pScreen, maGeometry.nX, maGeometry.nY ); - maGeometry.nDisplayScreenNumber = nScreen; -} - -GtkWidget *GtkSalFrame::getMouseEventWidget() const -{ -#if GTK_CHECK_VERSION(3,0,0) - return GTK_WIDGET(m_pEventBox); -#else - return m_pWindow; -#endif -} - -void GtkSalFrame::InitCommon() -{ -#if GTK_CHECK_VERSION(3,0,0) - m_pEventBox = GTK_EVENT_BOX(gtk_event_box_new()); - gtk_widget_add_events( GTK_WIDGET(m_pEventBox), - GDK_ALL_EVENTS_MASK ); - gtk_container_add( GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pEventBox) ); - - // add the fixed container child, - // fixed is needed since we have to position plugin windows - m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), NULL )); - gtk_container_add( GTK_CONTAINER(m_pEventBox), GTK_WIDGET(m_pFixedContainer) ); -#else - m_pEventBox = 0; - // add the fixed container child, - // fixed is needed since we have to position plugin windows - m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), NULL )); - gtk_container_add( GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pFixedContainer) ); -#endif - - GtkWidget *pEventWidget = getMouseEventWidget(); - - gtk_widget_set_app_paintable(GTK_WIDGET(m_pFixedContainer), true); - /*non-X11 displays won't show anything at all without double-buffering - enabled*/ - if (GDK_IS_X11_DISPLAY(getGdkDisplay())) - gtk_widget_set_double_buffered(GTK_WIDGET(m_pFixedContainer), false); - gtk_widget_set_redraw_on_allocate(GTK_WIDGET(m_pFixedContainer), false); - - - // connect signals - g_signal_connect( G_OBJECT(m_pWindow), "style-set", G_CALLBACK(signalStyleSet), this ); - 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), "motion-notify-event", G_CALLBACK(signalMotion), this )); - m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-release-event", G_CALLBACK(signalButton), this )); -#if GTK_CHECK_VERSION(3,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 ); -// g_signal_connect( G_OBJECT(m_pWindow), "state-flags-changed", G_CALLBACK(signalFlagsChanged), this ); -#if GTK_CHECK_VERSION(3,14,0) - GtkGesture *pSwipe = gtk_gesture_swipe_new(pEventWidget); - g_signal_connect(pSwipe, "swipe", G_CALLBACK(gestureSwipe), this); - gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pSwipe), GTK_PHASE_TARGET); - g_object_weak_ref(G_OBJECT(pEventWidget), reinterpret_cast(g_object_unref), pSwipe); - - GtkGesture *pLongPress = gtk_gesture_long_press_new(pEventWidget); - g_signal_connect(pLongPress, "pressed", G_CALLBACK(gestureLongPress), this); - gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pLongPress), GTK_PHASE_TARGET); - g_object_weak_ref(G_OBJECT(pEventWidget), reinterpret_cast(g_object_unref), pLongPress); - -#endif - -#else - g_signal_connect( G_OBJECT(m_pFixedContainer), "expose-event", G_CALLBACK(signalExpose), this ); -#endif - g_signal_connect( G_OBJECT(m_pWindow), "focus-in-event", G_CALLBACK(signalFocus), this ); - g_signal_connect( G_OBJECT(m_pWindow), "focus-out-event", G_CALLBACK(signalFocus), this ); - 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), "configure-event", G_CALLBACK(signalConfigure), this ); - 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 ); - g_signal_connect( G_OBJECT(m_pWindow), "delete-event", G_CALLBACK(signalDelete), this ); - g_signal_connect( G_OBJECT(m_pWindow), "window-state-event", G_CALLBACK(signalWindowState), this ); - g_signal_connect( G_OBJECT(m_pWindow), "scroll-event", G_CALLBACK(signalScroll), this ); - g_signal_connect( G_OBJECT(m_pWindow), "leave-notify-event", G_CALLBACK(signalCrossing), this ); - g_signal_connect( G_OBJECT(m_pWindow), "enter-notify-event", G_CALLBACK(signalCrossing), this ); - g_signal_connect( G_OBJECT(m_pWindow), "visibility-notify-event", G_CALLBACK(signalVisibility), this ); - g_signal_connect( G_OBJECT(m_pWindow), "destroy", G_CALLBACK(signalDestroy), this ); - - // init members - m_pCurrentCursor = NULL; - m_nKeyModifiers = 0; - m_bFullscreen = false; - m_bSpanMonitorsWhenFullscreen = false; - m_nState = GDK_WINDOW_STATE_WITHDRAWN; - m_nVisibility = GDK_VISIBILITY_FULLY_OBSCURED; -#if GTK_CHECK_VERSION(3,0,0) - m_nLastScrollEventTime = GDK_CURRENT_TIME; -#endif - m_bSendModChangeOnRelease = false; - m_pIMHandler = NULL; - m_hBackgroundPixmap = None; - m_nSavedScreenSaverTimeout = 0; - m_nGSMCookie = 0; - m_nExtStyle = 0; - m_pRegion = NULL; - m_ePointerStyle = static_cast(0xffff); - m_bSetFocusOnMap = false; - m_pSalMenu = NULL; - m_nWatcherId = 0; - m_nMenuExportId = 0; - m_nAppMenuExportId = 0; - m_nActionGroupExportId = 0; - m_nAppActionGroupExportId = 0; - m_nHudAwarenessId = 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_VISIBILITY_NOTIFY_MASK | GDK_SCROLL_MASK - ); - - // show the widgets -#if GTK_CHECK_VERSION(3,0,0) - gtk_widget_show_all( GTK_WIDGET(m_pEventBox) ); -#else - gtk_widget_show_all( GTK_WIDGET(m_pFixedContainer) ); -#endif - - // realize the window, we need an XWindow id - gtk_widget_realize( m_pWindow ); - - //system data - m_aSystemData.nSize = sizeof( SystemEnvData ); -#if !GTK_CHECK_VERSION(3,0,0) - GtkSalDisplay* pDisp = GetGtkSalData()->GetGtkDisplay(); - m_aSystemData.pDisplay = pDisp->GetDisplay(); - m_aSystemData.pVisual = pDisp->GetVisual( m_nXScreen ).GetVisual(); - m_aSystemData.nDepth = pDisp->GetVisual( m_nXScreen ).GetDepth(); - m_aSystemData.aColormap = pDisp->GetColormap( m_nXScreen ).GetXColormap(); - m_aSystemData.aWindow = widget_get_xid(m_pWindow); - m_aSystemData.aShellWindow = m_aSystemData.aWindow; -#else - static int nWindow = 0; - m_aSystemData.aWindow = nWindow; - m_aSystemData.aShellWindow = nWindow; - ++nWindow; -#endif - m_aSystemData.pSalFrame = this; - m_aSystemData.pWidget = m_pWindow; - m_aSystemData.nScreen = m_nXScreen.getXScreen(); - m_aSystemData.pAppContext = NULL; - m_aSystemData.pShellWidget = m_aSystemData.pWidget; - - // fake an initial geometry, gets updated via configure event or SetPosSize - if( m_bDefaultPos || m_bDefaultSize ) - { - Size aDefSize = calcDefaultSize(); - maGeometry.nX = -1; - maGeometry.nY = -1; - maGeometry.nWidth = aDefSize.Width(); - maGeometry.nHeight = aDefSize.Height(); - if( m_pParent ) - { - // approximation - maGeometry.nTopDecoration = m_pParent->maGeometry.nTopDecoration; - maGeometry.nBottomDecoration = m_pParent->maGeometry.nBottomDecoration; - maGeometry.nLeftDecoration = m_pParent->maGeometry.nLeftDecoration; - maGeometry.nRightDecoration = m_pParent->maGeometry.nRightDecoration; - } - else - { - maGeometry.nTopDecoration = 0; - maGeometry.nBottomDecoration = 0; - maGeometry.nLeftDecoration = 0; - maGeometry.nRightDecoration = 0; - } - } - else - { - resizeWindow( maGeometry.nWidth, maGeometry.nHeight ); - moveWindow( maGeometry.nX, maGeometry.nY ); - } - updateScreenNumber(); - - SetIcon(1); - -#if !GTK_CHECK_VERSION(3,0,0) - m_nWorkArea = pDisp->getWMAdaptor()->getCurrentWorkArea(); - /* #i64117# gtk sets a nice background pixmap - * but we actually don't really want that, so save - * some time on the Xserver as well as prevent - * some paint issues - */ - XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(), - widget_get_xid(m_pWindow), - m_hBackgroundPixmap ); -#endif -} - -/* Sadly gtk_window_set_accept_focus exists only since gtk 2.4 - * for achieving the same effect we will remove the WM_TAKE_FOCUS - * protocol from the window and set the input hint to false. - * But gtk_window_set_accept_focus needs to be called before - * window realization whereas the removal obviously can only happen - * after realization. - */ - -#if !GTK_CHECK_VERSION(3,0,0) -extern "C" { - typedef void(*setAcceptFn)( GtkWindow*, gboolean ); - static setAcceptFn p_gtk_window_set_accept_focus = NULL; - static bool bGetAcceptFocusFn = true; - - typedef void(*setUserTimeFn)( GdkWindow*, guint32 ); - static setUserTimeFn p_gdk_x11_window_set_user_time = NULL; - static bool bGetSetUserTimeFn = true; -} -#endif - -static void lcl_set_accept_focus( GtkWindow* pWindow, gboolean bAccept, bool bBeforeRealize ) -{ -#if !GTK_CHECK_VERSION(3,0,0) - if( bGetAcceptFocusFn ) - { - bGetAcceptFocusFn = false; - p_gtk_window_set_accept_focus = reinterpret_cast(osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_window_set_accept_focus" )); - } - if( p_gtk_window_set_accept_focus && bBeforeRealize ) - p_gtk_window_set_accept_focus( pWindow, bAccept ); - else if( ! bBeforeRealize ) - { - Display* pDisplay = GetGtkSalData()->GetGtkDisplay()->GetDisplay(); - ::Window aWindow = widget_get_xid(GTK_WIDGET(pWindow)); - XWMHints* pHints = XGetWMHints( pDisplay, aWindow ); - if( ! pHints ) - { - pHints = XAllocWMHints(); - pHints->flags = 0; - } - pHints->flags |= InputHint; - pHints->input = bAccept ? True : False; - XSetWMHints( pDisplay, aWindow, pHints ); - XFree( pHints ); - - if (GetGtkSalData()->GetGtkDisplay()->getWMAdaptor()->getWindowManagerName() == "compiz") - return; - - /* remove WM_TAKE_FOCUS protocol; this would usually be the - * right thing, but gtk handles it internally whereas we - * want to handle it ourselves (as to sometimes not get - * the focus) - */ - Atom* pProtocols = NULL; - int nProtocols = 0; - XGetWMProtocols( pDisplay, - aWindow, - &pProtocols, &nProtocols ); - if( pProtocols ) - { - bool bSet = false; - Atom nTakeFocus = XInternAtom( pDisplay, "WM_TAKE_FOCUS", True ); - if( nTakeFocus ) - { - for( int i = 0; i < nProtocols; i++ ) - { - if( pProtocols[i] == nTakeFocus ) - { - for( int n = i; n < nProtocols-1; n++ ) - pProtocols[n] = pProtocols[n+1]; - nProtocols--; - i--; - bSet = true; - } - } - } - if( bSet ) - XSetWMProtocols( pDisplay, aWindow, pProtocols, nProtocols ); - XFree( pProtocols ); - } - } -#else - gtk_window_set_accept_focus(pWindow, bAccept); - (void)bBeforeRealize; -#endif -} - -#if !GTK_CHECK_VERSION(3,0,0) -static void lcl_set_user_time( GtkWindow* i_pWindow, guint32 i_nTime ) -{ - if( bGetSetUserTimeFn ) - { - bGetSetUserTimeFn = false; - p_gdk_x11_window_set_user_time = reinterpret_cast(osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gdk_x11_window_set_user_time" )); - } - bool bSet = false; - if( p_gdk_x11_window_set_user_time ) - { - GdkWindow* pWin = widget_get_window(GTK_WIDGET(i_pWindow)); - if( pWin ) // only if the window is realized. - { - p_gdk_x11_window_set_user_time( pWin, i_nTime ); - bSet = true; - } - } - if( !bSet ) - { - Display* pDisplay = GetGtkSalData()->GetGtkDisplay()->GetDisplay(); - Atom nUserTime = XInternAtom( pDisplay, "_NET_WM_USER_TIME", True ); - if( nUserTime ) - { - XChangeProperty( pDisplay, widget_get_xid(GTK_WIDGET(i_pWindow)), - nUserTime, XA_CARDINAL, 32, - PropModeReplace, reinterpret_cast(&i_nTime), 1 ); - } - } -}; -#endif - -GtkSalFrame *GtkSalFrame::getFromWindow( GtkWindow *pWindow ) -{ - return static_cast(g_object_get_data( G_OBJECT( pWindow ), "SalFrame" )); -} - -void GtkSalFrame::Init( SalFrame* pParent, sal_uLong nStyle ) -{ - if( nStyle & SAL_FRAME_STYLE_DEFAULT ) // ensure default style - { - nStyle |= SAL_FRAME_STYLE_MOVEABLE | SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_CLOSEABLE; - nStyle &= ~SAL_FRAME_STYLE_FLOAT; - } - - m_pParent = static_cast(pParent); - m_pForeignParent = NULL; - m_aForeignParentWindow = None; - m_pForeignTopLevel = NULL; - m_aForeignTopLevelWindow = None; - m_nStyle = nStyle; - - GtkWindowType eWinType = ( (nStyle & SAL_FRAME_STYLE_FLOAT) && - ! (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION| - SAL_FRAME_STYLE_FLOAT_FOCUSABLE)) - ) - ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL; - - if( nStyle & SAL_FRAME_STYLE_SYSTEMCHILD ) - { - m_pWindow = gtk_event_box_new(); - if( m_pParent ) - { - // insert into container - gtk_fixed_put( m_pParent->getFixedContainer(), - m_pWindow, 0, 0 ); - - } - } - else - { - m_pWindow = gtk_widget_new( GTK_TYPE_WINDOW, "type", eWinType, - "visible", FALSE, NULL ); - } - g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", this ); - g_object_set_data( G_OBJECT( m_pWindow ), "libo-version", (gpointer)LIBO_VERSION_DOTTED); - - // force wm class hint - m_nExtStyle = ~0; - if (m_pParent) - m_sWMClass = m_pParent->m_sWMClass; - SetExtendedFrameStyle( 0 ); - - if( m_pParent && m_pParent->m_pWindow && ! isChild() ) - gtk_window_set_screen( GTK_WINDOW(m_pWindow), gtk_window_get_screen( GTK_WINDOW(m_pParent->m_pWindow) ) ); - - if (m_pParent) - { - if (!(m_pParent->m_nStyle & SAL_FRAME_STYLE_PLUG)) - gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), GTK_WINDOW(m_pParent->m_pWindow) ); - m_pParent->m_aChildren.push_back( this ); - } - - InitCommon(); - - // set window type - bool bDecoHandling = - ! isChild() && - ( ! (nStyle & SAL_FRAME_STYLE_FLOAT) || - (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) ); - - if( bDecoHandling ) - { - GdkWindowTypeHint eType = GDK_WINDOW_TYPE_HINT_NORMAL; - if( (nStyle & SAL_FRAME_STYLE_DIALOG) && m_pParent != 0 ) - eType = GDK_WINDOW_TYPE_HINT_DIALOG; - if( (nStyle & SAL_FRAME_STYLE_INTRO) ) - { - gtk_window_set_role( GTK_WINDOW(m_pWindow), "splashscreen" ); - eType = GDK_WINDOW_TYPE_HINT_SPLASHSCREEN; - } - else if( (nStyle & SAL_FRAME_STYLE_TOOLWINDOW ) ) - { - eType = GDK_WINDOW_TYPE_HINT_UTILITY; - gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow), true ); - } - else if( (nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) - { - eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; - lcl_set_accept_focus( GTK_WINDOW(m_pWindow), false, true ); - } - else if( (nStyle & SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) - { - eType = GDK_WINDOW_TYPE_HINT_UTILITY; - } -#if !GTK_CHECK_VERSION(3,0,0) - if( (nStyle & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN ) - && getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) - { - eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; - gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), true ); - } -#endif - gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), eType ); - gtk_window_set_gravity( GTK_WINDOW(m_pWindow), GDK_GRAVITY_STATIC ); - } - else if( (nStyle & SAL_FRAME_STYLE_FLOAT) ) - { - gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), GDK_WINDOW_TYPE_HINT_POPUP_MENU ); - } - -#if !GTK_CHECK_VERSION(3,0,0) - if( eWinType == GTK_WINDOW_TOPLEVEL ) - { -#ifdef ENABLE_GMENU_INTEGRATION - // Enable DBus native menu if available. - ensure_dbus_setup( this ); -#endif - - guint32 nUserTime = 0; - if( (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_TOOLWINDOW)) == 0 ) - { - nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); - } - lcl_set_user_time(GTK_WINDOW(m_pWindow), nUserTime); - } -#endif - - if( bDecoHandling ) - { - gtk_window_set_resizable( GTK_WINDOW(m_pWindow), (nStyle & SAL_FRAME_STYLE_SIZEABLE) != 0 ); - if( ( (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION)) ) ) - lcl_set_accept_focus( GTK_WINDOW(m_pWindow), false, false ); - } -} - -GdkNativeWindow GtkSalFrame::findTopLevelSystemWindow( GdkNativeWindow aWindow ) -{ -#if !GTK_CHECK_VERSION(3,0,0) - ::Window aRoot, aParent; - ::Window* pChildren; - unsigned int nChildren; - bool bBreak = false; - do - { - pChildren = NULL; - nChildren = 0; - aParent = aRoot = None; - XQueryTree( getDisplay()->GetDisplay(), aWindow, - &aRoot, &aParent, &pChildren, &nChildren ); - XFree( pChildren ); - if( aParent != aRoot ) - aWindow = aParent; - int nCount = 0; - Atom* pProps = XListProperties( getDisplay()->GetDisplay(), - aWindow, - &nCount ); - for( int i = 0; i < nCount && ! bBreak; ++i ) - bBreak = (pProps[i] == XA_WM_HINTS); - if( pProps ) - XFree( pProps ); - } while( aParent != aRoot && ! bBreak ); - - return aWindow; -#else - (void)aWindow; - //FIXME: no findToplevelSystemWindow - return 0; -#endif -} - -void GtkSalFrame::Init( SystemParentData* pSysData ) -{ - m_pParent = NULL; - m_aForeignParentWindow = (GdkNativeWindow)pSysData->aWindow; - m_pForeignParent = NULL; - m_aForeignTopLevelWindow = findTopLevelSystemWindow( (GdkNativeWindow)pSysData->aWindow ); - m_pForeignTopLevel = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignTopLevelWindow ); - gdk_window_set_events( m_pForeignTopLevel, GDK_STRUCTURE_MASK ); - - if( pSysData->nSize > sizeof(pSysData->nSize)+sizeof(pSysData->aWindow) && pSysData->bXEmbedSupport ) - { -#if GTK_CHECK_VERSION(3,0,0) - m_pWindow = gtk_plug_new_for_display( getGdkDisplay(), pSysData->aWindow ); -#else - m_pWindow = gtk_plug_new( pSysData->aWindow ); -#endif - m_bWindowIsGtkPlug = true; - widget_set_can_default( m_pWindow, true ); - widget_set_can_focus( m_pWindow, true ); - gtk_widget_set_sensitive( m_pWindow, true ); - } - else - { - m_pWindow = gtk_window_new( GTK_WINDOW_POPUP ); - m_bWindowIsGtkPlug = false; - } - m_nStyle = SAL_FRAME_STYLE_PLUG; - InitCommon(); - - m_pForeignParent = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignParentWindow ); - gdk_window_set_events( m_pForeignParent, GDK_STRUCTURE_MASK ); - -#if !GTK_CHECK_VERSION(3,0,0) - int x_ret, y_ret; - unsigned int w, h, bw, d; - ::Window aRoot; - XGetGeometry( getDisplay()->GetDisplay(), pSysData->aWindow, - &aRoot, &x_ret, &y_ret, &w, &h, &bw, &d ); - maGeometry.nWidth = w; - maGeometry.nHeight = h; - window_resize(w, h); - gtk_window_move( GTK_WINDOW(m_pWindow), 0, 0 ); - if( ! m_bWindowIsGtkPlug ) - { - XReparentWindow( getDisplay()->GetDisplay(), - widget_get_xid(m_pWindow), - (::Window)pSysData->aWindow, - 0, 0 ); - } -#else - //FIXME: Handling embedded windows, is going to be fun ... -#endif -} - -void GtkSalFrame::askForXEmbedFocus( sal_Int32 i_nTimeCode ) -{ -#if !GTK_CHECK_VERSION(3,0,0) - XEvent aEvent; - - memset( &aEvent, 0, sizeof(aEvent) ); - aEvent.xclient.window = m_aForeignParentWindow; - aEvent.xclient.type = ClientMessage; - aEvent.xclient.message_type = getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED ); - aEvent.xclient.format = 32; - aEvent.xclient.data.l[0] = i_nTimeCode ? i_nTimeCode : CurrentTime; - aEvent.xclient.data.l[1] = 3; // XEMBED_REQUEST_FOCUS - aEvent.xclient.data.l[2] = 0; - aEvent.xclient.data.l[3] = 0; - aEvent.xclient.data.l[4] = 0; - - GetGenericData()->ErrorTrapPush(); - XSendEvent( getDisplay()->GetDisplay(), - m_aForeignParentWindow, - False, NoEventMask, &aEvent ); - GetGenericData()->ErrorTrapPop(); -#else - (void) this; // loplugin:staticmethods - (void)i_nTimeCode; - //FIXME: no askForXEmbedFocus for gtk3 yet -#endif -} - -void GtkSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle ) -{ - if( nStyle != m_nExtStyle && ! isChild() ) - { - m_nExtStyle = nStyle; - updateWMClass(); - } -} - -SalGraphics* GtkSalFrame::AcquireGraphics() -{ - if( m_pWindow ) - { - for( int i = 0; i < nMaxGraphics; i++ ) - { - if( ! m_aGraphics[i].bInUse ) - { - m_aGraphics[i].bInUse = true; - if( ! m_aGraphics[i].pGraphics ) - { -#if GTK_CHECK_VERSION(3,0,0) - m_aGraphics[i].pGraphics = new GtkSalGraphics( this, m_pWindow ); - if( !m_aFrame.get() ) - { - AllocateFrame(); - TriggerPaintEvent(); - } - m_aGraphics[i].pGraphics->setDevice( m_aFrame ); -#else // common case: - m_aGraphics[i].pGraphics = new GtkSalGraphics( this, m_pWindow, m_nXScreen ); -#endif - } - return m_aGraphics[i].pGraphics; - } - } - } - - return NULL; -} - -void GtkSalFrame::ReleaseGraphics( SalGraphics* pGraphics ) -{ - for( int i = 0; i < nMaxGraphics; i++ ) - { - if( m_aGraphics[i].pGraphics == pGraphics ) - { - m_aGraphics[i].bInUse = false; - break; - } - } -} - -bool GtkSalFrame::PostEvent( void* pData ) -{ - getDisplay()->SendInternalEvent( this, pData ); - return true; -} - -void GtkSalFrame::SetTitle( const OUString& rTitle ) -{ - m_aTitle = rTitle; - if( m_pWindow && ! isChild() ) - gtk_window_set_title( GTK_WINDOW(m_pWindow), OUStringToOString( rTitle, RTL_TEXTENCODING_UTF8 ).getStr() ); -} - -static inline sal_uInt8 * -getRow( BitmapBuffer *pBuffer, sal_uLong nRow ) -{ - if( BMP_SCANLINE_ADJUSTMENT( pBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN ) - return pBuffer->mpBits + nRow * pBuffer->mnScanlineSize; - else - return pBuffer->mpBits + ( pBuffer->mnHeight - nRow - 1 ) * pBuffer->mnScanlineSize; -} - -static GdkPixbuf * -bitmapToPixbuf( SalBitmap *pSalBitmap, SalBitmap *pSalAlpha ) -{ - g_return_val_if_fail( pSalBitmap != NULL, NULL ); - g_return_val_if_fail( pSalAlpha != NULL, NULL ); - - BitmapBuffer *pBitmap = pSalBitmap->AcquireBuffer( BITMAP_READ_ACCESS ); - g_return_val_if_fail( pBitmap != NULL, NULL ); - g_return_val_if_fail( pBitmap->mnBitCount == 24 || pBitmap->mnBitCount == 32, NULL ); - - BitmapBuffer *pAlpha = pSalAlpha->AcquireBuffer( BITMAP_READ_ACCESS ); - g_return_val_if_fail( pAlpha != NULL, NULL ); - g_return_val_if_fail( pAlpha->mnBitCount == 8, NULL ); - - Size aSize = pSalBitmap->GetSize(); - g_return_val_if_fail( pSalAlpha->GetSize() == aSize, NULL ); - - int nX, nY; - guchar *pPixbufData = static_cast(g_malloc (4 * aSize.Width() * aSize.Height() )); - guchar *pDestData = pPixbufData; - - for( nY = 0; nY < pBitmap->mnHeight; nY++ ) - { - sal_uInt8 *pData = getRow( pBitmap, nY ); - sal_uInt8 *pAlphaData = getRow( pAlpha, nY ); - - for( nX = 0; nX < pBitmap->mnWidth; nX++ ) - { - BitmapColor aColor; - if (pBitmap->mnFormat == BMP_FORMAT_24BIT_TC_BGR) - { - aColor = BitmapColor(pData[2], pData[1], pData[0]); - pData += 3; - } - else if (pBitmap->mnFormat == BMP_FORMAT_24BIT_TC_RGB) - { - aColor = BitmapColor(pData[0], pData[1], pData[2]); - pData += 3; - } - else - { - pBitmap->maColorMask.GetColorFor32Bit(aColor, pData); - pData += 4; - } - *pDestData++ = aColor.GetRed(); - *pDestData++ = aColor.GetGreen(); - *pDestData++ = aColor.GetBlue(); - *pDestData++ = 255 - *pAlphaData++; - } - } - - pSalBitmap->ReleaseBuffer( pBitmap, BITMAP_READ_ACCESS ); - pSalAlpha->ReleaseBuffer( pAlpha, BITMAP_READ_ACCESS ); - - return gdk_pixbuf_new_from_data( pPixbufData, - GDK_COLORSPACE_RGB, true, 8, - aSize.Width(), aSize.Height(), - aSize.Width() * 4, - reinterpret_cast(g_free), - NULL ); -} - -void GtkSalFrame::SetIcon( sal_uInt16 nIcon ) -{ - if( (m_nStyle & (SAL_FRAME_STYLE_PLUG|SAL_FRAME_STYLE_SYSTEMCHILD|SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_INTRO|SAL_FRAME_STYLE_OWNERDRAWDECORATION)) - || ! m_pWindow ) - return; - - if( !ImplGetResMgr() ) - return; - - GdkPixbuf *pBuf; - GList *pIcons = NULL; - - sal_uInt16 nOffsets[2] = { SV_ICON_SMALL_START, SV_ICON_LARGE_START }; - sal_uInt16 nIndex; - - for( nIndex = 0; nIndex < sizeof(nOffsets)/ sizeof(sal_uInt16); nIndex++ ) - { - // #i44723# workaround gcc temporary problem - ResId aResId( nOffsets[nIndex] + nIcon, *ImplGetResMgr() ); - BitmapEx aIcon( aResId ); - - // #i81083# convert to 24bit/8bit alpha bitmap - Bitmap aBmp = aIcon.GetBitmap(); - if( aBmp.GetBitCount() != 24 || ! aIcon.IsAlpha() ) - { - if( aBmp.GetBitCount() != 24 ) - aBmp.Convert( BMP_CONVERSION_24BIT ); - AlphaMask aMask; - if( ! aIcon.IsAlpha() ) - { - switch( aIcon.GetTransparentType() ) - { - case TRANSPARENT_NONE: - { - sal_uInt8 nTrans = 0; - aMask = AlphaMask( aBmp.GetSizePixel(), &nTrans ); - } - break; - case TRANSPARENT_COLOR: - aMask = AlphaMask( aBmp.CreateMask( aIcon.GetTransparentColor() ) ); - break; - case TRANSPARENT_BITMAP: - aMask = AlphaMask( aIcon.GetMask() ); - break; - default: - OSL_FAIL( "unhandled transparent type" ); - break; - } - } - else - aMask = aIcon.GetAlpha(); - aIcon = BitmapEx( aBmp, aMask ); - } - - ImpBitmap *pIconImpBitmap = aIcon.ImplGetBitmapImpBitmap(); - ImpBitmap *pIconImpMask = aIcon.ImplGetMaskImpBitmap(); - - if( pIconImpBitmap && pIconImpMask ) - { - SalBitmap *pIconBitmap = - pIconImpBitmap->ImplGetSalBitmap(); - SalBitmap *pIconMask = - pIconImpMask->ImplGetSalBitmap(); - - if( ( pBuf = bitmapToPixbuf( pIconBitmap, pIconMask ) ) ) - pIcons = g_list_prepend( pIcons, pBuf ); - } - } - - gtk_window_set_icon_list( GTK_WINDOW(m_pWindow), pIcons ); - - g_list_foreach( pIcons, reinterpret_cast(g_object_unref), NULL ); - g_list_free( pIcons ); -} - -void GtkSalFrame::SetMenu( SalMenu* pSalMenu ) -{ -// if(m_pSalMenu) -// { -// static_cast(m_pSalMenu)->DisconnectFrame(); -// } - m_pSalMenu = pSalMenu; -} - -SalMenu* GtkSalFrame::GetMenu() -{ - return m_pSalMenu; -} - -void GtkSalFrame::DrawMenuBar() -{ -} - -void GtkSalFrame::Center() -{ - long nX, nY; - - if( m_pParent ) - { - nX = ((long)m_pParent->maGeometry.nWidth - (long)maGeometry.nWidth)/2; - nY = ((long)m_pParent->maGeometry.nHeight - (long)maGeometry.nHeight)/2; - } - else - { - GdkScreen *pScreen = NULL; - gint px, py; - GdkModifierType nMask; - gdk_display_get_pointer( getGdkDisplay(), &pScreen, &px, &py, &nMask ); - if( !pScreen ) - pScreen = gtk_widget_get_screen( m_pWindow ); - - gint nMonitor; - nMonitor = gdk_screen_get_monitor_at_point( pScreen, px, py ); - - GdkRectangle aMonitor; - gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aMonitor ); - - nX = aMonitor.x + (aMonitor.width - (long)maGeometry.nWidth)/2; - nY = aMonitor.y + (aMonitor.height - (long)maGeometry.nHeight)/2; - } - SetPosSize( nX, nY, 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ); -} - -Size GtkSalFrame::calcDefaultSize() -{ - return bestmaxFrameSizeForScreenSize(getDisplay()->GetScreenSize(GetDisplayScreen())); -} - -void GtkSalFrame::SetDefaultSize() -{ - Size aDefSize = calcDefaultSize(); - - SetPosSize( 0, 0, aDefSize.Width(), aDefSize.Height(), - SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); - - if( (m_nStyle & SAL_FRAME_STYLE_DEFAULT) && m_pWindow ) - gtk_window_maximize( GTK_WINDOW(m_pWindow) ); -} - -static void initClientId() -{ -#if !GTK_CHECK_VERSION(3,0,0) - static bool bOnce = false; - if (!bOnce) - { - bOnce = true; - const OString& rID = SessionManagerClient::getSessionID(); - if (!rID.isEmpty()) - gdk_set_sm_client_id(rID.getStr()); - } -#else - // No session management support for gtk3+ - this is now legacy. -#endif -} - -void GtkSalFrame::Show( bool bVisible, bool bNoActivate ) -{ - if( m_pWindow ) - { -#if !GTK_CHECK_VERSION(3,0,0) - if( m_pParent && (m_pParent->m_nStyle & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN) - && getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) - gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), bVisible ); -#endif - if( bVisible ) - { - initClientId(); - getDisplay()->startupNotificationCompleted(); - - if( m_bDefaultPos ) - Center(); - if( m_bDefaultSize ) - SetDefaultSize(); - setMinMaxSize(); - -#if !GTK_CHECK_VERSION(3,0,0) - // #i45160# switch to desktop where a dialog with parent will appear - if( m_pParent && m_pParent->m_nWorkArea != m_nWorkArea && IS_WIDGET_MAPPED(m_pParent->m_pWindow) ) - getDisplay()->getWMAdaptor()->switchToWorkArea( m_pParent->m_nWorkArea ); -#endif - - if( isFloatGrabWindow() && - m_pParent && - m_nFloats == 0 && - ! getDisplay()->GetCaptureFrame() ) - { - /* #i63086# - * outsmart Metacity's "focus:mouse" mode - * which insists on taking the focus from the document - * to the new float. Grab focus to parent frame BEFORE - * showing the float (cannot grab it to the float - * before show). - */ - m_pParent->grabPointer( true, true ); - } - -#if !GTK_CHECK_VERSION(3,0,0) - guint32 nUserTime = 0; - if( ! bNoActivate && (m_nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_TOOLWINDOW)) == 0 ) - nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); - - //For these floating windows we don't want the main window to lose focus, and metacity has... - // metacity-2.24.0/src/core/window.c - - // if ((focus_window != NULL) && XSERVER_TIME_IS_BEFORE (compare, focus_window->net_wm_user_time)) - // "compare" window focus prevented by other activity - - // where "compare" is this window - - // which leads to... - - // /* This happens for error dialogs or alerts; these need to remain on - // * top, but it would be confusing to have its ancestor remain - // * focused. - // */ - // if (meta_window_is_ancestor_of_transient (focus_window, window)) - // "The focus window %s is an ancestor of the newly mapped " - // "window %s which isn't being focused. Unfocusing the " - // "ancestor.\n", - - // i.e. having a time < that of the toplevel frame means that the toplevel frame gets unfocused. - // awesome. - if( nUserTime == 0 ) - { - nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); - } - lcl_set_user_time(GTK_WINDOW(m_pWindow), nUserTime ); -#endif - - if( ! bNoActivate && (m_nStyle & SAL_FRAME_STYLE_TOOLWINDOW) ) - m_bSetFocusOnMap = true; - - gtk_widget_show( m_pWindow ); - - if( isFloatGrabWindow() ) - { - m_nFloats++; - if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 1 ) - { - grabPointer(true, true); - GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this; - pKeyboardFrame->grabKeyboard(true); - } - // #i44068# reset parent's IM context - if( m_pParent ) - m_pParent->EndExtTextInput(0); - } - if( m_bWindowIsGtkPlug ) - askForXEmbedFocus( 0 ); - } - else - { - if( isFloatGrabWindow() ) - { - m_nFloats--; - if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 0) - { - GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this; - pKeyboardFrame->grabKeyboard(false); - grabPointer(false); - } - } - gtk_widget_hide( m_pWindow ); - if( m_pIMHandler ) - m_pIMHandler->focusChanged( false ); - // flush here; there may be a very seldom race between - // the display connection used for clipboard and our connection - Flush(); - } - CallCallback( SALEVENT_RESIZE, NULL ); - TriggerPaintEvent(); - } -} - -void GtkSalFrame::setMinMaxSize() -{ - /* #i34504# metacity (and possibly others) do not treat - * _NET_WM_STATE_FULLSCREEN and max_width/height independently; - * whether they should is undefined. So don't set the max size hint - * for a full screen window. - */ - if( m_pWindow && ! isChild() ) - { - GdkGeometry aGeo; - int aHints = 0; - if( m_nStyle & SAL_FRAME_STYLE_SIZEABLE ) - { - if( m_aMinSize.Width() && m_aMinSize.Height() && ! m_bFullscreen ) - { - aGeo.min_width = m_aMinSize.Width(); - aGeo.min_height = m_aMinSize.Height(); - aHints |= GDK_HINT_MIN_SIZE; - } - if( m_aMaxSize.Width() && m_aMaxSize.Height() && ! m_bFullscreen ) - { - aGeo.max_width = m_aMaxSize.Width(); - aGeo.max_height = m_aMaxSize.Height(); - aHints |= GDK_HINT_MAX_SIZE; - } - } - else - { - if( ! m_bFullscreen ) - { - aGeo.min_width = maGeometry.nWidth; - aGeo.min_height = maGeometry.nHeight; - aHints |= GDK_HINT_MIN_SIZE; - - aGeo.max_width = maGeometry.nWidth; - aGeo.max_height = maGeometry.nHeight; - aHints |= GDK_HINT_MAX_SIZE; - } - } - if( m_bFullscreen && m_aMaxSize.Width() && m_aMaxSize.Height() ) - { - aGeo.max_width = m_aMaxSize.Width(); - aGeo.max_height = m_aMaxSize.Height(); - aHints |= GDK_HINT_MAX_SIZE; - } - if( aHints ) - { - gtk_window_set_geometry_hints( GTK_WINDOW(m_pWindow), - NULL, - &aGeo, - GdkWindowHints( aHints ) ); - } - } -} - -void GtkSalFrame::SetMaxClientSize( long nWidth, long nHeight ) -{ - if( ! isChild() ) - { - m_aMaxSize = Size( nWidth, nHeight ); - // Show does a setMinMaxSize - if( IS_WIDGET_MAPPED( m_pWindow ) ) - setMinMaxSize(); - } -} -void GtkSalFrame::SetMinClientSize( long nWidth, long nHeight ) -{ - if( ! isChild() ) - { - m_aMinSize = Size( nWidth, nHeight ); - if( m_pWindow ) - { - widget_set_size_request(nWidth, nHeight ); - // Show does a setMinMaxSize - if( IS_WIDGET_MAPPED( m_pWindow ) ) - setMinMaxSize(); - } - } -} - -// FIXME: we should really be an SvpSalFrame sub-class, and -// share their AllocateFrame ! -void GtkSalFrame::AllocateFrame() -{ -#if GTK_CHECK_VERSION(3,0,0) - basegfx::B2IVector aFrameSize( maGeometry.nWidth, maGeometry.nHeight ); - if( ! m_aFrame.get() || m_aFrame->getSize() != aFrameSize ) - { - if( aFrameSize.getX() == 0 ) - aFrameSize.setX( 1 ); - if( aFrameSize.getY() == 0 ) - aFrameSize.setY( 1 ); - m_aFrame = basebmp::createBitmapDevice(aFrameSize, true, - basebmp::FORMAT_THIRTYTWO_BIT_TC_MASK_BGRX); - m_aFrame->setDamageTracker( - basebmp::IBitmapDeviceDamageTrackerSharedPtr(new DamageTracker(*this)) ); - SAL_INFO("vcl.gtk3", "allocated m_aFrame size of " << maGeometry.nWidth << " x " << maGeometry.nHeight); - -#if OSL_DEBUG_LEVEL > 0 // set background to orange - m_aFrame->clear( basebmp::Color( 255, 127, 0 ) ); -#endif - - // update device in existing graphics - for( unsigned int i = 0; i < SAL_N_ELEMENTS( m_aGraphics ); ++i ) - { - if( !m_aGraphics[i].pGraphics ) - continue; - m_aGraphics[i].pGraphics->setDevice( m_aFrame ); - } - } -#endif -} - -void GtkSalFrame::SetPosSize( long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags ) -{ - if( !m_pWindow || isChild( true, false ) ) - return; - - bool bSized = false, bMoved = false; - - if( (nFlags & ( SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT )) && - (nWidth > 0 && nHeight > 0 ) // sometimes stupid things happen - ) - { - m_bDefaultSize = false; - - if( (unsigned long)nWidth != maGeometry.nWidth || (unsigned long)nHeight != maGeometry.nHeight ) - bSized = true; - maGeometry.nWidth = nWidth; - maGeometry.nHeight = nHeight; - - if( isChild( false, true ) ) - widget_set_size_request(nWidth, nHeight); - else if( ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) ) - window_resize(nWidth, nHeight); - setMinMaxSize(); - } - else if( m_bDefaultSize ) - SetDefaultSize(); - - m_bDefaultSize = false; - - if( nFlags & ( SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ) ) - { - if( m_pParent ) - { - if( AllSettings::GetLayoutRTL() ) - nX = m_pParent->maGeometry.nWidth-maGeometry.nWidth-1-nX; - nX += m_pParent->maGeometry.nX; - nY += m_pParent->maGeometry.nY; - } - - if( nX != maGeometry.nX || nY != maGeometry.nY ) - bMoved = true; - maGeometry.nX = nX; - maGeometry.nY = nY; - - m_bDefaultPos = false; - - moveWindow( maGeometry.nX, maGeometry.nY ); - - updateScreenNumber(); - } - else if( m_bDefaultPos ) - Center(); - - m_bDefaultPos = false; - - if( bSized ) - AllocateFrame(); - - if( bSized && ! bMoved ) - CallCallback( SALEVENT_RESIZE, NULL ); - else if( bMoved && ! bSized ) - CallCallback( SALEVENT_MOVE, NULL ); - else if( bMoved && bSized ) - CallCallback( SALEVENT_MOVERESIZE, NULL ); - - if (bSized) - TriggerPaintEvent(); -} - -void GtkSalFrame::GetClientSize( long& rWidth, long& rHeight ) -{ - if( m_pWindow && !(m_nState & GDK_WINDOW_STATE_ICONIFIED) ) - { - rWidth = maGeometry.nWidth; - rHeight = maGeometry.nHeight; - } - else - rWidth = rHeight = 0; -} - -void GtkSalFrame::GetWorkArea( Rectangle& rRect ) -{ -#if !GTK_CHECK_VERSION(3,0,0) - rRect = GetGtkSalData()->GetGtkDisplay()->getWMAdaptor()->getWorkArea( 0 ); -#else - GdkScreen *pScreen = gtk_window_get_screen(GTK_WINDOW(m_pWindow)); - Rectangle aRetRect; - int max = gdk_screen_get_n_monitors (pScreen); - for (int i = 0; i < max; ++i) - { - GdkRectangle aRect; - gdk_screen_get_monitor_workarea(pScreen, i, &aRect); - Rectangle aMonitorRect(aRect.x, aRect.y, aRect.x+aRect.width, aRect.y+aRect.height); - aRetRect.Union(aMonitorRect); - } - rRect = aRetRect; -#endif -} - -SalFrame* GtkSalFrame::GetParent() const -{ - return m_pParent; -} - -void GtkSalFrame::SetWindowState( const SalFrameState* pState ) -{ - if( ! m_pWindow || ! pState || isChild( true, false ) ) - return; - - const sal_uLong nMaxGeometryMask = - WINDOWSTATE_MASK_X | WINDOWSTATE_MASK_Y | - WINDOWSTATE_MASK_WIDTH | WINDOWSTATE_MASK_HEIGHT | - WINDOWSTATE_MASK_MAXIMIZED_X | WINDOWSTATE_MASK_MAXIMIZED_Y | - WINDOWSTATE_MASK_MAXIMIZED_WIDTH | WINDOWSTATE_MASK_MAXIMIZED_HEIGHT; - - if( (pState->mnMask & WINDOWSTATE_MASK_STATE) && - ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) && - (pState->mnState & WINDOWSTATE_STATE_MAXIMIZED) && - (pState->mnMask & nMaxGeometryMask) == nMaxGeometryMask ) - { - resizeWindow( pState->mnWidth, pState->mnHeight ); - moveWindow( pState->mnX, pState->mnY ); - m_bDefaultPos = m_bDefaultSize = false; - - maGeometry.nX = pState->mnMaximizedX; - maGeometry.nY = pState->mnMaximizedY; - maGeometry.nWidth = pState->mnMaximizedWidth; - maGeometry.nHeight = pState->mnMaximizedHeight; - updateScreenNumber(); - - m_nState = GdkWindowState( m_nState | GDK_WINDOW_STATE_MAXIMIZED ); - m_aRestorePosSize = Rectangle( Point( pState->mnX, pState->mnY ), - Size( pState->mnWidth, pState->mnHeight ) ); - CallCallback( SALEVENT_RESIZE, NULL ); - } - else if( pState->mnMask & (WINDOWSTATE_MASK_X | WINDOWSTATE_MASK_Y | - WINDOWSTATE_MASK_WIDTH | WINDOWSTATE_MASK_HEIGHT ) ) - { - sal_uInt16 nPosSizeFlags = 0; - long nX = pState->mnX - (m_pParent ? m_pParent->maGeometry.nX : 0); - long nY = pState->mnY - (m_pParent ? m_pParent->maGeometry.nY : 0); - if( pState->mnMask & WINDOWSTATE_MASK_X ) - nPosSizeFlags |= SAL_FRAME_POSSIZE_X; - else - nX = maGeometry.nX - (m_pParent ? m_pParent->maGeometry.nX : 0); - if( pState->mnMask & WINDOWSTATE_MASK_Y ) - nPosSizeFlags |= SAL_FRAME_POSSIZE_Y; - else - nY = maGeometry.nY - (m_pParent ? m_pParent->maGeometry.nY : 0); - if( pState->mnMask & WINDOWSTATE_MASK_WIDTH ) - nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH; - if( pState->mnMask & WINDOWSTATE_MASK_HEIGHT ) - nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT; - SetPosSize( nX, nY, pState->mnWidth, pState->mnHeight, nPosSizeFlags ); - } - if( pState->mnMask & WINDOWSTATE_MASK_STATE && ! isChild() ) - { - if( pState->mnState & WINDOWSTATE_STATE_MAXIMIZED ) - gtk_window_maximize( GTK_WINDOW(m_pWindow) ); - else - gtk_window_unmaximize( GTK_WINDOW(m_pWindow) ); - /* #i42379# there is no rollup state in GDK; and rolled up windows are - * (probably depending on the WM) reported as iconified. If we iconify a - * window here that was e.g. a dialog, then it will be unmapped but still - * not be displayed in the task list, so it's an iconified window that - * the user cannot get out of this state. So do not set the iconified state - * on windows with a parent (that is transient frames) since these tend - * to not be represented in an icon task list. - */ - if( (pState->mnState & WINDOWSTATE_STATE_MINIMIZED) - && ! m_pParent ) - gtk_window_iconify( GTK_WINDOW(m_pWindow) ); - else - gtk_window_deiconify( GTK_WINDOW(m_pWindow) ); - } - TriggerPaintEvent(); -} - -bool GtkSalFrame::GetWindowState( SalFrameState* pState ) -{ - pState->mnState = WINDOWSTATE_STATE_NORMAL; - pState->mnMask = WINDOWSTATE_MASK_STATE; - // rollup ? gtk 2.2 does not seem to support the shaded state - if( (m_nState & GDK_WINDOW_STATE_ICONIFIED) ) - pState->mnState |= WINDOWSTATE_STATE_MINIMIZED; - if( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) - { - pState->mnState |= WINDOWSTATE_STATE_MAXIMIZED; - pState->mnX = m_aRestorePosSize.Left(); - pState->mnY = m_aRestorePosSize.Top(); - pState->mnWidth = m_aRestorePosSize.GetWidth(); - pState->mnHeight = m_aRestorePosSize.GetHeight(); - pState->mnMaximizedX = maGeometry.nX; - pState->mnMaximizedY = maGeometry.nY; - pState->mnMaximizedWidth = maGeometry.nWidth; - pState->mnMaximizedHeight = maGeometry.nHeight; - pState->mnMask |= WINDOWSTATE_MASK_MAXIMIZED_X | - WINDOWSTATE_MASK_MAXIMIZED_Y | - WINDOWSTATE_MASK_MAXIMIZED_WIDTH | - WINDOWSTATE_MASK_MAXIMIZED_HEIGHT; - } - else - { - pState->mnX = maGeometry.nX; - pState->mnY = maGeometry.nY; - pState->mnWidth = maGeometry.nWidth; - pState->mnHeight = maGeometry.nHeight; - } - pState->mnMask |= WINDOWSTATE_MASK_X | - WINDOWSTATE_MASK_Y | - WINDOWSTATE_MASK_WIDTH | - WINDOWSTATE_MASK_HEIGHT; - - return true; -} - -typedef enum { - SET_RETAIN_SIZE, - SET_FULLSCREEN, - SET_UN_FULLSCREEN -} SetType; - -void GtkSalFrame::SetScreen( unsigned int nNewScreen, int eType, Rectangle *pSize ) -{ - if( !m_pWindow ) - return; - - if (maGeometry.nDisplayScreenNumber == nNewScreen && eType == SET_RETAIN_SIZE) - return; - - GdkScreen *pScreen = NULL; - GdkRectangle aNewMonitor; - - bool bSpanAllScreens = nNewScreen == (unsigned int)-1; - m_bSpanMonitorsWhenFullscreen = bSpanAllScreens && getDisplay()->getSystem()->GetDisplayScreenCount() > 1; - - if (m_bSpanMonitorsWhenFullscreen) //span all screens - { - pScreen = gtk_widget_get_screen( m_pWindow ); - aNewMonitor.x = 0; - aNewMonitor.y = 0; - aNewMonitor.width = gdk_screen_get_width(pScreen); - aNewMonitor.height = gdk_screen_get_height(pScreen); - } - else - { - gint nMonitor; - bool bSameMonitor = false; - - if (!bSpanAllScreens) - { - pScreen = getDisplay()->getSystem()->getScreenMonitorFromIdx( nNewScreen, nMonitor ); - if (!pScreen) - { - g_warning ("Attempt to move GtkSalFrame to invalid screen %d => " - "fallback to current\n", nNewScreen); - } - } - - if (!pScreen) - { - pScreen = gtk_widget_get_screen( m_pWindow ); - bSameMonitor = true; - } - - // Heavy lifting, need to move screen ... - if( pScreen != gtk_widget_get_screen( m_pWindow )) - gtk_window_set_screen( GTK_WINDOW( m_pWindow ), pScreen ); - - gint nOldMonitor = gdk_screen_get_monitor_at_window( - pScreen, widget_get_window( m_pWindow ) ); - if (bSameMonitor) - nMonitor = nOldMonitor; - - #if OSL_DEBUG_LEVEL > 1 - if( nMonitor == nOldMonitor ) - g_warning( "An apparently pointless SetScreen - should we elide it ?" ); - #endif - - GdkRectangle aOldMonitor; - gdk_screen_get_monitor_geometry( pScreen, nOldMonitor, &aOldMonitor ); - gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aNewMonitor ); - - maGeometry.nX = aNewMonitor.x + maGeometry.nX - aOldMonitor.x; - maGeometry.nY = aNewMonitor.y + maGeometry.nY - aOldMonitor.y; - } - - bool bResize = false; - bool bVisible = IS_WIDGET_MAPPED( m_pWindow ); - if( bVisible ) - Show( false ); - - if( eType == SET_FULLSCREEN ) - { - maGeometry.nX = aNewMonitor.x; - maGeometry.nY = aNewMonitor.y; - maGeometry.nWidth = aNewMonitor.width; - maGeometry.nHeight = aNewMonitor.height; - m_nStyle |= SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; - bResize = true; - - // #i110881# for the benefit of compiz set a max size here - // else setting to fullscreen fails for unknown reasons - m_aMaxSize.Width() = aNewMonitor.width; - m_aMaxSize.Height() = aNewMonitor.height; - } - - if( pSize && eType == SET_UN_FULLSCREEN ) - { - maGeometry.nX = pSize->Left(); - maGeometry.nY = pSize->Top(); - maGeometry.nWidth = pSize->GetWidth(); - maGeometry.nHeight = pSize->GetHeight(); - m_nStyle &= ~SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; - bResize = true; - } - - if (bResize) - { - // temporarily re-sizeable - if( !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) ) - gtk_window_set_resizable( GTK_WINDOW(m_pWindow), TRUE ); - window_resize(maGeometry.nWidth, maGeometry.nHeight); - //I wonder if we should instead leave maGeometry alone and rely on - //configure-event to trigger signalConfigure and set it there - AllocateFrame(); - TriggerPaintEvent(); - } - - gtk_window_move( GTK_WINDOW( m_pWindow ), maGeometry.nX, maGeometry.nY ); - -#if !GTK_CHECK_VERSION(3,0,0) - // _NET_WM_STATE_FULLSCREEN (Metacity <-> KWin) - if( ! getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() ) -#endif - { -#if GTK_CHECK_VERSION(3,8,0) - gdk_window_set_fullscreen_mode( widget_get_window(m_pWindow), m_bSpanMonitorsWhenFullscreen - ? GDK_FULLSCREEN_ON_ALL_MONITORS : GDK_FULLSCREEN_ON_CURRENT_MONITOR ); -#endif - if( eType == SET_FULLSCREEN ) - gtk_window_fullscreen( GTK_WINDOW( m_pWindow ) ); - else if( eType == SET_UN_FULLSCREEN ) - gtk_window_unfullscreen( GTK_WINDOW( m_pWindow ) ); - } - - if( eType == SET_UN_FULLSCREEN && - !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) ) - gtk_window_set_resizable( GTK_WINDOW( m_pWindow ), FALSE ); - - // FIXME: we should really let gtk+ handle our widget hierarchy ... - if( m_pParent && gtk_widget_get_screen( m_pParent->m_pWindow ) != pScreen ) - SetParent( NULL ); - std::list< GtkSalFrame* > aChildren = m_aChildren; - for( std::list< GtkSalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it ) - (*it)->SetScreen( nNewScreen, SET_RETAIN_SIZE ); - - m_bDefaultPos = m_bDefaultSize = false; - updateScreenNumber(); - CallCallback( SALEVENT_MOVERESIZE, NULL ); - - if( bVisible ) - Show( true ); -} - -void GtkSalFrame::SetScreenNumber( unsigned int nNewScreen ) -{ - SetScreen( nNewScreen, SET_RETAIN_SIZE ); -} - -void GtkSalFrame::updateWMClass() -{ - OString aResClass = OUStringToOString(m_sWMClass, RTL_TEXTENCODING_ASCII_US); - const char *pResClass = !aResClass.isEmpty() ? aResClass.getStr() : - SalGenericSystem::getFrameClassName(); - Display *display; - - if (!getDisplay()->IsX11Display()) - return; - -#if GTK_CHECK_VERSION(3,0,0) - display = GDK_DISPLAY_XDISPLAY(getGdkDisplay()); -#else - display = getDisplay()->GetDisplay(); -#endif - - if( IS_WIDGET_REALIZED( m_pWindow ) ) - { - XClassHint* pClass = XAllocClassHint(); - OString aResName = SalGenericSystem::getFrameResName(); - pClass->res_name = const_cast(aResName.getStr()); - pClass->res_class = const_cast(pResClass); - XSetClassHint( display, - widget_get_xid(m_pWindow), - pClass ); - XFree( pClass ); - } -} - -void GtkSalFrame::SetApplicationID( const OUString &rWMClass ) -{ - if( rWMClass != m_sWMClass && ! isChild() ) - { - m_sWMClass = rWMClass; - updateWMClass(); - - for( std::list< GtkSalFrame* >::iterator it = m_aChildren.begin(); it != m_aChildren.end(); ++it ) - (*it)->SetApplicationID(rWMClass); - } -} - -void GtkSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nScreen ) -{ - m_bFullscreen = bFullScreen; - - if( !m_pWindow || isChild() ) - return; - - if( bFullScreen ) - { - m_aRestorePosSize = Rectangle( Point( maGeometry.nX, maGeometry.nY ), - Size( maGeometry.nWidth, maGeometry.nHeight ) ); - SetScreen( nScreen, SET_FULLSCREEN ); - } - else - { - SetScreen( nScreen, SET_UN_FULLSCREEN, - !m_aRestorePosSize.IsEmpty() ? &m_aRestorePosSize : NULL ); - m_aRestorePosSize = Rectangle(); - } -} - -/* definitions from xautolock.c (pl15) */ -#define XAUTOLOCK_DISABLE 1 -#define XAUTOLOCK_ENABLE 2 - -void GtkSalFrame::setAutoLock( bool bLock ) -{ - if( isChild() || !getDisplay()->IsX11Display() ) - return; - - GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(m_pWindow) ); - GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); - GdkWindow *pRootWin = gdk_screen_get_root_window( pScreen ); - - Atom nAtom = XInternAtom( GDK_DISPLAY_XDISPLAY( pDisplay ), - "XAUTOLOCK_MESSAGE", False ); - - int nMessage = bLock ? XAUTOLOCK_ENABLE : XAUTOLOCK_DISABLE; - - XChangeProperty( GDK_DISPLAY_XDISPLAY( pDisplay ), - GDK_WINDOW_XID( pRootWin ), - nAtom, XA_INTEGER, - 8, PropModeReplace, - reinterpret_cast(&nMessage), - sizeof( nMessage ) ); -} - -#ifdef ENABLE_DBUS -/** cookie is returned as an unsigned integer */ -static guint -dbus_inhibit_gsm (const gchar *appname, - const gchar *reason, - guint xid) -{ - gboolean res; - guint cookie; - GError *error = NULL; - DBusGProxy *proxy = NULL; - - /* get the DBUS session connection */ - DBusGConnection *session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); - if (error != NULL) { - g_debug ("DBUS cannot connect : %s", error->message); - g_error_free (error); - return -1; - } - - /* get the proxy with gnome-session-manager */ - proxy = dbus_g_proxy_new_for_name (session_connection, - GSM_DBUS_SERVICE, - GSM_DBUS_PATH, - GSM_DBUS_INTERFACE); - if (proxy == NULL) { - g_debug ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE); - return -1; - } - - res = dbus_g_proxy_call (proxy, - "Inhibit", &error, - G_TYPE_STRING, appname, - G_TYPE_UINT, xid, - G_TYPE_STRING, reason, - G_TYPE_UINT, 8, //Inhibit the session being marked as idle - G_TYPE_INVALID, - G_TYPE_UINT, &cookie, - G_TYPE_INVALID); - - /* check the return value */ - if (! res) { - cookie = -1; - g_debug ("Inhibit method failed"); - } - - /* check the error value */ - if (error != NULL) { - g_debug ("Inhibit problem : %s", error->message); - g_error_free (error); - cookie = -1; - } - - g_object_unref (G_OBJECT (proxy)); - return cookie; -} - -static void -dbus_uninhibit_gsm (guint cookie) -{ - gboolean res; - GError *error = NULL; - DBusGProxy *proxy = NULL; - DBusGConnection *session_connection = NULL; - - if (cookie == guint(-1)) { - g_debug ("Invalid cookie"); - return; - } - - /* get the DBUS session connection */ - session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); - if (error) { - g_debug ("DBUS cannot connect : %s", error->message); - g_error_free (error); - return; - } - - /* get the proxy with gnome-session-manager */ - proxy = dbus_g_proxy_new_for_name (session_connection, - GSM_DBUS_SERVICE, - GSM_DBUS_PATH, - GSM_DBUS_INTERFACE); - if (proxy == NULL) { - g_debug ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE); - return; - } - - res = dbus_g_proxy_call (proxy, - "Uninhibit", - &error, - G_TYPE_UINT, cookie, - G_TYPE_INVALID, - G_TYPE_INVALID); - - /* check the return value */ - if (! res) { - g_debug ("Uninhibit method failed"); - } - - /* check the error value */ - if (error != NULL) { - g_debug ("Uninhibit problem : %s", error->message); - g_error_free (error); - cookie = -1; - } - g_object_unref (G_OBJECT (proxy)); -} -#endif - -void GtkSalFrame::StartPresentation( bool bStart ) -{ - setAutoLock( !bStart ); - - if( !getDisplay()->IsX11Display() ) - return; - -#if !GTK_CHECK_VERSION(3,0,0) - Display *pDisplay = GDK_DISPLAY_XDISPLAY( getGdkDisplay() ); - - int nTimeout, nInterval, bPreferBlanking, bAllowExposures; - XGetScreenSaver( pDisplay, &nTimeout, &nInterval, - &bPreferBlanking, &bAllowExposures ); -#endif - if( bStart ) - { -#if !GTK_CHECK_VERSION(3,0,0) - if ( nTimeout ) - { - m_nSavedScreenSaverTimeout = nTimeout; - XResetScreenSaver( pDisplay ); - XSetScreenSaver( pDisplay, 0, nInterval, - bPreferBlanking, bAllowExposures ); - } -#endif -#ifdef ENABLE_DBUS - m_nGSMCookie = dbus_inhibit_gsm(g_get_application_name(), "presentation", - widget_get_xid(m_pWindow)); -#endif - } - else - { -#if !GTK_CHECK_VERSION(3,0,0) - if( m_nSavedScreenSaverTimeout ) - XSetScreenSaver( pDisplay, m_nSavedScreenSaverTimeout, - nInterval, bPreferBlanking, - bAllowExposures ); -#endif - m_nSavedScreenSaverTimeout = 0; -#ifdef ENABLE_DBUS - dbus_uninhibit_gsm(m_nGSMCookie); -#endif - } -} - -void GtkSalFrame::SetAlwaysOnTop( bool bOnTop ) -{ - if( m_pWindow ) - gtk_window_set_keep_above( GTK_WINDOW( m_pWindow ), bOnTop ); -} - -void GtkSalFrame::ToTop( sal_uInt16 nFlags ) -{ - if( m_pWindow ) - { - if( isChild( false, true ) ) - gtk_widget_grab_focus( m_pWindow ); - else if( IS_WIDGET_MAPPED( m_pWindow ) ) - { - if( ! (nFlags & SAL_FRAME_TOTOP_GRABFOCUS_ONLY) ) - gtk_window_present( GTK_WINDOW(m_pWindow) ); - else - { -#if !GTK_CHECK_VERSION(3,0,0) - guint32 nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); -#else - guint32 nUserTime = GDK_CURRENT_TIME; -#endif - gdk_window_focus( widget_get_window(m_pWindow), nUserTime ); - } -#if !GTK_CHECK_VERSION(3,0,0) - /* need to do an XSetInputFocus here because - * gdk_window_focus will ask a EWMH compliant WM to put the focus - * to our window - which it of course won't since our input hint - * is set to false. - */ - if( (m_nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_FLOAT_FOCUSABLE)) ) - { - // sad but true: this can cause an XError, we need to catch that - // to do this we need to synchronize with the XServer - GetGenericData()->ErrorTrapPush(); - XSetInputFocus( getDisplay()->GetDisplay(), widget_get_xid(m_pWindow), RevertToParent, CurrentTime ); - // fdo#46687 - an XSync should not be necessary - but for some reason it is. - XSync( getDisplay()->GetDisplay(), False ); - GetGenericData()->ErrorTrapPop(); - } -#endif - } - else - { - if( nFlags & SAL_FRAME_TOTOP_RESTOREWHENMIN ) - gtk_window_present( GTK_WINDOW(m_pWindow) ); - } - } -} - -void GtkSalFrame::SetPointer( PointerStyle ePointerStyle ) -{ - if( m_pWindow && ePointerStyle != m_ePointerStyle ) - { - m_ePointerStyle = ePointerStyle; - GdkCursor *pCursor = getDisplay()->getCursor( ePointerStyle ); - gdk_window_set_cursor( widget_get_window(m_pWindow), pCursor ); - m_pCurrentCursor = pCursor; - - // #i80791# use grabPointer the same way as CaptureMouse, respective float grab - if( getDisplay()->MouseCaptured( this ) ) - grabPointer( true, false ); - else if( m_nFloats > 0 ) - grabPointer( true, true ); - } -} - -void GtkSalFrame::grabPointer( bool bGrab, bool bOwnerEvents ) -{ - static const char* pEnv = getenv( "SAL_NO_MOUSEGRABS" ); - if (pEnv && *pEnv) - return; - - if (!m_pWindow) - return; - - const int nMask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); - -#if GTK_CHECK_VERSION(3,0,0) - GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay()); - GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager); - if (bGrab) - gdk_device_grab(pPointer, widget_get_window(getMouseEventWidget()), GDK_OWNERSHIP_NONE, bOwnerEvents, (GdkEventMask) nMask, m_pCurrentCursor, GDK_CURRENT_TIME); - else - gdk_device_ungrab(pPointer, GDK_CURRENT_TIME); -#else - if( bGrab ) - { - bool bUseGdkGrab = true; - const std::list< SalFrame* >& rFrames = getDisplay()->getFrames(); - for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) - { - const GtkSalFrame* pFrame = static_cast< const GtkSalFrame* >(*it); - if( pFrame->m_bWindowIsGtkPlug ) - { - bUseGdkGrab = false; - break; - } - } - if( bUseGdkGrab ) - { - gdk_pointer_grab( widget_get_window( m_pWindow ), bOwnerEvents, - (GdkEventMask) nMask, NULL, m_pCurrentCursor, - GDK_CURRENT_TIME ); - } - else - { - // FIXME: for some unknown reason gdk_pointer_grab does not - // really produce owner events for GtkPlug windows - // the cause is yet unknown - - // this is of course a bad hack, especially as we cannot - // set the right cursor this way - XGrabPointer( getDisplay()->GetDisplay(), - widget_get_xid( m_pWindow ), - bOwnerEvents, - PointerMotionMask | ButtonPressMask | ButtonReleaseMask, - GrabModeAsync, - GrabModeAsync, - None, - None, - CurrentTime - ); - } - } - else - { - // Two GdkDisplays may be open - gdk_display_pointer_ungrab( getGdkDisplay(), GDK_CURRENT_TIME); - } -#endif -} - -void GtkSalFrame::grabKeyboard( bool bGrab ) -{ - if (!m_pWindow) - return; - -#if GTK_CHECK_VERSION(3,0,0) - GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay()); - GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager); - GdkDevice* pKeyboard = gdk_device_get_associated_device(pPointer); - if (bGrab) - { - gdk_device_grab(pKeyboard, widget_get_window(m_pWindow), GDK_OWNERSHIP_NONE, - true, (GdkEventMask)(GDK_KEY_PRESS | GDK_KEY_RELEASE), NULL, GDK_CURRENT_TIME); - } - else - { - gdk_device_ungrab(pKeyboard, GDK_CURRENT_TIME); - } -#else - if( bGrab ) - { - gdk_keyboard_grab(widget_get_window(m_pWindow), true, - GDK_CURRENT_TIME); - } - else - { - gdk_keyboard_ungrab(GDK_CURRENT_TIME); - } -#endif -} - -void GtkSalFrame::CaptureMouse( bool bCapture ) -{ - getDisplay()->CaptureMouse( bCapture ? this : NULL ); -} - -void GtkSalFrame::SetPointerPos( long nX, long nY ) -{ - GtkSalFrame* pFrame = this; - while( pFrame && pFrame->isChild( false, true ) ) - pFrame = pFrame->m_pParent; - if( ! pFrame ) - return; - - GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(pFrame->m_pWindow) ); - GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); - - /* when the application tries to center the mouse in the dialog the - * window isn't mapped already. So use coordinates relative to the root window. - */ - unsigned int nWindowLeft = maGeometry.nX + nX; - unsigned int nWindowTop = maGeometry.nY + nY; - - XWarpPointer( GDK_DISPLAY_XDISPLAY (pDisplay), None, - GDK_WINDOW_XID (gdk_screen_get_root_window( pScreen ) ), - 0, 0, 0, 0, nWindowLeft, nWindowTop); - // #i38648# ask for the next motion hint - gint x, y; - GdkModifierType mask; - gdk_window_get_pointer( widget_get_window(pFrame->m_pWindow) , &x, &y, &mask ); -} - -void GtkSalFrame::Flush() -{ -#if GTK_CHECK_VERSION(3,0,0) - gdk_display_flush( getGdkDisplay() ); -#else - XFlush (GDK_DISPLAY_XDISPLAY (getGdkDisplay())); -#endif -} - -void GtkSalFrame::Sync() -{ - gdk_display_sync( getGdkDisplay() ); -} - -#ifndef GDK_Open -#define GDK_Open 0x1008ff6b -#endif -#ifndef GDK_Paste -#define GDK_Paste 0x1008ff6d -#endif -#ifndef GDK_Copy -#define GDK_Copy 0x1008ff57 -#endif -#ifndef GDK_Cut -#define GDK_Cut 0x1008ff58 -#endif - -void GtkSalFrame::KeyCodeToGdkKey(const vcl::KeyCode& rKeyCode, - guint* pGdkKeyCode, GdkModifierType *pGdkModifiers) -{ - if ( pGdkKeyCode == NULL || pGdkModifiers == NULL ) - return; - - // Get GDK key modifiers - GdkModifierType nModifiers = (GdkModifierType) 0; - - if ( rKeyCode.IsShift() ) - nModifiers = (GdkModifierType) ( nModifiers | GDK_SHIFT_MASK ); - - if ( rKeyCode.IsMod1() ) - nModifiers = (GdkModifierType) ( nModifiers | GDK_CONTROL_MASK ); - - if ( rKeyCode.IsMod2() ) - nModifiers = (GdkModifierType) ( nModifiers | GDK_MOD1_MASK ); - - *pGdkModifiers = nModifiers; - - // Get GDK keycode. - guint nKeyCode = 0; - - guint nCode = rKeyCode.GetCode(); - - if ( nCode >= KEY_0 && nCode <= KEY_9 ) - nKeyCode = ( nCode - KEY_0 ) + GDK_0; - else if ( nCode >= KEY_A && nCode <= KEY_Z ) - nKeyCode = ( nCode - KEY_A ) + GDK_A; - else if ( nCode >= KEY_F1 && nCode <= KEY_F26 ) - nKeyCode = ( nCode - KEY_F1 ) + GDK_F1; - else - { - switch( nCode ) - { - case KEY_DOWN: nKeyCode = GDK_Down; break; - case KEY_UP: nKeyCode = GDK_Up; break; - case KEY_LEFT: nKeyCode = GDK_Left; break; - case KEY_RIGHT: nKeyCode = GDK_Right; break; - case KEY_HOME: nKeyCode = GDK_Home; break; - case KEY_END: nKeyCode = GDK_End; break; - case KEY_PAGEUP: nKeyCode = GDK_Page_Up; break; - case KEY_PAGEDOWN: nKeyCode = GDK_Page_Down; break; - case KEY_RETURN: nKeyCode = GDK_Return; break; - case KEY_ESCAPE: nKeyCode = GDK_Escape; break; - case KEY_TAB: nKeyCode = GDK_Tab; break; - case KEY_BACKSPACE: nKeyCode = GDK_BackSpace; break; - case KEY_SPACE: nKeyCode = GDK_space; break; - case KEY_INSERT: nKeyCode = GDK_Insert; break; - case KEY_DELETE: nKeyCode = GDK_Delete; break; - case KEY_ADD: nKeyCode = GDK_plus; break; - case KEY_SUBTRACT: nKeyCode = GDK_minus; break; - case KEY_MULTIPLY: nKeyCode = GDK_asterisk; break; - case KEY_DIVIDE: nKeyCode = GDK_slash; break; - case KEY_POINT: nKeyCode = GDK_period; break; - case KEY_COMMA: nKeyCode = GDK_comma; break; - case KEY_LESS: nKeyCode = GDK_less; break; - case KEY_GREATER: nKeyCode = GDK_greater; break; - case KEY_EQUAL: nKeyCode = GDK_equal; break; - case KEY_FIND: nKeyCode = GDK_Find; break; - case KEY_CONTEXTMENU: nKeyCode = GDK_Menu; break; - case KEY_HELP: nKeyCode = GDK_Help; break; - case KEY_UNDO: nKeyCode = GDK_Undo; break; - case KEY_REPEAT: nKeyCode = GDK_Redo; break; - case KEY_DECIMAL: nKeyCode = GDK_KP_Decimal; break; - case KEY_TILDE: nKeyCode = GDK_asciitilde; break; - case KEY_QUOTELEFT: nKeyCode = GDK_quoteleft; break; - case KEY_BRACKETLEFT: nKeyCode = GDK_bracketleft; break; - case KEY_BRACKETRIGHT: nKeyCode = GDK_bracketright; break; - case KEY_SEMICOLON: nKeyCode = GDK_semicolon; break; - case KEY_QUOTERIGHT: nKeyCode = GDK_quoteright; break; - - // Special cases - case KEY_COPY: nKeyCode = GDK_Copy; break; - case KEY_CUT: nKeyCode = GDK_Cut; break; - case KEY_PASTE: nKeyCode = GDK_Paste; break; - case KEY_OPEN: nKeyCode = GDK_Open; break; - } - } - - *pGdkKeyCode = nKeyCode; -} - -OUString GtkSalFrame::GetKeyName( sal_uInt16 nKeyCode ) -{ -#if !GTK_CHECK_VERSION(3,0,0) - return getDisplay()->GetKeyName( nKeyCode ); -#else - guint nGtkKeyCode; - GdkModifierType nGtkModifiers; - KeyCodeToGdkKey(nKeyCode, &nGtkKeyCode, &nGtkModifiers ); - - gchar* pName = gtk_accelerator_get_label(nGtkKeyCode, nGtkModifiers); - OUString aRet(pName, rtl_str_getLength(pName), RTL_TEXTENCODING_UTF8); - g_free(pName); - return aRet; -#endif -} - -GdkDisplay *GtkSalFrame::getGdkDisplay() -{ - return GetGtkSalData()->GetGdkDisplay(); -} - -GtkSalDisplay *GtkSalFrame::getDisplay() -{ - return GetGtkSalData()->GetGtkDisplay(); -} - -SalFrame::SalPointerState GtkSalFrame::GetPointerState() -{ - SalPointerState aState; - GdkScreen* pScreen; - gint x, y; - GdkModifierType aMask; - gdk_display_get_pointer( getGdkDisplay(), &pScreen, &x, &y, &aMask ); - aState.maPos = Point( x - maGeometry.nX, y - maGeometry.nY ); - aState.mnState = GetMouseModCode( aMask ); - return aState; -} - -KeyIndicatorState GtkSalFrame::GetIndicatorState() -{ -#if !GTK_CHECK_VERSION(3,0,0) - return GetGtkSalData()->GetGtkDisplay()->GetIndicatorState(); -#else - KeyIndicatorState nState = KeyIndicatorState::NONE; - - GdkKeymap *pKeyMap = gdk_keymap_get_for_display(getGdkDisplay()); - - if (gdk_keymap_get_caps_lock_state(pKeyMap)) - nState |= KeyIndicatorState::CAPSLOCK; - if (gdk_keymap_get_num_lock_state(pKeyMap)) - nState |= KeyIndicatorState::NUMLOCK; -#if GTK_CHECK_VERSION(3,18,0) - if (gdk_keymap_get_scroll_lock_state(pKeyMap)) - nState |= KeyIndicatorState::SCROLLLOCK; -#endif - return nState; -#endif -} - -void GtkSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode ) -{ -#if !GTK_CHECK_VERSION(3,0,0) - GetGtkSalData()->GetGtkDisplay()->SimulateKeyPress(nKeyCode); -#else - g_warning ("missing simulate keypress %d", nKeyCode); -#endif -} - -void GtkSalFrame::SetInputContext( SalInputContext* pContext ) -{ - if( ! pContext ) - return; - - if( ! (pContext->mnOptions & InputContextFlags::Text) ) - return; - - // create a new im context - if( ! m_pIMHandler ) - m_pIMHandler = new IMHandler( this ); -} - -void GtkSalFrame::EndExtTextInput( sal_uInt16 nFlags ) -{ - if( m_pIMHandler ) - m_pIMHandler->endExtTextInput( nFlags ); -} - -bool GtkSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , vcl::KeyCode& ) -{ - // not supported yet - return false; -} - -LanguageType GtkSalFrame::GetInputLanguage() -{ - return LANGUAGE_DONTKNOW; -} - -void GtkSalFrame::UpdateSettings( AllSettings& rSettings ) -{ - if( ! m_pWindow ) - return; - - GtkSalGraphics* pGraphics = static_cast(m_aGraphics[0].pGraphics); - bool bFreeGraphics = false; - if( ! pGraphics ) - { - pGraphics = static_cast(AcquireGraphics()); - if ( !pGraphics ) - { - SAL_WARN("vcl", "Could not get graphics - unable to update settings"); - return; - } - bFreeGraphics = true; - } - - pGraphics->updateSettings( rSettings ); - - if( bFreeGraphics ) - ReleaseGraphics( pGraphics ); -} - -void GtkSalFrame::Beep() -{ - gdk_display_beep( getGdkDisplay() ); -} - -const SystemEnvData* GtkSalFrame::GetSystemData() const -{ - return &m_aSystemData; -} - -void GtkSalFrame::SetParent( SalFrame* pNewParent ) -{ - if( m_pParent ) - m_pParent->m_aChildren.remove( this ); - m_pParent = static_cast(pNewParent); - if( m_pParent ) - m_pParent->m_aChildren.push_back( this ); - if( ! isChild() ) - gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), - (m_pParent && ! m_pParent->isChild(true,false)) ? GTK_WINDOW(m_pParent->m_pWindow) : NULL - ); -} - -#if !GTK_CHECK_VERSION(3,0,0) - -void GtkSalFrame::createNewWindow( ::Window aNewParent, bool bXEmbed, SalX11Screen nXScreen ) -{ - bool bWasVisible = m_pWindow && IS_WIDGET_MAPPED(m_pWindow); - if( bWasVisible ) - Show( false ); - - if( (int)nXScreen.getXScreen() >= getDisplay()->GetXScreenCount() ) - nXScreen = m_nXScreen; - - SystemParentData aParentData; - aParentData.aWindow = aNewParent; - aParentData.bXEmbedSupport = bXEmbed; - if( aNewParent == None ) - { - aNewParent = getDisplay()->GetRootWindow(nXScreen); - aParentData.aWindow = None; - aParentData.bXEmbedSupport = false; - } - else - { - // is new parent a root window ? - Display* pDisp = getDisplay()->GetDisplay(); - int nScreens = getDisplay()->GetXScreenCount(); - for( int i = 0; i < nScreens; i++ ) - { - if( aNewParent == RootWindow( pDisp, i ) ) - { - nXScreen = SalX11Screen( i ); - aParentData.aWindow = None; - aParentData.bXEmbedSupport = false; - break; - } - } - } - - // free xrender resources - for( unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); i++ ) - if( m_aGraphics[i].bInUse ) - m_aGraphics[i].pGraphics->SetDrawable( None, m_nXScreen ); - - // first deinit frame - if( m_pIMHandler ) - { - delete m_pIMHandler; - m_pIMHandler = NULL; - } - if( m_pRegion ) - { - gdk_region_destroy( m_pRegion ); - } - - GtkWidget *pEventWidget = getMouseEventWidget(); - for (auto handler_id : m_aMouseSignalIds) - g_signal_handler_disconnect(G_OBJECT(pEventWidget), handler_id); - if( m_pFixedContainer ) - gtk_widget_destroy( GTK_WIDGET(m_pFixedContainer) ); - if( m_pEventBox ) - gtk_widget_destroy( GTK_WIDGET(m_pEventBox) ); - if( m_pWindow ) - gtk_widget_destroy( m_pWindow ); - if( m_pForeignParent ) - g_object_unref( G_OBJECT( m_pForeignParent ) ); - if( m_pForeignTopLevel ) - g_object_unref( G_OBJECT( m_pForeignTopLevel ) ); - - // init new window - m_bDefaultPos = m_bDefaultSize = false; - if( aParentData.aWindow != None ) - { - m_nStyle |= SAL_FRAME_STYLE_PLUG; - Init( &aParentData ); - } - else - { - m_nStyle &= ~SAL_FRAME_STYLE_PLUG; - Init( (m_pParent && m_pParent->m_nXScreen == m_nXScreen) ? m_pParent : NULL, m_nStyle ); - } - - // update graphics - for( unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); i++ ) - { - if( m_aGraphics[i].bInUse ) - { - m_aGraphics[i].pGraphics->SetDrawable( widget_get_xid(m_pWindow), m_nXScreen ); - m_aGraphics[i].pGraphics->SetWindow( m_pWindow ); - } - } - - if( ! m_aTitle.isEmpty() ) - SetTitle( m_aTitle ); - - if( bWasVisible ) - Show( true ); - - std::list< GtkSalFrame* > aChildren = m_aChildren; - m_aChildren.clear(); - for( std::list< GtkSalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it ) - (*it)->createNewWindow( None, false, m_nXScreen ); - - // FIXME: SalObjects -} -#endif - -bool GtkSalFrame::SetPluginParent( SystemParentData* pSysParent ) -{ -#if !GTK_CHECK_VERSION(3,0,0) - GetGenericData()->ErrorTrapPush(); // permanantly ignore unruly children's errors - createNewWindow( pSysParent->aWindow, (pSysParent->nSize > sizeof(long)) && pSysParent->bXEmbedSupport, m_nXScreen ); - return true; -#else - (void)pSysParent; - //FIXME: no SetPluginParent impl. for gtk3 - return false; -#endif -} - -void GtkSalFrame::ResetClipRegion() -{ - if( m_pWindow ) - gdk_window_shape_combine_region( widget_get_window( m_pWindow ), NULL, 0, 0 ); -} - -void GtkSalFrame::BeginSetClipRegion( sal_uLong ) -{ -#if GTK_CHECK_VERSION(3,0,0) - if( m_pRegion ) - cairo_region_destroy( m_pRegion ); - m_pRegion = cairo_region_create(); -#else - if( m_pRegion ) - gdk_region_destroy( m_pRegion ); - m_pRegion = gdk_region_new(); -#endif -} - -void GtkSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) -{ - if( m_pRegion ) - { - GdkRectangle aRect; - aRect.x = nX; - aRect.y = nY; - aRect.width = nWidth; - aRect.height = nHeight; -#if GTK_CHECK_VERSION(3,0,0) - cairo_region_union_rectangle( m_pRegion, &aRect ); -#else - gdk_region_union_with_rect( m_pRegion, &aRect ); -#endif - } -} - -void GtkSalFrame::EndSetClipRegion() -{ - if( m_pWindow && m_pRegion ) - gdk_window_shape_combine_region( widget_get_window(m_pWindow), m_pRegion, 0, 0 ); -} - -#if !GTK_CHECK_VERSION(3,0,0) -bool GtkSalFrame::Dispatch( const XEvent* pEvent ) -{ - bool bContinueDispatch = true; - - if( pEvent->type == PropertyNotify ) - { - vcl_sal::WMAdaptor* pAdaptor = getDisplay()->getWMAdaptor(); - Atom nDesktopAtom = pAdaptor->getAtom( vcl_sal::WMAdaptor::NET_WM_DESKTOP ); - if( pEvent->xproperty.atom == nDesktopAtom && - pEvent->xproperty.state == PropertyNewValue ) - { - m_nWorkArea = pAdaptor->getWindowWorkArea( widget_get_xid(m_pWindow) ); - } - } - else if( pEvent->type == ConfigureNotify ) - { - if( m_pForeignParent && pEvent->xconfigure.window == m_aForeignParentWindow ) - { - bContinueDispatch = false; - gtk_window_resize( GTK_WINDOW(m_pWindow), pEvent->xconfigure.width, pEvent->xconfigure.height ); - if( ( sal::static_int_cast< int >(maGeometry.nWidth) != - pEvent->xconfigure.width ) || - ( sal::static_int_cast< int >(maGeometry.nHeight) != - pEvent->xconfigure.height ) ) - { - maGeometry.nWidth = pEvent->xconfigure.width; - maGeometry.nHeight = pEvent->xconfigure.height; - setMinMaxSize(); - getDisplay()->SendInternalEvent( this, NULL, SALEVENT_RESIZE ); - } - } - else if( m_pForeignTopLevel && pEvent->xconfigure.window == m_aForeignTopLevelWindow ) - { - bContinueDispatch = false; - // update position - int x = 0, y = 0; - ::Window aChild; - XTranslateCoordinates( getDisplay()->GetDisplay(), - widget_get_xid(m_pWindow), - getDisplay()->GetRootWindow( getDisplay()->GetDefaultXScreen() ), - 0, 0, - &x, &y, - &aChild ); - if( x != maGeometry.nX || y != maGeometry.nY ) - { - maGeometry.nX = x; - maGeometry.nY = y; - getDisplay()->SendInternalEvent( this, NULL, SALEVENT_MOVE ); - } - } - } - else if( pEvent->type == ClientMessage && - pEvent->xclient.message_type == getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED ) && - pEvent->xclient.window == widget_get_xid(m_pWindow) && - m_bWindowIsGtkPlug - ) - { - // FIXME: this should not be necessary, GtkPlug should do this - // transparently for us - if( pEvent->xclient.data.l[1] == 1 || // XEMBED_WINDOW_ACTIVATE - pEvent->xclient.data.l[1] == 2 // XEMBED_WINDOW_DEACTIVATE - ) - { - GdkEventFocus aEvent; - aEvent.type = GDK_FOCUS_CHANGE; - aEvent.window = widget_get_window( m_pWindow ); - aEvent.send_event = gint8(TRUE); - aEvent.in = gint16(pEvent->xclient.data.l[1] == 1); - signalFocus( m_pWindow, &aEvent, this ); - } - } - - return bContinueDispatch; -} -#endif - -gboolean GtkSalFrame::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - - SalMouseEvent aEvent; - sal_uInt16 nEventType = 0; - switch( pEvent->type ) - { - case GDK_BUTTON_PRESS: - nEventType = SALEVENT_MOUSEBUTTONDOWN; - break; - case GDK_BUTTON_RELEASE: - nEventType = SALEVENT_MOUSEBUTTONUP; - break; - default: - return false; - } - switch( pEvent->button ) - { - case 1: aEvent.mnButton = MOUSE_LEFT; break; - case 2: aEvent.mnButton = MOUSE_MIDDLE; break; - case 3: aEvent.mnButton = MOUSE_RIGHT; break; - default: return false; - } - aEvent.mnTime = pEvent->time; - aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; - aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; - aEvent.mnCode = GetMouseModCode( pEvent->state ); - - bool bClosePopups = false; - if( pEvent->type == GDK_BUTTON_PRESS && - (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) == 0 - ) - { - if( m_nFloats > 0 ) - { - // close popups if user clicks outside our application - gint x, y; - bClosePopups = (gdk_display_get_window_at_pointer( GtkSalFrame::getGdkDisplay(), &x, &y ) == NULL); - } - /* #i30306# release implicit pointer grab if no popups are open; else - * Drag cannot grab the pointer and will fail. - */ - if( m_nFloats < 1 || bClosePopups ) - gdk_display_pointer_ungrab( GtkSalFrame::getGdkDisplay(), GDK_CURRENT_TIME ); - } - - if( pThis->m_bWindowIsGtkPlug && - pEvent->type == GDK_BUTTON_PRESS && - pEvent->button == 1 ) - { - pThis->askForXEmbedFocus( pEvent->time ); - } - - // --- RTL --- (mirror mouse pos) - if( AllSettings::GetLayoutRTL() ) - aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; - - vcl::DeletionListener aDel( pThis ); - - pThis->CallCallback( nEventType, &aEvent ); - - if( ! aDel.isDeleted() ) - { - if( bClosePopups ) - { - ImplSVData* pSVData = ImplGetSVData(); - if ( pSVData->maWinData.mpFirstFloat ) - { - static const char* pEnv = getenv( "SAL_FLOATWIN_NOAPPFOCUSCLOSE" ); - if ( !(pSVData->maWinData.mpFirstFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose) && !(pEnv && *pEnv) ) - pSVData->maWinData.mpFirstFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); - } - } - - if( ! aDel.isDeleted() ) - { - int frame_x = (int)(pEvent->x_root - pEvent->x); - int frame_y = (int)(pEvent->y_root - pEvent->y); - if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) - { - pThis->maGeometry.nX = frame_x; - pThis->maGeometry.nY = frame_y; - pThis->CallCallback( SALEVENT_MOVE, NULL ); - } - } - } - - return true; -} - -#if GTK_CHECK_VERSION(3,0,0) -void GtkSalFrame::signalFlagsChanged( GtkWidget* , GtkStateFlags state, gpointer frame ) -{ - //TO-DO: This isn't as helpful as I'd like it to be. The color selector puts the main - //windows into the backdrop, disabling everything, and the floating navigator window - //is also problematic. - GtkSalFrame* pThis = static_cast(frame); - - bool bOldBackDrop = state & GTK_STATE_FLAG_BACKDROP; - bool bNewBackDrop = (gtk_widget_get_state_flags(GTK_WIDGET(pThis->m_pWindow)) & GTK_STATE_FLAG_BACKDROP); - if (bNewBackDrop && !bOldBackDrop) - { - pThis->GetWindow()->Disable(); - } - else if (bOldBackDrop && !bNewBackDrop) - { - pThis->GetWindow()->Enable(); - } -} -#endif - -gboolean GtkSalFrame::signalScroll( GtkWidget*, GdkEvent* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - GdkEventScroll* pSEvent = reinterpret_cast(pEvent); - -#if GTK_CHECK_VERSION(3,0,0) - // gnome#726878 check for duplicate legacy scroll event - if (pSEvent->direction != GDK_SCROLL_SMOOTH && - pThis->m_nLastScrollEventTime == pSEvent->time) - { - return true; - } -#endif - - SalWheelMouseEvent aEvent; - - aEvent.mnTime = pSEvent->time; - aEvent.mnX = (sal_uLong)pSEvent->x; - aEvent.mnY = (sal_uLong)pSEvent->y; - aEvent.mnCode = GetMouseModCode( pSEvent->state ); - aEvent.mnScrollLines = 3; - - switch (pSEvent->direction) - { -#if GTK_CHECK_VERSION(3,0,0) - case GDK_SCROLL_SMOOTH: - { - double delta_x, delta_y; - gdk_event_get_scroll_deltas(pEvent, &delta_x, &delta_y); - //pick the bigger one I guess - aEvent.mbHorz = fabs(delta_x) > fabs(delta_y); - if (aEvent.mbHorz) - aEvent.mnDelta = -delta_x; - else - aEvent.mnDelta = -delta_y; - aEvent.mnScrollLines = 1; - pThis->m_nLastScrollEventTime = pSEvent->time; - break; - } -#endif - case GDK_SCROLL_UP: - aEvent.mnDelta = 120; - aEvent.mbHorz = false; - break; - case GDK_SCROLL_DOWN: - aEvent.mnDelta = -120; - aEvent.mbHorz = false; - break; - case GDK_SCROLL_LEFT: - aEvent.mbHorz = true; - aEvent.mnDelta = 120; - break; - case GDK_SCROLL_RIGHT: - aEvent.mnDelta = -120; - aEvent.mbHorz = true; - break; - }; - - aEvent.mnNotchDelta = aEvent.mnDelta < 0 ? -1 : 1; - - // --- RTL --- (mirror mouse pos) - if( AllSettings::GetLayoutRTL() ) - aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; - - pThis->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); - - return true; -} - -#if GTK_CHECK_VERSION(3,14,0) -void GtkSalFrame::gestureSwipe(GtkGestureSwipe* gesture, gdouble velocity_x, gdouble velocity_y, gpointer frame) -{ - gdouble x, y; - GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); - //I feel I want the first point of the sequence, not the last point which - //the docs say this gives, but for the moment assume we start and end - //within the same vcl window - if (gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y)) - { - GtkSalFrame* pThis = static_cast(frame); - - SalSwipeEvent aEvent; - aEvent.mnVelocityX = velocity_x; - aEvent.mnVelocityY = velocity_y; - aEvent.mnX = x; - aEvent.mnY = y; - - pThis->CallCallback(SALEVENT_SWIPE, &aEvent); - } -} - -void GtkSalFrame::gestureLongPress(GtkGestureLongPress* gesture, gpointer frame) -{ - GtkSalFrame* pThis = static_cast(frame); - - if(pThis) - { - SalLongPressEvent aEvent; - - gdouble x, y; - GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); - gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y); - aEvent.mnX = x; - aEvent.mnY = y; - - pThis->CallCallback(SALEVENT_LONGPRESS, &aEvent); - } -} - -#endif - -gboolean GtkSalFrame::signalMotion( GtkWidget*, GdkEventMotion* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - - SalMouseEvent aEvent; - aEvent.mnTime = pEvent->time; - aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; - aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; - aEvent.mnCode = GetMouseModCode( pEvent->state ); - aEvent.mnButton = 0; - - // --- RTL --- (mirror mouse pos) - if( AllSettings::GetLayoutRTL() ) - aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; - - vcl::DeletionListener aDel( pThis ); - - pThis->CallCallback( SALEVENT_MOUSEMOVE, &aEvent ); - - if( ! aDel.isDeleted() ) - { - int frame_x = (int)(pEvent->x_root - pEvent->x); - int frame_y = (int)(pEvent->y_root - pEvent->y); - if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) - { - pThis->maGeometry.nX = frame_x; - pThis->maGeometry.nY = frame_y; - pThis->CallCallback( SALEVENT_MOVE, NULL ); - } - - if( ! aDel.isDeleted() ) - { - // ask for the next hint - gint x, y; - GdkModifierType mask; - gdk_window_get_pointer( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &x, &y, &mask ); - } - } - - return true; -} - -gboolean GtkSalFrame::signalCrossing( GtkWidget*, GdkEventCrossing* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - SalMouseEvent aEvent; - aEvent.mnTime = pEvent->time; - aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; - aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; - aEvent.mnCode = GetMouseModCode( pEvent->state ); - aEvent.mnButton = 0; - - pThis->CallCallback( (pEvent->type == GDK_ENTER_NOTIFY) ? SALEVENT_MOUSEMOVE : SALEVENT_MOUSELEAVE, &aEvent ); - - return true; -} - -#if GTK_CHECK_VERSION(3,0,0) - -cairo_t* GtkSalFrame::getCairoContext() const -{ - cairo_t* cr = SvpSalGraphics::createCairoContext(m_aFrame); - assert(cr); - return cr; -} - -void GtkSalFrame::damaged (const basegfx::B2IBox& rDamageRect) -{ -#if OSL_DEBUG_LEVEL > 1 - long long area = rDamageRect.getWidth() * rDamageRect.getHeight(); - if( area > 32 * 1024 ) - { - fprintf( stderr, "bitmap damaged %d %d (%dx%d) area %lld widget\n", - (int) rDamageRect.getMinX(), - (int) rDamageRect.getMinY(), - (int) rDamageRect.getWidth(), - (int) rDamageRect.getHeight(), - area ); - } -#endif - - if (dumpframes) - { - static int frame; - OString tmp("/tmp/frame" + OString::number(frame++) + ".png"); - cairo_t* cr = getCairoContext(); - cairo_surface_write_to_png(cairo_get_target(cr), tmp.getStr()); - cairo_destroy(cr); - } - - gtk_widget_queue_draw_area(GTK_WIDGET(m_pFixedContainer), - rDamageRect.getMinX(), - rDamageRect.getMinY(), - rDamageRect.getWidth(), - rDamageRect.getHeight()); -} - -// blit our backing basebmp buffer to the target cairo context cr -gboolean GtkSalFrame::signalDraw( GtkWidget*, cairo_t *cr, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - - cairo_save(cr); - - cairo_t* source = pThis->getCairoContext(); - cairo_surface_t *pSurface = cairo_get_target(source); - - cairo_set_operator( cr, CAIRO_OPERATOR_OVER ); - cairo_set_source_surface(cr, pSurface, 0, 0); - cairo_paint(cr); - - cairo_destroy(source); - - cairo_restore(cr); - - cairo_surface_flush(cairo_get_target(cr)); - - return false; -} - -void GtkSalFrame::sizeAllocated(GtkWidget*, GdkRectangle *pAllocation, gpointer frame) -{ - GtkSalFrame* pThis = static_cast(frame); - - bool bSized = false; - - if( pThis->m_bFullscreen || (pThis->m_nStyle & (SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_PLUG)) == SAL_FRAME_STYLE_SIZEABLE ) - { - if( pAllocation->width != (int)pThis->maGeometry.nWidth || pAllocation->height != (int)pThis->maGeometry.nHeight ) - { - bSized = true; - pThis->maGeometry.nWidth = pAllocation->width; - pThis->maGeometry.nHeight = pAllocation->height; - } - } - - if( bSized ) - { - pThis->AllocateFrame(); - pThis->CallCallback( SALEVENT_RESIZE, nullptr ); - pThis->TriggerPaintEvent(); - } -} - -gboolean GtkSalFrame::signalConfigure(GtkWidget*, GdkEventConfigure* pEvent, gpointer frame) -{ - GtkSalFrame* pThis = static_cast(frame); - pThis->m_bPaintsBlocked = false; - - bool bMoved = false; - int x = pEvent->x, y = pEvent->y; - - /* HACK: during sizing/moving a toolbar pThis->maGeometry is actually - * already exact; even worse: due to the asynchronicity of configure - * events the borderwindow which would evaluate this event - * would size/move based on wrong data if we would actually evaluate - * this event. So let's swallow it. - */ - if( (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) && - GtkSalFrame::getDisplay()->GetCaptureFrame() == pThis ) - return false; - - /* #i31785# claims we cannot trust the x,y members of the event; - * they are e.g. not set correctly on maximize/demaximize; - * yet the gdkdisplay-x11.c code handling configure_events has - * done this XTranslateCoordinates work since the day ~zero. - */ - if( x != pThis->maGeometry.nX || y != pThis->maGeometry.nY ) - { - bMoved = true; - pThis->maGeometry.nX = x; - pThis->maGeometry.nY = y; - } - - // update decoration hints - if( ! (pThis->m_nStyle & SAL_FRAME_STYLE_PLUG) ) - { - GdkRectangle aRect; - gdk_window_get_frame_extents( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &aRect ); - pThis->maGeometry.nTopDecoration = y - aRect.y; - pThis->maGeometry.nBottomDecoration = aRect.y + aRect.height - y - pEvent->height; - pThis->maGeometry.nLeftDecoration = x - aRect.x; - pThis->maGeometry.nRightDecoration = aRect.x + aRect.width - x - pEvent->width; - } - else - { - pThis->maGeometry.nTopDecoration = - pThis->maGeometry.nBottomDecoration = - pThis->maGeometry.nLeftDecoration = - pThis->maGeometry.nRightDecoration = 0; - } - - pThis->updateScreenNumber(); - - if (bMoved) - pThis->CallCallback(SALEVENT_MOVE, nullptr); - - return false; -} -#else -gboolean GtkSalFrame::signalExpose( GtkWidget*, GdkEventExpose* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - pThis->m_bPaintsBlocked = false; - - struct SalPaintEvent aEvent( pEvent->area.x, pEvent->area.y, pEvent->area.width, pEvent->area.height ); - - pThis->CallCallback( SALEVENT_PAINT, &aEvent ); - - return false; -} - -gboolean GtkSalFrame::signalConfigure( GtkWidget*, GdkEventConfigure* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - pThis->m_bPaintsBlocked = false; - - bool bMoved = false, bSized = false; - int x = pEvent->x, y = pEvent->y; - - /* HACK: during sizing/moving a toolbar pThis->maGeometry is actually - * already exact; even worse: due to the asynchronicity of configure - * events the borderwindow which would evaluate this event - * would size/move based on wrong data if we would actually evaluate - * this event. So let's swallow it. - */ - if( (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) && - GtkSalFrame::getDisplay()->GetCaptureFrame() == pThis ) - return false; - - /* #i31785# claims we cannot trust the x,y members of the event; - * they are e.g. not set correctly on maximize/demaximize; - * yet the gdkdisplay-x11.c code handling configure_events has - * done this XTranslateCoordinates work since the day ~zero. - */ - if( x != pThis->maGeometry.nX || y != pThis->maGeometry.nY ) - { - bMoved = true; - pThis->maGeometry.nX = x; - pThis->maGeometry.nY = y; - } - /* #i86302# - * for non sizeable windows we set the min and max hint for the window manager to - * achieve correct sizing. However this is asynchronous and e.g. on Compiz - * it sometimes happens that the window gets resized to another size (some default) - * if we update the size here, subsequent setMinMaxSize will use this wrong size - * - which is not good since the window manager will now size the window back to this - * wrong size at some point. - */ - if( pThis->m_bFullscreen || (pThis->m_nStyle & (SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_PLUG)) == SAL_FRAME_STYLE_SIZEABLE ) - { - if( pEvent->width != (int)pThis->maGeometry.nWidth || pEvent->height != (int)pThis->maGeometry.nHeight ) - { - bSized = true; - pThis->maGeometry.nWidth = pEvent->width; - pThis->maGeometry.nHeight = pEvent->height; - } - } - - // update decoration hints - if( ! (pThis->m_nStyle & SAL_FRAME_STYLE_PLUG) ) - { - GdkRectangle aRect; - gdk_window_get_frame_extents( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &aRect ); - pThis->maGeometry.nTopDecoration = y - aRect.y; - pThis->maGeometry.nBottomDecoration = aRect.y + aRect.height - y - pEvent->height; - pThis->maGeometry.nLeftDecoration = x - aRect.x; - pThis->maGeometry.nRightDecoration = aRect.x + aRect.width - x - pEvent->width; - } - else - { - pThis->maGeometry.nTopDecoration = - pThis->maGeometry.nBottomDecoration = - pThis->maGeometry.nLeftDecoration = - pThis->maGeometry.nRightDecoration = 0; - } - - pThis->updateScreenNumber(); - if( bSized ) - pThis->AllocateFrame(); - - if( bMoved && bSized ) - pThis->CallCallback( SALEVENT_MOVERESIZE, nullptr ); - else if( bMoved ) - pThis->CallCallback( SALEVENT_MOVE, nullptr ); - else if( bSized ) - pThis->CallCallback( SALEVENT_RESIZE, nullptr ); - - if (bSized) - pThis->TriggerPaintEvent(); - return false; -} - -#endif // GTK_CHECK_VERSION(3,0,0) - -void GtkSalFrame::TriggerPaintEvent() -{ - //Under gtk2 we can basically paint directly into the XWindow and on - //additional "expose-event" events we can re-render the missing pieces - // - //Under gtk3 we have to keep our own buffer up to date and flush it into - //the given cairo context on "draw". So we emit a paint event on - //opportune resize trigger events to initially fill our backbuffer and then - //keep it up to date with our direct paints and tell gtk those regions - //have changed and then blit them into the provided cairo context when - //we get the "draw" - // - //The other alternative was to always paint everything on "draw", but - //that duplicates the amount of drawing and is hideously slow -#if GTK_CHECK_VERSION(3,0,0) - SAL_INFO("vcl.gtk3", "force painting" << 0 << "," << 0 << " " << maGeometry.nWidth << "x" << maGeometry.nHeight); - SalPaintEvent aPaintEvt(0, 0, maGeometry.nWidth, maGeometry.nHeight, true); - CallCallback(SALEVENT_PAINT, &aPaintEvt); - gtk_widget_queue_draw(GTK_WIDGET(m_pFixedContainer)); -#endif -} - -gboolean GtkSalFrame::signalFocus( GtkWidget*, GdkEventFocus* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - - SalGenericInstance *pSalInstance = - static_cast< SalGenericInstance* >(GetSalData()->m_pInstance); - - // check if printers have changed (analogous to salframe focus handler) - pSalInstance->updatePrinterUpdate(); - - if( !pEvent->in ) - { - pThis->m_nKeyModifiers = 0; - pThis->m_bSendModChangeOnRelease = false; - } - - if( pThis->m_pIMHandler ) - pThis->m_pIMHandler->focusChanged( pEvent->in ); - - // ask for changed printers like generic implementation - if( pEvent->in && pSalInstance->isPrinterInit() ) - pSalInstance->updatePrinterUpdate(); - - // FIXME: find out who the hell steals the focus from our frame - // while we have the pointer grabbed, this should not come from - // the window manager. Is this an event that was still queued ? - // The focus does not seem to get set inside our process - - // in the meantime do not propagate focus get/lose if floats are open - if( m_nFloats == 0 ) - pThis->CallCallback( pEvent->in ? SALEVENT_GETFOCUS : SALEVENT_LOSEFOCUS, NULL ); - - return false; -} - -#if !GTK_CHECK_VERSION(3,8,0) -static OString getDisplayString() -{ - int nParams = rtl_getAppCommandArgCount(); - OUString aParam; - for( int i = 0; i < nParams; i++ ) - { - rtl_getAppCommandArg( i, &aParam.pData ); - if( i < nParams-1 && (aParam == "-display" || aParam == "--display" ) ) - { - rtl_getAppCommandArg( i+1, &aParam.pData ); - return OUStringToOString( aParam, osl_getThreadTextEncoding() ); - } - } - return OString(); -} -#endif - -gboolean GtkSalFrame::signalMap( GtkWidget *pWidget, GdkEvent*, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - -#if !GTK_CHECK_VERSION(3,8,0) - //Spawn off a helper program that will attempt to set this fullscreen - //window to span all displays. - if (pThis->m_bFullscreen && pThis->m_bSpanMonitorsWhenFullscreen) - { - GdkWindow* gdkwin = widget_get_window(pThis->m_pWindow); - if (gdkwin) - { - OUString sProgramURL( "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/xid-fullscreen-on-all-monitors"); - rtl::Bootstrap::expandMacros(sProgramURL); - OUString sProgram; - if (osl::FileBase::getSystemPathFromFileURL(sProgramURL, sProgram) == osl::File::E_None) - { - OString sFinalProgram(OUStringToOString(sProgram, osl_getThreadTextEncoding()) - + " " + OString::number((int)GDK_WINDOW_XID(gdkwin))); - OString sDisplay(getDisplayString()); - if (!sDisplay.isEmpty()) - { - sFinalProgram += "--display " + sDisplay; - } - int returnValue = system(sFinalProgram.getStr()); - (void)returnValue; - } - } - } -#endif - - bool bSetFocus = pThis->m_bSetFocusOnMap; - pThis->m_bSetFocusOnMap = false; - -#if !GTK_CHECK_VERSION(3,0,0) - if( bSetFocus ) - { - GetGenericData()->ErrorTrapPush(); - XSetInputFocus( GtkSalFrame::getDisplay()->GetDisplay(), - widget_get_xid(pWidget), - RevertToParent, CurrentTime ); - XSync( GtkSalFrame::getDisplay()->GetDisplay(), False ); - GetGenericData()->ErrorTrapPop(); - } -#else - (void)pWidget; (void)bSetFocus; - //FIXME: no set input focus ... -#endif - - pThis->CallCallback( SALEVENT_RESIZE, NULL ); - pThis->TriggerPaintEvent(); - - return false; -} - -gboolean GtkSalFrame::signalUnmap( GtkWidget*, GdkEvent*, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - - pThis->CallCallback( SALEVENT_RESIZE, NULL ); - - return false; -} - -gboolean GtkSalFrame::signalKey( GtkWidget*, GdkEventKey* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - - vcl::DeletionListener aDel( pThis ); - - if( pThis->m_pIMHandler ) - { - if( pThis->m_pIMHandler->handleKeyEvent( pEvent ) ) - return true; - } - - // handle modifiers - if( pEvent->keyval == GDK_Shift_L || pEvent->keyval == GDK_Shift_R || - pEvent->keyval == GDK_Control_L || pEvent->keyval == GDK_Control_R || - pEvent->keyval == GDK_Alt_L || pEvent->keyval == GDK_Alt_R || - pEvent->keyval == GDK_Meta_L || pEvent->keyval == GDK_Meta_R || - pEvent->keyval == GDK_Super_L || pEvent->keyval == GDK_Super_R ) - { - SalKeyModEvent aModEvt; - - sal_uInt16 nModCode = GetKeyModCode( pEvent->state ); - - aModEvt.mnModKeyCode = 0; // emit no MODKEYCHANGE events - if( pEvent->type == GDK_KEY_PRESS && !pThis->m_nKeyModifiers ) - pThis->m_bSendModChangeOnRelease = true; - - else if( pEvent->type == GDK_KEY_RELEASE && - pThis->m_bSendModChangeOnRelease ) - { - aModEvt.mnModKeyCode = pThis->m_nKeyModifiers; - pThis->m_nKeyModifiers = 0; - } - - sal_uInt16 nExtModMask = 0; - sal_uInt16 nModMask = 0; - // pressing just the ctrl key leads to a keysym of XK_Control but - // the event state does not contain ControlMask. In the release - // event its the other way round: it does contain the Control mask. - // The modifier mode therefore has to be adapted manually. - switch( pEvent->keyval ) - { - case GDK_Control_L: - nExtModMask = MODKEY_LMOD1; - nModMask = KEY_MOD1; - break; - case GDK_Control_R: - nExtModMask = MODKEY_RMOD1; - nModMask = KEY_MOD1; - break; - case GDK_Alt_L: - nExtModMask = MODKEY_LMOD2; - nModMask = KEY_MOD2; - break; - case GDK_Alt_R: - nExtModMask = MODKEY_RMOD2; - nModMask = KEY_MOD2; - break; - case GDK_Shift_L: - nExtModMask = MODKEY_LSHIFT; - nModMask = KEY_SHIFT; - break; - case GDK_Shift_R: - nExtModMask = MODKEY_RSHIFT; - nModMask = KEY_SHIFT; - break; - // Map Meta/Super to MOD3 modifier on all Unix systems - // except Mac OS X - case GDK_Meta_L: - case GDK_Super_L: - nExtModMask = MODKEY_LMOD3; - nModMask = KEY_MOD3; - break; - case GDK_Meta_R: - case GDK_Super_R: - nExtModMask = MODKEY_RMOD3; - nModMask = KEY_MOD3; - break; - } - if( pEvent->type == GDK_KEY_RELEASE ) - { - nModCode &= ~nModMask; - pThis->m_nKeyModifiers &= ~nExtModMask; - } - else - { - nModCode |= nModMask; - pThis->m_nKeyModifiers |= nExtModMask; - } - - aModEvt.mnCode = nModCode; - aModEvt.mnTime = pEvent->time; - - pThis->CallCallback( SALEVENT_KEYMODCHANGE, &aModEvt ); - - } - else - { - pThis->doKeyCallback( pEvent->state, - pEvent->keyval, - pEvent->hardware_keycode, - pEvent->group, - pEvent->time, - sal_Unicode(gdk_keyval_to_unicode( pEvent->keyval )), - (pEvent->type == GDK_KEY_PRESS), - false ); - if( ! aDel.isDeleted() ) - pThis->m_bSendModChangeOnRelease = false; - } - - if( !aDel.isDeleted() && pThis->m_pIMHandler ) - pThis->m_pIMHandler->updateIMSpotLocation(); - - return true; -} - -gboolean GtkSalFrame::signalDelete( GtkWidget*, GdkEvent*, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - -#if GTK_CHECK_VERSION(3,0,0) - //If we went into the backdrop we disabled the toplevel window, if we - //receive a delete here, re-enable so we can process it - bool bBackDrop = (gtk_widget_get_state_flags(GTK_WIDGET(pThis->m_pWindow)) & GTK_STATE_FLAG_BACKDROP); - if (bBackDrop) - pThis->GetWindow()->Enable(); -#endif - - pThis->CallCallback( SALEVENT_CLOSE, NULL ); - - return true; -} - -void GtkSalFrame::signalStyleSet( GtkWidget*, GtkStyle* pPrevious, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - - // every frame gets an initial style set on creation - // do not post these as the whole application tends to - // redraw itself to adjust to the new style - // where there IS no new style resulting in tremendous unnecessary flickering - if( pPrevious != NULL ) - { - // signalStyleSet does NOT usually have the gdk lock - // so post user event to safely dispatch the SALEVENT_SETTINGSCHANGED - // note: settings changed for multiple frames is avoided in winproc.cxx ImplHandleSettings - GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_SETTINGSCHANGED ); - GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_FONTCHANGED ); - } - -#if !GTK_CHECK_VERSION(3,0,0) - /* #i64117# gtk sets a nice background pixmap - * but we actually don't really want that, so save - * some time on the Xserver as well as prevent - * some paint issues - */ - GdkWindow* pWin = widget_get_window(GTK_WIDGET(pThis->getWindow())); - if( pWin ) - { - ::Window aWin = GDK_WINDOW_XWINDOW(pWin); - if( aWin != None ) - XSetWindowBackgroundPixmap( GtkSalFrame::getDisplay()->GetDisplay(), - aWin, - pThis->m_hBackgroundPixmap ); - } - if( ! pThis->m_pParent ) - { - // signalize theme changed for NWF caches - // FIXME: should be called only once for a style change - GtkSalGraphics::bThemeChanged = true; - } -#endif -} - -gboolean GtkSalFrame::signalWindowState( GtkWidget*, GdkEvent* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - if( (pThis->m_nState & GDK_WINDOW_STATE_ICONIFIED) != (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_ICONIFIED ) ) - { - GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_RESIZE ); - pThis->TriggerPaintEvent(); - } - - if( (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_MAXIMIZED) && - ! (pThis->m_nState & GDK_WINDOW_STATE_MAXIMIZED) ) - { - pThis->m_aRestorePosSize = - Rectangle( Point( pThis->maGeometry.nX, pThis->maGeometry.nY ), - Size( pThis->maGeometry.nWidth, pThis->maGeometry.nHeight ) ); - } - pThis->m_nState = pEvent->window_state.new_window_state; - - #if OSL_DEBUG_LEVEL > 1 - if( (pEvent->window_state.changed_mask & GDK_WINDOW_STATE_FULLSCREEN) ) - { - fprintf( stderr, "window %p %s full screen state\n", - pThis, - (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_FULLSCREEN) ? "enters" : "leaves"); - } - #endif - - return false; -} - -gboolean GtkSalFrame::signalVisibility( GtkWidget*, GdkEventVisibility* pEvent, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - pThis->m_nVisibility = pEvent->state; - return true; -} - -void GtkSalFrame::signalDestroy( GtkWidget* pObj, gpointer frame ) -{ - GtkSalFrame* pThis = static_cast(frame); - if( pObj == pThis->m_pWindow ) - { - pThis->m_pFixedContainer = NULL; - pThis->m_pEventBox = NULL; - pThis->m_pWindow = NULL; - pThis->InvalidateGraphics(); - } -} - -// GtkSalFrame::IMHandler - -GtkSalFrame::IMHandler::IMHandler( GtkSalFrame* pFrame ) -: m_pFrame(pFrame), - m_nPrevKeyPresses( 0 ), - m_pIMContext( NULL ), - m_bFocused( true ), - m_bPreeditJustChanged( false ) -{ - m_aInputEvent.mpTextAttr = NULL; - createIMContext(); -} - -GtkSalFrame::IMHandler::~IMHandler() -{ - // cancel an eventual event posted to begin preedit again - GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); - deleteIMContext(); -} - -void GtkSalFrame::IMHandler::createIMContext() -{ - if( ! m_pIMContext ) - { - m_pIMContext = gtk_im_multicontext_new (); - 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 ); - g_signal_connect( m_pIMContext, "preedit_start", - G_CALLBACK (signalIMPreeditStart), this ); - g_signal_connect( m_pIMContext, "preedit_end", - G_CALLBACK (signalIMPreeditEnd), this ); - - GetGenericData()->ErrorTrapPush(); - gtk_im_context_set_client_window( m_pIMContext, widget_get_window(GTK_WIDGET(m_pFrame->m_pWindow)) ); - gtk_im_context_focus_in( m_pIMContext ); - GetGenericData()->ErrorTrapPop(); - m_bFocused = true; - } -} - -void GtkSalFrame::IMHandler::deleteIMContext() -{ - if( m_pIMContext ) - { - // first give IC a chance to deinitialize - GetGenericData()->ErrorTrapPush(); - gtk_im_context_set_client_window( m_pIMContext, NULL ); - GetGenericData()->ErrorTrapPop(); - // destroy old IC - g_object_unref( m_pIMContext ); - m_pIMContext = NULL; - } -} - -void GtkSalFrame::IMHandler::doCallEndExtTextInput() -{ - m_aInputEvent.mpTextAttr = NULL; - m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); -} - -void GtkSalFrame::IMHandler::updateIMSpotLocation() -{ - SalExtTextInputPosEvent aPosEvent; - m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent ); - GdkRectangle aArea; - aArea.x = aPosEvent.mnX; - aArea.y = aPosEvent.mnY; - aArea.width = aPosEvent.mnWidth; - aArea.height = aPosEvent.mnHeight; - GetGenericData()->ErrorTrapPush(); - gtk_im_context_set_cursor_location( m_pIMContext, &aArea ); - GetGenericData()->ErrorTrapPop(); -} - -void GtkSalFrame::IMHandler::sendEmptyCommit() -{ - vcl::DeletionListener aDel( m_pFrame ); - - SalExtTextInputEvent aEmptyEv; - aEmptyEv.mnTime = 0; - aEmptyEv.mpTextAttr = 0; - aEmptyEv.maText.clear(); - aEmptyEv.mnCursorPos = 0; - aEmptyEv.mnCursorFlags = 0; - aEmptyEv.mbOnlyCursor = False; - m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&aEmptyEv ); - if( ! aDel.isDeleted() ) - m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); -} - -void GtkSalFrame::IMHandler::endExtTextInput( sal_uInt16 /*nFlags*/ ) -{ - gtk_im_context_reset ( m_pIMContext ); - - if( m_aInputEvent.mpTextAttr ) - { - vcl::DeletionListener aDel( m_pFrame ); - // delete preedit in sal (commit an empty string) - sendEmptyCommit(); - if( ! aDel.isDeleted() ) - { - // mark previous preedit state again (will e.g. be sent at focus gain) - m_aInputEvent.mpTextAttr = &m_aInputFlags[0]; - if( m_bFocused ) - { - // begin preedit again - GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); - } - } - } -} - -void GtkSalFrame::IMHandler::focusChanged( bool bFocusIn ) -{ - m_bFocused = bFocusIn; - if( bFocusIn ) - { - GetGenericData()->ErrorTrapPush(); - gtk_im_context_focus_in( m_pIMContext ); - GetGenericData()->ErrorTrapPop(); - if( m_aInputEvent.mpTextAttr ) - { - sendEmptyCommit(); - // begin preedit again - GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); - } - } - else - { - GetGenericData()->ErrorTrapPush(); - gtk_im_context_focus_out( m_pIMContext ); - GetGenericData()->ErrorTrapPop(); - // cancel an eventual event posted to begin preedit again - GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); - } -} - -bool GtkSalFrame::IMHandler::handleKeyEvent( GdkEventKey* pEvent ) -{ - vcl::DeletionListener aDel( m_pFrame ); - - if( pEvent->type == GDK_KEY_PRESS ) - { - // Add this key press event to the list of previous key presses - // to which we compare key release events. If a later key release - // event has a matching key press event in this list, we swallow - // the key release because some GTK Input Methods don't swallow it - // for us. - m_aPrevKeyPresses.push_back( PreviousKeyPress(pEvent) ); - m_nPrevKeyPresses++; - - // Also pop off the earliest key press event if there are more than 10 - // already. - while (m_nPrevKeyPresses > 10) - { - m_aPrevKeyPresses.pop_front(); - m_nPrevKeyPresses--; - } - - GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); - - // #i51353# update spot location on every key input since we cannot - // know which key may activate a preedit choice window - updateIMSpotLocation(); - if( aDel.isDeleted() ) - return true; - - gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); - g_object_unref( pRef ); - - if( aDel.isDeleted() ) - return true; - - m_bPreeditJustChanged = false; - - if( bResult ) - return true; - else - { - DBG_ASSERT( m_nPrevKeyPresses > 0, "key press has vanished !" ); - if( ! m_aPrevKeyPresses.empty() ) // sanity check - { - // event was not swallowed, do not filter a following - // key release event - // note: this relies on gtk_im_context_filter_keypress - // returning without calling a handler (in the "not swallowed" - // case ) which might change the previous key press list so - // we would pop the wrong event here - m_aPrevKeyPresses.pop_back(); - m_nPrevKeyPresses--; - } - } - } - - // Determine if we got an earlier key press event corresponding to this key release - if (pEvent->type == GDK_KEY_RELEASE) - { - GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); - gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); - g_object_unref( pRef ); - - if( aDel.isDeleted() ) - return true; - - m_bPreeditJustChanged = false; - - std::list::iterator iter = m_aPrevKeyPresses.begin(); - std::list::iterator iter_end = m_aPrevKeyPresses.end(); - while (iter != iter_end) - { - // If we found a corresponding previous key press event, swallow the release - // and remove the earlier key press from our list - if (*iter == pEvent) - { - m_aPrevKeyPresses.erase(iter); - m_nPrevKeyPresses--; - return true; - } - ++iter; - } - - if( bResult ) - return true; - } - - return false; -} - -/* FIXME: -* #122282# still more hacking: some IMEs never start a preedit but simply commit -* in this case we cannot commit a single character. Workaround: do not do the -* single key hack for enter or space if the unicode committed does not match -*/ - -static bool checkSingleKeyCommitHack( guint keyval, sal_Unicode cCode ) -{ - bool bRet = true; - switch( keyval ) - { - case GDK_KP_Enter: - case GDK_Return: - if( cCode != '\n' && cCode != '\r' ) - bRet = false; - break; - case GDK_space: - case GDK_KP_Space: - if( cCode != ' ' ) - bRet = false; - break; - default: - break; - } - return bRet; -} - -void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext* pContext, gchar* pText, gpointer im_handler ) -{ - GtkSalFrame::IMHandler* pThis = static_cast(im_handler); - - SolarMutexGuard aGuard; - vcl::DeletionListener aDel( pThis->m_pFrame ); - { - const bool bWasPreedit = - (pThis->m_aInputEvent.mpTextAttr != 0) || - pThis->m_bPreeditJustChanged; - - pThis->m_aInputEvent.mnTime = 0; - pThis->m_aInputEvent.mpTextAttr = 0; - pThis->m_aInputEvent.maText = OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ); - pThis->m_aInputEvent.mnCursorPos = pThis->m_aInputEvent.maText.getLength(); - pThis->m_aInputEvent.mnCursorFlags = 0; - pThis->m_aInputEvent.mbOnlyCursor = False; - - pThis->m_aInputFlags.clear(); - - /* necessary HACK: all keyboard input comes in here as soon as a IMContext is set - * which is logical and consequent. But since even simple input like - * comes through the commit signal instead of signalKey - * and all kinds of windows only implement KeyInput (e.g. PushButtons, - * RadioButtons and a lot of other Controls), will send a single - * KeyInput/KeyUp sequence instead of an ExtText event if there - * never was a preedit and the text is only one character. - * - * In this case there the last ExtText event must have been - * SALEVENT_ENDEXTTEXTINPUT, either because of a regular commit - * or because there never was a preedit. - */ - bool bSingleCommit = false; - if( ! bWasPreedit - && pThis->m_aInputEvent.maText.getLength() == 1 - && ! pThis->m_aPrevKeyPresses.empty() - ) - { - const PreviousKeyPress& rKP = pThis->m_aPrevKeyPresses.back(); - sal_Unicode aOrigCode = pThis->m_aInputEvent.maText[0]; - - if( checkSingleKeyCommitHack( rKP.keyval, aOrigCode ) ) - { - pThis->m_pFrame->doKeyCallback( rKP.state, rKP.keyval, rKP.hardware_keycode, rKP.group, rKP.time, aOrigCode, true, true ); - bSingleCommit = true; - } - } - if( ! bSingleCommit ) - { - pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent); - if( ! aDel.isDeleted() ) - pThis->doCallEndExtTextInput(); - } - if( ! aDel.isDeleted() ) - { - // reset input event - pThis->m_aInputEvent.maText.clear(); - pThis->m_aInputEvent.mnCursorPos = 0; - pThis->updateIMSpotLocation(); - } - } -#ifdef SOLARIS - // #i51356# workaround a solaris IIIMP bug - // in case of partial commits the preedit changed signal - // and commit signal come in wrong order - if( ! aDel.isDeleted() ) - signalIMPreeditChanged( pContext, im_handler ); -#else - (void) pContext; -#endif -} - -void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext*, gpointer im_handler ) -{ - GtkSalFrame::IMHandler* pThis = static_cast(im_handler); - - char* pText = NULL; - PangoAttrList* pAttrs = NULL; - gint nCursorPos = 0; - - gtk_im_context_get_preedit_string( pThis->m_pIMContext, - &pText, - &pAttrs, - &nCursorPos ); - if( pText && ! *pText ) // empty string - { - // change from nothing to nothing -> do not start preedit - // e.g. this will activate input into a calc cell without - // user input - if( pThis->m_aInputEvent.maText.getLength() == 0 ) - { - g_free( pText ); - pango_attr_list_unref( pAttrs ); - return; - } - } - - pThis->m_bPreeditJustChanged = true; - - bool bEndPreedit = (!pText || !*pText) && pThis->m_aInputEvent.mpTextAttr != NULL; - pThis->m_aInputEvent.mnTime = 0; - pThis->m_aInputEvent.maText = pText ? OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ) : OUString(); - pThis->m_aInputEvent.mnCursorPos = nCursorPos; - pThis->m_aInputEvent.mnCursorFlags = 0; - pThis->m_aInputEvent.mbOnlyCursor = False; - - pThis->m_aInputFlags = std::vector( std::max( 1, (int)pThis->m_aInputEvent.maText.getLength() ), 0 ); - - PangoAttrIterator *iter = pango_attr_list_get_iterator(pAttrs); - do - { - GSList *attr_list = NULL; - GSList *tmp_list = NULL; - gint start, end; - guint sal_attr = 0; - - pango_attr_iterator_range (iter, &start, &end); - if (end == G_MAXINT) - end = pText ? strlen (pText) : 0; - if (end == start) - continue; - - start = g_utf8_pointer_to_offset (pText, pText + start); - end = g_utf8_pointer_to_offset (pText, pText + end); - - tmp_list = attr_list = pango_attr_iterator_get_attrs (iter); - while (tmp_list) - { - PangoAttribute *pango_attr = static_cast(tmp_list->data); - - switch (pango_attr->klass->type) - { - case PANGO_ATTR_BACKGROUND: - sal_attr |= (EXTTEXTINPUT_ATTR_HIGHLIGHT | EXTTEXTINPUT_CURSOR_INVISIBLE); - break; - case PANGO_ATTR_UNDERLINE: - sal_attr |= EXTTEXTINPUT_ATTR_UNDERLINE; - break; - case PANGO_ATTR_STRIKETHROUGH: - sal_attr |= EXTTEXTINPUT_ATTR_REDTEXT; - break; - default: - break; - } - pango_attribute_destroy (pango_attr); - tmp_list = tmp_list->next; - } - if (sal_attr == 0) - sal_attr |= EXTTEXTINPUT_ATTR_UNDERLINE; - g_slist_free (attr_list); - - // Set the sal attributes on our text - for (int i = start; i < end; ++i) - { - SAL_WARN_IF(i >= static_cast(pThis->m_aInputFlags.size()), - "vcl.gtk", "pango attrib out of range. Broken range: " - << start << "," << end << " Legal range: 0," - << pThis->m_aInputFlags.size()); - if (i >= static_cast(pThis->m_aInputFlags.size())) - continue; - pThis->m_aInputFlags[i] |= sal_attr; - } - } while (pango_attr_iterator_next (iter)); - pango_attr_iterator_destroy(iter); - - pThis->m_aInputEvent.mpTextAttr = &pThis->m_aInputFlags[0]; - - g_free( pText ); - pango_attr_list_unref( pAttrs ); - - SolarMutexGuard aGuard; - vcl::DeletionListener aDel( pThis->m_pFrame ); - - pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent); - if( bEndPreedit && ! aDel.isDeleted() ) - pThis->doCallEndExtTextInput(); - if( ! aDel.isDeleted() ) - pThis->updateIMSpotLocation(); -} - -void GtkSalFrame::IMHandler::signalIMPreeditStart( GtkIMContext*, gpointer /*im_handler*/ ) -{ -} - -void GtkSalFrame::IMHandler::signalIMPreeditEnd( GtkIMContext*, gpointer im_handler ) -{ - GtkSalFrame::IMHandler* pThis = static_cast(im_handler); - - pThis->m_bPreeditJustChanged = true; - - SolarMutexGuard aGuard; - vcl::DeletionListener aDel( pThis->m_pFrame ); - pThis->doCallEndExtTextInput(); - if( ! aDel.isDeleted() ) - pThis->updateIMSpotLocation(); -} - -uno::Reference - FindFocus(uno::Reference< accessibility::XAccessibleContext > xContext) -{ - if (!xContext.is()) - uno::Reference< accessibility::XAccessibleEditableText >(); - - uno::Reference xState = xContext->getAccessibleStateSet(); - if (xState.is()) - { - if (xState->contains(accessibility::AccessibleStateType::FOCUSED)) - return uno::Reference(xContext, uno::UNO_QUERY); - } - - for (sal_Int32 i = 0; i < xContext->getAccessibleChildCount(); ++i) - { - uno::Reference< accessibility::XAccessible > xChild = xContext->getAccessibleChild(i); - if (!xChild.is()) - continue; - uno::Reference< accessibility::XAccessibleContext > xChildContext = xChild->getAccessibleContext(); - if (!xChildContext.is()) - continue; - uno::Reference< accessibility::XAccessibleEditableText > xText = FindFocus(xChildContext); - if (xText.is()) - return xText; - } - return uno::Reference< accessibility::XAccessibleEditableText >(); -} - -static uno::Reference lcl_GetxText(vcl::Window *pFocusWin) -{ - uno::Reference xText; - try - { - uno::Reference< accessibility::XAccessible > xAccessible( pFocusWin->GetAccessible( true ) ); - if (xAccessible.is()) - xText = FindFocus(xAccessible->getAccessibleContext()); - } - catch(const uno::Exception& e) - { - SAL_WARN( "vcl.gtk", "Exception in getting input method surrounding text: " << e.Message); - } - return xText; -} - -gboolean GtkSalFrame::IMHandler::signalIMRetrieveSurrounding( GtkIMContext* pContext, gpointer /*im_handler*/ ) -{ - vcl::Window *pFocusWin = Application::GetFocusWindow(); - if (!pFocusWin) - return true; - - uno::Reference xText = lcl_GetxText(pFocusWin); - if (xText.is()) - { - sal_Int32 nPosition = xText->getCaretPosition(); - OUString sAllText = xText->getText(); - OString sUTF = OUStringToOString(sAllText, RTL_TEXTENCODING_UTF8); - OUString sCursorText(sAllText.copy(0, nPosition)); - gtk_im_context_set_surrounding(pContext, sUTF.getStr(), sUTF.getLength(), - OUStringToOString(sCursorText, RTL_TEXTENCODING_UTF8).getLength()); - return true; - } - - return false; -} - -gboolean GtkSalFrame::IMHandler::signalIMDeleteSurrounding( GtkIMContext*, gint offset, gint nchars, - gpointer /*im_handler*/ ) -{ - vcl::Window *pFocusWin = Application::GetFocusWindow(); - if (!pFocusWin) - return true; - - uno::Reference xText = lcl_GetxText(pFocusWin); - if (xText.is()) - { - sal_Int32 nPosition = xText->getCaretPosition(); - // #i111768# range checking - sal_Int32 nDeletePos = nPosition + offset; - sal_Int32 nDeleteEnd = nDeletePos + nchars; - if (nDeletePos < 0) - nDeletePos = 0; - if (nDeleteEnd < 0) - nDeleteEnd = 0; - if (nDeleteEnd > xText->getCharacterCount()) - nDeleteEnd = xText->getCharacterCount(); - - xText->deleteText(nDeletePos, nDeleteEnd); - //tdf91641 adjust cursor if deleted chars shift it forward (normal case) - if (nDeletePos < nPosition) - { - if (nDeleteEnd <= nPosition) - nPosition = nPosition - (nDeleteEnd - nDeletePos); - else - nPosition = nDeletePos; - - if (xText->getCharacterCount() >= nPosition) - xText->setCaretPosition( nPosition ); - } - return true; - } - - return false; -} - -Size GtkSalDisplay::GetScreenSize( int nDisplayScreen ) -{ - Rectangle aRect = m_pSys->GetDisplayScreenPosSizePixel( nDisplayScreen ); - return Size( aRect.GetWidth(), aRect.GetHeight() ); -} - -Window GtkSalFrame::GetX11Window() -{ - return widget_get_xid(m_pWindow); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/app/gtk3gtkinst.cxx b/vcl/unx/gtk3/app/gtk3gtkinst.cxx deleted file mode 100644 index 5716058..0000000 --- a/vcl/unx/gtk3/app/gtk3gtkinst.cxx +++ /dev/null @@ -1,641 +0,0 @@ -/* -*- 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/. - */ - -#include "../../gtk/app/gtkinst.cxx" - -#include -#include "com/sun/star/lang/XServiceInfo.hpp" -#include "com/sun/star/lang/XSingleServiceFactory.hpp" -#include "com/sun/star/lang/XInitialization.hpp" -#include "com/sun/star/lang/DisposedException.hpp" -#include "com/sun/star/datatransfer/XTransferable.hpp" -#include "com/sun/star/datatransfer/clipboard/XClipboard.hpp" -#include "com/sun/star/datatransfer/clipboard/XClipboardEx.hpp" -#include "com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp" -#include "com/sun/star/datatransfer/clipboard/XClipboardListener.hpp" -#include "com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp" -#include "com/sun/star/datatransfer/dnd/XDragSource.hpp" -#include "com/sun/star/datatransfer/dnd/XDropTarget.hpp" -#include "com/sun/star/datatransfer/dnd/DNDConstants.hpp" -#include -#include -#include "cppuhelper/compbase.hxx" -#include "cppuhelper/implbase1.hxx" -#include - -using namespace com::sun::star; -using namespace com::sun::star::uno; -using namespace com::sun::star::lang; - -namespace -{ - struct TypeEntry - { - const char* pNativeType; // string corresponding to nAtom for the case of nAtom being uninitialized - const char* pType; // Mime encoding on our side - }; - - static TypeEntry aConversionTab[] = - { - { "ISO10646-1", "text/plain;charset=utf-16" }, - { "UTF8_STRING", "text/plain;charset=utf-8" }, - { "UTF-8", "text/plain;charset=utf-8" }, - { "text/plain;charset=UTF-8", "text/plain;charset=utf-8" }, - // ISO encodings - { "ISO8859-2", "text/plain;charset=iso8859-2" }, - { "ISO8859-3", "text/plain;charset=iso8859-3" }, - { "ISO8859-4", "text/plain;charset=iso8859-4" }, - { "ISO8859-5", "text/plain;charset=iso8859-5" }, - { "ISO8859-6", "text/plain;charset=iso8859-6" }, - { "ISO8859-7", "text/plain;charset=iso8859-7" }, - { "ISO8859-8", "text/plain;charset=iso8859-8" }, - { "ISO8859-9", "text/plain;charset=iso8859-9" }, - { "ISO8859-10", "text/plain;charset=iso8859-10" }, - { "ISO8859-13", "text/plain;charset=iso8859-13" }, - { "ISO8859-14", "text/plain;charset=iso8859-14" }, - { "ISO8859-15", "text/plain;charset=iso8859-15" }, - // asian encodings - { "JISX0201.1976-0", "text/plain;charset=jisx0201.1976-0" }, - { "JISX0208.1983-0", "text/plain;charset=jisx0208.1983-0" }, - { "JISX0208.1990-0", "text/plain;charset=jisx0208.1990-0" }, - { "JISX0212.1990-0", "text/plain;charset=jisx0212.1990-0" }, - { "GB2312.1980-0", "text/plain;charset=gb2312.1980-0" }, - { "KSC5601.1992-0", "text/plain;charset=ksc5601.1992-0" }, - // eastern european encodings - { "KOI8-R", "text/plain;charset=koi8-r" }, - { "KOI8-U", "text/plain;charset=koi8-u" }, - // String (== iso8859-1) - { "STRING", "text/plain;charset=iso8859-1" }, - // special for compound text - { "COMPOUND_TEXT", "text/plain;charset=compound_text" }, - - // PIXMAP - { "PIXMAP", "image/bmp" } - }; - - class DataFlavorEq : public std::unary_function - { - private: - const css::datatransfer::DataFlavor& m_rData; - public: - explicit DataFlavorEq(const css::datatransfer::DataFlavor& rData) : m_rData(rData) {} - bool operator() (const css::datatransfer::DataFlavor& rData) const - { - return rData.MimeType == m_rData.MimeType && - rData.DataType == m_rData.DataType; - } - }; -} - -class GtkTransferable : public ::cppu::WeakImplHelper1 < - css::datatransfer::XTransferable > -{ -private: - GdkAtom m_nSelection; - std::map m_aMimeTypeToAtom; -public: - - GtkTransferable(GdkAtom nSelection) - : m_nSelection(nSelection) - { - } - - /* - * XTransferable - */ - - virtual css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor) - throw(css::datatransfer::UnsupportedFlavorException, - css::io::IOException, - css::uno::RuntimeException, std::exception - ) SAL_OVERRIDE - { - GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); - if (rFlavor.MimeType == "text/plain;charset=utf-16") - { - OUString aStr; - gchar *pText = gtk_clipboard_wait_for_text(clipboard); - if (pText) - aStr = OUString(pText, rtl_str_getLength(pText), RTL_TEXTENCODING_UTF8); - g_free(pText); - css::uno::Any aRet; - aRet <<= aStr.replaceAll("\r\n", "\n"); - return aRet; - } - - auto it = m_aMimeTypeToAtom.find(rFlavor.MimeType); - if (it == m_aMimeTypeToAtom.end()) - return css::uno::Any(); - - css::uno::Any aRet; - GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard, - it->second); - gint length; - const guchar *rawdata = gtk_selection_data_get_data_with_length(data, - &length); - Sequence aSeq(reinterpret_cast(rawdata), length); - gtk_selection_data_free(data); - aRet <<= aSeq; - return aRet; - } - - std::vector getTransferDataFlavorsAsVector() - { - std::vector aVector; - - GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); - - GdkAtom *targets; - gint n_targets; - if (gtk_clipboard_wait_for_targets(clipboard, &targets, &n_targets)) - { - bool bHaveText = false, bHaveUTF16 = false; - - for (gint i = 0; i < n_targets; ++i) - { - gchar* pName = gdk_atom_name(targets[i]); - const char* pFinalName = pName; - css::datatransfer::DataFlavor aFlavor; - - for (size_t j = 0; j < SAL_N_ELEMENTS(aConversionTab); ++j) - { - if (rtl_str_compare(pName, aConversionTab[j].pNativeType) == 0) - { - pFinalName = aConversionTab[j].pType; - break; - } - } - - aFlavor.MimeType = OUString(pFinalName, - rtl_str_getLength(pFinalName), - RTL_TEXTENCODING_UTF8); - - m_aMimeTypeToAtom[aFlavor.MimeType] = targets[i]; - - aFlavor.DataType = cppu::UnoType>::get(); - - sal_Int32 nIndex(0); - if (aFlavor.MimeType.getToken(0, ';', nIndex) == "text/plain") - { - bHaveText = true; - OUString aToken(aFlavor.MimeType.getToken(0, ';', nIndex)); - if (aToken == "charset=utf-16") - { - bHaveUTF16 = true; - aFlavor.DataType = cppu::UnoType::get(); - } - } - aVector.push_back(aFlavor); - g_free(pName); - } - - g_free(targets); - - //If we have text, but no UTF-16 format which is basically the only - //text-format LibreOffice supports for cnp then claim we do and we - //will convert on demand - if (bHaveText && !bHaveUTF16) - { - css::datatransfer::DataFlavor aFlavor; - aFlavor.MimeType = "text/plain;charset=utf-16"; - aFlavor.DataType = cppu::UnoType::get(); - aVector.push_back(aFlavor); - } - } - - return aVector; - } - - virtual css::uno::Sequence SAL_CALL getTransferDataFlavors() - throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE - { - return comphelper::containerToSequence(getTransferDataFlavorsAsVector()); - } - - virtual sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor) - throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE - { - const std::vector aAll = - getTransferDataFlavorsAsVector(); - - return std::find_if(aAll.begin(), aAll.end(), DataFlavorEq(rFlavor)) != aAll.end(); - } -}; - -//We want to use gtk_clipboard_get_owner own owner-change to distinguish between -//us gaining the clipboard ownership vs losing it. To do that we need to use -//gtk_clipboard_set_with_owner and to do that we need a GObject, so define -//one here for that purpose and just give it a VclGtkClipboard* member -class VclGtkClipboard; - -typedef struct _ClipboardOwner ClipboardOwner; -typedef struct _ClipboardOwnerClass ClipboardOwnerClass; - -struct _ClipboardOwner -{ - GObject parent_instance; - - /* instance members */ - VclGtkClipboard* m_pThis; -}; - -struct _ClipboardOwnerClass -{ - GObjectClass parent_class; - - /* class members */ -}; - -#define CLIPBOARD_OWNER_OBJECT (clipboard_owner_get_type ()) -#define CLIPBOARD_OWNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLIPBOARD_OWNER_OBJECT, ClipboardOwner)) - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif -G_DEFINE_TYPE(ClipboardOwner, clipboard_owner, G_TYPE_OBJECT); -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -static void clipboard_owner_class_init (ClipboardOwnerClass *) -{ -} - -static void clipboard_owner_init(ClipboardOwner *) -{ -} - -class VclGtkClipboard : - public cppu::WeakComponentImplHelper< - datatransfer::clipboard::XSystemClipboard, - XServiceInfo> -{ - GdkAtom m_nSelection; - osl::Mutex m_aMutex; - ClipboardOwner* m_pOwner; - gulong m_nOwnerChangedSignalId; - Reference m_aContents; - Reference m_aOwner; - std::list< Reference > m_aListeners; - std::vector m_aGtkTargets; - std::vector m_aInfoToFlavor; - -public: - - VclGtkClipboard(GdkAtom nSelection); - virtual ~VclGtkClipboard(); - - /* - * XServiceInfo - */ - - virtual OUString SAL_CALL getImplementationName() throw( RuntimeException, std::exception ) SAL_OVERRIDE; - virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw( RuntimeException, std::exception ) SAL_OVERRIDE; - virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() throw( RuntimeException, std::exception ) SAL_OVERRIDE; - - static OUString getImplementationName_static(); - static Sequence< OUString > getSupportedServiceNames_static(); - - /* - * XClipboard - */ - - virtual Reference< css::datatransfer::XTransferable > SAL_CALL getContents() - throw(RuntimeException, std::exception) SAL_OVERRIDE; - - virtual void SAL_CALL setContents( - const Reference< css::datatransfer::XTransferable >& xTrans, - const Reference< css::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ) - throw(RuntimeException, std::exception) SAL_OVERRIDE; - - virtual OUString SAL_CALL getName() - throw(RuntimeException, std::exception) SAL_OVERRIDE; - - /* - * XClipboardEx - */ - - virtual sal_Int8 SAL_CALL getRenderingCapabilities() - throw(RuntimeException, std::exception) SAL_OVERRIDE; - - /* - * XClipboardNotifier - */ - virtual void SAL_CALL addClipboardListener( - const Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) - throw(RuntimeException, std::exception) SAL_OVERRIDE; - - virtual void SAL_CALL removeClipboardListener( - const Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) - throw(RuntimeException, std::exception) SAL_OVERRIDE; - - void ClipboardGet(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info); - void ClipboardClear(GtkClipboard *clipboard); - void OwnerChanged(GtkClipboard *clipboard, GdkEvent *event); -private: - GtkTargetEntry makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor); -}; - -OUString VclGtkClipboard::getImplementationName_static() -{ - return OUString( "com.sun.star.datatransfer.VclGtkClipboard" ); -} - -Sequence< OUString > VclGtkClipboard::getSupportedServiceNames_static() -{ - Sequence< OUString > aRet(1); - aRet[0] = "com.sun.star.datatransfer.clipboard.SystemClipboard"; - return aRet; -} - -OUString VclGtkClipboard::getImplementationName() throw( RuntimeException, std::exception ) -{ - return getImplementationName_static(); -} - -Sequence< OUString > VclGtkClipboard::getSupportedServiceNames() throw( RuntimeException, std::exception ) -{ - return getSupportedServiceNames_static(); -} - -sal_Bool VclGtkClipboard::supportsService( const OUString& ServiceName ) throw( RuntimeException, std::exception ) -{ - return cppu::supportsService(this, ServiceName); -} - -Reference< css::datatransfer::XTransferable > VclGtkClipboard::getContents() throw( RuntimeException, std::exception ) -{ - if (G_OBJECT(m_pOwner) != gtk_clipboard_get_owner(gtk_clipboard_get(m_nSelection)) && - !m_aContents.is()) - { - //tdf#93887 This is the system clipboard/selection. We fetch it when we are not - //the owner of the clipboard and have not already fetched it. - m_aContents = new GtkTransferable(m_nSelection); - } - return m_aContents; -} - -void VclGtkClipboard::ClipboardGet(GtkClipboard* /*clipboard*/, GtkSelectionData *selection_data, - guint info) -{ - if (!m_aContents.is()) - return; - - GdkAtom type(gdk_atom_intern(OUStringToOString(m_aInfoToFlavor[info].MimeType, - RTL_TEXTENCODING_UTF8).getStr(), - false)); - - css::datatransfer::DataFlavor aFlavor(m_aInfoToFlavor[info]); - if (aFlavor.MimeType == "UTF8_STRING" || aFlavor.MimeType == "STRING") - aFlavor.MimeType = "text/plain;charset=utf-8"; - - Sequence aData; - Any aValue; - - try - { - aValue = m_aContents->getTransferData(aFlavor); - } - catch(...) - { - } - - if (aValue.getValueTypeClass() == TypeClass_STRING) - { - OUString aString; - aValue >>= aString; - aData = Sequence< sal_Int8 >( reinterpret_cast(aString.getStr()), aString.getLength() * sizeof( sal_Unicode ) ); - } - else if (aValue.getValueType() == cppu::UnoType>::get()) - { - aValue >>= aData; - } - else if (aFlavor.MimeType == "text/plain;charset=utf-8") - { - //didn't have utf-8, try utf-16 and convert - aFlavor.MimeType = "text/plain;charset=utf-16"; - aFlavor.DataType = cppu::UnoType::get(); - try - { - aValue = m_aContents->getTransferData(aFlavor); - } - catch(...) - { - } - OUString aString; - aValue >>= aString; - OString aUTF8String(OUStringToOString(aString, RTL_TEXTENCODING_UTF8)); - gtk_selection_data_set(selection_data, type, 8, - reinterpret_cast(aUTF8String.getStr()), - aUTF8String.getLength()); - return; - } - - gtk_selection_data_set(selection_data, type, 8, - reinterpret_cast(aData.getArray()), - aData.getLength()); -} - -void VclGtkClipboard::OwnerChanged(GtkClipboard* clipboard, GdkEvent* /*event*/) -{ - if (G_OBJECT(m_pOwner) != gtk_clipboard_get_owner(clipboard)) - { - //null out m_aContents to return control to the system-one which - //will be retrieved if getContents is called again - setContents(Reference(), - Reference()); - } -} - -void VclGtkClipboard::ClipboardClear(GtkClipboard * /*clipboard*/) -{ - for (auto &a : m_aGtkTargets) - free(a.target); - m_aGtkTargets.clear(); -} - -GtkTargetEntry VclGtkClipboard::makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor) -{ - GtkTargetEntry aEntry; - aEntry.target = - g_strdup(OUStringToOString(rFlavor.MimeType, RTL_TEXTENCODING_UTF8).getStr()); - aEntry.flags = 0; - auto it = std::find_if(m_aInfoToFlavor.begin(), m_aInfoToFlavor.end(), - DataFlavorEq(rFlavor)); - if (it != m_aInfoToFlavor.end()) - aEntry.info = std::distance(m_aInfoToFlavor.begin(), it); - else - { - aEntry.info = m_aInfoToFlavor.size(); - m_aInfoToFlavor.push_back(rFlavor); - } - return aEntry; -} - -namespace -{ - void ClipboardGetFunc(GtkClipboard *clipboard, GtkSelectionData *selection_data, - guint info, - gpointer user_data_or_owner) - { - VclGtkClipboard* pThis = CLIPBOARD_OWNER(user_data_or_owner)->m_pThis; - pThis->ClipboardGet(clipboard, selection_data, info); - } - - void ClipboardClearFunc(GtkClipboard *clipboard, gpointer user_data_or_owner) - { - VclGtkClipboard* pThis = CLIPBOARD_OWNER(user_data_or_owner)->m_pThis; - pThis->ClipboardClear(clipboard); - } - - void handle_owner_change(GtkClipboard *clipboard, GdkEvent *event, gpointer user_data) - { - VclGtkClipboard* pThis = static_cast(user_data); - pThis->OwnerChanged(clipboard, event); - } -} - -VclGtkClipboard::VclGtkClipboard(GdkAtom nSelection) - : cppu::WeakComponentImplHelper - (m_aMutex) - , m_nSelection(nSelection) -{ - GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); - m_nOwnerChangedSignalId = g_signal_connect(clipboard, "owner-change", - G_CALLBACK(handle_owner_change), this); - m_pOwner = CLIPBOARD_OWNER(g_object_new(CLIPBOARD_OWNER_OBJECT, NULL)); - m_pOwner->m_pThis = this; -} - -VclGtkClipboard::~VclGtkClipboard() -{ - GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); - g_signal_handler_disconnect(clipboard, m_nOwnerChangedSignalId); - g_object_unref(m_pOwner); -} - -void VclGtkClipboard::setContents( - const Reference< css::datatransfer::XTransferable >& xTrans, - const Reference< css::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ) - throw( RuntimeException, std::exception ) -{ - osl::ClearableMutexGuard aGuard( m_aMutex ); - Reference< datatransfer::clipboard::XClipboardOwner > xOldOwner( m_aOwner ); - Reference< datatransfer::XTransferable > xOldContents( m_aContents ); - m_aContents = xTrans; - m_aOwner = xClipboardOwner; - - std::list< Reference< datatransfer::clipboard::XClipboardListener > > xListeners( m_aListeners ); - datatransfer::clipboard::ClipboardEvent aEv; - - if (m_aContents.is()) - { - css::uno::Sequence aFormats = xTrans->getTransferDataFlavors(); - std::vector aGtkTargets; - bool bHaveText(false), bHaveUTF8(false); - for (int i = 0; i < aFormats.getLength(); ++i) - { - const css::datatransfer::DataFlavor& rFlavor = aFormats[i]; - - sal_Int32 nIndex(0); - if (rFlavor.MimeType.getToken(0, ';', nIndex) == "text/plain") - { - bHaveText = true; - OUString aToken(rFlavor.MimeType.getToken(0, ';', nIndex)); - if (aToken == "charset=utf-8") - { - bHaveUTF8 = true; - } - } - GtkTargetEntry aEntry(makeGtkTargetEntry(rFlavor)); - aGtkTargets.push_back(aEntry); - } - - if (bHaveText) - { - css::datatransfer::DataFlavor aFlavor; - aFlavor.DataType = cppu::UnoType>::get(); - if (!bHaveUTF8) - { - aFlavor.MimeType = "text/plain;charset=utf-8"; - aGtkTargets.push_back(makeGtkTargetEntry(aFlavor)); - } - aFlavor.MimeType = "UTF8_STRING"; - aGtkTargets.push_back(makeGtkTargetEntry(aFlavor)); - aFlavor.MimeType = "STRING"; - aGtkTargets.push_back(makeGtkTargetEntry(aFlavor)); - } - - //if there was a previous gtk_clipboard_set_with_data call then - //ClipboardClearFunc will be called now - GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); - //use with_owner with m_pOwner so we can distinguish in handle_owner_change - //if we have gained or lost ownership of the clipboard - gtk_clipboard_set_with_owner(clipboard, aGtkTargets.data(), aGtkTargets.size(), - ClipboardGetFunc, ClipboardClearFunc, G_OBJECT(m_pOwner)); - m_aGtkTargets = aGtkTargets; - } - - aEv.Contents = getContents(); - - aGuard.clear(); - - if( xOldOwner.is() && xOldOwner != xClipboardOwner ) - xOldOwner->lostOwnership( this, xOldContents ); - for( std::list< Reference< datatransfer::clipboard::XClipboardListener > >::iterator it = - xListeners.begin(); it != xListeners.end() ; ++it ) - { - (*it)->changedContents( aEv ); - } -} - -OUString VclGtkClipboard::getName() throw( RuntimeException, std::exception ) -{ - return OUString( "CLIPBOARD" ); -} - -sal_Int8 VclGtkClipboard::getRenderingCapabilities() throw( RuntimeException, std::exception ) -{ - return 0; -} - -void VclGtkClipboard::addClipboardListener( const Reference< datatransfer::clipboard::XClipboardListener >& listener ) - throw( RuntimeException, std::exception ) -{ - osl::ClearableMutexGuard aGuard( m_aMutex ); - - m_aListeners.push_back( listener ); -} - -void VclGtkClipboard::removeClipboardListener( const Reference< datatransfer::clipboard::XClipboardListener >& listener ) - throw( RuntimeException, std::exception ) -{ - osl::ClearableMutexGuard aGuard( m_aMutex ); - - m_aListeners.remove( listener ); -} - -Reference< XInterface > GtkInstance::CreateClipboard(const Sequence< Any >& arguments) -{ - OUString sel; - if (arguments.getLength() == 0) { - sel = "CLIPBOARD"; - } else if (arguments.getLength() != 1 || !(arguments[0] >>= sel)) { - throw css::lang::IllegalArgumentException( - "bad GtkInstance::CreateClipboard arguments", - css::uno::Reference(), -1); - } - - GdkAtom nSelection = (sel == "CLIPBOARD") ? GDK_SELECTION_CLIPBOARD : GDK_SELECTION_PRIMARY; - - return Reference< XInterface >( static_cast(new VclGtkClipboard(nSelection)) ); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3gtkframe.cxx b/vcl/unx/gtk3/gtk3gtkframe.cxx new file mode 100644 index 0000000..dcfebbf --- /dev/null +++ b/vcl/unx/gtk3/gtk3gtkframe.cxx @@ -0,0 +1,3805 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) +# include +#endif +#if defined ENABLE_GMENU_INTEGRATION // defined in gtksalmenu.hxx above +# include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#if OSL_DEBUG_LEVEL > 1 +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef ENABLE_DBUS +#include + +#define GSM_DBUS_SERVICE "org.gnome.SessionManager" +#define GSM_DBUS_PATH "/org/gnome/SessionManager" +#define GSM_DBUS_INTERFACE "org.gnome.SessionManager" +#endif + +#include + +#define IS_WIDGET_REALIZED gtk_widget_get_realized +#define IS_WIDGET_MAPPED gtk_widget_get_mapped + +using namespace com::sun::star; + +int GtkSalFrame::m_nFloats = 0; + +#if defined ENABLE_GMENU_INTEGRATION +static GDBusConnection* pSessionBus = NULL; +#endif + +static sal_uInt16 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_MOD1_MASK) ) + nCode |= KEY_MOD2; + + // Map Meta/Super keys to MOD3 modifier on all Unix systems + // except Mac OS X + if ( (state & GDK_META_MASK ) || ( state & GDK_SUPER_MASK ) ) + nCode |= KEY_MOD3; + return nCode; +} + +static sal_uInt16 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; +} + +static sal_uInt16 GetKeyCode( guint keyval ) +{ + sal_uInt16 nCode = 0; + if( keyval >= GDK_0 && keyval <= GDK_9 ) + nCode = KEY_0 + (keyval-GDK_0); + else if( keyval >= GDK_KP_0 && keyval <= GDK_KP_9 ) + nCode = KEY_0 + (keyval-GDK_KP_0); + else if( keyval >= GDK_A && keyval <= GDK_Z ) + nCode = KEY_A + (keyval-GDK_A ); + else if( keyval >= GDK_a && keyval <= GDK_z ) + nCode = KEY_A + (keyval-GDK_a ); + else if( keyval >= GDK_F1 && keyval <= GDK_F26 ) + { + switch( keyval ) + { + // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx + case GDK_L2: + nCode = KEY_F12; + break; + case GDK_L3: nCode = KEY_PROPERTIES; break; + case GDK_L4: nCode = KEY_UNDO; break; + case GDK_L6: nCode = KEY_COPY; break; // KEY_F16 + case GDK_L8: nCode = KEY_PASTE; break; // KEY_F18 + case GDK_L10: nCode = KEY_CUT; break; // KEY_F20 + default: + nCode = KEY_F1 + (keyval-GDK_F1); break; + } + } + else + { + switch( keyval ) + { + case GDK_KP_Down: + case GDK_Down: nCode = KEY_DOWN; break; + case GDK_KP_Up: + case GDK_Up: nCode = KEY_UP; break; + case GDK_KP_Left: + case GDK_Left: nCode = KEY_LEFT; break; + case GDK_KP_Right: + case GDK_Right: nCode = KEY_RIGHT; break; + case GDK_KP_Begin: + case GDK_KP_Home: + case GDK_Begin: + case GDK_Home: nCode = KEY_HOME; break; + case GDK_KP_End: + case GDK_End: nCode = KEY_END; break; + case GDK_KP_Page_Up: + case GDK_Page_Up: nCode = KEY_PAGEUP; break; + case GDK_KP_Page_Down: + case GDK_Page_Down: nCode = KEY_PAGEDOWN; break; + case GDK_KP_Enter: + case GDK_Return: nCode = KEY_RETURN; break; + case GDK_Escape: nCode = KEY_ESCAPE; break; + case GDK_ISO_Left_Tab: + case GDK_KP_Tab: + case GDK_Tab: nCode = KEY_TAB; break; + case GDK_BackSpace: nCode = KEY_BACKSPACE; break; + case GDK_KP_Space: + case GDK_space: nCode = KEY_SPACE; break; + case GDK_KP_Insert: + case GDK_Insert: nCode = KEY_INSERT; break; + case GDK_KP_Delete: + case GDK_Delete: nCode = KEY_DELETE; break; + case GDK_plus: + case GDK_KP_Add: nCode = KEY_ADD; break; + case GDK_minus: + case GDK_KP_Subtract: nCode = KEY_SUBTRACT; break; + case GDK_asterisk: + case GDK_KP_Multiply: nCode = KEY_MULTIPLY; break; + case GDK_slash: + case GDK_KP_Divide: nCode = KEY_DIVIDE; break; + case GDK_period: nCode = KEY_POINT; break; + case GDK_decimalpoint: nCode = KEY_POINT; break; + case GDK_comma: nCode = KEY_COMMA; break; + case GDK_less: nCode = KEY_LESS; break; + case GDK_greater: nCode = KEY_GREATER; break; + case GDK_KP_Equal: + case GDK_equal: nCode = KEY_EQUAL; break; + case GDK_Find: nCode = KEY_FIND; break; + case GDK_Menu: nCode = KEY_CONTEXTMENU;break; + case GDK_Help: nCode = KEY_HELP; break; + case GDK_Undo: nCode = KEY_UNDO; break; + case GDK_Redo: nCode = KEY_REPEAT; break; + case GDK_KP_Decimal: + case GDK_KP_Separator: nCode = KEY_DECIMAL; break; + case GDK_asciitilde: nCode = KEY_TILDE; break; + case GDK_leftsinglequotemark: + case GDK_quoteleft: nCode = KEY_QUOTELEFT; break; + case GDK_bracketleft: nCode = KEY_BRACKETLEFT; break; + case GDK_bracketright: nCode = KEY_BRACKETRIGHT; break; + case GDK_semicolon: nCode = KEY_SEMICOLON; break; + case GDK_quoteright: nCode = KEY_QUOTERIGHT; break; + // some special cases, also see saldisp.cxx + // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000 + 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 + case 0x1000FF00: + nCode = KEY_DELETE; + break; + // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000 + 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 + 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 + 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; + } + } + + return nCode; +} + +static guint 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, NULL, NULL, NULL); + return updated_keyval; +} + +// 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 ) {} +}; + +inline 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; +} + +namespace { +/// Decouple SalFrame lifetime from damagetracker lifetime +struct DamageTracker : public basebmp::IBitmapDeviceDamageTracker +{ + DamageTracker(GtkSalFrame& rFrame) : m_rFrame(rFrame) + {} + + virtual ~DamageTracker() {} + + virtual void damaged(const basegfx::B2IBox& rDamageRect) const SAL_OVERRIDE + { + m_rFrame.damaged(rDamageRect); + } + + GtkSalFrame& m_rFrame; +}; +} + +static bool dumpframes = false; + +void GtkSalFrame::doKeyCallback( guint state, + guint keyval, + guint16 hardware_keycode, + guint8 group, + guint32 time, + sal_Unicode aOrigCode, + bool bDown, + bool bSendRelease + ) +{ + SalKeyEvent aEvent; + + aEvent.mnTime = time; + aEvent.mnCharCode = aOrigCode; + aEvent.mnRepeat = 0; + + vcl::DeletionListener aDel( this ); + +#if 0 + // shift-zero forces a re-draw and event is swallowed + if (keyval == GDK_0) + { + fprintf( stderr, "force widget_queue_draw\n"); + gtk_widget_queue_draw (m_pFixedContainer); + return; + } + else if (keyval == GDK_1) + { + fprintf( stderr, "force repaint all\n"); + TriggerPaintEvent(); + return; + } + else if (keyval == GDK_2) + { + dumpframes = !dumpframes; + fprintf(stderr, "toggle dump frames to %d\n", dumpframes); + return; + } +#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( 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_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); + } + + aEvent.mnCode |= GetKeyModCode( state ); + + if( bDown ) + { + bool bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent ); + // #i46889# copy AlternatKeyCode handling from generic plugin + if( ! bHandled ) + { + KeyAlternate aAlternate = GetAlternateKeyCode( aEvent.mnCode ); + if( aAlternate.nKeyCode ) + { + aEvent.mnCode = aAlternate.nKeyCode; + if( aAlternate.nCharCode ) + aEvent.mnCharCode = aAlternate.nCharCode; + bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent ); + } + } + if( bSendRelease && ! aDel.isDeleted() ) + { + CallCallback( SALEVENT_KEYUP, &aEvent ); + } + } + else + CallCallback( SALEVENT_KEYUP, &aEvent ); +} + +GtkSalFrame::GraphicsHolder::~GraphicsHolder() +{ + delete pGraphics; +} + +GtkSalFrame::GtkSalFrame( SalFrame* pParent, sal_uLong nStyle ) + : m_nXScreen( getDisplay()->GetDefaultXScreen() ) +{ + getDisplay()->registerFrame( this ); + m_bDefaultPos = true; + m_bDefaultSize = ( (nStyle & SAL_FRAME_STYLE_SIZEABLE) && ! pParent ); + m_bWindowIsGtkPlug = false; +#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) + m_pLastSyncedDbusMenu = NULL; +#endif + Init( pParent, nStyle ); +} + +GtkSalFrame::GtkSalFrame( SystemParentData* pSysData ) + : m_nXScreen( getDisplay()->GetDefaultXScreen() ) +{ + getDisplay()->registerFrame( this ); + // permanently ignore errors from our unruly children ... + GetGenericData()->ErrorTrapPush(); + m_bDefaultPos = true; + m_bDefaultSize = true; +#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) + m_pLastSyncedDbusMenu = NULL; +#endif + Init( pSysData ); +} + +#ifdef ENABLE_GMENU_INTEGRATION + +static void +gdk_x11_window_set_utf8_property (GdkWindow *window, + const gchar *name, + const gchar *value) +{ +} + +// AppMenu watch functions. + +static void ObjectDestroyedNotify( gpointer data ) +{ + if ( data ) { + g_object_unref( data ); + } +} + +#if defined(ENABLE_DBUS) && defined(ENABLE_GIO) +void GtkSalFrame::EnsureDbusMenuSynced() +{ + GtkSalMenu* pSalMenu = static_cast(GetMenu()); + if(m_pLastSyncedDbusMenu != pSalMenu) { + m_pLastSyncedDbusMenu = pSalMenu; + static_cast(pSalMenu)->Activate(); + } +} +#endif + +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(); + } +} + +static void activate_uno(GSimpleAction *action, GVariant*, gpointer) +{ + uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + + uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext ); + + uno::Reference < css::frame::XFrame > xFrame(xDesktop->getActiveFrame()); + if (!xFrame.is()) + xFrame = uno::Reference < css::frame::XFrame >(xDesktop, uno::UNO_QUERY); + + if (!xFrame.is()) + return; + + uno::Reference< css::frame::XDispatchProvider > xDispatchProvider(xFrame, uno::UNO_QUERY); + if (!xDispatchProvider.is()) + return; + + gchar *strval = NULL; + g_object_get(action, "name", &strval, NULL); + if (!strval) + return; + + if (strcmp(strval, "New") == 0) + { + uno::Reference xModuleManager(frame::ModuleManager::create(xContext)); + OUString aModuleId(xModuleManager->identify(xFrame)); + if (aModuleId.isEmpty()) + return; + + comphelper::SequenceAsHashMap lModuleDescription(xModuleManager->getByName(aModuleId)); + OUString sFactoryService; + lModuleDescription[OUString("ooSetupFactoryEmptyDocumentURL")] >>= sFactoryService; + if (sFactoryService.isEmpty()) + return; + + uno::Sequence < css::beans::PropertyValue > args(0); + xDesktop->loadComponentFromURL(sFactoryService, OUString("_blank"), 0, args); + return; + } + + OUString sCommand(".uno:"); + sCommand += OUString(strval, strlen(strval), RTL_TEXTENCODING_UTF8); + g_free(strval); + + css::util::URL aCommand; + aCommand.Complete = sCommand; + uno::Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create(xContext); + xParser->parseStrict(aCommand); + + uno::Reference< css::frame::XDispatch > xDisp = xDispatchProvider->queryDispatch(aCommand, OUString(), 0); + + if (!xDisp.is()) + return; + + xDisp->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >()); +} + +static const GActionEntry app_entries[] = { + { "OptionsTreeDialog", activate_uno, NULL, NULL, NULL, {0} }, + { "About", activate_uno, NULL, NULL, NULL, {0} }, + { "HelpIndex", activate_uno, NULL, NULL, NULL, {0} }, + { "Quit", activate_uno, NULL, NULL, NULL, {0} }, + { "New", activate_uno, NULL, NULL, NULL, {0} } +}; + +gboolean ensure_dbus_setup( gpointer data ) +{ + GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( data ); + GdkWindow* gdkWindow = widget_get_window( pSalFrame->getWindow() ); + + if ( gdkWindow != NULL && g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) == NULL ) + { + // Get a DBus session connection. + if(!pSessionBus) + pSessionBus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + if( !pSessionBus ) + return FALSE; + + // Create menu model and action group attached to this frame. + GMenuModel* pMenuModel = G_MENU_MODEL( g_lo_menu_new() ); + GActionGroup* pActionGroup = reinterpret_cast(g_lo_action_group_new( static_cast< gpointer >( pSalFrame ) )); + + // Generate menu paths. + ::Window windowId = GDK_WINDOW_XID( gdkWindow ); + gchar* aDBusWindowPath = g_strdup_printf( "/org/libreoffice/window/%lu", windowId ); + gchar* aDBusMenubarPath = g_strdup_printf( "/org/libreoffice/window/%lu/menus/menubar", windowId ); + + // 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 ); + + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_ID", "org.libreoffice" ); + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_UNIQUE_BUS_NAME", g_dbus_connection_get_unique_name( pSessionBus ) ); + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_OBJECT_PATH", "/org/libreoffice" ); + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_WINDOW_OBJECT_PATH", aDBusWindowPath ); + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_MENUBAR_OBJECT_PATH", aDBusMenubarPath ); + + // 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, NULL); + SAL_INFO("vcl.unity", "exporting action group at " << pActionGroup << " for window " << windowId); + pSalFrame->m_nActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, aDBusWindowPath, pActionGroup, NULL); + pSalFrame->m_nHudAwarenessId = hud_awareness_register( pSessionBus, aDBusMenubarPath, hud_activated, pSalFrame, NULL, NULL ); + + // fdo#70885 we don't want app menu under Unity + bool bDesktopIsUnity = (SalGetDesktopEnvironment() == "UNITY"); + + if (!bDesktopIsUnity) + gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APP_MENU_OBJECT_PATH", "/org/libreoffice/menus/appmenu" ); + + //app menu, to-do translations, block normal menus when active, honor use appmenu settings + ResMgr* pMgr = ImplGetResMgr(); + if( pMgr && !bDesktopIsUnity ) + { + GMenu *menu = g_menu_new (); + GMenuItem* item; + + GMenu *firstsubmenu = g_menu_new (); + + OString sNew(OUStringToOString(ResId(SV_BUTTONTEXT_NEW, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sNew.getStr(), "app.New"); + g_menu_append_item( firstsubmenu, item ); + g_object_unref(item); + + g_menu_append_section( menu, NULL, G_MENU_MODEL(firstsubmenu)); + g_object_unref(firstsubmenu); + + GMenu *secondsubmenu = g_menu_new (); + + OString sPreferences(OUStringToOString(ResId(SV_STDTEXT_PREFERENCES, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sPreferences.getStr(), "app.OptionsTreeDialog"); + g_menu_append_item( secondsubmenu, item ); + g_object_unref(item); + + g_menu_append_section( menu, NULL, G_MENU_MODEL(secondsubmenu)); + g_object_unref(secondsubmenu); + + GMenu *thirdsubmenu = g_menu_new (); + + OString sHelp(OUStringToOString(ResId(SV_BUTTONTEXT_HELP, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sHelp.getStr(), "app.HelpIndex"); + g_menu_append_item( thirdsubmenu, item ); + g_object_unref(item); + + OString sAbout(OUStringToOString(ResId(SV_STDTEXT_ABOUT, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sAbout.getStr(), "app.About"); + g_menu_append_item( thirdsubmenu, item ); + g_object_unref(item); + + OString sQuit(OUStringToOString(ResId(SV_MENU_MAC_QUITAPP, *pMgr).toString(), + RTL_TEXTENCODING_UTF8).replaceFirst("~", "_")); + + item = g_menu_item_new(sQuit.getStr(), "app.Quit"); + g_menu_append_item( thirdsubmenu, item ); + g_object_unref(item); + g_menu_append_section( menu, NULL, G_MENU_MODEL(thirdsubmenu)); + g_object_unref(thirdsubmenu); + + GSimpleActionGroup *group = g_simple_action_group_new (); +#if GLIB_CHECK_VERSION(2,38,0) // g_simple_action_group_add_entries is deprecated since 2.38 + g_action_map_add_action_entries (G_ACTION_MAP (group), app_entries, G_N_ELEMENTS (app_entries), NULL); +#else + g_simple_action_group_add_entries (group, app_entries, G_N_ELEMENTS (app_entries), NULL); +#endif + GActionGroup* pAppActionGroup = G_ACTION_GROUP(group); + + pSalFrame->m_nAppActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, "/org/libreoffice", pAppActionGroup, NULL); + g_object_unref(pAppActionGroup); + pSalFrame->m_nAppMenuExportId = g_dbus_connection_export_menu_model (pSessionBus, "/org/libreoffice/menus/appmenu", G_MENU_MODEL (menu), NULL); + g_object_unref(menu); + } + + g_free( aDBusMenubarPath ); + g_free( aDBusWindowPath ); + } + + return FALSE; +} + +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 != NULL ) + { + GtkSalMenu* pGtkSalMenu = static_cast(pSalMenu); + pGtkSalMenu->Display( true ); + pGtkSalMenu->UpdateFull(); + } +} + +// 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"); + + //pSessionBus = NULL; + GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data ); + + SalMenu* pSalMenu = pSalFrame->GetMenu(); + + if ( pSalMenu ) { + GtkSalMenu* pGtkSalMenu = static_cast< GtkSalMenu* >( pSalMenu ); + pGtkSalMenu->Display( false ); + } +} +#endif + +void GtkSalFrame::EnsureAppMenuWatch() +{ +#ifdef ENABLE_GMENU_INTEGRATION + if ( !m_nWatcherId ) + { + // Get a DBus session connection. + if ( pSessionBus == NULL ) + { + pSessionBus = g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, NULL ); + + if ( pSessionBus == NULL ) + 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, + static_cast(this), + NULL ); + } + + //ensure_dbus_setup( this ); +#else + (void) this; // loplugin:staticmethods +#endif +} + +void GtkSalFrame::InvalidateGraphics() +{ + for (unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); ++i) + { + if( !m_aGraphics[i].pGraphics ) + continue; + m_aGraphics[i].bInUse = false; + } +} + +GtkSalFrame::~GtkSalFrame() +{ + InvalidateGraphics(); + + if( m_pParent ) + m_pParent->m_aChildren.remove( this ); + + getDisplay()->deregisterFrame( this ); + + if( m_pRegion ) + { + cairo_region_destroy( m_pRegion ); + } + + delete m_pIMHandler; + + GtkWidget *pEventWidget = getMouseEventWidget(); + for (auto handler_id : m_aMouseSignalIds) + g_signal_handler_disconnect(G_OBJECT(pEventWidget), handler_id); + if( m_pFixedContainer ) + gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer ) ); + if( m_pEventBox ) + gtk_widget_destroy( GTK_WIDGET(m_pEventBox) ); + { + SolarMutexGuard aGuard; +#if defined ENABLE_GMENU_INTEGRATION + if(m_nWatcherId) + g_bus_unwatch_name(m_nWatcherId); +#endif + if( m_pWindow ) + { + g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", NULL ); + +#if defined ENABLE_GMENU_INTEGRATION + 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_nAppMenuExportId ) + g_dbus_connection_unexport_menu_model( pSessionBus, m_nAppMenuExportId ); + if ( m_nActionGroupExportId ) + g_dbus_connection_unexport_action_group( pSessionBus, m_nActionGroupExportId ); + if ( m_nAppActionGroupExportId ) + g_dbus_connection_unexport_action_group( pSessionBus, m_nAppActionGroupExportId ); + } +#endif + gtk_widget_destroy( m_pWindow ); + } + } + if( m_pForeignParent ) + g_object_unref( G_OBJECT( m_pForeignParent ) ); + if( m_pForeignTopLevel ) + g_object_unref( G_OBJECT( m_pForeignTopLevel) ); +} + +void GtkSalFrame::moveWindow( long nX, long nY ) +{ + if( isChild( false, true ) ) + { + if( m_pParent ) + gtk_fixed_move( m_pParent->getFixedContainer(), + m_pWindow, + nX - m_pParent->maGeometry.nX, nY - m_pParent->maGeometry.nY ); + } + else + gtk_window_move( GTK_WINDOW(m_pWindow), nX, nY ); +} + +void GtkSalFrame::widget_set_size_request(long nWidth, long nHeight) +{ + gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer), nWidth, nHeight ); +} + +void GtkSalFrame::window_resize(long nWidth, long nHeight) +{ + gtk_window_resize(GTK_WINDOW(m_pWindow), nWidth, nHeight); +} + +void GtkSalFrame::resizeWindow( long nWidth, long nHeight ) +{ + if( isChild( false, true ) ) + { + widget_set_size_request(nWidth, nHeight); + } + else if( ! isChild( true, false ) ) + window_resize(nWidth, nHeight); +} + +static void +ooo_fixed_class_init(GtkFixedClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + widget_class->get_accessible = ooo_fixed_get_accessible; +} + +/* + * 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 */ + reinterpret_cast(ooo_fixed_class_init), /* class init */ + nullptr, /* class finalize */ + NULL, /* class data */ + sizeof (GtkFixed), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static( GTK_TYPE_FIXED, "OOoFixed", + &tinfo, (GTypeFlags) 0); + } + + return type; +} + +void GtkSalFrame::updateScreenNumber() +{ + int nScreen = 0; + GdkScreen *pScreen = gtk_widget_get_screen( m_pWindow ); + if( pScreen ) + nScreen = getDisplay()->getSystem()->getScreenMonitorIdx( pScreen, maGeometry.nX, maGeometry.nY ); + maGeometry.nDisplayScreenNumber = nScreen; +} + +GtkWidget *GtkSalFrame::getMouseEventWidget() const +{ + return GTK_WIDGET(m_pEventBox); +} + +void GtkSalFrame::InitCommon() +{ + m_pEventBox = GTK_EVENT_BOX(gtk_event_box_new()); + gtk_widget_add_events( GTK_WIDGET(m_pEventBox), + GDK_ALL_EVENTS_MASK ); + gtk_container_add( GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pEventBox) ); + + // add the fixed container child, + // fixed is needed since we have to position plugin windows + m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), NULL )); + gtk_container_add( GTK_CONTAINER(m_pEventBox), GTK_WIDGET(m_pFixedContainer) ); + + GtkWidget *pEventWidget = getMouseEventWidget(); + + gtk_widget_set_app_paintable(GTK_WIDGET(m_pFixedContainer), true); + /*non-X11 displays won't show anything at all without double-buffering + enabled*/ + if (GDK_IS_X11_DISPLAY(getGdkDisplay())) + gtk_widget_set_double_buffered(GTK_WIDGET(m_pFixedContainer), false); + gtk_widget_set_redraw_on_allocate(GTK_WIDGET(m_pFixedContainer), false); + + + // connect signals + g_signal_connect( G_OBJECT(m_pWindow), "style-set", G_CALLBACK(signalStyleSet), this ); + 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), "motion-notify-event", G_CALLBACK(signalMotion), this )); + m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-release-event", G_CALLBACK(signalButton), this )); + 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 ); + GtkGesture *pSwipe = gtk_gesture_swipe_new(pEventWidget); + g_signal_connect(pSwipe, "swipe", G_CALLBACK(gestureSwipe), this); + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pSwipe), GTK_PHASE_TARGET); + g_object_weak_ref(G_OBJECT(pEventWidget), reinterpret_cast(g_object_unref), pSwipe); + + GtkGesture *pLongPress = gtk_gesture_long_press_new(pEventWidget); + g_signal_connect(pLongPress, "pressed", G_CALLBACK(gestureLongPress), this); + gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pLongPress), GTK_PHASE_TARGET); + g_object_weak_ref(G_OBJECT(pEventWidget), reinterpret_cast(g_object_unref), pLongPress); + + g_signal_connect( G_OBJECT(m_pWindow), "focus-in-event", G_CALLBACK(signalFocus), this ); + g_signal_connect( G_OBJECT(m_pWindow), "focus-out-event", G_CALLBACK(signalFocus), this ); + 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), "configure-event", G_CALLBACK(signalConfigure), this ); + 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 ); + g_signal_connect( G_OBJECT(m_pWindow), "delete-event", G_CALLBACK(signalDelete), this ); + g_signal_connect( G_OBJECT(m_pWindow), "window-state-event", G_CALLBACK(signalWindowState), this ); + g_signal_connect( G_OBJECT(m_pWindow), "scroll-event", G_CALLBACK(signalScroll), this ); + g_signal_connect( G_OBJECT(m_pWindow), "leave-notify-event", G_CALLBACK(signalCrossing), this ); + g_signal_connect( G_OBJECT(m_pWindow), "enter-notify-event", G_CALLBACK(signalCrossing), this ); + g_signal_connect( G_OBJECT(m_pWindow), "visibility-notify-event", G_CALLBACK(signalVisibility), this ); + g_signal_connect( G_OBJECT(m_pWindow), "destroy", G_CALLBACK(signalDestroy), this ); + + // init members + m_pCurrentCursor = NULL; + m_nKeyModifiers = 0; + m_bFullscreen = false; + m_bSpanMonitorsWhenFullscreen = false; + m_nState = GDK_WINDOW_STATE_WITHDRAWN; + m_nVisibility = GDK_VISIBILITY_FULLY_OBSCURED; + m_nLastScrollEventTime = GDK_CURRENT_TIME; + m_bSendModChangeOnRelease = false; + m_pIMHandler = NULL; + m_hBackgroundPixmap = None; + m_nSavedScreenSaverTimeout = 0; + m_nGSMCookie = 0; + m_nExtStyle = 0; + m_pRegion = NULL; + m_ePointerStyle = static_cast(0xffff); + m_bSetFocusOnMap = false; + m_pSalMenu = NULL; + m_nWatcherId = 0; + m_nMenuExportId = 0; + m_nAppMenuExportId = 0; + m_nActionGroupExportId = 0; + m_nAppActionGroupExportId = 0; + m_nHudAwarenessId = 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_VISIBILITY_NOTIFY_MASK | GDK_SCROLL_MASK + ); + + // show the widgets + gtk_widget_show_all( GTK_WIDGET(m_pEventBox) ); + + // realize the window, we need an XWindow id + gtk_widget_realize( m_pWindow ); + + //system data + m_aSystemData.nSize = sizeof( SystemEnvData ); + static int nWindow = 0; + m_aSystemData.aWindow = nWindow; + m_aSystemData.aShellWindow = nWindow; + ++nWindow; + m_aSystemData.pSalFrame = this; + m_aSystemData.pWidget = m_pWindow; + m_aSystemData.nScreen = m_nXScreen.getXScreen(); + m_aSystemData.pAppContext = NULL; + m_aSystemData.pShellWidget = m_aSystemData.pWidget; + + // fake an initial geometry, gets updated via configure event or SetPosSize + if( m_bDefaultPos || m_bDefaultSize ) + { + Size aDefSize = calcDefaultSize(); + maGeometry.nX = -1; + maGeometry.nY = -1; + maGeometry.nWidth = aDefSize.Width(); + maGeometry.nHeight = aDefSize.Height(); + if( m_pParent ) + { + // approximation + maGeometry.nTopDecoration = m_pParent->maGeometry.nTopDecoration; + maGeometry.nBottomDecoration = m_pParent->maGeometry.nBottomDecoration; + maGeometry.nLeftDecoration = m_pParent->maGeometry.nLeftDecoration; + maGeometry.nRightDecoration = m_pParent->maGeometry.nRightDecoration; + } + else + { + maGeometry.nTopDecoration = 0; + maGeometry.nBottomDecoration = 0; + maGeometry.nLeftDecoration = 0; + maGeometry.nRightDecoration = 0; + } + } + updateScreenNumber(); + + SetIcon(1); + +} + +GtkSalFrame *GtkSalFrame::getFromWindow( GtkWindow *pWindow ) +{ + return static_cast(g_object_get_data( G_OBJECT( pWindow ), "SalFrame" )); +} + +void GtkSalFrame::Init( SalFrame* pParent, sal_uLong nStyle ) +{ + if( nStyle & SAL_FRAME_STYLE_DEFAULT ) // ensure default style + { + nStyle |= SAL_FRAME_STYLE_MOVEABLE | SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_CLOSEABLE; + nStyle &= ~SAL_FRAME_STYLE_FLOAT; + } + + m_pParent = static_cast(pParent); + m_pForeignParent = NULL; + m_aForeignParentWindow = None; + m_pForeignTopLevel = NULL; + m_aForeignTopLevelWindow = None; + m_nStyle = nStyle; + + GtkWindowType eWinType = ( (nStyle & SAL_FRAME_STYLE_FLOAT) && + ! (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION| + SAL_FRAME_STYLE_FLOAT_FOCUSABLE)) + ) + ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL; + + if( nStyle & SAL_FRAME_STYLE_SYSTEMCHILD ) + { + m_pWindow = gtk_event_box_new(); + if( m_pParent ) + { + // insert into container + gtk_fixed_put( m_pParent->getFixedContainer(), + m_pWindow, 0, 0 ); + + } + } + else + { + m_pWindow = gtk_widget_new( GTK_TYPE_WINDOW, "type", eWinType, + "visible", FALSE, NULL ); + } + g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", this ); + g_object_set_data( G_OBJECT( m_pWindow ), "libo-version", (gpointer)LIBO_VERSION_DOTTED); + + // force wm class hint + m_nExtStyle = ~0; + if (m_pParent) + m_sWMClass = m_pParent->m_sWMClass; + SetExtendedFrameStyle( 0 ); + + if( m_pParent && m_pParent->m_pWindow && ! isChild() ) + gtk_window_set_screen( GTK_WINDOW(m_pWindow), gtk_window_get_screen( GTK_WINDOW(m_pParent->m_pWindow) ) ); + + if (m_pParent) + { + if (!(m_pParent->m_nStyle & SAL_FRAME_STYLE_PLUG)) + gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), GTK_WINDOW(m_pParent->m_pWindow) ); + m_pParent->m_aChildren.push_back( this ); + } + + InitCommon(); + + // set window type + bool bDecoHandling = + ! isChild() && + ( ! (nStyle & SAL_FRAME_STYLE_FLOAT) || + (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) ); + + if( bDecoHandling ) + { + GdkWindowTypeHint eType = GDK_WINDOW_TYPE_HINT_NORMAL; + if( (nStyle & SAL_FRAME_STYLE_DIALOG) && m_pParent != 0 ) + eType = GDK_WINDOW_TYPE_HINT_DIALOG; + if( (nStyle & SAL_FRAME_STYLE_INTRO) ) + { + gtk_window_set_role( GTK_WINDOW(m_pWindow), "splashscreen" ); + eType = GDK_WINDOW_TYPE_HINT_SPLASHSCREEN; + } + else if( (nStyle & SAL_FRAME_STYLE_TOOLWINDOW ) ) + { + eType = GDK_WINDOW_TYPE_HINT_UTILITY; + gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow), true ); + } + else if( (nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) + { + eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; + gtk_window_set_accept_focus(GTK_WINDOW(m_pWindow), false); + } + else if( (nStyle & SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) + { + eType = GDK_WINDOW_TYPE_HINT_UTILITY; + } + gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), eType ); + gtk_window_set_gravity( GTK_WINDOW(m_pWindow), GDK_GRAVITY_STATIC ); + } + else if( (nStyle & SAL_FRAME_STYLE_FLOAT) ) + { + gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), GDK_WINDOW_TYPE_HINT_POPUP_MENU ); + } + + if( bDecoHandling ) + { + gtk_window_set_resizable( GTK_WINDOW(m_pWindow), (nStyle & SAL_FRAME_STYLE_SIZEABLE) != 0 ); + if( ( (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION)) ) ) + gtk_window_set_accept_focus(GTK_WINDOW(m_pWindow), false); + } +} + +GdkNativeWindow GtkSalFrame::findTopLevelSystemWindow( GdkNativeWindow aWindow ) +{ + (void)aWindow; + //FIXME: no findToplevelSystemWindow + return 0; +} + +void GtkSalFrame::Init( SystemParentData* pSysData ) +{ + m_pParent = NULL; + m_aForeignParentWindow = (GdkNativeWindow)pSysData->aWindow; + m_pForeignParent = NULL; + m_aForeignTopLevelWindow = findTopLevelSystemWindow( (GdkNativeWindow)pSysData->aWindow ); + m_pForeignTopLevel = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignTopLevelWindow ); + gdk_window_set_events( m_pForeignTopLevel, GDK_STRUCTURE_MASK ); + + if( pSysData->nSize > sizeof(pSysData->nSize)+sizeof(pSysData->aWindow) && pSysData->bXEmbedSupport ) + { + m_pWindow = gtk_plug_new_for_display( getGdkDisplay(), pSysData->aWindow ); + m_bWindowIsGtkPlug = true; + widget_set_can_default( m_pWindow, true ); + widget_set_can_focus( m_pWindow, true ); + gtk_widget_set_sensitive( m_pWindow, true ); + } + else + { + m_pWindow = gtk_window_new( GTK_WINDOW_POPUP ); + m_bWindowIsGtkPlug = false; + } + m_nStyle = SAL_FRAME_STYLE_PLUG; + InitCommon(); + + m_pForeignParent = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignParentWindow ); + gdk_window_set_events( m_pForeignParent, GDK_STRUCTURE_MASK ); + + //FIXME: Handling embedded windows, is going to be fun ... +} + +void GtkSalFrame::askForXEmbedFocus( sal_Int32 i_nTimeCode ) +{ + (void) this; // loplugin:staticmethods + (void)i_nTimeCode; + //FIXME: no askForXEmbedFocus for gtk3 yet +} + +void GtkSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle ) +{ + if( nStyle != m_nExtStyle && ! isChild() ) + { + m_nExtStyle = nStyle; + updateWMClass(); + } +} + +SalGraphics* GtkSalFrame::AcquireGraphics() +{ + if( m_pWindow ) + { + for( int i = 0; i < nMaxGraphics; i++ ) + { + if( ! m_aGraphics[i].bInUse ) + { + m_aGraphics[i].bInUse = true; + if( ! m_aGraphics[i].pGraphics ) + { + m_aGraphics[i].pGraphics = new GtkSalGraphics( this, m_pWindow ); + if( !m_aFrame.get() ) + { + AllocateFrame(); + TriggerPaintEvent(); + } + m_aGraphics[i].pGraphics->setDevice( m_aFrame ); + } + return m_aGraphics[i].pGraphics; + } + } + } + + return NULL; +} + +void GtkSalFrame::ReleaseGraphics( SalGraphics* pGraphics ) +{ + for( int i = 0; i < nMaxGraphics; i++ ) + { + if( m_aGraphics[i].pGraphics == pGraphics ) + { + m_aGraphics[i].bInUse = false; + break; + } + } +} + +bool GtkSalFrame::PostEvent( void* pData ) +{ + getDisplay()->SendInternalEvent( this, pData ); + return true; +} + +void GtkSalFrame::SetTitle( const OUString& rTitle ) +{ + m_aTitle = rTitle; + if( m_pWindow && ! isChild() ) + gtk_window_set_title( GTK_WINDOW(m_pWindow), OUStringToOString( rTitle, RTL_TEXTENCODING_UTF8 ).getStr() ); +} + +static inline sal_uInt8 * +getRow( BitmapBuffer *pBuffer, sal_uLong nRow ) +{ + if( BMP_SCANLINE_ADJUSTMENT( pBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN ) + return pBuffer->mpBits + nRow * pBuffer->mnScanlineSize; + else + return pBuffer->mpBits + ( pBuffer->mnHeight - nRow - 1 ) * pBuffer->mnScanlineSize; +} + +static GdkPixbuf * +bitmapToPixbuf( SalBitmap *pSalBitmap, SalBitmap *pSalAlpha ) +{ + g_return_val_if_fail( pSalBitmap != NULL, NULL ); + g_return_val_if_fail( pSalAlpha != NULL, NULL ); + + BitmapBuffer *pBitmap = pSalBitmap->AcquireBuffer( BITMAP_READ_ACCESS ); + g_return_val_if_fail( pBitmap != NULL, NULL ); + g_return_val_if_fail( pBitmap->mnBitCount == 24 || pBitmap->mnBitCount == 32, NULL ); + + BitmapBuffer *pAlpha = pSalAlpha->AcquireBuffer( BITMAP_READ_ACCESS ); + g_return_val_if_fail( pAlpha != NULL, NULL ); + g_return_val_if_fail( pAlpha->mnBitCount == 8, NULL ); + + Size aSize = pSalBitmap->GetSize(); + g_return_val_if_fail( pSalAlpha->GetSize() == aSize, NULL ); + + int nX, nY; + guchar *pPixbufData = static_cast(g_malloc (4 * aSize.Width() * aSize.Height() )); + guchar *pDestData = pPixbufData; + + for( nY = 0; nY < pBitmap->mnHeight; nY++ ) + { + sal_uInt8 *pData = getRow( pBitmap, nY ); + sal_uInt8 *pAlphaData = getRow( pAlpha, nY ); + + for( nX = 0; nX < pBitmap->mnWidth; nX++ ) + { + BitmapColor aColor; + if (pBitmap->mnFormat == BMP_FORMAT_24BIT_TC_BGR) + { + aColor = BitmapColor(pData[2], pData[1], pData[0]); + pData += 3; + } + else if (pBitmap->mnFormat == BMP_FORMAT_24BIT_TC_RGB) + { + aColor = BitmapColor(pData[0], pData[1], pData[2]); + pData += 3; + } + else + { + pBitmap->maColorMask.GetColorFor32Bit(aColor, pData); + pData += 4; + } + *pDestData++ = aColor.GetRed(); + *pDestData++ = aColor.GetGreen(); + *pDestData++ = aColor.GetBlue(); + *pDestData++ = 255 - *pAlphaData++; + } + } + + pSalBitmap->ReleaseBuffer( pBitmap, BITMAP_READ_ACCESS ); + pSalAlpha->ReleaseBuffer( pAlpha, BITMAP_READ_ACCESS ); + + return gdk_pixbuf_new_from_data( pPixbufData, + GDK_COLORSPACE_RGB, true, 8, + aSize.Width(), aSize.Height(), + aSize.Width() * 4, + reinterpret_cast(g_free), + NULL ); +} + +void GtkSalFrame::SetIcon( sal_uInt16 nIcon ) +{ + if( (m_nStyle & (SAL_FRAME_STYLE_PLUG|SAL_FRAME_STYLE_SYSTEMCHILD|SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_INTRO|SAL_FRAME_STYLE_OWNERDRAWDECORATION)) + || ! m_pWindow ) + return; + + if( !ImplGetResMgr() ) + return; + + GdkPixbuf *pBuf; + GList *pIcons = NULL; + + sal_uInt16 nOffsets[2] = { SV_ICON_SMALL_START, SV_ICON_LARGE_START }; + sal_uInt16 nIndex; + + for( nIndex = 0; nIndex < sizeof(nOffsets)/ sizeof(sal_uInt16); nIndex++ ) + { + // #i44723# workaround gcc temporary problem + ResId aResId( nOffsets[nIndex] + nIcon, *ImplGetResMgr() ); + BitmapEx aIcon( aResId ); + + // #i81083# convert to 24bit/8bit alpha bitmap + Bitmap aBmp = aIcon.GetBitmap(); + if( aBmp.GetBitCount() != 24 || ! aIcon.IsAlpha() ) + { + if( aBmp.GetBitCount() != 24 ) + aBmp.Convert( BMP_CONVERSION_24BIT ); + AlphaMask aMask; + if( ! aIcon.IsAlpha() ) + { + switch( aIcon.GetTransparentType() ) + { + case TRANSPARENT_NONE: + { + sal_uInt8 nTrans = 0; + aMask = AlphaMask( aBmp.GetSizePixel(), &nTrans ); + } + break; + case TRANSPARENT_COLOR: + aMask = AlphaMask( aBmp.CreateMask( aIcon.GetTransparentColor() ) ); + break; + case TRANSPARENT_BITMAP: + aMask = AlphaMask( aIcon.GetMask() ); + break; + default: + OSL_FAIL( "unhandled transparent type" ); + break; + } + } + else + aMask = aIcon.GetAlpha(); + aIcon = BitmapEx( aBmp, aMask ); + } + + ImpBitmap *pIconImpBitmap = aIcon.ImplGetBitmapImpBitmap(); + ImpBitmap *pIconImpMask = aIcon.ImplGetMaskImpBitmap(); + + if( pIconImpBitmap && pIconImpMask ) + { + SalBitmap *pIconBitmap = + pIconImpBitmap->ImplGetSalBitmap(); + SalBitmap *pIconMask = + pIconImpMask->ImplGetSalBitmap(); + + if( ( pBuf = bitmapToPixbuf( pIconBitmap, pIconMask ) ) ) + pIcons = g_list_prepend( pIcons, pBuf ); + } + } + + gtk_window_set_icon_list( GTK_WINDOW(m_pWindow), pIcons ); + + g_list_foreach( pIcons, reinterpret_cast(g_object_unref), NULL ); + g_list_free( pIcons ); +} + +void GtkSalFrame::SetMenu( SalMenu* pSalMenu ) +{ +// if(m_pSalMenu) +// { +// static_cast(m_pSalMenu)->DisconnectFrame(); +// } + m_pSalMenu = pSalMenu; +} + +SalMenu* GtkSalFrame::GetMenu() +{ + return m_pSalMenu; +} + +void GtkSalFrame::DrawMenuBar() +{ +} + +void GtkSalFrame::Center() +{ + if (m_pParent) + gtk_window_set_position(GTK_WINDOW(m_pWindow), GTK_WIN_POS_CENTER_ON_PARENT); + else + gtk_window_set_position(GTK_WINDOW(m_pWindow), GTK_WIN_POS_CENTER); +} + +Size GtkSalFrame::calcDefaultSize() +{ + return bestmaxFrameSizeForScreenSize(getDisplay()->GetScreenSize(GetDisplayScreen())); +} + +void GtkSalFrame::SetDefaultSize() +{ + Size aDefSize = calcDefaultSize(); + + SetPosSize( 0, 0, aDefSize.Width(), aDefSize.Height(), + SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + + if( (m_nStyle & SAL_FRAME_STYLE_DEFAULT) && m_pWindow ) + gtk_window_maximize( GTK_WINDOW(m_pWindow) ); +} + +static void initClientId() +{ + // No session management support for gtk3+ - this is now legacy. +} + +void GtkSalFrame::Show( bool bVisible, bool bNoActivate ) +{ + if( m_pWindow ) + { + if( bVisible ) + { + initClientId(); + getDisplay()->startupNotificationCompleted(); + + if( m_bDefaultPos ) + Center(); + if( m_bDefaultSize ) + SetDefaultSize(); + setMinMaxSize(); + + if( isFloatGrabWindow() && + m_pParent && + m_nFloats == 0 && + ! getDisplay()->GetCaptureFrame() ) + { + /* #i63086# + * outsmart Metacity's "focus:mouse" mode + * which insists on taking the focus from the document + * to the new float. Grab focus to parent frame BEFORE + * showing the float (cannot grab it to the float + * before show). + */ + m_pParent->grabPointer( true, true ); + } + + if( ! bNoActivate && (m_nStyle & SAL_FRAME_STYLE_TOOLWINDOW) ) + m_bSetFocusOnMap = true; + + gtk_widget_show( m_pWindow ); + + if( isFloatGrabWindow() ) + { + m_nFloats++; + if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 1 ) + { + grabPointer(true, true); + GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this; + pKeyboardFrame->grabKeyboard(true); + } + // #i44068# reset parent's IM context + if( m_pParent ) + m_pParent->EndExtTextInput(0); + } + if( m_bWindowIsGtkPlug ) + askForXEmbedFocus( 0 ); + } + else + { + if( isFloatGrabWindow() ) + { + m_nFloats--; + if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 0) + { + GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this; + pKeyboardFrame->grabKeyboard(false); + grabPointer(false); + } + } + gtk_widget_hide( m_pWindow ); + if( m_pIMHandler ) + m_pIMHandler->focusChanged( false ); + // flush here; there may be a very seldom race between + // the display connection used for clipboard and our connection + Flush(); + } + } +} + +void GtkSalFrame::setMinMaxSize() +{ + /* #i34504# metacity (and possibly others) do not treat + * _NET_WM_STATE_FULLSCREEN and max_width/height independently; + * whether they should is undefined. So don't set the max size hint + * for a full screen window. + */ + if( m_pWindow && ! isChild() ) + { + GdkGeometry aGeo; + int aHints = 0; + if( m_nStyle & SAL_FRAME_STYLE_SIZEABLE ) + { + if( m_aMinSize.Width() && m_aMinSize.Height() && ! m_bFullscreen ) + { + aGeo.min_width = m_aMinSize.Width(); + aGeo.min_height = m_aMinSize.Height(); + aHints |= GDK_HINT_MIN_SIZE; + } + if( m_aMaxSize.Width() && m_aMaxSize.Height() && ! m_bFullscreen ) + { + aGeo.max_width = m_aMaxSize.Width(); + aGeo.max_height = m_aMaxSize.Height(); + aHints |= GDK_HINT_MAX_SIZE; + } + } + if( m_bFullscreen && m_aMaxSize.Width() && m_aMaxSize.Height() ) + { + aGeo.max_width = m_aMaxSize.Width(); + aGeo.max_height = m_aMaxSize.Height(); + aHints |= GDK_HINT_MAX_SIZE; + } + if( aHints ) + { + gtk_window_set_geometry_hints( GTK_WINDOW(m_pWindow), + NULL, + &aGeo, + GdkWindowHints( aHints ) ); + } + } +} + +void GtkSalFrame::SetMaxClientSize( long nWidth, long nHeight ) +{ + if( ! isChild() ) + { + m_aMaxSize = Size( nWidth, nHeight ); + setMinMaxSize(); + } +} +void GtkSalFrame::SetMinClientSize( long nWidth, long nHeight ) +{ + if( ! isChild() ) + { + m_aMinSize = Size( nWidth, nHeight ); + if( m_pWindow ) + { + widget_set_size_request(nWidth, nHeight ); + setMinMaxSize(); + } + } +} + +// FIXME: we should really be an SvpSalFrame sub-class, and +// share their AllocateFrame ! +void GtkSalFrame::AllocateFrame() +{ + basegfx::B2IVector aFrameSize( maGeometry.nWidth, maGeometry.nHeight ); + if( ! m_aFrame.get() || m_aFrame->getSize() != aFrameSize ) + { + if( aFrameSize.getX() == 0 ) + aFrameSize.setX( 1 ); + if( aFrameSize.getY() == 0 ) + aFrameSize.setY( 1 ); + m_aFrame = basebmp::createBitmapDevice(aFrameSize, true, + basebmp::FORMAT_THIRTYTWO_BIT_TC_MASK_BGRX); + m_aFrame->setDamageTracker( + basebmp::IBitmapDeviceDamageTrackerSharedPtr(new DamageTracker(*this)) ); + SAL_INFO("vcl.gtk3", "allocated m_aFrame size of " << maGeometry.nWidth << " x " << maGeometry.nHeight); + +#if OSL_DEBUG_LEVEL > 0 // set background to orange + m_aFrame->clear( basebmp::Color( 255, 127, 0 ) ); +#endif + + // update device in existing graphics + for( unsigned int i = 0; i < SAL_N_ELEMENTS( m_aGraphics ); ++i ) + { + if( !m_aGraphics[i].pGraphics ) + continue; + m_aGraphics[i].pGraphics->setDevice( m_aFrame ); + } + } +} + +void GtkSalFrame::SetPosSize( long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags ) +{ + if( !m_pWindow || isChild( true, false ) ) + return; + + if( (nFlags & ( SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT )) && + (nWidth > 0 && nHeight > 0 ) // sometimes stupid things happen + ) + { + m_bDefaultSize = false; + + if( isChild( false, true ) ) + widget_set_size_request(nWidth, nHeight); + else if( ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) ) + window_resize(nWidth, nHeight); + + setMinMaxSize(); + } + else if( m_bDefaultSize ) + SetDefaultSize(); + + m_bDefaultSize = false; + + if( nFlags & ( SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ) ) + { + if( m_pParent ) + { + if( AllSettings::GetLayoutRTL() ) + nX = m_pParent->maGeometry.nWidth-maGeometry.nWidth-1-nX; + nX += m_pParent->maGeometry.nX; + nY += m_pParent->maGeometry.nY; + } + + m_bDefaultPos = false; + + moveWindow(nX, nY); + + updateScreenNumber(); + } + else if( m_bDefaultPos ) + Center(); + + m_bDefaultPos = false; +} + +void GtkSalFrame::GetClientSize( long& rWidth, long& rHeight ) +{ + if( m_pWindow && !(m_nState & GDK_WINDOW_STATE_ICONIFIED) ) + { + rWidth = maGeometry.nWidth; + rHeight = maGeometry.nHeight; + } + else + rWidth = rHeight = 0; +} + +void GtkSalFrame::GetWorkArea( Rectangle& rRect ) +{ + GdkScreen *pScreen = gtk_window_get_screen(GTK_WINDOW(m_pWindow)); + Rectangle aRetRect; + int max = gdk_screen_get_n_monitors (pScreen); + for (int i = 0; i < max; ++i) + { + GdkRectangle aRect; + gdk_screen_get_monitor_workarea(pScreen, i, &aRect); + Rectangle aMonitorRect(aRect.x, aRect.y, aRect.x+aRect.width, aRect.y+aRect.height); + aRetRect.Union(aMonitorRect); + } + rRect = aRetRect; +} + +SalFrame* GtkSalFrame::GetParent() const +{ + return m_pParent; +} + +void GtkSalFrame::SetWindowState( const SalFrameState* pState ) +{ + if( ! m_pWindow || ! pState || isChild( true, false ) ) + return; + + const sal_uLong nMaxGeometryMask = + WINDOWSTATE_MASK_X | WINDOWSTATE_MASK_Y | + WINDOWSTATE_MASK_WIDTH | WINDOWSTATE_MASK_HEIGHT | + WINDOWSTATE_MASK_MAXIMIZED_X | WINDOWSTATE_MASK_MAXIMIZED_Y | + WINDOWSTATE_MASK_MAXIMIZED_WIDTH | WINDOWSTATE_MASK_MAXIMIZED_HEIGHT; + + if( (pState->mnMask & WINDOWSTATE_MASK_STATE) && + ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) && + (pState->mnState & WINDOWSTATE_STATE_MAXIMIZED) && + (pState->mnMask & nMaxGeometryMask) == nMaxGeometryMask ) + { + resizeWindow( pState->mnWidth, pState->mnHeight ); + moveWindow( pState->mnX, pState->mnY ); + m_bDefaultPos = m_bDefaultSize = false; + + updateScreenNumber(); + + m_nState = GdkWindowState( m_nState | GDK_WINDOW_STATE_MAXIMIZED ); + m_aRestorePosSize = Rectangle( Point( pState->mnX, pState->mnY ), + Size( pState->mnWidth, pState->mnHeight ) ); + } + else if( pState->mnMask & (WINDOWSTATE_MASK_X | WINDOWSTATE_MASK_Y | + WINDOWSTATE_MASK_WIDTH | WINDOWSTATE_MASK_HEIGHT ) ) + { + sal_uInt16 nPosSizeFlags = 0; + long nX = pState->mnX - (m_pParent ? m_pParent->maGeometry.nX : 0); + long nY = pState->mnY - (m_pParent ? m_pParent->maGeometry.nY : 0); + if( pState->mnMask & WINDOWSTATE_MASK_X ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_X; + else + nX = maGeometry.nX - (m_pParent ? m_pParent->maGeometry.nX : 0); + if( pState->mnMask & WINDOWSTATE_MASK_Y ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_Y; + else + nY = maGeometry.nY - (m_pParent ? m_pParent->maGeometry.nY : 0); + if( pState->mnMask & WINDOWSTATE_MASK_WIDTH ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH; + if( pState->mnMask & WINDOWSTATE_MASK_HEIGHT ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT; + SetPosSize( nX, nY, pState->mnWidth, pState->mnHeight, nPosSizeFlags ); + } + if( pState->mnMask & WINDOWSTATE_MASK_STATE && ! isChild() ) + { + if( pState->mnState & WINDOWSTATE_STATE_MAXIMIZED ) + gtk_window_maximize( GTK_WINDOW(m_pWindow) ); + else + gtk_window_unmaximize( GTK_WINDOW(m_pWindow) ); + /* #i42379# there is no rollup state in GDK; and rolled up windows are + * (probably depending on the WM) reported as iconified. If we iconify a + * window here that was e.g. a dialog, then it will be unmapped but still + * not be displayed in the task list, so it's an iconified window that + * the user cannot get out of this state. So do not set the iconified state + * on windows with a parent (that is transient frames) since these tend + * to not be represented in an icon task list. + */ + if( (pState->mnState & WINDOWSTATE_STATE_MINIMIZED) + && ! m_pParent ) + gtk_window_iconify( GTK_WINDOW(m_pWindow) ); + else + gtk_window_deiconify( GTK_WINDOW(m_pWindow) ); + } + TriggerPaintEvent(); +} + +bool GtkSalFrame::GetWindowState( SalFrameState* pState ) +{ + pState->mnState = WINDOWSTATE_STATE_NORMAL; + pState->mnMask = WINDOWSTATE_MASK_STATE; + // rollup ? gtk 2.2 does not seem to support the shaded state + if( (m_nState & GDK_WINDOW_STATE_ICONIFIED) ) + pState->mnState |= WINDOWSTATE_STATE_MINIMIZED; + if( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) + { + pState->mnState |= WINDOWSTATE_STATE_MAXIMIZED; + pState->mnX = m_aRestorePosSize.Left(); + pState->mnY = m_aRestorePosSize.Top(); + pState->mnWidth = m_aRestorePosSize.GetWidth(); + pState->mnHeight = m_aRestorePosSize.GetHeight(); + pState->mnMaximizedX = maGeometry.nX; + pState->mnMaximizedY = maGeometry.nY; + pState->mnMaximizedWidth = maGeometry.nWidth; + pState->mnMaximizedHeight = maGeometry.nHeight; + pState->mnMask |= WINDOWSTATE_MASK_MAXIMIZED_X | + WINDOWSTATE_MASK_MAXIMIZED_Y | + WINDOWSTATE_MASK_MAXIMIZED_WIDTH | + WINDOWSTATE_MASK_MAXIMIZED_HEIGHT; + } + else + { + pState->mnX = maGeometry.nX; + pState->mnY = maGeometry.nY; + pState->mnWidth = maGeometry.nWidth; + pState->mnHeight = maGeometry.nHeight; + } + pState->mnMask |= WINDOWSTATE_MASK_X | + WINDOWSTATE_MASK_Y | + WINDOWSTATE_MASK_WIDTH | + WINDOWSTATE_MASK_HEIGHT; + + return true; +} + +typedef enum { + SET_RETAIN_SIZE, + SET_FULLSCREEN, + SET_UN_FULLSCREEN +} SetType; + +void GtkSalFrame::SetScreen( unsigned int nNewScreen, int eType, Rectangle *pSize ) +{ + if( !m_pWindow ) + return; + + if (maGeometry.nDisplayScreenNumber == nNewScreen && eType == SET_RETAIN_SIZE) + return; + + int nX = maGeometry.nX, nY = maGeometry.nY, + nWidth = maGeometry.nWidth, nHeight = maGeometry.nHeight; + GdkScreen *pScreen = NULL; + GdkRectangle aNewMonitor; + + bool bSpanAllScreens = nNewScreen == (unsigned int)-1; + m_bSpanMonitorsWhenFullscreen = bSpanAllScreens && getDisplay()->getSystem()->GetDisplayScreenCount() > 1; + + if (m_bSpanMonitorsWhenFullscreen) //span all screens + { + pScreen = gtk_widget_get_screen( m_pWindow ); + aNewMonitor.x = 0; + aNewMonitor.y = 0; + aNewMonitor.width = gdk_screen_get_width(pScreen); + aNewMonitor.height = gdk_screen_get_height(pScreen); + } + else + { + gint nMonitor; + bool bSameMonitor = false; + + if (!bSpanAllScreens) + { + pScreen = getDisplay()->getSystem()->getScreenMonitorFromIdx( nNewScreen, nMonitor ); + if (!pScreen) + { + g_warning ("Attempt to move GtkSalFrame to invalid screen %d => " + "fallback to current\n", nNewScreen); + } + } + + if (!pScreen) + { + pScreen = gtk_widget_get_screen( m_pWindow ); + bSameMonitor = true; + } + + // Heavy lifting, need to move screen ... + if( pScreen != gtk_widget_get_screen( m_pWindow )) + gtk_window_set_screen( GTK_WINDOW( m_pWindow ), pScreen ); + + gint nOldMonitor = gdk_screen_get_monitor_at_window( + pScreen, widget_get_window( m_pWindow ) ); + if (bSameMonitor) + nMonitor = nOldMonitor; + + #if OSL_DEBUG_LEVEL > 1 + if( nMonitor == nOldMonitor ) + g_warning( "An apparently pointless SetScreen - should we elide it ?" ); + #endif + + GdkRectangle aOldMonitor; + gdk_screen_get_monitor_geometry( pScreen, nOldMonitor, &aOldMonitor ); + gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aNewMonitor ); + + nX = aNewMonitor.x + maGeometry.nX - aOldMonitor.x; + nY = aNewMonitor.y + maGeometry.nY - aOldMonitor.y; + } + + bool bResize = false; + bool bVisible = IS_WIDGET_MAPPED( m_pWindow ); + if( bVisible ) + Show( false ); + + if( eType == SET_FULLSCREEN ) + { + nX = aNewMonitor.x; + nY = aNewMonitor.y; + nWidth = aNewMonitor.width; + nHeight = aNewMonitor.height; + m_nStyle |= SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; + bResize = true; + + // #i110881# for the benefit of compiz set a max size here + // else setting to fullscreen fails for unknown reasons + m_aMaxSize.Width() = aNewMonitor.width; + m_aMaxSize.Height() = aNewMonitor.height; + } + + if( pSize && eType == SET_UN_FULLSCREEN ) + { + nX = pSize->Left(); + nY = pSize->Top(); + nWidth = pSize->GetWidth(); + nHeight = pSize->GetHeight(); + m_nStyle &= ~SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; + bResize = true; + } + + if (bResize) + { + // temporarily re-sizeable + if( !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) ) + gtk_window_set_resizable( GTK_WINDOW(m_pWindow), TRUE ); + window_resize(nWidth, nHeight); + } + + gtk_window_move(GTK_WINDOW(m_pWindow), nX, nY); + + { + gdk_window_set_fullscreen_mode( widget_get_window(m_pWindow), m_bSpanMonitorsWhenFullscreen + ? GDK_FULLSCREEN_ON_ALL_MONITORS : GDK_FULLSCREEN_ON_CURRENT_MONITOR ); + if( eType == SET_FULLSCREEN ) + gtk_window_fullscreen( GTK_WINDOW( m_pWindow ) ); + else if( eType == SET_UN_FULLSCREEN ) + gtk_window_unfullscreen( GTK_WINDOW( m_pWindow ) ); + } + + if( eType == SET_UN_FULLSCREEN && + !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) ) + gtk_window_set_resizable( GTK_WINDOW( m_pWindow ), FALSE ); + + // FIXME: we should really let gtk+ handle our widget hierarchy ... + if( m_pParent && gtk_widget_get_screen( m_pParent->m_pWindow ) != pScreen ) + SetParent( NULL ); + std::list< GtkSalFrame* > aChildren = m_aChildren; + for( std::list< GtkSalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it ) + (*it)->SetScreen( nNewScreen, SET_RETAIN_SIZE ); + + m_bDefaultPos = m_bDefaultSize = false; + updateScreenNumber(); + + if( bVisible ) + Show( true ); +} + +void GtkSalFrame::SetScreenNumber( unsigned int nNewScreen ) +{ + SetScreen( nNewScreen, SET_RETAIN_SIZE ); +} + +void GtkSalFrame::updateWMClass() +{ + OString aResClass = OUStringToOString(m_sWMClass, RTL_TEXTENCODING_ASCII_US); + const char *pResClass = !aResClass.isEmpty() ? aResClass.getStr() : + SalGenericSystem::getFrameClassName(); + Display *display; + + if (!getDisplay()->IsX11Display()) + return; + + display = GDK_DISPLAY_XDISPLAY(getGdkDisplay()); + + if( IS_WIDGET_REALIZED( m_pWindow ) ) + { + XClassHint* pClass = XAllocClassHint(); + OString aResName = SalGenericSystem::getFrameResName(); + pClass->res_name = const_cast(aResName.getStr()); + pClass->res_class = const_cast(pResClass); + XSetClassHint( display, + widget_get_xid(m_pWindow), + pClass ); + XFree( pClass ); + } +} + +void GtkSalFrame::SetApplicationID( const OUString &rWMClass ) +{ + if( rWMClass != m_sWMClass && ! isChild() ) + { + m_sWMClass = rWMClass; + updateWMClass(); + + for( std::list< GtkSalFrame* >::iterator it = m_aChildren.begin(); it != m_aChildren.end(); ++it ) + (*it)->SetApplicationID(rWMClass); + } +} + +void GtkSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nScreen ) +{ + m_bFullscreen = bFullScreen; + + if( !m_pWindow || isChild() ) + return; + + if( bFullScreen ) + { + m_aRestorePosSize = Rectangle( Point( maGeometry.nX, maGeometry.nY ), + Size( maGeometry.nWidth, maGeometry.nHeight ) ); + SetScreen( nScreen, SET_FULLSCREEN ); + } + else + { + SetScreen( nScreen, SET_UN_FULLSCREEN, + !m_aRestorePosSize.IsEmpty() ? &m_aRestorePosSize : NULL ); + m_aRestorePosSize = Rectangle(); + } +} + +/* definitions from xautolock.c (pl15) */ +#define XAUTOLOCK_DISABLE 1 +#define XAUTOLOCK_ENABLE 2 + +void GtkSalFrame::setAutoLock( bool bLock ) +{ + if( isChild() || !getDisplay()->IsX11Display() ) + return; + + GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(m_pWindow) ); + GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); + GdkWindow *pRootWin = gdk_screen_get_root_window( pScreen ); + + Atom nAtom = XInternAtom( GDK_DISPLAY_XDISPLAY( pDisplay ), + "XAUTOLOCK_MESSAGE", False ); + + int nMessage = bLock ? XAUTOLOCK_ENABLE : XAUTOLOCK_DISABLE; + + XChangeProperty( GDK_DISPLAY_XDISPLAY( pDisplay ), + GDK_WINDOW_XID( pRootWin ), + nAtom, XA_INTEGER, + 8, PropModeReplace, + reinterpret_cast(&nMessage), + sizeof( nMessage ) ); +} + +#ifdef ENABLE_DBUS +/** cookie is returned as an unsigned integer */ +static guint +dbus_inhibit_gsm (const gchar *appname, + const gchar *reason, + guint xid) +{ + gboolean res; + guint cookie; + GError *error = NULL; + DBusGProxy *proxy = NULL; + + /* get the DBUS session connection */ + DBusGConnection *session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (error != NULL) { + g_debug ("DBUS cannot connect : %s", error->message); + g_error_free (error); + return -1; + } + + /* get the proxy with gnome-session-manager */ + proxy = dbus_g_proxy_new_for_name (session_connection, + GSM_DBUS_SERVICE, + GSM_DBUS_PATH, + GSM_DBUS_INTERFACE); + if (proxy == NULL) { + g_debug ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE); + return -1; + } + + res = dbus_g_proxy_call (proxy, + "Inhibit", &error, + G_TYPE_STRING, appname, + G_TYPE_UINT, xid, + G_TYPE_STRING, reason, + G_TYPE_UINT, 8, //Inhibit the session being marked as idle + G_TYPE_INVALID, + G_TYPE_UINT, &cookie, + G_TYPE_INVALID); + + /* check the return value */ + if (! res) { + cookie = -1; + g_debug ("Inhibit method failed"); + } + + /* check the error value */ + if (error != NULL) { + g_debug ("Inhibit problem : %s", error->message); + g_error_free (error); + cookie = -1; + } + + g_object_unref (G_OBJECT (proxy)); + return cookie; +} + +static void +dbus_uninhibit_gsm (guint cookie) +{ + gboolean res; + GError *error = NULL; + DBusGProxy *proxy = NULL; + DBusGConnection *session_connection = NULL; + + if (cookie == guint(-1)) { + g_debug ("Invalid cookie"); + return; + } + + /* get the DBUS session connection */ + session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (error) { + g_debug ("DBUS cannot connect : %s", error->message); + g_error_free (error); + return; + } + + /* get the proxy with gnome-session-manager */ + proxy = dbus_g_proxy_new_for_name (session_connection, + GSM_DBUS_SERVICE, + GSM_DBUS_PATH, + GSM_DBUS_INTERFACE); + if (proxy == NULL) { + g_debug ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE); + return; + } + + res = dbus_g_proxy_call (proxy, + "Uninhibit", + &error, + G_TYPE_UINT, cookie, + G_TYPE_INVALID, + G_TYPE_INVALID); + + /* check the return value */ + if (! res) { + g_debug ("Uninhibit method failed"); + } + + /* check the error value */ + if (error != NULL) { + g_debug ("Uninhibit problem : %s", error->message); + g_error_free (error); + cookie = -1; + } + g_object_unref (G_OBJECT (proxy)); +} +#endif + +void GtkSalFrame::StartPresentation( bool bStart ) +{ + setAutoLock( !bStart ); + + if( !getDisplay()->IsX11Display() ) + return; + + if( bStart ) + { +#ifdef ENABLE_DBUS + m_nGSMCookie = dbus_inhibit_gsm(g_get_application_name(), "presentation", + widget_get_xid(m_pWindow)); +#endif + } + else + { + m_nSavedScreenSaverTimeout = 0; +#ifdef ENABLE_DBUS + dbus_uninhibit_gsm(m_nGSMCookie); +#endif + } +} + +void GtkSalFrame::SetAlwaysOnTop( bool bOnTop ) +{ + if( m_pWindow ) + gtk_window_set_keep_above( GTK_WINDOW( m_pWindow ), bOnTop ); +} + +void GtkSalFrame::ToTop( sal_uInt16 nFlags ) +{ + if( m_pWindow ) + { + if( isChild( false, true ) ) + gtk_widget_grab_focus( m_pWindow ); + else if( IS_WIDGET_MAPPED( m_pWindow ) ) + { + if( ! (nFlags & SAL_FRAME_TOTOP_GRABFOCUS_ONLY) ) + gtk_window_present( GTK_WINDOW(m_pWindow) ); + else + { + guint32 nUserTime = GDK_CURRENT_TIME; + gdk_window_focus( widget_get_window(m_pWindow), nUserTime ); + } + } + else + { + if( nFlags & SAL_FRAME_TOTOP_RESTOREWHENMIN ) + gtk_window_present( GTK_WINDOW(m_pWindow) ); + } + } +} + +void GtkSalFrame::SetPointer( PointerStyle ePointerStyle ) +{ + if( m_pWindow && ePointerStyle != m_ePointerStyle ) + { + m_ePointerStyle = ePointerStyle; + GdkCursor *pCursor = getDisplay()->getCursor( ePointerStyle ); + gdk_window_set_cursor( widget_get_window(m_pWindow), pCursor ); + m_pCurrentCursor = pCursor; + + // #i80791# use grabPointer the same way as CaptureMouse, respective float grab + if( getDisplay()->MouseCaptured( this ) ) + grabPointer( true, false ); + else if( m_nFloats > 0 ) + grabPointer( true, true ); + } +} + +void GtkSalFrame::grabPointer( bool bGrab, bool bOwnerEvents ) +{ + static const char* pEnv = getenv( "SAL_NO_MOUSEGRABS" ); + if (pEnv && *pEnv) + return; + + if (!m_pWindow) + return; + + const int nMask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); + + GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay()); + GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager); + if (bGrab) + gdk_device_grab(pPointer, widget_get_window(getMouseEventWidget()), GDK_OWNERSHIP_NONE, bOwnerEvents, (GdkEventMask) nMask, m_pCurrentCursor, GDK_CURRENT_TIME); + else + gdk_device_ungrab(pPointer, GDK_CURRENT_TIME); +} + +void GtkSalFrame::grabKeyboard( bool bGrab ) +{ + if (!m_pWindow) + return; + + GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay()); + GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager); + GdkDevice* pKeyboard = gdk_device_get_associated_device(pPointer); + if (bGrab) + { + gdk_device_grab(pKeyboard, widget_get_window(m_pWindow), GDK_OWNERSHIP_NONE, + true, (GdkEventMask)(GDK_KEY_PRESS | GDK_KEY_RELEASE), NULL, GDK_CURRENT_TIME); + } + else + { + gdk_device_ungrab(pKeyboard, GDK_CURRENT_TIME); + } +} + +void GtkSalFrame::CaptureMouse( bool bCapture ) +{ + getDisplay()->CaptureMouse( bCapture ? this : NULL ); +} + +void GtkSalFrame::SetPointerPos( long nX, long nY ) +{ + GtkSalFrame* pFrame = this; + while( pFrame && pFrame->isChild( false, true ) ) + pFrame = pFrame->m_pParent; + if( ! pFrame ) + return; + + GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(pFrame->m_pWindow) ); + GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); + + /* when the application tries to center the mouse in the dialog the + * window isn't mapped already. So use coordinates relative to the root window. + */ + unsigned int nWindowLeft = maGeometry.nX + nX; + unsigned int nWindowTop = maGeometry.nY + nY; + + XWarpPointer( GDK_DISPLAY_XDISPLAY (pDisplay), None, + GDK_WINDOW_XID (gdk_screen_get_root_window( pScreen ) ), + 0, 0, 0, 0, nWindowLeft, nWindowTop); + // #i38648# ask for the next motion hint + gint x, y; + GdkModifierType mask; + gdk_window_get_pointer( widget_get_window(pFrame->m_pWindow) , &x, &y, &mask ); +} + +void GtkSalFrame::Flush() +{ + gdk_display_flush( getGdkDisplay() ); +} + +void GtkSalFrame::Sync() +{ + gdk_display_sync( getGdkDisplay() ); +} + +#ifndef GDK_Open +#define GDK_Open 0x1008ff6b +#endif +#ifndef GDK_Paste +#define GDK_Paste 0x1008ff6d +#endif +#ifndef GDK_Copy +#define GDK_Copy 0x1008ff57 +#endif +#ifndef GDK_Cut +#define GDK_Cut 0x1008ff58 +#endif + +void GtkSalFrame::KeyCodeToGdkKey(const vcl::KeyCode& rKeyCode, + guint* pGdkKeyCode, GdkModifierType *pGdkModifiers) +{ + if ( pGdkKeyCode == NULL || pGdkModifiers == NULL ) + return; + + // Get GDK key modifiers + GdkModifierType nModifiers = (GdkModifierType) 0; + + if ( rKeyCode.IsShift() ) + nModifiers = (GdkModifierType) ( nModifiers | GDK_SHIFT_MASK ); + + if ( rKeyCode.IsMod1() ) + nModifiers = (GdkModifierType) ( nModifiers | GDK_CONTROL_MASK ); + + if ( rKeyCode.IsMod2() ) + nModifiers = (GdkModifierType) ( nModifiers | GDK_MOD1_MASK ); + + *pGdkModifiers = nModifiers; + + // Get GDK keycode. + guint nKeyCode = 0; + + guint nCode = rKeyCode.GetCode(); + + if ( nCode >= KEY_0 && nCode <= KEY_9 ) + nKeyCode = ( nCode - KEY_0 ) + GDK_0; + else if ( nCode >= KEY_A && nCode <= KEY_Z ) + nKeyCode = ( nCode - KEY_A ) + GDK_A; + else if ( nCode >= KEY_F1 && nCode <= KEY_F26 ) + nKeyCode = ( nCode - KEY_F1 ) + GDK_F1; + else + { + switch( nCode ) + { + case KEY_DOWN: nKeyCode = GDK_Down; break; + case KEY_UP: nKeyCode = GDK_Up; break; + case KEY_LEFT: nKeyCode = GDK_Left; break; + case KEY_RIGHT: nKeyCode = GDK_Right; break; + case KEY_HOME: nKeyCode = GDK_Home; break; + case KEY_END: nKeyCode = GDK_End; break; + case KEY_PAGEUP: nKeyCode = GDK_Page_Up; break; + case KEY_PAGEDOWN: nKeyCode = GDK_Page_Down; break; + case KEY_RETURN: nKeyCode = GDK_Return; break; + case KEY_ESCAPE: nKeyCode = GDK_Escape; break; + case KEY_TAB: nKeyCode = GDK_Tab; break; + case KEY_BACKSPACE: nKeyCode = GDK_BackSpace; break; + case KEY_SPACE: nKeyCode = GDK_space; break; + case KEY_INSERT: nKeyCode = GDK_Insert; break; + case KEY_DELETE: nKeyCode = GDK_Delete; break; + case KEY_ADD: nKeyCode = GDK_plus; break; + case KEY_SUBTRACT: nKeyCode = GDK_minus; break; + case KEY_MULTIPLY: nKeyCode = GDK_asterisk; break; + case KEY_DIVIDE: nKeyCode = GDK_slash; break; + case KEY_POINT: nKeyCode = GDK_period; break; + case KEY_COMMA: nKeyCode = GDK_comma; break; + case KEY_LESS: nKeyCode = GDK_less; break; + case KEY_GREATER: nKeyCode = GDK_greater; break; + case KEY_EQUAL: nKeyCode = GDK_equal; break; + case KEY_FIND: nKeyCode = GDK_Find; break; + case KEY_CONTEXTMENU: nKeyCode = GDK_Menu; break; + case KEY_HELP: nKeyCode = GDK_Help; break; + case KEY_UNDO: nKeyCode = GDK_Undo; break; + case KEY_REPEAT: nKeyCode = GDK_Redo; break; + case KEY_DECIMAL: nKeyCode = GDK_KP_Decimal; break; + case KEY_TILDE: nKeyCode = GDK_asciitilde; break; + case KEY_QUOTELEFT: nKeyCode = GDK_quoteleft; break; + case KEY_BRACKETLEFT: nKeyCode = GDK_bracketleft; break; + case KEY_BRACKETRIGHT: nKeyCode = GDK_bracketright; break; + case KEY_SEMICOLON: nKeyCode = GDK_semicolon; break; + case KEY_QUOTERIGHT: nKeyCode = GDK_quoteright; break; + + // Special cases + case KEY_COPY: nKeyCode = GDK_Copy; break; + case KEY_CUT: nKeyCode = GDK_Cut; break; + case KEY_PASTE: nKeyCode = GDK_Paste; break; + case KEY_OPEN: nKeyCode = GDK_Open; break; + } + } + + *pGdkKeyCode = nKeyCode; +} + +OUString GtkSalFrame::GetKeyName( sal_uInt16 nKeyCode ) +{ + guint nGtkKeyCode; + GdkModifierType nGtkModifiers; + KeyCodeToGdkKey(nKeyCode, &nGtkKeyCode, &nGtkModifiers ); + + gchar* pName = gtk_accelerator_get_label(nGtkKeyCode, nGtkModifiers); + OUString aRet(pName, rtl_str_getLength(pName), RTL_TEXTENCODING_UTF8); + g_free(pName); + return aRet; +} + +GdkDisplay *GtkSalFrame::getGdkDisplay() +{ + return GetGtkSalData()->GetGdkDisplay(); +} + +GtkSalDisplay *GtkSalFrame::getDisplay() +{ + return GetGtkSalData()->GetGtkDisplay(); +} + +SalFrame::SalPointerState GtkSalFrame::GetPointerState() +{ + SalPointerState aState; + GdkScreen* pScreen; + gint x, y; + GdkModifierType aMask; + gdk_display_get_pointer( getGdkDisplay(), &pScreen, &x, &y, &aMask ); + aState.maPos = Point( x - maGeometry.nX, y - maGeometry.nY ); + aState.mnState = GetMouseModCode( aMask ); + return aState; +} + +KeyIndicatorState GtkSalFrame::GetIndicatorState() +{ + KeyIndicatorState nState = KeyIndicatorState::NONE; + + GdkKeymap *pKeyMap = gdk_keymap_get_for_display(getGdkDisplay()); + + if (gdk_keymap_get_caps_lock_state(pKeyMap)) + nState |= KeyIndicatorState::CAPSLOCK; + if (gdk_keymap_get_num_lock_state(pKeyMap)) + nState |= KeyIndicatorState::NUMLOCK; +#if GTK_CHECK_VERSION(3,18,0) + if (gdk_keymap_get_scroll_lock_state(pKeyMap)) + nState |= KeyIndicatorState::SCROLLLOCK; +#endif + return nState; +} + +void GtkSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode ) +{ + g_warning ("missing simulate keypress %d", nKeyCode); +} + +void GtkSalFrame::SetInputContext( SalInputContext* pContext ) +{ + if( ! pContext ) + return; + + if( ! (pContext->mnOptions & InputContextFlags::Text) ) + return; + + // create a new im context + if( ! m_pIMHandler ) + m_pIMHandler = new IMHandler( this ); +} + +void GtkSalFrame::EndExtTextInput( sal_uInt16 nFlags ) +{ + if( m_pIMHandler ) + m_pIMHandler->endExtTextInput( nFlags ); +} + +bool GtkSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , vcl::KeyCode& ) +{ + // not supported yet + return false; +} + +LanguageType GtkSalFrame::GetInputLanguage() +{ + return LANGUAGE_DONTKNOW; +} + +void GtkSalFrame::UpdateSettings( AllSettings& rSettings ) +{ + if( ! m_pWindow ) + return; + + GtkSalGraphics* pGraphics = static_cast(m_aGraphics[0].pGraphics); + bool bFreeGraphics = false; + if( ! pGraphics ) + { + pGraphics = static_cast(AcquireGraphics()); + if ( !pGraphics ) + { + SAL_WARN("vcl", "Could not get graphics - unable to update settings"); + return; + } + bFreeGraphics = true; + } + + pGraphics->updateSettings( rSettings ); + + if( bFreeGraphics ) + ReleaseGraphics( pGraphics ); +} + +void GtkSalFrame::Beep() +{ + gdk_display_beep( getGdkDisplay() ); +} + +const SystemEnvData* GtkSalFrame::GetSystemData() const +{ + return &m_aSystemData; +} + +void GtkSalFrame::SetParent( SalFrame* pNewParent ) +{ + if( m_pParent ) + m_pParent->m_aChildren.remove( this ); + m_pParent = static_cast(pNewParent); + if( m_pParent ) + m_pParent->m_aChildren.push_back( this ); + if( ! isChild() ) + gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), + (m_pParent && ! m_pParent->isChild(true,false)) ? GTK_WINDOW(m_pParent->m_pWindow) : NULL + ); +} + +bool GtkSalFrame::SetPluginParent( SystemParentData* pSysParent ) +{ + (void)pSysParent; + //FIXME: no SetPluginParent impl. for gtk3 + return false; +} + +void GtkSalFrame::ResetClipRegion() +{ + if( m_pWindow ) + gdk_window_shape_combine_region( widget_get_window( m_pWindow ), NULL, 0, 0 ); +} + +void GtkSalFrame::BeginSetClipRegion( sal_uLong ) +{ + if( m_pRegion ) + cairo_region_destroy( m_pRegion ); + m_pRegion = cairo_region_create(); +} + +void GtkSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + if( m_pRegion ) + { + GdkRectangle aRect; + aRect.x = nX; + aRect.y = nY; + aRect.width = nWidth; + aRect.height = nHeight; + cairo_region_union_rectangle( m_pRegion, &aRect ); + } +} + +void GtkSalFrame::EndSetClipRegion() +{ + if( m_pWindow && m_pRegion ) + gdk_window_shape_combine_region( widget_get_window(m_pWindow), m_pRegion, 0, 0 ); +} + +gboolean GtkSalFrame::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + SalMouseEvent aEvent; + sal_uInt16 nEventType = 0; + switch( pEvent->type ) + { + case GDK_BUTTON_PRESS: + nEventType = SALEVENT_MOUSEBUTTONDOWN; + break; + case GDK_BUTTON_RELEASE: + nEventType = SALEVENT_MOUSEBUTTONUP; + break; + default: + return false; + } + switch( pEvent->button ) + { + case 1: aEvent.mnButton = MOUSE_LEFT; break; + case 2: aEvent.mnButton = MOUSE_MIDDLE; break; + case 3: aEvent.mnButton = MOUSE_RIGHT; break; + default: return false; + } + aEvent.mnTime = pEvent->time; + aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; + aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; + aEvent.mnCode = GetMouseModCode( pEvent->state ); + + bool bClosePopups = false; + if( pEvent->type == GDK_BUTTON_PRESS && + (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) == 0 + ) + { + if( m_nFloats > 0 ) + { + // close popups if user clicks outside our application + gint x, y; + bClosePopups = (gdk_display_get_window_at_pointer( GtkSalFrame::getGdkDisplay(), &x, &y ) == NULL); + } + /* #i30306# release implicit pointer grab if no popups are open; else + * Drag cannot grab the pointer and will fail. + */ + if( m_nFloats < 1 || bClosePopups ) + gdk_display_pointer_ungrab( GtkSalFrame::getGdkDisplay(), GDK_CURRENT_TIME ); + } + + if( pThis->m_bWindowIsGtkPlug && + pEvent->type == GDK_BUTTON_PRESS && + pEvent->button == 1 ) + { + pThis->askForXEmbedFocus( pEvent->time ); + } + + // --- RTL --- (mirror mouse pos) + if( AllSettings::GetLayoutRTL() ) + aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; + + vcl::DeletionListener aDel( pThis ); + + pThis->CallCallback( nEventType, &aEvent ); + + if( ! aDel.isDeleted() ) + { + if( bClosePopups ) + { + ImplSVData* pSVData = ImplGetSVData(); + if ( pSVData->maWinData.mpFirstFloat ) + { + static const char* pEnv = getenv( "SAL_FLOATWIN_NOAPPFOCUSCLOSE" ); + if ( !(pSVData->maWinData.mpFirstFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose) && !(pEnv && *pEnv) ) + pSVData->maWinData.mpFirstFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); + } + } + + if( ! aDel.isDeleted() ) + { + int frame_x = (int)(pEvent->x_root - pEvent->x); + int frame_y = (int)(pEvent->y_root - pEvent->y); + if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) + { + pThis->maGeometry.nX = frame_x; + pThis->maGeometry.nY = frame_y; + pThis->CallCallback( SALEVENT_MOVE, NULL ); + } + } + } + + return true; +} + +gboolean GtkSalFrame::signalScroll( GtkWidget*, GdkEvent* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + GdkEventScroll* pSEvent = reinterpret_cast(pEvent); + + // gnome#726878 check for duplicate legacy scroll event + if (pSEvent->direction != GDK_SCROLL_SMOOTH && + pThis->m_nLastScrollEventTime == pSEvent->time) + { + return true; + } + + SalWheelMouseEvent aEvent; + + aEvent.mnTime = pSEvent->time; + aEvent.mnX = (sal_uLong)pSEvent->x; + aEvent.mnY = (sal_uLong)pSEvent->y; + aEvent.mnCode = GetMouseModCode( pSEvent->state ); + aEvent.mnScrollLines = 3; + + switch (pSEvent->direction) + { + case GDK_SCROLL_SMOOTH: + { + double delta_x, delta_y; + gdk_event_get_scroll_deltas(pEvent, &delta_x, &delta_y); + //pick the bigger one I guess + aEvent.mbHorz = fabs(delta_x) > fabs(delta_y); + if (aEvent.mbHorz) + aEvent.mnDelta = -delta_x; + else + aEvent.mnDelta = -delta_y; + aEvent.mnScrollLines = 1; + pThis->m_nLastScrollEventTime = pSEvent->time; + break; + } + case GDK_SCROLL_UP: + aEvent.mnDelta = 120; + aEvent.mbHorz = false; + break; + case GDK_SCROLL_DOWN: + aEvent.mnDelta = -120; + aEvent.mbHorz = false; + break; + case GDK_SCROLL_LEFT: + aEvent.mbHorz = true; + aEvent.mnDelta = 120; + break; + case GDK_SCROLL_RIGHT: + aEvent.mnDelta = -120; + aEvent.mbHorz = true; + break; + }; + + aEvent.mnNotchDelta = aEvent.mnDelta < 0 ? -1 : 1; + + // --- RTL --- (mirror mouse pos) + if( AllSettings::GetLayoutRTL() ) + aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; + + pThis->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); + + return true; +} + +void GtkSalFrame::gestureSwipe(GtkGestureSwipe* gesture, gdouble velocity_x, gdouble velocity_y, gpointer frame) +{ + gdouble x, y; + GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); + //I feel I want the first point of the sequence, not the last point which + //the docs say this gives, but for the moment assume we start and end + //within the same vcl window + if (gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y)) + { + GtkSalFrame* pThis = static_cast(frame); + + SalSwipeEvent aEvent; + aEvent.mnVelocityX = velocity_x; + aEvent.mnVelocityY = velocity_y; + aEvent.mnX = x; + aEvent.mnY = y; + + pThis->CallCallback(SALEVENT_SWIPE, &aEvent); + } +} + +void GtkSalFrame::gestureLongPress(GtkGestureLongPress* gesture, gpointer frame) +{ + GtkSalFrame* pThis = static_cast(frame); + + if(pThis) + { + SalLongPressEvent aEvent; + + gdouble x, y; + GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); + gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y); + aEvent.mnX = x; + aEvent.mnY = y; + + pThis->CallCallback(SALEVENT_LONGPRESS, &aEvent); + } +} + +gboolean GtkSalFrame::signalMotion( GtkWidget*, GdkEventMotion* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + SalMouseEvent aEvent; + aEvent.mnTime = pEvent->time; + aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; + aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; + aEvent.mnCode = GetMouseModCode( pEvent->state ); + aEvent.mnButton = 0; + + // --- RTL --- (mirror mouse pos) + if( AllSettings::GetLayoutRTL() ) + aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; + + vcl::DeletionListener aDel( pThis ); + + pThis->CallCallback( SALEVENT_MOUSEMOVE, &aEvent ); + + if( ! aDel.isDeleted() ) + { + int frame_x = (int)(pEvent->x_root - pEvent->x); + int frame_y = (int)(pEvent->y_root - pEvent->y); + if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) + { + pThis->maGeometry.nX = frame_x; + pThis->maGeometry.nY = frame_y; + pThis->CallCallback( SALEVENT_MOVE, NULL ); + } + + if( ! aDel.isDeleted() ) + { + // ask for the next hint + gint x, y; + GdkModifierType mask; + gdk_window_get_pointer( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &x, &y, &mask ); + } + } + + return true; +} + +gboolean GtkSalFrame::signalCrossing( GtkWidget*, GdkEventCrossing* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + SalMouseEvent aEvent; + aEvent.mnTime = pEvent->time; + aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; + aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; + aEvent.mnCode = GetMouseModCode( pEvent->state ); + aEvent.mnButton = 0; + + pThis->CallCallback( (pEvent->type == GDK_ENTER_NOTIFY) ? SALEVENT_MOUSEMOVE : SALEVENT_MOUSELEAVE, &aEvent ); + + return true; +} + +cairo_t* GtkSalFrame::getCairoContext() const +{ + cairo_t* cr = SvpSalGraphics::createCairoContext(m_aFrame); + assert(cr); + return cr; +} + +void GtkSalFrame::damaged (const basegfx::B2IBox& rDamageRect) +{ +#if OSL_DEBUG_LEVEL > 1 + long long area = rDamageRect.getWidth() * rDamageRect.getHeight(); + if( area > 32 * 1024 ) + { + fprintf( stderr, "bitmap damaged %d %d (%dx%d) area %lld widget\n", + (int) rDamageRect.getMinX(), + (int) rDamageRect.getMinY(), + (int) rDamageRect.getWidth(), + (int) rDamageRect.getHeight(), + area ); + } +#endif + + if (dumpframes) + { + static int frame; + OString tmp("/tmp/frame" + OString::number(frame++) + ".png"); + cairo_t* cr = getCairoContext(); + cairo_surface_write_to_png(cairo_get_target(cr), tmp.getStr()); + cairo_destroy(cr); + } + + gtk_widget_queue_draw_area(GTK_WIDGET(m_pFixedContainer), + rDamageRect.getMinX(), + rDamageRect.getMinY(), + rDamageRect.getWidth(), + rDamageRect.getHeight()); +} + +// blit our backing basebmp buffer to the target cairo context cr +gboolean GtkSalFrame::signalDraw( GtkWidget*, cairo_t *cr, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + cairo_save(cr); + + cairo_t* source = pThis->getCairoContext(); + cairo_surface_t *pSurface = cairo_get_target(source); + + cairo_set_operator( cr, CAIRO_OPERATOR_OVER ); + cairo_set_source_surface(cr, pSurface, 0, 0); + cairo_paint(cr); + + cairo_destroy(source); + + cairo_restore(cr); + + cairo_surface_flush(cairo_get_target(cr)); + + return false; +} + +void GtkSalFrame::sizeAllocated(GtkWidget*, GdkRectangle *pAllocation, gpointer frame) +{ + GtkSalFrame* pThis = static_cast(frame); + pThis->maGeometry.nWidth = pAllocation->width; + pThis->maGeometry.nHeight = pAllocation->height; + pThis->AllocateFrame(); + pThis->CallCallback( SALEVENT_RESIZE, nullptr ); + pThis->TriggerPaintEvent(); +} + +gboolean GtkSalFrame::signalConfigure(GtkWidget*, GdkEventConfigure* pEvent, gpointer frame) +{ + GtkSalFrame* pThis = static_cast(frame); + + bool bMoved = false; + int x = pEvent->x, y = pEvent->y; + + /* #i31785# claims we cannot trust the x,y members of the event; + * they are e.g. not set correctly on maximize/demaximize; + * yet the gdkdisplay-x11.c code handling configure_events has + * done this XTranslateCoordinates work since the day ~zero. + */ + if( x != pThis->maGeometry.nX || y != pThis->maGeometry.nY ) + { + bMoved = true; + pThis->maGeometry.nX = x; + pThis->maGeometry.nY = y; + } + + // update decoration hints + GdkRectangle aRect; + gdk_window_get_frame_extents( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &aRect ); + pThis->maGeometry.nTopDecoration = y - aRect.y; + pThis->maGeometry.nBottomDecoration = aRect.y + aRect.height - y - pEvent->height; + pThis->maGeometry.nLeftDecoration = x - aRect.x; + pThis->maGeometry.nRightDecoration = aRect.x + aRect.width - x - pEvent->width; + + pThis->updateScreenNumber(); + + if (bMoved) + pThis->CallCallback(SALEVENT_MOVE, nullptr); + + return false; +} + +void GtkSalFrame::TriggerPaintEvent() +{ + //Under gtk2 we can basically paint directly into the XWindow and on + //additional "expose-event" events we can re-render the missing pieces + // + //Under gtk3 we have to keep our own buffer up to date and flush it into + //the given cairo context on "draw". So we emit a paint event on + //opportune resize trigger events to initially fill our backbuffer and then + //keep it up to date with our direct paints and tell gtk those regions + //have changed and then blit them into the provided cairo context when + //we get the "draw" + // + //The other alternative was to always paint everything on "draw", but + //that duplicates the amount of drawing and is hideously slow + SAL_INFO("vcl.gtk3", "force painting" << 0 << "," << 0 << " " << maGeometry.nWidth << "x" << maGeometry.nHeight); + SalPaintEvent aPaintEvt(0, 0, maGeometry.nWidth, maGeometry.nHeight, true); + CallCallback(SALEVENT_PAINT, &aPaintEvt); + gtk_widget_queue_draw(GTK_WIDGET(m_pFixedContainer)); +} + +gboolean GtkSalFrame::signalFocus( GtkWidget*, GdkEventFocus* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + SalGenericInstance *pSalInstance = + static_cast< SalGenericInstance* >(GetSalData()->m_pInstance); + + // check if printers have changed (analogous to salframe focus handler) + pSalInstance->updatePrinterUpdate(); + + if( !pEvent->in ) + { + pThis->m_nKeyModifiers = 0; + pThis->m_bSendModChangeOnRelease = false; + } + + if( pThis->m_pIMHandler ) + pThis->m_pIMHandler->focusChanged( pEvent->in ); + + // ask for changed printers like generic implementation + if( pEvent->in && pSalInstance->isPrinterInit() ) + pSalInstance->updatePrinterUpdate(); + + // FIXME: find out who the hell steals the focus from our frame + // while we have the pointer grabbed, this should not come from + // the window manager. Is this an event that was still queued ? + // The focus does not seem to get set inside our process + + // in the meantime do not propagate focus get/lose if floats are open + if( m_nFloats == 0 ) + pThis->CallCallback( pEvent->in ? SALEVENT_GETFOCUS : SALEVENT_LOSEFOCUS, NULL ); + + return false; +} + +gboolean GtkSalFrame::signalMap( GtkWidget *pWidget, GdkEvent*, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + bool bSetFocus = pThis->m_bSetFocusOnMap; + pThis->m_bSetFocusOnMap = false; + + (void)pWidget; (void)bSetFocus; + //FIXME: no set input focus ... + + pThis->CallCallback( SALEVENT_RESIZE, NULL ); + pThis->TriggerPaintEvent(); + + return false; +} + +gboolean GtkSalFrame::signalUnmap( GtkWidget*, GdkEvent*, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + pThis->CallCallback( SALEVENT_RESIZE, NULL ); + + return false; +} + +gboolean GtkSalFrame::signalKey( GtkWidget*, GdkEventKey* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + vcl::DeletionListener aDel( pThis ); + + if( pThis->m_pIMHandler ) + { + if( pThis->m_pIMHandler->handleKeyEvent( pEvent ) ) + return true; + } + + // handle modifiers + if( pEvent->keyval == GDK_Shift_L || pEvent->keyval == GDK_Shift_R || + pEvent->keyval == GDK_Control_L || pEvent->keyval == GDK_Control_R || + pEvent->keyval == GDK_Alt_L || pEvent->keyval == GDK_Alt_R || + pEvent->keyval == GDK_Meta_L || pEvent->keyval == GDK_Meta_R || + pEvent->keyval == GDK_Super_L || pEvent->keyval == GDK_Super_R ) + { + SalKeyModEvent aModEvt; + + sal_uInt16 nModCode = GetKeyModCode( pEvent->state ); + + aModEvt.mnModKeyCode = 0; // emit no MODKEYCHANGE events + if( pEvent->type == GDK_KEY_PRESS && !pThis->m_nKeyModifiers ) + pThis->m_bSendModChangeOnRelease = true; + + else if( pEvent->type == GDK_KEY_RELEASE && + pThis->m_bSendModChangeOnRelease ) + { + aModEvt.mnModKeyCode = pThis->m_nKeyModifiers; + pThis->m_nKeyModifiers = 0; + } + + sal_uInt16 nExtModMask = 0; + sal_uInt16 nModMask = 0; + // pressing just the ctrl key leads to a keysym of XK_Control but + // the event state does not contain ControlMask. In the release + // event its the other way round: it does contain the Control mask. + // The modifier mode therefore has to be adapted manually. + switch( pEvent->keyval ) + { + case GDK_Control_L: + nExtModMask = MODKEY_LMOD1; + nModMask = KEY_MOD1; + break; + case GDK_Control_R: + nExtModMask = MODKEY_RMOD1; + nModMask = KEY_MOD1; + break; + case GDK_Alt_L: + nExtModMask = MODKEY_LMOD2; + nModMask = KEY_MOD2; + break; + case GDK_Alt_R: + nExtModMask = MODKEY_RMOD2; + nModMask = KEY_MOD2; + break; + case GDK_Shift_L: + nExtModMask = MODKEY_LSHIFT; + nModMask = KEY_SHIFT; + break; + case GDK_Shift_R: + nExtModMask = MODKEY_RSHIFT; + nModMask = KEY_SHIFT; + break; + // Map Meta/Super to MOD3 modifier on all Unix systems + // except Mac OS X + case GDK_Meta_L: + case GDK_Super_L: + nExtModMask = MODKEY_LMOD3; + nModMask = KEY_MOD3; + break; + case GDK_Meta_R: + case GDK_Super_R: + nExtModMask = MODKEY_RMOD3; + nModMask = KEY_MOD3; + break; + } + if( pEvent->type == GDK_KEY_RELEASE ) + { + nModCode &= ~nModMask; + pThis->m_nKeyModifiers &= ~nExtModMask; + } + else + { + nModCode |= nModMask; + pThis->m_nKeyModifiers |= nExtModMask; + } + + aModEvt.mnCode = nModCode; + aModEvt.mnTime = pEvent->time; + + pThis->CallCallback( SALEVENT_KEYMODCHANGE, &aModEvt ); + + } + else + { + pThis->doKeyCallback( pEvent->state, + pEvent->keyval, + pEvent->hardware_keycode, + pEvent->group, + pEvent->time, + sal_Unicode(gdk_keyval_to_unicode( pEvent->keyval )), + (pEvent->type == GDK_KEY_PRESS), + false ); + if( ! aDel.isDeleted() ) + pThis->m_bSendModChangeOnRelease = false; + } + + if( !aDel.isDeleted() && pThis->m_pIMHandler ) + pThis->m_pIMHandler->updateIMSpotLocation(); + + return true; +} + +gboolean GtkSalFrame::signalDelete( GtkWidget*, GdkEvent*, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + pThis->CallCallback( SALEVENT_CLOSE, NULL ); + + return true; +} + +void GtkSalFrame::signalStyleSet( GtkWidget*, GtkStyle* pPrevious, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + + // every frame gets an initial style set on creation + // do not post these as the whole application tends to + // redraw itself to adjust to the new style + // where there IS no new style resulting in tremendous unnecessary flickering + if( pPrevious != NULL ) + { + // signalStyleSet does NOT usually have the gdk lock + // so post user event to safely dispatch the SALEVENT_SETTINGSCHANGED + // note: settings changed for multiple frames is avoided in winproc.cxx ImplHandleSettings + GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_SETTINGSCHANGED ); + GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_FONTCHANGED ); + } +} + +gboolean GtkSalFrame::signalWindowState( GtkWidget*, GdkEvent* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + if( (pThis->m_nState & GDK_WINDOW_STATE_ICONIFIED) != (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_ICONIFIED ) ) + { + GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_RESIZE ); + pThis->TriggerPaintEvent(); + } + + if( (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_MAXIMIZED) && + ! (pThis->m_nState & GDK_WINDOW_STATE_MAXIMIZED) ) + { + pThis->m_aRestorePosSize = + Rectangle( Point( pThis->maGeometry.nX, pThis->maGeometry.nY ), + Size( pThis->maGeometry.nWidth, pThis->maGeometry.nHeight ) ); + } + pThis->m_nState = pEvent->window_state.new_window_state; + + #if OSL_DEBUG_LEVEL > 1 + if( (pEvent->window_state.changed_mask & GDK_WINDOW_STATE_FULLSCREEN) ) + { + fprintf( stderr, "window %p %s full screen state\n", + pThis, + (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_FULLSCREEN) ? "enters" : "leaves"); + } + #endif + + return false; +} + +gboolean GtkSalFrame::signalVisibility( GtkWidget*, GdkEventVisibility* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + pThis->m_nVisibility = pEvent->state; + return true; +} + +void GtkSalFrame::signalDestroy( GtkWidget* pObj, gpointer frame ) +{ + GtkSalFrame* pThis = static_cast(frame); + if( pObj == pThis->m_pWindow ) + { + pThis->m_pFixedContainer = NULL; + pThis->m_pEventBox = NULL; + pThis->m_pWindow = NULL; + pThis->InvalidateGraphics(); + } +} + +// GtkSalFrame::IMHandler + +GtkSalFrame::IMHandler::IMHandler( GtkSalFrame* pFrame ) +: m_pFrame(pFrame), + m_nPrevKeyPresses( 0 ), + m_pIMContext( NULL ), + m_bFocused( true ), + m_bPreeditJustChanged( false ) +{ + m_aInputEvent.mpTextAttr = NULL; + createIMContext(); +} + +GtkSalFrame::IMHandler::~IMHandler() +{ + // cancel an eventual event posted to begin preedit again + GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + deleteIMContext(); +} + +void GtkSalFrame::IMHandler::createIMContext() +{ + if( ! m_pIMContext ) + { + m_pIMContext = gtk_im_multicontext_new (); + 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 ); + g_signal_connect( m_pIMContext, "preedit_start", + G_CALLBACK (signalIMPreeditStart), this ); + g_signal_connect( m_pIMContext, "preedit_end", + G_CALLBACK (signalIMPreeditEnd), this ); + + GetGenericData()->ErrorTrapPush(); + gtk_im_context_set_client_window( m_pIMContext, widget_get_window(GTK_WIDGET(m_pFrame->m_pWindow)) ); + gtk_im_context_focus_in( m_pIMContext ); + GetGenericData()->ErrorTrapPop(); + m_bFocused = true; + } +} + +void GtkSalFrame::IMHandler::deleteIMContext() +{ + if( m_pIMContext ) + { + // first give IC a chance to deinitialize + GetGenericData()->ErrorTrapPush(); + gtk_im_context_set_client_window( m_pIMContext, NULL ); + GetGenericData()->ErrorTrapPop(); + // destroy old IC + g_object_unref( m_pIMContext ); + m_pIMContext = NULL; + } +} + +void GtkSalFrame::IMHandler::doCallEndExtTextInput() +{ + m_aInputEvent.mpTextAttr = NULL; + m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); +} + +void GtkSalFrame::IMHandler::updateIMSpotLocation() +{ + SalExtTextInputPosEvent aPosEvent; + m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent ); + GdkRectangle aArea; + aArea.x = aPosEvent.mnX; + aArea.y = aPosEvent.mnY; + aArea.width = aPosEvent.mnWidth; + aArea.height = aPosEvent.mnHeight; + GetGenericData()->ErrorTrapPush(); + gtk_im_context_set_cursor_location( m_pIMContext, &aArea ); + GetGenericData()->ErrorTrapPop(); +} + +void GtkSalFrame::IMHandler::sendEmptyCommit() +{ + vcl::DeletionListener aDel( m_pFrame ); + + SalExtTextInputEvent aEmptyEv; + aEmptyEv.mnTime = 0; + aEmptyEv.mpTextAttr = 0; + aEmptyEv.maText.clear(); + aEmptyEv.mnCursorPos = 0; + aEmptyEv.mnCursorFlags = 0; + aEmptyEv.mbOnlyCursor = False; + m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&aEmptyEv ); + if( ! aDel.isDeleted() ) + m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); +} + +void GtkSalFrame::IMHandler::endExtTextInput( sal_uInt16 /*nFlags*/ ) +{ + gtk_im_context_reset ( m_pIMContext ); + + if( m_aInputEvent.mpTextAttr ) + { + vcl::DeletionListener aDel( m_pFrame ); + // delete preedit in sal (commit an empty string) + sendEmptyCommit(); + if( ! aDel.isDeleted() ) + { + // mark previous preedit state again (will e.g. be sent at focus gain) + m_aInputEvent.mpTextAttr = &m_aInputFlags[0]; + if( m_bFocused ) + { + // begin preedit again + GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + } + } + } +} + +void GtkSalFrame::IMHandler::focusChanged( bool bFocusIn ) +{ + m_bFocused = bFocusIn; + if( bFocusIn ) + { + GetGenericData()->ErrorTrapPush(); + gtk_im_context_focus_in( m_pIMContext ); + GetGenericData()->ErrorTrapPop(); + if( m_aInputEvent.mpTextAttr ) + { + sendEmptyCommit(); + // begin preedit again + GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + } + } + else + { + GetGenericData()->ErrorTrapPush(); + gtk_im_context_focus_out( m_pIMContext ); + GetGenericData()->ErrorTrapPop(); + // cancel an eventual event posted to begin preedit again + GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + } +} + +bool GtkSalFrame::IMHandler::handleKeyEvent( GdkEventKey* pEvent ) +{ + vcl::DeletionListener aDel( m_pFrame ); + + if( pEvent->type == GDK_KEY_PRESS ) + { + // Add this key press event to the list of previous key presses + // to which we compare key release events. If a later key release + // event has a matching key press event in this list, we swallow + // the key release because some GTK Input Methods don't swallow it + // for us. + m_aPrevKeyPresses.push_back( PreviousKeyPress(pEvent) ); + m_nPrevKeyPresses++; + + // Also pop off the earliest key press event if there are more than 10 + // already. + while (m_nPrevKeyPresses > 10) + { + m_aPrevKeyPresses.pop_front(); + m_nPrevKeyPresses--; + } + + GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); + + // #i51353# update spot location on every key input since we cannot + // know which key may activate a preedit choice window + updateIMSpotLocation(); + if( aDel.isDeleted() ) + return true; + + gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); + g_object_unref( pRef ); + + if( aDel.isDeleted() ) + return true; + + m_bPreeditJustChanged = false; + + if( bResult ) + return true; + else + { + DBG_ASSERT( m_nPrevKeyPresses > 0, "key press has vanished !" ); + if( ! m_aPrevKeyPresses.empty() ) // sanity check + { + // event was not swallowed, do not filter a following + // key release event + // note: this relies on gtk_im_context_filter_keypress + // returning without calling a handler (in the "not swallowed" + // case ) which might change the previous key press list so + // we would pop the wrong event here + m_aPrevKeyPresses.pop_back(); + m_nPrevKeyPresses--; + } + } + } + + // Determine if we got an earlier key press event corresponding to this key release + if (pEvent->type == GDK_KEY_RELEASE) + { + GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); + gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); + g_object_unref( pRef ); + + if( aDel.isDeleted() ) + return true; + + m_bPreeditJustChanged = false; + + std::list::iterator iter = m_aPrevKeyPresses.begin(); + std::list::iterator iter_end = m_aPrevKeyPresses.end(); + while (iter != iter_end) + { + // If we found a corresponding previous key press event, swallow the release + // and remove the earlier key press from our list + if (*iter == pEvent) + { + m_aPrevKeyPresses.erase(iter); + m_nPrevKeyPresses--; + return true; + } + ++iter; + } + + if( bResult ) + return true; + } + + return false; +} + +/* FIXME: +* #122282# still more hacking: some IMEs never start a preedit but simply commit +* in this case we cannot commit a single character. Workaround: do not do the +* single key hack for enter or space if the unicode committed does not match +*/ + +static bool checkSingleKeyCommitHack( guint keyval, sal_Unicode cCode ) +{ + bool bRet = true; + switch( keyval ) + { + case GDK_KP_Enter: + case GDK_Return: + if( cCode != '\n' && cCode != '\r' ) + bRet = false; + break; + case GDK_space: + case GDK_KP_Space: + if( cCode != ' ' ) + bRet = false; + break; + default: + break; + } + return bRet; +} + +void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext* pContext, gchar* pText, gpointer im_handler ) +{ + GtkSalFrame::IMHandler* pThis = static_cast(im_handler); + + SolarMutexGuard aGuard; + vcl::DeletionListener aDel( pThis->m_pFrame ); + { + const bool bWasPreedit = + (pThis->m_aInputEvent.mpTextAttr != 0) || + pThis->m_bPreeditJustChanged; + + pThis->m_aInputEvent.mnTime = 0; + pThis->m_aInputEvent.mpTextAttr = 0; + pThis->m_aInputEvent.maText = OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ); + pThis->m_aInputEvent.mnCursorPos = pThis->m_aInputEvent.maText.getLength(); + pThis->m_aInputEvent.mnCursorFlags = 0; + pThis->m_aInputEvent.mbOnlyCursor = False; + + pThis->m_aInputFlags.clear(); + + /* necessary HACK: all keyboard input comes in here as soon as a IMContext is set + * which is logical and consequent. But since even simple input like + * comes through the commit signal instead of signalKey + * and all kinds of windows only implement KeyInput (e.g. PushButtons, + * RadioButtons and a lot of other Controls), will send a single + * KeyInput/KeyUp sequence instead of an ExtText event if there + * never was a preedit and the text is only one character. + * + * In this case there the last ExtText event must have been + * SALEVENT_ENDEXTTEXTINPUT, either because of a regular commit + * or because there never was a preedit. + */ + bool bSingleCommit = false; + if( ! bWasPreedit + && pThis->m_aInputEvent.maText.getLength() == 1 + && ! pThis->m_aPrevKeyPresses.empty() + ) + { + const PreviousKeyPress& rKP = pThis->m_aPrevKeyPresses.back(); + sal_Unicode aOrigCode = pThis->m_aInputEvent.maText[0]; + + if( checkSingleKeyCommitHack( rKP.keyval, aOrigCode ) ) + { + pThis->m_pFrame->doKeyCallback( rKP.state, rKP.keyval, rKP.hardware_keycode, rKP.group, rKP.time, aOrigCode, true, true ); + bSingleCommit = true; + } + } + if( ! bSingleCommit ) + { + pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent); + if( ! aDel.isDeleted() ) + pThis->doCallEndExtTextInput(); + } + if( ! aDel.isDeleted() ) + { + // reset input event + pThis->m_aInputEvent.maText.clear(); + pThis->m_aInputEvent.mnCursorPos = 0; + pThis->updateIMSpotLocation(); + } + } +#ifdef SOLARIS + // #i51356# workaround a solaris IIIMP bug + // in case of partial commits the preedit changed signal + // and commit signal come in wrong order + if( ! aDel.isDeleted() ) + signalIMPreeditChanged( pContext, im_handler ); +#else + (void) pContext; +#endif +} + +void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext*, gpointer im_handler ) +{ + GtkSalFrame::IMHandler* pThis = static_cast(im_handler); + + char* pText = NULL; + PangoAttrList* pAttrs = NULL; + gint nCursorPos = 0; + + gtk_im_context_get_preedit_string( pThis->m_pIMContext, + &pText, + &pAttrs, + &nCursorPos ); + if( pText && ! *pText ) // empty string + { + // change from nothing to nothing -> do not start preedit + // e.g. this will activate input into a calc cell without + // user input + if( pThis->m_aInputEvent.maText.getLength() == 0 ) + { + g_free( pText ); + pango_attr_list_unref( pAttrs ); + return; + } + } + + pThis->m_bPreeditJustChanged = true; + + bool bEndPreedit = (!pText || !*pText) && pThis->m_aInputEvent.mpTextAttr != NULL; + pThis->m_aInputEvent.mnTime = 0; + pThis->m_aInputEvent.maText = pText ? OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ) : OUString(); + pThis->m_aInputEvent.mnCursorPos = nCursorPos; + pThis->m_aInputEvent.mnCursorFlags = 0; + pThis->m_aInputEvent.mbOnlyCursor = False; + + pThis->m_aInputFlags = std::vector( std::max( 1, (int)pThis->m_aInputEvent.maText.getLength() ), 0 ); + + PangoAttrIterator *iter = pango_attr_list_get_iterator(pAttrs); + do + { + GSList *attr_list = NULL; + GSList *tmp_list = NULL; + gint start, end; + guint sal_attr = 0; + + pango_attr_iterator_range (iter, &start, &end); + if (end == G_MAXINT) + end = pText ? strlen (pText) : 0; + if (end == start) + continue; + + start = g_utf8_pointer_to_offset (pText, pText + start); + end = g_utf8_pointer_to_offset (pText, pText + end); + + tmp_list = attr_list = pango_attr_iterator_get_attrs (iter); + while (tmp_list) + { + PangoAttribute *pango_attr = static_cast(tmp_list->data); + + switch (pango_attr->klass->type) + { + case PANGO_ATTR_BACKGROUND: + sal_attr |= (EXTTEXTINPUT_ATTR_HIGHLIGHT | EXTTEXTINPUT_CURSOR_INVISIBLE); + break; + case PANGO_ATTR_UNDERLINE: + sal_attr |= EXTTEXTINPUT_ATTR_UNDERLINE; + break; + case PANGO_ATTR_STRIKETHROUGH: + sal_attr |= EXTTEXTINPUT_ATTR_REDTEXT; + break; + default: + break; + } + pango_attribute_destroy (pango_attr); + tmp_list = tmp_list->next; + } + if (sal_attr == 0) + sal_attr |= EXTTEXTINPUT_ATTR_UNDERLINE; + g_slist_free (attr_list); + + // Set the sal attributes on our text + for (int i = start; i < end; ++i) + { + SAL_WARN_IF(i >= static_cast(pThis->m_aInputFlags.size()), + "vcl.gtk", "pango attrib out of range. Broken range: " + << start << "," << end << " Legal range: 0," + << pThis->m_aInputFlags.size()); + if (i >= static_cast(pThis->m_aInputFlags.size())) + continue; + pThis->m_aInputFlags[i] |= sal_attr; + } + } while (pango_attr_iterator_next (iter)); + pango_attr_iterator_destroy(iter); + + pThis->m_aInputEvent.mpTextAttr = &pThis->m_aInputFlags[0]; + + g_free( pText ); + pango_attr_list_unref( pAttrs ); + + SolarMutexGuard aGuard; + vcl::DeletionListener aDel( pThis->m_pFrame ); + + pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent); + if( bEndPreedit && ! aDel.isDeleted() ) + pThis->doCallEndExtTextInput(); + if( ! aDel.isDeleted() ) + pThis->updateIMSpotLocation(); +} + +void GtkSalFrame::IMHandler::signalIMPreeditStart( GtkIMContext*, gpointer /*im_handler*/ ) +{ +} + +void GtkSalFrame::IMHandler::signalIMPreeditEnd( GtkIMContext*, gpointer im_handler ) +{ + GtkSalFrame::IMHandler* pThis = static_cast(im_handler); + + pThis->m_bPreeditJustChanged = true; + + SolarMutexGuard aGuard; + vcl::DeletionListener aDel( pThis->m_pFrame ); + pThis->doCallEndExtTextInput(); + if( ! aDel.isDeleted() ) + pThis->updateIMSpotLocation(); +} + +uno::Reference + FindFocus(uno::Reference< accessibility::XAccessibleContext > xContext) +{ + if (!xContext.is()) + uno::Reference< accessibility::XAccessibleEditableText >(); + + uno::Reference xState = xContext->getAccessibleStateSet(); + if (xState.is()) + { + if (xState->contains(accessibility::AccessibleStateType::FOCUSED)) + return uno::Reference(xContext, uno::UNO_QUERY); + } + + for (sal_Int32 i = 0; i < xContext->getAccessibleChildCount(); ++i) + { + uno::Reference< accessibility::XAccessible > xChild = xContext->getAccessibleChild(i); + if (!xChild.is()) + continue; + uno::Reference< accessibility::XAccessibleContext > xChildContext = xChild->getAccessibleContext(); + if (!xChildContext.is()) + continue; + uno::Reference< accessibility::XAccessibleEditableText > xText = FindFocus(xChildContext); + if (xText.is()) + return xText; + } + return uno::Reference< accessibility::XAccessibleEditableText >(); +} + +static uno::Reference lcl_GetxText(vcl::Window *pFocusWin) +{ + uno::Reference xText; + try + { + uno::Reference< accessibility::XAccessible > xAccessible( pFocusWin->GetAccessible( true ) ); + if (xAccessible.is()) + xText = FindFocus(xAccessible->getAccessibleContext()); + } + catch(const uno::Exception& e) + { + SAL_WARN( "vcl.gtk", "Exception in getting input method surrounding text: " << e.Message); + } + return xText; +} + +gboolean GtkSalFrame::IMHandler::signalIMRetrieveSurrounding( GtkIMContext* pContext, gpointer /*im_handler*/ ) +{ + vcl::Window *pFocusWin = Application::GetFocusWindow(); + if (!pFocusWin) + return true; + + uno::Reference xText = lcl_GetxText(pFocusWin); + if (xText.is()) + { + sal_Int32 nPosition = xText->getCaretPosition(); + OUString sAllText = xText->getText(); + OString sUTF = OUStringToOString(sAllText, RTL_TEXTENCODING_UTF8); + OUString sCursorText(sAllText.copy(0, nPosition)); + gtk_im_context_set_surrounding(pContext, sUTF.getStr(), sUTF.getLength(), + OUStringToOString(sCursorText, RTL_TEXTENCODING_UTF8).getLength()); + return true; + } + + return false; +} + +gboolean GtkSalFrame::IMHandler::signalIMDeleteSurrounding( GtkIMContext*, gint offset, gint nchars, + gpointer /*im_handler*/ ) +{ + vcl::Window *pFocusWin = Application::GetFocusWindow(); + if (!pFocusWin) + return true; + + uno::Reference xText = lcl_GetxText(pFocusWin); + if (xText.is()) + { + sal_Int32 nPosition = xText->getCaretPosition(); + // #i111768# range checking + sal_Int32 nDeletePos = nPosition + offset; + sal_Int32 nDeleteEnd = nDeletePos + nchars; + if (nDeletePos < 0) + nDeletePos = 0; + if (nDeleteEnd < 0) + nDeleteEnd = 0; + if (nDeleteEnd > xText->getCharacterCount()) + nDeleteEnd = xText->getCharacterCount(); + + xText->deleteText(nDeletePos, nDeleteEnd); + //tdf91641 adjust cursor if deleted chars shift it forward (normal case) + if (nDeletePos < nPosition) + { + if (nDeleteEnd <= nPosition) + nPosition = nPosition - (nDeleteEnd - nDeletePos); + else + nPosition = nDeletePos; + + if (xText->getCharacterCount() >= nPosition) + xText->setCaretPosition( nPosition ); + } + return true; + } + + return false; +} + +Size GtkSalDisplay::GetScreenSize( int nDisplayScreen ) +{ + Rectangle aRect = m_pSys->GetDisplayScreenPosSizePixel( nDisplayScreen ); + return Size( aRect.GetWidth(), aRect.GetHeight() ); +} + +Window GtkSalFrame::GetX11Window() +{ + return widget_get_xid(m_pWindow); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx new file mode 100644 index 0000000..5716058 --- /dev/null +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -0,0 +1,641 @@ +/* -*- 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/. + */ + +#include "../../gtk/app/gtkinst.cxx" + +#include +#include "com/sun/star/lang/XServiceInfo.hpp" +#include "com/sun/star/lang/XSingleServiceFactory.hpp" +#include "com/sun/star/lang/XInitialization.hpp" +#include "com/sun/star/lang/DisposedException.hpp" +#include "com/sun/star/datatransfer/XTransferable.hpp" +#include "com/sun/star/datatransfer/clipboard/XClipboard.hpp" +#include "com/sun/star/datatransfer/clipboard/XClipboardEx.hpp" +#include "com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp" +#include "com/sun/star/datatransfer/clipboard/XClipboardListener.hpp" +#include "com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp" +#include "com/sun/star/datatransfer/dnd/XDragSource.hpp" +#include "com/sun/star/datatransfer/dnd/XDropTarget.hpp" +#include "com/sun/star/datatransfer/dnd/DNDConstants.hpp" +#include +#include +#include "cppuhelper/compbase.hxx" +#include "cppuhelper/implbase1.hxx" +#include + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; + +namespace +{ + struct TypeEntry + { + const char* pNativeType; // string corresponding to nAtom for the case of nAtom being uninitialized + const char* pType; // Mime encoding on our side + }; + + static TypeEntry aConversionTab[] = + { + { "ISO10646-1", "text/plain;charset=utf-16" }, + { "UTF8_STRING", "text/plain;charset=utf-8" }, + { "UTF-8", "text/plain;charset=utf-8" }, + { "text/plain;charset=UTF-8", "text/plain;charset=utf-8" }, + // ISO encodings + { "ISO8859-2", "text/plain;charset=iso8859-2" }, + { "ISO8859-3", "text/plain;charset=iso8859-3" }, + { "ISO8859-4", "text/plain;charset=iso8859-4" }, + { "ISO8859-5", "text/plain;charset=iso8859-5" }, + { "ISO8859-6", "text/plain;charset=iso8859-6" }, + { "ISO8859-7", "text/plain;charset=iso8859-7" }, + { "ISO8859-8", "text/plain;charset=iso8859-8" }, + { "ISO8859-9", "text/plain;charset=iso8859-9" }, + { "ISO8859-10", "text/plain;charset=iso8859-10" }, + { "ISO8859-13", "text/plain;charset=iso8859-13" }, + { "ISO8859-14", "text/plain;charset=iso8859-14" }, + { "ISO8859-15", "text/plain;charset=iso8859-15" }, + // asian encodings + { "JISX0201.1976-0", "text/plain;charset=jisx0201.1976-0" }, + { "JISX0208.1983-0", "text/plain;charset=jisx0208.1983-0" }, + { "JISX0208.1990-0", "text/plain;charset=jisx0208.1990-0" }, + { "JISX0212.1990-0", "text/plain;charset=jisx0212.1990-0" }, + { "GB2312.1980-0", "text/plain;charset=gb2312.1980-0" }, + { "KSC5601.1992-0", "text/plain;charset=ksc5601.1992-0" }, + // eastern european encodings + { "KOI8-R", "text/plain;charset=koi8-r" }, + { "KOI8-U", "text/plain;charset=koi8-u" }, + // String (== iso8859-1) + { "STRING", "text/plain;charset=iso8859-1" }, + // special for compound text + { "COMPOUND_TEXT", "text/plain;charset=compound_text" }, + + // PIXMAP + { "PIXMAP", "image/bmp" } + }; + + class DataFlavorEq : public std::unary_function + { + private: + const css::datatransfer::DataFlavor& m_rData; + public: + explicit DataFlavorEq(const css::datatransfer::DataFlavor& rData) : m_rData(rData) {} + bool operator() (const css::datatransfer::DataFlavor& rData) const + { + return rData.MimeType == m_rData.MimeType && + rData.DataType == m_rData.DataType; + } + }; +} + +class GtkTransferable : public ::cppu::WeakImplHelper1 < + css::datatransfer::XTransferable > +{ +private: + GdkAtom m_nSelection; + std::map m_aMimeTypeToAtom; +public: + + GtkTransferable(GdkAtom nSelection) + : m_nSelection(nSelection) + { + } + + /* + * XTransferable + */ + + virtual css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor) + throw(css::datatransfer::UnsupportedFlavorException, + css::io::IOException, + css::uno::RuntimeException, std::exception + ) SAL_OVERRIDE + { + GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); + if (rFlavor.MimeType == "text/plain;charset=utf-16") + { + OUString aStr; + gchar *pText = gtk_clipboard_wait_for_text(clipboard); + if (pText) + aStr = OUString(pText, rtl_str_getLength(pText), RTL_TEXTENCODING_UTF8); + g_free(pText); + css::uno::Any aRet; + aRet <<= aStr.replaceAll("\r\n", "\n"); + return aRet; + } + + auto it = m_aMimeTypeToAtom.find(rFlavor.MimeType); + if (it == m_aMimeTypeToAtom.end()) + return css::uno::Any(); + + css::uno::Any aRet; + GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard, + it->second); + gint length; + const guchar *rawdata = gtk_selection_data_get_data_with_length(data, + &length); + Sequence aSeq(reinterpret_cast(rawdata), length); + gtk_selection_data_free(data); + aRet <<= aSeq; + return aRet; + } + + std::vector getTransferDataFlavorsAsVector() + { + std::vector aVector; + + GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); + + GdkAtom *targets; + gint n_targets; + if (gtk_clipboard_wait_for_targets(clipboard, &targets, &n_targets)) + { + bool bHaveText = false, bHaveUTF16 = false; + + for (gint i = 0; i < n_targets; ++i) + { + gchar* pName = gdk_atom_name(targets[i]); + const char* pFinalName = pName; + css::datatransfer::DataFlavor aFlavor; + + for (size_t j = 0; j < SAL_N_ELEMENTS(aConversionTab); ++j) + { + if (rtl_str_compare(pName, aConversionTab[j].pNativeType) == 0) + { + pFinalName = aConversionTab[j].pType; + break; + } + } + + aFlavor.MimeType = OUString(pFinalName, + rtl_str_getLength(pFinalName), + RTL_TEXTENCODING_UTF8); + + m_aMimeTypeToAtom[aFlavor.MimeType] = targets[i]; + + aFlavor.DataType = cppu::UnoType>::get(); + + sal_Int32 nIndex(0); + if (aFlavor.MimeType.getToken(0, ';', nIndex) == "text/plain") + { + bHaveText = true; + OUString aToken(aFlavor.MimeType.getToken(0, ';', nIndex)); + if (aToken == "charset=utf-16") + { + bHaveUTF16 = true; + aFlavor.DataType = cppu::UnoType::get(); + } + } + aVector.push_back(aFlavor); + g_free(pName); + } + + g_free(targets); + + //If we have text, but no UTF-16 format which is basically the only + //text-format LibreOffice supports for cnp then claim we do and we + //will convert on demand + if (bHaveText && !bHaveUTF16) + { + css::datatransfer::DataFlavor aFlavor; + aFlavor.MimeType = "text/plain;charset=utf-16"; + aFlavor.DataType = cppu::UnoType::get(); + aVector.push_back(aFlavor); + } + } + + return aVector; + } + + virtual css::uno::Sequence SAL_CALL getTransferDataFlavors() + throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE + { + return comphelper::containerToSequence(getTransferDataFlavorsAsVector()); + } + + virtual sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor) + throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE + { + const std::vector aAll = + getTransferDataFlavorsAsVector(); + + return std::find_if(aAll.begin(), aAll.end(), DataFlavorEq(rFlavor)) != aAll.end(); + } +}; + +//We want to use gtk_clipboard_get_owner own owner-change to distinguish between +//us gaining the clipboard ownership vs losing it. To do that we need to use +//gtk_clipboard_set_with_owner and to do that we need a GObject, so define +//one here for that purpose and just give it a VclGtkClipboard* member +class VclGtkClipboard; + +typedef struct _ClipboardOwner ClipboardOwner; +typedef struct _ClipboardOwnerClass ClipboardOwnerClass; + +struct _ClipboardOwner +{ + GObject parent_instance; + + /* instance members */ + VclGtkClipboard* m_pThis; +}; + +struct _ClipboardOwnerClass +{ + GObjectClass parent_class; + + /* class members */ +}; + +#define CLIPBOARD_OWNER_OBJECT (clipboard_owner_get_type ()) +#define CLIPBOARD_OWNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLIPBOARD_OWNER_OBJECT, ClipboardOwner)) + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +#endif +G_DEFINE_TYPE(ClipboardOwner, clipboard_owner, G_TYPE_OBJECT); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +static void clipboard_owner_class_init (ClipboardOwnerClass *) +{ +} + +static void clipboard_owner_init(ClipboardOwner *) +{ +} + +class VclGtkClipboard : + public cppu::WeakComponentImplHelper< + datatransfer::clipboard::XSystemClipboard, + XServiceInfo> +{ + GdkAtom m_nSelection; + osl::Mutex m_aMutex; + ClipboardOwner* m_pOwner; + gulong m_nOwnerChangedSignalId; + Reference m_aContents; + Reference m_aOwner; + std::list< Reference > m_aListeners; + std::vector m_aGtkTargets; + std::vector m_aInfoToFlavor; + +public: + + VclGtkClipboard(GdkAtom nSelection); + virtual ~VclGtkClipboard(); + + /* + * XServiceInfo + */ + + virtual OUString SAL_CALL getImplementationName() throw( RuntimeException, std::exception ) SAL_OVERRIDE; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw( RuntimeException, std::exception ) SAL_OVERRIDE; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() throw( RuntimeException, std::exception ) SAL_OVERRIDE; + + static OUString getImplementationName_static(); + static Sequence< OUString > getSupportedServiceNames_static(); + + /* + * XClipboard + */ + + virtual Reference< css::datatransfer::XTransferable > SAL_CALL getContents() + throw(RuntimeException, std::exception) SAL_OVERRIDE; + + virtual void SAL_CALL setContents( + const Reference< css::datatransfer::XTransferable >& xTrans, + const Reference< css::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ) + throw(RuntimeException, std::exception) SAL_OVERRIDE; + + virtual OUString SAL_CALL getName() + throw(RuntimeException, std::exception) SAL_OVERRIDE; + + /* + * XClipboardEx + */ + + virtual sal_Int8 SAL_CALL getRenderingCapabilities() + throw(RuntimeException, std::exception) SAL_OVERRIDE; + + /* + * XClipboardNotifier + */ + virtual void SAL_CALL addClipboardListener( + const Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) + throw(RuntimeException, std::exception) SAL_OVERRIDE; + + virtual void SAL_CALL removeClipboardListener( + const Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) + throw(RuntimeException, std::exception) SAL_OVERRIDE; + + void ClipboardGet(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info); + void ClipboardClear(GtkClipboard *clipboard); + void OwnerChanged(GtkClipboard *clipboard, GdkEvent *event); +private: + GtkTargetEntry makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor); +}; + +OUString VclGtkClipboard::getImplementationName_static() +{ + return OUString( "com.sun.star.datatransfer.VclGtkClipboard" ); +} + +Sequence< OUString > VclGtkClipboard::getSupportedServiceNames_static() +{ + Sequence< OUString > aRet(1); + aRet[0] = "com.sun.star.datatransfer.clipboard.SystemClipboard"; + return aRet; +} + +OUString VclGtkClipboard::getImplementationName() throw( RuntimeException, std::exception ) +{ + return getImplementationName_static(); +} + +Sequence< OUString > VclGtkClipboard::getSupportedServiceNames() throw( RuntimeException, std::exception ) +{ + return getSupportedServiceNames_static(); +} + +sal_Bool VclGtkClipboard::supportsService( const OUString& ServiceName ) throw( RuntimeException, std::exception ) +{ + return cppu::supportsService(this, ServiceName); +} + +Reference< css::datatransfer::XTransferable > VclGtkClipboard::getContents() throw( RuntimeException, std::exception ) +{ + if (G_OBJECT(m_pOwner) != gtk_clipboard_get_owner(gtk_clipboard_get(m_nSelection)) && + !m_aContents.is()) + { + //tdf#93887 This is the system clipboard/selection. We fetch it when we are not + //the owner of the clipboard and have not already fetched it. + m_aContents = new GtkTransferable(m_nSelection); + } + return m_aContents; +} + +void VclGtkClipboard::ClipboardGet(GtkClipboard* /*clipboard*/, GtkSelectionData *selection_data, + guint info) +{ + if (!m_aContents.is()) + return; + + GdkAtom type(gdk_atom_intern(OUStringToOString(m_aInfoToFlavor[info].MimeType, + RTL_TEXTENCODING_UTF8).getStr(), + false)); + + css::datatransfer::DataFlavor aFlavor(m_aInfoToFlavor[info]); + if (aFlavor.MimeType == "UTF8_STRING" || aFlavor.MimeType == "STRING") + aFlavor.MimeType = "text/plain;charset=utf-8"; + + Sequence aData; + Any aValue; + + try + { + aValue = m_aContents->getTransferData(aFlavor); + } + catch(...) + { + } + + if (aValue.getValueTypeClass() == TypeClass_STRING) + { + OUString aString; + aValue >>= aString; + aData = Sequence< sal_Int8 >( reinterpret_cast(aString.getStr()), aString.getLength() * sizeof( sal_Unicode ) ); + } + else if (aValue.getValueType() == cppu::UnoType>::get()) + { + aValue >>= aData; + } + else if (aFlavor.MimeType == "text/plain;charset=utf-8") + { + //didn't have utf-8, try utf-16 and convert + aFlavor.MimeType = "text/plain;charset=utf-16"; + aFlavor.DataType = cppu::UnoType::get(); + try + { + aValue = m_aContents->getTransferData(aFlavor); + } + catch(...) + { + } + OUString aString; + aValue >>= aString; + OString aUTF8String(OUStringToOString(aString, RTL_TEXTENCODING_UTF8)); + gtk_selection_data_set(selection_data, type, 8, + reinterpret_cast(aUTF8String.getStr()), + aUTF8String.getLength()); + return; + } + + gtk_selection_data_set(selection_data, type, 8, + reinterpret_cast(aData.getArray()), + aData.getLength()); +} + +void VclGtkClipboard::OwnerChanged(GtkClipboard* clipboard, GdkEvent* /*event*/) +{ + if (G_OBJECT(m_pOwner) != gtk_clipboard_get_owner(clipboard)) + { + //null out m_aContents to return control to the system-one which + //will be retrieved if getContents is called again + setContents(Reference(), + Reference()); + } +} + +void VclGtkClipboard::ClipboardClear(GtkClipboard * /*clipboard*/) +{ + for (auto &a : m_aGtkTargets) + free(a.target); + m_aGtkTargets.clear(); +} + +GtkTargetEntry VclGtkClipboard::makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor) +{ + GtkTargetEntry aEntry; + aEntry.target = + g_strdup(OUStringToOString(rFlavor.MimeType, RTL_TEXTENCODING_UTF8).getStr()); + aEntry.flags = 0; + auto it = std::find_if(m_aInfoToFlavor.begin(), m_aInfoToFlavor.end(), + DataFlavorEq(rFlavor)); + if (it != m_aInfoToFlavor.end()) + aEntry.info = std::distance(m_aInfoToFlavor.begin(), it); + else + { + aEntry.info = m_aInfoToFlavor.size(); + m_aInfoToFlavor.push_back(rFlavor); + } + return aEntry; +} + +namespace +{ + void ClipboardGetFunc(GtkClipboard *clipboard, GtkSelectionData *selection_data, + guint info, + gpointer user_data_or_owner) + { + VclGtkClipboard* pThis = CLIPBOARD_OWNER(user_data_or_owner)->m_pThis; + pThis->ClipboardGet(clipboard, selection_data, info); + } + + void ClipboardClearFunc(GtkClipboard *clipboard, gpointer user_data_or_owner) + { + VclGtkClipboard* pThis = CLIPBOARD_OWNER(user_data_or_owner)->m_pThis; + pThis->ClipboardClear(clipboard); + } + + void handle_owner_change(GtkClipboard *clipboard, GdkEvent *event, gpointer user_data) + { + VclGtkClipboard* pThis = static_cast(user_data); + pThis->OwnerChanged(clipboard, event); + } +} + +VclGtkClipboard::VclGtkClipboard(GdkAtom nSelection) + : cppu::WeakComponentImplHelper + (m_aMutex) + , m_nSelection(nSelection) +{ + GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); + m_nOwnerChangedSignalId = g_signal_connect(clipboard, "owner-change", + G_CALLBACK(handle_owner_change), this); + m_pOwner = CLIPBOARD_OWNER(g_object_new(CLIPBOARD_OWNER_OBJECT, NULL)); + m_pOwner->m_pThis = this; +} + +VclGtkClipboard::~VclGtkClipboard() +{ + GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); + g_signal_handler_disconnect(clipboard, m_nOwnerChangedSignalId); + g_object_unref(m_pOwner); +} + +void VclGtkClipboard::setContents( + const Reference< css::datatransfer::XTransferable >& xTrans, + const Reference< css::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ) + throw( RuntimeException, std::exception ) +{ + osl::ClearableMutexGuard aGuard( m_aMutex ); + Reference< datatransfer::clipboard::XClipboardOwner > xOldOwner( m_aOwner ); + Reference< datatransfer::XTransferable > xOldContents( m_aContents ); + m_aContents = xTrans; + m_aOwner = xClipboardOwner; + + std::list< Reference< datatransfer::clipboard::XClipboardListener > > xListeners( m_aListeners ); + datatransfer::clipboard::ClipboardEvent aEv; + + if (m_aContents.is()) + { + css::uno::Sequence aFormats = xTrans->getTransferDataFlavors(); + std::vector aGtkTargets; + bool bHaveText(false), bHaveUTF8(false); + for (int i = 0; i < aFormats.getLength(); ++i) + { + const css::datatransfer::DataFlavor& rFlavor = aFormats[i]; + + sal_Int32 nIndex(0); + if (rFlavor.MimeType.getToken(0, ';', nIndex) == "text/plain") + { + bHaveText = true; + OUString aToken(rFlavor.MimeType.getToken(0, ';', nIndex)); + if (aToken == "charset=utf-8") + { + bHaveUTF8 = true; + } + } + GtkTargetEntry aEntry(makeGtkTargetEntry(rFlavor)); + aGtkTargets.push_back(aEntry); + } + + if (bHaveText) + { + css::datatransfer::DataFlavor aFlavor; + aFlavor.DataType = cppu::UnoType>::get(); + if (!bHaveUTF8) + { + aFlavor.MimeType = "text/plain;charset=utf-8"; + aGtkTargets.push_back(makeGtkTargetEntry(aFlavor)); + } + aFlavor.MimeType = "UTF8_STRING"; + aGtkTargets.push_back(makeGtkTargetEntry(aFlavor)); + aFlavor.MimeType = "STRING"; + aGtkTargets.push_back(makeGtkTargetEntry(aFlavor)); + } + + //if there was a previous gtk_clipboard_set_with_data call then + //ClipboardClearFunc will be called now + GtkClipboard* clipboard = gtk_clipboard_get(m_nSelection); + //use with_owner with m_pOwner so we can distinguish in handle_owner_change + //if we have gained or lost ownership of the clipboard + gtk_clipboard_set_with_owner(clipboard, aGtkTargets.data(), aGtkTargets.size(), + ClipboardGetFunc, ClipboardClearFunc, G_OBJECT(m_pOwner)); + m_aGtkTargets = aGtkTargets; + } + + aEv.Contents = getContents(); + + aGuard.clear(); + + if( xOldOwner.is() && xOldOwner != xClipboardOwner ) + xOldOwner->lostOwnership( this, xOldContents ); + for( std::list< Reference< datatransfer::clipboard::XClipboardListener > >::iterator it = + xListeners.begin(); it != xListeners.end() ; ++it ) + { + (*it)->changedContents( aEv ); + } +} + +OUString VclGtkClipboard::getName() throw( RuntimeException, std::exception ) +{ + return OUString( "CLIPBOARD" ); +} + +sal_Int8 VclGtkClipboard::getRenderingCapabilities() throw( RuntimeException, std::exception ) +{ + return 0; +} + +void VclGtkClipboard::addClipboardListener( const Reference< datatransfer::clipboard::XClipboardListener >& listener ) + throw( RuntimeException, std::exception ) +{ + osl::ClearableMutexGuard aGuard( m_aMutex ); + + m_aListeners.push_back( listener ); +} + +void VclGtkClipboard::removeClipboardListener( const Reference< datatransfer::clipboard::XClipboardListener >& listener ) + throw( RuntimeException, std::exception ) +{ + osl::ClearableMutexGuard aGuard( m_aMutex ); + + m_aListeners.remove( listener ); +} + +Reference< XInterface > GtkInstance::CreateClipboard(const Sequence< Any >& arguments) +{ + OUString sel; + if (arguments.getLength() == 0) { + sel = "CLIPBOARD"; + } else if (arguments.getLength() != 1 || !(arguments[0] >>= sel)) { + throw css::lang::IllegalArgumentException( + "bad GtkInstance::CreateClipboard arguments", + css::uno::Reference(), -1); + } + + GdkAtom nSelection = (sel == "CLIPBOARD") ? GDK_SELECTION_CLIPBOARD : GDK_SELECTION_PRIMARY; + + return Reference< XInterface >( static_cast(new VclGtkClipboard(nSelection)) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/window/gtk3gtkframe.cxx b/vcl/unx/gtk3/window/gtk3gtkframe.cxx deleted file mode 100644 index 5710d50..0000000 --- a/vcl/unx/gtk3/window/gtk3gtkframe.cxx +++ /dev/null @@ -1,12 +0,0 @@ -/* -*- 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/. - */ - -#include "../../gtk/window/gtksalframe.cxx" - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- 2.5.0