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/help.hxx>
27 #include <vcl/keycodes.hxx>
28 #include <vcl/layout.hxx>
29 #include <unx/wmadaptor.hxx>
31 #include <unx/salbmp.h>
32 #include <unx/genprn.h>
33 #include <unx/geninst.h>
34 #include <headless/svpgdi.hxx>
35 #include <o3tl/runtimetooustring.hxx>
36 #include <osl/file.hxx>
37 #include <rtl/bootstrap.hxx>
38 #include <rtl/process.h>
39 #include <vcl/floatwin.hxx>
40 #include <vcl/svapp.hxx>
41 #include <vcl/weld.hxx>
42 #include <vcl/window.hxx>
43 #include <vcl/settings.hxx>
44 #include <cppuhelper/exc_hlp.hxx>
46 #include <config_gio.h>
51 #include <X11/Xutil.h>
52 #include <X11/Xatom.h>
53 #if defined(GDK_WINDOWING_X11)
54 # include <gdk/gdkx.h>
56 #if defined(GDK_WINDOWING_WAYLAND)
57 # include <gdk/gdkwayland.h>
61 #include <vcl/salbtype.hxx>
63 #include <strings.hrc>
64 #include <bitmaps.hlst>
65 #include <sal/macros.h>
67 #include <basegfx/range/b2ibox.hxx>
68 #include <basegfx/vector/b2ivector.hxx>
71 #include <glib/gprintf.h>
73 #if OSL_DEBUG_LEVEL > 1
77 #include <i18nlangtag/mslangid.hxx>
82 #include <comphelper/processfactory.hxx>
83 #include <comphelper/sequenceashashmap.hxx>
84 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
85 #include <com/sun/star/accessibility/AccessibleRole.hpp>
86 #include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
87 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
88 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
89 #include <com/sun/star/awt/MouseButton.hpp>
90 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
91 #include <com/sun/star/frame/Desktop.hpp>
92 #include <com/sun/star/frame/ModuleManager.hpp>
93 #include <com/sun/star/frame/XFrame.hpp>
94 #include <com/sun/star/util/URLTransformer.hpp>
96 #include <config_folders.h>
98 #define IS_WIDGET_REALIZED gtk_widget_get_realized
99 #define IS_WIDGET_MAPPED gtk_widget_get_mapped
101 #ifndef GDK_IS_X11_DISPLAY
102 #define GDK_IS_X11_DISPLAY(foo) (true)
106 using namespace com::sun::star
;
108 int GtkSalFrame::m_nFloats
= 0;
110 static GDBusConnection
* pSessionBus
= nullptr;
112 sal_uInt16
GtkSalFrame::GetKeyModCode( guint state
)
114 sal_uInt16 nCode
= 0;
115 if( state
& GDK_SHIFT_MASK
)
117 if( state
& GDK_CONTROL_MASK
)
119 if( state
& GDK_MOD1_MASK
)
121 if( state
& GDK_SUPER_MASK
)
126 sal_uInt16
GtkSalFrame::GetMouseModCode( guint state
)
128 sal_uInt16 nCode
= GetKeyModCode( state
);
129 if( state
& GDK_BUTTON1_MASK
)
131 if( state
& GDK_BUTTON2_MASK
)
132 nCode
|= MOUSE_MIDDLE
;
133 if( state
& GDK_BUTTON3_MASK
)
134 nCode
|= MOUSE_RIGHT
;
139 sal_uInt16
GtkSalFrame::GetKeyCode(guint keyval
)
141 sal_uInt16 nCode
= 0;
142 if( keyval
>= GDK_KEY_0
&& keyval
<= GDK_KEY_9
)
143 nCode
= KEY_0
+ (keyval
-GDK_KEY_0
);
144 else if( keyval
>= GDK_KEY_KP_0
&& keyval
<= GDK_KEY_KP_9
)
145 nCode
= KEY_0
+ (keyval
-GDK_KEY_KP_0
);
146 else if( keyval
>= GDK_KEY_A
&& keyval
<= GDK_KEY_Z
)
147 nCode
= KEY_A
+ (keyval
-GDK_KEY_A
);
148 else if( keyval
>= GDK_KEY_a
&& keyval
<= GDK_KEY_z
)
149 nCode
= KEY_A
+ (keyval
-GDK_KEY_a
);
150 else if( keyval
>= GDK_KEY_F1
&& keyval
<= GDK_KEY_F26
)
151 // KEY_F26 is the last function key known to keycodes.hxx
155 // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx
156 // althopugh GDK_KEY_F1 ... GDK_KEY_L10 are known to
157 // gdk/gdkkeysyms.h, they are unlikely to be generated
158 // except possibly by Solaris systems
159 // this whole section needs review
163 case GDK_KEY_L3
: nCode
= KEY_PROPERTIES
; break;
164 case GDK_KEY_L4
: nCode
= KEY_UNDO
; break;
165 case GDK_KEY_L6
: nCode
= KEY_COPY
; break; // KEY_F16
166 case GDK_KEY_L8
: nCode
= KEY_PASTE
; break; // KEY_F18
167 case GDK_KEY_L10
: nCode
= KEY_CUT
; break; // KEY_F20
169 nCode
= KEY_F1
+ (keyval
-GDK_KEY_F1
); break;
176 case GDK_KEY_KP_Down
:
177 case GDK_KEY_Down
: nCode
= KEY_DOWN
; break;
179 case GDK_KEY_Up
: nCode
= KEY_UP
; break;
180 case GDK_KEY_KP_Left
:
181 case GDK_KEY_Left
: nCode
= KEY_LEFT
; break;
182 case GDK_KEY_KP_Right
:
183 case GDK_KEY_Right
: nCode
= KEY_RIGHT
; break;
184 case GDK_KEY_KP_Begin
:
185 case GDK_KEY_KP_Home
:
187 case GDK_KEY_Home
: nCode
= KEY_HOME
; break;
189 case GDK_KEY_End
: nCode
= KEY_END
; break;
190 case GDK_KEY_KP_Page_Up
:
191 case GDK_KEY_Page_Up
: nCode
= KEY_PAGEUP
; break;
192 case GDK_KEY_KP_Page_Down
:
193 case GDK_KEY_Page_Down
: nCode
= KEY_PAGEDOWN
; break;
194 case GDK_KEY_KP_Enter
:
195 case GDK_KEY_Return
: nCode
= KEY_RETURN
; break;
196 case GDK_KEY_Escape
: nCode
= KEY_ESCAPE
; break;
197 case GDK_KEY_ISO_Left_Tab
:
199 case GDK_KEY_Tab
: nCode
= KEY_TAB
; break;
200 case GDK_KEY_BackSpace
: nCode
= KEY_BACKSPACE
; break;
201 case GDK_KEY_KP_Space
:
202 case GDK_KEY_space
: nCode
= KEY_SPACE
; break;
203 case GDK_KEY_KP_Insert
:
204 case GDK_KEY_Insert
: nCode
= KEY_INSERT
; break;
205 case GDK_KEY_KP_Delete
:
206 case GDK_KEY_Delete
: nCode
= KEY_DELETE
; break;
208 case GDK_KEY_KP_Add
: nCode
= KEY_ADD
; break;
210 case GDK_KEY_KP_Subtract
: nCode
= KEY_SUBTRACT
; break;
211 case GDK_KEY_asterisk
:
212 case GDK_KEY_KP_Multiply
: nCode
= KEY_MULTIPLY
; break;
214 case GDK_KEY_KP_Divide
: nCode
= KEY_DIVIDE
; break;
215 case GDK_KEY_period
: nCode
= KEY_POINT
; break;
216 case GDK_KEY_decimalpoint
: nCode
= KEY_POINT
; break;
217 case GDK_KEY_comma
: nCode
= KEY_COMMA
; break;
218 case GDK_KEY_less
: nCode
= KEY_LESS
; break;
219 case GDK_KEY_greater
: nCode
= KEY_GREATER
; break;
220 case GDK_KEY_KP_Equal
:
221 case GDK_KEY_equal
: nCode
= KEY_EQUAL
; break;
222 case GDK_KEY_Find
: nCode
= KEY_FIND
; break;
223 case GDK_KEY_Menu
: nCode
= KEY_CONTEXTMENU
;break;
224 case GDK_KEY_Help
: nCode
= KEY_HELP
; break;
225 case GDK_KEY_Undo
: nCode
= KEY_UNDO
; break;
226 case GDK_KEY_Redo
: nCode
= KEY_REPEAT
; break;
227 // on a sun keyboard this actually is usually SunXK_Stop = 0x0000FF69 (XK_Cancel),
228 // but VCL doesn't have a key definition for that
229 case GDK_KEY_Cancel
: nCode
= KEY_F11
; break;
230 case GDK_KEY_KP_Decimal
:
231 case GDK_KEY_KP_Separator
: nCode
= KEY_DECIMAL
; break;
232 case GDK_KEY_asciitilde
: nCode
= KEY_TILDE
; break;
233 case GDK_KEY_leftsinglequotemark
:
234 case GDK_KEY_quoteleft
: nCode
= KEY_QUOTELEFT
; break;
235 case GDK_KEY_bracketleft
: nCode
= KEY_BRACKETLEFT
; break;
236 case GDK_KEY_bracketright
: nCode
= KEY_BRACKETRIGHT
; break;
237 case GDK_KEY_semicolon
: nCode
= KEY_SEMICOLON
; break;
238 case GDK_KEY_quoteright
: nCode
= KEY_QUOTERIGHT
; break;
239 // some special cases, also see saldisp.cxx
240 // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000
241 // These can be found in ap_keysym.h
242 case 0x1000FF02: // apXK_Copy
245 case 0x1000FF03: // apXK_Cut
248 case 0x1000FF04: // apXK_Paste
251 case 0x1000FF14: // apXK_Repeat
255 // - - - - - - - - - - - - - - D E C - - - - - - - - - - - - - 0x1000
256 // These can be found in DECkeysym.h
260 // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000
261 // These can be found in HPkeysym.h
262 case 0x1000FF73: // hpXK_DeleteChar
265 case 0x1000FF74: // hpXK_BackTab
266 case 0x1000FF75: // hpXK_KP_BackTab
269 // - - - - - - - - - - - - - - I B M - - - - - - - - - - - - -
270 // - - - - - - - - - - - - - - O S F - - - - - - - - - - - - - 0x1004
271 // These also can be found in HPkeysym.h
272 case 0x1004FF02: // osfXK_Copy
275 case 0x1004FF03: // osfXK_Cut
278 case 0x1004FF04: // osfXK_Paste
281 case 0x1004FF07: // osfXK_BackTab
284 case 0x1004FF08: // osfXK_BackSpace
285 nCode
= KEY_BACKSPACE
;
287 case 0x1004FF1B: // osfXK_Escape
290 // Up, Down, Left, Right, PageUp, PageDown
291 // - - - - - - - - - - - - - - S C O - - - - - - - - - - - - -
292 // - - - - - - - - - - - - - - S G I - - - - - - - - - - - - - 0x1007
293 // - - - - - - - - - - - - - - S N I - - - - - - - - - - - - -
294 // - - - - - - - - - - - - - - S U N - - - - - - - - - - - - - 0x1005
295 // These can be found in Sunkeysym.h
296 case 0x1005FF10: // SunXK_F36
299 case 0x1005FF11: // SunXK_F37
302 case 0x1005FF70: // SunXK_Props
303 nCode
= KEY_PROPERTIES
;
305 case 0x1005FF71: // SunXK_Front
308 case 0x1005FF72: // SunXK_Copy
311 case 0x1005FF73: // SunXK_Open
314 case 0x1005FF74: // SunXK_Paste
317 case 0x1005FF75: // SunXK_Cut
320 // - - - - - - - - - - - - - X F 8 6 - - - - - - - - - - - - - 0x1008
321 // These can be found in XF86keysym.h
322 // but more importantly they are also available GTK/Gdk version 3
323 // and hence are already provided in gdk/gdkkeysyms.h, and hence
325 case GDK_KEY_Copy
: nCode
= KEY_COPY
; break; // 0x1008ff57
326 case GDK_KEY_Cut
: nCode
= KEY_CUT
; break; // 0x1008ff58
327 case GDK_KEY_Open
: nCode
= KEY_OPEN
; break; // 0x1008ff6b
328 case GDK_KEY_Paste
: nCode
= KEY_PASTE
; break; // 0x1008ff6d
335 guint
GtkSalFrame::GetKeyValFor(GdkKeymap
* pKeyMap
, guint16 hardware_keycode
, guint8 group
)
337 guint updated_keyval
= 0;
338 gdk_keymap_translate_keyboard_state(pKeyMap
, hardware_keycode
,
339 GdkModifierType(0), group
, &updated_keyval
, nullptr, nullptr, nullptr);
340 return updated_keyval
;
343 // F10 means either KEY_F10 or KEY_MENU, which has to be decided
344 // in the independent part.
348 sal_Unicode nCharCode
;
349 KeyAlternate() : nKeyCode( 0 ), nCharCode( 0 ) {}
350 KeyAlternate( sal_uInt16 nKey
, sal_Unicode nChar
= 0 ) : nKeyCode( nKey
), nCharCode( nChar
) {}
354 GetAlternateKeyCode( const sal_uInt16 nKeyCode
)
356 KeyAlternate aAlternate
;
360 case KEY_F10
: aAlternate
= KeyAlternate( KEY_MENU
);break;
361 case KEY_F24
: aAlternate
= KeyAlternate( KEY_SUBTRACT
, '-' );break;
367 #if OSL_DEBUG_LEVEL > 0
368 static bool dumpframes
= false;
371 bool GtkSalFrame::doKeyCallback( guint state
,
373 guint16 hardware_keycode
,
375 sal_Unicode aOrigCode
,
382 aEvent
.mnCharCode
= aOrigCode
;
385 vcl::DeletionListener
aDel( this );
387 #if OSL_DEBUG_LEVEL > 0
388 const char* pKeyDebug
= getenv("VCL_GTK3_PAINTDEBUG");
390 if (pKeyDebug
&& *pKeyDebug
== '1')
394 // shift-zero forces a re-draw and event is swallowed
395 if (keyval
== GDK_KEY_0
)
397 fprintf( stderr
, "force widget_queue_draw\n");
398 gtk_widget_queue_draw(GTK_WIDGET(m_pFixedContainer
));
401 else if (keyval
== GDK_KEY_1
)
403 fprintf( stderr
, "force repaint all\n");
407 else if (keyval
== GDK_KEY_2
)
409 dumpframes
= !dumpframes
;
410 fprintf(stderr
, "toggle dump frames to %d\n", dumpframes
);
418 * #i42122# translate all keys with Ctrl and/or Alt to group 0 else
419 * shortcuts (e.g. Ctrl-o) will not work but be inserted by the
422 * #i52338# do this for all keys that the independent part has no key code
425 * fdo#41169 rather than use group 0, detect if there is a group which can
426 * be used to input Latin text and use that if possible
428 aEvent
.mnCode
= GetKeyCode( keyval
);
429 if( aEvent
.mnCode
== 0 )
431 gint best_group
= SAL_MAX_INT32
;
433 // Try and find Latin layout
434 GdkKeymap
* keymap
= gdk_keymap_get_default();
437 if (gdk_keymap_get_entries_for_keyval(keymap
, GDK_KEY_A
, &keys
, &n_keys
))
439 // Find the lowest group that supports Latin layout
440 for (gint i
= 0; i
< n_keys
; ++i
)
442 if (keys
[i
].level
!= 0 && keys
[i
].level
!= 1)
444 best_group
= std::min(best_group
, keys
[i
].group
);
451 //Unavailable, go with original group then I suppose
452 if (best_group
== SAL_MAX_INT32
)
455 guint updated_keyval
= GetKeyValFor(keymap
, hardware_keycode
, best_group
);
456 aEvent
.mnCode
= GetKeyCode(updated_keyval
);
459 aEvent
.mnCode
|= GetKeyModCode( state
);
461 bool bStopProcessingKey
;
464 bStopProcessingKey
= CallCallbackExc(SalEvent::KeyInput
, &aEvent
);
465 // #i46889# copy AlternateKeyCode handling from generic plugin
466 if (!bStopProcessingKey
)
468 KeyAlternate aAlternate
= GetAlternateKeyCode( aEvent
.mnCode
);
469 if( aAlternate
.nKeyCode
)
471 aEvent
.mnCode
= aAlternate
.nKeyCode
;
472 if( aAlternate
.nCharCode
)
473 aEvent
.mnCharCode
= aAlternate
.nCharCode
;
474 bStopProcessingKey
= CallCallbackExc(SalEvent::KeyInput
, &aEvent
);
477 if( bSendRelease
&& ! aDel
.isDeleted() )
479 CallCallbackExc(SalEvent::KeyUp
, &aEvent
);
483 bStopProcessingKey
= CallCallbackExc(SalEvent::KeyUp
, &aEvent
);
484 return bStopProcessingKey
;
487 GtkSalFrame::GtkSalFrame( SalFrame
* pParent
, SalFrameStyleFlags nStyle
)
488 : m_nXScreen( getDisplay()->GetDefaultXScreen() )
489 , m_pHeaderBar(nullptr)
490 , m_pGraphics(nullptr)
493 getDisplay()->registerFrame( this );
494 m_bDefaultPos
= true;
495 m_bDefaultSize
= ( (nStyle
& SalFrameStyleFlags::SIZEABLE
) && ! pParent
);
496 m_bWindowIsGtkPlug
= false;
497 Init( pParent
, nStyle
);
500 GtkSalFrame::GtkSalFrame( SystemParentData
* pSysData
)
501 : m_nXScreen( getDisplay()->GetDefaultXScreen() )
502 , m_pHeaderBar(nullptr)
503 , m_pGraphics(nullptr)
506 getDisplay()->registerFrame( this );
507 // permanently ignore errors from our unruly children ...
508 GetGenericUnixSalData()->ErrorTrapPush();
509 m_bDefaultPos
= true;
510 m_bDefaultSize
= true;
514 // AppMenu watch functions.
516 static void ObjectDestroyedNotify( gpointer data
)
519 g_object_unref( data
);
523 static void hud_activated( gboolean hud_active
, gpointer user_data
)
527 SolarMutexGuard aGuard
;
528 GtkSalFrame
* pSalFrame
= static_cast< GtkSalFrame
* >( user_data
);
529 GtkSalMenu
* pSalMenu
= reinterpret_cast< GtkSalMenu
* >( pSalFrame
->GetMenu() );
532 pSalMenu
->UpdateFull();
536 static void activate_uno(GSimpleAction
*action
, GVariant
*, gpointer
)
538 uno::Reference
< css::uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
540 uno::Reference
< css::frame::XDesktop2
> xDesktop
= css::frame::Desktop::create( xContext
);
542 uno::Reference
< css::frame::XFrame
> xFrame(xDesktop
->getActiveFrame());
544 xFrame
.set(xDesktop
, uno::UNO_QUERY
);
549 uno::Reference
< css::frame::XDispatchProvider
> xDispatchProvider(xFrame
, uno::UNO_QUERY
);
550 if (!xDispatchProvider
.is())
553 gchar
*strval
= nullptr;
554 g_object_get(action
, "name", &strval
, nullptr);
558 if (strcmp(strval
, "New") == 0)
562 uno::Reference
<frame::XModuleManager2
> xModuleManager(frame::ModuleManager::create(xContext
));
563 OUString
aModuleId(xModuleManager
->identify(xFrame
));
564 if (aModuleId
.isEmpty())
567 comphelper::SequenceAsHashMap
lModuleDescription(xModuleManager
->getByName(aModuleId
));
568 OUString sFactoryService
;
569 lModuleDescription
[OUString("ooSetupFactoryEmptyDocumentURL")] >>= sFactoryService
;
570 if (sFactoryService
.isEmpty())
573 uno::Sequence
< css::beans::PropertyValue
> args(0);
574 xDesktop
->loadComponentFromURL(sFactoryService
, "_blank", 0, args
);
578 OUString
sCommand(".uno:");
579 sCommand
+= OUString(strval
, strlen(strval
), RTL_TEXTENCODING_UTF8
);
582 css::util::URL aCommand
;
583 aCommand
.Complete
= sCommand
;
584 uno::Reference
< css::util::XURLTransformer
> xParser
= css::util::URLTransformer::create(xContext
);
585 xParser
->parseStrict(aCommand
);
587 uno::Reference
< css::frame::XDispatch
> xDisp
= xDispatchProvider
->queryDispatch(aCommand
, OUString(), 0);
592 xDisp
->dispatch(aCommand
, css::uno::Sequence
< css::beans::PropertyValue
>());
595 static const GActionEntry app_entries
[] = {
596 { "OptionsTreeDialog", activate_uno
, nullptr, nullptr, nullptr, {0} },
597 { "About", activate_uno
, nullptr, nullptr, nullptr, {0} },
598 { "HelpIndex", activate_uno
, nullptr, nullptr, nullptr, {0} },
599 { "Quit", activate_uno
, nullptr, nullptr, nullptr, {0} },
600 { "New", activate_uno
, nullptr, nullptr, nullptr, {0} }
603 gboolean
ensure_dbus_setup( gpointer data
)
605 GtkSalFrame
* pSalFrame
= static_cast< GtkSalFrame
* >( data
);
606 GdkWindow
* gdkWindow
= widget_get_window( pSalFrame
->getWindow() );
608 if ( gdkWindow
!= nullptr && g_object_get_data( G_OBJECT( gdkWindow
), "g-lo-menubar" ) == nullptr )
610 // Get a DBus session connection.
612 pSessionBus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, nullptr, nullptr);
618 // Create menu model and action group attached to this frame.
619 GMenuModel
* pMenuModel
= G_MENU_MODEL( g_lo_menu_new() );
620 GActionGroup
* pActionGroup
= reinterpret_cast<GActionGroup
*>(g_lo_action_group_new());
622 // Generate menu paths.
623 sal_uIntPtr windowId
= pSalFrame
->GetNativeWindowHandle(pSalFrame
->getWindow());
624 gchar
* aDBusWindowPath
= g_strdup_printf( "/org/libreoffice/window/%lu", windowId
);
625 gchar
* aDBusMenubarPath
= g_strdup_printf( "/org/libreoffice/window/%lu/menus/menubar", windowId
);
627 // Set window properties.
628 g_object_set_data_full( G_OBJECT( gdkWindow
), "g-lo-menubar", pMenuModel
, ObjectDestroyedNotify
);
629 g_object_set_data_full( G_OBJECT( gdkWindow
), "g-lo-action-group", pActionGroup
, ObjectDestroyedNotify
);
631 GdkDisplay
*pDisplay
= GtkSalFrame::getGdkDisplay();
632 // fdo#70885 we don't want app menu under Unity
633 const bool bDesktopIsUnity
= (SalGetDesktopEnvironment() == "UNITY");
634 #if defined(GDK_WINDOWING_X11)
635 if (GDK_IS_X11_DISPLAY(pDisplay
))
637 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_APPLICATION_ID", "org.libreoffice" );
638 if (!bDesktopIsUnity
)
639 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_APP_MENU_OBJECT_PATH", "/org/libreoffice/menus/appmenu" );
640 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_MENUBAR_OBJECT_PATH", aDBusMenubarPath
);
641 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_WINDOW_OBJECT_PATH", aDBusWindowPath
);
642 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_APPLICATION_OBJECT_PATH", "/org/libreoffice" );
643 gdk_x11_window_set_utf8_property( gdkWindow
, "_GTK_UNIQUE_BUS_NAME", g_dbus_connection_get_unique_name( pSessionBus
) );
646 #if defined(GDK_WINDOWING_WAYLAND)
647 if (GDK_IS_WAYLAND_DISPLAY(pDisplay
))
649 gdk_wayland_window_set_dbus_properties_libgtk_only(gdkWindow
, "org.libreoffice",
650 "/org/libreoffice/menus/appmenu",
651 !bDesktopIsUnity
? aDBusMenubarPath
: nullptr,
654 g_dbus_connection_get_unique_name( pSessionBus
));
657 // Publish the menu model and the action group.
658 SAL_INFO("vcl.unity", "exporting menu model at " << pMenuModel
<< " for window " << windowId
);
659 pSalFrame
->m_nMenuExportId
= g_dbus_connection_export_menu_model (pSessionBus
, aDBusMenubarPath
, pMenuModel
, nullptr);
660 SAL_INFO("vcl.unity", "exporting action group at " << pActionGroup
<< " for window " << windowId
);
661 pSalFrame
->m_nActionGroupExportId
= g_dbus_connection_export_action_group( pSessionBus
, aDBusWindowPath
, pActionGroup
, nullptr);
662 pSalFrame
->m_nHudAwarenessId
= hud_awareness_register( pSessionBus
, aDBusMenubarPath
, hud_activated
, pSalFrame
, nullptr, nullptr );
664 //app menu, to-do translations, block normal menus when active, honor use appmenu settings
665 if (!bDesktopIsUnity
)
667 GMenu
*menu
= g_menu_new ();
670 GMenu
*firstsubmenu
= g_menu_new ();
672 OString
sNew(OUStringToOString(VclResId(SV_BUTTONTEXT_NEW
),
673 RTL_TEXTENCODING_UTF8
).replaceFirst("~", "_"));
675 item
= g_menu_item_new(sNew
.getStr(), "app.New");
676 g_menu_append_item( firstsubmenu
, item
);
677 g_object_unref(item
);
679 g_menu_append_section( menu
, nullptr, G_MENU_MODEL(firstsubmenu
));
680 g_object_unref(firstsubmenu
);
682 GMenu
*secondsubmenu
= g_menu_new ();
684 OString
sPreferences(OUStringToOString(VclResId(SV_STDTEXT_PREFERENCES
),
685 RTL_TEXTENCODING_UTF8
).replaceFirst("~", "_"));
687 item
= g_menu_item_new(sPreferences
.getStr(), "app.OptionsTreeDialog");
688 g_menu_append_item( secondsubmenu
, item
);
689 g_object_unref(item
);
691 g_menu_append_section( menu
, nullptr, G_MENU_MODEL(secondsubmenu
));
692 g_object_unref(secondsubmenu
);
694 GMenu
*thirdsubmenu
= g_menu_new ();
696 OString
sHelp(OUStringToOString(VclResId(SV_BUTTONTEXT_HELP
),
697 RTL_TEXTENCODING_UTF8
).replaceFirst("~", "_"));
699 item
= g_menu_item_new(sHelp
.getStr(), "app.HelpIndex");
700 g_menu_append_item( thirdsubmenu
, item
);
701 g_object_unref(item
);
703 OString
sAbout(OUStringToOString(VclResId(SV_STDTEXT_ABOUT
),
704 RTL_TEXTENCODING_UTF8
).replaceFirst("~", "_"));
706 item
= g_menu_item_new(sAbout
.getStr(), "app.About");
707 g_menu_append_item( thirdsubmenu
, item
);
708 g_object_unref(item
);
710 OString
sQuit(OUStringToOString(VclResId(SV_MENU_MAC_QUITAPP
),
711 RTL_TEXTENCODING_UTF8
).replaceFirst("~", "_"));
713 item
= g_menu_item_new(sQuit
.getStr(), "app.Quit");
714 g_menu_append_item( thirdsubmenu
, item
);
715 g_object_unref(item
);
716 g_menu_append_section( menu
, nullptr, G_MENU_MODEL(thirdsubmenu
));
717 g_object_unref(thirdsubmenu
);
719 GSimpleActionGroup
*group
= g_simple_action_group_new ();
720 g_action_map_add_action_entries (G_ACTION_MAP (group
), app_entries
, G_N_ELEMENTS (app_entries
), nullptr);
721 GActionGroup
* pAppActionGroup
= G_ACTION_GROUP(group
);
723 pSalFrame
->m_nAppActionGroupExportId
= g_dbus_connection_export_action_group( pSessionBus
, "/org/libreoffice", pAppActionGroup
, nullptr);
724 g_object_unref(pAppActionGroup
);
725 pSalFrame
->m_nAppMenuExportId
= g_dbus_connection_export_menu_model (pSessionBus
, "/org/libreoffice/menus/appmenu", G_MENU_MODEL (menu
), nullptr);
726 g_object_unref(menu
);
729 g_free( aDBusMenubarPath
);
730 g_free( aDBusWindowPath
);
736 void on_registrar_available( GDBusConnection
* /*connection*/,
737 const gchar
* /*name*/,
738 const gchar
* /*name_owner*/,
741 SolarMutexGuard aGuard
;
743 GtkSalFrame
* pSalFrame
= static_cast< GtkSalFrame
* >( user_data
);
745 SalMenu
* pSalMenu
= pSalFrame
->GetMenu();
747 if ( pSalMenu
!= nullptr )
749 GtkSalMenu
* pGtkSalMenu
= static_cast<GtkSalMenu
*>(pSalMenu
);
750 pGtkSalMenu
->EnableUnity(true);
754 // This is called when the registrar becomes unavailable. It shows the menubar.
755 void on_registrar_unavailable( GDBusConnection
* /*connection*/,
756 const gchar
* /*name*/,
759 SolarMutexGuard aGuard
;
761 SAL_INFO("vcl.unity", "on_registrar_unavailable");
763 //pSessionBus = NULL;
764 GtkSalFrame
* pSalFrame
= static_cast< GtkSalFrame
* >( user_data
);
766 SalMenu
* pSalMenu
= pSalFrame
->GetMenu();
769 GtkSalMenu
* pGtkSalMenu
= static_cast< GtkSalMenu
* >( pSalMenu
);
770 pGtkSalMenu
->EnableUnity(false);
774 void GtkSalFrame::EnsureAppMenuWatch()
778 // Get a DBus session connection.
779 if ( pSessionBus
== nullptr )
781 pSessionBus
= g_bus_get_sync( G_BUS_TYPE_SESSION
, nullptr, nullptr );
783 if ( pSessionBus
== nullptr )
787 // Publish the menu only if AppMenu registrar is available.
788 m_nWatcherId
= g_bus_watch_name_on_connection( pSessionBus
,
789 "com.canonical.AppMenu.Registrar",
790 G_BUS_NAME_WATCHER_FLAGS_NONE
,
791 on_registrar_available
,
792 on_registrar_unavailable
,
798 void GtkSalFrame::InvalidateGraphics()
806 GtkSalFrame::~GtkSalFrame()
808 m_aSmoothScrollIdle
.Stop();
809 m_aSmoothScrollIdle
.ClearInvokeHandler();
813 m_pDropTarget
->deinitialize();
814 m_pDropTarget
= nullptr;
819 m_pDragSource
->deinitialize();
820 m_pDragSource
= nullptr;
823 InvalidateGraphics();
827 m_pParent
->m_aChildren
.remove( this );
830 getDisplay()->deregisterFrame( this );
834 cairo_region_destroy( m_pRegion
);
837 m_pIMHandler
.reset();
839 //tdf#108705 remove grabs on event widget before
840 //destroying event widget
844 GtkWidget
*pEventWidget
= getMouseEventWidget();
845 for (auto handler_id
: m_aMouseSignalIds
)
846 g_signal_handler_disconnect(G_OBJECT(pEventWidget
), handler_id
);
847 if( m_pFixedContainer
)
848 gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer
) );
850 gtk_widget_destroy( GTK_WIDGET(m_pEventBox
) );
851 if( m_pTopLevelGrid
)
852 gtk_widget_destroy( GTK_WIDGET(m_pTopLevelGrid
) );
854 SolarMutexGuard aGuard
;
857 g_bus_unwatch_name(m_nWatcherId
);
861 g_object_set_data( G_OBJECT( m_pWindow
), "SalFrame", nullptr );
865 if ( m_nHudAwarenessId
)
866 hud_awareness_unregister( pSessionBus
, m_nHudAwarenessId
);
867 if ( m_nMenuExportId
)
868 g_dbus_connection_unexport_menu_model( pSessionBus
, m_nMenuExportId
);
869 if ( m_nAppMenuExportId
)
870 g_dbus_connection_unexport_menu_model( pSessionBus
, m_nAppMenuExportId
);
871 if ( m_nActionGroupExportId
)
872 g_dbus_connection_unexport_action_group( pSessionBus
, m_nActionGroupExportId
);
873 if ( m_nAppActionGroupExportId
)
874 g_dbus_connection_unexport_action_group( pSessionBus
, m_nAppActionGroupExportId
);
876 gtk_widget_destroy( m_pWindow
);
879 if( m_pForeignParent
)
880 g_object_unref( G_OBJECT( m_pForeignParent
) );
881 if( m_pForeignTopLevel
)
882 g_object_unref( G_OBJECT( m_pForeignTopLevel
) );
887 cairo_surface_destroy(m_pSurface
);
890 void GtkSalFrame::moveWindow( long nX
, long nY
)
892 if( isChild( false ) )
895 gtk_fixed_move( m_pParent
->getFixedContainer(),
897 nX
- m_pParent
->maGeometry
.nX
, nY
- m_pParent
->maGeometry
.nY
);
900 gtk_window_move( GTK_WINDOW(m_pWindow
), nX
, nY
);
903 void GtkSalFrame::widget_set_size_request(long nWidth
, long nHeight
)
905 gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer
), nWidth
, nHeight
);
908 void GtkSalFrame::window_resize(long nWidth
, long nHeight
)
910 m_nWidthRequest
= nWidth
;
911 m_nHeightRequest
= nHeight
;
912 gtk_window_set_default_size(GTK_WINDOW(m_pWindow
), nWidth
, nHeight
);
913 if (gtk_widget_get_visible(m_pWindow
))
914 gtk_window_resize(GTK_WINDOW(m_pWindow
), nWidth
, nHeight
);
917 void GtkSalFrame::resizeWindow( long nWidth
, long nHeight
)
919 if( isChild( false ) )
921 widget_set_size_request(nWidth
, nHeight
);
923 else if( ! isChild( true, false ) )
924 window_resize(nWidth
, nHeight
);
928 ooo_fixed_class_init(GtkFixedClass
*klass
)
930 GtkWidgetClass
*widget_class
= GTK_WIDGET_CLASS(klass
);
931 widget_class
->get_accessible
= ooo_fixed_get_accessible
;
935 * Always use a sub-class of GtkFixed we can tag for a11y. This allows us to
936 * utilize GAIL for the toplevel window and toolkit implementation incl.
937 * key event listener support ..
943 static GType type
= 0;
946 static const GTypeInfo tinfo
=
948 sizeof (GtkFixedClass
),
949 nullptr, /* base init */
950 nullptr, /* base finalize */
951 reinterpret_cast<GClassInitFunc
>(ooo_fixed_class_init
), /* class init */
952 nullptr, /* class finalize */
953 nullptr, /* class data */
954 sizeof (GtkFixed
), /* instance size */
955 0, /* nb preallocs */
956 nullptr, /* instance init */
957 nullptr /* value table */
960 type
= g_type_register_static( GTK_TYPE_FIXED
, "OOoFixed",
961 &tinfo
, GTypeFlags(0));
967 void GtkSalFrame::updateScreenNumber()
970 GdkScreen
*pScreen
= gtk_widget_get_screen( m_pWindow
);
972 nScreen
= getDisplay()->getSystem()->getScreenMonitorIdx( pScreen
, maGeometry
.nX
, maGeometry
.nY
);
973 maGeometry
.nDisplayScreenNumber
= nScreen
;
976 GtkWidget
*GtkSalFrame::getMouseEventWidget() const
978 return GTK_WIDGET(m_pEventBox
);
981 static void damaged(void *handle
,
982 sal_Int32 nExtentsX
, sal_Int32 nExtentsY
,
983 sal_Int32 nExtentsWidth
, sal_Int32 nExtentsHeight
)
985 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(handle
);
986 pThis
->damaged(nExtentsX
, nExtentsY
, nExtentsWidth
, nExtentsHeight
);
989 void GtkSalFrame::InitCommon()
991 m_pSurface
= nullptr;
993 m_bSalObjectSetPosSize
= false;
995 m_aDamageHandler
.handle
= this;
996 m_aDamageHandler
.damaged
= ::damaged
;
998 m_aSmoothScrollIdle
.SetInvokeHandler(LINK(this, GtkSalFrame
, AsyncScroll
));
1000 m_pTopLevelGrid
= GTK_GRID(gtk_grid_new());
1001 gtk_container_add(GTK_CONTAINER(m_pWindow
), GTK_WIDGET(m_pTopLevelGrid
));
1003 m_pEventBox
= GTK_EVENT_BOX(gtk_event_box_new());
1004 gtk_widget_add_events( GTK_WIDGET(m_pEventBox
),
1005 GDK_ALL_EVENTS_MASK
);
1006 gtk_widget_set_vexpand(GTK_WIDGET(m_pEventBox
), true);
1007 gtk_widget_set_hexpand(GTK_WIDGET(m_pEventBox
), true);
1008 gtk_grid_attach(m_pTopLevelGrid
, GTK_WIDGET(m_pEventBox
), 0, 0, 1, 1);
1010 // add the fixed container child,
1011 // fixed is needed since we have to position plugin windows
1012 m_pFixedContainer
= GTK_FIXED(g_object_new( ooo_fixed_get_type(), nullptr ));
1013 gtk_container_add( GTK_CONTAINER(m_pEventBox
), GTK_WIDGET(m_pFixedContainer
) );
1015 GtkWidget
*pEventWidget
= getMouseEventWidget();
1017 gtk_widget_set_app_paintable(GTK_WIDGET(m_pFixedContainer
), true);
1018 /*non-X11 displays won't show anything at all without double-buffering
1020 if (GDK_IS_X11_DISPLAY(getGdkDisplay()))
1021 gtk_widget_set_double_buffered(GTK_WIDGET(m_pFixedContainer
), false);
1022 gtk_widget_set_redraw_on_allocate(GTK_WIDGET(m_pFixedContainer
), false);
1026 g_signal_connect( G_OBJECT(m_pWindow
), "style-updated", G_CALLBACK(signalStyleUpdated
), this );
1027 gtk_widget_set_has_tooltip(pEventWidget
, true);
1028 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "query-tooltip", G_CALLBACK(signalTooltipQuery
), this ));
1029 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "button-press-event", G_CALLBACK(signalButton
), this ));
1030 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "motion-notify-event", G_CALLBACK(signalMotion
), this ));
1031 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "button-release-event", G_CALLBACK(signalButton
), this ));
1032 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "leave-notify-event", G_CALLBACK(signalCrossing
), this ));
1033 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "enter-notify-event", G_CALLBACK(signalCrossing
), this ));
1037 gtk_drag_dest_set(GTK_WIDGET(pEventWidget
), GtkDestDefaults(0), nullptr, 0, GdkDragAction(0));
1038 gtk_drag_dest_set_track_motion(GTK_WIDGET(pEventWidget
), true);
1039 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-motion", G_CALLBACK(signalDragMotion
), this ));
1040 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-drop", G_CALLBACK(signalDragDrop
), this ));
1041 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-data-received", G_CALLBACK(signalDragDropReceived
), this ));
1042 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-leave", G_CALLBACK(signalDragLeave
), this ));
1045 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-end", G_CALLBACK(signalDragEnd
), this ));
1046 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-failed", G_CALLBACK(signalDragFailed
), this ));
1047 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-data-delete", G_CALLBACK(signalDragDelete
), this ));
1048 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "drag-data-get", G_CALLBACK(signalDragDataGet
), this ));
1049 m_aMouseSignalIds
.push_back(g_signal_connect( G_OBJECT(pEventWidget
), "scroll-event", G_CALLBACK(signalScroll
), this ));
1051 g_signal_connect( G_OBJECT(m_pFixedContainer
), "draw", G_CALLBACK(signalDraw
), this );
1052 g_signal_connect( G_OBJECT(m_pFixedContainer
), "realize", G_CALLBACK(signalRealize
), this );
1053 g_signal_connect( G_OBJECT(m_pFixedContainer
), "size-allocate", G_CALLBACK(sizeAllocated
), this );
1055 GtkGesture
*pSwipe
= gtk_gesture_swipe_new(pEventWidget
);
1056 g_signal_connect(pSwipe
, "swipe", G_CALLBACK(gestureSwipe
), this);
1057 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pSwipe
), GTK_PHASE_TARGET
);
1058 g_object_weak_ref(G_OBJECT(pEventWidget
), reinterpret_cast<GWeakNotify
>(g_object_unref
), pSwipe
);
1060 GtkGesture
*pLongPress
= gtk_gesture_long_press_new(pEventWidget
);
1061 g_signal_connect(pLongPress
, "pressed", G_CALLBACK(gestureLongPress
), this);
1062 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pLongPress
), GTK_PHASE_TARGET
);
1063 g_object_weak_ref(G_OBJECT(pEventWidget
), reinterpret_cast<GWeakNotify
>(g_object_unref
), pLongPress
);
1065 g_signal_connect( G_OBJECT(m_pWindow
), "focus-in-event", G_CALLBACK(signalFocus
), this );
1066 g_signal_connect( G_OBJECT(m_pWindow
), "focus-out-event", G_CALLBACK(signalFocus
), this );
1067 g_signal_connect( G_OBJECT(m_pWindow
), "map-event", G_CALLBACK(signalMap
), this );
1068 g_signal_connect( G_OBJECT(m_pWindow
), "unmap-event", G_CALLBACK(signalUnmap
), this );
1069 g_signal_connect( G_OBJECT(m_pWindow
), "configure-event", G_CALLBACK(signalConfigure
), this );
1070 g_signal_connect( G_OBJECT(m_pWindow
), "key-press-event", G_CALLBACK(signalKey
), this );
1071 g_signal_connect( G_OBJECT(m_pWindow
), "key-release-event", G_CALLBACK(signalKey
), this );
1072 g_signal_connect( G_OBJECT(m_pWindow
), "delete-event", G_CALLBACK(signalDelete
), this );
1073 g_signal_connect( G_OBJECT(m_pWindow
), "window-state-event", G_CALLBACK(signalWindowState
), this );
1074 g_signal_connect( G_OBJECT(m_pWindow
), "visibility-notify-event", G_CALLBACK(signalVisibility
), this );
1075 g_signal_connect( G_OBJECT(m_pWindow
), "destroy", G_CALLBACK(signalDestroy
), this );
1078 m_pCurrentCursor
= nullptr;
1079 m_nKeyModifiers
= ModKeyFlags::NONE
;
1080 m_bFullscreen
= false;
1081 m_bSpanMonitorsWhenFullscreen
= false;
1082 m_nState
= GDK_WINDOW_STATE_WITHDRAWN
;
1083 m_pIMHandler
= nullptr;
1084 m_pRegion
= nullptr;
1085 m_pDropTarget
= nullptr;
1086 m_pDragSource
= nullptr;
1088 m_pFormatConversionRequest
= nullptr;
1089 m_bGeometryIsProvisional
= false;
1090 m_ePointerStyle
= static_cast<PointerStyle
>(0xffff);
1091 m_pSalMenu
= nullptr;
1093 m_nMenuExportId
= 0;
1094 m_nAppMenuExportId
= 0;
1095 m_nActionGroupExportId
= 0;
1096 m_nAppActionGroupExportId
= 0;
1097 m_nHudAwarenessId
= 0;
1099 gtk_widget_add_events( m_pWindow
,
1100 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
1101 GDK_POINTER_MOTION_MASK
| GDK_POINTER_MOTION_HINT_MASK
|
1102 GDK_VISIBILITY_NOTIFY_MASK
| GDK_SCROLL_MASK
1106 gtk_widget_show_all(GTK_WIDGET(m_pTopLevelGrid
));
1108 // realize the window, we need an XWindow id
1109 gtk_widget_realize( m_pWindow
);
1112 m_aSystemData
.nSize
= sizeof( SystemEnvData
);
1113 m_aSystemData
.aWindow
= GetNativeWindowHandle(m_pWindow
);
1114 m_aSystemData
.aShellWindow
= reinterpret_cast<sal_IntPtr
>(this);
1115 m_aSystemData
.pSalFrame
= this;
1116 m_aSystemData
.pWidget
= m_pWindow
;
1117 m_aSystemData
.nScreen
= m_nXScreen
.getXScreen();
1118 m_aSystemData
.pToolkit
= "gtk3";
1119 GdkScreen
* pScreen
= gtk_window_get_screen(GTK_WINDOW(m_pWindow
));
1120 GdkVisual
* pVisual
= gdk_screen_get_system_visual(pScreen
);
1122 #if defined(GDK_WINDOWING_X11)
1123 GdkDisplay
*pDisplay
= getGdkDisplay();
1124 if (GDK_IS_X11_DISPLAY(pDisplay
))
1126 m_aSystemData
.pDisplay
= gdk_x11_display_get_xdisplay(pDisplay
);
1127 m_aSystemData
.pVisual
= gdk_x11_visual_get_xvisual(pVisual
);
1131 m_bGraphics
= false;
1132 m_pGraphics
= nullptr;
1134 m_nFloatFlags
= FloatWinPopupFlags::NONE
;
1135 m_bFloatPositioned
= false;
1137 m_nWidthRequest
= 0;
1138 m_nHeightRequest
= 0;
1140 // fake an initial geometry, gets updated via configure event or SetPosSize
1141 if (m_bDefaultPos
|| m_bDefaultSize
)
1143 Size aDefSize
= calcDefaultSize();
1146 maGeometry
.nWidth
= aDefSize
.Width();
1147 maGeometry
.nHeight
= aDefSize
.Height();
1148 maGeometry
.nTopDecoration
= 0;
1149 maGeometry
.nBottomDecoration
= 0;
1150 maGeometry
.nLeftDecoration
= 0;
1151 maGeometry
.nRightDecoration
= 0;
1153 updateScreenNumber();
1155 SetIcon(SV_ICON_ID_OFFICE
);
1158 GtkSalFrame
*GtkSalFrame::getFromWindow( GtkWindow
*pWindow
)
1160 return static_cast<GtkSalFrame
*>(g_object_get_data( G_OBJECT( pWindow
), "SalFrame" ));
1163 void GtkSalFrame::Init( SalFrame
* pParent
, SalFrameStyleFlags nStyle
)
1165 if( nStyle
& SalFrameStyleFlags::DEFAULT
) // ensure default style
1167 nStyle
|= SalFrameStyleFlags::MOVEABLE
| SalFrameStyleFlags::SIZEABLE
| SalFrameStyleFlags::CLOSEABLE
;
1168 nStyle
&= ~SalFrameStyleFlags::FLOAT
;
1171 m_pParent
= static_cast<GtkSalFrame
*>(pParent
);
1172 m_pForeignParent
= nullptr;
1173 m_aForeignParentWindow
= None
;
1174 m_pForeignTopLevel
= nullptr;
1175 m_aForeignTopLevelWindow
= None
;
1178 GtkWindowType eWinType
= ( (nStyle
& SalFrameStyleFlags::FLOAT
) &&
1179 ! (nStyle
& SalFrameStyleFlags::OWNERDRAWDECORATION
)
1181 ? GTK_WINDOW_POPUP
: GTK_WINDOW_TOPLEVEL
;
1183 if( nStyle
& SalFrameStyleFlags::SYSTEMCHILD
)
1185 m_pWindow
= gtk_event_box_new();
1188 // insert into container
1189 gtk_fixed_put( m_pParent
->getFixedContainer(),
1196 m_pWindow
= gtk_window_new(eWinType
);
1199 g_object_set_data( G_OBJECT( m_pWindow
), "SalFrame", this );
1200 g_object_set_data( G_OBJECT( m_pWindow
), "libo-version", const_cast<char *>(LIBO_VERSION_DOTTED
));
1202 // force wm class hint
1206 m_sWMClass
= m_pParent
->m_sWMClass
;
1210 if( m_pParent
&& m_pParent
->m_pWindow
&& ! isChild() )
1211 gtk_window_set_screen( GTK_WINDOW(m_pWindow
), gtk_window_get_screen( GTK_WINDOW(m_pParent
->m_pWindow
) ) );
1215 if (!(m_pParent
->m_nStyle
& SalFrameStyleFlags::PLUG
))
1216 gtk_window_set_transient_for( GTK_WINDOW(m_pWindow
), GTK_WINDOW(m_pParent
->m_pWindow
) );
1217 m_pParent
->m_aChildren
.push_back( this );
1218 gtk_window_group_add_window(gtk_window_get_group(GTK_WINDOW(m_pParent
->m_pWindow
)), GTK_WINDOW(m_pWindow
));
1222 gtk_window_group_add_window(gtk_window_group_new(), GTK_WINDOW(m_pWindow
));
1223 g_object_unref(gtk_window_get_group(GTK_WINDOW(m_pWindow
)));
1227 bool bDecoHandling
=
1229 ( ! (nStyle
& SalFrameStyleFlags::FLOAT
) ||
1230 (nStyle
& SalFrameStyleFlags::OWNERDRAWDECORATION
) );
1234 GdkWindowTypeHint eType
= GDK_WINDOW_TYPE_HINT_NORMAL
;
1235 if( (nStyle
& SalFrameStyleFlags::DIALOG
) && m_pParent
!= nullptr )
1236 eType
= GDK_WINDOW_TYPE_HINT_DIALOG
;
1237 if( nStyle
& SalFrameStyleFlags::INTRO
)
1239 gtk_window_set_role( GTK_WINDOW(m_pWindow
), "splashscreen" );
1240 eType
= GDK_WINDOW_TYPE_HINT_SPLASHSCREEN
;
1242 else if( nStyle
& SalFrameStyleFlags::TOOLWINDOW
)
1244 eType
= GDK_WINDOW_TYPE_HINT_DIALOG
;
1245 gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow
), true );
1247 else if( nStyle
& SalFrameStyleFlags::OWNERDRAWDECORATION
)
1249 eType
= GDK_WINDOW_TYPE_HINT_TOOLBAR
;
1250 gtk_window_set_focus_on_map(GTK_WINDOW(m_pWindow
), false);
1251 gtk_window_set_decorated(GTK_WINDOW(m_pWindow
), false);
1253 gtk_window_set_type_hint( GTK_WINDOW(m_pWindow
), eType
);
1254 gtk_window_set_gravity( GTK_WINDOW(m_pWindow
), GDK_GRAVITY_STATIC
);
1255 gtk_window_set_resizable( GTK_WINDOW(m_pWindow
), bool(nStyle
& SalFrameStyleFlags::SIZEABLE
) );
1257 #if defined(GDK_WINDOWING_WAYLAND)
1258 //rhbz#1392145 under wayland/csd if we've overridden the default widget direction in order to set LibreOffice's
1259 //UI to the configured ui language but the system ui locale is a different text direction, then the toplevel
1260 //built-in close button of the titlebar follows the overridden direction rather than continue in the same
1261 //direction as every other titlebar on the user's desktop. So if they don't match set an explicit
1262 //header bar with the desired 'outside' direction
1263 if ((eType
== GDK_WINDOW_TYPE_HINT_NORMAL
|| eType
== GDK_WINDOW_TYPE_HINT_DIALOG
) && GDK_IS_WAYLAND_DISPLAY(GtkSalFrame::getGdkDisplay()))
1265 const bool bDesktopIsRTL
= MsLangId::isRightToLeft(MsLangId::getSystemUILanguage());
1266 const bool bAppIsRTL
= gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL
;
1267 if (bDesktopIsRTL
!= bAppIsRTL
)
1269 m_pHeaderBar
= GTK_HEADER_BAR(gtk_header_bar_new());
1270 gtk_widget_set_direction(GTK_WIDGET(m_pHeaderBar
), bDesktopIsRTL
? GTK_TEXT_DIR_RTL
: GTK_TEXT_DIR_LTR
);
1271 gtk_header_bar_set_show_close_button(m_pHeaderBar
, true);
1272 gtk_window_set_titlebar(GTK_WINDOW(m_pWindow
), GTK_WIDGET(m_pHeaderBar
));
1273 gtk_widget_show(GTK_WIDGET(m_pHeaderBar
));
1278 else if( nStyle
& SalFrameStyleFlags::FLOAT
)
1279 gtk_window_set_type_hint( GTK_WINDOW(m_pWindow
), GDK_WINDOW_TYPE_HINT_POPUP_MENU
);
1283 if( eWinType
== GTK_WINDOW_TOPLEVEL
)
1285 // Enable DBus native menu if available.
1286 ensure_dbus_setup( this );
1291 GdkNativeWindow
GtkSalFrame::findTopLevelSystemWindow( GdkNativeWindow
)
1293 //FIXME: no findToplevelSystemWindow
1297 void GtkSalFrame::Init( SystemParentData
* pSysData
)
1299 m_pParent
= nullptr;
1300 m_aForeignParentWindow
= pSysData
->aWindow
;
1301 m_pForeignParent
= nullptr;
1302 m_aForeignTopLevelWindow
= findTopLevelSystemWindow(pSysData
->aWindow
);
1303 m_pForeignTopLevel
= gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignTopLevelWindow
);
1304 gdk_window_set_events( m_pForeignTopLevel
, GDK_STRUCTURE_MASK
);
1306 if( pSysData
->nSize
> sizeof(pSysData
->nSize
)+sizeof(pSysData
->aWindow
) && pSysData
->bXEmbedSupport
)
1308 m_pWindow
= gtk_plug_new_for_display( getGdkDisplay(), pSysData
->aWindow
);
1309 m_bWindowIsGtkPlug
= true;
1310 widget_set_can_default( m_pWindow
, true );
1311 widget_set_can_focus( m_pWindow
, true );
1312 gtk_widget_set_sensitive( m_pWindow
, true );
1316 m_pWindow
= gtk_window_new( GTK_WINDOW_POPUP
);
1317 m_bWindowIsGtkPlug
= false;
1319 m_nStyle
= SalFrameStyleFlags::PLUG
;
1322 m_pForeignParent
= gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignParentWindow
);
1323 gdk_window_set_events( m_pForeignParent
, GDK_STRUCTURE_MASK
);
1325 //FIXME: Handling embedded windows, is going to be fun ...
1328 void GtkSalFrame::SetExtendedFrameStyle(SalExtStyle
)
1332 SalGraphics
* GtkSalFrame::AcquireGraphics()
1339 m_pGraphics
.reset( new GtkSalGraphics( this, m_pWindow
) );
1343 TriggerPaintEvent();
1345 m_pGraphics
->setSurface(m_pSurface
, m_aFrameSize
);
1348 return m_pGraphics
.get();
1351 void GtkSalFrame::ReleaseGraphics( SalGraphics
* pGraphics
)
1354 assert( pGraphics
== m_pGraphics
.get() );
1355 m_bGraphics
= false;
1358 bool GtkSalFrame::PostEvent(ImplSVEvent
* pData
)
1360 getDisplay()->SendInternalEvent( this, pData
);
1364 void GtkSalFrame::SetTitle( const OUString
& rTitle
)
1367 if( m_pWindow
&& ! isChild() )
1369 OString
sTitle(OUStringToOString(rTitle
, RTL_TEXTENCODING_UTF8
));
1370 gtk_window_set_title(GTK_WINDOW(m_pWindow
), sTitle
.getStr());
1372 gtk_header_bar_set_title(m_pHeaderBar
, sTitle
.getStr());
1376 void GtkSalFrame::SetIcon( sal_uInt16 nIcon
)
1378 if( (m_nStyle
& (SalFrameStyleFlags::PLUG
|SalFrameStyleFlags::SYSTEMCHILD
|SalFrameStyleFlags::FLOAT
|SalFrameStyleFlags::INTRO
|SalFrameStyleFlags::OWNERDRAWDECORATION
))
1384 if (nIcon
== SV_ICON_ID_TEXT
)
1385 appicon
= g_strdup ("libreoffice-writer");
1386 else if (nIcon
== SV_ICON_ID_SPREADSHEET
)
1387 appicon
= g_strdup ("libreoffice-calc");
1388 else if (nIcon
== SV_ICON_ID_DRAWING
)
1389 appicon
= g_strdup ("libreoffice-draw");
1390 else if (nIcon
== SV_ICON_ID_PRESENTATION
)
1391 appicon
= g_strdup ("libreoffice-impress");
1392 else if (nIcon
== SV_ICON_ID_DATABASE
)
1393 appicon
= g_strdup ("libreoffice-base");
1394 else if (nIcon
== SV_ICON_ID_FORMULA
)
1395 appicon
= g_strdup ("libreoffice-math");
1397 appicon
= g_strdup ("libreoffice-startcenter");
1399 gtk_window_set_icon_name (GTK_WINDOW (m_pWindow
), appicon
);
1403 void GtkSalFrame::SetMenu( SalMenu
* pSalMenu
)
1405 m_pSalMenu
= static_cast<GtkSalMenu
*>(pSalMenu
);
1408 SalMenu
* GtkSalFrame::GetMenu()
1413 void GtkSalFrame::DrawMenuBar()
1417 void GtkSalFrame::Center()
1420 gtk_window_set_position(GTK_WINDOW(m_pWindow
), GTK_WIN_POS_CENTER_ON_PARENT
);
1422 gtk_window_set_position(GTK_WINDOW(m_pWindow
), GTK_WIN_POS_CENTER
);
1425 Size
GtkSalFrame::calcDefaultSize()
1427 Size
aScreenSize(getDisplay()->GetScreenSize(GetDisplayScreen()));
1428 int scale
= gtk_widget_get_scale_factor(m_pWindow
);
1429 aScreenSize
.setWidth( aScreenSize
.Width() / scale
);
1430 aScreenSize
.setHeight( aScreenSize
.Height() / scale
);
1431 return bestmaxFrameSizeForScreenSize(aScreenSize
);
1434 void GtkSalFrame::SetDefaultSize()
1436 Size aDefSize
= calcDefaultSize();
1438 SetPosSize( 0, 0, aDefSize
.Width(), aDefSize
.Height(),
1439 SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
);
1441 if( (m_nStyle
& SalFrameStyleFlags::DEFAULT
) && m_pWindow
)
1442 gtk_window_maximize( GTK_WINDOW(m_pWindow
) );
1445 void GtkSalFrame::Show( bool bVisible
, bool /*bNoActivate*/ )
1451 getDisplay()->startupNotificationCompleted();
1455 if( m_bDefaultSize
)
1459 if (isFloatGrabWindow() && !getDisplay()->GetCaptureFrame())
1461 m_pParent
->grabPointer(true, true);
1462 m_pParent
->addGrabLevel();
1465 #if defined(GDK_WINDOWING_WAYLAND)
1466 //rhbz#1334915, gnome#779143, tdf#100158
1467 //gtk under wayland lacks a way to change the app_id
1468 //of a window, so brute force everything as a
1469 //startcenter when initially shown to at least get
1470 //the default LibreOffice icon and not the broken
1472 if (GDK_IS_WAYLAND_DISPLAY(getGdkDisplay()))
1474 OString
sOrigName(g_get_prgname());
1475 g_set_prgname("libreoffice-startcenter");
1476 gtk_widget_show(m_pWindow
);
1477 g_set_prgname(sOrigName
.getStr());
1481 gtk_widget_show(m_pWindow
);
1484 gtk_widget_show(m_pWindow
);
1487 if( isFloatGrabWindow() )
1490 if (!getDisplay()->GetCaptureFrame())
1492 grabPointer(true, true);
1495 // #i44068# reset parent's IM context
1497 m_pParent
->EndExtTextInput(EndExtTextInputFlags::NONE
);
1502 if( isFloatGrabWindow() )
1505 if (!getDisplay()->GetCaptureFrame())
1509 m_pParent
->removeGrabLevel();
1510 m_pParent
->grabPointer(false);
1513 gtk_widget_hide( m_pWindow
);
1515 m_pIMHandler
->focusChanged( false );
1520 void GtkSalFrame::setMinMaxSize()
1522 /* #i34504# metacity (and possibly others) do not treat
1523 * _NET_WM_STATE_FULLSCREEN and max_width/height independently;
1524 * whether they should is undefined. So don't set the max size hint
1525 * for a full screen window.
1527 if( m_pWindow
&& ! isChild() )
1531 if( m_nStyle
& SalFrameStyleFlags::SIZEABLE
)
1533 if( m_aMinSize
.Width() && m_aMinSize
.Height() && ! m_bFullscreen
)
1535 aGeo
.min_width
= m_aMinSize
.Width();
1536 aGeo
.min_height
= m_aMinSize
.Height();
1537 aHints
|= GDK_HINT_MIN_SIZE
;
1539 if( m_aMaxSize
.Width() && m_aMaxSize
.Height() && ! m_bFullscreen
)
1541 aGeo
.max_width
= m_aMaxSize
.Width();
1542 aGeo
.max_height
= m_aMaxSize
.Height();
1543 aHints
|= GDK_HINT_MAX_SIZE
;
1548 if (!m_bFullscreen
&& m_nWidthRequest
&& m_nHeightRequest
)
1550 aGeo
.min_width
= m_nWidthRequest
;
1551 aGeo
.min_height
= m_nHeightRequest
;
1552 aHints
|= GDK_HINT_MIN_SIZE
;
1554 aGeo
.max_width
= m_nWidthRequest
;
1555 aGeo
.max_height
= m_nHeightRequest
;
1556 aHints
|= GDK_HINT_MAX_SIZE
;
1560 if( m_bFullscreen
&& m_aMaxSize
.Width() && m_aMaxSize
.Height() )
1562 aGeo
.max_width
= m_aMaxSize
.Width();
1563 aGeo
.max_height
= m_aMaxSize
.Height();
1564 aHints
|= GDK_HINT_MAX_SIZE
;
1568 gtk_window_set_geometry_hints( GTK_WINDOW(m_pWindow
),
1571 GdkWindowHints( aHints
) );
1576 void GtkSalFrame::SetMaxClientSize( long nWidth
, long nHeight
)
1580 m_aMaxSize
= Size( nWidth
, nHeight
);
1584 void GtkSalFrame::SetMinClientSize( long nWidth
, long nHeight
)
1588 m_aMinSize
= Size( nWidth
, nHeight
);
1591 widget_set_size_request(nWidth
, nHeight
);
1597 void GtkSalFrame::AllocateFrame()
1599 basegfx::B2IVector
aFrameSize( maGeometry
.nWidth
, maGeometry
.nHeight
);
1600 if (!m_pSurface
|| m_aFrameSize
.getX() != aFrameSize
.getX() ||
1601 m_aFrameSize
.getY() != aFrameSize
.getY() )
1603 if( aFrameSize
.getX() == 0 )
1604 aFrameSize
.setX( 1 );
1605 if( aFrameSize
.getY() == 0 )
1606 aFrameSize
.setY( 1 );
1609 cairo_surface_destroy(m_pSurface
);
1611 m_pSurface
= gdk_window_create_similar_surface(widget_get_window(m_pWindow
),
1612 CAIRO_CONTENT_COLOR_ALPHA
,
1615 m_aFrameSize
= aFrameSize
;
1617 cairo_surface_set_user_data(m_pSurface
, SvpSalGraphics::getDamageKey(), &m_aDamageHandler
, nullptr);
1618 SAL_INFO("vcl.gtk3", "allocated Frame size of " << maGeometry
.nWidth
<< " x " << maGeometry
.nHeight
);
1621 m_pGraphics
->setSurface(m_pSurface
, m_aFrameSize
);
1625 void GtkSalFrame::SetPosSize( long nX
, long nY
, long nWidth
, long nHeight
, sal_uInt16 nFlags
)
1627 if( !m_pWindow
|| isChild( true, false ) )
1630 if( (nFlags
& ( SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
)) &&
1631 (nWidth
> 0 && nHeight
> 0 ) // sometimes stupid things happen
1634 m_bDefaultSize
= false;
1636 if( isChild( false ) )
1637 widget_set_size_request(nWidth
, nHeight
);
1638 else if( ! ( m_nState
& GDK_WINDOW_STATE_MAXIMIZED
) )
1639 window_resize(nWidth
, nHeight
);
1643 else if( m_bDefaultSize
)
1646 m_bDefaultSize
= false;
1648 if( nFlags
& ( SAL_FRAME_POSSIZE_X
| SAL_FRAME_POSSIZE_Y
) )
1652 if( AllSettings::GetLayoutRTL() )
1653 nX
= m_pParent
->maGeometry
.nWidth
-m_nWidthRequest
-1-nX
;
1654 nX
+= m_pParent
->maGeometry
.nX
;
1655 nY
+= m_pParent
->maGeometry
.nY
;
1660 m_bGeometryIsProvisional
= true;
1662 m_bDefaultPos
= false;
1666 updateScreenNumber();
1668 else if( m_bDefaultPos
)
1671 m_bDefaultPos
= false;
1674 void GtkSalFrame::GetClientSize( long& rWidth
, long& rHeight
)
1676 if( m_pWindow
&& !(m_nState
& GDK_WINDOW_STATE_ICONIFIED
) )
1678 rWidth
= maGeometry
.nWidth
;
1679 rHeight
= maGeometry
.nHeight
;
1682 rWidth
= rHeight
= 0;
1685 void GtkSalFrame::GetWorkArea( tools::Rectangle
& rRect
)
1687 GdkScreen
*pScreen
= gtk_window_get_screen(GTK_WINDOW(m_pWindow
));
1688 tools::Rectangle aRetRect
;
1689 int max
= gdk_screen_get_n_monitors (pScreen
);
1690 for (int i
= 0; i
< max
; ++i
)
1693 gdk_screen_get_monitor_workarea(pScreen
, i
, &aRect
);
1694 tools::Rectangle
aMonitorRect(aRect
.x
, aRect
.y
, aRect
.x
+aRect
.width
, aRect
.y
+aRect
.height
);
1695 aRetRect
.Union(aMonitorRect
);
1700 SalFrame
* GtkSalFrame::GetParent() const
1705 void GtkSalFrame::SetWindowState( const SalFrameState
* pState
)
1707 if( ! m_pWindow
|| ! pState
|| isChild( true, false ) )
1710 const WindowStateMask nMaxGeometryMask
=
1711 WindowStateMask::X
| WindowStateMask::Y
|
1712 WindowStateMask::Width
| WindowStateMask::Height
|
1713 WindowStateMask::MaximizedX
| WindowStateMask::MaximizedY
|
1714 WindowStateMask::MaximizedWidth
| WindowStateMask::MaximizedHeight
;
1716 if( (pState
->mnMask
& WindowStateMask::State
) &&
1717 ! ( m_nState
& GDK_WINDOW_STATE_MAXIMIZED
) &&
1718 (pState
->mnState
& WindowStateState::Maximized
) &&
1719 (pState
->mnMask
& nMaxGeometryMask
) == nMaxGeometryMask
)
1721 resizeWindow( pState
->mnWidth
, pState
->mnHeight
);
1722 moveWindow( pState
->mnX
, pState
->mnY
);
1723 m_bDefaultPos
= m_bDefaultSize
= false;
1725 updateScreenNumber();
1727 m_nState
= GdkWindowState( m_nState
| GDK_WINDOW_STATE_MAXIMIZED
);
1728 m_aRestorePosSize
= tools::Rectangle( Point( pState
->mnX
, pState
->mnY
),
1729 Size( pState
->mnWidth
, pState
->mnHeight
) );
1731 else if( pState
->mnMask
& (WindowStateMask::X
| WindowStateMask::Y
|
1732 WindowStateMask::Width
| WindowStateMask::Height
) )
1734 sal_uInt16 nPosSizeFlags
= 0;
1735 long nX
= pState
->mnX
- (m_pParent
? m_pParent
->maGeometry
.nX
: 0);
1736 long nY
= pState
->mnY
- (m_pParent
? m_pParent
->maGeometry
.nY
: 0);
1737 if( pState
->mnMask
& WindowStateMask::X
)
1738 nPosSizeFlags
|= SAL_FRAME_POSSIZE_X
;
1740 nX
= maGeometry
.nX
- (m_pParent
? m_pParent
->maGeometry
.nX
: 0);
1741 if( pState
->mnMask
& WindowStateMask::Y
)
1742 nPosSizeFlags
|= SAL_FRAME_POSSIZE_Y
;
1744 nY
= maGeometry
.nY
- (m_pParent
? m_pParent
->maGeometry
.nY
: 0);
1745 if( pState
->mnMask
& WindowStateMask::Width
)
1746 nPosSizeFlags
|= SAL_FRAME_POSSIZE_WIDTH
;
1747 if( pState
->mnMask
& WindowStateMask::Height
)
1748 nPosSizeFlags
|= SAL_FRAME_POSSIZE_HEIGHT
;
1749 SetPosSize( nX
, nY
, pState
->mnWidth
, pState
->mnHeight
, nPosSizeFlags
);
1751 if( pState
->mnMask
& WindowStateMask::State
&& ! isChild() )
1753 if( pState
->mnState
& WindowStateState::Maximized
)
1754 gtk_window_maximize( GTK_WINDOW(m_pWindow
) );
1756 gtk_window_unmaximize( GTK_WINDOW(m_pWindow
) );
1757 /* #i42379# there is no rollup state in GDK; and rolled up windows are
1758 * (probably depending on the WM) reported as iconified. If we iconify a
1759 * window here that was e.g. a dialog, then it will be unmapped but still
1760 * not be displayed in the task list, so it's an iconified window that
1761 * the user cannot get out of this state. So do not set the iconified state
1762 * on windows with a parent (that is transient frames) since these tend
1763 * to not be represented in an icon task list.
1765 if( (pState
->mnState
& WindowStateState::Minimized
)
1767 gtk_window_iconify( GTK_WINDOW(m_pWindow
) );
1769 gtk_window_deiconify( GTK_WINDOW(m_pWindow
) );
1771 TriggerPaintEvent();
1776 void GetPosAndSize(GtkWindow
*pWindow
, long& rX
, long &rY
, long &rWidth
, long &rHeight
)
1778 gint root_x
, root_y
;
1779 gtk_window_get_position(GTK_WINDOW(pWindow
), &root_x
, &root_y
);
1783 gtk_window_get_size(GTK_WINDOW(pWindow
), &width
, &height
);
1788 tools::Rectangle
GetPosAndSize(GtkWindow
*pWindow
)
1790 long nX
, nY
, nWidth
, nHeight
;
1791 GetPosAndSize(pWindow
, nX
, nY
, nWidth
, nHeight
);
1792 return tools::Rectangle(nX
, nY
, nX
+ nWidth
, nY
+ nHeight
);
1796 bool GtkSalFrame::GetWindowState( SalFrameState
* pState
)
1798 pState
->mnState
= WindowStateState::Normal
;
1799 pState
->mnMask
= WindowStateMask::State
;
1800 // rollup ? gtk 2.2 does not seem to support the shaded state
1801 if( m_nState
& GDK_WINDOW_STATE_ICONIFIED
)
1802 pState
->mnState
|= WindowStateState::Minimized
;
1803 if( m_nState
& GDK_WINDOW_STATE_MAXIMIZED
)
1805 pState
->mnState
|= WindowStateState::Maximized
;
1806 pState
->mnX
= m_aRestorePosSize
.Left();
1807 pState
->mnY
= m_aRestorePosSize
.Top();
1808 pState
->mnWidth
= m_aRestorePosSize
.GetWidth();
1809 pState
->mnHeight
= m_aRestorePosSize
.GetHeight();
1810 GetPosAndSize(GTK_WINDOW(m_pWindow
), pState
->mnMaximizedX
, pState
->mnMaximizedY
,
1811 pState
->mnMaximizedWidth
, pState
->mnMaximizedHeight
);
1812 pState
->mnMask
|= WindowStateMask::MaximizedX
|
1813 WindowStateMask::MaximizedY
|
1814 WindowStateMask::MaximizedWidth
|
1815 WindowStateMask::MaximizedHeight
;
1819 GetPosAndSize(GTK_WINDOW(m_pWindow
), pState
->mnX
, pState
->mnY
,
1820 pState
->mnWidth
, pState
->mnHeight
);
1822 pState
->mnMask
|= WindowStateMask::X
|
1823 WindowStateMask::Y
|
1824 WindowStateMask::Width
|
1825 WindowStateMask::Height
;
1830 void GtkSalFrame::SetScreen( unsigned int nNewScreen
, SetType eType
, tools::Rectangle
const *pSize
)
1835 if (maGeometry
.nDisplayScreenNumber
== nNewScreen
&& eType
== SetType::RetainSize
)
1838 int nX
= maGeometry
.nX
, nY
= maGeometry
.nY
,
1839 nWidth
= maGeometry
.nWidth
, nHeight
= maGeometry
.nHeight
;
1840 GdkScreen
*pScreen
= nullptr;
1841 GdkRectangle aNewMonitor
;
1843 bool bSpanAllScreens
= nNewScreen
== static_cast<unsigned int>(-1);
1844 m_bSpanMonitorsWhenFullscreen
= bSpanAllScreens
&& getDisplay()->getSystem()->GetDisplayScreenCount() > 1;
1846 if (m_bSpanMonitorsWhenFullscreen
) //span all screens
1848 pScreen
= gtk_widget_get_screen( m_pWindow
);
1851 aNewMonitor
.width
= gdk_screen_get_width(pScreen
);
1852 aNewMonitor
.height
= gdk_screen_get_height(pScreen
);
1856 bool bSameMonitor
= false;
1858 if (!bSpanAllScreens
)
1860 pScreen
= getDisplay()->getSystem()->getScreenMonitorFromIdx( nNewScreen
, nMonitor
);
1863 g_warning ("Attempt to move GtkSalFrame to invalid screen %d => "
1864 "fallback to current\n", nNewScreen
);
1870 pScreen
= gtk_widget_get_screen( m_pWindow
);
1871 bSameMonitor
= true;
1874 // Heavy lifting, need to move screen ...
1875 if( pScreen
!= gtk_widget_get_screen( m_pWindow
))
1876 gtk_window_set_screen( GTK_WINDOW( m_pWindow
), pScreen
);
1878 gint nOldMonitor
= gdk_screen_get_monitor_at_window(
1879 pScreen
, widget_get_window( m_pWindow
) );
1881 nMonitor
= nOldMonitor
;
1883 #if OSL_DEBUG_LEVEL > 1
1884 if( nMonitor
== nOldMonitor
)
1885 g_warning( "An apparently pointless SetScreen - should we elide it ?" );
1888 GdkRectangle aOldMonitor
;
1889 gdk_screen_get_monitor_geometry( pScreen
, nOldMonitor
, &aOldMonitor
);
1890 gdk_screen_get_monitor_geometry( pScreen
, nMonitor
, &aNewMonitor
);
1892 nX
= aNewMonitor
.x
+ nX
- aOldMonitor
.x
;
1893 nY
= aNewMonitor
.y
+ nY
- aOldMonitor
.y
;
1896 bool bResize
= false;
1897 bool bVisible
= IS_WIDGET_MAPPED( m_pWindow
);
1901 if( eType
== SetType::Fullscreen
)
1905 nWidth
= aNewMonitor
.width
;
1906 nHeight
= aNewMonitor
.height
;
1907 m_nStyle
|= SalFrameStyleFlags::PARTIAL_FULLSCREEN
;
1910 // #i110881# for the benefit of compiz set a max size here
1911 // else setting to fullscreen fails for unknown reasons
1912 m_aMaxSize
.setWidth( aNewMonitor
.width
);
1913 m_aMaxSize
.setHeight( aNewMonitor
.height
);
1916 if( pSize
&& eType
== SetType::UnFullscreen
)
1920 nWidth
= pSize
->GetWidth();
1921 nHeight
= pSize
->GetHeight();
1922 m_nStyle
&= ~SalFrameStyleFlags::PARTIAL_FULLSCREEN
;
1928 // temporarily re-sizeable
1929 if( !(m_nStyle
& SalFrameStyleFlags::SIZEABLE
) )
1930 gtk_window_set_resizable( GTK_WINDOW(m_pWindow
), TRUE
);
1931 window_resize(nWidth
, nHeight
);
1934 gtk_window_move(GTK_WINDOW(m_pWindow
), nX
, nY
);
1936 gdk_window_set_fullscreen_mode( widget_get_window(m_pWindow
), m_bSpanMonitorsWhenFullscreen
1937 ? GDK_FULLSCREEN_ON_ALL_MONITORS
: GDK_FULLSCREEN_ON_CURRENT_MONITOR
);
1939 GtkWidget
* pMenuBarContainerWidget
= m_pSalMenu
? m_pSalMenu
->GetMenuBarContainerWidget() : nullptr;
1940 if( eType
== SetType::Fullscreen
)
1942 if (pMenuBarContainerWidget
)
1943 gtk_widget_hide(pMenuBarContainerWidget
);
1944 if (m_bSpanMonitorsWhenFullscreen
)
1945 gtk_window_fullscreen(GTK_WINDOW(m_pWindow
));
1948 gtk_window_fullscreen_on_monitor(GTK_WINDOW(m_pWindow
), pScreen
, nMonitor
);
1952 else if( eType
== SetType::UnFullscreen
)
1954 if (pMenuBarContainerWidget
)
1955 gtk_widget_show(pMenuBarContainerWidget
);
1956 gtk_window_unfullscreen( GTK_WINDOW( m_pWindow
) );
1959 if( eType
== SetType::UnFullscreen
&&
1960 !(m_nStyle
& SalFrameStyleFlags::SIZEABLE
) )
1961 gtk_window_set_resizable( GTK_WINDOW( m_pWindow
), FALSE
);
1963 // FIXME: we should really let gtk+ handle our widget hierarchy ...
1964 if( m_pParent
&& gtk_widget_get_screen( m_pParent
->m_pWindow
) != pScreen
)
1965 SetParent( nullptr );
1966 std::list
< GtkSalFrame
* > aChildren
= m_aChildren
;
1967 for (auto const& child
: aChildren
)
1968 child
->SetScreen( nNewScreen
, SetType::RetainSize
);
1970 m_bDefaultPos
= m_bDefaultSize
= false;
1971 updateScreenNumber();
1977 void GtkSalFrame::SetScreenNumber( unsigned int nNewScreen
)
1979 SetScreen( nNewScreen
, SetType::RetainSize
);
1982 void GtkSalFrame::updateWMClass()
1984 OString aResClass
= OUStringToOString(m_sWMClass
, RTL_TEXTENCODING_ASCII_US
);
1985 const char *pResClass
= !aResClass
.isEmpty() ? aResClass
.getStr() :
1986 SalGenericSystem::getFrameClassName();
1989 if (!getDisplay()->IsX11Display())
1992 display
= GDK_DISPLAY_XDISPLAY(getGdkDisplay());
1994 if( IS_WIDGET_REALIZED( m_pWindow
) )
1996 XClassHint
* pClass
= XAllocClassHint();
1997 OString aResName
= SalGenericSystem::getFrameResName();
1998 pClass
->res_name
= const_cast<char*>(aResName
.getStr());
1999 pClass
->res_class
= const_cast<char*>(pResClass
);
2000 XSetClassHint( display
,
2001 widget_get_xid(m_pWindow
),
2007 void GtkSalFrame::SetApplicationID( const OUString
&rWMClass
)
2009 if( rWMClass
!= m_sWMClass
&& ! isChild() )
2011 m_sWMClass
= rWMClass
;
2014 for (auto const& child
: m_aChildren
)
2015 child
->SetApplicationID(rWMClass
);
2019 void GtkSalFrame::ShowFullScreen( bool bFullScreen
, sal_Int32 nScreen
)
2021 m_bFullscreen
= bFullScreen
;
2023 if( !m_pWindow
|| isChild() )
2028 m_aRestorePosSize
= GetPosAndSize(GTK_WINDOW(m_pWindow
));
2029 SetScreen( nScreen
, SetType::Fullscreen
);
2033 SetScreen( nScreen
, SetType::UnFullscreen
,
2034 !m_aRestorePosSize
.IsEmpty() ? &m_aRestorePosSize
: nullptr );
2035 m_aRestorePosSize
= tools::Rectangle();
2039 void GtkSalFrame::StartPresentation( bool bStart
)
2041 boost::optional
<guint
> aWindow
;
2042 boost::optional
<Display
*> aDisplay
;
2043 if( getDisplay()->IsX11Display() )
2045 aWindow
= widget_get_xid(m_pWindow
);
2046 aDisplay
= GDK_DISPLAY_XDISPLAY( getGdkDisplay() );
2049 m_ScreenSaverInhibitor
.inhibit( bStart
,
2051 getDisplay()->IsX11Display(),
2056 void GtkSalFrame::SetAlwaysOnTop( bool bOnTop
)
2059 gtk_window_set_keep_above( GTK_WINDOW( m_pWindow
), bOnTop
);
2062 static guint32 nLastUserInputTime
= GDK_CURRENT_TIME
;
2064 guint32
GtkSalFrame::GetLastInputEventTime()
2066 return nLastUserInputTime
;
2069 void GtkSalFrame::UpdateLastInputEventTime(guint32 nUserInputTime
)
2071 //gtk3 can generate a synthetic crossing event with a useless 0
2072 //(GDK_CURRENT_TIME) timestamp on showing a menu from the main
2073 //menubar, which is unhelpful, so ignore the 0 timestamps
2074 if (nUserInputTime
== GDK_CURRENT_TIME
)
2076 nLastUserInputTime
= nUserInputTime
;
2079 void GtkSalFrame::ToTop( SalFrameToTop nFlags
)
2083 if( isChild( false ) )
2084 gtk_widget_grab_focus( m_pWindow
);
2085 else if( IS_WIDGET_MAPPED( m_pWindow
) )
2087 if (!(nFlags
& SalFrameToTop::GrabFocusOnly
))
2088 gtk_window_present_with_time(GTK_WINDOW(m_pWindow
), GetLastInputEventTime());
2090 gdk_window_focus(widget_get_window(m_pWindow
), GetLastInputEventTime());
2094 if( nFlags
& SalFrameToTop::RestoreWhenMin
)
2095 gtk_window_present( GTK_WINDOW(m_pWindow
) );
2100 void GtkSalFrame::SetPointer( PointerStyle ePointerStyle
)
2102 if( m_pWindow
&& ePointerStyle
!= m_ePointerStyle
)
2104 m_ePointerStyle
= ePointerStyle
;
2105 GdkCursor
*pCursor
= getDisplay()->getCursor( ePointerStyle
);
2106 gdk_window_set_cursor( widget_get_window(m_pWindow
), pCursor
);
2107 m_pCurrentCursor
= pCursor
;
2109 // #i80791# use grabPointer the same way as CaptureMouse, respective float grab
2110 if( getDisplay()->MouseCaptured( this ) )
2111 grabPointer( true );
2112 else if( m_nFloats
> 0 )
2113 grabPointer( true, true );
2117 void GtkSalFrame::grabPointer( bool bGrab
, bool bOwnerEvents
)
2119 static const char* pEnv
= getenv( "SAL_NO_MOUSEGRABS" );
2126 #if GTK_CHECK_VERSION(3, 20, 0)
2127 if (gtk_check_version(3, 20, 0) == nullptr)
2129 GdkSeat
* pSeat
= gdk_display_get_default_seat(getGdkDisplay());
2132 gdk_seat_grab(pSeat
, widget_get_window(getMouseEventWidget()), GDK_SEAT_CAPABILITY_ALL_POINTING
,
2133 bOwnerEvents
, nullptr, nullptr, nullptr, nullptr);
2137 gdk_seat_ungrab(pSeat
);
2144 const int nMask
= (GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK
| GDK_POINTER_MOTION_HINT_MASK
);
2146 GdkDeviceManager
* pDeviceManager
= gdk_display_get_device_manager(getGdkDisplay());
2147 GdkDevice
* pPointer
= gdk_device_manager_get_client_pointer(pDeviceManager
);
2150 gdk_device_grab(pPointer
, widget_get_window(getMouseEventWidget()), GDK_OWNERSHIP_NONE
,
2151 bOwnerEvents
, GdkEventMask(nMask
), m_pCurrentCursor
, gtk_get_current_event_time());
2155 gdk_device_ungrab(pPointer
, gtk_get_current_event_time());
2159 void GtkSalFrame::CaptureMouse( bool bCapture
)
2161 getDisplay()->CaptureMouse( bCapture
? this : nullptr );
2164 void GtkSalFrame::SetPointerPos( long nX
, long nY
)
2166 GtkSalFrame
* pFrame
= this;
2167 while( pFrame
&& pFrame
->isChild( false ) )
2168 pFrame
= pFrame
->m_pParent
;
2172 GdkScreen
*pScreen
= gtk_window_get_screen( GTK_WINDOW(pFrame
->m_pWindow
) );
2173 GdkDisplay
*pDisplay
= gdk_screen_get_display( pScreen
);
2175 /* when the application tries to center the mouse in the dialog the
2176 * window isn't mapped already. So use coordinates relative to the root window.
2178 unsigned int nWindowLeft
= maGeometry
.nX
+ nX
;
2179 unsigned int nWindowTop
= maGeometry
.nY
+ nY
;
2181 GdkDeviceManager
* pManager
= gdk_display_get_device_manager(pDisplay
);
2182 gdk_device_warp(gdk_device_manager_get_client_pointer(pManager
), pScreen
, nWindowLeft
, nWindowTop
);
2184 // #i38648# ask for the next motion hint
2186 GdkModifierType mask
;
2187 gdk_window_get_pointer( widget_get_window(pFrame
->m_pWindow
) , &x
, &y
, &mask
);
2190 void GtkSalFrame::Flush()
2192 gdk_display_flush( getGdkDisplay() );
2195 void GtkSalFrame::KeyCodeToGdkKey(const vcl::KeyCode
& rKeyCode
,
2196 guint
* pGdkKeyCode
, GdkModifierType
*pGdkModifiers
)
2198 if ( pGdkKeyCode
== nullptr || pGdkModifiers
== nullptr )
2201 // Get GDK key modifiers
2202 GdkModifierType nModifiers
= GdkModifierType(0);
2204 if ( rKeyCode
.IsShift() )
2205 nModifiers
= static_cast<GdkModifierType
>( nModifiers
| GDK_SHIFT_MASK
);
2207 if ( rKeyCode
.IsMod1() )
2208 nModifiers
= static_cast<GdkModifierType
>( nModifiers
| GDK_CONTROL_MASK
);
2210 if ( rKeyCode
.IsMod2() )
2211 nModifiers
= static_cast<GdkModifierType
>( nModifiers
| GDK_MOD1_MASK
);
2213 *pGdkModifiers
= nModifiers
;
2218 guint nCode
= rKeyCode
.GetCode();
2220 if ( nCode
>= KEY_0
&& nCode
<= KEY_9
)
2221 nKeyCode
= ( nCode
- KEY_0
) + GDK_KEY_0
;
2222 else if ( nCode
>= KEY_A
&& nCode
<= KEY_Z
)
2223 nKeyCode
= ( nCode
- KEY_A
) + GDK_KEY_A
;
2224 else if ( nCode
>= KEY_F1
&& nCode
<= KEY_F26
)
2225 nKeyCode
= ( nCode
- KEY_F1
) + GDK_KEY_F1
;
2230 case KEY_DOWN
: nKeyCode
= GDK_KEY_Down
; break;
2231 case KEY_UP
: nKeyCode
= GDK_KEY_Up
; break;
2232 case KEY_LEFT
: nKeyCode
= GDK_KEY_Left
; break;
2233 case KEY_RIGHT
: nKeyCode
= GDK_KEY_Right
; break;
2234 case KEY_HOME
: nKeyCode
= GDK_KEY_Home
; break;
2235 case KEY_END
: nKeyCode
= GDK_KEY_End
; break;
2236 case KEY_PAGEUP
: nKeyCode
= GDK_KEY_Page_Up
; break;
2237 case KEY_PAGEDOWN
: nKeyCode
= GDK_KEY_Page_Down
; break;
2238 case KEY_RETURN
: nKeyCode
= GDK_KEY_Return
; break;
2239 case KEY_ESCAPE
: nKeyCode
= GDK_KEY_Escape
; break;
2240 case KEY_TAB
: nKeyCode
= GDK_KEY_Tab
; break;
2241 case KEY_BACKSPACE
: nKeyCode
= GDK_KEY_BackSpace
; break;
2242 case KEY_SPACE
: nKeyCode
= GDK_KEY_space
; break;
2243 case KEY_INSERT
: nKeyCode
= GDK_KEY_Insert
; break;
2244 case KEY_DELETE
: nKeyCode
= GDK_KEY_Delete
; break;
2245 case KEY_ADD
: nKeyCode
= GDK_KEY_plus
; break;
2246 case KEY_SUBTRACT
: nKeyCode
= GDK_KEY_minus
; break;
2247 case KEY_MULTIPLY
: nKeyCode
= GDK_KEY_asterisk
; break;
2248 case KEY_DIVIDE
: nKeyCode
= GDK_KEY_slash
; break;
2249 case KEY_POINT
: nKeyCode
= GDK_KEY_period
; break;
2250 case KEY_COMMA
: nKeyCode
= GDK_KEY_comma
; break;
2251 case KEY_LESS
: nKeyCode
= GDK_KEY_less
; break;
2252 case KEY_GREATER
: nKeyCode
= GDK_KEY_greater
; break;
2253 case KEY_EQUAL
: nKeyCode
= GDK_KEY_equal
; break;
2254 case KEY_FIND
: nKeyCode
= GDK_KEY_Find
; break;
2255 case KEY_CONTEXTMENU
: nKeyCode
= GDK_KEY_Menu
; break;
2256 case KEY_HELP
: nKeyCode
= GDK_KEY_Help
; break;
2257 case KEY_UNDO
: nKeyCode
= GDK_KEY_Undo
; break;
2258 case KEY_REPEAT
: nKeyCode
= GDK_KEY_Redo
; break;
2259 case KEY_DECIMAL
: nKeyCode
= GDK_KEY_KP_Decimal
; break;
2260 case KEY_TILDE
: nKeyCode
= GDK_KEY_asciitilde
; break;
2261 case KEY_QUOTELEFT
: nKeyCode
= GDK_KEY_quoteleft
; break;
2262 case KEY_BRACKETLEFT
: nKeyCode
= GDK_KEY_bracketleft
; break;
2263 case KEY_BRACKETRIGHT
: nKeyCode
= GDK_KEY_bracketright
; break;
2264 case KEY_SEMICOLON
: nKeyCode
= GDK_KEY_semicolon
; break;
2265 case KEY_QUOTERIGHT
: nKeyCode
= GDK_KEY_quoteright
; break;
2268 case KEY_COPY
: nKeyCode
= GDK_KEY_Copy
; break;
2269 case KEY_CUT
: nKeyCode
= GDK_KEY_Cut
; break;
2270 case KEY_PASTE
: nKeyCode
= GDK_KEY_Paste
; break;
2271 case KEY_OPEN
: nKeyCode
= GDK_KEY_Open
; break;
2275 *pGdkKeyCode
= nKeyCode
;
2278 OUString
GtkSalFrame::GetKeyName( sal_uInt16 nKeyCode
)
2281 GdkModifierType nGtkModifiers
;
2282 KeyCodeToGdkKey(nKeyCode
, &nGtkKeyCode
, &nGtkModifiers
);
2284 gchar
* pName
= gtk_accelerator_get_label(nGtkKeyCode
, nGtkModifiers
);
2285 OUString
aRet(pName
, rtl_str_getLength(pName
), RTL_TEXTENCODING_UTF8
);
2290 GdkDisplay
*GtkSalFrame::getGdkDisplay()
2292 return GetGtkSalData()->GetGdkDisplay();
2295 GtkSalDisplay
*GtkSalFrame::getDisplay()
2297 return GetGtkSalData()->GetGtkDisplay();
2300 SalFrame::SalPointerState
GtkSalFrame::GetPointerState()
2302 SalPointerState aState
;
2305 GdkModifierType aMask
;
2306 gdk_display_get_pointer( getGdkDisplay(), &pScreen
, &x
, &y
, &aMask
);
2307 aState
.maPos
= Point( x
- maGeometry
.nX
, y
- maGeometry
.nY
);
2308 aState
.mnState
= GetMouseModCode( aMask
);
2312 KeyIndicatorState
GtkSalFrame::GetIndicatorState()
2314 KeyIndicatorState nState
= KeyIndicatorState::NONE
;
2316 GdkKeymap
*pKeyMap
= gdk_keymap_get_for_display(getGdkDisplay());
2318 if (gdk_keymap_get_caps_lock_state(pKeyMap
))
2319 nState
|= KeyIndicatorState::CAPSLOCK
;
2320 if (gdk_keymap_get_num_lock_state(pKeyMap
))
2321 nState
|= KeyIndicatorState::NUMLOCK
;
2322 if (gdk_keymap_get_scroll_lock_state(pKeyMap
))
2323 nState
|= KeyIndicatorState::SCROLLLOCK
;
2328 void GtkSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode
)
2330 g_warning ("missing simulate keypress %d", nKeyCode
);
2333 void GtkSalFrame::SetInputContext( SalInputContext
* pContext
)
2338 if( ! (pContext
->mnOptions
& InputContextFlags::Text
) )
2341 // create a new im context
2342 if( ! m_pIMHandler
)
2343 m_pIMHandler
.reset( new IMHandler( this ) );
2346 void GtkSalFrame::EndExtTextInput( EndExtTextInputFlags nFlags
)
2349 m_pIMHandler
->endExtTextInput( nFlags
);
2352 bool GtkSalFrame::MapUnicodeToKeyCode( sal_Unicode
, LanguageType
, vcl::KeyCode
& )
2354 // not supported yet
2358 LanguageType
GtkSalFrame::GetInputLanguage()
2360 return LANGUAGE_DONTKNOW
;
2363 void GtkSalFrame::UpdateSettings( AllSettings
& rSettings
)
2368 GtkSalGraphics
* pGraphics
= m_pGraphics
.get();
2369 bool bFreeGraphics
= false;
2372 pGraphics
= static_cast<GtkSalGraphics
*>(AcquireGraphics());
2375 SAL_WARN("vcl.gtk3", "Could not get graphics - unable to update settings");
2378 bFreeGraphics
= true;
2381 pGraphics
->updateSettings( rSettings
);
2384 ReleaseGraphics( pGraphics
);
2387 void GtkSalFrame::Beep()
2389 gdk_display_beep( getGdkDisplay() );
2392 const SystemEnvData
* GtkSalFrame::GetSystemData() const
2394 return &m_aSystemData
;
2397 void GtkSalFrame::SetParent( SalFrame
* pNewParent
)
2401 gtk_window_group_remove_window(gtk_window_get_group(GTK_WINDOW(m_pParent
->m_pWindow
)), GTK_WINDOW(m_pWindow
));
2402 m_pParent
->m_aChildren
.remove(this);
2404 m_pParent
= static_cast<GtkSalFrame
*>(pNewParent
);
2407 m_pParent
->m_aChildren
.push_back(this);
2408 gtk_window_group_add_window(gtk_window_get_group(GTK_WINDOW(m_pParent
->m_pWindow
)), GTK_WINDOW(m_pWindow
));
2411 gtk_window_set_transient_for( GTK_WINDOW(m_pWindow
),
2412 (m_pParent
&& ! m_pParent
->isChild(true,false)) ? GTK_WINDOW(m_pParent
->m_pWindow
) : nullptr
2416 bool GtkSalFrame::SetPluginParent( SystemParentData
* )
2418 //FIXME: no SetPluginParent impl. for gtk3
2422 void GtkSalFrame::ResetClipRegion()
2425 gdk_window_shape_combine_region( widget_get_window( m_pWindow
), nullptr, 0, 0 );
2428 void GtkSalFrame::BeginSetClipRegion( sal_uLong
)
2431 cairo_region_destroy( m_pRegion
);
2432 m_pRegion
= cairo_region_create();
2435 void GtkSalFrame::UnionClipRegion( long nX
, long nY
, long nWidth
, long nHeight
)
2442 aRect
.width
= nWidth
;
2443 aRect
.height
= nHeight
;
2444 cairo_region_union_rectangle( m_pRegion
, &aRect
);
2448 void GtkSalFrame::EndSetClipRegion()
2450 if( m_pWindow
&& m_pRegion
)
2451 gdk_window_shape_combine_region( widget_get_window(m_pWindow
), m_pRegion
, 0, 0 );
2454 void GtkSalFrame::PositionByToolkit(const tools::Rectangle
& rRect
, FloatWinPopupFlags nFlags
)
2456 if (ImplGetSVData()->maNWFData
.mbCanDetermineWindowPosition
)
2459 m_aFloatRect
= rRect
;
2460 m_nFloatFlags
= nFlags
;
2461 m_bFloatPositioned
= true;
2464 void GtkSalFrame::SetModal(bool bModal
)
2468 gtk_window_set_modal(GTK_WINDOW(m_pWindow
), bModal
);
2471 gboolean
GtkSalFrame::signalTooltipQuery(GtkWidget
*, gint
/*x*/, gint
/*y*/,
2472 gboolean
/*keyboard_mode*/, GtkTooltip
*tooltip
,
2475 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
2476 if (pThis
->m_aTooltip
.isEmpty())
2478 gtk_tooltip_set_text(tooltip
,
2479 OUStringToOString(pThis
->m_aTooltip
, RTL_TEXTENCODING_UTF8
).getStr());
2480 GdkRectangle aHelpArea
;
2481 aHelpArea
.x
= pThis
->m_aHelpArea
.Left();
2482 aHelpArea
.y
= pThis
->m_aHelpArea
.Top();
2483 aHelpArea
.width
= pThis
->m_aHelpArea
.GetWidth();
2484 aHelpArea
.height
= pThis
->m_aHelpArea
.GetHeight();
2485 if (AllSettings::GetLayoutRTL())
2486 aHelpArea
.x
= pThis
->maGeometry
.nWidth
-aHelpArea
.width
-1-aHelpArea
.x
;
2487 gtk_tooltip_set_tip_area(tooltip
, &aHelpArea
);
2491 bool GtkSalFrame::ShowTooltip(const OUString
& rHelpText
, const tools::Rectangle
& rHelpArea
)
2493 m_aTooltip
= rHelpText
;
2494 m_aHelpArea
= rHelpArea
;
2495 gtk_widget_trigger_tooltip_query(getMouseEventWidget());
2499 void GtkSalFrame::HideTooltip()
2502 gtk_widget_trigger_tooltip_query(getMouseEventWidget());
2507 void set_pointing_to(GtkPopover
*pPopOver
, vcl::Window
* pParent
, const tools::Rectangle
& rHelpArea
, const SalFrameGeometry
& rGeometry
)
2510 aRect
.x
= FloatingWindow::ImplConvertToAbsPos(pParent
, rHelpArea
).Left() - rGeometry
.nX
;
2511 aRect
.y
= rHelpArea
.Top();
2515 GtkPositionType ePos
= gtk_popover_get_position(pPopOver
);
2518 case GTK_POS_BOTTOM
:
2520 aRect
.width
= rHelpArea
.GetWidth();
2524 aRect
.height
= rHelpArea
.GetHeight();
2528 gtk_popover_set_pointing_to(pPopOver
, &aRect
);
2532 void* GtkSalFrame::ShowPopover(const OUString
& rHelpText
, vcl::Window
* pParent
, const tools::Rectangle
& rHelpArea
, QuickHelpFlags nFlags
)
2534 GtkWidget
*pWidget
= gtk_popover_new(getMouseEventWidget());
2535 OString sUTF
= OUStringToOString(rHelpText
, RTL_TEXTENCODING_UTF8
);
2536 GtkWidget
*pLabel
= gtk_label_new(sUTF
.getStr());
2537 gtk_container_add(GTK_CONTAINER(pWidget
), pLabel
);
2539 if (nFlags
& QuickHelpFlags::Top
)
2540 gtk_popover_set_position(GTK_POPOVER(pWidget
), GTK_POS_BOTTOM
);
2541 else if (nFlags
& QuickHelpFlags::Bottom
)
2542 gtk_popover_set_position(GTK_POPOVER(pWidget
), GTK_POS_TOP
);
2543 else if (nFlags
& QuickHelpFlags::Left
)
2544 gtk_popover_set_position(GTK_POPOVER(pWidget
), GTK_POS_RIGHT
);
2545 else if (nFlags
& QuickHelpFlags::Right
)
2546 gtk_popover_set_position(GTK_POPOVER(pWidget
), GTK_POS_LEFT
);
2548 set_pointing_to(GTK_POPOVER(pWidget
), pParent
, rHelpArea
, maGeometry
);
2550 gtk_popover_set_modal(GTK_POPOVER(pWidget
), false);
2552 gtk_widget_show_all(pWidget
);
2557 bool GtkSalFrame::UpdatePopover(void* nId
, const OUString
& rHelpText
, vcl::Window
* pParent
, const tools::Rectangle
& rHelpArea
)
2559 GtkWidget
*pWidget
= static_cast<GtkWidget
*>(nId
);
2561 set_pointing_to(GTK_POPOVER(pWidget
), pParent
, rHelpArea
, maGeometry
);
2563 GtkWidget
*pLabel
= gtk_bin_get_child(GTK_BIN(pWidget
));
2564 OString sUTF
= OUStringToOString(rHelpText
, RTL_TEXTENCODING_UTF8
);
2565 gtk_label_set_text(GTK_LABEL(pLabel
), sUTF
.getStr());
2570 bool GtkSalFrame::HidePopover(void* nId
)
2572 GtkWidget
*pWidget
= static_cast<GtkWidget
*>(nId
);
2573 gtk_widget_destroy(pWidget
);
2577 void GtkSalFrame::addGrabLevel()
2579 if (m_nGrabLevel
== 0)
2580 gtk_grab_add(getMouseEventWidget());
2584 void GtkSalFrame::removeGrabLevel()
2586 if (m_nGrabLevel
> 0)
2589 if (m_nGrabLevel
== 0)
2590 gtk_grab_remove(getMouseEventWidget());
2594 void GtkSalFrame::closePopup()
2598 ImplSVData
* pSVData
= ImplGetSVData();
2599 if (!pSVData
->maWinData
.mpFirstFloat
)
2601 if (pSVData
->maWinData
.mpFirstFloat
->ImplGetFrame() != this)
2603 pSVData
->maWinData
.mpFirstFloat
->EndPopupMode(FloatWinPopupEndFlags::Cancel
| FloatWinPopupEndFlags::CloseAll
);
2608 //tdf#117981 translate embedded video window mouse events to parent coordinates
2609 void translate_coords(GdkWindow
* pSourceWindow
, GtkWidget
* pTargetWidget
, int& rEventX
, int& rEventY
)
2611 gpointer user_data
=nullptr;
2612 gdk_window_get_user_data(pSourceWindow
, &user_data
);
2613 GtkWidget
* pRealEventWidget
= static_cast<GtkWidget
*>(user_data
);
2614 if (pRealEventWidget
)
2616 gtk_widget_translate_coordinates(pRealEventWidget
, pTargetWidget
, rEventX
, rEventY
, &rEventX
, &rEventY
);
2621 gboolean
GtkSalFrame::signalButton( GtkWidget
*, GdkEventButton
* pEvent
, gpointer frame
)
2623 UpdateLastInputEventTime(pEvent
->time
);
2625 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
2626 GtkWidget
* pEventWidget
= pThis
->getMouseEventWidget();
2627 bool bDifferentEventWindow
= pEvent
->window
!= widget_get_window(pEventWidget
);
2629 // tdf#120764 It isn't allowed under wayland to have two visible popups that share
2630 // the same top level parent. The problem is that since gtk 3.24 tooltips are also
2631 // implemented as popups, which means that we cannot show any popup if there is a
2632 // visible tooltip. In fact, gtk will hide the tooltip by itself after this handler,
2633 // in case of a button press event. But if we intend to show a popup on button press
2634 // it will be too late, so just do it here:
2635 if (pEvent
->type
== GDK_BUTTON_PRESS
)
2636 pThis
->HideTooltip();
2638 SalMouseEvent aEvent
;
2639 SalEvent nEventType
= SalEvent::NONE
;
2640 switch( pEvent
->type
)
2642 case GDK_BUTTON_PRESS
:
2643 nEventType
= SalEvent::MouseButtonDown
;
2645 case GDK_BUTTON_RELEASE
:
2646 nEventType
= SalEvent::MouseButtonUp
;
2651 switch( pEvent
->button
)
2653 case 1: aEvent
.mnButton
= MOUSE_LEFT
; break;
2654 case 2: aEvent
.mnButton
= MOUSE_MIDDLE
; break;
2655 case 3: aEvent
.mnButton
= MOUSE_RIGHT
; break;
2656 default: return false;
2659 vcl::DeletionListener
aDel( pThis
);
2661 if (pThis
->isFloatGrabWindow())
2663 //rhbz#1505379 if the window that got the event isn't our one, or there's none
2664 //of our windows under the mouse then close this popup window
2665 if (bDifferentEventWindow
||
2666 gdk_device_get_window_at_position(pEvent
->device
, nullptr, nullptr) == nullptr)
2668 if (pEvent
->type
== GDK_BUTTON_PRESS
)
2669 pThis
->closePopup();
2670 else if (pEvent
->type
== GDK_BUTTON_RELEASE
)
2675 int nEventX
= pEvent
->x
;
2676 int nEventY
= pEvent
->y
;
2678 if (bDifferentEventWindow
)
2679 translate_coords(pEvent
->window
, pEventWidget
, nEventX
, nEventY
);
2681 if (!aDel
.isDeleted())
2683 int frame_x
= static_cast<int>(pEvent
->x_root
- nEventX
);
2684 int frame_y
= static_cast<int>(pEvent
->y_root
- nEventY
);
2685 if (pThis
->m_bGeometryIsProvisional
|| frame_x
!= pThis
->maGeometry
.nX
|| frame_y
!= pThis
->maGeometry
.nY
)
2687 pThis
->m_bGeometryIsProvisional
= false;
2688 pThis
->maGeometry
.nX
= frame_x
;
2689 pThis
->maGeometry
.nY
= frame_y
;
2690 ImplSVData
* pSVData
= ImplGetSVData();
2691 if (pSVData
->maNWFData
.mbCanDetermineWindowPosition
)
2692 pThis
->CallCallbackExc(SalEvent::Move
, nullptr);
2696 if (!aDel
.isDeleted())
2698 aEvent
.mnTime
= pEvent
->time
;
2699 aEvent
.mnX
= static_cast<long>(pEvent
->x_root
) - pThis
->maGeometry
.nX
;
2700 aEvent
.mnY
= static_cast<long>(pEvent
->y_root
) - pThis
->maGeometry
.nY
;
2701 aEvent
.mnCode
= GetMouseModCode( pEvent
->state
);
2703 if( AllSettings::GetLayoutRTL() )
2704 aEvent
.mnX
= pThis
->maGeometry
.nWidth
-1-aEvent
.mnX
;
2706 pThis
->CallCallbackExc( nEventType
, &aEvent
);
2712 void GtkSalFrame::LaunchAsyncScroll(GdkEvent
const * pEvent
)
2714 //if we don't match previous pending states, flush that queue now
2715 if (!m_aPendingScrollEvents
.empty() && pEvent
->scroll
.state
!= m_aPendingScrollEvents
.back()->scroll
.state
)
2717 m_aSmoothScrollIdle
.Stop();
2718 m_aSmoothScrollIdle
.Invoke();
2719 assert(m_aPendingScrollEvents
.empty());
2721 //add scroll event to queue
2722 m_aPendingScrollEvents
.push_back(gdk_event_copy(pEvent
));
2723 if (!m_aSmoothScrollIdle
.IsActive())
2724 m_aSmoothScrollIdle
.Start();
2727 IMPL_LINK_NOARG(GtkSalFrame
, AsyncScroll
, Timer
*, void)
2729 assert(!m_aPendingScrollEvents
.empty());
2731 SalWheelMouseEvent aEvent
;
2733 GdkEvent
* pEvent
= m_aPendingScrollEvents
.back();
2735 aEvent
.mnTime
= pEvent
->scroll
.time
;
2736 aEvent
.mnX
= static_cast<sal_uLong
>(pEvent
->scroll
.x
);
2737 // --- RTL --- (mirror mouse pos)
2738 if (AllSettings::GetLayoutRTL())
2739 aEvent
.mnX
= maGeometry
.nWidth
- 1 - aEvent
.mnX
;
2740 aEvent
.mnY
= static_cast<sal_uLong
>(pEvent
->scroll
.y
);
2741 aEvent
.mnCode
= GetMouseModCode( pEvent
->scroll
.state
);
2743 double delta_x(0.0), delta_y(0.0);
2744 for (auto pSubEvent
: m_aPendingScrollEvents
)
2746 delta_x
+= pSubEvent
->scroll
.delta_x
;
2747 delta_y
+= pSubEvent
->scroll
.delta_y
;
2748 gdk_event_free(pSubEvent
);
2750 m_aPendingScrollEvents
.clear();
2752 // rhbz#1344042 "Traditionally" in gtk3 we tool a single up/down event as
2753 // equating to 3 scroll lines and a delta of 120. So scale the delta here
2754 // by 120 where a single mouse wheel click is an incoming delta_x of 1
2755 // and divide that by 40 to get the number of scroll lines
2758 aEvent
.mnDelta
= -delta_x
* 120;
2759 aEvent
.mnNotchDelta
= aEvent
.mnDelta
< 0 ? -1 : +1;
2760 if (aEvent
.mnDelta
== 0)
2761 aEvent
.mnDelta
= aEvent
.mnNotchDelta
;
2762 aEvent
.mbHorz
= true;
2763 aEvent
.mnScrollLines
= std::abs(aEvent
.mnDelta
) / 40.0;
2764 CallCallbackExc(SalEvent::WheelMouse
, &aEvent
);
2769 aEvent
.mnDelta
= -delta_y
* 120;
2770 aEvent
.mnNotchDelta
= aEvent
.mnDelta
< 0 ? -1 : +1;
2771 if (aEvent
.mnDelta
== 0)
2772 aEvent
.mnDelta
= aEvent
.mnNotchDelta
;
2773 aEvent
.mbHorz
= false;
2774 aEvent
.mnScrollLines
= std::abs(aEvent
.mnDelta
) / 40.0;
2775 CallCallbackExc(SalEvent::WheelMouse
, &aEvent
);
2779 gboolean
GtkSalFrame::signalScroll(GtkWidget
*, GdkEvent
* pInEvent
, gpointer frame
)
2781 GdkEventScroll
& rEvent
= pInEvent
->scroll
;
2783 UpdateLastInputEventTime(rEvent
.time
);
2785 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
2787 if (rEvent
.direction
== GDK_SCROLL_SMOOTH
)
2789 pThis
->LaunchAsyncScroll(pInEvent
);
2793 //if we have smooth scrolling previous pending states, flush that queue now
2794 if (!pThis
->m_aPendingScrollEvents
.empty())
2796 pThis
->m_aSmoothScrollIdle
.Stop();
2797 pThis
->m_aSmoothScrollIdle
.Invoke();
2798 assert(pThis
->m_aPendingScrollEvents
.empty());
2801 SalWheelMouseEvent aEvent
;
2803 aEvent
.mnTime
= rEvent
.time
;
2804 aEvent
.mnX
= static_cast<sal_uLong
>(rEvent
.x
);
2805 // --- RTL --- (mirror mouse pos)
2806 if (AllSettings::GetLayoutRTL())
2807 aEvent
.mnX
= pThis
->maGeometry
.nWidth
- 1 - aEvent
.mnX
;
2808 aEvent
.mnY
= static_cast<sal_uLong
>(rEvent
.y
);
2809 aEvent
.mnCode
= GetMouseModCode(rEvent
.state
);
2811 switch (rEvent
.direction
)
2814 aEvent
.mnDelta
= 120;
2815 aEvent
.mnNotchDelta
= 1;
2816 aEvent
.mnScrollLines
= 3;
2817 aEvent
.mbHorz
= false;
2818 pThis
->CallCallbackExc(SalEvent::WheelMouse
, &aEvent
);
2821 case GDK_SCROLL_DOWN
:
2822 aEvent
.mnDelta
= -120;
2823 aEvent
.mnNotchDelta
= -1;
2824 aEvent
.mnScrollLines
= 3;
2825 aEvent
.mbHorz
= false;
2826 pThis
->CallCallbackExc(SalEvent::WheelMouse
, &aEvent
);
2829 case GDK_SCROLL_LEFT
:
2830 aEvent
.mnDelta
= 120;
2831 aEvent
.mnNotchDelta
= 1;
2832 aEvent
.mnScrollLines
= 3;
2833 aEvent
.mbHorz
= true;
2834 pThis
->CallCallbackExc(SalEvent::WheelMouse
, &aEvent
);
2837 case GDK_SCROLL_RIGHT
:
2838 aEvent
.mnDelta
= -120;
2839 aEvent
.mnNotchDelta
= -1;
2840 aEvent
.mnScrollLines
= 3;
2841 aEvent
.mbHorz
= true;
2842 pThis
->CallCallbackExc(SalEvent::WheelMouse
, &aEvent
);
2851 void GtkSalFrame::gestureSwipe(GtkGestureSwipe
* gesture
, gdouble velocity_x
, gdouble velocity_y
, gpointer frame
)
2854 GdkEventSequence
*sequence
= gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture
));
2855 //I feel I want the first point of the sequence, not the last point which
2856 //the docs say this gives, but for the moment assume we start and end
2857 //within the same vcl window
2858 if (gtk_gesture_get_point(GTK_GESTURE(gesture
), sequence
, &x
, &y
))
2860 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
2862 SalSwipeEvent aEvent
;
2863 aEvent
.mnVelocityX
= velocity_x
;
2864 aEvent
.mnVelocityY
= velocity_y
;
2868 pThis
->CallCallbackExc(SalEvent::Swipe
, &aEvent
);
2872 void GtkSalFrame::gestureLongPress(GtkGestureLongPress
* gesture
, gpointer frame
)
2874 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
2878 SalLongPressEvent aEvent
;
2881 GdkEventSequence
*sequence
= gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture
));
2882 gtk_gesture_get_point(GTK_GESTURE(gesture
), sequence
, &x
, &y
);
2886 pThis
->CallCallbackExc(SalEvent::LongPress
, &aEvent
);
2890 gboolean
GtkSalFrame::signalMotion( GtkWidget
*, GdkEventMotion
* pEvent
, gpointer frame
)
2892 UpdateLastInputEventTime(pEvent
->time
);
2894 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
2895 GtkWidget
* pEventWidget
= pThis
->getMouseEventWidget();
2896 bool bDifferentEventWindow
= pEvent
->window
!= widget_get_window(pEventWidget
);
2898 //If a menu, e.g. font name dropdown, is open, then under wayland moving the
2899 //mouse in the top left corner of the toplevel window in a
2900 //0,0,float-width,float-height area generates motion events which are
2901 //delivered to the dropdown
2902 if (pThis
->isFloatGrabWindow() && bDifferentEventWindow
)
2905 vcl::DeletionListener
aDel( pThis
);
2907 int nEventX
= pEvent
->x
;
2908 int nEventY
= pEvent
->y
;
2910 if (bDifferentEventWindow
)
2911 translate_coords(pEvent
->window
, pEventWidget
, nEventX
, nEventY
);
2913 int frame_x
= static_cast<int>(pEvent
->x_root
- nEventX
);
2914 int frame_y
= static_cast<int>(pEvent
->y_root
- nEventY
);
2916 if (pThis
->m_bGeometryIsProvisional
|| frame_x
!= pThis
->maGeometry
.nX
|| frame_y
!= pThis
->maGeometry
.nY
)
2918 pThis
->m_bGeometryIsProvisional
= false;
2919 pThis
->maGeometry
.nX
= frame_x
;
2920 pThis
->maGeometry
.nY
= frame_y
;
2921 ImplSVData
* pSVData
= ImplGetSVData();
2922 if (pSVData
->maNWFData
.mbCanDetermineWindowPosition
)
2923 pThis
->CallCallbackExc(SalEvent::Move
, nullptr);
2926 if (!aDel
.isDeleted())
2928 SalMouseEvent aEvent
;
2929 aEvent
.mnTime
= pEvent
->time
;
2930 aEvent
.mnX
= static_cast<long>(pEvent
->x_root
) - pThis
->maGeometry
.nX
;
2931 aEvent
.mnY
= static_cast<long>(pEvent
->y_root
) - pThis
->maGeometry
.nY
;
2932 aEvent
.mnCode
= GetMouseModCode( pEvent
->state
);
2933 aEvent
.mnButton
= 0;
2935 if( AllSettings::GetLayoutRTL() )
2936 aEvent
.mnX
= pThis
->maGeometry
.nWidth
-1-aEvent
.mnX
;
2938 pThis
->CallCallbackExc( SalEvent::MouseMove
, &aEvent
);
2941 if (!aDel
.isDeleted())
2943 // ask for the next hint
2945 GdkModifierType mask
;
2946 gdk_window_get_pointer( widget_get_window(GTK_WIDGET(pThis
->m_pWindow
)), &x
, &y
, &mask
);
2952 gboolean
GtkSalFrame::signalCrossing( GtkWidget
*, GdkEventCrossing
* pEvent
, gpointer frame
)
2954 UpdateLastInputEventTime(pEvent
->time
);
2956 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
2957 SalMouseEvent aEvent
;
2958 aEvent
.mnTime
= pEvent
->time
;
2959 aEvent
.mnX
= static_cast<long>(pEvent
->x_root
) - pThis
->maGeometry
.nX
;
2960 aEvent
.mnY
= static_cast<long>(pEvent
->y_root
) - pThis
->maGeometry
.nY
;
2961 aEvent
.mnCode
= GetMouseModCode( pEvent
->state
);
2962 aEvent
.mnButton
= 0;
2964 if (AllSettings::GetLayoutRTL())
2965 aEvent
.mnX
= pThis
->maGeometry
.nWidth
-1-aEvent
.mnX
;
2967 pThis
->CallCallbackExc( (pEvent
->type
== GDK_ENTER_NOTIFY
) ? SalEvent::MouseMove
: SalEvent::MouseLeave
, &aEvent
);
2972 cairo_t
* GtkSalFrame::getCairoContext() const
2974 cairo_t
* cr
= cairo_create(m_pSurface
);
2979 void GtkSalFrame::damaged(sal_Int32 nExtentsX
, sal_Int32 nExtentsY
,
2980 sal_Int32 nExtentsWidth
, sal_Int32 nExtentsHeight
) const
2982 #if OSL_DEBUG_LEVEL > 0
2986 OString
tmp("/tmp/frame" + OString::number(frame
++) + ".png");
2987 cairo_t
* cr
= getCairoContext();
2988 cairo_surface_write_to_png(cairo_get_target(cr
), tmp
.getStr());
2993 gtk_widget_queue_draw_area(GTK_WIDGET(m_pFixedContainer
),
2994 nExtentsX
, nExtentsY
,
2995 nExtentsWidth
, nExtentsHeight
);
2998 // blit our backing cairo surface to the target cairo context
2999 gboolean
GtkSalFrame::signalDraw(GtkWidget
*, cairo_t
*cr
, gpointer frame
)
3001 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3003 cairo_set_source_surface(cr
, pThis
->m_pSurface
, 0, 0);
3009 void GtkSalFrame::sizeAllocated(GtkWidget
* pWidget
, GdkRectangle
*pAllocation
, gpointer frame
)
3011 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3012 pThis
->maGeometry
.nWidth
= pAllocation
->width
;
3013 pThis
->maGeometry
.nHeight
= pAllocation
->height
;
3014 bool bRealized
= gtk_widget_get_realized(pWidget
);
3016 pThis
->AllocateFrame();
3017 pThis
->CallCallbackExc( SalEvent::Resize
, nullptr );
3018 if (bRealized
&& !pThis
->m_bSalObjectSetPosSize
)
3019 pThis
->TriggerPaintEvent();
3022 #if GTK_CHECK_VERSION(3,23,0)
3025 void swapDirection(GdkGravity
& gravity
)
3027 if (gravity
== GDK_GRAVITY_NORTH_WEST
)
3028 gravity
= GDK_GRAVITY_NORTH_EAST
;
3029 else if (gravity
== GDK_GRAVITY_NORTH_EAST
)
3030 gravity
= GDK_GRAVITY_NORTH_WEST
;
3031 else if (gravity
== GDK_GRAVITY_SOUTH_WEST
)
3032 gravity
= GDK_GRAVITY_SOUTH_EAST
;
3033 else if (gravity
== GDK_GRAVITY_SOUTH_EAST
)
3034 gravity
= GDK_GRAVITY_SOUTH_WEST
;
3040 void GtkSalFrame::signalRealize(GtkWidget
*, gpointer frame
)
3042 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3043 pThis
->AllocateFrame();
3044 if (pThis
->m_bSalObjectSetPosSize
)
3046 pThis
->TriggerPaintEvent();
3048 #if GTK_CHECK_VERSION(3,23,0)
3049 if (gtk_check_version(3, 23, 0) == nullptr && pThis
->m_bFloatPositioned
)
3051 GdkGravity rect_anchor
= GDK_GRAVITY_SOUTH_WEST
, menu_anchor
= GDK_GRAVITY_NORTH_WEST
;
3053 if (pThis
->m_nFloatFlags
& FloatWinPopupFlags::Left
)
3055 rect_anchor
= GDK_GRAVITY_NORTH_WEST
;
3056 menu_anchor
= GDK_GRAVITY_NORTH_EAST
;
3058 else if (pThis
->m_nFloatFlags
& FloatWinPopupFlags::Up
)
3060 rect_anchor
= GDK_GRAVITY_NORTH_WEST
;
3061 menu_anchor
= GDK_GRAVITY_SOUTH_WEST
;
3063 else if (pThis
->m_nFloatFlags
& FloatWinPopupFlags::Right
)
3065 rect_anchor
= GDK_GRAVITY_NORTH_EAST
;
3068 VclPtr
<vcl::Window
> pVclParent
= pThis
->GetWindow()->GetParent();
3069 if (pVclParent
->HasMirroredGraphics() && pVclParent
->IsRTLEnabled())
3071 swapDirection(rect_anchor
);
3072 swapDirection(menu_anchor
);
3075 tools::Rectangle aFloatRect
= FloatingWindow::ImplConvertToAbsPos(pVclParent
, pThis
->m_aFloatRect
);
3076 if (gdk_window_get_window_type(widget_get_window(pThis
->m_pParent
->m_pWindow
)) != GDK_WINDOW_TOPLEVEL
)
3077 aFloatRect
.Move(-pThis
->m_pParent
->maGeometry
.nX
, -pThis
->m_pParent
->maGeometry
.nY
);
3079 GdkRectangle rect
{static_cast<int>(aFloatRect
.Left()), static_cast<int>(aFloatRect
.Top()),
3080 static_cast<int>(aFloatRect
.GetWidth()), static_cast<int>(aFloatRect
.GetHeight())};
3082 GdkWindow
* gdkWindow
= widget_get_window(pThis
->m_pWindow
);
3083 gdk_window_move_to_rect(gdkWindow
, &rect
, rect_anchor
, menu_anchor
, GDK_ANCHOR_FLIP
, 0, 0);
3088 gboolean
GtkSalFrame::signalConfigure(GtkWidget
*, GdkEventConfigure
* pEvent
, gpointer frame
)
3090 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3092 bool bMoved
= false;
3093 int x
= pEvent
->x
, y
= pEvent
->y
;
3095 /* #i31785# claims we cannot trust the x,y members of the event;
3096 * they are e.g. not set correctly on maximize/demaximize;
3097 * yet the gdkdisplay-x11.c code handling configure_events has
3098 * done this XTranslateCoordinates work since the day ~zero.
3100 if (pThis
->m_bGeometryIsProvisional
|| x
!= pThis
->maGeometry
.nX
|| y
!= pThis
->maGeometry
.nY
)
3103 pThis
->m_bGeometryIsProvisional
= false;
3104 pThis
->maGeometry
.nX
= x
;
3105 pThis
->maGeometry
.nY
= y
;
3108 // update decoration hints
3110 gdk_window_get_frame_extents( widget_get_window(GTK_WIDGET(pThis
->m_pWindow
)), &aRect
);
3111 pThis
->maGeometry
.nTopDecoration
= y
- aRect
.y
;
3112 pThis
->maGeometry
.nBottomDecoration
= aRect
.y
+ aRect
.height
- y
- pEvent
->height
;
3113 pThis
->maGeometry
.nLeftDecoration
= x
- aRect
.x
;
3114 pThis
->maGeometry
.nRightDecoration
= aRect
.x
+ aRect
.width
- x
- pEvent
->width
;
3115 pThis
->updateScreenNumber();
3119 ImplSVData
* pSVData
= ImplGetSVData();
3120 if (pSVData
->maNWFData
.mbCanDetermineWindowPosition
)
3121 pThis
->CallCallbackExc(SalEvent::Move
, nullptr);
3127 void GtkSalFrame::TriggerPaintEvent()
3129 //Under gtk2 we can basically paint directly into the XWindow and on
3130 //additional "expose-event" events we can re-render the missing pieces
3132 //Under gtk3 we have to keep our own buffer up to date and flush it into
3133 //the given cairo context on "draw". So we emit a paint event on
3134 //opportune resize trigger events to initially fill our backbuffer and then
3135 //keep it up to date with our direct paints and tell gtk those regions
3136 //have changed and then blit them into the provided cairo context when
3139 //The other alternative was to always paint everything on "draw", but
3140 //that duplicates the amount of drawing and is hideously slow
3141 SAL_INFO("vcl.gtk3", "force painting" << 0 << "," << 0 << " " << maGeometry
.nWidth
<< "x" << maGeometry
.nHeight
);
3142 SalPaintEvent
aPaintEvt(0, 0, maGeometry
.nWidth
, maGeometry
.nHeight
, true);
3143 CallCallbackExc(SalEvent::Paint
, &aPaintEvt
);
3144 gtk_widget_queue_draw(GTK_WIDGET(m_pFixedContainer
));
3147 gboolean
GtkSalFrame::signalFocus( GtkWidget
*, GdkEventFocus
* pEvent
, gpointer frame
)
3149 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3151 SalGenericInstance
*pSalInstance
=
3152 static_cast< SalGenericInstance
* >(GetSalData()->m_pInstance
);
3154 // check if printers have changed (analogous to salframe focus handler)
3155 pSalInstance
->updatePrinterUpdate();
3158 pThis
->m_nKeyModifiers
= ModKeyFlags::NONE
;
3160 if( pThis
->m_pIMHandler
)
3161 pThis
->m_pIMHandler
->focusChanged( pEvent
->in
!= 0 );
3163 // ask for changed printers like generic implementation
3164 if( pEvent
->in
&& pSalInstance
->isPrinterInit() )
3165 pSalInstance
->updatePrinterUpdate();
3167 // FIXME: find out who the hell steals the focus from our frame
3168 // while we have the pointer grabbed, this should not come from
3169 // the window manager. Is this an event that was still queued ?
3170 // The focus does not seem to get set inside our process
3172 // in the meantime do not propagate focus get/lose if floats are open
3173 if( m_nFloats
== 0 )
3174 pThis
->CallCallbackExc( pEvent
->in
? SalEvent::GetFocus
: SalEvent::LoseFocus
, nullptr );
3179 gboolean
GtkSalFrame::signalMap(GtkWidget
*, GdkEvent
*, gpointer frame
)
3181 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3183 pThis
->CallCallbackExc( SalEvent::Resize
, nullptr );
3184 pThis
->TriggerPaintEvent();
3189 gboolean
GtkSalFrame::signalUnmap( GtkWidget
*, GdkEvent
*, gpointer frame
)
3191 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3193 pThis
->CallCallbackExc( SalEvent::Resize
, nullptr );
3195 if (pThis
->m_bFloatPositioned
)
3197 // Unrealize is needed for cases where we reuse the same popup
3198 // (e.g. the font name control), making the realize signal fire
3199 // again on next show.
3200 gtk_widget_unrealize(pThis
->m_pWindow
);
3201 pThis
->m_bFloatPositioned
= false;
3207 gboolean
GtkSalFrame::signalKey(GtkWidget
* pWidget
, GdkEventKey
* pEvent
, gpointer frame
)
3209 UpdateLastInputEventTime(pEvent
->time
);
3211 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3213 if (pThis
->isFloatGrabWindow())
3214 return signalKey(pWidget
, pEvent
, pThis
->m_pParent
);
3216 vcl::DeletionListener
aDel( pThis
);
3218 if( pThis
->m_pIMHandler
)
3220 if( pThis
->m_pIMHandler
->handleKeyEvent( pEvent
) )
3224 bool bStopProcessingKey
= false;
3227 if( pEvent
->keyval
== GDK_KEY_Shift_L
|| pEvent
->keyval
== GDK_KEY_Shift_R
||
3228 pEvent
->keyval
== GDK_KEY_Control_L
|| pEvent
->keyval
== GDK_KEY_Control_R
||
3229 pEvent
->keyval
== GDK_KEY_Alt_L
|| pEvent
->keyval
== GDK_KEY_Alt_R
||
3230 pEvent
->keyval
== GDK_KEY_Meta_L
|| pEvent
->keyval
== GDK_KEY_Meta_R
||
3231 pEvent
->keyval
== GDK_KEY_Super_L
|| pEvent
->keyval
== GDK_KEY_Super_R
)
3233 sal_uInt16 nModCode
= GetKeyModCode( pEvent
->state
);
3234 ModKeyFlags nExtModMask
= ModKeyFlags::NONE
;
3235 sal_uInt16 nModMask
= 0;
3236 // pressing just the ctrl key leads to a keysym of XK_Control but
3237 // the event state does not contain ControlMask. In the release
3238 // event it's the other way round: it does contain the Control mask.
3239 // The modifier mode therefore has to be adapted manually.
3240 switch( pEvent
->keyval
)
3242 case GDK_KEY_Control_L
:
3243 nExtModMask
= ModKeyFlags::LeftMod1
;
3244 nModMask
= KEY_MOD1
;
3246 case GDK_KEY_Control_R
:
3247 nExtModMask
= ModKeyFlags::RightMod1
;
3248 nModMask
= KEY_MOD1
;
3251 nExtModMask
= ModKeyFlags::LeftMod2
;
3252 nModMask
= KEY_MOD2
;
3255 nExtModMask
= ModKeyFlags::RightMod2
;
3256 nModMask
= KEY_MOD2
;
3258 case GDK_KEY_Shift_L
:
3259 nExtModMask
= ModKeyFlags::LeftShift
;
3260 nModMask
= KEY_SHIFT
;
3262 case GDK_KEY_Shift_R
:
3263 nExtModMask
= ModKeyFlags::RightShift
;
3264 nModMask
= KEY_SHIFT
;
3266 // Map Meta/Super to MOD3 modifier on all Unix systems
3268 case GDK_KEY_Meta_L
:
3269 case GDK_KEY_Super_L
:
3270 nExtModMask
= ModKeyFlags::LeftMod3
;
3271 nModMask
= KEY_MOD3
;
3273 case GDK_KEY_Meta_R
:
3274 case GDK_KEY_Super_R
:
3275 nExtModMask
= ModKeyFlags::RightMod3
;
3276 nModMask
= KEY_MOD3
;
3280 SalKeyModEvent aModEvt
;
3281 aModEvt
.mbDown
= pEvent
->type
== GDK_KEY_PRESS
;
3282 aModEvt
.mnCode
= nModCode
;
3284 if( pEvent
->type
== GDK_KEY_RELEASE
)
3286 aModEvt
.mnModKeyCode
= pThis
->m_nKeyModifiers
;
3287 nModCode
&= ~nModMask
;
3288 pThis
->m_nKeyModifiers
&= ~nExtModMask
;
3292 nModCode
|= nModMask
;
3293 pThis
->m_nKeyModifiers
|= nExtModMask
;
3294 aModEvt
.mnModKeyCode
= pThis
->m_nKeyModifiers
;
3297 pThis
->CallCallbackExc( SalEvent::KeyModChange
, &aModEvt
);
3301 bStopProcessingKey
= pThis
->doKeyCallback(pEvent
->state
,
3303 pEvent
->hardware_keycode
,
3305 sal_Unicode(gdk_keyval_to_unicode( pEvent
->keyval
)),
3306 (pEvent
->type
== GDK_KEY_PRESS
),
3308 if( ! aDel
.isDeleted() )
3309 pThis
->m_nKeyModifiers
= ModKeyFlags::NONE
;
3312 if( !aDel
.isDeleted() && pThis
->m_pIMHandler
)
3313 pThis
->m_pIMHandler
->updateIMSpotLocation();
3315 return bStopProcessingKey
;
3318 gboolean
GtkSalFrame::signalDelete( GtkWidget
*, GdkEvent
*, gpointer frame
)
3320 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3322 //If we went into the backdrop we disabled the toplevel window, if we
3323 //receive a delete here, re-enable so we can process it
3324 bool bBackDrop
= (gtk_widget_get_state_flags(GTK_WIDGET(pThis
->m_pWindow
)) & GTK_STATE_FLAG_BACKDROP
);
3326 pThis
->GetWindow()->Enable();
3328 pThis
->CallCallbackExc( SalEvent::Close
, nullptr );
3333 void GtkSalFrame::signalStyleUpdated(GtkWidget
*, gpointer frame
)
3335 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3337 // note: settings changed for multiple frames is avoided in winproc.cxx ImplHandleSettings
3338 GtkSalFrame::getDisplay()->SendInternalEvent( pThis
, nullptr, SalEvent::SettingsChanged
);
3340 // fire off font-changed when the system cairo font hints change
3341 GtkInstance
*pInstance
= static_cast<GtkInstance
*>(GetSalData()->m_pInstance
);
3342 const cairo_font_options_t
* pLastCairoFontOptions
= pInstance
->GetLastSeenCairoFontOptions();
3343 const cairo_font_options_t
* pCurrentCairoFontOptions
= gdk_screen_get_font_options(gdk_screen_get_default());
3344 bool bFontSettingsChanged
= true;
3345 if (pLastCairoFontOptions
&& pCurrentCairoFontOptions
)
3346 bFontSettingsChanged
= !cairo_font_options_equal(pLastCairoFontOptions
, pCurrentCairoFontOptions
);
3347 else if (!pLastCairoFontOptions
&& !pCurrentCairoFontOptions
)
3348 bFontSettingsChanged
= false;
3349 if (bFontSettingsChanged
)
3351 pInstance
->ResetLastSeenCairoFontOptions(pCurrentCairoFontOptions
);
3352 GtkSalFrame::getDisplay()->SendInternalEvent( pThis
, nullptr, SalEvent::FontChanged
);
3356 gboolean
GtkSalFrame::signalWindowState( GtkWidget
*, GdkEvent
* pEvent
, gpointer frame
)
3358 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3359 if( (pThis
->m_nState
& GDK_WINDOW_STATE_ICONIFIED
) != (pEvent
->window_state
.new_window_state
& GDK_WINDOW_STATE_ICONIFIED
) )
3361 GtkSalFrame::getDisplay()->SendInternalEvent( pThis
, nullptr, SalEvent::Resize
);
3362 pThis
->TriggerPaintEvent();
3365 if ((pEvent
->window_state
.new_window_state
& GDK_WINDOW_STATE_MAXIMIZED
) &&
3366 !(pThis
->m_nState
& GDK_WINDOW_STATE_MAXIMIZED
))
3368 pThis
->m_aRestorePosSize
= GetPosAndSize(GTK_WINDOW(pThis
->m_pWindow
));
3371 if ((pEvent
->window_state
.new_window_state
& GDK_WINDOW_STATE_WITHDRAWN
) &&
3372 !(pThis
->m_nState
& GDK_WINDOW_STATE_WITHDRAWN
))
3374 if (pThis
->isFloatGrabWindow())
3375 pThis
->closePopup();
3378 pThis
->m_nState
= pEvent
->window_state
.new_window_state
;
3380 #if OSL_DEBUG_LEVEL > 1
3381 if( (pEvent
->window_state
.changed_mask
& GDK_WINDOW_STATE_FULLSCREEN
) )
3383 fprintf( stderr
, "window %p %s full screen state\n",
3385 (pEvent
->window_state
.new_window_state
& GDK_WINDOW_STATE_FULLSCREEN
) ? "enters" : "leaves");
3392 gboolean
GtkSalFrame::signalVisibility( GtkWidget
*, GdkEventVisibility
* /*pEvent*/, gpointer
/*frame*/ )
3399 GdkDragAction
VclToGdk(sal_Int8 dragOperation
)
3401 GdkDragAction
eRet(static_cast<GdkDragAction
>(0));
3402 if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_COPY
)
3403 eRet
= static_cast<GdkDragAction
>(eRet
| GDK_ACTION_COPY
);
3404 if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_MOVE
)
3405 eRet
= static_cast<GdkDragAction
>(eRet
| GDK_ACTION_MOVE
);
3406 if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_LINK
)
3407 eRet
= static_cast<GdkDragAction
>(eRet
| GDK_ACTION_LINK
);
3411 sal_Int8
GdkToVcl(GdkDragAction dragOperation
)
3414 if (dragOperation
& GDK_ACTION_COPY
)
3415 nRet
|= css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
3416 if (dragOperation
& GDK_ACTION_MOVE
)
3417 nRet
|= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
3418 if (dragOperation
& GDK_ACTION_LINK
)
3419 nRet
|= css::datatransfer::dnd::DNDConstants::ACTION_LINK
;
3426 GdkDragAction
getPreferredDragAction(sal_Int8 dragOperation
)
3428 GdkDragAction
eAct(static_cast<GdkDragAction
>(0));
3430 if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_MOVE
)
3431 eAct
= GDK_ACTION_MOVE
;
3432 else if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_COPY
)
3433 eAct
= GDK_ACTION_COPY
;
3434 else if (dragOperation
& css::datatransfer::dnd::DNDConstants::ACTION_LINK
)
3435 eAct
= GDK_ACTION_LINK
;
3441 static bool g_DropSuccessSet
= false;
3442 static bool g_DropSuccess
= false;
3444 class GtkDropTargetDropContext
: public cppu::WeakImplHelper
<css::datatransfer::dnd::XDropTargetDropContext
>
3446 GdkDragContext
*m_pContext
;
3449 GtkDropTargetDropContext(GdkDragContext
*pContext
, guint nTime
)
3450 : m_pContext(pContext
)
3455 // XDropTargetDropContext
3456 virtual void SAL_CALL
acceptDrop(sal_Int8 dragOperation
) override
3458 gdk_drag_status(m_pContext
, getPreferredDragAction(dragOperation
), m_nTime
);
3461 virtual void SAL_CALL
rejectDrop() override
3463 gdk_drag_status(m_pContext
, static_cast<GdkDragAction
>(0), m_nTime
);
3466 virtual void SAL_CALL
dropComplete(sal_Bool bSuccess
) override
3468 gtk_drag_finish(m_pContext
, bSuccess
, false, m_nTime
);
3469 if (GtkDragSource::g_ActiveDragSource
)
3471 g_DropSuccessSet
= true;
3472 g_DropSuccess
= bSuccess
;
3477 class GtkDnDTransferable
: public GtkTransferable
3479 GdkDragContext
*m_pContext
;
3481 GtkWidget
*m_pWidget
;
3482 GtkSalFrame
*m_pFrame
;
3484 GtkSelectionData
*m_pData
;
3486 GtkDnDTransferable(GdkDragContext
*pContext
, guint nTime
, GtkWidget
*pWidget
, GtkSalFrame
*pFrame
)
3487 : m_pContext(pContext
)
3489 , m_pWidget(pWidget
)
3496 virtual css::uno::Any SAL_CALL
getTransferData(const css::datatransfer::DataFlavor
& rFlavor
) override
3498 css::datatransfer::DataFlavor
aFlavor(rFlavor
);
3499 if (aFlavor
.MimeType
== "text/plain;charset=utf-16")
3500 aFlavor
.MimeType
= "text/plain;charset=utf-8";
3502 auto it
= m_aMimeTypeToAtom
.find(aFlavor
.MimeType
);
3503 if (it
== m_aMimeTypeToAtom
.end())
3504 return css::uno::Any();
3506 /* like gtk_clipboard_wait_for_contents run a sub loop
3507 * waiting for drag-data-received triggered from
3511 m_pLoop
= g_main_loop_new(nullptr, true);
3512 m_pFrame
->SetFormatConversionRequest(this);
3514 gtk_drag_get_data(m_pWidget
, m_pContext
, it
->second
, m_nTime
);
3516 if (g_main_loop_is_running(m_pLoop
))
3518 gdk_threads_leave();
3519 g_main_loop_run(m_pLoop
);
3520 gdk_threads_enter();
3523 g_main_loop_unref(m_pLoop
);
3525 m_pFrame
->SetFormatConversionRequest(nullptr);
3530 if (aFlavor
.MimeType
== "text/plain;charset=utf-8")
3533 gchar
*pText
= reinterpret_cast<gchar
*>(gtk_selection_data_get_text(m_pData
));
3535 aStr
= OUString(pText
, rtl_str_getLength(pText
), RTL_TEXTENCODING_UTF8
);
3537 aRet
<<= aStr
.replaceAll("\r\n", "\n");
3542 const guchar
*rawdata
= gtk_selection_data_get_data_with_length(m_pData
,
3544 css::uno::Sequence
<sal_Int8
> aSeq(reinterpret_cast<const sal_Int8
*>(rawdata
), length
);
3548 gtk_selection_data_free(m_pData
);
3553 virtual std::vector
<css::datatransfer::DataFlavor
> getTransferDataFlavorsAsVector() override
3555 std::vector
<GdkAtom
> targets
;
3556 for (GList
* l
= gdk_drag_context_list_targets(m_pContext
); l
; l
= l
->next
)
3557 targets
.push_back(static_cast<GdkAtom
>(l
->data
));
3558 return GtkTransferable::getTransferDataFlavorsAsVector(targets
.data(), targets
.size());
3561 void LoopEnd(GtkSelectionData
*pData
)
3564 g_main_loop_quit(m_pLoop
);
3568 // For LibreOffice internal D&D we provide the Transferable without Gtk
3569 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this
3570 GtkDragSource
* GtkDragSource::g_ActiveDragSource
;
3572 gboolean
GtkSalFrame::signalDragDrop(GtkWidget
* pWidget
, GdkDragContext
* context
, gint x
, gint y
, guint time
, gpointer frame
)
3574 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3576 if (!pThis
->m_pDropTarget
)
3579 css::datatransfer::dnd::DropTargetDropEvent aEvent
;
3580 aEvent
.Source
= static_cast<css::datatransfer::dnd::XDropTarget
*>(pThis
->m_pDropTarget
);
3581 aEvent
.Context
= new GtkDropTargetDropContext(context
, time
);
3582 aEvent
.LocationX
= x
;
3583 aEvent
.LocationY
= y
;
3584 aEvent
.DropAction
= GdkToVcl(gdk_drag_context_get_selected_action(context
));
3585 // ACTION_DEFAULT is documented as...
3586 // 'This means the user did not press any key during the Drag and Drop operation
3587 // and the action that was combined with ACTION_DEFAULT is the system default action'
3588 // in tdf#107031 writer won't insert a link when a heading is dragged from the
3589 // navigator unless this is set. Its unclear really what ACTION_DEFAULT means,
3590 // there is a deprecated 'GDK_ACTION_DEFAULT Means nothing, and should not be used'
3591 // possible equivalent in gtk.
3592 // So (tdf#109227) set ACTION_DEFAULT if no modifier key is held down
3593 GdkModifierType mask
;
3594 gdk_window_get_pointer(widget_get_window(pWidget
), nullptr, nullptr, &mask
);
3595 if (!(mask
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
)))
3596 aEvent
.DropAction
|= css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT
;
3597 aEvent
.SourceActions
= GdkToVcl(gdk_drag_context_get_actions(context
));
3598 css::uno::Reference
<css::datatransfer::XTransferable
> xTransferable
;
3599 // For LibreOffice internal D&D we provide the Transferable without Gtk
3600 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this
3601 if (GtkDragSource::g_ActiveDragSource
)
3602 xTransferable
= GtkDragSource::g_ActiveDragSource
->GetTransferrable();
3604 xTransferable
= new GtkDnDTransferable(context
, time
, pWidget
, pThis
);
3605 aEvent
.Transferable
= xTransferable
;
3607 pThis
->m_pDropTarget
->fire_drop(aEvent
);
3612 class GtkDropTargetDragContext
: public cppu::WeakImplHelper
<css::datatransfer::dnd::XDropTargetDragContext
>
3614 GdkDragContext
*m_pContext
;
3617 GtkDropTargetDragContext(GdkDragContext
*pContext
, guint nTime
)
3618 : m_pContext(pContext
)
3623 virtual void SAL_CALL
acceptDrag(sal_Int8 dragOperation
) override
3625 gdk_drag_status(m_pContext
, getPreferredDragAction(dragOperation
), m_nTime
);
3628 virtual void SAL_CALL
rejectDrag() override
3630 gdk_drag_status(m_pContext
, static_cast<GdkDragAction
>(0), m_nTime
);
3634 void GtkSalFrame::signalDragDropReceived(GtkWidget
* /*pWidget*/, GdkDragContext
* /*context*/, gint
/*x*/, gint
/*y*/, GtkSelectionData
* data
, guint
/*ttype*/, guint
/*time*/, gpointer frame
)
3636 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3639 * If we get a drop, then we will call like gtk_clipboard_wait_for_contents
3640 * with a loop inside a loop to get the right format, so if this is the
3641 * case return to the outer loop here with a copy of the desired data
3643 * don't look at me like that.
3645 if (!pThis
->m_pFormatConversionRequest
)
3648 pThis
->m_pFormatConversionRequest
->LoopEnd(gtk_selection_data_copy(data
));
3651 gboolean
GtkSalFrame::signalDragMotion(GtkWidget
*pWidget
, GdkDragContext
*context
, gint x
, gint y
, guint time
, gpointer frame
)
3653 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3655 if (!pThis
->m_pDropTarget
)
3658 if (!pThis
->m_bInDrag
)
3659 gtk_drag_highlight(pWidget
);
3661 css::datatransfer::dnd::DropTargetDragEnterEvent aEvent
;
3662 aEvent
.Source
= static_cast<css::datatransfer::dnd::XDropTarget
*>(pThis
->m_pDropTarget
);
3663 GtkDropTargetDragContext
* pContext
= new GtkDropTargetDragContext(context
, time
);
3664 //preliminary accept the Drag and select the preferred action, the fire_* will
3665 //inform the original caller of our choice and the callsite can decide
3666 //to overrule this choice. i.e. typically here we default to ACTION_MOVE
3667 sal_Int8 nSourceActions
= GdkToVcl(gdk_drag_context_get_actions(context
));
3668 GdkModifierType mask
;
3669 gdk_window_get_pointer(widget_get_window(pWidget
), nullptr, nullptr, &mask
);
3671 // tdf#109227 if a modifier is held down, default to the matching
3672 // action for that modifier combo, otherwise pick the preferred
3673 // default from the possible source actions
3674 sal_Int8 nNewDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
3675 if ((mask
& GDK_SHIFT_MASK
) && !(mask
& GDK_CONTROL_MASK
))
3676 nNewDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_MOVE
;
3677 else if ((mask
& GDK_CONTROL_MASK
) && !(mask
& GDK_SHIFT_MASK
))
3678 nNewDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_COPY
;
3679 else if ((mask
& GDK_SHIFT_MASK
) && (mask
& GDK_CONTROL_MASK
) )
3680 nNewDropAction
= css::datatransfer::dnd::DNDConstants::ACTION_LINK
;
3681 nNewDropAction
&= nSourceActions
;
3683 GdkDragAction eAction
;
3684 if (!(mask
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
)) && !nNewDropAction
)
3685 eAction
= getPreferredDragAction(nSourceActions
);
3687 eAction
= getPreferredDragAction(nNewDropAction
);
3689 gdk_drag_status(context
, eAction
, time
);
3690 aEvent
.Context
= pContext
;
3691 aEvent
.LocationX
= x
;
3692 aEvent
.LocationY
= y
;
3693 //under wayland at least, the action selected by gdk_drag_status on the
3694 //context is not immediately available via gdk_drag_context_get_selected_action
3695 //so here we set the DropAction from what we selected on the context, not
3696 //what the context says is selected
3697 aEvent
.DropAction
= GdkToVcl(eAction
);
3698 aEvent
.SourceActions
= nSourceActions
;
3700 if (!pThis
->m_bInDrag
)
3702 css::uno::Reference
<css::datatransfer::XTransferable
> xTransferable
;
3703 // For LibreOffice internal D&D we provide the Transferable without Gtk
3704 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this
3705 if (GtkDragSource::g_ActiveDragSource
)
3706 xTransferable
= GtkDragSource::g_ActiveDragSource
->GetTransferrable();
3708 xTransferable
= new GtkDnDTransferable(context
, time
, pWidget
, pThis
);
3709 css::uno::Sequence
<css::datatransfer::DataFlavor
> aFormats
= xTransferable
->getTransferDataFlavors();
3710 aEvent
.SupportedDataFlavors
= aFormats
;
3711 pThis
->m_pDropTarget
->fire_dragEnter(aEvent
);
3712 pThis
->m_bInDrag
= true;
3716 pThis
->m_pDropTarget
->fire_dragOver(aEvent
);
3722 void GtkSalFrame::signalDragLeave(GtkWidget
*pWidget
, GdkDragContext
* /*context*/, guint
/*time*/, gpointer frame
)
3724 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3725 if (!pThis
->m_pDropTarget
)
3727 pThis
->m_bInDrag
= false;
3728 gtk_drag_unhighlight(pWidget
);
3731 void GtkSalFrame::signalDestroy( GtkWidget
* pObj
, gpointer frame
)
3733 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
3734 if( pObj
== pThis
->m_pWindow
)
3736 pThis
->m_pFixedContainer
= nullptr;
3737 pThis
->m_pEventBox
= nullptr;
3738 pThis
->m_pTopLevelGrid
= nullptr;
3739 pThis
->m_pWindow
= nullptr;
3740 pThis
->m_xFrameWeld
.reset();
3741 pThis
->InvalidateGraphics();
3745 // GtkSalFrame::IMHandler
3747 GtkSalFrame::IMHandler::IMHandler( GtkSalFrame
* pFrame
)
3749 m_nPrevKeyPresses( 0 ),
3750 m_pIMContext( nullptr ),
3752 m_bPreeditJustChanged( false )
3754 m_aInputEvent
.mpTextAttr
= nullptr;
3758 GtkSalFrame::IMHandler::~IMHandler()
3760 // cancel an eventual event posted to begin preedit again
3761 GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame
, &m_aInputEvent
, SalEvent::ExtTextInput
);
3765 void GtkSalFrame::IMHandler::createIMContext()
3770 m_pIMContext
= gtk_im_multicontext_new ();
3771 g_signal_connect( m_pIMContext
, "commit",
3772 G_CALLBACK (signalIMCommit
), this );
3773 g_signal_connect( m_pIMContext
, "preedit_changed",
3774 G_CALLBACK (signalIMPreeditChanged
), this );
3775 g_signal_connect( m_pIMContext
, "retrieve_surrounding",
3776 G_CALLBACK (signalIMRetrieveSurrounding
), this );
3777 g_signal_connect( m_pIMContext
, "delete_surrounding",
3778 G_CALLBACK (signalIMDeleteSurrounding
), this );
3779 g_signal_connect( m_pIMContext
, "preedit_start",
3780 G_CALLBACK (signalIMPreeditStart
), this );
3781 g_signal_connect( m_pIMContext
, "preedit_end",
3782 G_CALLBACK (signalIMPreeditEnd
), this );
3784 GetGenericUnixSalData()->ErrorTrapPush();
3785 gtk_im_context_set_client_window(m_pIMContext
, widget_get_window(m_pFrame
->getMouseEventWidget()));
3786 gtk_im_context_focus_in( m_pIMContext
);
3787 GetGenericUnixSalData()->ErrorTrapPop();
3792 void GtkSalFrame::IMHandler::deleteIMContext()
3796 // first give IC a chance to deinitialize
3797 GetGenericUnixSalData()->ErrorTrapPush();
3798 gtk_im_context_set_client_window( m_pIMContext
, nullptr );
3799 GetGenericUnixSalData()->ErrorTrapPop();
3801 g_object_unref( m_pIMContext
);
3802 m_pIMContext
= nullptr;
3806 void GtkSalFrame::IMHandler::doCallEndExtTextInput()
3808 m_aInputEvent
.mpTextAttr
= nullptr;
3809 m_pFrame
->CallCallbackExc( SalEvent::EndExtTextInput
, nullptr );
3812 void GtkSalFrame::IMHandler::updateIMSpotLocation()
3814 SalExtTextInputPosEvent aPosEvent
;
3815 m_pFrame
->CallCallbackExc( SalEvent::ExtTextInputPos
, static_cast<void*>(&aPosEvent
) );
3817 aArea
.x
= aPosEvent
.mnX
;
3818 aArea
.y
= aPosEvent
.mnY
;
3819 aArea
.width
= aPosEvent
.mnWidth
;
3820 aArea
.height
= aPosEvent
.mnHeight
;
3821 GetGenericUnixSalData()->ErrorTrapPush();
3822 gtk_im_context_set_cursor_location( m_pIMContext
, &aArea
);
3823 GetGenericUnixSalData()->ErrorTrapPop();
3826 void GtkSalFrame::IMHandler::sendEmptyCommit()
3828 vcl::DeletionListener
aDel( m_pFrame
);
3830 SalExtTextInputEvent aEmptyEv
;
3831 aEmptyEv
.mpTextAttr
= nullptr;
3832 aEmptyEv
.maText
.clear();
3833 aEmptyEv
.mnCursorPos
= 0;
3834 aEmptyEv
.mnCursorFlags
= 0;
3835 m_pFrame
->CallCallbackExc( SalEvent::ExtTextInput
, static_cast<void*>(&aEmptyEv
) );
3836 if( ! aDel
.isDeleted() )
3837 m_pFrame
->CallCallbackExc( SalEvent::EndExtTextInput
, nullptr );
3840 void GtkSalFrame::IMHandler::endExtTextInput( EndExtTextInputFlags
/*nFlags*/ )
3842 gtk_im_context_reset ( m_pIMContext
);
3844 if( m_aInputEvent
.mpTextAttr
)
3846 vcl::DeletionListener
aDel( m_pFrame
);
3847 // delete preedit in sal (commit an empty string)
3849 if( ! aDel
.isDeleted() )
3851 // mark previous preedit state again (will e.g. be sent at focus gain)
3852 m_aInputEvent
.mpTextAttr
= &m_aInputFlags
[0];
3855 // begin preedit again
3856 GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame
, &m_aInputEvent
, SalEvent::ExtTextInput
);
3862 void GtkSalFrame::IMHandler::focusChanged( bool bFocusIn
)
3864 m_bFocused
= bFocusIn
;
3867 GetGenericUnixSalData()->ErrorTrapPush();
3868 gtk_im_context_focus_in( m_pIMContext
);
3869 GetGenericUnixSalData()->ErrorTrapPop();
3870 if( m_aInputEvent
.mpTextAttr
)
3873 // begin preedit again
3874 GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame
, &m_aInputEvent
, SalEvent::ExtTextInput
);
3879 GetGenericUnixSalData()->ErrorTrapPush();
3880 gtk_im_context_focus_out( m_pIMContext
);
3881 GetGenericUnixSalData()->ErrorTrapPop();
3882 // cancel an eventual event posted to begin preedit again
3883 GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame
, &m_aInputEvent
, SalEvent::ExtTextInput
);
3887 bool GtkSalFrame::IMHandler::handleKeyEvent( GdkEventKey
* pEvent
)
3889 vcl::DeletionListener
aDel( m_pFrame
);
3891 if( pEvent
->type
== GDK_KEY_PRESS
)
3893 // Add this key press event to the list of previous key presses
3894 // to which we compare key release events. If a later key release
3895 // event has a matching key press event in this list, we swallow
3896 // the key release because some GTK Input Methods don't swallow it
3898 m_aPrevKeyPresses
.emplace_back(pEvent
);
3899 m_nPrevKeyPresses
++;
3901 // Also pop off the earliest key press event if there are more than 10
3903 while (m_nPrevKeyPresses
> 10)
3905 m_aPrevKeyPresses
.pop_front();
3906 m_nPrevKeyPresses
--;
3909 GObject
* pRef
= G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext
) ) );
3911 // #i51353# update spot location on every key input since we cannot
3912 // know which key may activate a preedit choice window
3913 updateIMSpotLocation();
3914 if( aDel
.isDeleted() )
3917 gboolean bResult
= gtk_im_context_filter_keypress( m_pIMContext
, pEvent
);
3918 g_object_unref( pRef
);
3920 if( aDel
.isDeleted() )
3923 m_bPreeditJustChanged
= false;
3929 SAL_WARN_IF( m_nPrevKeyPresses
<= 0, "vcl.gtk3", "key press has vanished !" );
3930 if( ! m_aPrevKeyPresses
.empty() ) // sanity check
3932 // event was not swallowed, do not filter a following
3933 // key release event
3934 // note: this relies on gtk_im_context_filter_keypress
3935 // returning without calling a handler (in the "not swallowed"
3936 // case ) which might change the previous key press list so
3937 // we would pop the wrong event here
3938 m_aPrevKeyPresses
.pop_back();
3939 m_nPrevKeyPresses
--;
3944 // Determine if we got an earlier key press event corresponding to this key release
3945 if (pEvent
->type
== GDK_KEY_RELEASE
)
3947 GObject
* pRef
= G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext
) ) );
3948 gboolean bResult
= gtk_im_context_filter_keypress( m_pIMContext
, pEvent
);
3949 g_object_unref( pRef
);
3951 if( aDel
.isDeleted() )
3954 m_bPreeditJustChanged
= false;
3956 std::list
<PreviousKeyPress
>::iterator iter
= m_aPrevKeyPresses
.begin();
3957 std::list
<PreviousKeyPress
>::iterator iter_end
= m_aPrevKeyPresses
.end();
3958 while (iter
!= iter_end
)
3960 // If we found a corresponding previous key press event, swallow the release
3961 // and remove the earlier key press from our list
3962 if (*iter
== pEvent
)
3964 m_aPrevKeyPresses
.erase(iter
);
3965 m_nPrevKeyPresses
--;
3979 * #122282# still more hacking: some IMEs never start a preedit but simply commit
3980 * in this case we cannot commit a single character. Workaround: do not do the
3981 * single key hack for enter or space if the unicode committed does not match
3984 static bool checkSingleKeyCommitHack( guint keyval
, sal_Unicode cCode
)
3989 case GDK_KEY_KP_Enter
:
3990 case GDK_KEY_Return
:
3991 if( cCode
!= '\n' && cCode
!= '\r' )
3995 case GDK_KEY_KP_Space
:
4005 void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext
* /*pContext*/, gchar
* pText
, gpointer im_handler
)
4007 GtkSalFrame::IMHandler
* pThis
= static_cast<GtkSalFrame::IMHandler
*>(im_handler
);
4009 SolarMutexGuard aGuard
;
4010 vcl::DeletionListener
aDel( pThis
->m_pFrame
);
4012 const bool bWasPreedit
=
4013 (pThis
->m_aInputEvent
.mpTextAttr
!= nullptr) ||
4014 pThis
->m_bPreeditJustChanged
;
4016 pThis
->m_aInputEvent
.mpTextAttr
= nullptr;
4017 pThis
->m_aInputEvent
.maText
= OUString( pText
, strlen(pText
), RTL_TEXTENCODING_UTF8
);
4018 pThis
->m_aInputEvent
.mnCursorPos
= pThis
->m_aInputEvent
.maText
.getLength();
4019 pThis
->m_aInputEvent
.mnCursorFlags
= 0;
4021 pThis
->m_aInputFlags
.clear();
4023 /* necessary HACK: all keyboard input comes in here as soon as a IMContext is set
4024 * which is logical and consequent. But since even simple input like
4025 * <space> comes through the commit signal instead of signalKey
4026 * and all kinds of windows only implement KeyInput (e.g. PushButtons,
4027 * RadioButtons and a lot of other Controls), will send a single
4028 * KeyInput/KeyUp sequence instead of an ExtText event if there
4029 * never was a preedit and the text is only one character.
4031 * In this case there the last ExtText event must have been
4032 * SalEvent::EndExtTextInput, either because of a regular commit
4033 * or because there never was a preedit.
4035 bool bSingleCommit
= false;
4037 && pThis
->m_aInputEvent
.maText
.getLength() == 1
4038 && ! pThis
->m_aPrevKeyPresses
.empty()
4041 const PreviousKeyPress
& rKP
= pThis
->m_aPrevKeyPresses
.back();
4042 sal_Unicode aOrigCode
= pThis
->m_aInputEvent
.maText
[0];
4044 if( checkSingleKeyCommitHack( rKP
.keyval
, aOrigCode
) )
4046 pThis
->m_pFrame
->doKeyCallback( rKP
.state
, rKP
.keyval
, rKP
.hardware_keycode
, rKP
.group
, aOrigCode
, true, true );
4047 bSingleCommit
= true;
4050 if( ! bSingleCommit
)
4052 pThis
->m_pFrame
->CallCallbackExc( SalEvent::ExtTextInput
, static_cast<void*>(&pThis
->m_aInputEvent
));
4053 if( ! aDel
.isDeleted() )
4054 pThis
->doCallEndExtTextInput();
4056 if( ! aDel
.isDeleted() )
4058 // reset input event
4059 pThis
->m_aInputEvent
.maText
.clear();
4060 pThis
->m_aInputEvent
.mnCursorPos
= 0;
4061 pThis
->updateIMSpotLocation();
4066 void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext
*, gpointer im_handler
)
4068 GtkSalFrame::IMHandler
* pThis
= static_cast<GtkSalFrame::IMHandler
*>(im_handler
);
4070 char* pText
= nullptr;
4071 PangoAttrList
* pAttrs
= nullptr;
4072 gint nCursorPos
= 0;
4074 gtk_im_context_get_preedit_string( pThis
->m_pIMContext
,
4078 if( pText
&& ! *pText
) // empty string
4080 // change from nothing to nothing -> do not start preedit
4081 // e.g. this will activate input into a calc cell without
4083 if( pThis
->m_aInputEvent
.maText
.getLength() == 0 )
4086 pango_attr_list_unref( pAttrs
);
4091 pThis
->m_bPreeditJustChanged
= true;
4093 bool bEndPreedit
= (!pText
|| !*pText
) && pThis
->m_aInputEvent
.mpTextAttr
!= nullptr;
4094 pThis
->m_aInputEvent
.maText
= pText
? OUString( pText
, strlen(pText
), RTL_TEXTENCODING_UTF8
) : OUString();
4095 pThis
->m_aInputEvent
.mnCursorPos
= nCursorPos
;
4096 pThis
->m_aInputEvent
.mnCursorFlags
= 0;
4098 pThis
->m_aInputFlags
= std::vector
<ExtTextInputAttr
>( std::max( 1, static_cast<int>(pThis
->m_aInputEvent
.maText
.getLength()) ), ExtTextInputAttr::NONE
);
4100 PangoAttrIterator
*iter
= pango_attr_list_get_iterator(pAttrs
);
4103 GSList
*attr_list
= nullptr;
4104 GSList
*tmp_list
= nullptr;
4106 ExtTextInputAttr sal_attr
= ExtTextInputAttr::NONE
;
4108 pango_attr_iterator_range (iter
, &start
, &end
);
4109 if (start
== G_MAXINT
|| end
== G_MAXINT
)
4111 auto len
= pText
? g_utf8_strlen(pText
, -1) : 0;
4112 if (end
== G_MAXINT
)
4114 if (start
== G_MAXINT
)
4120 start
= g_utf8_pointer_to_offset (pText
, pText
+ start
);
4121 end
= g_utf8_pointer_to_offset (pText
, pText
+ end
);
4123 tmp_list
= attr_list
= pango_attr_iterator_get_attrs (iter
);
4126 PangoAttribute
*pango_attr
= static_cast<PangoAttribute
*>(tmp_list
->data
);
4128 switch (pango_attr
->klass
->type
)
4130 case PANGO_ATTR_BACKGROUND
:
4131 sal_attr
|= ExtTextInputAttr::Highlight
;
4132 pThis
->m_aInputEvent
.mnCursorFlags
|= EXTTEXTINPUT_CURSOR_INVISIBLE
;
4134 case PANGO_ATTR_UNDERLINE
:
4135 sal_attr
|= ExtTextInputAttr::Underline
;
4137 case PANGO_ATTR_STRIKETHROUGH
:
4138 sal_attr
|= ExtTextInputAttr::RedText
;
4143 pango_attribute_destroy (pango_attr
);
4144 tmp_list
= tmp_list
->next
;
4146 if (sal_attr
== ExtTextInputAttr::NONE
)
4147 sal_attr
|= ExtTextInputAttr::Underline
;
4148 g_slist_free (attr_list
);
4150 // Set the sal attributes on our text
4151 for (int i
= start
; i
< end
; ++i
)
4153 SAL_WARN_IF(i
>= static_cast<int>(pThis
->m_aInputFlags
.size()),
4154 "vcl.gtk3", "pango attrib out of range. Broken range: "
4155 << start
<< "," << end
<< " Legal range: 0,"
4156 << pThis
->m_aInputFlags
.size());
4157 if (i
>= static_cast<int>(pThis
->m_aInputFlags
.size()))
4159 pThis
->m_aInputFlags
[i
] |= sal_attr
;
4161 } while (pango_attr_iterator_next (iter
));
4162 pango_attr_iterator_destroy(iter
);
4164 pThis
->m_aInputEvent
.mpTextAttr
= &pThis
->m_aInputFlags
[0];
4167 pango_attr_list_unref( pAttrs
);
4169 SolarMutexGuard aGuard
;
4170 vcl::DeletionListener
aDel( pThis
->m_pFrame
);
4172 pThis
->m_pFrame
->CallCallbackExc( SalEvent::ExtTextInput
, static_cast<void*>(&pThis
->m_aInputEvent
));
4173 if( bEndPreedit
&& ! aDel
.isDeleted() )
4174 pThis
->doCallEndExtTextInput();
4175 if( ! aDel
.isDeleted() )
4176 pThis
->updateIMSpotLocation();
4179 void GtkSalFrame::IMHandler::signalIMPreeditStart( GtkIMContext
*, gpointer
/*im_handler*/ )
4183 void GtkSalFrame::IMHandler::signalIMPreeditEnd( GtkIMContext
*, gpointer im_handler
)
4185 GtkSalFrame::IMHandler
* pThis
= static_cast<GtkSalFrame::IMHandler
*>(im_handler
);
4187 pThis
->m_bPreeditJustChanged
= true;
4189 SolarMutexGuard aGuard
;
4190 vcl::DeletionListener
aDel( pThis
->m_pFrame
);
4191 pThis
->doCallEndExtTextInput();
4192 if( ! aDel
.isDeleted() )
4193 pThis
->updateIMSpotLocation();
4196 uno::Reference
<accessibility::XAccessibleEditableText
>
4197 FindFocus(uno::Reference
< accessibility::XAccessibleContext
> const & xContext
)
4200 return uno::Reference
< accessibility::XAccessibleEditableText
>();
4202 uno::Reference
<accessibility::XAccessibleStateSet
> xState
= xContext
->getAccessibleStateSet();
4205 if (xState
->contains(accessibility::AccessibleStateType::FOCUSED
))
4207 uno::Reference
< accessibility::XAccessibleEditableText
> xText
=
4208 uno::Reference
<accessibility::XAccessibleEditableText
>(xContext
, uno::UNO_QUERY
);
4211 if (xState
->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS
))
4212 return uno::Reference
< accessibility::XAccessibleEditableText
>();
4216 for (sal_Int32 i
= 0; i
< xContext
->getAccessibleChildCount(); ++i
)
4218 uno::Reference
< accessibility::XAccessible
> xChild
= xContext
->getAccessibleChild(i
);
4221 uno::Reference
< accessibility::XAccessibleContext
> xChildContext
= xChild
->getAccessibleContext();
4222 if (!xChildContext
.is())
4224 uno::Reference
< accessibility::XAccessibleEditableText
> xText
= FindFocus(xChildContext
);
4228 return uno::Reference
< accessibility::XAccessibleEditableText
>();
4231 static uno::Reference
<accessibility::XAccessibleEditableText
> lcl_GetxText(vcl::Window
*pFocusWin
)
4233 uno::Reference
<accessibility::XAccessibleEditableText
> xText
;
4236 uno::Reference
< accessibility::XAccessible
> xAccessible( pFocusWin
->GetAccessible() );
4237 if (xAccessible
.is())
4238 xText
= FindFocus(xAccessible
->getAccessibleContext());
4240 catch(const uno::Exception
& e
)
4242 SAL_WARN( "vcl.gtk3", "Exception in getting input method surrounding text: " << e
);
4247 gboolean
GtkSalFrame::IMHandler::signalIMRetrieveSurrounding( GtkIMContext
* pContext
, gpointer
/*im_handler*/ )
4249 vcl::Window
*pFocusWin
= Application::GetFocusWindow();
4253 uno::Reference
<accessibility::XAccessibleEditableText
> xText
= lcl_GetxText(pFocusWin
);
4256 sal_Int32 nPosition
= xText
->getCaretPosition();
4257 OUString sAllText
= xText
->getText();
4258 OString sUTF
= OUStringToOString(sAllText
, RTL_TEXTENCODING_UTF8
);
4259 OUString
sCursorText(sAllText
.copy(0, nPosition
));
4260 gtk_im_context_set_surrounding(pContext
, sUTF
.getStr(), sUTF
.getLength(),
4261 OUStringToOString(sCursorText
, RTL_TEXTENCODING_UTF8
).getLength());
4268 gboolean
GtkSalFrame::IMHandler::signalIMDeleteSurrounding( GtkIMContext
*, gint offset
, gint nchars
,
4269 gpointer
/*im_handler*/ )
4271 vcl::Window
*pFocusWin
= Application::GetFocusWindow();
4275 uno::Reference
<accessibility::XAccessibleEditableText
> xText
= lcl_GetxText(pFocusWin
);
4278 sal_Int32 nPosition
= xText
->getCaretPosition();
4279 // #i111768# range checking
4280 sal_Int32 nDeletePos
= nPosition
+ offset
;
4281 sal_Int32 nDeleteEnd
= nDeletePos
+ nchars
;
4286 if (nDeleteEnd
> xText
->getCharacterCount())
4287 nDeleteEnd
= xText
->getCharacterCount();
4289 xText
->deleteText(nDeletePos
, nDeleteEnd
);
4290 //tdf91641 adjust cursor if deleted chars shift it forward (normal case)
4291 if (nDeletePos
< nPosition
)
4293 if (nDeleteEnd
<= nPosition
)
4294 nPosition
= nPosition
- (nDeleteEnd
- nDeletePos
);
4296 nPosition
= nDeletePos
;
4298 if (xText
->getCharacterCount() >= nPosition
)
4299 xText
->setCaretPosition( nPosition
);
4307 Size
GtkSalDisplay::GetScreenSize( int nDisplayScreen
)
4309 tools::Rectangle aRect
= m_pSys
->GetDisplayScreenPosSizePixel( nDisplayScreen
);
4310 return Size( aRect
.GetWidth(), aRect
.GetHeight() );
4313 sal_uIntPtr
GtkSalFrame::GetNativeWindowHandle(GtkWidget
*pWidget
)
4315 (void) this; // Silence loplugin:staticmethods
4316 GdkDisplay
*pDisplay
= getGdkDisplay();
4317 GdkWindow
*pWindow
= gtk_widget_get_window(pWidget
);
4319 #if defined(GDK_WINDOWING_X11)
4320 if (GDK_IS_X11_DISPLAY(pDisplay
))
4322 return GDK_WINDOW_XID(pWindow
);
4325 #if defined(GDK_WINDOWING_WAYLAND)
4326 if (GDK_IS_WAYLAND_DISPLAY(pDisplay
))
4328 return reinterpret_cast<sal_uIntPtr
>(gdk_wayland_window_get_wl_surface(pWindow
));
4334 sal_uIntPtr
GtkSalFrame::GetNativeWindowHandle()
4336 return GetNativeWindowHandle(m_pWindow
);
4339 void GtkDragSource::startDrag(const datatransfer::dnd::DragGestureEvent
& rEvent
,
4340 sal_Int8 sourceActions
, sal_Int32
/*cursor*/, sal_Int32
/*image*/,
4341 const css::uno::Reference
<css::datatransfer::XTransferable
>& rTrans
,
4342 const css::uno::Reference
<css::datatransfer::dnd::XDragSourceListener
>& rListener
)
4344 m_xListener
= rListener
;
4349 css::uno::Sequence
<css::datatransfer::DataFlavor
> aFormats
= rTrans
->getTransferDataFlavors();
4350 std::vector
<GtkTargetEntry
> aGtkTargets(m_aConversionHelper
.FormatsToGtk(aFormats
));
4351 GtkTargetList
*pTargetList
= gtk_target_list_new(aGtkTargets
.data(), aGtkTargets
.size());
4353 gint nDragButton
= 1; // default to left button
4354 css::awt::MouseEvent aEvent
;
4355 if (rEvent
.Event
>>= aEvent
)
4357 if (aEvent
.Buttons
& css::awt::MouseButton::LEFT
)
4359 else if (aEvent
.Buttons
& css::awt::MouseButton::RIGHT
)
4361 else if (aEvent
.Buttons
& css::awt::MouseButton::MIDDLE
)
4365 // For LibreOffice internal D&D we provide the Transferable without Gtk
4366 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this
4367 g_ActiveDragSource
= this;
4368 g_DropSuccessSet
= false;
4369 g_DropSuccess
= false;
4371 m_pFrame
->startDrag(nDragButton
, rEvent
.DragOriginX
, rEvent
.DragOriginY
,
4372 VclToGdk(sourceActions
), pTargetList
);
4374 gtk_target_list_unref(pTargetList
);
4375 for (auto &a
: aGtkTargets
)
4382 void GtkSalFrame::startDrag(gint nButton
, gint nDragOriginX
, gint nDragOriginY
,
4383 GdkDragAction sourceActions
, GtkTargetList
* pTargetList
)
4385 SolarMutexGuard aGuard
;
4387 assert(m_pDragSource
);
4389 GdkEvent aFakeEvent
;
4390 memset(&aFakeEvent
, 0, sizeof(GdkEvent
));
4391 aFakeEvent
.type
= GDK_BUTTON_PRESS
;
4392 aFakeEvent
.button
.window
= widget_get_window(getMouseEventWidget());
4393 aFakeEvent
.button
.time
= GDK_CURRENT_TIME
;
4394 GdkDeviceManager
* pDeviceManager
= gdk_display_get_device_manager(getGdkDisplay());
4395 aFakeEvent
.button
.device
= gdk_device_manager_get_client_pointer(pDeviceManager
);
4397 GdkDragContext
*pContext
= gtk_drag_begin_with_coordinates(getMouseEventWidget(),
4406 m_pDragSource
->dragFailed();
4409 void GtkDragSource::dragFailed()
4411 if (m_xListener
.is())
4413 datatransfer::dnd::DragSourceDropEvent aEv
;
4414 aEv
.DropAction
= datatransfer::dnd::DNDConstants::ACTION_NONE
;
4415 aEv
.DropSuccess
= false;
4416 auto xListener
= m_xListener
;
4417 m_xListener
.clear();
4418 xListener
->dragDropEnd(aEv
);
4422 gboolean
GtkSalFrame::signalDragFailed(GtkWidget
* /*widget*/, GdkDragContext
* /*context*/, GtkDragResult
/*result*/, gpointer frame
)
4424 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4425 if (!pThis
->m_pDragSource
)
4427 pThis
->m_pDragSource
->dragFailed();
4431 void GtkDragSource::dragDelete()
4433 if (m_xListener
.is())
4435 datatransfer::dnd::DragSourceDropEvent aEv
;
4436 aEv
.DropAction
= datatransfer::dnd::DNDConstants::ACTION_MOVE
;
4437 aEv
.DropSuccess
= true;
4438 auto xListener
= m_xListener
;
4439 m_xListener
.clear();
4440 xListener
->dragDropEnd(aEv
);
4444 void GtkSalFrame::signalDragDelete(GtkWidget
* /*widget*/, GdkDragContext
* /*context*/, gpointer frame
)
4446 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4447 if (!pThis
->m_pDragSource
)
4449 pThis
->m_pDragSource
->dragDelete();
4452 void GtkDragSource::dragEnd(GdkDragContext
* context
)
4454 if (m_xListener
.is())
4456 datatransfer::dnd::DragSourceDropEvent aEv
;
4457 aEv
.DropAction
= GdkToVcl(gdk_drag_context_get_selected_action(context
));
4458 // an internal drop can accept the drop but fail with dropComplete( false )
4459 // this is different than the GTK API
4460 if (g_DropSuccessSet
)
4461 aEv
.DropSuccess
= g_DropSuccess
;
4463 aEv
.DropSuccess
= true;
4464 auto xListener
= m_xListener
;
4465 m_xListener
.clear();
4466 xListener
->dragDropEnd(aEv
);
4468 g_ActiveDragSource
= nullptr;
4471 void GtkSalFrame::signalDragEnd(GtkWidget
* /*widget*/, GdkDragContext
* context
, gpointer frame
)
4473 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4474 if (!pThis
->m_pDragSource
)
4476 pThis
->m_pDragSource
->dragEnd(context
);
4479 void GtkDragSource::dragDataGet(GtkSelectionData
*data
, guint info
)
4481 m_aConversionHelper
.setSelectionData(m_xTrans
, data
, info
);
4484 void GtkSalFrame::signalDragDataGet(GtkWidget
* /*widget*/, GdkDragContext
* /*context*/, GtkSelectionData
*data
, guint info
,
4485 guint
/*time*/, gpointer frame
)
4487 GtkSalFrame
* pThis
= static_cast<GtkSalFrame
*>(frame
);
4488 if (!pThis
->m_pDragSource
)
4490 pThis
->m_pDragSource
->dragDataGet(data
, info
);
4493 bool GtkSalFrame::CallCallbackExc(SalEvent nEvent
, const void* pEvent
) const
4498 nRet
= CallCallback(nEvent
, pEvent
);
4500 catch (const css::uno::Exception
&)
4502 auto e
= cppu::getCaughtException();
4503 GtkSalData
*pSalData
= static_cast<GtkSalData
*>(GetSalData());
4504 pSalData
->setException(e
);
4506 catch (std::exception
& e
)
4508 static_cast<GtkSalData
*>(GetSalData())->setException(
4510 css::uno::RuntimeException(
4511 "wrapped std::exception "
4512 + o3tl::runtimeToOUString(e
.what()))));
4516 static_cast<GtkSalData
*>(GetSalData())->setException(
4518 css::uno::RuntimeException("wrapped unknown exception")));
4523 void GtkSalFrame::nopaint_container_resize_children(GtkContainer
*pContainer
)
4525 m_bSalObjectSetPosSize
= true;
4526 gtk_container_resize_children(pContainer
);
4527 m_bSalObjectSetPosSize
= false;
4530 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */