1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <unx/gtk/gtkframe.hxx>
21 #include <unx/gtk/gtkdata.hxx>
22 #include <unx/gtk/gtkinst.hxx>
23 #include <unx/gtk/gtkgdi.hxx>
24 #include <unx/gtk/gtksalmenu.hxx>
25 #include <unx/gtk/hudawareness.h>
26 #include <vcl/event.hxx>
27 #include <vcl/i18nhelp.hxx>
28 #include <vcl/keycodes.hxx>
29 #include <unx/geninst.h>
30 #include <headless/svpgdi.hxx>
31 #include <sal/log.hxx>
32 #include <comphelper/diagnose_ex.hxx>
33 #include <vcl/toolkit/floatwin.hxx>
34 #include <vcl/toolkit/unowrap.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/weld.hxx>
37 #include <vcl/window.hxx>
38 #include <vcl/settings.hxx>
43 #include <X11/Xutil.h>
44 #include <unx/gtk/gtkbackend.hxx>
46 #include <strings.hrc>
49 #include <basegfx/vector/b2ivector.hxx>
50 #include <officecfg/Office/Common.hxx>
56 #if OSL_DEBUG_LEVEL > 1
60 #include <i18nlangtag/mslangid.hxx>
65 #include <com/sun/star/awt/MouseButton.hpp>
66 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
67 #include <com/sun/star/frame/Desktop.hpp>
68 #include <com/sun/star/util/XModifiable.hpp>
70 #if !GTK_CHECK_VERSION(4, 0, 0)
71 # define GDK_ALT_MASK GDK_MOD1_MASK
72 # define GDK_TOPLEVEL_STATE_MAXIMIZED GDK_WINDOW_STATE_MAXIMIZED
73 # define GDK_TOPLEVEL_STATE_MINIMIZED GDK_WINDOW_STATE_ICONIFIED
74 # define gdk_wayland_surface_get_wl_surface gdk_wayland_window_get_wl_surface
75 # define gdk_x11_surface_get_xid gdk_x11_window_get_xid
78 using namespace com::sun::star
;
80 int GtkSalFrame::m_nFloats
= 0;
82 static GDBusConnection
* pSessionBus
= nullptr;
84 static void EnsureSessionBus()
87 pSessionBus
= g_bus_get_sync(G_BUS_TYPE_SESSION
, nullptr, nullptr);
90 sal_uInt16
GtkSalFrame::GetKeyModCode( guint state
)
93 if( state
& GDK_SHIFT_MASK
)
95 if( state
& GDK_CONTROL_MASK
)
97 if (state
& GDK_ALT_MASK
)
99 if( state
& GDK_SUPER_MASK
)
104 sal_uInt16
GtkSalFrame::GetMouseModCode( guint state
)
106 sal_uInt16 nCode
= GetKeyModCode( state
);
107 if( state
& GDK_BUTTON1_MASK
)
109 if( state
& GDK_BUTTON2_MASK
)
110 nCode
|= MOUSE_MIDDLE
;
111 if( state
& GDK_BUTTON3_MASK
)
112 nCode
|= MOUSE_RIGHT
;
117 // KEY_F26 is the last function key known to keycodes.hxx
118 static bool IsFunctionKeyVal(guint keyval
)
120 return keyval
>= GDK_KEY_F1
&& keyval
<= GDK_KEY_F26
;
123 sal_uInt16
GtkSalFrame::GetKeyCode(guint keyval
)
125 sal_uInt16 nCode
= 0;
126 if( keyval
>= GDK_KEY_0
&& keyval
<= GDK_KEY_9
)
127 nCode
= KEY_0
+ (keyval
-GDK_KEY_0
);
128 else if( keyval
>= GDK_KEY_KP_0
&& keyval
<= GDK_KEY_KP_9
)
129 nCode
= KEY_0
+ (keyval
-GDK_KEY_KP_0
);
130 else if( keyval
>= GDK_KEY_A
&& keyval
<= GDK_KEY_Z
)
131 nCode
= KEY_A
+ (keyval
-GDK_KEY_A
);
132 else if( keyval
>= GDK_KEY_a
&& keyval
<= GDK_KEY_z
)
133 nCode
= KEY_A
+ (keyval
-GDK_KEY_a
);
134 else if (IsFunctionKeyVal(keyval
))
138 // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx
139 // although GDK_KEY_F1 ... GDK_KEY_L10 are known to
140 // gdk/gdkkeysyms.h, they are unlikely to be generated
141 // except possibly by Solaris systems
142 // this whole section needs review
146 case GDK_KEY_L3
: nCode
= KEY_PROPERTIES
; break;
147 case GDK_KEY_L4
: nCode
= KEY_UNDO
; break;
148 case GDK_KEY_L6
: nCode
= KEY_COPY
; break; // KEY_F16
149 case GDK_KEY_L8
: nCode
= KEY_PASTE
; break; // KEY_F18
150 case GDK_KEY_L10
: nCode
= KEY_CUT
; break; // KEY_F20
152 nCode
= KEY_F1
+ (keyval
-GDK_KEY_F1
); break;
159 case GDK_KEY_KP_Down
:
160 case GDK_KEY_Down
: nCode
= KEY_DOWN
; break;
162 case GDK_KEY_Up
: nCode
= KEY_UP
; break;
163 case GDK_KEY_KP_Left
:
164 case GDK_KEY_Left
: nCode
= KEY_LEFT
; break;
165 case GDK_KEY_KP_Right
:
166 case GDK_KEY_Right
: nCode
= KEY_RIGHT
; break;
167 case GDK_KEY_KP_Begin
:
168 case GDK_KEY_KP_Home
:
170 case GDK_KEY_Home
: nCode
= KEY_HOME
; break;
172 case GDK_KEY_End
: nCode
= KEY_END
; break;
173 case GDK_KEY_KP_Page_Up
:
174 case GDK_KEY_Page_Up
: nCode
= KEY_PAGEUP
; break;
175 case GDK_KEY_KP_Page_Down
:
176 case GDK_KEY_Page_Down
: nCode
= KEY_PAGEDOWN
; break;
177 case GDK_KEY_KP_Enter
:
178 case GDK_KEY_Return
: nCode
= KEY_RETURN
; break;
179 case GDK_KEY_Escape
: nCode
= KEY_ESCAPE
; break;
180 case GDK_KEY_ISO_Left_Tab
:
182 case GDK_KEY_Tab
: nCode
= KEY_TAB
; break;
183 case GDK_KEY_BackSpace
: nCode
= KEY_BACKSPACE
; break;
184 case GDK_KEY_KP_Space
:
185 case GDK_KEY_space
: nCode
= KEY_SPACE
; break;
186 case GDK_KEY_KP_Insert
:
187 case GDK_KEY_Insert
: nCode
= KEY_INSERT
; break;
188 case GDK_KEY_KP_Delete
:
189 case GDK_KEY_Delete
: nCode
= KEY_DELETE
; break;
191 case GDK_KEY_KP_Add
: nCode
= KEY_ADD
; break;
193 case GDK_KEY_KP_Subtract
: nCode
= KEY_SUBTRACT
; break;
194 case GDK_KEY_asterisk
:
195 case GDK_KEY_KP_Multiply
: nCode
= KEY_MULTIPLY
; break;
197 case GDK_KEY_KP_Divide
: nCode
= KEY_DIVIDE
; break;
198 case GDK_KEY_period
: nCode
= KEY_POINT
; break;
199 case GDK_KEY_decimalpoint
: nCode
= KEY_POINT
; break;
200 case GDK_KEY_comma
: nCode
= KEY_COMMA
; break;
201 case GDK_KEY_less
: nCode
= KEY_LESS
; break;
202 case GDK_KEY_greater
: nCode
= KEY_GREATER
; break;
203 case GDK_KEY_KP_Equal
:
204 case GDK_KEY_equal
: nCode
= KEY_EQUAL
; break;
205 case GDK_KEY_Find
: nCode
= KEY_FIND
; break;
206 case GDK_KEY_Menu
: nCode
= KEY_CONTEXTMENU
;break;
207 case GDK_KEY_Help
: nCode
= KEY_HELP
; break;
208 case GDK_KEY_Undo
: nCode
= KEY_UNDO
; break;
209 case GDK_KEY_Redo
: nCode
= KEY_REPEAT
; break;
210 // on a sun keyboard this actually is usually SunXK_Stop = 0x0000FF69 (XK_Cancel),
211 // but VCL doesn't have a key definition for that
212 case GDK_KEY_Cancel
: nCode
= KEY_F11
; break;
213 case GDK_KEY_KP_Decimal
:
214 case GDK_KEY_KP_Separator
: nCode
= KEY_DECIMAL
; break;
215 case GDK_KEY_asciitilde
: nCode
= KEY_TILDE
; break;
216 case GDK_KEY_leftsinglequotemark
:
217 case GDK_KEY_quoteleft
: nCode
= KEY_QUOTELEFT
; break;
218 case GDK_KEY_bracketleft
: nCode
= KEY_BRACKETLEFT
; break;
219 case GDK_KEY_bracketright
: nCode
= KEY_BRACKETRIGHT
; break;
220 case GDK_KEY_semicolon
: nCode
= KEY_SEMICOLON
; break;
221 case GDK_KEY_quoteright
: nCode
= KEY_QUOTERIGHT
; break;
222 case GDK_KEY_braceright
: nCode
= KEY_RIGHTCURLYBRACKET
; break;
223 case GDK_KEY_colon
: nCode
= KEY_COLON
; break;
224 // some special cases, also see saldisp.cxx
225 // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000
226 // These can be found in ap_keysym.h
227 case 0x1000FF02: // apXK_Copy
230 case 0x1000FF03: // apXK_Cut
233 case 0x1000FF04: // apXK_Paste
236 case 0x1000FF14: // apXK_Repeat
240 // - - - - - - - - - - - - - - D E C - - - - - - - - - - - - - 0x1000
241 // These can be found in DECkeysym.h
245 // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000
246 // These can be found in HPkeysym.h
247 case 0x1000FF73: // hpXK_DeleteChar
250 case 0x1000FF74: // hpXK_BackTab
251 case 0x1000FF75: // hpXK_KP_BackTab
254 // - - - - - - - - - - - - - - I B M - - - - - - - - - - - - -
255 // - - - - - - - - - - - - - - O S F - - - - - - - - - - - - - 0x1004
256 // These also can be found in HPkeysym.h
257 case 0x1004FF02: // osfXK_Copy
260 case 0x1004FF03: // osfXK_Cut
263 case 0x1004FF04: // osfXK_Paste
266 case 0x1004FF07: // osfXK_BackTab
269 case 0x1004FF08: // osfXK_BackSpace
270 nCode
= KEY_BACKSPACE
;
272 case 0x1004FF1B: // osfXK_Escape
275 // Up, Down, Left, Right, PageUp, PageDown
276 // - - - - - - - - - - - - - - S C O - - - - - - - - - - - - -
277 // - - - - - - - - - - - - - - S G I - - - - - - - - - - - - - 0x1007
278 // - - - - - - - - - - - - - - S N I - - - - - - - - - - - - -
279 // - - - - - - - - - - - - - - S U N - - - - - - - - - - - - - 0x1005
280 // These can be found in Sunkeysym.h
281 case 0x1005FF10: // SunXK_F36
284 case 0x1005FF11: // SunXK_F37
287 case 0x1005FF70: // SunXK_Props
288 nCode
= KEY_PROPERTIES
;
290 case 0x1005FF71: // SunXK_Front
293 case 0x1005FF72: // SunXK_Copy
296 case 0x1005FF73: // SunXK_Open
299 case 0x1005FF74: // SunXK_Paste
302 case 0x1005FF75: // SunXK_Cut
305 // - - - - - - - - - - - - - X F 8 6 - - - - - - - - - - - - - 0x1008
306 // These can be found in XF86keysym.h
307 // but more importantly they are also available GTK/Gdk version 3
308 // and hence are already provided in gdk/gdkkeysyms.h, and hence
310 case GDK_KEY_Copy
: nCode
= KEY_COPY
; break; // 0x1008ff57
311 case GDK_KEY_Cut
: nCode
= KEY_CUT
; break; // 0x1008ff58
312 case GDK_KEY_Open
: nCode
= KEY_OPEN
; break; // 0x1008ff6b
313 case GDK_KEY_Paste
: nCode
= KEY_PASTE
; break; // 0x1008ff6d
320 #if !GTK_CHECK_VERSION(4, 0, 0)
321 guint
GtkSalFrame::GetKeyValFor(GdkKeymap
* pKeyMap
, guint16 hardware_keycode
, guint8 group
)
323 guint updated_keyval
= 0;
324 gdk_keymap_translate_keyboard_state(pKeyMap
, hardware_keycode
,
325 GdkModifierType(0), group
, &updated_keyval
, nullptr, nullptr, nullptr);
326 return updated_keyval
;
332 // F10 means either KEY_F10 or KEY_MENU, which has to be decided
333 // in the independent part.
337 sal_Unicode nCharCode
;
338 KeyAlternate() : nKeyCode( 0 ), nCharCode( 0 ) {}
339 KeyAlternate( sal_uInt16 nKey
, sal_Unicode nChar
= 0 ) : nKeyCode( nKey
), nCharCode( nChar
) {}
345 GetAlternateKeyCode( const sal_uInt16 nKeyCode
)
347 KeyAlternate aAlternate
;
351 case KEY_F10
: aAlternate
= KeyAlternate( KEY_MENU
);break;
352 case KEY_F24
: aAlternate
= KeyAlternate( KEY_SUBTRACT
, '-' );break;
358 #if OSL_DEBUG_LEVEL > 0
359 static bool dumpframes
= false;
362 bool GtkSalFrame::doKeyCallback( guint state
,
364 guint16 hardware_keycode
,
366 sal_Unicode aOrigCode
,
373 aEvent
.mnCharCode
= aOrigCode
;
376 vcl::DeletionListener
aDel( this );
378 #if OSL_DEBUG_LEVEL > 0
379 const char* pKeyDebug
= getenv("VCL_GTK3_PAINTDEBUG");
381 if (pKeyDebug
&& *pKeyDebug
== '1')
385 // shift-zero forces a re-draw and event is swallowed
386 if (keyval
== GDK_KEY_0
)
388 SAL_INFO("vcl.gtk3", "force widget_queue_draw.");
389 gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea
));
392 else if (keyval
== GDK_KEY_1
)
394 SAL_INFO("vcl.gtk3", "force repaint all.");
398 else if (keyval
== GDK_KEY_2
)
400 dumpframes
= !dumpframes
;
401 SAL_INFO("vcl.gtk3", "toggle dump frames to " << dumpframes
);
409 * #i42122# translate all keys with Ctrl and/or Alt to group 0 else
410 * shortcuts (e.g. Ctrl-o) will not work but be inserted by the
413 * #i52338# do this for all keys that the independent part has no key code
416 * fdo#41169 rather than use group 0, detect if there is a group which can
417 * be used to input Latin text and use that if possible
419 aEvent
.mnCode
= GetKeyCode( keyval
);
420 #if !GTK_CHECK_VERSION(4, 0, 0)
421 if( aEvent
.mnCode
== 0 )
423 gint best_group
= SAL_MAX_INT32
;
425 // Try and find Latin layout
426 GdkKeymap
* keymap
= gdk_keymap_get_default();
429 if (gdk_keymap_get_entries_for_keyval(keymap
, GDK_KEY_A
, &keys
, &n_keys
))
431 // Find the lowest group that supports Latin layout
432 for (gint i
= 0; i
< n_keys
; ++i
)
434 if (keys
[i
].level
!= 0 && keys
[i
].level
!= 1)
436 best_group
= std::min(best_group
, keys
[i
].group
);
443 //Unavailable, go with original group then I suppose
444 if (best_group
== SAL_MAX_INT32
)
447 guint updated_keyval
= GetKeyValFor(keymap
, hardware_keycode
, best_group
);
448 aEvent
.mnCode
= GetKeyCode(updated_keyval
);
451 (void)hardware_keycode
;
455 aEvent
.mnCode
|= GetKeyModCode( state
);
457 bool bStopProcessingKey
;
460 // tdf#152404 Commit uncommitted text before dispatching key shortcuts. In
461 // certain cases such as pressing Control-Alt-C in a Writer document while
462 // there is uncommitted text will call GtkSalFrame::EndExtTextInput() which
463 // will dispatch a SalEvent::EndExtTextInput event. Writer's handler for that
464 // event will delete the uncommitted text and then insert the committed text
465 // but LibreOffice will crash when deleting the uncommitted text because
466 // deletion of the text also removes and deletes the newly inserted comment.
467 if (m_pIMHandler
&& !m_pIMHandler
->m_aInputEvent
.maText
.isEmpty() && (aEvent
.mnCode
& (KEY_MOD1
| KEY_MOD2
)))
468 m_pIMHandler
->doCallEndExtTextInput();
470 bStopProcessingKey
= CallCallbackExc(SalEvent::KeyInput
, &aEvent
);
471 // #i46889# copy AlternateKeyCode handling from generic plugin
472 if (!bStopProcessingKey
)
474 KeyAlternate aAlternate
= GetAlternateKeyCode( aEvent
.mnCode
);
475 if( aAlternate
.nKeyCode
)
477 aEvent
.mnCode
= aAlternate
.nKeyCode
;
478 if( aAlternate
.nCharCode
)
479 aEvent
.mnCharCode
= aAlternate
.nCharCode
;
480 bStopProcessingKey
= CallCallbackExc(SalEvent::KeyInput
, &aEvent
);
483 if( bSendRelease
&& ! aDel
.isDeleted() )
485 CallCallbackExc(SalEvent::KeyUp
, &aEvent
);
489 bStopProcessingKey
= CallCallbackExc(SalEvent::KeyUp
, &aEvent
);
490 return bStopProcessingKey
;
493 GtkSalFrame::GtkSalFrame( SalFrame
* pParent
, SalFrameStyleFlags nStyle
)
494 : m_nXScreen( getDisplay()->GetDefaultXScreen() )
495 , m_pHeaderBar(nullptr)
497 , m_nSetFocusSignalId(0)
498 #if !GTK_CHECK_VERSION(4, 0, 0)
499 , m_aSmoothScrollIdle("GtkSalFrame m_aSmoothScrollIdle")
502 getDisplay()->registerFrame( this );
503 m_bDefaultPos
= true;
504 m_bDefaultSize
= ( (nStyle
& SalFrameStyleFlags::SIZEABLE
) && ! pParent
);
505 Init( pParent
, nStyle
);
508 GtkSalFrame::GtkSalFrame( SystemParentData
* pSysData
)
509 : m_nXScreen( getDisplay()->GetDefaultXScreen() )
510 , m_pHeaderBar(nullptr)
512 , m_nSetFocusSignalId(0)
513 #if !GTK_CHECK_VERSION(4, 0, 0)
514 , m_aSmoothScrollIdle("GtkSalFrame m_aSmoothScrollIdle")
517 getDisplay()->registerFrame( this );
518 // permanently ignore errors from our unruly children ...
519 GetGenericUnixSalData()->ErrorTrapPush();
520 m_bDefaultPos
= true;
521 m_bDefaultSize
= true;
525 // AppMenu watch functions.
527 static void ObjectDestroyedNotify( gpointer data
)
530 g_object_unref( data
);
534 #if !GTK_CHECK_VERSION(4,0,0)
535 static void hud_activated( gboolean hud_active
, gpointer user_data
)
539 SolarMutexGuard aGuard
;
540 GtkSalFrame
* pSalFrame
= static_cast< GtkSalFrame
* >( user_data
);
541 GtkSalMenu
* pSalMenu
= reinterpret_cast< GtkSalMenu
* >( pSalFrame
->GetMenu() );
544 pSalMenu
->UpdateFull();
549 static void attach_menu_model(GtkSalFrame
* pSalFrame
)
551 GtkWidget
* pWidget
= pSalFrame
->getWindow();
552 GdkSurface
* gdkWindow
= widget_get_surface(pWidget
);
554 if ( gdkWindow
== nullptr || g_object_get_data( G_OBJECT( gdkWindow
), "g-lo-menubar" ) != nullptr )
557 // Create menu model and action group attached to this frame.
558 GMenuModel
* pMenuModel
= G_MENU_MODEL( g_lo_menu_new() );
559 GActionGroup
* pActionGroup
= reinterpret_cast<GActionGroup
*>(g_lo_action_group_new());
561 // Set window properties.
562 g_object_set_data_full( G_OBJECT( gdkWindow
), "g-lo-menubar", pMenuModel
, ObjectDestroyedNotify
);
563 g_object_set_data_full( G_OBJECT( gdkWindow
), "g-lo-action-group", pActionGroup
, ObjectDestroyedNotify
);
565 #if !GTK_CHECK_VERSION(4,0,0)
566 // Get a DBus session connection.
571 // Generate menu paths.
572 sal_uIntPtr windowId
= GtkSalFrame::GetNativeWindowHandle(pWidget
);
573 gchar
* aDBusWindowPath
= g_strdup_printf( "/org/libreoffice/window/%lu", windowId
);
574 gchar
* aDBusMenubarPath
= g_strdup_printf( "/org/libreoffice/window/%lu/menus/menubar", windowId
);
576 GdkDisplay
*pDisplay
= GtkSalFrame::getGdkDisplay();
577 #if defined(GDK_WINDOWING_X11)
578 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay
))
580 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_APPLICATION_ID", "org.libreoffice" );
581 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_MENUBAR_OBJECT_PATH", aDBusMenubarPath
);
582 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_WINDOW_OBJECT_PATH", aDBusWindowPath
);
583 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_APPLICATION_OBJECT_PATH", "/org/libreoffice" );
584 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_UNIQUE_BUS_NAME", g_dbus_connection_get_unique_name( pSessionBus
) );
587 #if defined(GDK_WINDOWING_WAYLAND)
588 if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay
))
590 gdk_wayland_window_set_dbus_properties_libgtk_only(gdkWindow
, "org.libreoffice",
595 g_dbus_connection_get_unique_name( pSessionBus
));
598 // Publish the menu model and the action group.
599 SAL_INFO("vcl.unity", "exporting menu model at " << pMenuModel
<< " for window " << windowId
);
600 pSalFrame
->m_nMenuExportId
= g_dbus_connection_export_menu_model (pSessionBus
, aDBusMenubarPath
, pMenuModel
, nullptr);
601 SAL_INFO("vcl.unity", "exporting action group at " << pActionGroup
<< " for window " << windowId
);
602 pSalFrame
->m_nActionGroupExportId
= g_dbus_connection_export_action_group( pSessionBus
, aDBusWindowPath
, pActionGroup
, nullptr);
603 pSalFrame
->m_nHudAwarenessId
= hud_awareness_register( pSessionBus
, aDBusMenubarPath
, hud_activated
, pSalFrame
, nullptr, nullptr );
605 g_free( aDBusWindowPath
);
606 g_free( aDBusMenubarPath
);
610 void on_registrar_available( GDBusConnection
* /*connection*/,
611 const gchar
* /*name*/,
612 const gchar
* /*name_owner*/,
615 SolarMutexGuard aGuard
;
617 GtkSalFrame
* pSalFrame
= static_cast< GtkSalFrame
* >( user_data
);
619 SalMenu
* pSalMenu
= pSalFrame
->GetMenu();
621 if ( pSalMenu
!= nullptr )
623 GtkSalMenu
* pGtkSalMenu
= static_cast<GtkSalMenu
*>(pSalMenu
);
624 pGtkSalMenu
->EnableUnity(true);
628 // This is called when the registrar becomes unavailable. It shows the menubar.
629 void on_registrar_unavailable( GDBusConnection
* /*connection*/,
630 const gchar
* /*name*/,
633 SolarMutexGuard aGuard
;
635 SAL_INFO("vcl.unity", "on_registrar_unavailable");
637 GtkSalFrame
* pSalFrame
= static_cast< GtkSalFrame
* >( user_data
);
639 SalMenu
* pSalMenu
= pSalFrame
->GetMenu();
642 GtkSalMenu
* pGtkSalMenu
= static_cast< GtkSalMenu
* >( pSalMenu
);
643 pGtkSalMenu
->EnableUnity(false);
647 void GtkSalFrame::EnsureAppMenuWatch()
652 // Get a DBus session connection.
657 // Publish the menu only if AppMenu registrar is available.
658 m_nWatcherId
= g_bus_watch_name_on_connection( pSessionBus
,
659 "com.canonical.AppMenu.Registrar",
660 G_BUS_NAME_WATCHER_FLAGS_NONE
,
661 on_registrar_available
,
662 on_registrar_unavailable
,
667 void GtkSalFrame::InvalidateGraphics()
675 GtkSalFrame::~GtkSalFrame()
677 #if !GTK_CHECK_VERSION(4,0,0)
678 m_aSmoothScrollIdle
.Stop();
679 m_aSmoothScrollIdle
.ClearInvokeHandler();
684 m_pDropTarget
->deinitialize();
685 m_pDropTarget
= nullptr;
690 m_pDragSource
->deinitialize();
691 m_pDragSource
= nullptr;
694 InvalidateGraphics();
698 m_pParent
->m_aChildren
.remove( this );
701 getDisplay()->deregisterFrame( this );
705 cairo_region_destroy( m_pRegion
);
708 m_pIMHandler
.reset();
710 //tdf#108705 remove grabs on event widget before
711 //destroying event widget
716 SolarMutexGuard aGuard
;
719 g_bus_unwatch_name(m_nWatcherId
);
721 if (m_nPortalSettingChangedSignalId
)
722 g_signal_handler_disconnect(m_pSettingsPortal
, m_nPortalSettingChangedSignalId
);
724 if (m_pSettingsPortal
)
725 g_object_unref(m_pSettingsPortal
);
727 if (m_nSessionClientSignalId
)
728 g_signal_handler_disconnect(m_pSessionClient
, m_nSessionClientSignalId
);
730 if (m_pSessionClient
)
731 g_object_unref(m_pSessionClient
);
733 if (m_pSessionManager
)
734 g_object_unref(m_pSessionManager
);
737 GtkWidget
*pEventWidget
= getMouseEventWidget();
738 for (auto handler_id
: m_aMouseSignalIds
)
739 g_signal_handler_disconnect(G_OBJECT(pEventWidget
), handler_id
);
741 #if !GTK_CHECK_VERSION(4, 0, 0)
742 if( m_pFixedContainer
)
743 gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer
) );
745 gtk_widget_destroy( GTK_WIDGET(m_pEventBox
) );
746 if( m_pTopLevelGrid
)
747 gtk_widget_destroy( GTK_WIDGET(m_pTopLevelGrid
) );
749 g_signal_handler_disconnect(G_OBJECT(gtk_widget_get_display(pEventWidget
)), m_nSettingChangedSignalId
);
752 SolarMutexGuard aGuard
;
756 g_object_set_data( G_OBJECT( m_pWindow
), "SalFrame", nullptr );
760 if ( m_nHudAwarenessId
)
761 hud_awareness_unregister( pSessionBus
, m_nHudAwarenessId
);
762 if ( m_nMenuExportId
)
763 g_dbus_connection_unexport_menu_model( pSessionBus
, m_nMenuExportId
);
764 if ( m_nActionGroupExportId
)
765 g_dbus_connection_unexport_action_group( pSessionBus
, m_nActionGroupExportId
);
767 m_xFrameWeld
.reset();
768 #if !GTK_CHECK_VERSION(4,0,0)
769 gtk_widget_destroy( m_pWindow
);
771 if (GTK_IS_WINDOW(m_pWindow
))
772 gtk_window_destroy(GTK_WINDOW(m_pWindow
));
774 g_clear_pointer(&m_pWindow
, gtk_widget_unparent
);
779 #if !GTK_CHECK_VERSION(4,0,0)
780 if( m_pForeignParent
)
781 g_object_unref( G_OBJECT( m_pForeignParent
) );
782 if( m_pForeignTopLevel
)
783 g_object_unref( G_OBJECT( m_pForeignTopLevel
) );
789 cairo_surface_destroy(m_pSurface
);
792 void GtkSalFrame::moveWindow( tools::Long nX
, tools::Long nY
)
794 if( isChild( false ) )
796 GtkWidget
* pParent
= m_pParent
? gtk_widget_get_parent(m_pWindow
) : nullptr;
797 // tdf#130414 it's possible that we were reparented and are no longer inside
798 // our original GtkFixed parent
799 if (pParent
&& GTK_IS_FIXED(pParent
))
801 gtk_fixed_move( GTK_FIXED(pParent
),
803 nX
- m_pParent
->maGeometry
.x(), nY
- m_pParent
->maGeometry
.y() );
807 #if GTK_CHECK_VERSION(4,0,0)
808 if (GTK_IS_POPOVER(m_pWindow
))
815 gtk_popover_set_pointing_to(GTK_POPOVER(m_pWindow
), &aRect
);
819 gtk_window_move( GTK_WINDOW(m_pWindow
), nX
, nY
);
823 void GtkSalFrame::widget_set_size_request(tools::Long nWidth
, tools::Long nHeight
)
825 gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer
), nWidth
, nHeight
);
826 #if GTK_CHECK_VERSION(4,0,0)
827 gtk_widget_set_size_request(GTK_WIDGET(m_pDrawingArea
), nWidth
, nHeight
);
831 void GtkSalFrame::window_resize(tools::Long nWidth
, tools::Long nHeight
)
833 m_nWidthRequest
= nWidth
;
834 m_nHeightRequest
= nHeight
;
835 if (!GTK_IS_WINDOW(m_pWindow
))
837 #if GTK_CHECK_VERSION(4,0,0)
838 gtk_widget_set_size_request(GTK_WIDGET(m_pDrawingArea
), nWidth
, nHeight
);
842 gtk_window_set_default_size(GTK_WINDOW(m_pWindow
), nWidth
, nHeight
);
843 #if !GTK_CHECK_VERSION(4,0,0)
844 gtk_window_resize(GTK_WINDOW(m_pWindow
), nWidth
, nHeight
);
848 void GtkSalFrame::resizeWindow( tools::Long nWidth
, tools::Long nHeight
)
850 if( isChild( false ) )
852 widget_set_size_request(nWidth
, nHeight
);
854 else if( ! isChild( true, false ) )
855 window_resize(nWidth
, nHeight
);
858 #if !GTK_CHECK_VERSION(4,0,0)
859 // tdf#124694 GtkFixed takes the max size of all its children as its
860 // preferred size, causing it to not clip its child, but grow instead.
863 ooo_fixed_get_preferred_height(GtkWidget
*, gint
*minimum
, gint
*natural
)
870 ooo_fixed_get_preferred_width(GtkWidget
*, gint
*minimum
, gint
*natural
)
877 ooo_fixed_class_init(GtkFixedClass
*klass
)
879 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
880 widget_class
->get_accessible
= ooo_fixed_get_accessible
;
881 widget_class
->get_preferred_height
= ooo_fixed_get_preferred_height
;
882 widget_class
->get_preferred_width
= ooo_fixed_get_preferred_width
;
886 * Always use a sub-class of GtkFixed we can tag for a11y. This allows us to
887 * utilize GAIL for the toplevel window and toolkit implementation incl.
888 * key event listener support ..
894 static GType type
= 0;
897 static const GTypeInfo tinfo
=
899 sizeof (GtkFixedClass
),
900 nullptr, /* base init */
901 nullptr, /* base finalize */
902 reinterpret_cast<GClassInitFunc
>(ooo_fixed_class_init
), /* class init */
903 nullptr, /* class finalize */
904 nullptr, /* class data */
905 sizeof (GtkFixed
), /* instance size */
906 0, /* nb preallocs */
907 nullptr, /* instance init */
908 nullptr /* value table */
911 type
= g_type_register_static( GTK_TYPE_FIXED
, "OOoFixed",
912 &tinfo
, GTypeFlags(0));
920 void GtkSalFrame::updateScreenNumber()
922 #if !GTK_CHECK_VERSION(4,0,0)
924 GdkScreen
*pScreen
= gtk_widget_get_screen( m_pWindow
);
926 nScreen
= getDisplay()->getSystem()->getScreenMonitorIdx( pScreen
, maGeometry
.x(), maGeometry
.y() );
927 maGeometry
.setScreen(nScreen
);
931 GtkWidget
*GtkSalFrame::getMouseEventWidget() const
933 #if !GTK_CHECK_VERSION(4,0,0)
934 return GTK_WIDGET(m_pEventBox
);
936 return GTK_WIDGET(m_pFixedContainer
);
940 static void damaged(void *handle
,
941 sal_Int32 nExtentsX
, sal_Int32 nExtentsY
,
942 sal_Int32 nExtentsWidth
, sal_Int32 nExtentsHeight
)
944 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(handle
);
945 pThis
->damaged(nExtentsX
, nExtentsY
, nExtentsWidth
, nExtentsHeight
);
948 void GtkSalFrame::InitCommon()
950 m_pSurface
= nullptr;
952 m_bSalObjectSetPosSize
= false;
953 m_nPortalSettingChangedSignalId
= 0;
954 m_nSessionClientSignalId
= 0;
955 m_pSettingsPortal
= nullptr;
956 m_pSessionManager
= nullptr;
957 m_pSessionClient
= nullptr;
959 m_aDamageHandler
.handle
= this;
960 m_aDamageHandler
.damaged
= ::damaged
;
962 #if !GTK_CHECK_VERSION(4,0,0)
963 m_aSmoothScrollIdle
.SetInvokeHandler(LINK(this, GtkSalFrame
, AsyncScroll
));
966 m_pTopLevelGrid
= GTK_GRID(gtk_grid_new());
967 container_add(m_pWindow
, GTK_WIDGET(m_pTopLevelGrid
));
969 #if !GTK_CHECK_VERSION(4,0,0)
970 m_pEventBox
= GTK_EVENT_BOX(gtk_event_box_new());
971 gtk_widget_add_events( GTK_WIDGET(m_pEventBox
),
972 GDK_ALL_EVENTS_MASK
);
973 gtk_widget_set_vexpand(GTK_WIDGET(m_pEventBox
), true);
974 gtk_widget_set_hexpand(GTK_WIDGET(m_pEventBox
), true);
975 gtk_grid_attach(m_pTopLevelGrid
, GTK_WIDGET(m_pEventBox
), 0, 0, 1, 1);
978 // add the fixed container child,
979 // fixed is needed since we have to position plugin windows
980 #if !GTK_CHECK_VERSION(4,0,0)
981 m_pFixedContainer
= GTK_FIXED(g_object_new( ooo_fixed_get_type(), nullptr ));
982 m_pDrawingArea
= m_pFixedContainer
;
984 m_pOverlay
= GTK_OVERLAY(gtk_overlay_new());
985 #if GTK_CHECK_VERSION(4,9,0)
986 m_pFixedContainer
= GTK_FIXED(g_object_new( ooo_fixed_get_type(), nullptr ));
988 m_pFixedContainer
= GTK_FIXED(gtk_fixed_new());
990 m_pDrawingArea
= GTK_DRAWING_AREA(gtk_drawing_area_new());
992 gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer
), true);
993 gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer
), 1, 1);
994 #if !GTK_CHECK_VERSION(4,0,0)
995 gtk_container_add( GTK_CONTAINER(m_pEventBox
), GTK_WIDGET(m_pFixedContainer
) );
997 gtk_widget_set_vexpand(GTK_WIDGET(m_pOverlay
), true);
998 gtk_widget_set_hexpand(GTK_WIDGET(m_pOverlay
), true);
999 gtk_grid_attach(m_pTopLevelGrid
, GTK_WIDGET(m_pOverlay
), 0, 0, 1, 1);
1000 gtk_overlay_set_child(m_pOverlay
, GTK_WIDGET(m_pDrawingArea
));
1001 gtk_overlay_add_overlay(m_pOverlay
, GTK_WIDGET(m_pFixedContainer
));
1004 GtkWidget
*pEventWidget
= getMouseEventWidget();
1005 #if !GTK_CHECK_VERSION(4,0,0)
1006 gtk_widget_set_app_paintable(GTK_WIDGET(m_pFixedContainer
), true);
1007 gtk_widget_set_redraw_on_allocate(GTK_WIDGET(m_pFixedContainer
), false);
1010 #if GTK_CHECK_VERSION(4,0,0)
1011 m_nSettingChangedSignalId
= g_signal_connect(G_OBJECT(gtk_widget_get_display(pEventWidget
)), "setting-changed", G_CALLBACK(signalStyleUpdated
), this);
1013 // use pEventWidget instead of m_pWindow to avoid infinite event loop under Linux Mint Mate 18.3
1014 g_signal_connect(G_OBJECT(pEventWidget
), "style-updated", G_CALLBACK(signalStyleUpdated
), this);
1016 gtk_widget_set_has_tooltip(pEventWidget
, true);
1018 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "query-tooltip", G_CALLBACK(signalTooltipQuery
), this ));
1019 #if !GTK_CHECK_VERSION(4,0,0)
1020 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "button-press-event", G_CALLBACK(signalButton
), this ));
1021 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "button-release-event", G_CALLBACK(signalButton
), this ));
1023 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "motion-notify-event", G_CALLBACK(signalMotion
), this ));
1024 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "leave-notify-event", G_CALLBACK(signalCrossing
), this ));
1025 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "enter-notify-event", G_CALLBACK(signalCrossing
), this ));
1027 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "scroll-event", G_CALLBACK(signalScroll
), this ));
1029 GtkGesture
*pClick
= gtk_gesture_click_new();
1030 gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(pClick
), 0);
1031 // use GTK_PHASE_TARGET instead of default GTK_PHASE_BUBBLE because I don't
1032 // want click events in gtk widgets inside the overlay, e.g. the font size
1033 // combobox GtkEntry in the toolbar, to be propagated down to this widget
1034 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(pClick
), GTK_PHASE_TARGET
);
1035 gtk_widget_add_controller(pEventWidget
, GTK_EVENT_CONTROLLER(pClick
));
1036 g_signal_connect(pClick
, "pressed", G_CALLBACK(gesturePressed
), this);
1037 g_signal_connect(pClick
, "released", G_CALLBACK(gestureReleased
), this);
1039 GtkEventController
* pMotionController
= gtk_event_controller_motion_new();
1040 g_signal_connect(pMotionController
, "motion", G_CALLBACK(signalMotion
), this);
1041 g_signal_connect(pMotionController
, "enter", G_CALLBACK(signalEnter
), this);
1042 g_signal_connect(pMotionController
, "leave", G_CALLBACK(signalLeave
), this);
1043 gtk_widget_add_controller(pEventWidget
, pMotionController
);
1045 GtkEventController
* pScrollController
= gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES
);
1046 g_signal_connect(pScrollController
, "scroll", G_CALLBACK(signalScroll
), this);
1047 gtk_widget_add_controller(pEventWidget
, pScrollController
);
1050 #if GTK_CHECK_VERSION(4,0,0)
1051 GtkGesture
* pZoomGesture
= gtk_gesture_zoom_new();
1052 gtk_widget_add_controller(pEventWidget
, GTK_EVENT_CONTROLLER(pZoomGesture
));
1054 GtkGesture
* pZoomGesture
= gtk_gesture_zoom_new(GTK_WIDGET(pEventWidget
));
1055 g_object_weak_ref(G_OBJECT(pEventWidget
), reinterpret_cast<GWeakNotify
>(g_object_unref
), pZoomGesture
);
1057 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(pZoomGesture
),
1059 // Note that the default zoom gesture signal handler needs to run first to setup correct
1060 // scale delta. Otherwise the first "begin" event will always contain scale delta of infinity.
1061 g_signal_connect_after(pZoomGesture
, "begin", G_CALLBACK(signalZoomBegin
), this);
1062 g_signal_connect_after(pZoomGesture
, "update", G_CALLBACK(signalZoomUpdate
), this);
1063 g_signal_connect_after(pZoomGesture
, "end", G_CALLBACK(signalZoomEnd
), this);
1065 #if GTK_CHECK_VERSION(4,0,0)
1066 GtkGesture
* pRotateGesture
= gtk_gesture_rotate_new();
1067 gtk_widget_add_controller(pEventWidget
, GTK_EVENT_CONTROLLER(pRotateGesture
));
1069 GtkGesture
* pRotateGesture
= gtk_gesture_rotate_new(GTK_WIDGET(pEventWidget
));
1070 g_object_weak_ref(G_OBJECT(pEventWidget
), reinterpret_cast<GWeakNotify
>(g_object_unref
), pRotateGesture
);
1072 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(pRotateGesture
),
1074 g_signal_connect(pRotateGesture
, "begin", G_CALLBACK(signalRotateBegin
), this);
1075 g_signal_connect(pRotateGesture
, "update", G_CALLBACK(signalRotateUpdate
), this);
1076 g_signal_connect(pRotateGesture
, "end", G_CALLBACK(signalRotateEnd
), this);
1079 #if GTK_CHECK_VERSION(4,0,0)
1080 GtkDropTargetAsync
* pDropTarget
= gtk_drop_target_async_new(nullptr, GdkDragAction(GDK_ACTION_ALL
));
1081 g_signal_connect(G_OBJECT(pDropTarget
), "drag-enter", G_CALLBACK(signalDragMotion
), this);
1082 g_signal_connect(G_OBJECT(pDropTarget
), "drag-motion", G_CALLBACK(signalDragMotion
), this);
1083 g_signal_connect(G_OBJECT(pDropTarget
), "drag-leave", G_CALLBACK(signalDragLeave
), this);
1084 g_signal_connect(G_OBJECT(pDropTarget
), "drop", G_CALLBACK(signalDragDrop
), this);
1085 gtk_widget_add_controller(pEventWidget
, GTK_EVENT_CONTROLLER(pDropTarget
));
1087 gtk_drag_dest_set(GTK_WIDGET(pEventWidget
), GtkDestDefaults(0), nullptr, 0, GdkDragAction(0));
1088 gtk_drag_dest_set_track_motion(GTK_WIDGET(pEventWidget
), true);
1089 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-motion", G_CALLBACK(signalDragMotion
), this ));
1090 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-drop", G_CALLBACK(signalDragDrop
), this ));
1091 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-data-received", G_CALLBACK(signalDragDropReceived
), this ));
1092 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-leave", G_CALLBACK(signalDragLeave
), this ));
1095 #if !GTK_CHECK_VERSION(4,0,0)
1097 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-end", G_CALLBACK(signalDragEnd
), this ));
1098 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-failed", G_CALLBACK(signalDragFailed
), this ));
1099 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-data-delete", G_CALLBACK(signalDragDelete
), this ));
1100 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-data-get", G_CALLBACK(signalDragDataGet
), this ));
1103 #if !GTK_CHECK_VERSION(4,0,0)
1104 g_signal_connect( G_OBJECT(m_pFixedContainer
), "draw", G_CALLBACK(signalDraw
), this );
1105 g_signal_connect( G_OBJECT(m_pFixedContainer
), "size-allocate", G_CALLBACK(sizeAllocated
), this );
1107 gtk_drawing_area_set_draw_func(m_pDrawingArea
, signalDraw
, this, nullptr);
1108 g_signal_connect(G_OBJECT(m_pDrawingArea
), "resize", G_CALLBACK(sizeAllocated
), this);
1111 g_signal_connect(G_OBJECT(m_pFixedContainer
), "realize", G_CALLBACK(signalRealize
), this);
1113 #if !GTK_CHECK_VERSION(4,0,0)
1114 GtkGesture
*pSwipe
= gtk_gesture_swipe_new(pEventWidget
);
1115 g_object_weak_ref(G_OBJECT(pEventWidget
), reinterpret_cast<GWeakNotify
>(g_object_unref
), pSwipe
);
1117 GtkGesture
*pSwipe
= gtk_gesture_swipe_new();
1118 gtk_widget_add_controller(pEventWidget
, GTK_EVENT_CONTROLLER(pSwipe
));
1120 g_signal_connect(pSwipe
, "swipe", G_CALLBACK(gestureSwipe
), this);
1121 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pSwipe
), GTK_PHASE_TARGET
);
1123 #if !GTK_CHECK_VERSION(4,0,0)
1124 GtkGesture
*pLongPress
= gtk_gesture_long_press_new(pEventWidget
);
1125 g_object_weak_ref(G_OBJECT(pEventWidget
), reinterpret_cast<GWeakNotify
>(g_object_unref
), pLongPress
);
1127 GtkGesture
*pLongPress
= gtk_gesture_long_press_new();
1128 gtk_widget_add_controller(pEventWidget
, GTK_EVENT_CONTROLLER(pLongPress
));
1130 g_signal_connect(pLongPress
, "pressed", G_CALLBACK(gestureLongPress
), this);
1131 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pLongPress
), GTK_PHASE_TARGET
);
1133 #if !GTK_CHECK_VERSION(4,0,0)
1134 g_signal_connect_after( G_OBJECT(m_pWindow
), "focus-in-event", G_CALLBACK(signalFocus
), this );
1135 g_signal_connect_after( G_OBJECT(m_pWindow
), "focus-out-event", G_CALLBACK(signalFocus
), this );
1136 if (GTK_IS_WINDOW(m_pWindow
)) // i.e. not if it's a GtkEventBox which doesn't have the signal
1137 m_nSetFocusSignalId
= g_signal_connect( G_OBJECT(m_pWindow
), "set-focus", G_CALLBACK(signalSetFocus
), this );
1139 GtkEventController
* pFocusController
= gtk_event_controller_focus_new();
1140 g_signal_connect(pFocusController
, "enter", G_CALLBACK(signalFocusEnter
), this);
1141 g_signal_connect(pFocusController
, "leave", G_CALLBACK(signalFocusLeave
), this);
1142 gtk_widget_set_focusable(pEventWidget
, true);
1143 gtk_widget_add_controller(pEventWidget
, pFocusController
);
1144 if (GTK_IS_WINDOW(m_pWindow
)) // i.e. not if it's a GtkEventBox which doesn't have the property (presumably?)
1145 m_nSetFocusSignalId
= g_signal_connect( G_OBJECT(m_pWindow
), "notify::focus-widget", G_CALLBACK(signalSetFocus
), this );
1147 #if !GTK_CHECK_VERSION(4,0,0)
1148 g_signal_connect( G_OBJECT(m_pWindow
), "map-event", G_CALLBACK(signalMap
), this );
1149 g_signal_connect( G_OBJECT(m_pWindow
), "unmap-event", G_CALLBACK(signalUnmap
), this );
1150 g_signal_connect( G_OBJECT(m_pWindow
), "delete-event", G_CALLBACK(signalDelete
), this );
1152 g_signal_connect( G_OBJECT(m_pWindow
), "map", G_CALLBACK(signalMap
), this );
1153 g_signal_connect( G_OBJECT(m_pWindow
), "unmap", G_CALLBACK(signalUnmap
), this );
1154 if (GTK_IS_WINDOW(m_pWindow
))
1155 g_signal_connect( G_OBJECT(m_pWindow
), "close-request", G_CALLBACK(signalDelete
), this );
1157 #if !GTK_CHECK_VERSION(4,0,0)
1158 g_signal_connect( G_OBJECT(m_pWindow
), "configure-event", G_CALLBACK(signalConfigure
), this );
1161 #if !GTK_CHECK_VERSION(4,0,0)
1162 g_signal_connect( G_OBJECT(m_pWindow
), "key-press-event", G_CALLBACK(signalKey
), this );
1163 g_signal_connect( G_OBJECT(m_pWindow
), "key-release-event", G_CALLBACK(signalKey
), this );
1165 m_pKeyController
= GTK_EVENT_CONTROLLER_KEY(gtk_event_controller_key_new());
1166 g_signal_connect(m_pKeyController
, "key-pressed", G_CALLBACK(signalKeyPressed
), this);
1167 g_signal_connect(m_pKeyController
, "key-released", G_CALLBACK(signalKeyReleased
), this);
1168 gtk_widget_add_controller(pEventWidget
, GTK_EVENT_CONTROLLER(m_pKeyController
));
1171 g_signal_connect( G_OBJECT(m_pWindow
), "destroy", G_CALLBACK(signalDestroy
), this );
1174 m_nKeyModifiers
= ModKeyFlags::NONE
;
1175 m_bFullscreen
= false;
1176 #if GTK_CHECK_VERSION(4,0,0)
1177 m_nState
= static_cast<GdkToplevelState
>(0);
1179 m_nState
= GDK_WINDOW_STATE_WITHDRAWN
;
1181 m_pIMHandler
= nullptr;
1182 m_pRegion
= nullptr;
1183 m_pDropTarget
= nullptr;
1184 m_pDragSource
= nullptr;
1185 m_bGeometryIsProvisional
= false;
1186 m_bIconSetWhileUnmapped
= false;
1187 m_bTooltipBlocked
= false;
1188 m_ePointerStyle
= static_cast<PointerStyle
>(0xffff);
1189 m_pSalMenu
= nullptr;
1191 m_nMenuExportId
= 0;
1192 m_nActionGroupExportId
= 0;
1193 m_nHudAwarenessId
= 0;
1195 #if !GTK_CHECK_VERSION(4,0,0)
1196 gtk_widget_add_events( m_pWindow
,
1197 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
1198 GDK_POINTER_MOTION_MASK
| GDK_POINTER_MOTION_HINT_MASK
|
1199 GDK_SCROLL_MASK
| GDK_TOUCHPAD_GESTURE_MASK
1204 #if !GTK_CHECK_VERSION(4,0,0)
1205 gtk_widget_show_all(GTK_WIDGET(m_pTopLevelGrid
));
1207 gtk_widget_show(GTK_WIDGET(m_pTopLevelGrid
));
1210 // realize the window, we need an XWindow id
1211 gtk_widget_realize( m_pWindow
);
1213 if (GTK_IS_WINDOW(m_pWindow
))
1215 #if !GTK_CHECK_VERSION(4,0,0)
1216 g_signal_connect(G_OBJECT(m_pWindow
), "window-state-event", G_CALLBACK(signalWindowState
), this);
1218 GdkSurface
* gdkWindow
= widget_get_surface(m_pWindow
);
1219 g_signal_connect(G_OBJECT(gdkWindow
), "notify::state", G_CALLBACK(signalWindowState
), this);
1224 m_aSystemData
.SetWindowHandle(GetNativeWindowHandle(m_pWindow
));
1225 m_aSystemData
.aShellWindow
= reinterpret_cast<sal_IntPtr
>(this);
1226 m_aSystemData
.pSalFrame
= this;
1227 m_aSystemData
.pWidget
= m_pWindow
;
1228 m_aSystemData
.nScreen
= m_nXScreen
.getXScreen();
1229 m_aSystemData
.toolkit
= SystemEnvData::Toolkit::Gtk
;
1231 #if defined(GDK_WINDOWING_X11)
1232 GdkDisplay
*pDisplay
= getGdkDisplay();
1233 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay
))
1235 m_aSystemData
.pDisplay
= gdk_x11_display_get_xdisplay(pDisplay
);
1236 m_aSystemData
.platform
= SystemEnvData::Platform::Xcb
;
1237 #if !GTK_CHECK_VERSION(4,0,0)
1238 GdkScreen
* pScreen
= gtk_widget_get_screen(m_pWindow
);
1239 GdkVisual
* pVisual
= gdk_screen_get_system_visual(pScreen
);
1240 m_aSystemData
.pVisual
= gdk_x11_visual_get_xvisual(pVisual
);
1244 #if defined(GDK_WINDOWING_WAYLAND)
1245 if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay
))
1247 m_aSystemData
.pDisplay
= gdk_wayland_display_get_wl_display(pDisplay
);
1248 m_aSystemData
.platform
= SystemEnvData::Platform::Wayland
;
1252 m_bGraphics
= false;
1253 m_pGraphics
= nullptr;
1255 m_nFloatFlags
= FloatWinPopupFlags::NONE
;
1256 m_bFloatPositioned
= false;
1258 m_nWidthRequest
= 0;
1259 m_nHeightRequest
= 0;
1261 // fake an initial geometry, gets updated via configure event or SetPosSize
1262 if (m_bDefaultPos
|| m_bDefaultSize
)
1264 Size aDefSize
= calcDefaultSize();
1265 maGeometry
.setPosSize({ -1, -1 }, aDefSize
);
1266 maGeometry
.setDecorations(0, 0, 0, 0);
1268 updateScreenNumber();
1270 SetIcon(SV_ICON_ID_OFFICE
);
1273 GtkSalFrame
*GtkSalFrame::getFromWindow( GtkWidget
*pWindow
)
1275 return static_cast<GtkSalFrame
*>(g_object_get_data( G_OBJECT( pWindow
), "SalFrame" ));
1278 void GtkSalFrame::DisallowCycleFocusOut()
1280 if (!m_nSetFocusSignalId
)
1282 // don't enable/disable can-focus as control enters and leaves
1283 // embedded native gtk widgets
1284 g_signal_handler_disconnect(G_OBJECT(m_pWindow
), m_nSetFocusSignalId
);
1285 m_nSetFocusSignalId
= 0;
1287 #if !GTK_CHECK_VERSION(4, 0, 0)
1288 // gtk3: set container without can-focus and focus will tab between
1289 // the native embedded widgets using the default gtk handling for
1291 // gtk4: no need because the native widgets are the only
1292 // thing in the overlay and the drawing widget is underneath so
1293 // the natural focus cycle is sufficient
1294 gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer
), false);
1298 bool GtkSalFrame::IsCycleFocusOutDisallowed() const
1300 return m_nSetFocusSignalId
== 0;
1303 void GtkSalFrame::AllowCycleFocusOut()
1305 if (m_nSetFocusSignalId
)
1307 #if !GTK_CHECK_VERSION(4,0,0)
1308 // enable/disable can-focus as control enters and leaves
1309 // embedded native gtk widgets
1310 m_nSetFocusSignalId
= g_signal_connect(G_OBJECT(m_pWindow
), "set-focus", G_CALLBACK(signalSetFocus
), this);
1312 m_nSetFocusSignalId
= g_signal_connect(G_OBJECT(m_pWindow
), "notify::focus-widget", G_CALLBACK(signalSetFocus
), this);
1315 #if !GTK_CHECK_VERSION(4, 0, 0)
1316 // set container without can-focus and focus will tab between
1317 // the native embedded widgets using the default gtk handling for
1319 // gtk4: no need because the native widgets are the only
1320 // thing in the overlay and the drawing widget is underneath so
1321 // the natural focus cycle is sufficient
1322 gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer
), true);
1335 void ReadColorScheme(GDBusProxy
* proxy
, GVariant
** out
)
1337 g_autoptr (GVariant
) ret
=
1338 g_dbus_proxy_call_sync(proxy
, "Read",
1339 g_variant_new ("(ss)", "org.freedesktop.appearance", "color-scheme"),
1340 G_DBUS_CALL_FLAGS_NONE
, G_MAXINT
, nullptr, nullptr);
1344 g_autoptr (GVariant
) child
= nullptr;
1345 g_variant_get(ret
, "(v)", &child
);
1346 g_variant_get(child
, "v", out
);
1352 void GtkSalFrame::SetColorScheme(GVariant
* variant
)
1357 guint32 color_scheme
;
1359 switch (officecfg::Office::Common::Misc::Appearance::get())
1366 color_scheme
= g_variant_get_uint32(variant
);
1367 if (color_scheme
> PREFER_LIGHT
)
1368 color_scheme
= DEFAULT
;
1371 color_scheme
= DEFAULT
;
1375 color_scheme
= PREFER_LIGHT
;
1378 color_scheme
= PREFER_DARK
;
1382 bool bDarkIconTheme(color_scheme
== PREFER_DARK
);
1383 GtkSettings
* pSettings
= gtk_widget_get_settings(m_pWindow
);
1384 g_object_set(pSettings
, "gtk-application-prefer-dark-theme", bDarkIconTheme
, nullptr);
1387 bool GtkSalFrame::GetUseDarkMode() const
1391 GtkSettings
* pSettings
= gtk_widget_get_settings(m_pWindow
);
1392 gboolean bDarkIconTheme
= false;
1393 g_object_get(pSettings
, "gtk-application-prefer-dark-theme", &bDarkIconTheme
, nullptr);
1394 return bDarkIconTheme
;
1397 static void settings_portal_changed_cb(GDBusProxy
*, const char*, const char* signal_name
,
1398 GVariant
* parameters
, gpointer frame
)
1400 if (g_strcmp0(signal_name
, "SettingChanged"))
1403 g_autoptr (GVariant
) value
= nullptr;
1404 const char *name_space
;
1406 g_variant_get(parameters
, "(&s&sv)", &name_space
, &name
, &value
);
1408 if (g_strcmp0(name_space
, "org.freedesktop.appearance") ||
1409 g_strcmp0(name
, "color-scheme"))
1412 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
1413 pThis
->SetColorScheme(value
);
1416 void GtkSalFrame::ListenPortalSettings()
1423 m_pSettingsPortal
= g_dbus_proxy_new_sync(pSessionBus
,
1424 G_DBUS_PROXY_FLAGS_NONE
,
1426 "org.freedesktop.portal.Desktop",
1427 "/org/freedesktop/portal/desktop",
1428 "org.freedesktop.portal.Settings",
1434 if (!m_pSettingsPortal
)
1437 m_nPortalSettingChangedSignalId
= g_signal_connect(m_pSettingsPortal
, "g-signal", G_CALLBACK(settings_portal_changed_cb
), this);
1440 static void session_client_response(GDBusProxy
* client_proxy
)
1442 g_dbus_proxy_call(client_proxy
,
1443 "EndSessionResponse",
1444 g_variant_new ("(bs)", true, ""),
1445 G_DBUS_CALL_FLAGS_NONE
,
1447 nullptr, nullptr, nullptr);
1450 // unset documents "modify" flag so they won't veto closing
1451 static void clear_modify_and_terminate()
1453 css::uno::Reference
<css::uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
1454 uno::Reference
<frame::XDesktop
> xDesktop(frame::Desktop::create(xContext
));
1455 uno::Reference
<css::container::XEnumeration
> xComponents
= xDesktop
->getComponents()->createEnumeration();
1456 while (xComponents
->hasMoreElements())
1458 css::uno::Reference
<css::util::XModifiable
> xModifiable(xComponents
->nextElement(), css::uno::UNO_QUERY
);
1460 xModifiable
->setModified(false);
1462 xDesktop
->terminate();
1465 static void session_client_signal(GDBusProxy
* client_proxy
, const char*, const char* signal_name
,
1466 GVariant
* /*parameters*/, gpointer frame
)
1468 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
1470 if (g_str_equal (signal_name
, "QueryEndSession"))
1472 css::uno::Reference
<css::uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
1473 uno::Reference
<frame::XDesktop2
> xDesktop(frame::Desktop::create(xContext
));
1475 bool bModified
= false;
1477 // find the XModifiable for this GtkSalFrame
1478 if (UnoWrapperBase
* pWrapper
= UnoWrapperBase::GetUnoWrapper(false))
1480 VclPtr
<vcl::Window
> xThisWindow
= pThis
->GetWindow();
1481 css::uno::Reference
<css::container::XIndexAccess
> xList
= xDesktop
->getFrames();
1482 sal_Int32 nFrameCount
= xList
->getCount();
1483 for (sal_Int32 i
= 0; i
< nFrameCount
; ++i
)
1485 css::uno::Reference
<css::frame::XFrame
> xFrame
;
1486 xList
->getByIndex(i
) >>= xFrame
;
1489 VclPtr
<vcl::Window
> xWin
= pWrapper
->GetWindow(xFrame
->getContainerWindow());
1492 if (xWin
->GetFrameWindow() != xThisWindow
)
1494 css::uno::Reference
<css::frame::XController
> xController
= xFrame
->getController();
1497 css::uno::Reference
<css::util::XModifiable
> xModifiable(xController
->getModel(), css::uno::UNO_QUERY
);
1500 bModified
= xModifiable
->isModified();
1505 pThis
->SessionManagerInhibit(bModified
, APPLICATION_INHIBIT_LOGOUT
, VclResId(STR_UNSAVED_DOCUMENTS
),
1506 gtk_window_get_icon_name(GTK_WINDOW(pThis
->getWindow())));
1508 session_client_response(client_proxy
);
1510 else if (g_str_equal (signal_name
, "CancelEndSession"))
1512 // restore back to uninhibited (to set again if queried), so frames
1513 // that go away before the next logout don't affect that logout
1514 pThis
->SessionManagerInhibit(false, APPLICATION_INHIBIT_LOGOUT
, VclResId(STR_UNSAVED_DOCUMENTS
),
1515 gtk_window_get_icon_name(GTK_WINDOW(pThis
->getWindow())));
1517 else if (g_str_equal (signal_name
, "EndSession"))
1519 session_client_response(client_proxy
);
1520 clear_modify_and_terminate();
1522 else if (g_str_equal (signal_name
, "Stop"))
1524 clear_modify_and_terminate();
1528 void GtkSalFrame::ListenSessionManager()
1535 m_pSessionManager
= g_dbus_proxy_new_sync(pSessionBus
,
1536 G_DBUS_PROXY_FLAGS_NONE
,
1538 "org.gnome.SessionManager",
1539 "/org/gnome/SessionManager",
1540 "org.gnome.SessionManager",
1544 if (!m_pSessionManager
)
1547 GVariant
* res
= g_dbus_proxy_call_sync(m_pSessionManager
,
1549 g_variant_new ("(ss)", "org.libreoffice", ""),
1550 G_DBUS_CALL_FLAGS_NONE
,
1559 g_variant_get(res
, "(o)", &client_path
);
1560 g_variant_unref(res
);
1562 m_pSessionClient
= g_dbus_proxy_new_sync(pSessionBus
,
1563 G_DBUS_PROXY_FLAGS_NONE
,
1565 "org.gnome.SessionManager",
1567 "org.gnome.SessionManager.ClientPrivate",
1571 g_free(client_path
);
1573 if (!m_pSessionClient
)
1576 m_nSessionClientSignalId
= g_signal_connect(m_pSessionClient
, "g-signal", G_CALLBACK(session_client_signal
), this);
1579 void GtkSalFrame::UpdateDarkMode()
1581 g_autoptr (GVariant
) value
= nullptr;
1582 if (m_pSettingsPortal
)
1583 ReadColorScheme(m_pSettingsPortal
, &value
);
1584 SetColorScheme(value
);
1587 #if GTK_CHECK_VERSION(4,0,0)
1588 static void PopoverClosed(GtkPopover
*, GtkSalFrame
* pThis
)
1590 SolarMutexGuard aGuard
;
1591 pThis
->closePopup();
1595 void GtkSalFrame::Init( SalFrame
* pParent
, SalFrameStyleFlags nStyle
)
1597 if( nStyle
& SalFrameStyleFlags::DEFAULT
) // ensure default style
1599 nStyle
|= SalFrameStyleFlags::MOVEABLE
| SalFrameStyleFlags::SIZEABLE
| SalFrameStyleFlags::CLOSEABLE
;
1600 nStyle
&= ~SalFrameStyleFlags::FLOAT
;
1603 m_pParent
= static_cast<GtkSalFrame
*>(pParent
);
1604 #if !GTK_CHECK_VERSION(4,0,0)
1605 m_pForeignParent
= nullptr;
1606 m_aForeignParentWindow
= None
;
1607 m_pForeignTopLevel
= nullptr;
1608 m_aForeignTopLevelWindow
= None
;
1612 bool bPopup
= ((nStyle
& SalFrameStyleFlags::FLOAT
) &&
1613 !(nStyle
& SalFrameStyleFlags::OWNERDRAWDECORATION
));
1615 if( nStyle
& SalFrameStyleFlags::SYSTEMCHILD
)
1617 #if !GTK_CHECK_VERSION(4,0,0)
1618 m_pWindow
= gtk_event_box_new();
1620 m_pWindow
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0);
1624 // insert into container
1625 gtk_fixed_put( m_pParent
->getFixedContainer(),
1631 #if !GTK_CHECK_VERSION(4,0,0)
1632 m_pWindow
= gtk_window_new(bPopup
? GTK_WINDOW_POPUP
: GTK_WINDOW_TOPLEVEL
);
1635 m_pWindow
= gtk_window_new();
1638 m_pWindow
= gtk_popover_new();
1639 gtk_popover_set_has_arrow(GTK_POPOVER(m_pWindow
), false);
1640 g_signal_connect(m_pWindow
, "closed", G_CALLBACK(PopoverClosed
), this);
1644 #if !GTK_CHECK_VERSION(4,0,0)
1645 // hook up F1 to show help for embedded native gtk widgets
1646 GtkAccelGroup
*pGroup
= gtk_accel_group_new();
1647 GClosure
* closure
= g_cclosure_new(G_CALLBACK(GtkSalFrame::NativeWidgetHelpPressed
), GTK_WINDOW(m_pWindow
), nullptr);
1648 gtk_accel_group_connect(pGroup
, GDK_KEY_F1
, static_cast<GdkModifierType
>(0), GTK_ACCEL_LOCKED
, closure
);
1649 gtk_window_add_accel_group(GTK_WINDOW(m_pWindow
), pGroup
);
1653 g_object_set_data( G_OBJECT( m_pWindow
), "SalFrame", this );
1654 g_object_set_data( G_OBJECT( m_pWindow
), "libo-version", const_cast<char *>(LIBO_VERSION_DOTTED
));
1656 // force wm class hint
1660 m_sWMClass
= m_pParent
->m_sWMClass
;
1664 if (GTK_IS_WINDOW(m_pWindow
))
1668 GtkWidget
* pTopLevel
= widget_get_toplevel(m_pParent
->m_pWindow
);
1669 #if !GTK_CHECK_VERSION(4,0,0)
1671 gtk_window_set_screen(GTK_WINDOW(m_pWindow
), gtk_widget_get_screen(pTopLevel
));
1674 if (!(m_pParent
->m_nStyle
& SalFrameStyleFlags::PLUG
))
1675 gtk_window_set_transient_for(GTK_WINDOW(m_pWindow
), GTK_WINDOW(pTopLevel
));
1676 m_pParent
->m_aChildren
.push_back( this );
1677 gtk_window_group_add_window(gtk_window_get_group(GTK_WINDOW(pTopLevel
)), GTK_WINDOW(m_pWindow
));
1681 gtk_window_group_add_window(gtk_window_group_new(), GTK_WINDOW(m_pWindow
));
1682 g_object_unref(gtk_window_get_group(GTK_WINDOW(m_pWindow
)));
1685 else if (GTK_IS_POPOVER(m_pWindow
))
1688 gtk_widget_set_parent(m_pWindow
, m_pParent
->getMouseEventWidget());
1692 bool bDecoHandling
=
1694 ( ! (nStyle
& SalFrameStyleFlags::FLOAT
) ||
1695 (nStyle
& SalFrameStyleFlags::OWNERDRAWDECORATION
) );
1699 #if !GTK_CHECK_VERSION(4,0,0)
1700 GdkWindowTypeHint eType
= GDK_WINDOW_TYPE_HINT_NORMAL
;
1701 if( (nStyle
& SalFrameStyleFlags::DIALOG
) && m_pParent
!= nullptr )
1702 eType
= GDK_WINDOW_TYPE_HINT_DIALOG
;
1704 if( nStyle
& SalFrameStyleFlags::INTRO
)
1706 #if !GTK_CHECK_VERSION(4,0,0)
1707 gtk_window_set_role( GTK_WINDOW(m_pWindow
), "splashscreen" );
1708 eType
= GDK_WINDOW_TYPE_HINT_SPLASHSCREEN
;
1711 else if( nStyle
& SalFrameStyleFlags::TOOLWINDOW
)
1713 #if !GTK_CHECK_VERSION(4,0,0)
1714 eType
= GDK_WINDOW_TYPE_HINT_DIALOG
;
1715 gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow
), true );
1718 else if( nStyle
& SalFrameStyleFlags::OWNERDRAWDECORATION
)
1720 #if !GTK_CHECK_VERSION(4,0,0)
1721 eType
= GDK_WINDOW_TYPE_HINT_TOOLBAR
;
1722 gtk_window_set_focus_on_map(GTK_WINDOW(m_pWindow
), false);
1724 gtk_window_set_decorated(GTK_WINDOW(m_pWindow
), false);
1726 #if !GTK_CHECK_VERSION(4,0,0)
1727 gtk_window_set_type_hint( GTK_WINDOW(m_pWindow
), eType
);
1728 gtk_window_set_gravity( GTK_WINDOW(m_pWindow
), GDK_GRAVITY_STATIC
);
1730 gtk_window_set_resizable( GTK_WINDOW(m_pWindow
), bool(nStyle
& SalFrameStyleFlags::SIZEABLE
) );
1732 #if !GTK_CHECK_VERSION(4,0,0)
1733 #if defined(GDK_WINDOWING_WAYLAND)
1734 //rhbz#1392145 under wayland/csd if we've overridden the default widget direction in order to set LibreOffice's
1735 //UI to the configured ui language but the system ui locale is a different text direction, then the toplevel
1736 //built-in close button of the titlebar follows the overridden direction rather than continue in the same
1737 //direction as every other titlebar on the user's desktop. So if they don't match set an explicit
1738 //header bar with the desired 'outside' direction
1739 if ((eType
== GDK_WINDOW_TYPE_HINT_NORMAL
|| eType
== GDK_WINDOW_TYPE_HINT_DIALOG
) && DLSYM_GDK_IS_WAYLAND_DISPLAY(GtkSalFrame::getGdkDisplay()))
1741 const bool bDesktopIsRTL
= MsLangId::isRightToLeft(MsLangId::getConfiguredSystemUILanguage());
1742 const bool bAppIsRTL
= gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL
;
1743 if (bDesktopIsRTL
!= bAppIsRTL
)
1745 m_pHeaderBar
= GTK_HEADER_BAR(gtk_header_bar_new());
1746 gtk_widget_set_direction(GTK_WIDGET(m_pHeaderBar
), bDesktopIsRTL
? GTK_TEXT_DIR_RTL
: GTK_TEXT_DIR_LTR
);
1747 gtk_header_bar_set_show_close_button(m_pHeaderBar
, true);
1748 gtk_window_set_titlebar(GTK_WINDOW(m_pWindow
), GTK_WIDGET(m_pHeaderBar
));
1749 gtk_widget_show(GTK_WIDGET(m_pHeaderBar
));
1755 #if !GTK_CHECK_VERSION(4,0,0)
1756 else if( nStyle
& SalFrameStyleFlags::FLOAT
)
1757 gtk_window_set_type_hint( GTK_WINDOW(m_pWindow
), GDK_WINDOW_TYPE_HINT_POPUP_MENU
);
1764 // Enable GMenuModel native menu
1765 attach_menu_model(this);
1767 // Listen to portal settings for e.g. prefer dark theme
1768 ListenPortalSettings();
1770 // Listen to session manager for e.g. query-end
1771 ListenSessionManager();
1775 #if !GTK_CHECK_VERSION(4,0,0)
1776 GdkNativeWindow
GtkSalFrame::findTopLevelSystemWindow( GdkNativeWindow
)
1778 //FIXME: no findToplevelSystemWindow
1783 void GtkSalFrame::Init( SystemParentData
* pSysData
)
1785 m_pParent
= nullptr;
1786 #if !GTK_CHECK_VERSION(4,0,0)
1787 m_aForeignParentWindow
= pSysData
->aWindow
;
1788 m_pForeignParent
= nullptr;
1789 m_aForeignTopLevelWindow
= findTopLevelSystemWindow(pSysData
->aWindow
);
1790 m_pForeignTopLevel
= gdk_x11_window_foreign_new_for_display( getGdkDisplay(), m_aForeignTopLevelWindow
);
1791 gdk_window_set_events( m_pForeignTopLevel
, GDK_STRUCTURE_MASK
);
1793 if( pSysData
->nSize
> sizeof(pSysData
->nSize
)+sizeof(pSysData
->aWindow
) && pSysData
->bXEmbedSupport
)
1795 m_pWindow
= gtk_plug_new_for_display( getGdkDisplay(), pSysData
->aWindow
);
1796 gtk_widget_set_can_default(m_pWindow
, true);
1797 gtk_widget_set_can_focus(m_pWindow
, true);
1798 gtk_widget_set_sensitive(m_pWindow
, true);
1802 m_pWindow
= gtk_window_new( GTK_WINDOW_POPUP
);
1805 m_nStyle
= SalFrameStyleFlags::PLUG
;
1808 #if !GTK_CHECK_VERSION(4,0,0)
1809 m_pForeignParent
= gdk_x11_window_foreign_new_for_display( getGdkDisplay(), m_aForeignParentWindow
);
1810 gdk_window_set_events( m_pForeignParent
, GDK_STRUCTURE_MASK
);
1815 //FIXME: Handling embedded windows, is going to be fun ...
1818 void GtkSalFrame::SetExtendedFrameStyle(SalExtStyle
)
1822 SalGraphics
* GtkSalFrame::AcquireGraphics()
1829 m_pGraphics
.reset( new GtkSalGraphics( this, m_pWindow
) );
1833 TriggerPaintEvent();
1835 m_pGraphics
->setSurface(m_pSurface
, m_aFrameSize
);
1838 return m_pGraphics
.get();
1841 void GtkSalFrame::ReleaseGraphics( SalGraphics
* pGraphics
)
1844 assert( pGraphics
== m_pGraphics
.get() );
1845 m_bGraphics
= false;
1848 bool GtkSalFrame::PostEvent(std::unique_ptr
<ImplSVEvent
> pData
)
1850 getDisplay()->SendInternalEvent( this, pData
.release() );
1854 void GtkSalFrame::SetTitle( const OUString
& rTitle
)
1856 if (m_pWindow
&& GTK_IS_WINDOW(m_pWindow
) && !isChild())
1858 OString
sTitle(OUStringToOString(rTitle
, RTL_TEXTENCODING_UTF8
));
1859 gtk_window_set_title(GTK_WINDOW(m_pWindow
), sTitle
.getStr());
1860 #if !GTK_CHECK_VERSION(4,0,0)
1862 gtk_header_bar_set_title(m_pHeaderBar
, sTitle
.getStr());
1867 void GtkSalFrame::SetIcon(const char* appicon
)
1869 gtk_window_set_icon_name(GTK_WINDOW(m_pWindow
), appicon
);
1871 #if defined(GDK_WINDOWING_WAYLAND)
1872 if (!DLSYM_GDK_IS_WAYLAND_DISPLAY(getGdkDisplay()))
1875 #if GTK_CHECK_VERSION(4,0,0)
1876 GdkSurface
* gdkWindow
= gtk_native_get_surface(gtk_widget_get_native(m_pWindow
));
1877 gdk_wayland_toplevel_set_application_id((GDK_TOPLEVEL(gdkWindow
)), appicon
);
1879 static auto set_application_id
= reinterpret_cast<void (*) (GdkWindow
*, const char*)>(
1880 dlsym(nullptr, "gdk_wayland_window_set_application_id"));
1881 if (set_application_id
)
1883 GdkSurface
* gdkWindow
= widget_get_surface(m_pWindow
);
1884 set_application_id(gdkWindow
, appicon
);
1887 // gdk_wayland_window_set_application_id doesn't seem to work before
1888 // the window is mapped, so set this for real when/if we are mapped
1889 m_bIconSetWhileUnmapped
= !gtk_widget_get_mapped(m_pWindow
);
1893 void GtkSalFrame::SetIcon( sal_uInt16 nIcon
)
1895 if( (m_nStyle
& (SalFrameStyleFlags::PLUG
|SalFrameStyleFlags::SYSTEMCHILD
|SalFrameStyleFlags::FLOAT
|SalFrameStyleFlags::INTRO
|SalFrameStyleFlags::OWNERDRAWDECORATION
))
1901 if (nIcon
== SV_ICON_ID_TEXT
)
1902 appicon
= g_strdup ("libreoffice-writer");
1903 else if (nIcon
== SV_ICON_ID_SPREADSHEET
)
1904 appicon
= g_strdup ("libreoffice-calc");
1905 else if (nIcon
== SV_ICON_ID_DRAWING
)
1906 appicon
= g_strdup ("libreoffice-draw");
1907 else if (nIcon
== SV_ICON_ID_PRESENTATION
)
1908 appicon
= g_strdup ("libreoffice-impress");
1909 else if (nIcon
== SV_ICON_ID_DATABASE
)
1910 appicon
= g_strdup ("libreoffice-base");
1911 else if (nIcon
== SV_ICON_ID_FORMULA
)
1912 appicon
= g_strdup ("libreoffice-math");
1914 appicon
= g_strdup ("libreoffice-startcenter");
1921 void GtkSalFrame::SetMenu( SalMenu
* pSalMenu
)
1923 m_pSalMenu
= static_cast<GtkSalMenu
*>(pSalMenu
);
1926 SalMenu
* GtkSalFrame::GetMenu()
1931 void GtkSalFrame::Center()
1933 if (!GTK_IS_WINDOW(m_pWindow
))
1935 #if !GTK_CHECK_VERSION(4,0,0)
1937 gtk_window_set_position(GTK_WINDOW(m_pWindow
), GTK_WIN_POS_CENTER_ON_PARENT
);
1939 gtk_window_set_position(GTK_WINDOW(m_pWindow
), GTK_WIN_POS_CENTER
);
1943 Size
GtkSalFrame::calcDefaultSize()
1945 Size
aScreenSize(getDisplay()->GetScreenSize(GetDisplayScreen()));
1946 int scale
= gtk_widget_get_scale_factor(m_pWindow
);
1947 aScreenSize
.setWidth( aScreenSize
.Width() / scale
);
1948 aScreenSize
.setHeight( aScreenSize
.Height() / scale
);
1949 return bestmaxFrameSizeForScreenSize(aScreenSize
);
1952 void GtkSalFrame::SetDefaultSize()
1954 Size aDefSize
= calcDefaultSize();
1956 SetPosSize( 0, 0, aDefSize
.Width(), aDefSize
.Height(),
1957 SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
);
1959 if( (m_nStyle
& SalFrameStyleFlags::DEFAULT
) && m_pWindow
)
1960 gtk_window_maximize( GTK_WINDOW(m_pWindow
) );
1963 void GtkSalFrame::Show( bool bVisible
, bool /*bNoActivate*/ )
1970 getDisplay()->startupNotificationCompleted();
1974 if( m_bDefaultSize
)
1978 if (isFloatGrabWindow() && !getDisplay()->GetCaptureFrame())
1980 m_pParent
->grabPointer(true, true, true);
1981 m_pParent
->addGrabLevel();
1984 #if defined(GDK_WINDOWING_WAYLAND) && !GTK_CHECK_VERSION(4,0,0)
1986 rhbz#1334915, gnome#779143, tdf#100158
1987 https://gitlab.gnome.org/GNOME/gtk/-/issues/767
1989 before gdk_wayland_window_set_application_id was available gtk
1990 under wayland lacked a way to change the app_id of a window, so
1991 brute force everything as a startcenter when initially shown to at
1992 least get the default LibreOffice icon and not the broken app icon
1994 static bool bAppIdImmutable
= DLSYM_GDK_IS_WAYLAND_DISPLAY(getGdkDisplay()) &&
1995 !dlsym(nullptr, "gdk_wayland_window_set_application_id");
1996 if (bAppIdImmutable
)
1998 OString
sOrigName(g_get_prgname());
1999 g_set_prgname("libreoffice-startcenter");
2000 gtk_widget_show(m_pWindow
);
2001 g_set_prgname(sOrigName
.getStr());
2005 gtk_widget_show(m_pWindow
);
2008 gtk_widget_show(m_pWindow
);
2011 if( isFloatGrabWindow() )
2014 if (!getDisplay()->GetCaptureFrame())
2016 grabPointer(true, true, true);
2019 // #i44068# reset parent's IM context
2021 m_pParent
->EndExtTextInput(EndExtTextInputFlags::NONE
);
2026 if( isFloatGrabWindow() )
2029 if (!getDisplay()->GetCaptureFrame())
2032 grabPointer(false, true, false);
2033 m_pParent
->removeGrabLevel();
2034 bool bParentIsFloatGrabWindow
= m_pParent
->isFloatGrabWindow();
2035 m_pParent
->grabPointer(bParentIsFloatGrabWindow
, true, bParentIsFloatGrabWindow
);
2038 gtk_widget_hide( m_pWindow
);
2040 m_pIMHandler
->focusChanged( false );
2044 void GtkSalFrame::setMinMaxSize()
2046 /* #i34504# metacity (and possibly others) do not treat
2047 * _NET_WM_STATE_FULLSCREEN and max_width/height independently;
2048 * whether they should is undefined. So don't set the max size hint
2049 * for a full screen window.
2051 if( !m_pWindow
|| isChild() )
2054 #if !GTK_CHECK_VERSION(4, 0, 0)
2057 if( m_nStyle
& SalFrameStyleFlags::SIZEABLE
)
2059 if( m_aMinSize
.Width() && m_aMinSize
.Height() && ! m_bFullscreen
)
2061 aGeo
.min_width
= m_aMinSize
.Width();
2062 aGeo
.min_height
= m_aMinSize
.Height();
2063 aHints
|= GDK_HINT_MIN_SIZE
;
2065 if( m_aMaxSize
.Width() && m_aMaxSize
.Height() && ! m_bFullscreen
)
2067 aGeo
.max_width
= m_aMaxSize
.Width();
2068 aGeo
.max_height
= m_aMaxSize
.Height();
2069 aHints
|= GDK_HINT_MAX_SIZE
;
2074 if (!m_bFullscreen
&& m_nWidthRequest
&& m_nHeightRequest
)
2076 aGeo
.min_width
= m_nWidthRequest
;
2077 aGeo
.min_height
= m_nHeightRequest
;
2078 aHints
|= GDK_HINT_MIN_SIZE
;
2080 aGeo
.max_width
= m_nWidthRequest
;
2081 aGeo
.max_height
= m_nHeightRequest
;
2082 aHints
|= GDK_HINT_MAX_SIZE
;
2086 if( m_bFullscreen
&& m_aMaxSize
.Width() && m_aMaxSize
.Height() )
2088 aGeo
.max_width
= m_aMaxSize
.Width();
2089 aGeo
.max_height
= m_aMaxSize
.Height();
2090 aHints
|= GDK_HINT_MAX_SIZE
;
2094 gtk_window_set_geometry_hints( GTK_WINDOW(m_pWindow
),
2097 GdkWindowHints( aHints
) );
2102 void GtkSalFrame::SetMaxClientSize( tools::Long nWidth
, tools::Long nHeight
)
2106 m_aMaxSize
= Size( nWidth
, nHeight
);
2110 void GtkSalFrame::SetMinClientSize( tools::Long nWidth
, tools::Long nHeight
)
2114 m_aMinSize
= Size( nWidth
, nHeight
);
2117 widget_set_size_request(nWidth
, nHeight
);
2123 void GtkSalFrame::AllocateFrame()
2125 basegfx::B2IVector
aFrameSize( maGeometry
.width(), maGeometry
.height() );
2126 if (m_pSurface
&& m_aFrameSize
.getX() == aFrameSize
.getX() &&
2127 m_aFrameSize
.getY() == aFrameSize
.getY() )
2130 if( aFrameSize
.getX() == 0 )
2131 aFrameSize
.setX( 1 );
2132 if( aFrameSize
.getY() == 0 )
2133 aFrameSize
.setY( 1 );
2136 cairo_surface_destroy(m_pSurface
);
2138 m_pSurface
= surface_create_similar_surface(widget_get_surface(m_pWindow
),
2139 CAIRO_CONTENT_COLOR_ALPHA
,
2142 m_aFrameSize
= aFrameSize
;
2144 cairo_surface_set_user_data(m_pSurface
, SvpSalGraphics::getDamageKey(), &m_aDamageHandler
, nullptr);
2145 SAL_INFO("vcl.gtk3", "allocated Frame size of " << maGeometry
.width() << " x " << maGeometry
.height());
2148 m_pGraphics
->setSurface(m_pSurface
, m_aFrameSize
);
2151 void GtkSalFrame::SetPosSize( tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
, sal_uInt16 nFlags
)
2153 if( !m_pWindow
|| isChild( true, false ) )
2156 if( (nFlags
& ( SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
)) &&
2157 (nWidth
> 0 && nHeight
> 0 ) // sometimes stupid things happen
2160 m_bDefaultSize
= false;
2162 maGeometry
.setSize({ nWidth
, nHeight
});
2164 if (isChild(false) || GTK_IS_POPOVER(m_pWindow
))
2165 widget_set_size_request(nWidth
, nHeight
);
2166 else if( ! ( m_nState
& GDK_TOPLEVEL_STATE_MAXIMIZED
) )
2167 window_resize(nWidth
, nHeight
);
2171 else if( m_bDefaultSize
)
2174 m_bDefaultSize
= false;
2176 if( nFlags
& ( SAL_FRAME_POSSIZE_X
| SAL_FRAME_POSSIZE_Y
) )
2180 if( AllSettings::GetLayoutRTL() )
2181 nX
= m_pParent
->maGeometry
.width()-m_nWidthRequest
-1-nX
;
2182 nX
+= m_pParent
->maGeometry
.x();
2183 nY
+= m_pParent
->maGeometry
.y();
2186 if (nFlags
& SAL_FRAME_POSSIZE_X
)
2187 maGeometry
.setX(nX
);
2188 if (nFlags
& SAL_FRAME_POSSIZE_Y
)
2189 maGeometry
.setY(nY
);
2190 m_bGeometryIsProvisional
= true;
2192 m_bDefaultPos
= false;
2194 moveWindow(maGeometry
.x(), maGeometry
.y());
2196 updateScreenNumber();
2198 else if( m_bDefaultPos
)
2201 m_bDefaultPos
= false;
2204 void GtkSalFrame::GetClientSize( tools::Long
& rWidth
, tools::Long
& rHeight
)
2206 if( m_pWindow
&& !(m_nState
& GDK_TOPLEVEL_STATE_MINIMIZED
) )
2208 rWidth
= maGeometry
.width();
2209 rHeight
= maGeometry
.height();
2212 rWidth
= rHeight
= 0;
2215 void GtkSalFrame::GetWorkArea( tools::Rectangle
& rRect
)
2218 #if !GTK_CHECK_VERSION(4, 0, 0)
2219 GdkScreen
*pScreen
= gtk_widget_get_screen(m_pWindow
);
2220 tools::Rectangle aRetRect
;
2221 int max
= gdk_screen_get_n_monitors (pScreen
);
2222 for (int i
= 0; i
< max
; ++i
)
2224 gdk_screen_get_monitor_workarea(pScreen
, i
, &aRect
);
2225 tools::Rectangle
aMonitorRect(aRect
.x
, aRect
.y
, aRect
.x
+aRect
.width
, aRect
.y
+aRect
.height
);
2226 aRetRect
.Union(aMonitorRect
);
2230 GdkDisplay
* pDisplay
= gtk_widget_get_display(m_pWindow
);
2231 GdkSurface
* gdkWindow
= widget_get_surface(m_pWindow
);
2232 GdkMonitor
* pMonitor
= gdk_display_get_monitor_at_surface(pDisplay
, gdkWindow
);
2233 gdk_monitor_get_geometry(pMonitor
, &aRect
);
2234 rRect
= tools::Rectangle(aRect
.x
, aRect
.y
, aRect
.x
+aRect
.width
, aRect
.y
+aRect
.height
);
2238 SalFrame
* GtkSalFrame::GetParent() const
2243 void GtkSalFrame::SetWindowState(const vcl::WindowData
* pState
)
2245 if( ! m_pWindow
|| ! pState
|| isChild( true, false ) )
2248 const vcl::WindowDataMask nMaxGeometryMask
= vcl::WindowDataMask::PosSize
|
2249 vcl::WindowDataMask::MaximizedX
| vcl::WindowDataMask::MaximizedY
|
2250 vcl::WindowDataMask::MaximizedWidth
| vcl::WindowDataMask::MaximizedHeight
;
2252 if( (pState
->mask() & vcl::WindowDataMask::State
) &&
2253 ! ( m_nState
& GDK_TOPLEVEL_STATE_MAXIMIZED
) &&
2254 (pState
->state() & vcl::WindowState::Maximized
) &&
2255 (pState
->mask() & nMaxGeometryMask
) == nMaxGeometryMask
)
2257 resizeWindow(pState
->width(), pState
->height());
2258 moveWindow(pState
->x(), pState
->y());
2259 m_bDefaultPos
= m_bDefaultSize
= false;
2261 updateScreenNumber();
2263 m_nState
= GdkToplevelState(m_nState
| GDK_TOPLEVEL_STATE_MAXIMIZED
);
2264 m_aRestorePosSize
= pState
->posSize();
2266 else if (pState
->mask() & vcl::WindowDataMask::PosSize
)
2268 sal_uInt16 nPosSizeFlags
= 0;
2269 tools::Long nX
= pState
->x() - (m_pParent
? m_pParent
->maGeometry
.x() : 0);
2270 tools::Long nY
= pState
->y() - (m_pParent
? m_pParent
->maGeometry
.y() : 0);
2271 if (pState
->mask() & vcl::WindowDataMask::X
)
2272 nPosSizeFlags
|= SAL_FRAME_POSSIZE_X
;
2274 nX
= maGeometry
.x() - (m_pParent
? m_pParent
->maGeometry
.x() : 0);
2275 if (pState
->mask() & vcl::WindowDataMask::Y
)
2276 nPosSizeFlags
|= SAL_FRAME_POSSIZE_Y
;
2278 nY
= maGeometry
.y() - (m_pParent
? m_pParent
->maGeometry
.y() : 0);
2279 if (pState
->mask() & vcl::WindowDataMask::Width
)
2280 nPosSizeFlags
|= SAL_FRAME_POSSIZE_WIDTH
;
2281 if (pState
->mask() & vcl::WindowDataMask::Height
)
2282 nPosSizeFlags
|= SAL_FRAME_POSSIZE_HEIGHT
;
2283 SetPosSize(nX
, nY
, pState
->width(), pState
->height(), nPosSizeFlags
);
2286 if (pState
->mask() & vcl::WindowDataMask::State
&& !isChild())
2288 if (pState
->state() & vcl::WindowState::Maximized
)
2289 gtk_window_maximize( GTK_WINDOW(m_pWindow
) );
2291 gtk_window_unmaximize( GTK_WINDOW(m_pWindow
) );
2292 /* #i42379# there is no rollup state in GDK; and rolled up windows are
2293 * (probably depending on the WM) reported as iconified. If we iconify a
2294 * window here that was e.g. a dialog, then it will be unmapped but still
2295 * not be displayed in the task list, so it's an iconified window that
2296 * the user cannot get out of this state. So do not set the iconified state
2297 * on windows with a parent (that is transient frames) since these tend
2298 * to not be represented in an icon task list.
2300 bool bMinimize
= pState
->state() & vcl::WindowState::Minimized
&& !m_pParent
;
2301 #if GTK_CHECK_VERSION(4, 0, 0)
2303 gtk_window_minimize(GTK_WINDOW(m_pWindow
));
2305 gtk_window_unminimize(GTK_WINDOW(m_pWindow
));
2308 gtk_window_iconify(GTK_WINDOW(m_pWindow
));
2310 gtk_window_deiconify(GTK_WINDOW(m_pWindow
));
2313 TriggerPaintEvent();
2318 void GetPosAndSize(GtkWindow
*pWindow
, tools::Long
& rX
, tools::Long
&rY
, tools::Long
&rWidth
, tools::Long
&rHeight
)
2321 #if !GTK_CHECK_VERSION(4, 0, 0)
2322 gint root_x
, root_y
;
2323 gtk_window_get_position(GTK_WINDOW(pWindow
), &root_x
, &root_y
);
2327 gtk_window_get_size(GTK_WINDOW(pWindow
), &width
, &height
);
2331 gtk_window_get_default_size(GTK_WINDOW(pWindow
), &width
, &height
);
2337 tools::Rectangle
GetPosAndSize(GtkWindow
*pWindow
)
2339 tools::Long nX
, nY
, nWidth
, nHeight
;
2340 GetPosAndSize(pWindow
, nX
, nY
, nWidth
, nHeight
);
2341 return tools::Rectangle(nX
, nY
, nX
+ nWidth
, nY
+ nHeight
);
2345 bool GtkSalFrame::GetWindowState(vcl::WindowData
* pState
)
2347 pState
->setState(vcl::WindowState::Normal
);
2348 pState
->setMask(vcl::WindowDataMask::PosSizeState
);
2350 // rollup ? gtk 2.2 does not seem to support the shaded state
2351 if( m_nState
& GDK_TOPLEVEL_STATE_MINIMIZED
)
2352 pState
->rState() |= vcl::WindowState::Minimized
;
2353 if( m_nState
& GDK_TOPLEVEL_STATE_MAXIMIZED
)
2355 pState
->rState() |= vcl::WindowState::Maximized
;
2356 pState
->setPosSize(m_aRestorePosSize
);
2357 tools::Rectangle aPosSize
= GetPosAndSize(GTK_WINDOW(m_pWindow
));
2358 pState
->SetMaximizedX(aPosSize
.Left());
2359 pState
->SetMaximizedY(aPosSize
.Top());
2360 pState
->SetMaximizedWidth(aPosSize
.GetWidth());
2361 pState
->SetMaximizedHeight(aPosSize
.GetHeight());
2362 pState
->rMask() |= vcl::WindowDataMask::MaximizedX
|
2363 vcl::WindowDataMask::MaximizedY
|
2364 vcl::WindowDataMask::MaximizedWidth
|
2365 vcl::WindowDataMask::MaximizedHeight
;
2368 pState
->setPosSize(GetPosAndSize(GTK_WINDOW(m_pWindow
)));
2373 void GtkSalFrame::SetScreen( unsigned int nNewScreen
, SetType eType
, tools::Rectangle
const *pSize
)
2378 if (maGeometry
.screen() == nNewScreen
&& eType
== SetType::RetainSize
)
2381 #if !GTK_CHECK_VERSION(4, 0, 0)
2382 int nX
= maGeometry
.x(), nY
= maGeometry
.y(),
2383 nWidth
= maGeometry
.width(), nHeight
= maGeometry
.height();
2384 GdkScreen
*pScreen
= nullptr;
2385 GdkRectangle aNewMonitor
;
2387 bool bSpanAllScreens
= nNewScreen
== static_cast<unsigned int>(-1);
2388 bool bSpanMonitorsWhenFullscreen
= bSpanAllScreens
&& getDisplay()->getSystem()->GetDisplayScreenCount() > 1;
2390 if (bSpanMonitorsWhenFullscreen
) //span all screens
2392 pScreen
= gtk_widget_get_screen( m_pWindow
);
2395 aNewMonitor
.width
= gdk_screen_get_width(pScreen
);
2396 aNewMonitor
.height
= gdk_screen_get_height(pScreen
);
2400 bool bSameMonitor
= false;
2402 if (!bSpanAllScreens
)
2404 pScreen
= getDisplay()->getSystem()->getScreenMonitorFromIdx( nNewScreen
, nMonitor
);
2407 g_warning ("Attempt to move GtkSalFrame to invalid screen %d => "
2408 "fallback to current\n", nNewScreen
);
2414 pScreen
= gtk_widget_get_screen( m_pWindow
);
2415 bSameMonitor
= true;
2418 // Heavy lifting, need to move screen ...
2419 if( pScreen
!= gtk_widget_get_screen( m_pWindow
))
2420 gtk_window_set_screen( GTK_WINDOW( m_pWindow
), pScreen
);
2422 gint nOldMonitor
= gdk_screen_get_monitor_at_window(
2423 pScreen
, widget_get_surface( m_pWindow
) );
2425 nMonitor
= nOldMonitor
;
2427 #if OSL_DEBUG_LEVEL > 1
2428 if( nMonitor
== nOldMonitor
)
2429 g_warning( "An apparently pointless SetScreen - should we elide it ?" );
2432 GdkRectangle aOldMonitor
;
2433 gdk_screen_get_monitor_geometry( pScreen
, nOldMonitor
, &aOldMonitor
);
2434 gdk_screen_get_monitor_geometry( pScreen
, nMonitor
, &aNewMonitor
);
2436 nX
= aNewMonitor
.x
+ nX
- aOldMonitor
.x
;
2437 nY
= aNewMonitor
.y
+ nY
- aOldMonitor
.y
;
2440 bool bResize
= false;
2441 bool bVisible
= gtk_widget_get_mapped( m_pWindow
);
2445 if( eType
== SetType::Fullscreen
)
2449 nWidth
= aNewMonitor
.width
;
2450 nHeight
= aNewMonitor
.height
;
2453 // #i110881# for the benefit of compiz set a max size here
2454 // else setting to fullscreen fails for unknown reasons
2455 m_aMaxSize
.setWidth( aNewMonitor
.width
);
2456 m_aMaxSize
.setHeight( aNewMonitor
.height
);
2459 if( pSize
&& eType
== SetType::UnFullscreen
)
2463 nWidth
= pSize
->GetWidth();
2464 nHeight
= pSize
->GetHeight();
2470 // temporarily re-sizeable
2471 if( !(m_nStyle
& SalFrameStyleFlags::SIZEABLE
) )
2472 gtk_window_set_resizable( GTK_WINDOW(m_pWindow
), true );
2473 window_resize(nWidth
, nHeight
);
2476 gtk_window_move(GTK_WINDOW(m_pWindow
), nX
, nY
);
2478 GdkFullscreenMode eMode
=
2479 bSpanMonitorsWhenFullscreen
? GDK_FULLSCREEN_ON_ALL_MONITORS
: GDK_FULLSCREEN_ON_CURRENT_MONITOR
;
2481 gdk_window_set_fullscreen_mode(widget_get_surface(m_pWindow
), eMode
);
2483 GtkWidget
* pMenuBarContainerWidget
= m_pSalMenu
? m_pSalMenu
->GetMenuBarContainerWidget() : nullptr;
2484 if( eType
== SetType::Fullscreen
)
2486 if (pMenuBarContainerWidget
)
2487 gtk_widget_hide(pMenuBarContainerWidget
);
2488 if (bSpanMonitorsWhenFullscreen
)
2489 gtk_window_fullscreen(GTK_WINDOW(m_pWindow
));
2491 gtk_window_fullscreen_on_monitor(GTK_WINDOW(m_pWindow
), pScreen
, nMonitor
);
2494 else if( eType
== SetType::UnFullscreen
)
2496 if (pMenuBarContainerWidget
)
2497 gtk_widget_show(pMenuBarContainerWidget
);
2498 gtk_window_unfullscreen( GTK_WINDOW( m_pWindow
) );
2501 if( eType
== SetType::UnFullscreen
&&
2502 !(m_nStyle
& SalFrameStyleFlags::SIZEABLE
) )
2503 gtk_window_set_resizable( GTK_WINDOW( m_pWindow
), FALSE
);
2505 // FIXME: we should really let gtk+ handle our widget hierarchy ...
2506 if( m_pParent
&& gtk_widget_get_screen( m_pParent
->m_pWindow
) != pScreen
)
2507 SetParent( nullptr );
2509 std::list
< GtkSalFrame
* > aChildren
= m_aChildren
;
2510 for (auto const& child
: aChildren
)
2511 child
->SetScreen( nNewScreen
, SetType::RetainSize
);
2513 m_bDefaultPos
= m_bDefaultSize
= false;
2514 updateScreenNumber();
2520 (void)pSize
; // assume restore will restore the original size without our help
2522 bool bSpanMonitorsWhenFullscreen
= nNewScreen
== static_cast<unsigned int>(-1);
2524 GdkFullscreenMode eMode
=
2525 bSpanMonitorsWhenFullscreen
? GDK_FULLSCREEN_ON_ALL_MONITORS
: GDK_FULLSCREEN_ON_CURRENT_MONITOR
;
2527 g_object_set(widget_get_surface(m_pWindow
), "fullscreen-mode", eMode
, nullptr);
2529 GtkWidget
* pMenuBarContainerWidget
= m_pSalMenu
? m_pSalMenu
->GetMenuBarContainerWidget() : nullptr;
2530 if (eType
== SetType::Fullscreen
)
2532 if (!(m_nStyle
& SalFrameStyleFlags::SIZEABLE
))
2534 // temp make it resizable, restore when unfullscreened
2535 gtk_window_set_resizable(GTK_WINDOW(m_pWindow
), true);
2538 if (pMenuBarContainerWidget
)
2539 gtk_widget_hide(pMenuBarContainerWidget
);
2540 if (bSpanMonitorsWhenFullscreen
)
2541 gtk_window_fullscreen(GTK_WINDOW(m_pWindow
));
2544 GdkDisplay
* pDisplay
= gtk_widget_get_display(m_pWindow
);
2545 GListModel
* pList
= gdk_display_get_monitors(pDisplay
);
2546 GdkMonitor
* pMonitor
= static_cast<GdkMonitor
*>(g_list_model_get_item(pList
, nNewScreen
));
2548 pMonitor
= gdk_display_get_monitor_at_surface(pDisplay
, widget_get_surface(m_pWindow
));
2549 gtk_window_fullscreen_on_monitor(GTK_WINDOW(m_pWindow
), pMonitor
);
2552 else if (eType
== SetType::UnFullscreen
)
2554 if (pMenuBarContainerWidget
)
2555 gtk_widget_show(pMenuBarContainerWidget
);
2556 gtk_window_unfullscreen(GTK_WINDOW(m_pWindow
));
2558 if (!(m_nStyle
& SalFrameStyleFlags::SIZEABLE
))
2560 // restore temp resizability
2561 gtk_window_set_resizable(GTK_WINDOW(m_pWindow
), false);
2565 for (auto const& child
: m_aChildren
)
2566 child
->SetScreen(nNewScreen
, SetType::RetainSize
);
2568 m_bDefaultPos
= m_bDefaultSize
= false;
2569 updateScreenNumber();
2573 void GtkSalFrame::SetScreenNumber( unsigned int nNewScreen
)
2575 SetScreen( nNewScreen
, SetType::RetainSize
);
2578 void GtkSalFrame::updateWMClass()
2580 if (!DLSYM_GDK_IS_X11_DISPLAY(getGdkDisplay()))
2583 if (!gtk_widget_get_realized(m_pWindow
))
2586 OString aResClass
= OUStringToOString(m_sWMClass
, RTL_TEXTENCODING_ASCII_US
);
2587 const char *pResClass
= !aResClass
.isEmpty() ? aResClass
.getStr() :
2588 SalGenericSystem::getFrameClassName();
2589 XClassHint
* pClass
= XAllocClassHint();
2590 OString aResName
= SalGenericSystem::getFrameResName();
2591 pClass
->res_name
= const_cast<char*>(aResName
.getStr());
2592 pClass
->res_class
= const_cast<char*>(pResClass
);
2593 Display
*display
= gdk_x11_display_get_xdisplay(getGdkDisplay());
2594 XSetClassHint( display
,
2595 GtkSalFrame::GetNativeWindowHandle(m_pWindow
),
2600 void GtkSalFrame::SetApplicationID( const OUString
&rWMClass
)
2602 if( rWMClass
!= m_sWMClass
&& ! isChild() )
2604 m_sWMClass
= rWMClass
;
2607 for (auto const& child
: m_aChildren
)
2608 child
->SetApplicationID(rWMClass
);
2612 void GtkSalFrame::ShowFullScreen( bool bFullScreen
, sal_Int32 nScreen
)
2614 m_bFullscreen
= bFullScreen
;
2616 if( !m_pWindow
|| isChild() )
2621 m_aRestorePosSize
= GetPosAndSize(GTK_WINDOW(m_pWindow
));
2622 SetScreen( nScreen
, SetType::Fullscreen
);
2626 SetScreen( nScreen
, SetType::UnFullscreen
,
2627 !m_aRestorePosSize
.IsEmpty() ? &m_aRestorePosSize
: nullptr );
2628 m_aRestorePosSize
= tools::Rectangle();
2632 void GtkSalFrame::SessionManagerInhibit(bool bStart
, ApplicationInhibitFlags eType
, std::u16string_view sReason
, const char* application_id
)
2635 std::optional
<Display
*> aDisplay
;
2637 if (DLSYM_GDK_IS_X11_DISPLAY(getGdkDisplay()))
2639 nWindow
= GtkSalFrame::GetNativeWindowHandle(m_pWindow
);
2640 aDisplay
= gdk_x11_display_get_xdisplay(getGdkDisplay());
2643 m_SessionManagerInhibitor
.inhibit(bStart
, sReason
, eType
,
2644 nWindow
, aDisplay
, application_id
);
2647 void GtkSalFrame::StartPresentation( bool bStart
)
2649 SessionManagerInhibit(bStart
, APPLICATION_INHIBIT_IDLE
, u
"presentation", nullptr);
2652 void GtkSalFrame::SetAlwaysOnTop( bool bOnTop
)
2654 #if !GTK_CHECK_VERSION(4, 0, 0)
2656 gtk_window_set_keep_above( GTK_WINDOW( m_pWindow
), bOnTop
);
2662 static guint32 nLastUserInputTime
= GDK_CURRENT_TIME
;
2664 guint32
GtkSalFrame::GetLastInputEventTime()
2666 return nLastUserInputTime
;
2669 void GtkSalFrame::UpdateLastInputEventTime(guint32 nUserInputTime
)
2671 //gtk3 can generate a synthetic crossing event with a useless 0
2672 //(GDK_CURRENT_TIME) timestamp on showing a menu from the main
2673 //menubar, which is unhelpful, so ignore the 0 timestamps
2674 if (nUserInputTime
== GDK_CURRENT_TIME
)
2676 nLastUserInputTime
= nUserInputTime
;
2679 void GtkSalFrame::ToTop( SalFrameToTop nFlags
)
2684 if( isChild( false ) )
2686 else if( gtk_widget_get_mapped( m_pWindow
) )
2688 auto nTimestamp
= GetLastInputEventTime();
2689 #ifdef GDK_WINDOWING_X11
2690 GdkDisplay
*pDisplay
= GtkSalFrame::getGdkDisplay();
2691 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay
))
2692 nTimestamp
= gdk_x11_display_get_user_time(pDisplay
);
2694 if (!(nFlags
& SalFrameToTop::GrabFocusOnly
))
2695 gtk_window_present_with_time(GTK_WINDOW(m_pWindow
), nTimestamp
);
2696 #if !GTK_CHECK_VERSION(4, 0, 0)
2698 gdk_window_focus(widget_get_surface(m_pWindow
), nTimestamp
);
2704 if( nFlags
& SalFrameToTop::RestoreWhenMin
)
2705 gtk_window_present( GTK_WINDOW(m_pWindow
) );
2709 void GtkSalFrame::SetPointer( PointerStyle ePointerStyle
)
2711 if( !m_pWindow
|| ePointerStyle
== m_ePointerStyle
)
2714 m_ePointerStyle
= ePointerStyle
;
2715 GdkCursor
*pCursor
= getDisplay()->getCursor( ePointerStyle
);
2716 widget_set_cursor(GTK_WIDGET(m_pWindow
), pCursor
);
2719 void GtkSalFrame::grabPointer( bool bGrab
, bool bKeyboardAlso
, bool bOwnerEvents
)
2723 // tdf#135779 move focus back inside usual input window out of any
2724 // other gtk widgets before grabbing the pointer
2728 static const char* pEnv
= getenv( "SAL_NO_MOUSEGRABS" );
2735 #if !GTK_CHECK_VERSION(4, 0, 0)
2736 GdkSeat
* pSeat
= gdk_display_get_default_seat(getGdkDisplay());
2739 GdkSeatCapabilities eCapability
= bKeyboardAlso
? GDK_SEAT_CAPABILITY_ALL
: GDK_SEAT_CAPABILITY_ALL_POINTING
;
2740 gdk_seat_grab(pSeat
, widget_get_surface(getMouseEventWidget()), eCapability
,
2741 bOwnerEvents
, nullptr, nullptr, nullptr, nullptr);
2745 gdk_seat_ungrab(pSeat
);
2748 (void)bKeyboardAlso
;
2753 void GtkSalFrame::CaptureMouse( bool bCapture
)
2755 getDisplay()->CaptureMouse( bCapture
? this : nullptr );
2758 void GtkSalFrame::SetPointerPos( tools::Long nX
, tools::Long nY
)
2760 #if !GTK_CHECK_VERSION(4, 0, 0)
2761 GtkSalFrame
* pFrame
= this;
2762 while( pFrame
&& pFrame
->isChild( false ) )
2763 pFrame
= pFrame
->m_pParent
;
2767 GdkScreen
*pScreen
= gtk_widget_get_screen(pFrame
->m_pWindow
);
2768 GdkDisplay
*pDisplay
= gdk_screen_get_display( pScreen
);
2770 /* when the application tries to center the mouse in the dialog the
2771 * window isn't mapped already. So use coordinates relative to the root window.
2773 unsigned int nWindowLeft
= maGeometry
.x() + nX
;
2774 unsigned int nWindowTop
= maGeometry
.y() + nY
;
2776 GdkDeviceManager
* pManager
= gdk_display_get_device_manager(pDisplay
);
2777 gdk_device_warp(gdk_device_manager_get_client_pointer(pManager
), pScreen
, nWindowLeft
, nWindowTop
);
2779 // #i38648# ask for the next motion hint
2781 GdkModifierType mask
;
2782 gdk_window_get_pointer( widget_get_surface(pFrame
->m_pWindow
) , &x
, &y
, &mask
);
2789 void GtkSalFrame::Flush()
2791 gdk_display_flush( getGdkDisplay() );
2794 void GtkSalFrame::KeyCodeToGdkKey(const vcl::KeyCode
& rKeyCode
,
2795 guint
* pGdkKeyCode
, GdkModifierType
*pGdkModifiers
)
2797 if ( pGdkKeyCode
== nullptr || pGdkModifiers
== nullptr )
2800 // Get GDK key modifiers
2801 GdkModifierType nModifiers
= GdkModifierType(0);
2803 if ( rKeyCode
.IsShift() )
2804 nModifiers
= static_cast<GdkModifierType
>( nModifiers
| GDK_SHIFT_MASK
);
2806 if ( rKeyCode
.IsMod1() )
2807 nModifiers
= static_cast<GdkModifierType
>( nModifiers
| GDK_CONTROL_MASK
);
2809 if ( rKeyCode
.IsMod2() )
2810 nModifiers
= static_cast<GdkModifierType
>( nModifiers
| GDK_ALT_MASK
);
2812 *pGdkModifiers
= nModifiers
;
2817 guint nCode
= rKeyCode
.GetCode();
2819 if ( nCode
>= KEY_0
&& nCode
<= KEY_9
)
2820 nKeyCode
= ( nCode
- KEY_0
) + GDK_KEY_0
;
2821 else if ( nCode
>= KEY_A
&& nCode
<= KEY_Z
)
2822 nKeyCode
= ( nCode
- KEY_A
) + GDK_KEY_A
;
2823 else if ( nCode
>= KEY_F1
&& nCode
<= KEY_F26
)
2824 nKeyCode
= ( nCode
- KEY_F1
) + GDK_KEY_F1
;
2829 case KEY_DOWN
: nKeyCode
= GDK_KEY_Down
; break;
2830 case KEY_UP
: nKeyCode
= GDK_KEY_Up
; break;
2831 case KEY_LEFT
: nKeyCode
= GDK_KEY_Left
; break;
2832 case KEY_RIGHT
: nKeyCode
= GDK_KEY_Right
; break;
2833 case KEY_HOME
: nKeyCode
= GDK_KEY_Home
; break;
2834 case KEY_END
: nKeyCode
= GDK_KEY_End
; break;
2835 case KEY_PAGEUP
: nKeyCode
= GDK_KEY_Page_Up
; break;
2836 case KEY_PAGEDOWN
: nKeyCode
= GDK_KEY_Page_Down
; break;
2837 case KEY_RETURN
: nKeyCode
= GDK_KEY_Return
; break;
2838 case KEY_ESCAPE
: nKeyCode
= GDK_KEY_Escape
; break;
2839 case KEY_TAB
: nKeyCode
= GDK_KEY_Tab
; break;
2840 case KEY_BACKSPACE
: nKeyCode
= GDK_KEY_BackSpace
; break;
2841 case KEY_SPACE
: nKeyCode
= GDK_KEY_space
; break;
2842 case KEY_INSERT
: nKeyCode
= GDK_KEY_Insert
; break;
2843 case KEY_DELETE
: nKeyCode
= GDK_KEY_Delete
; break;
2844 case KEY_ADD
: nKeyCode
= GDK_KEY_plus
; break;
2845 case KEY_SUBTRACT
: nKeyCode
= GDK_KEY_minus
; break;
2846 case KEY_MULTIPLY
: nKeyCode
= GDK_KEY_asterisk
; break;
2847 case KEY_DIVIDE
: nKeyCode
= GDK_KEY_slash
; break;
2848 case KEY_POINT
: nKeyCode
= GDK_KEY_period
; break;
2849 case KEY_COMMA
: nKeyCode
= GDK_KEY_comma
; break;
2850 case KEY_LESS
: nKeyCode
= GDK_KEY_less
; break;
2851 case KEY_GREATER
: nKeyCode
= GDK_KEY_greater
; break;
2852 case KEY_EQUAL
: nKeyCode
= GDK_KEY_equal
; break;
2853 case KEY_FIND
: nKeyCode
= GDK_KEY_Find
; break;
2854 case KEY_CONTEXTMENU
: nKeyCode
= GDK_KEY_Menu
; break;
2855 case KEY_HELP
: nKeyCode
= GDK_KEY_Help
; break;
2856 case KEY_UNDO
: nKeyCode
= GDK_KEY_Undo
; break;
2857 case KEY_REPEAT
: nKeyCode
= GDK_KEY_Redo
; break;
2858 case KEY_DECIMAL
: nKeyCode
= GDK_KEY_KP_Decimal
; break;
2859 case KEY_TILDE
: nKeyCode
= GDK_KEY_asciitilde
; break;
2860 case KEY_QUOTELEFT
: nKeyCode
= GDK_KEY_quoteleft
; break;
2861 case KEY_BRACKETLEFT
: nKeyCode
= GDK_KEY_bracketleft
; break;
2862 case KEY_BRACKETRIGHT
: nKeyCode
= GDK_KEY_bracketright
; break;
2863 case KEY_SEMICOLON
: nKeyCode
= GDK_KEY_semicolon
; break;
2864 case KEY_QUOTERIGHT
: nKeyCode
= GDK_KEY_quoteright
; break;
2865 case KEY_RIGHTCURLYBRACKET
: nKeyCode
= GDK_KEY_braceright
; break;
2866 case KEY_COLON
: nKeyCode
= GDK_KEY_colon
; break;
2869 case KEY_COPY
: nKeyCode
= GDK_KEY_Copy
; break;
2870 case KEY_CUT
: nKeyCode
= GDK_KEY_Cut
; break;
2871 case KEY_PASTE
: nKeyCode
= GDK_KEY_Paste
; break;
2872 case KEY_OPEN
: nKeyCode
= GDK_KEY_Open
; break;
2876 *pGdkKeyCode
= nKeyCode
;
2879 OUString
GtkSalFrame::GetKeyName( sal_uInt16 nKeyCode
)
2882 GdkModifierType nGtkModifiers
;
2883 KeyCodeToGdkKey(nKeyCode
, &nGtkKeyCode
, &nGtkModifiers
);
2885 gchar
* pName
= gtk_accelerator_get_label(nGtkKeyCode
, nGtkModifiers
);
2886 OUString aRet
= OStringToOUString(pName
, RTL_TEXTENCODING_UTF8
);
2891 GdkDisplay
*GtkSalFrame::getGdkDisplay()
2893 return GetGtkSalData()->GetGdkDisplay();
2896 GtkSalDisplay
*GtkSalFrame::getDisplay()
2898 return GetGtkSalData()->GetGtkDisplay();
2901 SalFrame::SalPointerState
GtkSalFrame::GetPointerState()
2903 SalPointerState aState
;
2904 #if !GTK_CHECK_VERSION(4, 0, 0)
2907 GdkModifierType aMask
;
2908 gdk_display_get_pointer( getGdkDisplay(), &pScreen
, &x
, &y
, &aMask
);
2909 aState
.maPos
= Point( x
- maGeometry
.x(), y
- maGeometry
.y() );
2910 aState
.mnState
= GetMouseModCode( aMask
);
2915 KeyIndicatorState
GtkSalFrame::GetIndicatorState()
2917 KeyIndicatorState nState
= KeyIndicatorState::NONE
;
2919 #if !GTK_CHECK_VERSION(4, 0, 0)
2920 GdkKeymap
*pKeyMap
= gdk_keymap_get_for_display(getGdkDisplay());
2922 if (gdk_keymap_get_caps_lock_state(pKeyMap
))
2923 nState
|= KeyIndicatorState::CAPSLOCK
;
2924 if (gdk_keymap_get_num_lock_state(pKeyMap
))
2925 nState
|= KeyIndicatorState::NUMLOCK
;
2926 if (gdk_keymap_get_scroll_lock_state(pKeyMap
))
2927 nState
|= KeyIndicatorState::SCROLLLOCK
;
2933 void GtkSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode
)
2935 g_warning ("missing simulate keypress %d", nKeyCode
);
2938 void GtkSalFrame::SetInputContext( SalInputContext
* pContext
)
2943 if( ! (pContext
->mnOptions
& InputContextFlags::Text
) )
2946 // create a new im context
2947 if( ! m_pIMHandler
)
2948 m_pIMHandler
.reset( new IMHandler( this ) );
2951 void GtkSalFrame::EndExtTextInput( EndExtTextInputFlags nFlags
)
2954 m_pIMHandler
->endExtTextInput( nFlags
);
2957 bool GtkSalFrame::MapUnicodeToKeyCode( sal_Unicode
, LanguageType
, vcl::KeyCode
& )
2959 // not supported yet
2963 LanguageType
GtkSalFrame::GetInputLanguage()
2965 return LANGUAGE_DONTKNOW
;
2968 void GtkSalFrame::UpdateSettings( AllSettings
& rSettings
)
2973 GtkSalGraphics
* pGraphics
= m_pGraphics
.get();
2974 bool bFreeGraphics
= false;
2977 pGraphics
= static_cast<GtkSalGraphics
*>(AcquireGraphics());
2980 SAL_WARN("vcl.gtk3", "Could not get graphics - unable to update settings");
2983 bFreeGraphics
= true;
2986 pGraphics
->UpdateSettings( rSettings
);
2989 ReleaseGraphics( pGraphics
);
2992 void GtkSalFrame::Beep()
2994 gdk_display_beep( getGdkDisplay() );
2997 const SystemEnvData
* GtkSalFrame::GetSystemData() const
2999 return &m_aSystemData
;
3002 void GtkSalFrame::ResolveWindowHandle(SystemEnvData
& rData
) const
3006 SAL_WARN("vcl.gtk3", "its undesirable to need the NativeWindowHandle, see tdf#139609");
3007 rData
.SetWindowHandle(GetNativeWindowHandle(static_cast<GtkWidget
*>(rData
.pWidget
)));
3010 void GtkSalFrame::SetParent( SalFrame
* pNewParent
)
3012 GtkWindow
* pWindow
= GTK_IS_WINDOW(m_pWindow
) ? GTK_WINDOW(m_pWindow
) : nullptr;
3015 if (pWindow
&& GTK_IS_WINDOW(m_pParent
->m_pWindow
))
3016 gtk_window_group_remove_window(gtk_window_get_group(GTK_WINDOW(m_pParent
->m_pWindow
)), pWindow
);
3017 m_pParent
->m_aChildren
.remove(this);
3019 m_pParent
= static_cast<GtkSalFrame
*>(pNewParent
);
3022 m_pParent
->m_aChildren
.push_back(this);
3023 if (pWindow
&& GTK_IS_WINDOW(m_pParent
->m_pWindow
))
3024 gtk_window_group_add_window(gtk_window_get_group(GTK_WINDOW(m_pParent
->m_pWindow
)), pWindow
);
3026 if (!isChild() && pWindow
)
3027 gtk_window_set_transient_for( pWindow
,
3028 (m_pParent
&& ! m_pParent
->isChild(true,false)) ? GTK_WINDOW(m_pParent
->m_pWindow
) : nullptr
3032 void GtkSalFrame::SetPluginParent( SystemParentData
* )
3034 //FIXME: no SetPluginParent impl. for gtk3
3037 void GtkSalFrame::ResetClipRegion()
3039 #if !GTK_CHECK_VERSION(4, 0, 0)
3041 gdk_window_shape_combine_region( widget_get_surface( m_pWindow
), nullptr, 0, 0 );
3045 void GtkSalFrame::BeginSetClipRegion( sal_uInt32
)
3048 cairo_region_destroy( m_pRegion
);
3049 m_pRegion
= cairo_region_create();
3052 void GtkSalFrame::UnionClipRegion( tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
)
3059 aRect
.width
= nWidth
;
3060 aRect
.height
= nHeight
;
3061 cairo_region_union_rectangle( m_pRegion
, &aRect
);
3065 void GtkSalFrame::EndSetClipRegion()
3067 #if !GTK_CHECK_VERSION(4, 0, 0)
3068 if( m_pWindow
&& m_pRegion
)
3069 gdk_window_shape_combine_region( widget_get_surface(m_pWindow
), m_pRegion
, 0, 0 );
3073 void GtkSalFrame::PositionByToolkit(const tools::Rectangle
& rRect
, FloatWinPopupFlags nFlags
)
3075 if (ImplGetSVData()->maNWFData
.mbCanDetermineWindowPosition
)
3078 m_aFloatRect
= rRect
;
3079 m_nFloatFlags
= nFlags
;
3080 m_bFloatPositioned
= true;
3083 void GtkSalFrame::SetModal(bool bModal
)
3087 gtk_window_set_modal(GTK_WINDOW(m_pWindow
), bModal
);
3090 bool GtkSalFrame::GetModal() const
3094 return gtk_window_get_modal(GTK_WINDOW(m_pWindow
));
3097 gboolean
GtkSalFrame::signalTooltipQuery(GtkWidget
*, gint
/*x*/, gint
/*y*/,
3098 gboolean
/*keyboard_mode*/, GtkTooltip
*tooltip
,
3101 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3102 if (pThis
->m_aTooltip
.isEmpty() || pThis
->m_bTooltipBlocked
)
3104 gtk_tooltip_set_text(tooltip
,
3105 OUStringToOString(pThis
->m_aTooltip
, RTL_TEXTENCODING_UTF8
).getStr());
3106 GdkRectangle aHelpArea
;
3107 aHelpArea
.x
= pThis
->m_aHelpArea
.Left();
3108 aHelpArea
.y
= pThis
->m_aHelpArea
.Top();
3109 aHelpArea
.width
= pThis
->m_aHelpArea
.GetWidth();
3110 aHelpArea
.height
= pThis
->m_aHelpArea
.GetHeight();
3111 if (AllSettings::GetLayoutRTL())
3112 aHelpArea
.x
= pThis
->maGeometry
.width()-aHelpArea
.width
-1-aHelpArea
.x
;
3113 gtk_tooltip_set_tip_area(tooltip
, &aHelpArea
);
3117 bool GtkSalFrame::ShowTooltip(const OUString
& rHelpText
, const tools::Rectangle
& rHelpArea
)
3119 m_aTooltip
= rHelpText
;
3120 m_aHelpArea
= rHelpArea
;
3121 gtk_widget_trigger_tooltip_query(getMouseEventWidget());
3125 void GtkSalFrame::BlockTooltip()
3127 m_bTooltipBlocked
= true;
3130 void GtkSalFrame::UnblockTooltip()
3132 m_bTooltipBlocked
= false;
3135 void GtkSalFrame::HideTooltip()
3138 GtkWidget
* pEventWidget
= getMouseEventWidget();
3139 gtk_widget_trigger_tooltip_query(pEventWidget
);
3144 void set_pointing_to(GtkPopover
*pPopOver
, vcl::Window
* pParent
, const tools::Rectangle
& rHelpArea
, const SalFrameGeometry
& rGeometry
)
3147 aRect
.x
= FloatingWindow::ImplConvertToAbsPos(pParent
, rHelpArea
).Left() - rGeometry
.x();
3148 aRect
.y
= rHelpArea
.Top();
3152 GtkPositionType ePos
= gtk_popover_get_position(pPopOver
);
3155 case GTK_POS_BOTTOM
:
3157 aRect
.width
= rHelpArea
.GetWidth();
3161 aRect
.height
= rHelpArea
.GetHeight();
3165 gtk_popover_set_pointing_to(pPopOver
, &aRect
);
3169 void* GtkSalFrame::ShowPopover(const OUString
& rHelpText
, vcl::Window
* pParent
, const tools::Rectangle
& rHelpArea
, QuickHelpFlags nFlags
)
3171 #if !GTK_CHECK_VERSION(4, 0, 0)
3172 GtkWidget
*pWidget
= gtk_popover_new(getMouseEventWidget());
3174 GtkWidget
*pWidget
= gtk_popover_new();
3175 gtk_widget_set_parent(pWidget
, getMouseEventWidget());
3177 OString sUTF
= OUStringToOString(rHelpText
, RTL_TEXTENCODING_UTF8
);
3178 GtkWidget
*pLabel
= gtk_label_new(sUTF
.getStr());
3179 #if !GTK_CHECK_VERSION(4, 0, 0)
3180 gtk_container_add(GTK_CONTAINER(pWidget
), pLabel
);
3182 gtk_popover_set_child(GTK_POPOVER(pWidget
), pLabel
);
3185 if (nFlags
& QuickHelpFlags::Top
)
3186 gtk_popover_set_position(GTK_POPOVER(pWidget
), GTK_POS_BOTTOM
);
3187 else if (nFlags
& QuickHelpFlags::Bottom
)
3188 gtk_popover_set_position(GTK_POPOVER(pWidget
), GTK_POS_TOP
);
3189 else if (nFlags
& QuickHelpFlags::Left
)
3190 gtk_popover_set_position(GTK_POPOVER(pWidget
), GTK_POS_RIGHT
);
3191 else if (nFlags
& QuickHelpFlags::Right
)
3192 gtk_popover_set_position(GTK_POPOVER(pWidget
), GTK_POS_LEFT
);
3194 set_pointing_to(GTK_POPOVER(pWidget
), pParent
, rHelpArea
, maGeometry
);
3196 #if !GTK_CHECK_VERSION(4, 0, 0)
3197 gtk_popover_set_modal(GTK_POPOVER(pWidget
), false);
3199 gtk_popover_set_autohide(GTK_POPOVER(pWidget
), false);
3202 gtk_widget_show(pLabel
);
3203 gtk_widget_show(pWidget
);
3208 bool GtkSalFrame::UpdatePopover(void* nId
, const OUString
& rHelpText
, vcl::Window
* pParent
, const tools::Rectangle
& rHelpArea
)
3210 GtkWidget
*pWidget
= static_cast<GtkWidget
*>(nId
);
3212 set_pointing_to(GTK_POPOVER(pWidget
), pParent
, rHelpArea
, maGeometry
);
3214 #if !GTK_CHECK_VERSION(4, 0, 0)
3215 GtkWidget
*pLabel
= gtk_bin_get_child(GTK_BIN(pWidget
));
3217 GtkWidget
*pLabel
= gtk_popover_get_child(GTK_POPOVER(pWidget
));
3219 OString sUTF
= OUStringToOString(rHelpText
, RTL_TEXTENCODING_UTF8
);
3220 gtk_label_set_text(GTK_LABEL(pLabel
), sUTF
.getStr());
3225 bool GtkSalFrame::HidePopover(void* nId
)
3227 GtkWidget
*pWidget
= static_cast<GtkWidget
*>(nId
);
3228 #if !GTK_CHECK_VERSION(4, 0, 0)
3229 gtk_widget_destroy(pWidget
);
3231 g_clear_pointer(&pWidget
, gtk_widget_unparent
);
3236 void GtkSalFrame::addGrabLevel()
3238 #if !GTK_CHECK_VERSION(4, 0, 0)
3239 if (m_nGrabLevel
== 0)
3240 gtk_grab_add(getMouseEventWidget());
3245 void GtkSalFrame::removeGrabLevel()
3247 if (m_nGrabLevel
> 0)
3250 #if !GTK_CHECK_VERSION(4, 0, 0)
3251 if (m_nGrabLevel
== 0)
3252 gtk_grab_remove(getMouseEventWidget());
3257 void GtkSalFrame::closePopup()
3261 ImplSVData
* pSVData
= ImplGetSVData();
3262 if (!pSVData
->mpWinData
->mpFirstFloat
)
3264 if (pSVData
->mpWinData
->mpFirstFloat
->ImplGetFrame() != this)
3266 pSVData
->mpWinData
->mpFirstFloat
->EndPopupMode(FloatWinPopupEndFlags::Cancel
| FloatWinPopupEndFlags::CloseAll
);
3269 #if !GTK_CHECK_VERSION(4, 0, 0)
3272 //tdf#117981 translate embedded video window mouse events to parent coordinates
3273 void translate_coords(GdkWindow
* pSourceWindow
, GtkWidget
* pTargetWidget
, int& rEventX
, int& rEventY
)
3275 gpointer user_data
=nullptr;
3276 gdk_window_get_user_data(pSourceWindow
, &user_data
);
3277 GtkWidget
* pRealEventWidget
= static_cast<GtkWidget
*>(user_data
);
3278 if (pRealEventWidget
)
3280 gtk_coord
fX(0.0), fY(0.0);
3281 gtk_widget_translate_coordinates(pRealEventWidget
, pTargetWidget
, rEventX
, rEventY
, &fX
, &fY
);
3289 void GtkSalFrame::GrabFocus()
3291 GtkWidget
* pGrabWidget
;
3292 #if !GTK_CHECK_VERSION(4, 0, 0)
3293 if (GTK_IS_EVENT_BOX(m_pWindow
))
3294 pGrabWidget
= GTK_WIDGET(m_pWindow
);
3296 pGrabWidget
= GTK_WIDGET(m_pFixedContainer
);
3297 // m_nSetFocusSignalId is 0 for the DisallowCycleFocusOut case where
3298 // we don't allow focus to enter the toplevel, but expect it to
3299 // stay in some embedded native gtk widget
3300 if (!gtk_widget_get_can_focus(pGrabWidget
) && m_nSetFocusSignalId
)
3301 gtk_widget_set_can_focus(pGrabWidget
, true);
3303 pGrabWidget
= GTK_WIDGET(m_pFixedContainer
);
3305 if (!gtk_widget_has_focus(pGrabWidget
))
3307 gtk_widget_grab_focus(pGrabWidget
);
3309 m_pIMHandler
->focusChanged(true);
3313 bool GtkSalFrame::DrawingAreaButton(SalEvent nEventType
, int nEventX
, int nEventY
, int nButton
, guint32 nTime
, guint nState
)
3315 UpdateLastInputEventTime(nTime
);
3317 SalMouseEvent aEvent
;
3320 case 1: aEvent
.mnButton
= MOUSE_LEFT
; break;
3321 case 2: aEvent
.mnButton
= MOUSE_MIDDLE
; break;
3322 case 3: aEvent
.mnButton
= MOUSE_RIGHT
; break;
3323 default: return false;
3326 aEvent
.mnTime
= nTime
;
3327 aEvent
.mnX
= nEventX
;
3328 aEvent
.mnY
= nEventY
;
3329 aEvent
.mnCode
= GetMouseModCode(nState
);
3331 if( AllSettings::GetLayoutRTL() )
3332 aEvent
.mnX
= maGeometry
.width()-1-aEvent
.mnX
;
3334 CallCallbackExc(nEventType
, &aEvent
);
3339 #if !GTK_CHECK_VERSION(4, 0, 0)
3341 void GtkSalFrame::UpdateGeometryFromEvent(int x_root
, int y_root
, int nEventX
, int nEventY
)
3343 //tdf#151509 don't overwrite geometry for system children
3344 if (m_nStyle
& SalFrameStyleFlags::SYSTEMCHILD
)
3347 int frame_x
= x_root
- nEventX
;
3348 int frame_y
= y_root
- nEventY
;
3349 if (m_bGeometryIsProvisional
|| frame_x
!= maGeometry
.x() || frame_y
!= maGeometry
.y())
3351 m_bGeometryIsProvisional
= false;
3352 maGeometry
.setPos({ frame_x
, frame_y
});
3353 ImplSVData
* pSVData
= ImplGetSVData();
3354 if (pSVData
->maNWFData
.mbCanDetermineWindowPosition
)
3355 CallCallbackExc(SalEvent::Move
, nullptr);
3359 gboolean
GtkSalFrame::signalButton(GtkWidget
*, GdkEventButton
* pEvent
, gpointer frame
)
3361 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3362 GtkWidget
* pEventWidget
= pThis
->getMouseEventWidget();
3363 bool bDifferentEventWindow
= pEvent
->window
!= widget_get_surface(pEventWidget
);
3365 if (pEvent
->type
== GDK_BUTTON_PRESS
)
3367 // tdf#120764 It isn't allowed under wayland to have two visible popups that share
3368 // the same top level parent. The problem is that since gtk 3.24 tooltips are also
3369 // implemented as popups, which means that we cannot show any popup if there is a
3370 // visible tooltip. In fact, gtk will hide the tooltip by itself after this handler,
3371 // in case of a button press event. But if we intend to show a popup on button press
3372 // it will be too late, so just do it here:
3373 pThis
->HideTooltip();
3376 if (!bDifferentEventWindow
)
3380 SalEvent nEventType
= SalEvent::NONE
;
3381 switch( pEvent
->type
)
3383 case GDK_BUTTON_PRESS
:
3384 nEventType
= SalEvent::MouseButtonDown
;
3386 case GDK_BUTTON_RELEASE
:
3387 nEventType
= SalEvent::MouseButtonUp
;
3393 vcl::DeletionListener
aDel( pThis
);
3395 if (pThis
->isFloatGrabWindow())
3397 //rhbz#1505379 if the window that got the event isn't our one, or there's none
3398 //of our windows under the mouse then close this popup window
3399 if (bDifferentEventWindow
||
3400 gdk_device_get_window_at_position(pEvent
->device
, nullptr, nullptr) == nullptr)
3402 if (pEvent
->type
== GDK_BUTTON_PRESS
)
3403 pThis
->closePopup();
3404 else if (pEvent
->type
== GDK_BUTTON_RELEASE
)
3409 int nEventX
= pEvent
->x
;
3410 int nEventY
= pEvent
->y
;
3412 if (bDifferentEventWindow
)
3413 translate_coords(pEvent
->window
, pEventWidget
, nEventX
, nEventY
);
3415 if (!aDel
.isDeleted())
3416 pThis
->UpdateGeometryFromEvent(pEvent
->x_root
, pEvent
->y_root
, nEventX
, nEventY
);
3419 if (!aDel
.isDeleted())
3421 bRet
= pThis
->DrawingAreaButton(nEventType
,
3432 void GtkSalFrame::gesturePressed(GtkGestureClick
* pGesture
, int /*n_press*/, gdouble x
, gdouble y
, gpointer frame
)
3434 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3435 pThis
->gestureButton(pGesture
, SalEvent::MouseButtonDown
, x
, y
);
3438 void GtkSalFrame::gestureReleased(GtkGestureClick
* pGesture
, int /*n_press*/, gdouble x
, gdouble y
, gpointer frame
)
3440 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3441 pThis
->gestureButton(pGesture
, SalEvent::MouseButtonUp
, x
, y
);
3444 void GtkSalFrame::gestureButton(GtkGestureClick
* pGesture
, SalEvent nEventType
, gdouble x
, gdouble y
)
3446 GdkEvent
* pEvent
= gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pGesture
));
3447 GdkModifierType eType
= gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pGesture
));
3448 int nButton
= gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(pGesture
));
3449 DrawingAreaButton(nEventType
, x
, y
, nButton
, gdk_event_get_time(pEvent
), eType
);
3453 #if !GTK_CHECK_VERSION(4, 0, 0)
3454 void GtkSalFrame::LaunchAsyncScroll(GdkEvent
const * pEvent
)
3456 //if we don't match previous pending states, flush that queue now
3457 if (!m_aPendingScrollEvents
.empty() && pEvent
->scroll
.state
!= m_aPendingScrollEvents
.back()->scroll
.state
)
3459 m_aSmoothScrollIdle
.Stop();
3460 m_aSmoothScrollIdle
.Invoke();
3461 assert(m_aPendingScrollEvents
.empty());
3463 //add scroll event to queue
3464 m_aPendingScrollEvents
.push_back(gdk_event_copy(pEvent
));
3465 if (!m_aSmoothScrollIdle
.IsActive())
3466 m_aSmoothScrollIdle
.Start();
3470 void GtkSalFrame::DrawingAreaScroll(double delta_x
, double delta_y
, int nEventX
, int nEventY
, guint32 nTime
, guint nState
)
3472 SalWheelMouseEvent aEvent
;
3474 aEvent
.mnTime
= nTime
;
3475 aEvent
.mnX
= nEventX
;
3476 // --- RTL --- (mirror mouse pos)
3477 if (AllSettings::GetLayoutRTL())
3478 aEvent
.mnX
= maGeometry
.width() - 1 - aEvent
.mnX
;
3479 aEvent
.mnY
= nEventY
;
3480 aEvent
.mnCode
= GetMouseModCode(nState
);
3482 // rhbz#1344042 "Traditionally" in gtk3 we took a single up/down event as
3483 // equating to 3 scroll lines and a delta of 120. So scale the delta here
3484 // by 120 where a single mouse wheel click is an incoming delta_x of 1
3485 // and divide that by 40 to get the number of scroll lines
3488 aEvent
.mnDelta
= -delta_x
* 120;
3489 aEvent
.mnNotchDelta
= aEvent
.mnDelta
< 0 ? -1 : +1;
3490 if (aEvent
.mnDelta
== 0)
3491 aEvent
.mnDelta
= aEvent
.mnNotchDelta
;
3492 aEvent
.mbHorz
= true;
3493 aEvent
.mnScrollLines
= std::abs(aEvent
.mnDelta
) / 40.0;
3494 CallCallbackExc(SalEvent::WheelMouse
, &aEvent
);
3499 aEvent
.mnDelta
= -delta_y
* 120;
3500 aEvent
.mnNotchDelta
= aEvent
.mnDelta
< 0 ? -1 : +1;
3501 if (aEvent
.mnDelta
== 0)
3502 aEvent
.mnDelta
= aEvent
.mnNotchDelta
;
3503 aEvent
.mbHorz
= false;
3504 aEvent
.mnScrollLines
= std::abs(aEvent
.mnDelta
) / 40.0;
3505 CallCallbackExc(SalEvent::WheelMouse
, &aEvent
);
3509 #if !GTK_CHECK_VERSION(4, 0, 0)
3510 IMPL_LINK_NOARG(GtkSalFrame
, AsyncScroll
, Timer
*, void)
3512 assert(!m_aPendingScrollEvents
.empty());
3514 GdkEvent
* pEvent
= m_aPendingScrollEvents
.back();
3515 auto nEventX
= pEvent
->scroll
.x
;
3516 auto nEventY
= pEvent
->scroll
.y
;
3517 auto nTime
= pEvent
->scroll
.time
;
3518 auto nState
= pEvent
->scroll
.state
;
3520 double delta_x(0.0), delta_y(0.0);
3521 for (auto pSubEvent
: m_aPendingScrollEvents
)
3523 delta_x
+= pSubEvent
->scroll
.delta_x
;
3524 delta_y
+= pSubEvent
->scroll
.delta_y
;
3525 gdk_event_free(pSubEvent
);
3527 m_aPendingScrollEvents
.clear();
3529 DrawingAreaScroll(delta_x
, delta_y
, nEventX
, nEventY
, nTime
, nState
);
3533 #if !GTK_CHECK_VERSION(4, 0, 0)
3534 SalWheelMouseEvent
GtkSalFrame::GetWheelEvent(const GdkEventScroll
& rEvent
)
3536 SalWheelMouseEvent aEvent
;
3538 aEvent
.mnTime
= rEvent
.time
;
3539 aEvent
.mnX
= static_cast<sal_uLong
>(rEvent
.x
);
3540 aEvent
.mnY
= static_cast<sal_uLong
>(rEvent
.y
);
3541 aEvent
.mnCode
= GetMouseModCode(rEvent
.state
);
3543 switch (rEvent
.direction
)
3546 aEvent
.mnDelta
= 120;
3547 aEvent
.mnNotchDelta
= 1;
3548 aEvent
.mnScrollLines
= 3;
3549 aEvent
.mbHorz
= false;
3552 case GDK_SCROLL_DOWN
:
3553 aEvent
.mnDelta
= -120;
3554 aEvent
.mnNotchDelta
= -1;
3555 aEvent
.mnScrollLines
= 3;
3556 aEvent
.mbHorz
= false;
3559 case GDK_SCROLL_LEFT
:
3560 aEvent
.mnDelta
= 120;
3561 aEvent
.mnNotchDelta
= 1;
3562 aEvent
.mnScrollLines
= 3;
3563 aEvent
.mbHorz
= true;
3566 case GDK_SCROLL_RIGHT
:
3567 aEvent
.mnDelta
= -120;
3568 aEvent
.mnNotchDelta
= -1;
3569 aEvent
.mnScrollLines
= 3;
3570 aEvent
.mbHorz
= true;
3579 gboolean
GtkSalFrame::signalScroll(GtkWidget
*, GdkEvent
* pInEvent
, gpointer frame
)
3581 GdkEventScroll
& rEvent
= pInEvent
->scroll
;
3583 UpdateLastInputEventTime(rEvent
.time
);
3585 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3587 if (rEvent
.direction
== GDK_SCROLL_SMOOTH
)
3589 pThis
->LaunchAsyncScroll(pInEvent
);
3593 //if we have smooth scrolling previous pending states, flush that queue now
3594 if (!pThis
->m_aPendingScrollEvents
.empty())
3596 pThis
->m_aSmoothScrollIdle
.Stop();
3597 pThis
->m_aSmoothScrollIdle
.Invoke();
3598 assert(pThis
->m_aPendingScrollEvents
.empty());
3601 SalWheelMouseEvent
aEvent(GetWheelEvent(rEvent
));
3603 // --- RTL --- (mirror mouse pos)
3604 if (AllSettings::GetLayoutRTL())
3605 aEvent
.mnX
= pThis
->maGeometry
.width() - 1 - aEvent
.mnX
;
3607 pThis
->CallCallbackExc(SalEvent::WheelMouse
, &aEvent
);
3612 gboolean
GtkSalFrame::signalScroll(GtkEventControllerScroll
* pController
, double delta_x
, double delta_y
, gpointer frame
)
3614 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3616 GdkEvent
* pEvent
= gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController
));
3617 GdkModifierType eState
= gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pController
));
3619 auto nTime
= gdk_event_get_time(pEvent
);
3621 UpdateLastInputEventTime(nTime
);
3623 double nEventX(0.0), nEventY(0.0);
3624 gdk_event_get_position(pEvent
, &nEventX
, &nEventY
);
3626 pThis
->DrawingAreaScroll(delta_x
, delta_y
, nEventX
, nEventY
, nTime
, eState
);
3631 gboolean
GtkSalFrame::event_controller_scroll_forward(GtkEventControllerScroll
* pController
, double delta_x
, double delta_y
)
3633 return GtkSalFrame::signalScroll(pController
, delta_x
, delta_y
, this);
3638 void GtkSalFrame::gestureSwipe(GtkGestureSwipe
* gesture
, gdouble velocity_x
, gdouble velocity_y
, gpointer frame
)
3641 GdkEventSequence
*sequence
= gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture
));
3642 //I feel I want the first point of the sequence, not the last point which
3643 //the docs say this gives, but for the moment assume we start and end
3644 //within the same vcl window
3645 if (gtk_gesture_get_point(GTK_GESTURE(gesture
), sequence
, &x
, &y
))
3647 SalGestureSwipeEvent aEvent
;
3648 aEvent
.mnVelocityX
= velocity_x
;
3649 aEvent
.mnVelocityY
= velocity_y
;
3653 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3654 pThis
->CallCallbackExc(SalEvent::GestureSwipe
, &aEvent
);
3658 void GtkSalFrame::gestureLongPress(GtkGestureLongPress
* gesture
, gdouble x
, gdouble y
, gpointer frame
)
3660 GdkEventSequence
*sequence
= gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture
));
3661 if (gtk_gesture_get_point(GTK_GESTURE(gesture
), sequence
, &x
, &y
))
3663 SalGestureLongPressEvent aEvent
;
3667 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3668 pThis
->CallCallbackExc(SalEvent::GestureLongPress
, &aEvent
);
3672 void GtkSalFrame::DrawingAreaMotion(int nEventX
, int nEventY
, guint32 nTime
, guint nState
)
3674 UpdateLastInputEventTime(nTime
);
3676 SalMouseEvent aEvent
;
3677 aEvent
.mnTime
= nTime
;
3678 aEvent
.mnX
= nEventX
;
3679 aEvent
.mnY
= nEventY
;
3680 aEvent
.mnCode
= GetMouseModCode(nState
);
3681 aEvent
.mnButton
= 0;
3683 if( AllSettings::GetLayoutRTL() )
3684 aEvent
.mnX
= maGeometry
.width() - 1 - aEvent
.mnX
;
3686 CallCallbackExc(SalEvent::MouseMove
, &aEvent
);
3689 #if GTK_CHECK_VERSION(4, 0, 0)
3690 void GtkSalFrame::signalMotion(GtkEventControllerMotion
*pController
, double x
, double y
, gpointer frame
)
3692 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3693 GdkEvent
* pEvent
= gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController
));
3694 GdkModifierType eType
= gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pController
));
3695 pThis
->DrawingAreaMotion(x
, y
, gdk_event_get_time(pEvent
), eType
);
3698 gboolean
GtkSalFrame::signalMotion( GtkWidget
*, GdkEventMotion
* pEvent
, gpointer frame
)
3700 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3701 GtkWidget
* pEventWidget
= pThis
->getMouseEventWidget();
3702 bool bDifferentEventWindow
= pEvent
->window
!= widget_get_surface(pEventWidget
);
3704 //If a menu, e.g. font name dropdown, is open, then under wayland moving the
3705 //mouse in the top left corner of the toplevel window in a
3706 //0,0,float-width,float-height area generates motion events which are
3707 //delivered to the dropdown
3708 if (pThis
->isFloatGrabWindow() && bDifferentEventWindow
)
3711 vcl::DeletionListener
aDel( pThis
);
3713 int nEventX
= pEvent
->x
;
3714 int nEventY
= pEvent
->y
;
3716 if (bDifferentEventWindow
)
3717 translate_coords(pEvent
->window
, pEventWidget
, nEventX
, nEventY
);
3719 pThis
->UpdateGeometryFromEvent(pEvent
->x_root
, pEvent
->y_root
, nEventX
, nEventY
);
3721 if (!aDel
.isDeleted())
3722 pThis
->DrawingAreaMotion(nEventX
, nEventY
, pEvent
->time
, pEvent
->state
);
3724 if (!aDel
.isDeleted())
3726 // ask for the next hint
3728 GdkModifierType mask
;
3729 gdk_window_get_pointer( widget_get_surface(GTK_WIDGET(pThis
->m_pWindow
)), &x
, &y
, &mask
);
3736 void GtkSalFrame::DrawingAreaCrossing(SalEvent nEventType
, int nEventX
, int nEventY
, guint32 nTime
, guint nState
)
3738 UpdateLastInputEventTime(nTime
);
3740 SalMouseEvent aEvent
;
3741 aEvent
.mnTime
= nTime
;
3742 aEvent
.mnX
= nEventX
;
3743 aEvent
.mnY
= nEventY
;
3744 aEvent
.mnCode
= GetMouseModCode(nState
);
3745 aEvent
.mnButton
= 0;
3747 if (AllSettings::GetLayoutRTL())
3748 aEvent
.mnX
= maGeometry
.width()-1-aEvent
.mnX
;
3750 CallCallbackExc(nEventType
, &aEvent
);
3753 #if GTK_CHECK_VERSION(4, 0, 0)
3754 void GtkSalFrame::signalEnter(GtkEventControllerMotion
*pController
, double x
, double y
, gpointer frame
)
3756 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3757 GdkEvent
* pEvent
= gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController
));
3758 GdkModifierType eType
= gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pController
));
3759 pThis
->DrawingAreaCrossing(SalEvent::MouseMove
, x
, y
, pEvent
? gdk_event_get_time(pEvent
) : GDK_CURRENT_TIME
, eType
);
3762 void GtkSalFrame::signalLeave(GtkEventControllerMotion
*pController
, gpointer frame
)
3764 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3765 GdkEvent
* pEvent
= gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController
));
3766 GdkModifierType eType
= gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pController
));
3767 pThis
->DrawingAreaCrossing(SalEvent::MouseLeave
, -1, -1, pEvent
? gdk_event_get_time(pEvent
) : GDK_CURRENT_TIME
, eType
);
3770 gboolean
GtkSalFrame::signalCrossing( GtkWidget
*, GdkEventCrossing
* pEvent
, gpointer frame
)
3772 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3773 pThis
->DrawingAreaCrossing((pEvent
->type
== GDK_ENTER_NOTIFY
) ? SalEvent::MouseMove
: SalEvent::MouseLeave
,
3782 cairo_t
* GtkSalFrame::getCairoContext() const
3784 cairo_t
* cr
= cairo_create(m_pSurface
);
3789 void GtkSalFrame::damaged(sal_Int32 nExtentsX
, sal_Int32 nExtentsY
,
3790 sal_Int32 nExtentsWidth
, sal_Int32 nExtentsHeight
) const
3792 #if OSL_DEBUG_LEVEL > 0
3796 OString
tmp("/tmp/frame" + OString::number(frame
++) + ".png");
3797 cairo_t
* cr
= getCairoContext();
3798 cairo_surface_write_to_png(cairo_get_target(cr
), tmp
.getStr());
3803 // quite a bit of noise in RTL mode with negative widths
3804 if (nExtentsWidth
<= 0 || nExtentsHeight
<= 0)
3807 #if !GTK_CHECK_VERSION(4, 0, 0)
3808 gtk_widget_queue_draw_area(GTK_WIDGET(m_pDrawingArea
),
3809 nExtentsX
, nExtentsY
,
3810 nExtentsWidth
, nExtentsHeight
);
3812 gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea
));
3818 // blit our backing cairo surface to the target cairo context
3819 void GtkSalFrame::DrawingAreaDraw(cairo_t
*cr
)
3821 cairo_set_source_surface(cr
, m_pSurface
, 0, 0);
3825 #if !GTK_CHECK_VERSION(4, 0, 0)
3826 gboolean
GtkSalFrame::signalDraw(GtkWidget
*, cairo_t
*cr
, gpointer frame
)
3828 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3829 pThis
->DrawingAreaDraw(cr
);
3833 void GtkSalFrame::signalDraw(GtkDrawingArea
*, cairo_t
*cr
, int /*width*/, int /*height*/, gpointer frame
)
3835 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3836 pThis
->DrawingAreaDraw(cr
);
3840 void GtkSalFrame::DrawingAreaResized(GtkWidget
* pWidget
, int nWidth
, int nHeight
)
3842 // ignore size-allocations that occur during configuring an embedded SalObject
3843 if (m_bSalObjectSetPosSize
)
3845 maGeometry
.setSize({ nWidth
, nHeight
});
3846 bool bRealized
= gtk_widget_get_realized(pWidget
);
3849 CallCallbackExc( SalEvent::Resize
, nullptr );
3851 TriggerPaintEvent();
3854 #if !GTK_CHECK_VERSION(4, 0, 0)
3855 void GtkSalFrame::sizeAllocated(GtkWidget
* pWidget
, GdkRectangle
*pAllocation
, gpointer frame
)
3857 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3858 pThis
->DrawingAreaResized(pWidget
, pAllocation
->width
, pAllocation
->height
);
3861 void GtkSalFrame::sizeAllocated(GtkWidget
* pWidget
, int nWidth
, int nHeight
, gpointer frame
)
3863 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3864 pThis
->DrawingAreaResized(pWidget
, nWidth
, nHeight
);
3868 #if !GTK_CHECK_VERSION(4, 0, 0)
3871 void swapDirection(GdkGravity
& gravity
)
3873 if (gravity
== GDK_GRAVITY_NORTH_WEST
)
3874 gravity
= GDK_GRAVITY_NORTH_EAST
;
3875 else if (gravity
== GDK_GRAVITY_NORTH_EAST
)
3876 gravity
= GDK_GRAVITY_NORTH_WEST
;
3877 else if (gravity
== GDK_GRAVITY_SOUTH_WEST
)
3878 gravity
= GDK_GRAVITY_SOUTH_EAST
;
3879 else if (gravity
== GDK_GRAVITY_SOUTH_EAST
)
3880 gravity
= GDK_GRAVITY_SOUTH_WEST
;
3886 void GtkSalFrame::signalRealize(GtkWidget
*, gpointer frame
)
3888 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3889 pThis
->AllocateFrame();
3890 if (pThis
->m_bSalObjectSetPosSize
)
3892 pThis
->TriggerPaintEvent();
3894 #if !GTK_CHECK_VERSION(4, 0, 0)
3895 if (!pThis
->m_bFloatPositioned
)
3898 static auto window_move_to_rect
= reinterpret_cast<void (*) (GdkWindow
*, const GdkRectangle
*, GdkGravity
,
3899 GdkGravity
, GdkAnchorHints
, gint
, gint
)>(
3900 dlsym(nullptr, "gdk_window_move_to_rect"));
3901 if (!window_move_to_rect
)
3904 GdkGravity rect_anchor
= GDK_GRAVITY_SOUTH_WEST
, menu_anchor
= GDK_GRAVITY_NORTH_WEST
;
3906 if (pThis
->m_nFloatFlags
& FloatWinPopupFlags::Left
)
3908 rect_anchor
= GDK_GRAVITY_NORTH_WEST
;
3909 menu_anchor
= GDK_GRAVITY_NORTH_EAST
;
3911 else if (pThis
->m_nFloatFlags
& FloatWinPopupFlags::Up
)
3913 rect_anchor
= GDK_GRAVITY_NORTH_WEST
;
3914 menu_anchor
= GDK_GRAVITY_SOUTH_WEST
;
3916 else if (pThis
->m_nFloatFlags
& FloatWinPopupFlags::Right
)
3918 rect_anchor
= GDK_GRAVITY_NORTH_EAST
;
3921 VclPtr
<vcl::Window
> pVclParent
= pThis
->GetWindow()->GetParent();
3922 if (pVclParent
->GetOutDev()->HasMirroredGraphics() && pVclParent
->IsRTLEnabled())
3924 swapDirection(rect_anchor
);
3925 swapDirection(menu_anchor
);
3928 tools::Rectangle aFloatRect
= FloatingWindow::ImplConvertToAbsPos(pVclParent
, pThis
->m_aFloatRect
);
3929 if (gdk_window_get_window_type(widget_get_surface(pThis
->m_pParent
->m_pWindow
)) != GDK_WINDOW_TOPLEVEL
)
3931 // See tdf#152155 for an example
3932 gtk_coord
nX(0), nY(0.0);
3933 gtk_widget_translate_coordinates(pThis
->m_pParent
->m_pWindow
, widget_get_toplevel(pThis
->m_pParent
->m_pWindow
), 0, 0, &nX
, &nY
);
3934 aFloatRect
.Move(nX
, nY
);
3937 GdkRectangle rect
{static_cast<int>(aFloatRect
.Left()), static_cast<int>(aFloatRect
.Top()),
3938 static_cast<int>(aFloatRect
.GetWidth()), static_cast<int>(aFloatRect
.GetHeight())};
3940 GdkSurface
* gdkWindow
= widget_get_surface(pThis
->m_pWindow
);
3941 window_move_to_rect(gdkWindow
, &rect
, rect_anchor
, menu_anchor
, static_cast<GdkAnchorHints
>(GDK_ANCHOR_FLIP
| GDK_ANCHOR_SLIDE
), 0, 0);
3945 #if !GTK_CHECK_VERSION(4, 0, 0)
3946 gboolean
GtkSalFrame::signalConfigure(GtkWidget
*, GdkEventConfigure
* pEvent
, gpointer frame
)
3948 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3950 bool bMoved
= false;
3951 int x
= pEvent
->x
, y
= pEvent
->y
;
3953 /* #i31785# claims we cannot trust the x,y members of the event;
3954 * they are e.g. not set correctly on maximize/demaximize;
3955 * yet the gdkdisplay-x11.c code handling configure_events has
3956 * done this XTranslateCoordinates work since the day ~zero.
3958 if (pThis
->m_bGeometryIsProvisional
|| x
!= pThis
->maGeometry
.x() || y
!= pThis
->maGeometry
.y() )
3961 pThis
->m_bGeometryIsProvisional
= false;
3962 pThis
->maGeometry
.setPos({ x
, y
});
3965 // update decoration hints
3967 gdk_window_get_frame_extents( widget_get_surface(GTK_WIDGET(pThis
->m_pWindow
)), &aRect
);
3968 pThis
->maGeometry
.setTopDecoration(y
- aRect
.y
);
3969 pThis
->maGeometry
.setBottomDecoration(aRect
.y
+ aRect
.height
- y
- pEvent
->height
);
3970 pThis
->maGeometry
.setLeftDecoration(x
- aRect
.x
);
3971 pThis
->maGeometry
.setRightDecoration(aRect
.x
+ aRect
.width
- x
- pEvent
->width
);
3972 pThis
->updateScreenNumber();
3976 ImplSVData
* pSVData
= ImplGetSVData();
3977 if (pSVData
->maNWFData
.mbCanDetermineWindowPosition
)
3978 pThis
->CallCallbackExc(SalEvent::Move
, nullptr);
3985 void GtkSalFrame::queue_draw()
3987 gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea
));
3990 void GtkSalFrame::TriggerPaintEvent()
3992 //Under gtk2 we can basically paint directly into the XWindow and on
3993 //additional "expose-event" events we can re-render the missing pieces
3995 //Under gtk3 we have to keep our own buffer up to date and flush it into
3996 //the given cairo context on "draw". So we emit a paint event on
3997 //opportune resize trigger events to initially fill our backbuffer and then
3998 //keep it up to date with our direct paints and tell gtk those regions
3999 //have changed and then blit them into the provided cairo context when
4002 //The other alternative was to always paint everything on "draw", but
4003 //that duplicates the amount of drawing and is hideously slow
4004 SAL_INFO("vcl.gtk3", "force painting" << 0 << "," << 0 << " " << maGeometry
.width() << "x" << maGeometry
.height());
4005 SalPaintEvent
aPaintEvt(0, 0, maGeometry
.width(), maGeometry
.height(), true);
4006 CallCallbackExc(SalEvent::Paint
, &aPaintEvt
);
4010 void GtkSalFrame::DrawingAreaFocusInOut(SalEvent nEventType
)
4012 SalGenericInstance
* pSalInstance
= GetGenericInstance();
4014 // check if printers have changed (analogous to salframe focus handler)
4015 pSalInstance
->updatePrinterUpdate();
4017 if (nEventType
== SalEvent::LoseFocus
)
4018 m_nKeyModifiers
= ModKeyFlags::NONE
;
4022 bool bFocusInAnotherGtkWidget
= false;
4023 if (GTK_IS_WINDOW(m_pWindow
))
4025 GtkWidget
* pFocusWindow
= gtk_window_get_focus(GTK_WINDOW(m_pWindow
));
4026 bFocusInAnotherGtkWidget
= pFocusWindow
&& pFocusWindow
!= GTK_WIDGET(m_pFixedContainer
);
4028 if (!bFocusInAnotherGtkWidget
)
4029 m_pIMHandler
->focusChanged(nEventType
== SalEvent::GetFocus
);
4032 // ask for changed printers like generic implementation
4033 if (nEventType
== SalEvent::GetFocus
&& pSalInstance
->isPrinterInit())
4034 pSalInstance
->updatePrinterUpdate();
4036 CallCallbackExc(nEventType
, nullptr);
4039 #if !GTK_CHECK_VERSION(4, 0, 0)
4040 gboolean
GtkSalFrame::signalFocus( GtkWidget
*, GdkEventFocus
* pEvent
, gpointer frame
)
4042 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4044 SalGenericInstance
*pSalInstance
= GetGenericInstance();
4046 // check if printers have changed (analogous to salframe focus handler)
4047 pSalInstance
->updatePrinterUpdate();
4050 pThis
->m_nKeyModifiers
= ModKeyFlags::NONE
;
4052 if( pThis
->m_pIMHandler
)
4054 bool bFocusInAnotherGtkWidget
= false;
4055 if (GTK_IS_WINDOW(pThis
->m_pWindow
))
4057 GtkWidget
* pFocusWindow
= gtk_window_get_focus(GTK_WINDOW(pThis
->m_pWindow
));
4058 bFocusInAnotherGtkWidget
= pFocusWindow
&& pFocusWindow
!= GTK_WIDGET(pThis
->m_pFixedContainer
);
4060 if (!bFocusInAnotherGtkWidget
)
4061 pThis
->m_pIMHandler
->focusChanged( pEvent
->in
!= 0 );
4064 // ask for changed printers like generic implementation
4065 if( pEvent
->in
&& pSalInstance
->isPrinterInit() )
4066 pSalInstance
->updatePrinterUpdate();
4068 // FIXME: find out who the hell steals the focus from our frame
4069 // while we have the pointer grabbed, this should not come from
4070 // the window manager. Is this an event that was still queued ?
4071 // The focus does not seem to get set inside our process
4072 // in the meantime do not propagate focus get/lose if floats are open
4073 if( m_nFloats
== 0 )
4075 GtkWidget
* pGrabWidget
;
4076 if (GTK_IS_EVENT_BOX(pThis
->m_pWindow
))
4077 pGrabWidget
= GTK_WIDGET(pThis
->m_pWindow
);
4079 pGrabWidget
= GTK_WIDGET(pThis
->m_pFixedContainer
);
4080 bool bHasFocus
= gtk_widget_has_focus(pGrabWidget
);
4081 pThis
->CallCallbackExc(bHasFocus
? SalEvent::GetFocus
: SalEvent::LoseFocus
, nullptr);
4087 void GtkSalFrame::signalFocusEnter(GtkEventControllerFocus
*, gpointer frame
)
4089 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4090 pThis
->DrawingAreaFocusInOut(SalEvent::GetFocus
);
4093 void GtkSalFrame::signalFocusLeave(GtkEventControllerFocus
*, gpointer frame
)
4095 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4096 pThis
->DrawingAreaFocusInOut(SalEvent::LoseFocus
);
4100 // change of focus between native widgets within the toplevel
4101 #if !GTK_CHECK_VERSION(4, 0, 0)
4102 void GtkSalFrame::signalSetFocus(GtkWindow
*, GtkWidget
* pWidget
, gpointer frame
)
4104 void GtkSalFrame::signalSetFocus(GtkWindow
*, GParamSpec
*, gpointer frame
)
4107 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4109 GtkWidget
* pGrabWidget
= GTK_WIDGET(pThis
->m_pFixedContainer
);
4111 GtkWidget
* pTopLevel
= widget_get_toplevel(pGrabWidget
);
4112 // see commentary in GtkSalObjectWidgetClip::Show
4113 if (pTopLevel
&& g_object_get_data(G_OBJECT(pTopLevel
), "g-lo-BlockFocusChange"))
4116 #if GTK_CHECK_VERSION(4, 0, 0)
4117 GtkWidget
* pWidget
= gtk_window_get_focus(GTK_WINDOW(pThis
->m_pWindow
));
4120 // tdf#129634 interpret losing focus as focus passing explicitly to another widget
4121 bool bLoseFocus
= pWidget
&& pWidget
!= pGrabWidget
;
4123 // do not propagate focus get/lose if floats are open
4124 pThis
->CallCallbackExc(bLoseFocus
? SalEvent::LoseFocus
: SalEvent::GetFocus
, nullptr);
4126 #if !GTK_CHECK_VERSION(4, 0, 0)
4127 gtk_widget_set_can_focus(GTK_WIDGET(pThis
->m_pFixedContainer
), !bLoseFocus
);
4131 void GtkSalFrame::WindowMap()
4133 if (m_bIconSetWhileUnmapped
)
4134 SetIcon(gtk_window_get_icon_name(GTK_WINDOW(m_pWindow
)));
4136 CallCallbackExc( SalEvent::Resize
, nullptr );
4137 TriggerPaintEvent();
4140 void GtkSalFrame::WindowUnmap()
4142 CallCallbackExc( SalEvent::Resize
, nullptr );
4144 if (m_bFloatPositioned
)
4146 // Unrealize is needed for cases where we reuse the same popup
4147 // (e.g. the font name control), making the realize signal fire
4148 // again on next show.
4149 gtk_widget_unrealize(m_pWindow
);
4150 m_bFloatPositioned
= false;
4154 #if GTK_CHECK_VERSION(4, 0, 0)
4155 void GtkSalFrame::signalMap(GtkWidget
*, gpointer frame
)
4157 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4161 void GtkSalFrame::signalUnmap(GtkWidget
*, gpointer frame
)
4163 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4164 pThis
->WindowUnmap();
4167 gboolean
GtkSalFrame::signalMap(GtkWidget
*, GdkEvent
*, gpointer frame
)
4169 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4174 gboolean
GtkSalFrame::signalUnmap(GtkWidget
*, GdkEvent
*, gpointer frame
)
4176 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4177 pThis
->WindowUnmap();
4182 #if !GTK_CHECK_VERSION(4, 0, 0)
4184 static bool key_forward(GdkEventKey
* pEvent
, GtkWindow
* pDest
)
4186 gpointer pClass
= g_type_class_ref(GTK_TYPE_WINDOW
);
4187 GtkWidgetClass
* pWindowClass
= GTK_WIDGET_CLASS(pClass
);
4188 bool bHandled
= pEvent
->type
== GDK_KEY_PRESS
4189 ? pWindowClass
->key_press_event(GTK_WIDGET(pDest
), pEvent
)
4190 : pWindowClass
->key_release_event(GTK_WIDGET(pDest
), pEvent
);
4191 g_type_class_unref(pClass
);
4195 static bool activate_menubar_mnemonic(GtkWidget
* pWidget
, guint nKeyval
)
4197 const char* pLabel
= gtk_menu_item_get_label(GTK_MENU_ITEM(pWidget
));
4198 gunichar cAccelChar
= 0;
4199 if (!pango_parse_markup(pLabel
, -1, '_', nullptr, nullptr, &cAccelChar
, nullptr))
4203 auto nMnemonicKeyval
= gdk_keyval_to_lower(gdk_unicode_to_keyval(cAccelChar
));
4204 if (nKeyval
== nMnemonicKeyval
)
4205 return gtk_widget_mnemonic_activate(pWidget
, false);
4209 bool GtkSalFrame::HandleMenubarMnemonic(guint eState
, guint nKeyval
)
4211 bool bUsedInMenuBar
= false;
4212 if (eState
& GDK_ALT_MASK
)
4214 if (GtkWidget
* pMenuBar
= m_pSalMenu
? m_pSalMenu
->GetMenuBarWidget() : nullptr)
4216 GList
* pChildren
= gtk_container_get_children(GTK_CONTAINER(pMenuBar
));
4217 for (GList
* pChild
= g_list_first(pChildren
); pChild
; pChild
= g_list_next(pChild
))
4219 bUsedInMenuBar
= activate_menubar_mnemonic(static_cast<GtkWidget
*>(pChild
->data
), nKeyval
);
4223 g_list_free(pChildren
);
4226 return bUsedInMenuBar
;
4229 gboolean
GtkSalFrame::signalKey(GtkWidget
* pWidget
, GdkEventKey
* pEvent
, gpointer frame
)
4231 UpdateLastInputEventTime(pEvent
->time
);
4233 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4235 bool bFocusInAnotherGtkWidget
= false;
4237 VclPtr
<vcl::Window
> xTopLevelInterimWindow
;
4239 if (GTK_IS_WINDOW(pThis
->m_pWindow
))
4241 GtkWidget
* pFocusWindow
= gtk_window_get_focus(GTK_WINDOW(pThis
->m_pWindow
));
4242 bFocusInAnotherGtkWidget
= pFocusWindow
&& pFocusWindow
!= GTK_WIDGET(pThis
->m_pFixedContainer
);
4243 if (bFocusInAnotherGtkWidget
)
4245 if (!gtk_widget_get_realized(pFocusWindow
))
4248 // if the focus is not in our main widget, see if there is a handler
4249 // for this key stroke in GtkWindow first
4250 if (key_forward(pEvent
, GTK_WINDOW(pThis
->m_pWindow
)))
4253 // Is focus inside an InterimItemWindow? In which case find that
4254 // InterimItemWindow and send unconsumed keystrokes to it to
4255 // support ctrl-q etc shortcuts. Only bother to search for the
4256 // InterimItemWindow if it is a toplevel that fills its frame, or
4257 // the keystroke is sufficiently special its worth passing on,
4258 // e.g. F6 to switch between task-panels or F5 to close a navigator
4259 if (pThis
->IsCycleFocusOutDisallowed() || IsFunctionKeyVal(pEvent
->keyval
))
4261 GtkWidget
* pSearch
= pFocusWindow
;
4264 void* pData
= g_object_get_data(G_OBJECT(pSearch
), "InterimWindowGlue");
4267 xTopLevelInterimWindow
= static_cast<vcl::Window
*>(pData
);
4270 pSearch
= gtk_widget_get_parent(pSearch
);
4276 if (pThis
->isFloatGrabWindow())
4277 return signalKey(pWidget
, pEvent
, pThis
->m_pParent
);
4279 vcl::DeletionListener
aDel( pThis
);
4281 if (!bFocusInAnotherGtkWidget
&& pThis
->m_pIMHandler
&& pThis
->m_pIMHandler
->handleKeyEvent(pEvent
))
4284 bool bStopProcessingKey
= false;
4287 if( pEvent
->keyval
== GDK_KEY_Shift_L
|| pEvent
->keyval
== GDK_KEY_Shift_R
||
4288 pEvent
->keyval
== GDK_KEY_Control_L
|| pEvent
->keyval
== GDK_KEY_Control_R
||
4289 pEvent
->keyval
== GDK_KEY_Alt_L
|| pEvent
->keyval
== GDK_KEY_Alt_R
||
4290 pEvent
->keyval
== GDK_KEY_Meta_L
|| pEvent
->keyval
== GDK_KEY_Meta_R
||
4291 pEvent
->keyval
== GDK_KEY_Super_L
|| pEvent
->keyval
== GDK_KEY_Super_R
)
4293 sal_uInt16 nModCode
= GetKeyModCode( pEvent
->state
);
4294 ModKeyFlags nExtModMask
= ModKeyFlags::NONE
;
4295 sal_uInt16 nModMask
= 0;
4296 // pressing just the ctrl key leads to a keysym of XK_Control but
4297 // the event state does not contain ControlMask. In the release
4298 // event it's the other way round: it does contain the Control mask.
4299 // The modifier mode therefore has to be adapted manually.
4300 switch( pEvent
->keyval
)
4302 case GDK_KEY_Control_L
:
4303 nExtModMask
= ModKeyFlags::LeftMod1
;
4304 nModMask
= KEY_MOD1
;
4306 case GDK_KEY_Control_R
:
4307 nExtModMask
= ModKeyFlags::RightMod1
;
4308 nModMask
= KEY_MOD1
;
4311 nExtModMask
= ModKeyFlags::LeftMod2
;
4312 nModMask
= KEY_MOD2
;
4315 nExtModMask
= ModKeyFlags::RightMod2
;
4316 nModMask
= KEY_MOD2
;
4318 case GDK_KEY_Shift_L
:
4319 nExtModMask
= ModKeyFlags::LeftShift
;
4320 nModMask
= KEY_SHIFT
;
4322 case GDK_KEY_Shift_R
:
4323 nExtModMask
= ModKeyFlags::RightShift
;
4324 nModMask
= KEY_SHIFT
;
4326 // Map Meta/Super to MOD3 modifier on all Unix systems
4328 case GDK_KEY_Meta_L
:
4329 case GDK_KEY_Super_L
:
4330 nExtModMask
= ModKeyFlags::LeftMod3
;
4331 nModMask
= KEY_MOD3
;
4333 case GDK_KEY_Meta_R
:
4334 case GDK_KEY_Super_R
:
4335 nExtModMask
= ModKeyFlags::RightMod3
;
4336 nModMask
= KEY_MOD3
;
4340 SalKeyModEvent aModEvt
;
4341 aModEvt
.mbDown
= pEvent
->type
== GDK_KEY_PRESS
;
4343 if( pEvent
->type
== GDK_KEY_RELEASE
)
4345 aModEvt
.mnModKeyCode
= pThis
->m_nKeyModifiers
;
4346 aModEvt
.mnCode
= nModCode
& ~nModMask
;
4347 pThis
->m_nKeyModifiers
&= ~nExtModMask
;
4351 aModEvt
.mnCode
= nModCode
| nModMask
;
4352 pThis
->m_nKeyModifiers
|= nExtModMask
;
4353 aModEvt
.mnModKeyCode
= pThis
->m_nKeyModifiers
;
4356 pThis
->CallCallbackExc( SalEvent::KeyModChange
, &aModEvt
);
4360 bool bRestoreDisallowCycleFocusOut
= false;
4362 VclPtr
<vcl::Window
> xOrigFrameFocusWin
;
4363 VclPtr
<vcl::Window
> xOrigFocusWin
;
4364 if (xTopLevelInterimWindow
)
4366 // Focus is inside an InterimItemWindow so send unconsumed
4367 // keystrokes to by setting it as the mpFocusWin
4368 VclPtr
<vcl::Window
> xVclWindow
= pThis
->GetWindow();
4369 ImplFrameData
* pFrameData
= xVclWindow
->ImplGetWindowImpl()->mpFrameData
;
4370 xOrigFrameFocusWin
= pFrameData
->mpFocusWin
;
4371 pFrameData
->mpFocusWin
= xTopLevelInterimWindow
;
4373 ImplSVData
* pSVData
= ImplGetSVData();
4374 xOrigFocusWin
= pSVData
->mpWinData
->mpFocusWin
;
4375 pSVData
->mpWinData
->mpFocusWin
= xTopLevelInterimWindow
;
4377 if (pEvent
->keyval
== GDK_KEY_F6
&& pThis
->IsCycleFocusOutDisallowed())
4379 // For F6, allow the focus to leave the InterimItemWindow
4380 pThis
->AllowCycleFocusOut();
4381 bRestoreDisallowCycleFocusOut
= true;
4385 bStopProcessingKey
= pThis
->doKeyCallback(pEvent
->state
,
4387 pEvent
->hardware_keycode
,
4389 sal_Unicode(gdk_keyval_to_unicode( pEvent
->keyval
)),
4390 (pEvent
->type
== GDK_KEY_PRESS
),
4393 // tdf#144846 If this is registered as a menubar mnemonic then ensure
4394 // that any other widget won't be considered as a candidate by taking
4395 // over the task of launch the menubar menu outself
4396 // The code was moved here from its original position at beginning
4397 // of this function in order to resolve tdf#146174.
4398 if (!bStopProcessingKey
&& // module key handler did not process key
4399 pEvent
->type
== GDK_KEY_PRESS
&& // module key handler handles only GDK_KEY_PRESS
4400 GTK_IS_WINDOW(pThis
->m_pWindow
) &&
4401 pThis
->HandleMenubarMnemonic(pEvent
->state
, pEvent
->keyval
))
4406 if (!aDel
.isDeleted())
4408 pThis
->m_nKeyModifiers
= ModKeyFlags::NONE
;
4410 if (xTopLevelInterimWindow
)
4412 // Focus was inside an InterimItemWindow, restore the original
4413 // focus win, unless the focus was changed away from the
4414 // InterimItemWindow which should only be possible with F6
4415 VclPtr
<vcl::Window
> xVclWindow
= pThis
->GetWindow();
4416 ImplFrameData
* pFrameData
= xVclWindow
->ImplGetWindowImpl()->mpFrameData
;
4417 if (pFrameData
->mpFocusWin
== xTopLevelInterimWindow
)
4418 pFrameData
->mpFocusWin
= xOrigFrameFocusWin
;
4420 ImplSVData
* pSVData
= ImplGetSVData();
4421 if (pSVData
->mpWinData
->mpFocusWin
== xTopLevelInterimWindow
)
4422 pSVData
->mpWinData
->mpFocusWin
= xOrigFocusWin
;
4424 if (bRestoreDisallowCycleFocusOut
)
4426 // undo the above AllowCycleFocusOut for F6
4427 pThis
->DisallowCycleFocusOut();
4434 if (!bFocusInAnotherGtkWidget
&& !aDel
.isDeleted() && pThis
->m_pIMHandler
)
4435 pThis
->m_pIMHandler
->updateIMSpotLocation();
4437 return bStopProcessingKey
;
4441 bool GtkSalFrame::DrawingAreaKey(GtkEventControllerKey
* pController
, SalEvent nEventType
, guint keyval
, guint keycode
, guint state
)
4443 guint32 nTime
= gdk_event_get_time(gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController
)));
4444 UpdateLastInputEventTime(nTime
);
4446 bool bFocusInAnotherGtkWidget
= false;
4448 VclPtr
<vcl::Window
> xTopLevelInterimWindow
;
4450 if (GTK_IS_WINDOW(m_pWindow
))
4452 GtkWidget
* pFocusWindow
= gtk_window_get_focus(GTK_WINDOW(m_pWindow
));
4453 bFocusInAnotherGtkWidget
= pFocusWindow
&& pFocusWindow
!= GTK_WIDGET(m_pFixedContainer
);
4454 if (bFocusInAnotherGtkWidget
)
4456 if (!gtk_widget_get_realized(pFocusWindow
))
4458 // if the focus is not in our main widget, see if there is a handler
4459 // for this key stroke in GtkWindow first
4460 bool bHandled
= gtk_event_controller_key_forward(pController
, m_pWindow
);
4464 // Is focus inside an InterimItemWindow? In which case find that
4465 // InterimItemWindow and send unconsumed keystrokes to it to
4466 // support ctrl-q etc shortcuts. Only bother to search for the
4467 // InterimItemWindow if it is a toplevel that fills its frame, or
4468 // the keystroke is sufficiently special its worth passing on,
4469 // e.g. F6 to switch between task-panels or F5 to close a navigator
4470 if (IsCycleFocusOutDisallowed() || IsFunctionKeyVal(keyval
))
4472 GtkWidget
* pSearch
= pFocusWindow
;
4475 void* pData
= g_object_get_data(G_OBJECT(pSearch
), "InterimWindowGlue");
4478 xTopLevelInterimWindow
= static_cast<vcl::Window
*>(pData
);
4481 pSearch
= gtk_widget_get_parent(pSearch
);
4487 vcl::DeletionListener
aDel(this);
4489 bool bStopProcessingKey
= false;
4492 if( keyval
== GDK_KEY_Shift_L
|| keyval
== GDK_KEY_Shift_R
||
4493 keyval
== GDK_KEY_Control_L
|| keyval
== GDK_KEY_Control_R
||
4494 keyval
== GDK_KEY_Alt_L
|| keyval
== GDK_KEY_Alt_R
||
4495 keyval
== GDK_KEY_Meta_L
|| keyval
== GDK_KEY_Meta_R
||
4496 keyval
== GDK_KEY_Super_L
|| keyval
== GDK_KEY_Super_R
)
4498 sal_uInt16 nModCode
= GetKeyModCode(state
);
4499 ModKeyFlags nExtModMask
= ModKeyFlags::NONE
;
4500 sal_uInt16 nModMask
= 0;
4501 // pressing just the ctrl key leads to a keysym of XK_Control but
4502 // the event state does not contain ControlMask. In the release
4503 // event it's the other way round: it does contain the Control mask.
4504 // The modifier mode therefore has to be adapted manually.
4507 case GDK_KEY_Control_L
:
4508 nExtModMask
= ModKeyFlags::LeftMod1
;
4509 nModMask
= KEY_MOD1
;
4511 case GDK_KEY_Control_R
:
4512 nExtModMask
= ModKeyFlags::RightMod1
;
4513 nModMask
= KEY_MOD1
;
4516 nExtModMask
= ModKeyFlags::LeftMod2
;
4517 nModMask
= KEY_MOD2
;
4520 nExtModMask
= ModKeyFlags::RightMod2
;
4521 nModMask
= KEY_MOD2
;
4523 case GDK_KEY_Shift_L
:
4524 nExtModMask
= ModKeyFlags::LeftShift
;
4525 nModMask
= KEY_SHIFT
;
4527 case GDK_KEY_Shift_R
:
4528 nExtModMask
= ModKeyFlags::RightShift
;
4529 nModMask
= KEY_SHIFT
;
4531 // Map Meta/Super to MOD3 modifier on all Unix systems
4533 case GDK_KEY_Meta_L
:
4534 case GDK_KEY_Super_L
:
4535 nExtModMask
= ModKeyFlags::LeftMod3
;
4536 nModMask
= KEY_MOD3
;
4538 case GDK_KEY_Meta_R
:
4539 case GDK_KEY_Super_R
:
4540 nExtModMask
= ModKeyFlags::RightMod3
;
4541 nModMask
= KEY_MOD3
;
4545 SalKeyModEvent aModEvt
;
4546 aModEvt
.mbDown
= nEventType
== SalEvent::KeyInput
;
4548 if (!aModEvt
.mbDown
)
4550 aModEvt
.mnModKeyCode
= m_nKeyModifiers
;
4551 aModEvt
.mnCode
= nModCode
& ~nModMask
;
4552 m_nKeyModifiers
&= ~nExtModMask
;
4556 aModEvt
.mnCode
= nModCode
| nModMask
;
4557 m_nKeyModifiers
|= nExtModMask
;
4558 aModEvt
.mnModKeyCode
= m_nKeyModifiers
;
4561 CallCallbackExc(SalEvent::KeyModChange
, &aModEvt
);
4565 bool bRestoreDisallowCycleFocusOut
= false;
4567 VclPtr
<vcl::Window
> xOrigFrameFocusWin
;
4568 VclPtr
<vcl::Window
> xOrigFocusWin
;
4569 if (xTopLevelInterimWindow
)
4571 // Focus is inside an InterimItemWindow so send unconsumed
4572 // keystrokes to by setting it as the mpFocusWin
4573 VclPtr
<vcl::Window
> xVclWindow
= GetWindow();
4574 ImplFrameData
* pFrameData
= xVclWindow
->ImplGetWindowImpl()->mpFrameData
;
4575 xOrigFrameFocusWin
= pFrameData
->mpFocusWin
;
4576 pFrameData
->mpFocusWin
= xTopLevelInterimWindow
;
4578 ImplSVData
* pSVData
= ImplGetSVData();
4579 xOrigFocusWin
= pSVData
->mpWinData
->mpFocusWin
;
4580 pSVData
->mpWinData
->mpFocusWin
= xTopLevelInterimWindow
;
4582 if (keyval
== GDK_KEY_F6
&& IsCycleFocusOutDisallowed())
4584 // For F6, allow the focus to leave the InterimItemWindow
4585 AllowCycleFocusOut();
4586 bRestoreDisallowCycleFocusOut
= true;
4591 bStopProcessingKey
= doKeyCallback(state
,
4595 sal_Unicode(gdk_keyval_to_unicode(keyval
)),
4596 nEventType
== SalEvent::KeyInput
,
4599 if (!aDel
.isDeleted())
4601 m_nKeyModifiers
= ModKeyFlags::NONE
;
4603 if (xTopLevelInterimWindow
)
4605 // Focus was inside an InterimItemWindow, restore the original
4606 // focus win, unless the focus was changed away from the
4607 // InterimItemWindow which should only be possible with F6
4608 VclPtr
<vcl::Window
> xVclWindow
= GetWindow();
4609 ImplFrameData
* pFrameData
= xVclWindow
->ImplGetWindowImpl()->mpFrameData
;
4610 if (pFrameData
->mpFocusWin
== xTopLevelInterimWindow
)
4611 pFrameData
->mpFocusWin
= xOrigFrameFocusWin
;
4613 ImplSVData
* pSVData
= ImplGetSVData();
4614 if (pSVData
->mpWinData
->mpFocusWin
== xTopLevelInterimWindow
)
4615 pSVData
->mpWinData
->mpFocusWin
= xOrigFocusWin
;
4617 if (bRestoreDisallowCycleFocusOut
)
4619 // undo the above AllowCycleFocusOut for F6
4620 DisallowCycleFocusOut();
4627 m_pIMHandler
->updateIMSpotLocation();
4629 return bStopProcessingKey
;
4632 gboolean
GtkSalFrame::signalKeyPressed(GtkEventControllerKey
* pController
, guint keyval
, guint keycode
, GdkModifierType state
, gpointer frame
)
4634 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4635 return pThis
->DrawingAreaKey(pController
, SalEvent::KeyInput
, keyval
, keycode
, state
);
4638 gboolean
GtkSalFrame::signalKeyReleased(GtkEventControllerKey
* pController
, guint keyval
, guint keycode
, GdkModifierType state
, gpointer frame
)
4640 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4641 return pThis
->DrawingAreaKey(pController
, SalEvent::KeyUp
, keyval
, keycode
, state
);
4645 bool GtkSalFrame::WindowCloseRequest()
4647 CallCallbackExc(SalEvent::Close
, nullptr);
4651 #if GTK_CHECK_VERSION(4, 0, 0)
4652 gboolean
GtkSalFrame::signalDelete(GtkWidget
*, gpointer frame
)
4654 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4655 return pThis
->WindowCloseRequest();
4658 gboolean
GtkSalFrame::signalDelete(GtkWidget
*, GdkEvent
*, gpointer frame
)
4660 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4661 return pThis
->WindowCloseRequest();
4665 const cairo_font_options_t
* GtkSalFrame::get_font_options()
4667 GtkWidget
* pWidget
= getMouseEventWidget();
4668 #if GTK_CHECK_VERSION(4, 0, 0)
4669 PangoContext
* pContext
= gtk_widget_get_pango_context(pWidget
);
4671 return pango_cairo_context_get_font_options(pContext
);
4673 return gdk_screen_get_font_options(gtk_widget_get_screen(pWidget
));
4677 #if GTK_CHECK_VERSION(4, 0, 0)
4678 void GtkSalFrame::signalStyleUpdated(GtkWidget
*, const gchar
* /*pSetting*/, gpointer frame
)
4680 void GtkSalFrame::signalStyleUpdated(GtkWidget
*, gpointer frame
)
4683 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4685 // note: settings changed for multiple frames is avoided in winproc.cxx ImplHandleSettings
4686 GtkSalFrame::getDisplay()->SendInternalEvent( pThis
, nullptr, SalEvent::SettingsChanged
);
4688 // a plausible alternative might be to send SalEvent::FontChanged if pSetting starts with "gtk-xft"
4690 // fire off font-changed when the system cairo font hints change
4691 GtkInstance
*pInstance
= GetGtkInstance();
4692 const cairo_font_options_t
* pLastCairoFontOptions
= pInstance
->GetLastSeenCairoFontOptions();
4693 const cairo_font_options_t
* pCurrentCairoFontOptions
= pThis
->get_font_options();
4694 bool bFontSettingsChanged
= true;
4695 if (pLastCairoFontOptions
&& pCurrentCairoFontOptions
)
4696 bFontSettingsChanged
= !cairo_font_options_equal(pLastCairoFontOptions
, pCurrentCairoFontOptions
);
4697 else if (!pLastCairoFontOptions
&& !pCurrentCairoFontOptions
)
4698 bFontSettingsChanged
= false;
4699 if (bFontSettingsChanged
)
4701 pInstance
->ResetLastSeenCairoFontOptions(pCurrentCairoFontOptions
);
4702 GtkSalFrame::getDisplay()->SendInternalEvent( pThis
, nullptr, SalEvent::FontChanged
);
4706 #if !GTK_CHECK_VERSION(4, 0, 0)
4707 gboolean
GtkSalFrame::signalWindowState( GtkWidget
*, GdkEvent
* pEvent
, gpointer frame
)
4709 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4710 if( (pThis
->m_nState
& GDK_TOPLEVEL_STATE_MINIMIZED
) != (pEvent
->window_state
.new_window_state
& GDK_TOPLEVEL_STATE_MINIMIZED
) )
4712 GtkSalFrame::getDisplay()->SendInternalEvent( pThis
, nullptr, SalEvent::Resize
);
4713 pThis
->TriggerPaintEvent();
4716 if ((pEvent
->window_state
.new_window_state
& GDK_TOPLEVEL_STATE_MAXIMIZED
) &&
4717 !(pThis
->m_nState
& GDK_TOPLEVEL_STATE_MAXIMIZED
))
4719 pThis
->m_aRestorePosSize
= GetPosAndSize(GTK_WINDOW(pThis
->m_pWindow
));
4722 if ((pEvent
->window_state
.new_window_state
& GDK_WINDOW_STATE_WITHDRAWN
) &&
4723 !(pThis
->m_nState
& GDK_WINDOW_STATE_WITHDRAWN
))
4725 if (pThis
->isFloatGrabWindow())
4726 pThis
->closePopup();
4729 pThis
->m_nState
= pEvent
->window_state
.new_window_state
;
4734 void GtkSalFrame::signalWindowState(GdkToplevel
* pSurface
, GParamSpec
*, gpointer frame
)
4736 GdkToplevelState eNewWindowState
= gdk_toplevel_get_state(pSurface
);
4738 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4739 if( (pThis
->m_nState
& GDK_TOPLEVEL_STATE_MINIMIZED
) != (eNewWindowState
& GDK_TOPLEVEL_STATE_MINIMIZED
) )
4741 GtkSalFrame::getDisplay()->SendInternalEvent( pThis
, nullptr, SalEvent::Resize
);
4742 pThis
->TriggerPaintEvent();
4745 if ((eNewWindowState
& GDK_TOPLEVEL_STATE_MAXIMIZED
) &&
4746 !(pThis
->m_nState
& GDK_TOPLEVEL_STATE_MAXIMIZED
))
4748 pThis
->m_aRestorePosSize
= GetPosAndSize(GTK_WINDOW(pThis
->m_pWindow
));
4751 pThis
->m_nState
= eNewWindowState
;
4757 bool handleSignalZoom(GtkGesture
* gesture
, GdkEventSequence
* sequence
, gpointer frame
,
4758 GestureEventZoomType eEventType
)
4762 gtk_gesture_get_point(gesture
, sequence
, &x
, &y
);
4764 SalGestureZoomEvent aEvent
;
4765 aEvent
.meEventType
= eEventType
;
4768 aEvent
.mfScaleDelta
= gtk_gesture_zoom_get_scale_delta(GTK_GESTURE_ZOOM(gesture
));
4769 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4770 pThis
->CallCallbackExc(SalEvent::GestureZoom
, &aEvent
);
4774 bool handleSignalRotate(GtkGesture
* gesture
, GdkEventSequence
* sequence
, gpointer frame
,
4775 GestureEventRotateType eEventType
)
4779 gtk_gesture_get_point(gesture
, sequence
, &x
, &y
);
4781 SalGestureRotateEvent aEvent
;
4782 aEvent
.meEventType
= eEventType
;
4785 aEvent
.mfAngleDelta
= gtk_gesture_rotate_get_angle_delta(GTK_GESTURE_ROTATE(gesture
));
4786 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4787 pThis
->CallCallbackExc(SalEvent::GestureRotate
, &aEvent
);
4792 bool GtkSalFrame::signalZoomBegin(GtkGesture
* gesture
, GdkEventSequence
* sequence
, gpointer frame
)
4794 return handleSignalZoom(gesture
, sequence
, frame
, GestureEventZoomType::Begin
);
4797 bool GtkSalFrame::signalZoomUpdate(GtkGesture
* gesture
, GdkEventSequence
* sequence
, gpointer frame
)
4799 return handleSignalZoom(gesture
, sequence
, frame
, GestureEventZoomType::Update
);
4802 bool GtkSalFrame::signalZoomEnd(GtkGesture
* gesture
, GdkEventSequence
* sequence
, gpointer frame
)
4804 return handleSignalZoom(gesture
, sequence
, frame
, GestureEventZoomType::End
);
4807 bool GtkSalFrame::signalRotateBegin(GtkGesture
* gesture
, GdkEventSequence
* sequence
,
4810 return handleSignalRotate(gesture
, sequence
, frame
, GestureEventRotateType::Begin
);
4813 bool GtkSalFrame::signalRotateUpdate(GtkGesture
* gesture
, GdkEventSequence
* sequence
,
4816 return handleSignalRotate(gesture
, sequence
, frame
, GestureEventRotateType::Update
);
4819 bool GtkSalFrame::signalRotateEnd(GtkGesture
* gesture
, GdkEventSequence
* sequence
,
4822 return handleSignalRotate(gesture
, sequence
, frame
, GestureEventRotateType::End
);
4827 GdkDragAction
VclToGdk(sal_Int8 dragOperation
)
4829 GdkDragAction
eRet(static_cast<GdkDragAction
>(0));
4830 if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_COPY
)
4831 eRet
= static_cast<GdkDragAction
>(eRet
| GDK_ACTION_COPY
);
4832 if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_MOVE
)
4833 eRet
= static_cast<GdkDragAction
>(eRet
| GDK_ACTION_MOVE
);
4834 if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_LINK
)
4835 eRet
= static_cast<GdkDragAction
>(eRet
| GDK_ACTION_LINK
);
4839 sal_Int8
GdkToVcl(GdkDragAction dragOperation
)
4842 if (dragOperation
& GDK_ACTION_COPY
)
4843 nRet
|= css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
4844 if (dragOperation
& GDK_ACTION_MOVE
)
4845 nRet
|= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
4846 if (dragOperation
& GDK_ACTION_LINK
)
4847 nRet
|= css::datatransfer::dnd::DNDConstants::ACTION_LINK
;
4854 GdkDragAction
getPreferredDragAction(sal_Int8 dragOperation
)
4856 GdkDragAction
eAct(static_cast<GdkDragAction
>(0));
4858 if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_MOVE
)
4859 eAct
= GDK_ACTION_MOVE
;
4860 else if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_COPY
)
4861 eAct
= GDK_ACTION_COPY
;
4862 else if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_LINK
)
4863 eAct
= GDK_ACTION_LINK
;
4869 static bool g_DropSuccessSet
= false;
4870 static bool g_DropSuccess
= false;
4874 #if GTK_CHECK_VERSION(4, 0, 0)
4876 void read_drop_async_completed(GObject
* source
, GAsyncResult
* res
, gpointer user_data
)
4878 GdkDrop
* drop
= GDK_DROP(source
);
4879 read_transfer_result
* pRes
= static_cast<read_transfer_result
*>(user_data
);
4881 GInputStream
* pResult
= gdk_drop_read_finish(drop
, res
, nullptr, nullptr);
4886 g_main_context_wakeup(nullptr);
4890 pRes
->aVector
.resize(read_transfer_result::BlockSize
);
4892 g_input_stream_read_async(pResult
,
4893 pRes
->aVector
.data(),
4894 pRes
->aVector
.size(),
4897 read_transfer_result::read_block_async_completed
,
4902 class GtkDropTargetDropContext
: public cppu::WeakImplHelper
<css::datatransfer::dnd::XDropTargetDropContext
>
4904 #if !GTK_CHECK_VERSION(4, 0, 0)
4905 GdkDragContext
*m_pContext
;
4911 #if !GTK_CHECK_VERSION(4, 0, 0)
4912 GtkDropTargetDropContext(GdkDragContext
* pContext
, guint nTime
)
4913 : m_pContext(pContext
)
4916 GtkDropTargetDropContext(GdkDrop
* pDrop
)
4922 // XDropTargetDropContext
4923 virtual void SAL_CALL
acceptDrop(sal_Int8 dragOperation
) override
4925 #if !GTK_CHECK_VERSION(4, 0, 0)
4926 gdk_drag_status(m_pContext
, getPreferredDragAction(dragOperation
), m_nTime
);
4928 GdkDragAction eDragAction
= getPreferredDragAction(dragOperation
);
4929 gdk_drop_status(m_pDrop
,
4930 static_cast<GdkDragAction
>(eDragAction
| gdk_drop_get_actions(m_pDrop
)),
4935 virtual void SAL_CALL
rejectDrop() override
4937 #if !GTK_CHECK_VERSION(4, 0, 0)
4938 gdk_drag_status(m_pContext
, static_cast<GdkDragAction
>(0), m_nTime
);
4940 gdk_drop_status(m_pDrop
, gdk_drop_get_actions(m_pDrop
), static_cast<GdkDragAction
>(0));
4944 virtual void SAL_CALL
dropComplete(sal_Bool bSuccess
) override
4946 #if !GTK_CHECK_VERSION(4, 0, 0)
4947 gtk_drag_finish(m_pContext
, bSuccess
, false, m_nTime
);
4949 // should we do something better here
4950 gdk_drop_finish(m_pDrop
, bSuccess
4951 ? gdk_drop_get_actions(m_pDrop
)
4952 : static_cast<GdkDragAction
>(0));
4954 if (GtkInstDragSource::g_ActiveDragSource
)
4956 g_DropSuccessSet
= true;
4957 g_DropSuccess
= bSuccess
;
4964 class GtkDnDTransferable
: public GtkTransferable
4966 #if !GTK_CHECK_VERSION(4, 0, 0)
4967 GdkDragContext
*m_pContext
;
4969 GtkWidget
*m_pWidget
;
4970 GtkInstDropTarget
* m_pDropTarget
;
4974 #if !GTK_CHECK_VERSION(4, 0, 0)
4976 GtkSelectionData
*m_pData
;
4979 #if !GTK_CHECK_VERSION(4, 0, 0)
4980 GtkDnDTransferable(GdkDragContext
*pContext
, guint nTime
, GtkWidget
*pWidget
, GtkInstDropTarget
*pDropTarget
)
4981 : m_pContext(pContext
)
4983 , m_pWidget(pWidget
)
4984 , m_pDropTarget(pDropTarget
)
4986 GtkDnDTransferable(GdkDrop
*pDrop
)
4989 #if !GTK_CHECK_VERSION(4, 0, 0)
4996 virtual css::uno::Any SAL_CALL
getTransferData(const css::datatransfer::DataFlavor
& rFlavor
) override
4998 css::datatransfer::DataFlavor
aFlavor(rFlavor
);
4999 if (aFlavor
.MimeType
== "text/plain;charset=utf-16")
5000 aFlavor
.MimeType
= "text/plain;charset=utf-8";
5002 auto it
= m_aMimeTypeToGtkType
.find(aFlavor
.MimeType
);
5003 if (it
== m_aMimeTypeToGtkType
.end())
5004 return css::uno::Any();
5008 #if !GTK_CHECK_VERSION(4, 0, 0)
5009 /* like gtk_clipboard_wait_for_contents run a sub loop
5010 * waiting for drag-data-received triggered from
5014 m_pLoop
= g_main_loop_new(nullptr, true);
5015 m_pDropTarget
->SetFormatConversionRequest(this);
5017 gtk_drag_get_data(m_pWidget
, m_pContext
, it
->second
, m_nTime
);
5019 if (g_main_loop_is_running(m_pLoop
))
5020 main_loop_run(m_pLoop
);
5022 g_main_loop_unref(m_pLoop
);
5024 m_pDropTarget
->SetFormatConversionRequest(nullptr);
5027 if (aFlavor
.MimeType
== "text/plain;charset=utf-8")
5030 gchar
*pText
= reinterpret_cast<gchar
*>(gtk_selection_data_get_text(m_pData
));
5032 aStr
= OStringToOUString(pText
, RTL_TEXTENCODING_UTF8
);
5034 aRet
<<= aStr
.replaceAll("\r\n", "\n");
5039 const guchar
*rawdata
= gtk_selection_data_get_data_with_length(m_pData
,
5041 // seen here was rawhide == nullptr and length set to -1
5044 css::uno::Sequence
<sal_Int8
> aSeq(reinterpret_cast<const sal_Int8
*>(rawdata
), length
);
5049 gtk_selection_data_free(m_pData
);
5051 SalInstance
* pInstance
= GetSalInstance();
5052 read_transfer_result aRes
;
5053 const char *mime_types
[] = { it
->second
.getStr(), nullptr };
5055 gdk_drop_read_async(m_pDrop
,
5059 read_drop_async_completed
,
5063 pInstance
->DoYield(true, false);
5065 if (aFlavor
.MimeType
== "text/plain;charset=utf-8")
5066 aRet
<<= aRes
.get_as_string();
5068 aRet
<<= aRes
.get_as_sequence();
5073 virtual std::vector
<css::datatransfer::DataFlavor
> getTransferDataFlavorsAsVector() override
5075 #if !GTK_CHECK_VERSION(4, 0, 0)
5076 std::vector
<GdkAtom
> targets
;
5077 for (GList
* l
= gdk_drag_context_list_targets(m_pContext
); l
; l
= l
->next
)
5078 targets
.push_back(static_cast<GdkAtom
>(l
->data
));
5079 return GtkTransferable::getTransferDataFlavorsAsVector(targets
.data(), targets
.size());
5081 GdkContentFormats
* pFormats
= gdk_drop_get_formats(m_pDrop
);
5083 const char * const *targets
= gdk_content_formats_get_mime_types(pFormats
, &n_targets
);
5084 return GtkTransferable::getTransferDataFlavorsAsVector(targets
, n_targets
);
5088 #if !GTK_CHECK_VERSION(4, 0, 0)
5089 void LoopEnd(GtkSelectionData
*pData
)
5092 g_main_loop_quit(m_pLoop
);
5097 // For LibreOffice internal D&D we provide the Transferable without Gtk
5098 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this
5099 GtkInstDragSource
* GtkInstDragSource::g_ActiveDragSource
;
5101 #if GTK_CHECK_VERSION(4, 0, 0)
5103 gboolean
GtkSalFrame::signalDragDrop(GtkDropTargetAsync
* context
, GdkDrop
* drop
, double x
, double y
, gpointer frame
)
5105 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
5106 if (!pThis
->m_pDropTarget
)
5108 return pThis
->m_pDropTarget
->signalDragDrop(context
, drop
, x
, y
);
5111 gboolean
GtkSalFrame::signalDragDrop(GtkWidget
* pWidget
, GdkDragContext
* context
, gint x
, gint y
, guint time
, gpointer frame
)
5113 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
5114 if (!pThis
->m_pDropTarget
)
5116 return pThis
->m_pDropTarget
->signalDragDrop(pWidget
, context
, x
, y
, time
);
5120 #if !GTK_CHECK_VERSION(4, 0, 0)
5121 gboolean
GtkInstDropTarget::signalDragDrop(GtkWidget
* pWidget
, GdkDragContext
* context
, gint x
, gint y
, guint time
)
5123 gboolean
GtkInstDropTarget::signalDragDrop(GtkDropTargetAsync
* context
, GdkDrop
* drop
, double x
, double y
)
5126 // remove the deferred dragExit, as we'll do a drop
5130 g_idle_remove_by_data(this);
5132 #if !GTK_CHECK_VERSION(4, 0, 0)
5139 css::datatransfer::dnd::DropTargetDropEvent aEvent
;
5140 aEvent
.Source
= static_cast<css::datatransfer::dnd::XDropTarget
*>(this);
5141 #if !GTK_CHECK_VERSION(4, 0, 0)
5142 aEvent
.Context
= new GtkDropTargetDropContext(context
, time
);
5144 aEvent
.Context
= new GtkDropTargetDropContext(drop
);
5146 aEvent
.LocationX
= x
;
5147 aEvent
.LocationY
= y
;
5148 #if !GTK_CHECK_VERSION(4, 0, 0)
5149 aEvent
.DropAction
= GdkToVcl(gdk_drag_context_get_selected_action(context
));
5151 aEvent
.DropAction
= GdkToVcl(getPreferredDragAction(GdkToVcl(gdk_drop_get_actions(drop
))));
5153 // ACTION_DEFAULT is documented as...
5154 // 'This means the user did not press any key during the Drag and Drop operation
5155 // and the action that was combined with ACTION_DEFAULT is the system default action'
5156 // in tdf#107031 writer won't insert a link when a heading is dragged from the
5157 // navigator unless this is set. Its unclear really what ACTION_DEFAULT means,
5158 // there is a deprecated 'GDK_ACTION_DEFAULT Means nothing, and should not be used'
5159 // possible equivalent in gtk.
5160 // So (tdf#109227) set ACTION_DEFAULT if no modifier key is held down
5161 #if !GTK_CHECK_VERSION(4,0,0)
5162 aEvent
.SourceActions
= GdkToVcl(gdk_drag_context_get_actions(context
));
5163 GdkModifierType mask
;
5164 gdk_window_get_pointer(widget_get_surface(pWidget
), nullptr, nullptr, &mask
);
5166 aEvent
.SourceActions
= GdkToVcl(gdk_drop_get_actions(drop
));
5167 GdkModifierType mask
= gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(context
));
5169 if (!(mask
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
)))
5170 aEvent
.DropAction
|= css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT
;
5172 css::uno::Reference
<css::datatransfer::XTransferable
> xTransferable
;
5173 // For LibreOffice internal D&D we provide the Transferable without Gtk
5174 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this
5175 if (GtkInstDragSource::g_ActiveDragSource
)
5176 xTransferable
= GtkInstDragSource::g_ActiveDragSource
->GetTransferable();
5179 #if GTK_CHECK_VERSION(4,0,0)
5180 xTransferable
= new GtkDnDTransferable(drop
);
5182 xTransferable
= new GtkDnDTransferable(context
, time
, pWidget
, this);
5185 aEvent
.Transferable
= xTransferable
;
5194 class GtkDropTargetDragContext
: public cppu::WeakImplHelper
<css::datatransfer::dnd::XDropTargetDragContext
>
5196 #if !GTK_CHECK_VERSION(4, 0, 0)
5197 GdkDragContext
*m_pContext
;
5203 #if !GTK_CHECK_VERSION(4, 0, 0)
5204 GtkDropTargetDragContext(GdkDragContext
*pContext
, guint nTime
)
5205 : m_pContext(pContext
)
5208 GtkDropTargetDragContext(GdkDrop
* pDrop
)
5214 virtual void SAL_CALL
acceptDrag(sal_Int8 dragOperation
) override
5216 #if !GTK_CHECK_VERSION(4, 0, 0)
5217 gdk_drag_status(m_pContext
, getPreferredDragAction(dragOperation
), m_nTime
);
5219 gdk_drop_status(m_pDrop
, gdk_drop_get_actions(m_pDrop
), getPreferredDragAction(dragOperation
));
5223 virtual void SAL_CALL
rejectDrag() override
5225 #if !GTK_CHECK_VERSION(4, 0, 0)
5226 gdk_drag_status(m_pContext
, static_cast<GdkDragAction
>(0), m_nTime
);
5228 gdk_drop_status(m_pDrop
, gdk_drop_get_actions(m_pDrop
), static_cast<GdkDragAction
>(0));
5235 #if !GTK_CHECK_VERSION(4, 0, 0)
5236 void GtkSalFrame::signalDragDropReceived(GtkWidget
* pWidget
, GdkDragContext
* context
, gint x
, gint y
, GtkSelectionData
* data
, guint ttype
, guint time
, gpointer frame
)
5238 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
5239 if (!pThis
->m_pDropTarget
)
5241 pThis
->m_pDropTarget
->signalDragDropReceived(pWidget
, context
, x
, y
, data
, ttype
, time
);
5244 void GtkInstDropTarget::signalDragDropReceived(GtkWidget
* /*pWidget*/, GdkDragContext
* /*context*/, gint
/*x*/, gint
/*y*/, GtkSelectionData
* data
, guint
/*ttype*/, guint
/*time*/)
5247 * If we get a drop, then we will call like gtk_clipboard_wait_for_contents
5248 * with a loop inside a loop to get the right format, so if this is the
5249 * case return to the outer loop here with a copy of the desired data
5251 * don't look at me like that.
5253 if (!m_pFormatConversionRequest
)
5256 m_pFormatConversionRequest
->LoopEnd(gtk_selection_data_copy(data
));
5260 #if GTK_CHECK_VERSION(4,0,0)
5261 GdkDragAction
GtkSalFrame::signalDragMotion(GtkDropTargetAsync
*dest
, GdkDrop
*drop
, double x
, double y
, gpointer frame
)
5263 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
5264 if (!pThis
->m_pDropTarget
)
5265 return GdkDragAction(0);
5266 return pThis
->m_pDropTarget
->signalDragMotion(dest
, drop
, x
, y
);
5269 gboolean
GtkSalFrame::signalDragMotion(GtkWidget
*pWidget
, GdkDragContext
*context
, gint x
, gint y
, guint time
, gpointer frame
)
5271 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
5272 if (!pThis
->m_pDropTarget
)
5274 return pThis
->m_pDropTarget
->signalDragMotion(pWidget
, context
, x
, y
, time
);
5278 #if !GTK_CHECK_VERSION(4,0,0)
5279 gboolean
GtkInstDropTarget::signalDragMotion(GtkWidget
*pWidget
, GdkDragContext
*context
, gint x
, gint y
, guint time
)
5281 GdkDragAction
GtkInstDropTarget::signalDragMotion(GtkDropTargetAsync
*context
, GdkDrop
*pDrop
, double x
, double y
)
5286 #if !GTK_CHECK_VERSION(4,0,0)
5287 GtkWidget
* pHighlightWidget
= m_pFrame
? GTK_WIDGET(m_pFrame
->getFixedContainer()) : pWidget
;
5288 gtk_drag_highlight(pHighlightWidget
);
5290 GtkWidget
* pHighlightWidget
= m_pFrame
? GTK_WIDGET(m_pFrame
->getFixedContainer()) :
5291 gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(context
));
5292 gtk_widget_set_state_flags(pHighlightWidget
, GTK_STATE_FLAG_DROP_ACTIVE
, false);
5296 css::datatransfer::dnd::DropTargetDragEnterEvent aEvent
;
5297 aEvent
.Source
= static_cast<css::datatransfer::dnd::XDropTarget
*>(this);
5298 #if !GTK_CHECK_VERSION(4,0,0)
5299 rtl::Reference
<GtkDropTargetDragContext
> pContext
= new GtkDropTargetDragContext(context
, time
);
5301 rtl::Reference
<GtkDropTargetDragContext
> pContext
= new GtkDropTargetDragContext(pDrop
);
5303 //preliminary accept the Drag and select the preferred action, the fire_* will
5304 //inform the original caller of our choice and the callsite can decide
5305 //to overrule this choice. i.e. typically here we default to ACTION_MOVE
5306 #if !GTK_CHECK_VERSION(4,0,0)
5307 sal_Int8 nSourceActions
= GdkToVcl(gdk_drag_context_get_actions(context
));
5308 GdkModifierType mask
;
5309 gdk_window_get_pointer(widget_get_surface(pWidget
), nullptr, nullptr, &mask
);
5311 sal_Int8 nSourceActions
= GdkToVcl(gdk_drop_get_actions(pDrop
));
5312 GdkModifierType mask
= gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(context
));
5315 // tdf#124411 default to move if drag originates within LO itself, default
5316 // to copy if it comes from outside, this is similar to srcAndDestEqual
5317 // in macosx DropTarget::determineDropAction equivalent
5318 sal_Int8 nNewDropAction
= GtkInstDragSource::g_ActiveDragSource
?
5319 css::datatransfer::dnd::DNDConstants::ACTION_MOVE
:
5320 css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
5322 // tdf#109227 if a modifier is held down, default to the matching
5323 // action for that modifier combo, otherwise pick the preferred
5324 // default from the possible source actions
5325 if ((mask
& GDK_SHIFT_MASK
) && !(mask
& GDK_CONTROL_MASK
))
5326 nNewDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
5327 else if ((mask
& GDK_CONTROL_MASK
) && !(mask
& GDK_SHIFT_MASK
))
5328 nNewDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
5329 else if ((mask
& GDK_SHIFT_MASK
) && (mask
& GDK_CONTROL_MASK
) )
5330 nNewDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_LINK
;
5331 nNewDropAction
&= nSourceActions
;
5333 GdkDragAction eAction
;
5334 if (!(mask
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
)) && !nNewDropAction
)
5335 eAction
= getPreferredDragAction(nSourceActions
);
5337 eAction
= getPreferredDragAction(nNewDropAction
);
5339 #if !GTK_CHECK_VERSION(4,0,0)
5340 gdk_drag_status(context
, eAction
, time
);
5342 gdk_drop_status(pDrop
,
5343 static_cast<GdkDragAction
>(eAction
| gdk_drop_get_actions(pDrop
)),
5346 aEvent
.Context
= pContext
;
5347 aEvent
.LocationX
= x
;
5348 aEvent
.LocationY
= y
;
5349 //under wayland at least, the action selected by gdk_drag_status on the
5350 //context is not immediately available via gdk_drag_context_get_selected_action
5351 //so here we set the DropAction from what we selected on the context, not
5352 //what the context says is selected
5353 aEvent
.DropAction
= GdkToVcl(eAction
);
5354 aEvent
.SourceActions
= nSourceActions
;
5358 css::uno::Reference
<css::datatransfer::XTransferable
> xTransferable
;
5359 // For LibreOffice internal D&D we provide the Transferable without Gtk
5360 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this
5361 if (GtkInstDragSource::g_ActiveDragSource
)
5362 xTransferable
= GtkInstDragSource::g_ActiveDragSource
->GetTransferable();
5365 #if !GTK_CHECK_VERSION(4,0,0)
5366 xTransferable
= new GtkDnDTransferable(context
, time
, pWidget
, this);
5368 xTransferable
= new GtkDnDTransferable(pDrop
);
5371 css::uno::Sequence
<css::datatransfer::DataFlavor
> aFormats
= xTransferable
->getTransferDataFlavors();
5372 aEvent
.SupportedDataFlavors
= aFormats
;
5373 fire_dragEnter(aEvent
);
5378 fire_dragOver(aEvent
);
5381 #if !GTK_CHECK_VERSION(4,0,0)
5388 #if GTK_CHECK_VERSION(4,0,0)
5389 void GtkSalFrame::signalDragLeave(GtkDropTargetAsync
* pDest
, GdkDrop
* /*drop*/, gpointer frame
)
5391 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
5392 if (!pThis
->m_pDropTarget
)
5394 pThis
->m_pDropTarget
->signalDragLeave(gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(pDest
)));
5397 void GtkSalFrame::signalDragLeave(GtkWidget
* pWidget
, GdkDragContext
* /*context*/, guint
/*time*/, gpointer frame
)
5399 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
5400 if (!pThis
->m_pDropTarget
)
5402 pThis
->m_pDropTarget
->signalDragLeave(pWidget
);
5406 static gboolean
lcl_deferred_dragExit(gpointer user_data
)
5408 GtkInstDropTarget
* pThis
= static_cast<GtkInstDropTarget
*>(user_data
);
5409 css::datatransfer::dnd::DropTargetEvent aEvent
;
5410 aEvent
.Source
= static_cast<css::datatransfer::dnd::XDropTarget
*>(pThis
);
5411 pThis
->fire_dragExit(aEvent
);
5415 void GtkInstDropTarget::signalDragLeave(GtkWidget
*pWidget
)
5419 GtkWidget
* pHighlightWidget
= m_pFrame
? GTK_WIDGET(m_pFrame
->getFixedContainer()) : pWidget
;
5420 #if !GTK_CHECK_VERSION(4,0,0)
5421 gtk_drag_unhighlight(pHighlightWidget
);
5423 gtk_widget_unset_state_flags(pHighlightWidget
, GTK_STATE_FLAG_DROP_ACTIVE
);
5426 // defer fire_dragExit, since gtk also sends a drag-leave before the drop, while
5427 // LO expect to either handle the drop or the exit... at least in Writer.
5428 // but since we don't know there will be a drop following the leave, defer the
5429 // exit handling to an idle.
5430 g_idle_add(lcl_deferred_dragExit
, this);
5433 void GtkSalFrame::signalDestroy( GtkWidget
* pObj
, gpointer frame
)
5435 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
5436 if( pObj
!= pThis
->m_pWindow
)
5439 pThis
->m_aDamageHandler
.damaged
= nullptr;
5440 pThis
->m_aDamageHandler
.handle
= nullptr;
5441 if (pThis
->m_pSurface
)
5442 cairo_surface_set_user_data(pThis
->m_pSurface
, SvpSalGraphics::getDamageKey(), nullptr, nullptr);
5443 pThis
->m_pFixedContainer
= nullptr;
5444 pThis
->m_pDrawingArea
= nullptr;
5445 #if !GTK_CHECK_VERSION(4, 0, 0)
5446 pThis
->m_pEventBox
= nullptr;
5448 pThis
->m_pTopLevelGrid
= nullptr;
5449 pThis
->m_pWindow
= nullptr;
5450 pThis
->m_xFrameWeld
.reset();
5451 pThis
->InvalidateGraphics();
5454 // GtkSalFrame::IMHandler
5456 GtkSalFrame::IMHandler::IMHandler( GtkSalFrame
* pFrame
)
5458 m_nPrevKeyPresses( 0 ),
5459 m_pIMContext( nullptr ),
5461 m_bPreeditJustChanged( false )
5463 m_aInputEvent
.mpTextAttr
= nullptr;
5467 GtkSalFrame::IMHandler::~IMHandler()
5469 // cancel an eventual event posted to begin preedit again
5470 GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame
, &m_aInputEvent
, SalEvent::ExtTextInput
);
5474 void GtkSalFrame::IMHandler::createIMContext()
5479 m_pIMContext
= gtk_im_multicontext_new ();
5480 g_signal_connect( m_pIMContext
, "commit",
5481 G_CALLBACK (signalIMCommit
), this );
5482 g_signal_connect( m_pIMContext
, "preedit_changed",
5483 G_CALLBACK (signalIMPreeditChanged
), this );
5484 g_signal_connect( m_pIMContext
, "retrieve_surrounding",
5485 G_CALLBACK (signalIMRetrieveSurrounding
), this );
5486 g_signal_connect( m_pIMContext
, "delete_surrounding",
5487 G_CALLBACK (signalIMDeleteSurrounding
), this );
5488 g_signal_connect( m_pIMContext
, "preedit_start",
5489 G_CALLBACK (signalIMPreeditStart
), this );
5490 g_signal_connect( m_pIMContext
, "preedit_end",
5491 G_CALLBACK (signalIMPreeditEnd
), this );
5493 GetGenericUnixSalData()->ErrorTrapPush();
5494 im_context_set_client_widget(m_pIMContext
, m_pFrame
->getMouseEventWidget());
5495 #if GTK_CHECK_VERSION(4, 0, 0)
5496 gtk_event_controller_key_set_im_context(m_pFrame
->m_pKeyController
, m_pIMContext
);
5498 gtk_im_context_focus_in( m_pIMContext
);
5499 GetGenericUnixSalData()->ErrorTrapPop();
5504 void GtkSalFrame::IMHandler::deleteIMContext()
5509 // first give IC a chance to deinitialize
5510 GetGenericUnixSalData()->ErrorTrapPush();
5511 #if GTK_CHECK_VERSION(4, 0, 0)
5512 gtk_event_controller_key_set_im_context(m_pFrame
->m_pKeyController
, nullptr);
5514 im_context_set_client_widget(m_pIMContext
, nullptr);
5515 GetGenericUnixSalData()->ErrorTrapPop();
5517 g_object_unref( m_pIMContext
);
5518 m_pIMContext
= nullptr;
5521 void GtkSalFrame::IMHandler::doCallEndExtTextInput()
5523 m_aInputEvent
.mpTextAttr
= nullptr;
5524 m_pFrame
->CallCallbackExc( SalEvent::EndExtTextInput
, nullptr );
5527 void GtkSalFrame::IMHandler::updateIMSpotLocation()
5529 SalExtTextInputPosEvent aPosEvent
;
5530 m_pFrame
->CallCallbackExc( SalEvent::ExtTextInputPos
, static_cast<void*>(&aPosEvent
) );
5532 aArea
.x
= aPosEvent
.mnX
;
5533 aArea
.y
= aPosEvent
.mnY
;
5534 aArea
.width
= aPosEvent
.mnWidth
;
5535 aArea
.height
= aPosEvent
.mnHeight
;
5536 GetGenericUnixSalData()->ErrorTrapPush();
5537 gtk_im_context_set_cursor_location( m_pIMContext
, &aArea
);
5538 GetGenericUnixSalData()->ErrorTrapPop();
5541 void GtkSalFrame::IMHandler::sendEmptyCommit()
5543 vcl::DeletionListener
aDel( m_pFrame
);
5545 SalExtTextInputEvent aEmptyEv
;
5546 aEmptyEv
.mpTextAttr
= nullptr;
5547 aEmptyEv
.maText
.clear();
5548 aEmptyEv
.mnCursorPos
= 0;
5549 aEmptyEv
.mnCursorFlags
= 0;
5550 m_pFrame
->CallCallbackExc( SalEvent::ExtTextInput
, static_cast<void*>(&aEmptyEv
) );
5551 if( ! aDel
.isDeleted() )
5552 m_pFrame
->CallCallbackExc( SalEvent::EndExtTextInput
, nullptr );
5555 void GtkSalFrame::IMHandler::endExtTextInput( EndExtTextInputFlags
/*nFlags*/ )
5557 gtk_im_context_reset ( m_pIMContext
);
5559 if( !m_aInputEvent
.mpTextAttr
)
5562 vcl::DeletionListener
aDel( m_pFrame
);
5563 // delete preedit in sal (commit an empty string)
5565 if( ! aDel
.isDeleted() )
5567 // mark previous preedit state again (will e.g. be sent at focus gain)
5568 m_aInputEvent
.mpTextAttr
= m_aInputFlags
.data();
5571 // begin preedit again
5572 GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame
, &m_aInputEvent
, SalEvent::ExtTextInput
);
5577 void GtkSalFrame::IMHandler::focusChanged( bool bFocusIn
)
5579 m_bFocused
= bFocusIn
;
5582 GetGenericUnixSalData()->ErrorTrapPush();
5583 gtk_im_context_focus_in( m_pIMContext
);
5584 GetGenericUnixSalData()->ErrorTrapPop();
5585 if( m_aInputEvent
.mpTextAttr
)
5588 // begin preedit again
5589 GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame
, &m_aInputEvent
, SalEvent::ExtTextInput
);
5594 GetGenericUnixSalData()->ErrorTrapPush();
5595 gtk_im_context_focus_out( m_pIMContext
);
5596 GetGenericUnixSalData()->ErrorTrapPop();
5597 // cancel an eventual event posted to begin preedit again
5598 GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame
, &m_aInputEvent
, SalEvent::ExtTextInput
);
5602 #if !GTK_CHECK_VERSION(4, 0, 0)
5603 bool GtkSalFrame::IMHandler::handleKeyEvent( GdkEventKey
* pEvent
)
5605 vcl::DeletionListener
aDel( m_pFrame
);
5607 if( pEvent
->type
== GDK_KEY_PRESS
)
5609 // Add this key press event to the list of previous key presses
5610 // to which we compare key release events. If a later key release
5611 // event has a matching key press event in this list, we swallow
5612 // the key release because some GTK Input Methods don't swallow it
5614 m_aPrevKeyPresses
.emplace_back(pEvent
);
5615 m_nPrevKeyPresses
++;
5617 // Also pop off the earliest key press event if there are more than 10
5619 while (m_nPrevKeyPresses
> 10)
5621 m_aPrevKeyPresses
.pop_front();
5622 m_nPrevKeyPresses
--;
5625 GObject
* pRef
= G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext
) ) );
5627 // #i51353# update spot location on every key input since we cannot
5628 // know which key may activate a preedit choice window
5629 updateIMSpotLocation();
5630 if( aDel
.isDeleted() )
5633 bool bResult
= gtk_im_context_filter_keypress( m_pIMContext
, pEvent
);
5634 g_object_unref( pRef
);
5636 if( aDel
.isDeleted() )
5639 m_bPreeditJustChanged
= false;
5645 SAL_WARN_IF( m_nPrevKeyPresses
<= 0, "vcl.gtk3", "key press has vanished !" );
5646 if( ! m_aPrevKeyPresses
.empty() ) // sanity check
5648 // event was not swallowed, do not filter a following
5649 // key release event
5650 // note: this relies on gtk_im_context_filter_keypress
5651 // returning without calling a handler (in the "not swallowed"
5652 // case ) which might change the previous key press list so
5653 // we would pop the wrong event here
5654 m_aPrevKeyPresses
.pop_back();
5655 m_nPrevKeyPresses
--;
5660 // Determine if we got an earlier key press event corresponding to this key release
5661 if (pEvent
->type
== GDK_KEY_RELEASE
)
5663 GObject
* pRef
= G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext
) ) );
5664 bool bResult
= gtk_im_context_filter_keypress( m_pIMContext
, pEvent
);
5665 g_object_unref( pRef
);
5667 if( aDel
.isDeleted() )
5670 m_bPreeditJustChanged
= false;
5672 auto iter
= std::find(m_aPrevKeyPresses
.begin(), m_aPrevKeyPresses
.end(), pEvent
);
5673 // If we found a corresponding previous key press event, swallow the release
5674 // and remove the earlier key press from our list
5675 if (iter
!= m_aPrevKeyPresses
.end())
5677 m_aPrevKeyPresses
.erase(iter
);
5678 m_nPrevKeyPresses
--;
5690 * #122282# still more hacking: some IMEs never start a preedit but simply commit
5691 * in this case we cannot commit a single character. Workaround: do not do the
5692 * single key hack for enter or space if the unicode committed does not match
5695 static bool checkSingleKeyCommitHack( guint keyval
, sal_Unicode cCode
)
5700 case GDK_KEY_KP_Enter
:
5701 case GDK_KEY_Return
:
5702 if( cCode
!= '\n' && cCode
!= '\r' )
5706 case GDK_KEY_KP_Space
:
5716 void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext
* /*pContext*/, gchar
* pText
, gpointer im_handler
)
5718 GtkSalFrame::IMHandler
* pThis
= static_cast<GtkSalFrame::IMHandler
*>(im_handler
);
5720 SolarMutexGuard aGuard
;
5721 vcl::DeletionListener
aDel( pThis
->m_pFrame
);
5723 const bool bWasPreedit
=
5724 (pThis
->m_aInputEvent
.mpTextAttr
!= nullptr) ||
5725 pThis
->m_bPreeditJustChanged
;
5727 pThis
->m_aInputEvent
.mpTextAttr
= nullptr;
5728 pThis
->m_aInputEvent
.maText
= OUString( pText
, strlen(pText
), RTL_TEXTENCODING_UTF8
);
5729 pThis
->m_aInputEvent
.mnCursorPos
= pThis
->m_aInputEvent
.maText
.getLength();
5730 pThis
->m_aInputEvent
.mnCursorFlags
= 0;
5732 pThis
->m_aInputFlags
.clear();
5734 /* necessary HACK: all keyboard input comes in here as soon as an IMContext is set
5735 * which is logical and consequent. But since even simple input like
5736 * <space> comes through the commit signal instead of signalKey
5737 * and all kinds of windows only implement KeyInput (e.g. PushButtons,
5738 * RadioButtons and a lot of other Controls), will send a single
5739 * KeyInput/KeyUp sequence instead of an ExtText event if there
5740 * never was a preedit and the text is only one character.
5742 * In this case there the last ExtText event must have been
5743 * SalEvent::EndExtTextInput, either because of a regular commit
5744 * or because there never was a preedit.
5746 bool bSingleCommit
= false;
5748 && pThis
->m_aInputEvent
.maText
.getLength() == 1
5749 && ! pThis
->m_aPrevKeyPresses
.empty()
5752 const PreviousKeyPress
& rKP
= pThis
->m_aPrevKeyPresses
.back();
5753 sal_Unicode aOrigCode
= pThis
->m_aInputEvent
.maText
[0];
5755 if( checkSingleKeyCommitHack( rKP
.keyval
, aOrigCode
) )
5757 pThis
->m_pFrame
->doKeyCallback( rKP
.state
, rKP
.keyval
, rKP
.hardware_keycode
, rKP
.group
, aOrigCode
, true, true );
5758 bSingleCommit
= true;
5761 if( ! bSingleCommit
)
5763 pThis
->m_pFrame
->CallCallbackExc( SalEvent::ExtTextInput
, static_cast<void*>(&pThis
->m_aInputEvent
));
5764 if( ! aDel
.isDeleted() )
5765 pThis
->doCallEndExtTextInput();
5767 if( ! aDel
.isDeleted() )
5769 // reset input event
5770 pThis
->m_aInputEvent
.maText
.clear();
5771 pThis
->m_aInputEvent
.mnCursorPos
= 0;
5772 pThis
->updateIMSpotLocation();
5777 void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext
* /*pContext*/, gchar
* pText
, gpointer im_handler
)
5779 GtkSalFrame::IMHandler
* pThis
= static_cast<GtkSalFrame::IMHandler
*>(im_handler
);
5781 SolarMutexGuard aGuard
;
5782 vcl::DeletionListener
aDel( pThis
->m_pFrame
);
5785 const bool bWasPreedit
=
5786 (pThis
->m_aInputEvent
.mpTextAttr
!= nullptr) ||
5787 pThis
->m_bPreeditJustChanged
;
5790 pThis
->m_aInputEvent
.mpTextAttr
= nullptr;
5791 pThis
->m_aInputEvent
.maText
= OUString( pText
, strlen(pText
), RTL_TEXTENCODING_UTF8
);
5792 pThis
->m_aInputEvent
.mnCursorPos
= pThis
->m_aInputEvent
.maText
.getLength();
5793 pThis
->m_aInputEvent
.mnCursorFlags
= 0;
5795 pThis
->m_aInputFlags
.clear();
5797 /* necessary HACK: all keyboard input comes in here as soon as an IMContext is set
5798 * which is logical and consequent. But since even simple input like
5799 * <space> comes through the commit signal instead of signalKey
5800 * and all kinds of windows only implement KeyInput (e.g. PushButtons,
5801 * RadioButtons and a lot of other Controls), will send a single
5802 * KeyInput/KeyUp sequence instead of an ExtText event if there
5803 * never was a preedit and the text is only one character.
5805 * In this case there the last ExtText event must have been
5806 * SalEvent::EndExtTextInput, either because of a regular commit
5807 * or because there never was a preedit.
5809 bool bSingleCommit
= false;
5811 // TODO this needs a rethink to work again if necessary
5813 && pThis
->m_aInputEvent
.maText
.getLength() == 1
5814 && ! pThis
->m_aPrevKeyPresses
.empty()
5817 const PreviousKeyPress
& rKP
= pThis
->m_aPrevKeyPresses
.back();
5818 sal_Unicode aOrigCode
= pThis
->m_aInputEvent
.maText
[0];
5820 if( checkSingleKeyCommitHack( rKP
.keyval
, aOrigCode
) )
5822 pThis
->m_pFrame
->doKeyCallback( rKP
.state
, rKP
.keyval
, rKP
.hardware_keycode
, rKP
.group
, aOrigCode
, true, true );
5823 bSingleCommit
= true;
5827 if( ! bSingleCommit
)
5829 pThis
->m_pFrame
->CallCallbackExc( SalEvent::ExtTextInput
, static_cast<void*>(&pThis
->m_aInputEvent
));
5830 if( ! aDel
.isDeleted() )
5831 pThis
->doCallEndExtTextInput();
5833 if( ! aDel
.isDeleted() )
5835 // reset input event
5836 pThis
->m_aInputEvent
.maText
.clear();
5837 pThis
->m_aInputEvent
.mnCursorPos
= 0;
5838 pThis
->updateIMSpotLocation();
5844 OUString
GtkSalFrame::GetPreeditDetails(GtkIMContext
* pIMContext
, std::vector
<ExtTextInputAttr
>& rInputFlags
, sal_Int32
& rCursorPos
, sal_uInt8
& rCursorFlags
)
5846 char* pText
= nullptr;
5847 PangoAttrList
* pAttrs
= nullptr;
5848 gint nCursorPos
= 0;
5850 gtk_im_context_get_preedit_string( pIMContext
,
5855 gint nUtf8Len
= pText
? strlen(pText
) : 0;
5856 OUString sText
= pText
? OUString(pText
, nUtf8Len
, RTL_TEXTENCODING_UTF8
) : OUString();
5858 std::vector
<sal_Int32
> aUtf16Offsets
;
5859 for (sal_Int32 nUtf16Offset
= 0; nUtf16Offset
< sText
.getLength(); sText
.iterateCodePoints(&nUtf16Offset
))
5860 aUtf16Offsets
.push_back(nUtf16Offset
);
5862 sal_Int32 nUtf32Len
= aUtf16Offsets
.size();
5863 // from the above loop filling aUtf16Offsets, we know that its size() fits into sal_Int32
5864 aUtf16Offsets
.push_back(sText
.getLength());
5866 // sanitize the CurPos which is in utf-32
5869 else if (nCursorPos
> nUtf32Len
)
5870 nCursorPos
= nUtf32Len
;
5872 rCursorPos
= aUtf16Offsets
[nCursorPos
];
5875 rInputFlags
.resize(std::max(1, static_cast<int>(sText
.getLength())), ExtTextInputAttr::NONE
);
5877 PangoAttrIterator
*iter
= pango_attr_list_get_iterator(pAttrs
);
5880 GSList
*attr_list
= nullptr;
5881 GSList
*tmp_list
= nullptr;
5882 gint nUtf8Start
, nUtf8End
;
5883 ExtTextInputAttr sal_attr
= ExtTextInputAttr::NONE
;
5885 // docs say... "Get the range of the current segment ... the stored
5886 // return values are signed, not unsigned like the values in
5887 // PangoAttribute", which implies that the units are otherwise the same
5888 // as that of PangoAttribute whose docs state these units are "in
5890 // so this is the utf8 range
5891 pango_attr_iterator_range(iter
, &nUtf8Start
, &nUtf8End
);
5893 // sanitize the utf8 range
5894 nUtf8Start
= std::min(nUtf8Start
, nUtf8Len
);
5895 nUtf8End
= std::min(nUtf8End
, nUtf8Len
);
5896 if (nUtf8Start
>= nUtf8End
)
5899 // get the utf32 range
5900 sal_Int32 nUtf32Start
= g_utf8_pointer_to_offset(pText
, pText
+ nUtf8Start
);
5901 sal_Int32 nUtf32End
= g_utf8_pointer_to_offset(pText
, pText
+ nUtf8End
);
5903 // sanitize the utf32 range
5904 nUtf32Start
= std::min(nUtf32Start
, nUtf32Len
);
5905 nUtf32End
= std::min(nUtf32End
, nUtf32Len
);
5906 if (nUtf32Start
>= nUtf32End
)
5909 tmp_list
= attr_list
= pango_attr_iterator_get_attrs (iter
);
5912 PangoAttribute
*pango_attr
= static_cast<PangoAttribute
*>(tmp_list
->data
);
5914 switch (pango_attr
->klass
->type
)
5916 case PANGO_ATTR_BACKGROUND
:
5917 sal_attr
|= ExtTextInputAttr::Highlight
;
5918 rCursorFlags
|= EXTTEXTINPUT_CURSOR_INVISIBLE
;
5920 case PANGO_ATTR_UNDERLINE
:
5922 PangoAttrInt
* pango_underline
= reinterpret_cast<PangoAttrInt
*>(pango_attr
);
5923 switch (pango_underline
->value
)
5925 case PANGO_UNDERLINE_NONE
:
5927 case PANGO_UNDERLINE_DOUBLE
:
5928 sal_attr
|= ExtTextInputAttr::DoubleUnderline
;
5931 sal_attr
|= ExtTextInputAttr::Underline
;
5936 case PANGO_ATTR_STRIKETHROUGH
:
5937 sal_attr
|= ExtTextInputAttr::RedText
;
5942 pango_attribute_destroy (pango_attr
);
5943 tmp_list
= tmp_list
->next
;
5946 sal_attr
|= ExtTextInputAttr::Underline
;
5947 g_slist_free (attr_list
);
5949 // Set the sal attributes on our text
5950 // rhbz#1648281 apply over our utf-16 range derived from the input utf-32 range
5951 for (sal_Int32 i
= aUtf16Offsets
[nUtf32Start
]; i
< aUtf16Offsets
[nUtf32End
]; ++i
)
5953 SAL_WARN_IF(i
>= static_cast<int>(rInputFlags
.size()),
5954 "vcl.gtk3", "pango attrib out of range. Broken range: "
5955 << aUtf16Offsets
[nUtf32Start
] << "," << aUtf16Offsets
[nUtf32End
] << " Legal range: 0,"
5956 << rInputFlags
.size());
5957 if (i
>= static_cast<int>(rInputFlags
.size()))
5959 rInputFlags
[i
] |= sal_attr
;
5961 } while (pango_attr_iterator_next (iter
));
5962 pango_attr_iterator_destroy(iter
);
5965 pango_attr_list_unref( pAttrs
);
5970 void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext
* pIMContext
, gpointer im_handler
)
5972 GtkSalFrame::IMHandler
* pThis
= static_cast<GtkSalFrame::IMHandler
*>(im_handler
);
5974 sal_Int32
nCursorPos(0);
5975 sal_uInt8
nCursorFlags(0);
5976 std::vector
<ExtTextInputAttr
> aInputFlags
;
5977 OUString sText
= GtkSalFrame::GetPreeditDetails(pIMContext
, aInputFlags
, nCursorPos
, nCursorFlags
);
5978 if (sText
.isEmpty() && pThis
->m_aInputEvent
.maText
.isEmpty())
5980 // change from nothing to nothing -> do not start preedit
5981 // e.g. this will activate input into a calc cell without
5986 pThis
->m_bPreeditJustChanged
= true;
5988 bool bEndPreedit
= sText
.isEmpty() && pThis
->m_aInputEvent
.mpTextAttr
!= nullptr;
5989 pThis
->m_aInputEvent
.maText
= sText
;
5990 pThis
->m_aInputEvent
.mnCursorPos
= nCursorPos
;
5991 pThis
->m_aInputEvent
.mnCursorFlags
= nCursorFlags
;
5992 pThis
->m_aInputFlags
= aInputFlags
;
5993 pThis
->m_aInputEvent
.mpTextAttr
= pThis
->m_aInputFlags
.data();
5995 SolarMutexGuard aGuard
;
5996 vcl::DeletionListener
aDel( pThis
->m_pFrame
);
5998 pThis
->m_pFrame
->CallCallbackExc( SalEvent::ExtTextInput
, static_cast<void*>(&pThis
->m_aInputEvent
));
5999 if( bEndPreedit
&& ! aDel
.isDeleted() )
6000 pThis
->doCallEndExtTextInput();
6001 if( ! aDel
.isDeleted() )
6002 pThis
->updateIMSpotLocation();
6005 void GtkSalFrame::IMHandler::signalIMPreeditStart( GtkIMContext
*, gpointer
/*im_handler*/ )
6009 void GtkSalFrame::IMHandler::signalIMPreeditEnd( GtkIMContext
*, gpointer im_handler
)
6011 GtkSalFrame::IMHandler
* pThis
= static_cast<GtkSalFrame::IMHandler
*>(im_handler
);
6013 pThis
->m_bPreeditJustChanged
= true;
6015 SolarMutexGuard aGuard
;
6016 vcl::DeletionListener
aDel( pThis
->m_pFrame
);
6017 pThis
->doCallEndExtTextInput();
6018 if( ! aDel
.isDeleted() )
6019 pThis
->updateIMSpotLocation();
6022 gboolean
GtkSalFrame::IMHandler::signalIMRetrieveSurrounding( GtkIMContext
* pContext
, gpointer im_handler
)
6024 GtkSalFrame::IMHandler
* pThis
= static_cast<GtkSalFrame::IMHandler
*>(im_handler
);
6026 SalSurroundingTextRequestEvent aEvt
;
6027 aEvt
.maText
.clear();
6028 aEvt
.mnStart
= aEvt
.mnEnd
= 0;
6030 SolarMutexGuard aGuard
;
6031 pThis
->m_pFrame
->CallCallback(SalEvent::SurroundingTextRequest
, &aEvt
);
6033 OString sUTF
= OUStringToOString(aEvt
.maText
, RTL_TEXTENCODING_UTF8
);
6034 std::u16string_view
sCursorText(aEvt
.maText
.subView(0, aEvt
.mnStart
));
6035 gtk_im_context_set_surrounding(pContext
, sUTF
.getStr(), sUTF
.getLength(),
6036 OUStringToOString(sCursorText
, RTL_TEXTENCODING_UTF8
).getLength());
6040 gboolean
GtkSalFrame::IMHandler::signalIMDeleteSurrounding( GtkIMContext
*, gint offset
, gint nchars
,
6041 gpointer im_handler
)
6043 GtkSalFrame::IMHandler
* pThis
= static_cast<GtkSalFrame::IMHandler
*>(im_handler
);
6045 // First get the surrounding text
6046 SalSurroundingTextRequestEvent aSurroundingTextEvt
;
6047 aSurroundingTextEvt
.maText
.clear();
6048 aSurroundingTextEvt
.mnStart
= aSurroundingTextEvt
.mnEnd
= 0;
6050 SolarMutexGuard aGuard
;
6051 pThis
->m_pFrame
->CallCallback(SalEvent::SurroundingTextRequest
, &aSurroundingTextEvt
);
6053 // Turn offset, nchars into a utf-16 selection
6054 Selection aSelection
= SalFrame::CalcDeleteSurroundingSelection(aSurroundingTextEvt
.maText
,
6055 aSurroundingTextEvt
.mnStart
,
6057 Selection
aInvalid(SAL_MAX_UINT32
, SAL_MAX_UINT32
);
6058 if (aSelection
== aInvalid
)
6061 SalSurroundingTextSelectionChangeEvent aEvt
;
6062 aEvt
.mnStart
= aSelection
.Min();
6063 aEvt
.mnEnd
= aSelection
.Max();
6065 pThis
->m_pFrame
->CallCallback(SalEvent::DeleteSurroundingTextRequest
, &aEvt
);
6067 aSelection
= Selection(aEvt
.mnStart
, aEvt
.mnEnd
);
6068 if (aSelection
== aInvalid
)
6074 Size
GtkSalDisplay::GetScreenSize( int nDisplayScreen
)
6076 tools::Rectangle aRect
= m_pSys
->GetDisplayScreenPosSizePixel( nDisplayScreen
);
6077 return Size( aRect
.GetWidth(), aRect
.GetHeight() );
6080 sal_uIntPtr
GtkSalFrame::GetNativeWindowHandle(GtkWidget
*pWidget
)
6082 GdkSurface
* pSurface
= widget_get_surface(pWidget
);
6083 GdkDisplay
*pDisplay
= getGdkDisplay();
6085 #if defined(GDK_WINDOWING_X11)
6086 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay
))
6088 return gdk_x11_surface_get_xid(pSurface
);
6092 #if defined(GDK_WINDOWING_WAYLAND)
6093 if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay
))
6095 return reinterpret_cast<sal_uIntPtr
>(gdk_wayland_surface_get_wl_surface(pSurface
));
6102 void GtkInstDragSource::set_datatransfer(const css::uno::Reference
<css::datatransfer::XTransferable
>& rTrans
,
6103 const css::uno::Reference
<css::datatransfer::dnd::XDragSourceListener
>& rListener
)
6105 m_xListener
= rListener
;
6109 void GtkInstDragSource::setActiveDragSource()
6111 // For LibreOffice internal D&D we provide the Transferable without Gtk
6112 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this
6113 g_ActiveDragSource
= this;
6114 g_DropSuccessSet
= false;
6115 g_DropSuccess
= false;
6118 #if !GTK_CHECK_VERSION(4, 0, 0)
6119 std::vector
<GtkTargetEntry
> GtkInstDragSource::FormatsToGtk(const css::uno::Sequence
<css::datatransfer::DataFlavor
> &rFormats
)
6121 return m_aConversionHelper
.FormatsToGtk(rFormats
);
6125 void GtkInstDragSource::startDrag(const datatransfer::dnd::DragGestureEvent
& rEvent
,
6126 sal_Int8 sourceActions
, sal_Int32
/*cursor*/, sal_Int32
/*image*/,
6127 const css::uno::Reference
<css::datatransfer::XTransferable
>& rTrans
,
6128 const css::uno::Reference
<css::datatransfer::dnd::XDragSourceListener
>& rListener
)
6130 set_datatransfer(rTrans
, rListener
);
6134 setActiveDragSource();
6136 m_pFrame
->startDrag(rEvent
, rTrans
, m_aConversionHelper
,VclToGdk(sourceActions
));
6142 void GtkSalFrame::startDrag(const css::datatransfer::dnd::DragGestureEvent
& rEvent
,
6143 const css::uno::Reference
<css::datatransfer::XTransferable
>& rTrans
,
6144 VclToGtkHelper
& rConversionHelper
,
6145 GdkDragAction sourceActions
)
6147 SolarMutexGuard aGuard
;
6149 assert(m_pDragSource
);
6151 #if GTK_CHECK_VERSION(4, 0, 0)
6153 GdkSeat
*pSeat
= gdk_display_get_default_seat(getGdkDisplay());
6154 GdkDrag
* pDrag
= gdk_drag_begin(widget_get_surface(getMouseEventWidget()),
6155 gdk_seat_get_pointer(pSeat
),
6156 transerable_content_new(&rConversionHelper
, rTrans
.get()),
6158 rEvent
.DragOriginX
, rEvent
.DragOriginY
);
6160 g_signal_connect(G_OBJECT(pDrag
), "drop-performed", G_CALLBACK(signalDragEnd
), this);
6161 g_signal_connect(G_OBJECT(pDrag
), "cancel", G_CALLBACK(signalDragFailed
), this);
6162 g_signal_connect(G_OBJECT(pDrag
), "dnd-finished", G_CALLBACK(signalDragDelete
), this);
6166 auto aFormats
= rTrans
->getTransferDataFlavors();
6167 auto aGtkTargets
= rConversionHelper
.FormatsToGtk(aFormats
);
6169 GtkTargetList
*pTargetList
= gtk_target_list_new(aGtkTargets
.data(), aGtkTargets
.size());
6171 gint nDragButton
= 1; // default to left button
6172 css::awt::MouseEvent aEvent
;
6173 if (rEvent
.Event
>>= aEvent
)
6175 if (aEvent
.Buttons
& css::awt::MouseButton::LEFT
)
6177 else if (aEvent
.Buttons
& css::awt::MouseButton::RIGHT
)
6179 else if (aEvent
.Buttons
& css::awt::MouseButton::MIDDLE
)
6183 GdkEvent aFakeEvent
;
6184 memset(&aFakeEvent
, 0, sizeof(GdkEvent
));
6185 aFakeEvent
.type
= GDK_BUTTON_PRESS
;
6186 aFakeEvent
.button
.window
= widget_get_surface(getMouseEventWidget());
6187 aFakeEvent
.button
.time
= GDK_CURRENT_TIME
;
6189 aFakeEvent
.button
.device
= gtk_get_current_event_device();
6190 // if no current event to determine device, or (tdf#140272) the device will be unsuitable then find an
6191 // appropriate device to use.
6192 if (!aFakeEvent
.button
.device
|| !gdk_device_get_window_at_position(aFakeEvent
.button
.device
, nullptr, nullptr))
6194 GdkDeviceManager
* pDeviceManager
= gdk_display_get_device_manager(getGdkDisplay());
6195 GList
* pDevices
= gdk_device_manager_list_devices(pDeviceManager
, GDK_DEVICE_TYPE_MASTER
);
6196 for (GList
* pEntry
= pDevices
; pEntry
; pEntry
= pEntry
->next
)
6198 GdkDevice
* pDevice
= static_cast<GdkDevice
*>(pEntry
->data
);
6199 if (gdk_device_get_source(pDevice
) == GDK_SOURCE_KEYBOARD
)
6201 if (gdk_device_get_window_at_position(pDevice
, nullptr, nullptr))
6203 aFakeEvent
.button
.device
= pDevice
;
6207 g_list_free(pDevices
);
6210 GdkDragContext
*pDrag
;
6211 if (!aFakeEvent
.button
.device
|| !gdk_device_get_window_at_position(aFakeEvent
.button
.device
, nullptr, nullptr))
6214 pDrag
= gtk_drag_begin_with_coordinates(getMouseEventWidget(),
6220 rEvent
.DragOriginY
);
6222 gtk_target_list_unref(pTargetList
);
6224 for (auto &a
: aGtkTargets
)
6229 m_pDragSource
->dragFailed();
6232 void GtkInstDragSource::dragFailed()
6234 if (m_xListener
.is())
6236 datatransfer::dnd::DragSourceDropEvent aEv
;
6237 aEv
.DropAction
= datatransfer::dnd::DNDConstants::ACTION_NONE
;
6238 aEv
.DropSuccess
= false;
6239 auto xListener
= m_xListener
;
6240 m_xListener
.clear();
6241 xListener
->dragDropEnd(aEv
);
6245 #if GTK_CHECK_VERSION(4, 0, 0)
6246 void GtkSalFrame::signalDragFailed(GdkDrag
* /*drag*/, GdkDragCancelReason
/*reason*/, gpointer frame
)
6248 gboolean
GtkSalFrame::signalDragFailed(GtkWidget
* /*widget*/, GdkDragContext
* /*context*/, GtkDragResult
/*result*/, gpointer frame
)
6251 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
6252 if (pThis
->m_pDragSource
)
6253 pThis
->m_pDragSource
->dragFailed();
6254 #if !GTK_CHECK_VERSION(4, 0, 0)
6259 void GtkInstDragSource::dragDelete()
6261 if (m_xListener
.is())
6263 datatransfer::dnd::DragSourceDropEvent aEv
;
6264 aEv
.DropAction
= datatransfer::dnd::DNDConstants::ACTION_MOVE
;
6265 aEv
.DropSuccess
= true;
6266 auto xListener
= m_xListener
;
6267 m_xListener
.clear();
6268 xListener
->dragDropEnd(aEv
);
6272 #if GTK_CHECK_VERSION(4, 0, 0)
6273 void GtkSalFrame::signalDragDelete(GdkDrag
* /*context*/, gpointer frame
)
6275 void GtkSalFrame::signalDragDelete(GtkWidget
* /*widget*/, GdkDragContext
* /*context*/, gpointer frame
)
6278 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
6279 if (!pThis
->m_pDragSource
)
6281 pThis
->m_pDragSource
->dragDelete();
6284 #if GTK_CHECK_VERSION(4, 0, 0)
6285 void GtkInstDragSource::dragEnd(GdkDrag
* context
)
6287 void GtkInstDragSource::dragEnd(GdkDragContext
* context
)
6290 if (m_xListener
.is())
6292 datatransfer::dnd::DragSourceDropEvent aEv
;
6293 #if GTK_CHECK_VERSION(4, 0, 0)
6294 aEv
.DropAction
= GdkToVcl(gdk_drag_get_selected_action(context
));
6296 aEv
.DropAction
= GdkToVcl(gdk_drag_context_get_selected_action(context
));
6298 // an internal drop can accept the drop but fail with dropComplete( false )
6299 // this is different than the GTK API
6300 if (g_DropSuccessSet
)
6301 aEv
.DropSuccess
= g_DropSuccess
;
6303 aEv
.DropSuccess
= true;
6304 auto xListener
= m_xListener
;
6305 m_xListener
.clear();
6306 xListener
->dragDropEnd(aEv
);
6308 g_ActiveDragSource
= nullptr;
6311 #if GTK_CHECK_VERSION(4, 0, 0)
6312 void GtkSalFrame::signalDragEnd(GdkDrag
* context
, gpointer frame
)
6314 void GtkSalFrame::signalDragEnd(GtkWidget
* /*widget*/, GdkDragContext
* context
, gpointer frame
)
6317 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
6318 if (!pThis
->m_pDragSource
)
6320 pThis
->m_pDragSource
->dragEnd(context
);
6323 #if !GTK_CHECK_VERSION(4, 0, 0)
6324 void GtkInstDragSource::dragDataGet(GtkSelectionData
*data
, guint info
)
6326 m_aConversionHelper
.setSelectionData(m_xTrans
, data
, info
);
6329 void GtkSalFrame::signalDragDataGet(GtkWidget
* /*widget*/, GdkDragContext
* /*context*/, GtkSelectionData
*data
, guint info
,
6330 guint
/*time*/, gpointer frame
)
6332 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
6333 if (!pThis
->m_pDragSource
)
6335 pThis
->m_pDragSource
->dragDataGet(data
, info
);
6339 bool GtkSalFrame::CallCallbackExc(SalEvent nEvent
, const void* pEvent
) const
6341 SolarMutexGuard aGuard
;
6345 nRet
= CallCallback(nEvent
, pEvent
);
6349 GetGtkSalData()->setException(std::current_exception());
6354 #if !GTK_CHECK_VERSION(4, 0, 0)
6355 void GtkSalFrame::nopaint_container_resize_children(GtkContainer
*pContainer
)
6357 bool bOrigSalObjectSetPosSize
= m_bSalObjectSetPosSize
;
6358 m_bSalObjectSetPosSize
= true;
6359 gtk_container_resize_children(pContainer
);
6360 m_bSalObjectSetPosSize
= bOrigSalObjectSetPosSize
;
6364 GdkEvent
* GtkSalFrame::makeFakeKeyPress(GtkWidget
* pWidget
)
6366 #if !GTK_CHECK_VERSION(4, 0, 0)
6367 GdkEvent
*event
= gdk_event_new(GDK_KEY_PRESS
);
6368 event
->key
.window
= GDK_WINDOW(g_object_ref(widget_get_surface(pWidget
)));
6370 GdkSeat
*seat
= gdk_display_get_default_seat(gtk_widget_get_display(pWidget
));
6371 gdk_event_set_device(event
, gdk_seat_get_keyboard(seat
));
6373 event
->key
.send_event
= 1 /* TRUE */;
6374 event
->key
.time
= gtk_get_current_event_time();
6375 event
->key
.state
= 0;
6376 event
->key
.keyval
= 0;
6377 event
->key
.length
= 0;
6378 event
->key
.string
= nullptr;
6379 event
->key
.hardware_keycode
= 0;
6380 event
->key
.group
= 0;
6381 event
->key
.is_modifier
= false;
6389 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */