1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 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/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 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 . 18 */ 19 20 #include <config_version.h> 21 22 #include <unx/gtk/gtkframe.hxx> 23 #include <unx/gtk/gtkdata.hxx> 24 #include <unx/gtk/gtkinst.hxx> 25 #include <unx/gtk/gtkgdi.hxx> 26 #include <unx/gtk/gtksalmenu.hxx> 27 #include <unx/gtk/hudawareness.h> 28 #include <vcl/event.hxx> 29 #include <vcl/i18nhelp.hxx> 30 #include <vcl/keycodes.hxx> 31 #include <unx/geninst.h> 32 #include <headless/svpgdi.hxx> 33 #include <sal/log.hxx> 34 #include <comphelper/diagnose_ex.hxx> 35 #include <vcl/toolkit/floatwin.hxx> 36 #include <vcl/toolkit/unowrap.hxx> 37 #include <vcl/svapp.hxx> 38 #include <vcl/weld.hxx> 39 #include <vcl/window.hxx> 40 #include <vcl/settings.hxx> 41 42 #include <gtk/gtk.h> 43 44 #include <X11/Xlib.h> 45 #include <X11/Xutil.h> 46 #include <unx/gtk/gtkbackend.hxx> 47 48 #include <strings.hrc> 49 #include <window.h> 50 51 #include <basegfx/vector/b2ivector.hxx> 52 #include <officecfg/Office/Common.hxx> 53 54 #include <dlfcn.h> 55 56 #include <algorithm> 57 58 #if OSL_DEBUG_LEVEL > 1 59 # include <cstdio> 60 #endif 61 62 #include <i18nlangtag/mslangid.hxx> 63 64 #include <cstdlib> 65 #include <cmath> 66 67 #include <com/sun/star/awt/MouseButton.hpp> 68 #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> 69 #include <com/sun/star/frame/Desktop.hpp> 70 #include <com/sun/star/util/XModifiable.hpp> 71 72 #if !GTK_CHECK_VERSION(4, 0, 0) 73 # define GDK_ALT_MASK GDK_MOD1_MASK 74 # define GDK_TOPLEVEL_STATE_MAXIMIZED GDK_WINDOW_STATE_MAXIMIZED 75 # define GDK_TOPLEVEL_STATE_MINIMIZED GDK_WINDOW_STATE_ICONIFIED 76 # define gdk_wayland_surface_get_wl_surface gdk_wayland_window_get_wl_surface 77 # define gdk_x11_surface_get_xid gdk_x11_window_get_xid 78 #endif 79 80 using namespace com::sun::star; 81 82 int GtkSalFrame::m_nFloats = 0; 83 84 static GDBusConnection* pSessionBus = nullptr; 85 86 static void EnsureSessionBus() 87 { 88 if (!pSessionBus) 89 pSessionBus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr); 90 } 91 92 sal_uInt16 GtkSalFrame::GetKeyModCode( guint state ) 93 { 94 sal_uInt16 nCode = 0; 95 if( state & GDK_SHIFT_MASK ) 96 nCode |= KEY_SHIFT; 97 if( state & GDK_CONTROL_MASK ) 98 nCode |= KEY_MOD1; 99 if (state & GDK_ALT_MASK) 100 nCode |= KEY_MOD2; 101 if( state & GDK_SUPER_MASK ) 102 nCode |= KEY_MOD3; 103 return nCode; 104 } 105 106 sal_uInt16 GtkSalFrame::GetMouseModCode( guint state ) 107 { 108 sal_uInt16 nCode = GetKeyModCode( state ); 109 if( state & GDK_BUTTON1_MASK ) 110 nCode |= MOUSE_LEFT; 111 if( state & GDK_BUTTON2_MASK ) 112 nCode |= MOUSE_MIDDLE; 113 if( state & GDK_BUTTON3_MASK ) 114 nCode |= MOUSE_RIGHT; 115 116 return nCode; 117 } 118 119 // KEY_F26 is the last function key known to keycodes.hxx 120 static bool IsFunctionKeyVal(guint keyval) 121 { 122 return keyval >= GDK_KEY_F1 && keyval <= GDK_KEY_F26; 123 } 124 125 sal_uInt16 GtkSalFrame::GetKeyCode(guint keyval) 126 { 127 sal_uInt16 nCode = 0; 128 if( keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9 ) 129 nCode = KEY_0 + (keyval-GDK_KEY_0); 130 else if( keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9 ) 131 nCode = KEY_0 + (keyval-GDK_KEY_KP_0); 132 else if( keyval >= GDK_KEY_A && keyval <= GDK_KEY_Z ) 133 nCode = KEY_A + (keyval-GDK_KEY_A ); 134 else if( keyval >= GDK_KEY_a && keyval <= GDK_KEY_z ) 135 nCode = KEY_A + (keyval-GDK_KEY_a ); 136 else if (IsFunctionKeyVal(keyval)) 137 { 138 switch( keyval ) 139 { 140 // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx 141 // although GDK_KEY_F1 ... GDK_KEY_L10 are known to 142 // gdk/gdkkeysyms.h, they are unlikely to be generated 143 // except possibly by Solaris systems 144 // this whole section needs review 145 case GDK_KEY_L2: 146 nCode = KEY_F12; 147 break; 148 case GDK_KEY_L3: nCode = KEY_PROPERTIES; break; 149 case GDK_KEY_L4: nCode = KEY_UNDO; break; 150 case GDK_KEY_L6: nCode = KEY_COPY; break; // KEY_F16 151 case GDK_KEY_L8: nCode = KEY_PASTE; break; // KEY_F18 152 case GDK_KEY_L10: nCode = KEY_CUT; break; // KEY_F20 153 default: 154 nCode = KEY_F1 + (keyval-GDK_KEY_F1); break; 155 } 156 } 157 else 158 { 159 switch( keyval ) 160 { 161 case GDK_KEY_KP_Down: 162 case GDK_KEY_Down: nCode = KEY_DOWN; break; 163 case GDK_KEY_KP_Up: 164 case GDK_KEY_Up: nCode = KEY_UP; break; 165 case GDK_KEY_KP_Left: 166 case GDK_KEY_Left: nCode = KEY_LEFT; break; 167 case GDK_KEY_KP_Right: 168 case GDK_KEY_Right: nCode = KEY_RIGHT; break; 169 case GDK_KEY_KP_Begin: 170 case GDK_KEY_KP_Home: 171 case GDK_KEY_Begin: 172 case GDK_KEY_Home: nCode = KEY_HOME; break; 173 case GDK_KEY_KP_End: 174 case GDK_KEY_End: nCode = KEY_END; break; 175 case GDK_KEY_KP_Page_Up: 176 case GDK_KEY_Page_Up: nCode = KEY_PAGEUP; break; 177 case GDK_KEY_KP_Page_Down: 178 case GDK_KEY_Page_Down: nCode = KEY_PAGEDOWN; break; 179 case GDK_KEY_KP_Enter: 180 case GDK_KEY_Return: nCode = KEY_RETURN; break; 181 case GDK_KEY_Escape: nCode = KEY_ESCAPE; break; 182 case GDK_KEY_ISO_Left_Tab: 183 case GDK_KEY_KP_Tab: 184 case GDK_KEY_Tab: nCode = KEY_TAB; break; 185 case GDK_KEY_BackSpace: nCode = KEY_BACKSPACE; break; 186 case GDK_KEY_KP_Space: 187 case GDK_KEY_space: nCode = KEY_SPACE; break; 188 case GDK_KEY_KP_Insert: 189 case GDK_KEY_Insert: nCode = KEY_INSERT; break; 190 case GDK_KEY_KP_Delete: 191 case GDK_KEY_Delete: nCode = KEY_DELETE; break; 192 case GDK_KEY_plus: 193 case GDK_KEY_KP_Add: nCode = KEY_ADD; break; 194 case GDK_KEY_minus: 195 case GDK_KEY_KP_Subtract: nCode = KEY_SUBTRACT; break; 196 case GDK_KEY_asterisk: 197 case GDK_KEY_KP_Multiply: nCode = KEY_MULTIPLY; break; 198 case GDK_KEY_slash: 199 case GDK_KEY_KP_Divide: nCode = KEY_DIVIDE; break; 200 case GDK_KEY_period: nCode = KEY_POINT; break; 201 case GDK_KEY_decimalpoint: nCode = KEY_POINT; break; 202 case GDK_KEY_comma: nCode = KEY_COMMA; break; 203 case GDK_KEY_less: nCode = KEY_LESS; break; 204 case GDK_KEY_greater: nCode = KEY_GREATER; break; 205 case GDK_KEY_KP_Equal: 206 case GDK_KEY_equal: nCode = KEY_EQUAL; break; 207 case GDK_KEY_Find: nCode = KEY_FIND; break; 208 case GDK_KEY_Menu: nCode = KEY_CONTEXTMENU;break; 209 case GDK_KEY_Help: nCode = KEY_HELP; break; 210 case GDK_KEY_Undo: nCode = KEY_UNDO; break; 211 case GDK_KEY_Redo: nCode = KEY_REPEAT; break; 212 // on a sun keyboard this actually is usually SunXK_Stop = 0x0000FF69 (XK_Cancel), 213 // but VCL doesn't have a key definition for that 214 case GDK_KEY_Cancel: nCode = KEY_F11; break; 215 case GDK_KEY_KP_Decimal: 216 case GDK_KEY_KP_Separator: nCode = KEY_DECIMAL; break; 217 case GDK_KEY_asciitilde: nCode = KEY_TILDE; break; 218 case GDK_KEY_leftsinglequotemark: 219 case GDK_KEY_quoteleft: nCode = KEY_QUOTELEFT; break; 220 case GDK_KEY_bracketleft: nCode = KEY_BRACKETLEFT; break; 221 case GDK_KEY_bracketright: nCode = KEY_BRACKETRIGHT; break; 222 case GDK_KEY_semicolon: nCode = KEY_SEMICOLON; break; 223 case GDK_KEY_quoteright: nCode = KEY_QUOTERIGHT; break; 224 case GDK_KEY_braceright: nCode = KEY_RIGHTCURLYBRACKET; break; 225 case GDK_KEY_numbersign: nCode = KEY_NUMBERSIGN; break; 226 case GDK_KEY_Forward: nCode = KEY_XF86FORWARD; break; 227 case GDK_KEY_Back: nCode = KEY_XF86BACK; break; 228 case GDK_KEY_colon: nCode = KEY_COLON; break; 229 // some special cases, also see saldisp.cxx 230 // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000 231 // These can be found in ap_keysym.h 232 case 0x1000FF02: // apXK_Copy 233 nCode = KEY_COPY; 234 break; 235 case 0x1000FF03: // apXK_Cut 236 nCode = KEY_CUT; 237 break; 238 case 0x1000FF04: // apXK_Paste 239 nCode = KEY_PASTE; 240 break; 241 case 0x1000FF14: // apXK_Repeat 242 nCode = KEY_REPEAT; 243 break; 244 // Exit, Save 245 // - - - - - - - - - - - - - - D E C - - - - - - - - - - - - - 0x1000 246 // These can be found in DECkeysym.h 247 case 0x1000FF00: 248 nCode = KEY_DELETE; 249 break; 250 // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000 251 // These can be found in HPkeysym.h 252 case 0x1000FF73: // hpXK_DeleteChar 253 nCode = KEY_DELETE; 254 break; 255 case 0x1000FF74: // hpXK_BackTab 256 case 0x1000FF75: // hpXK_KP_BackTab 257 nCode = KEY_TAB; 258 break; 259 // - - - - - - - - - - - - - - I B M - - - - - - - - - - - - - 260 // - - - - - - - - - - - - - - O S F - - - - - - - - - - - - - 0x1004 261 // These also can be found in HPkeysym.h 262 case 0x1004FF02: // osfXK_Copy 263 nCode = KEY_COPY; 264 break; 265 case 0x1004FF03: // osfXK_Cut 266 nCode = KEY_CUT; 267 break; 268 case 0x1004FF04: // osfXK_Paste 269 nCode = KEY_PASTE; 270 break; 271 case 0x1004FF07: // osfXK_BackTab 272 nCode = KEY_TAB; 273 break; 274 case 0x1004FF08: // osfXK_BackSpace 275 nCode = KEY_BACKSPACE; 276 break; 277 case 0x1004FF1B: // osfXK_Escape 278 nCode = KEY_ESCAPE; 279 break; 280 // Up, Down, Left, Right, PageUp, PageDown 281 // - - - - - - - - - - - - - - S C O - - - - - - - - - - - - - 282 // - - - - - - - - - - - - - - S G I - - - - - - - - - - - - - 0x1007 283 // - - - - - - - - - - - - - - S N I - - - - - - - - - - - - - 284 // - - - - - - - - - - - - - - S U N - - - - - - - - - - - - - 0x1005 285 // These can be found in Sunkeysym.h 286 case 0x1005FF10: // SunXK_F36 287 nCode = KEY_F11; 288 break; 289 case 0x1005FF11: // SunXK_F37 290 nCode = KEY_F12; 291 break; 292 case 0x1005FF70: // SunXK_Props 293 nCode = KEY_PROPERTIES; 294 break; 295 case 0x1005FF71: // SunXK_Front 296 nCode = KEY_FRONT; 297 break; 298 case 0x1005FF72: // SunXK_Copy 299 nCode = KEY_COPY; 300 break; 301 case 0x1005FF73: // SunXK_Open 302 nCode = KEY_OPEN; 303 break; 304 case 0x1005FF74: // SunXK_Paste 305 nCode = KEY_PASTE; 306 break; 307 case 0x1005FF75: // SunXK_Cut 308 nCode = KEY_CUT; 309 break; 310 // - - - - - - - - - - - - - X F 8 6 - - - - - - - - - - - - - 0x1008 311 // These can be found in XF86keysym.h 312 // but more importantly they are also available GTK/Gdk version 3 313 // and hence are already provided in gdk/gdkkeysyms.h, and hence 314 // in gdk/gdk.h 315 case GDK_KEY_Copy: nCode = KEY_COPY; break; // 0x1008ff57 316 case GDK_KEY_Cut: nCode = KEY_CUT; break; // 0x1008ff58 317 case GDK_KEY_Open: nCode = KEY_OPEN; break; // 0x1008ff6b 318 case GDK_KEY_Paste: nCode = KEY_PASTE; break; // 0x1008ff6d 319 } 320 } 321 322 return nCode; 323 } 324 325 #if !GTK_CHECK_VERSION(4, 0, 0) 326 guint GtkSalFrame::GetKeyValFor(GdkKeymap* pKeyMap, guint16 hardware_keycode, guint8 group) 327 { 328 guint updated_keyval = 0; 329 gdk_keymap_translate_keyboard_state(pKeyMap, hardware_keycode, 330 GdkModifierType(0), group, &updated_keyval, nullptr, nullptr, nullptr); 331 return updated_keyval; 332 } 333 #endif 334 335 namespace { 336 337 // F10 means either KEY_F10 or KEY_MENU, which has to be decided 338 // in the independent part. 339 struct KeyAlternate 340 { 341 sal_uInt16 nKeyCode; 342 sal_Unicode nCharCode; 343 KeyAlternate() : nKeyCode( 0 ), nCharCode( 0 ) {} 344 KeyAlternate( sal_uInt16 nKey, sal_Unicode nChar = 0 ) : nKeyCode( nKey ), nCharCode( nChar ) {} 345 }; 346 347 } 348 349 static KeyAlternate 350 GetAlternateKeyCode( const sal_uInt16 nKeyCode ) 351 { 352 KeyAlternate aAlternate; 353 354 switch( nKeyCode ) 355 { 356 case KEY_F10: aAlternate = KeyAlternate( KEY_MENU );break; 357 case KEY_F24: aAlternate = KeyAlternate( KEY_SUBTRACT, '-' );break; 358 } 359 360 return aAlternate; 361 } 362 363 #if OSL_DEBUG_LEVEL > 0 364 static bool dumpframes = false; 365 #endif 366 367 bool GtkSalFrame::doKeyCallback( guint state, 368 guint keyval, 369 guint16 hardware_keycode, 370 guint8 group, 371 sal_Unicode aOrigCode, 372 bool bDown, 373 bool bSendRelease 374 ) 375 { 376 SalKeyEvent aEvent; 377 378 aEvent.mnCharCode = aOrigCode; 379 aEvent.mnRepeat = 0; 380 381 vcl::DeletionListener aDel( this ); 382 383 #if OSL_DEBUG_LEVEL > 0 384 const char* pKeyDebug = getenv("VCL_GTK3_PAINTDEBUG"); 385 386 if (pKeyDebug && *pKeyDebug == '1') 387 { 388 if (bDown) 389 { 390 // shift-zero forces a re-draw and event is swallowed 391 if (keyval == GDK_KEY_0) 392 { 393 SAL_INFO("vcl.gtk3", "force widget_queue_draw."); 394 gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea)); 395 return false; 396 } 397 else if (keyval == GDK_KEY_1) 398 { 399 SAL_INFO("vcl.gtk3", "force repaint all."); 400 TriggerPaintEvent(); 401 return false; 402 } 403 else if (keyval == GDK_KEY_2) 404 { 405 dumpframes = !dumpframes; 406 SAL_INFO("vcl.gtk3", "toggle dump frames to " << dumpframes); 407 return false; 408 } 409 } 410 } 411 #endif 412 413 /* 414 * #i42122# translate all keys with Ctrl and/or Alt to group 0 else 415 * shortcuts (e.g. Ctrl-o) will not work but be inserted by the 416 * application 417 * 418 * #i52338# do this for all keys that the independent part has no key code 419 * for 420 * 421 * fdo#41169 rather than use group 0, detect if there is a group which can 422 * be used to input Latin text and use that if possible 423 */ 424 aEvent.mnCode = GetKeyCode( keyval ); 425 #if !GTK_CHECK_VERSION(4, 0, 0) 426 if( aEvent.mnCode == 0 ) 427 { 428 gint best_group = SAL_MAX_INT32; 429 430 // Try and find Latin layout 431 GdkKeymap* keymap = gdk_keymap_get_default(); 432 GdkKeymapKey *keys; 433 gint n_keys; 434 if (gdk_keymap_get_entries_for_keyval(keymap, GDK_KEY_A, &keys, &n_keys)) 435 { 436 // Find the lowest group that supports Latin layout 437 for (gint i = 0; i < n_keys; ++i) 438 { 439 if (keys[i].level != 0 && keys[i].level != 1) 440 continue; 441 best_group = std::min(best_group, keys[i].group); 442 if (best_group == 0) 443 break; 444 } 445 g_free(keys); 446 } 447 448 //Unavailable, go with original group then I suppose 449 if (best_group == SAL_MAX_INT32) 450 best_group = group; 451 452 guint updated_keyval = GetKeyValFor(keymap, hardware_keycode, best_group); 453 aEvent.mnCode = GetKeyCode(updated_keyval); 454 } 455 #else 456 (void)hardware_keycode; 457 (void)group; 458 #endif 459 460 aEvent.mnCode |= GetKeyModCode( state ); 461 462 bool bStopProcessingKey; 463 if (bDown) 464 { 465 // tdf#152404 Commit uncommitted text before dispatching key shortcuts. In 466 // certain cases such as pressing Control-Alt-C in a Writer document while 467 // there is uncommitted text will call GtkSalFrame::EndExtTextInput() which 468 // will dispatch a SalEvent::EndExtTextInput event. Writer's handler for that 469 // event will delete the uncommitted text and then insert the committed text 470 // but LibreOffice will crash when deleting the uncommitted text because 471 // deletion of the text also removes and deletes the newly inserted comment. 472 if (m_pIMHandler && !m_pIMHandler->m_aInputEvent.maText.isEmpty() && (aEvent.mnCode & (KEY_MOD1 | KEY_MOD2))) 473 m_pIMHandler->doCallEndExtTextInput(); 474 475 bStopProcessingKey = CallCallbackExc(SalEvent::KeyInput, &aEvent); 476 // #i46889# copy AlternateKeyCode handling from generic plugin 477 if (!bStopProcessingKey) 478 { 479 KeyAlternate aAlternate = GetAlternateKeyCode( aEvent.mnCode ); 480 if( aAlternate.nKeyCode ) 481 { 482 aEvent.mnCode = aAlternate.nKeyCode; 483 if( aAlternate.nCharCode ) 484 aEvent.mnCharCode = aAlternate.nCharCode; 485 bStopProcessingKey = CallCallbackExc(SalEvent::KeyInput, &aEvent); 486 } 487 } 488 if( bSendRelease && ! aDel.isDeleted() ) 489 { 490 CallCallbackExc(SalEvent::KeyUp, &aEvent); 491 } 492 } 493 else 494 bStopProcessingKey = CallCallbackExc(SalEvent::KeyUp, &aEvent); 495 return bStopProcessingKey; 496 } 497 498 GtkSalFrame::GtkSalFrame( SalFrame* pParent, SalFrameStyleFlags nStyle ) 499 : m_nXScreen( getDisplay()->GetDefaultXScreen() ) 500 , m_pHeaderBar(nullptr) 501 , m_bGraphics(false) 502 , m_nSetFocusSignalId(0) 503 #if !GTK_CHECK_VERSION(4, 0, 0) 504 , m_aSmoothScrollIdle("GtkSalFrame m_aSmoothScrollIdle") 505 #endif 506 { 507 getDisplay()->registerFrame( this ); 508 m_bDefaultPos = true; 509 m_bDefaultSize = ( (nStyle & SalFrameStyleFlags::SIZEABLE) && ! pParent ); 510 Init( pParent, nStyle ); 511 } 512 513 GtkSalFrame::GtkSalFrame( SystemParentData* pSysData ) 514 : m_nXScreen( getDisplay()->GetDefaultXScreen() ) 515 , m_pHeaderBar(nullptr) 516 , m_bGraphics(false) 517 , m_nSetFocusSignalId(0) 518 #if !GTK_CHECK_VERSION(4, 0, 0) 519 , m_aSmoothScrollIdle("GtkSalFrame m_aSmoothScrollIdle") 520 #endif 521 { 522 getDisplay()->registerFrame( this ); 523 // permanently ignore errors from our unruly children ... 524 GetGenericUnixSalData()->ErrorTrapPush(); 525 m_bDefaultPos = true; 526 m_bDefaultSize = true; 527 Init( pSysData ); 528 } 529 530 // AppMenu watch functions. 531 532 static void ObjectDestroyedNotify( gpointer data ) 533 { 534 if ( data ) { 535 g_object_unref( data ); 536 } 537 } 538 539 #if !GTK_CHECK_VERSION(4,0,0) 540 static void hud_activated( gboolean hud_active, gpointer user_data ) 541 { 542 if ( hud_active ) 543 { 544 SolarMutexGuard aGuard; 545 GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data ); 546 GtkSalMenu* pSalMenu = reinterpret_cast< GtkSalMenu* >( pSalFrame->GetMenu() ); 547 548 if ( pSalMenu ) 549 pSalMenu->UpdateFull(); 550 } 551 } 552 #endif 553 554 static void attach_menu_model(GtkSalFrame* pSalFrame) 555 { 556 GtkWidget* pWidget = pSalFrame->getWindow(); 557 GdkSurface* gdkWindow = widget_get_surface(pWidget); 558 559 if ( gdkWindow == nullptr || g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) != nullptr ) 560 return; 561 562 // Create menu model and action group attached to this frame. 563 GMenuModel* pMenuModel = G_MENU_MODEL( g_lo_menu_new() ); 564 GActionGroup* pActionGroup = reinterpret_cast<GActionGroup*>(g_lo_action_group_new()); 565 566 // Set window properties. 567 g_object_set_data_full( G_OBJECT( gdkWindow ), "g-lo-menubar", pMenuModel, ObjectDestroyedNotify ); 568 g_object_set_data_full( G_OBJECT( gdkWindow ), "g-lo-action-group", pActionGroup, ObjectDestroyedNotify ); 569 570 #if !GTK_CHECK_VERSION(4,0,0) 571 // Get a DBus session connection. 572 EnsureSessionBus(); 573 if (!pSessionBus) 574 return; 575 576 // Generate menu paths. 577 sal_uIntPtr windowId = GtkSalFrame::GetNativeWindowHandle(pWidget); 578 gchar* aDBusWindowPath = g_strdup_printf( "/org/libreoffice/window/%lu", windowId ); 579 gchar* aDBusMenubarPath = g_strdup_printf( "/org/libreoffice/window/%lu/menus/menubar", windowId ); 580 581 GdkDisplay *pDisplay = GtkSalFrame::getGdkDisplay(); 582 #if defined(GDK_WINDOWING_X11) 583 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay)) 584 { 585 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_ID", "org.libreoffice" ); 586 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_MENUBAR_OBJECT_PATH", aDBusMenubarPath ); 587 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_WINDOW_OBJECT_PATH", aDBusWindowPath ); 588 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_OBJECT_PATH", "/org/libreoffice" ); 589 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_UNIQUE_BUS_NAME", g_dbus_connection_get_unique_name( pSessionBus ) ); 590 } 591 #endif 592 #if defined(GDK_WINDOWING_WAYLAND) 593 if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay)) 594 { 595 gdk_wayland_window_set_dbus_properties_libgtk_only(gdkWindow, "org.libreoffice", 596 nullptr, 597 aDBusMenubarPath, 598 aDBusWindowPath, 599 "/org/libreoffice", 600 g_dbus_connection_get_unique_name( pSessionBus )); 601 } 602 #endif 603 // Publish the menu model and the action group. 604 SAL_INFO("vcl.unity", "exporting menu model at " << pMenuModel << " for window " << windowId); 605 pSalFrame->m_nMenuExportId = g_dbus_connection_export_menu_model (pSessionBus, aDBusMenubarPath, pMenuModel, nullptr); 606 SAL_INFO("vcl.unity", "exporting action group at " << pActionGroup << " for window " << windowId); 607 pSalFrame->m_nActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, aDBusWindowPath, pActionGroup, nullptr); 608 pSalFrame->m_nHudAwarenessId = hud_awareness_register( pSessionBus, aDBusMenubarPath, hud_activated, pSalFrame, nullptr, nullptr ); 609 610 g_free( aDBusWindowPath ); 611 g_free( aDBusMenubarPath ); 612 #endif 613 } 614 615 void on_registrar_available( GDBusConnection * /*connection*/, 616 const gchar * /*name*/, 617 const gchar * /*name_owner*/, 618 gpointer user_data ) 619 { 620 SolarMutexGuard aGuard; 621 622 GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data ); 623 624 SalMenu* pSalMenu = pSalFrame->GetMenu(); 625 626 if ( pSalMenu != nullptr ) 627 { 628 GtkSalMenu* pGtkSalMenu = static_cast<GtkSalMenu*>(pSalMenu); 629 pGtkSalMenu->EnableUnity(true); 630 } 631 } 632 633 // This is called when the registrar becomes unavailable. It shows the menubar. 634 void on_registrar_unavailable( GDBusConnection * /*connection*/, 635 const gchar * /*name*/, 636 gpointer user_data ) 637 { 638 SolarMutexGuard aGuard; 639 640 SAL_INFO("vcl.unity", "on_registrar_unavailable"); 641 642 GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data ); 643 644 SalMenu* pSalMenu = pSalFrame->GetMenu(); 645 646 if ( pSalMenu ) { 647 GtkSalMenu* pGtkSalMenu = static_cast< GtkSalMenu* >( pSalMenu ); 648 pGtkSalMenu->EnableUnity(false); 649 } 650 } 651 652 void GtkSalFrame::EnsureAppMenuWatch() 653 { 654 if ( m_nWatcherId ) 655 return; 656 657 // Get a DBus session connection. 658 EnsureSessionBus(); 659 if (!pSessionBus) 660 return; 661 662 // Publish the menu only if AppMenu registrar is available. 663 m_nWatcherId = g_bus_watch_name_on_connection( pSessionBus, 664 "com.canonical.AppMenu.Registrar", 665 G_BUS_NAME_WATCHER_FLAGS_NONE, 666 on_registrar_available, 667 on_registrar_unavailable, 668 this, 669 nullptr ); 670 } 671 672 void GtkSalFrame::InvalidateGraphics() 673 { 674 if( m_pGraphics ) 675 { 676 m_bGraphics = false; 677 } 678 } 679 680 GtkSalFrame::~GtkSalFrame() 681 { 682 #if !GTK_CHECK_VERSION(4,0,0) 683 m_aSmoothScrollIdle.Stop(); 684 m_aSmoothScrollIdle.ClearInvokeHandler(); 685 #endif 686 687 if (m_pDropTarget) 688 { 689 m_pDropTarget->deinitialize(); 690 m_pDropTarget = nullptr; 691 } 692 693 if (m_pDragSource) 694 { 695 m_pDragSource->deinitialize(); 696 m_pDragSource= nullptr; 697 } 698 699 InvalidateGraphics(); 700 701 if (m_pParent) 702 { 703 m_pParent->m_aChildren.remove( this ); 704 } 705 706 getDisplay()->deregisterFrame( this ); 707 708 if( m_pRegion ) 709 { 710 cairo_region_destroy( m_pRegion ); 711 } 712 713 m_pIMHandler.reset(); 714 715 //tdf#108705 remove grabs on event widget before 716 //destroying event widget 717 while (m_nGrabLevel) 718 removeGrabLevel(); 719 720 { 721 SolarMutexGuard aGuard; 722 723 if (m_nWatcherId) 724 g_bus_unwatch_name(m_nWatcherId); 725 726 if (m_nPortalSettingChangedSignalId) 727 g_signal_handler_disconnect(m_pSettingsPortal, m_nPortalSettingChangedSignalId); 728 729 if (m_pSettingsPortal) 730 g_object_unref(m_pSettingsPortal); 731 732 if (m_nSessionClientSignalId) 733 g_signal_handler_disconnect(m_pSessionClient, m_nSessionClientSignalId); 734 735 if (m_pSessionClient) 736 g_object_unref(m_pSessionClient); 737 738 if (m_pSessionManager) 739 g_object_unref(m_pSessionManager); 740 } 741 742 GtkWidget *pEventWidget = getMouseEventWidget(); 743 for (auto handler_id : m_aMouseSignalIds) 744 g_signal_handler_disconnect(G_OBJECT(pEventWidget), handler_id); 745 746 #if !GTK_CHECK_VERSION(4, 0, 0) 747 if( m_pFixedContainer ) 748 gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer ) ); 749 if( m_pEventBox ) 750 gtk_widget_destroy( GTK_WIDGET(m_pEventBox) ); 751 if( m_pTopLevelGrid ) 752 gtk_widget_destroy( GTK_WIDGET(m_pTopLevelGrid) ); 753 #else 754 g_signal_handler_disconnect(G_OBJECT(gtk_widget_get_display(pEventWidget)), m_nSettingChangedSignalId); 755 #endif 756 { 757 SolarMutexGuard aGuard; 758 759 if( m_pWindow ) 760 { 761 g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", nullptr ); 762 763 if ( pSessionBus ) 764 { 765 if ( m_nHudAwarenessId ) 766 hud_awareness_unregister( pSessionBus, m_nHudAwarenessId ); 767 if ( m_nMenuExportId ) 768 g_dbus_connection_unexport_menu_model( pSessionBus, m_nMenuExportId ); 769 if ( m_nActionGroupExportId ) 770 g_dbus_connection_unexport_action_group( pSessionBus, m_nActionGroupExportId ); 771 } 772 m_xFrameWeld.reset(); 773 #if !GTK_CHECK_VERSION(4,0,0) 774 gtk_widget_destroy( m_pWindow ); 775 #else 776 if (GTK_IS_WINDOW(m_pWindow)) 777 gtk_window_destroy(GTK_WINDOW(m_pWindow)); 778 else 779 g_clear_pointer(&m_pWindow, gtk_widget_unparent); 780 #endif 781 } 782 } 783 784 #if !GTK_CHECK_VERSION(4,0,0) 785 if( m_pForeignParent ) 786 g_object_unref( G_OBJECT( m_pForeignParent ) ); 787 if( m_pForeignTopLevel ) 788 g_object_unref( G_OBJECT( m_pForeignTopLevel) ); 789 #endif 790 791 m_pGraphics.reset(); 792 793 if (m_pSurface) 794 cairo_surface_destroy(m_pSurface); 795 } 796 797 void GtkSalFrame::moveWindow( tools::Long nX, tools::Long nY ) 798 { 799 if( isChild( false ) ) 800 { 801 GtkWidget* pParent = m_pParent ? gtk_widget_get_parent(m_pWindow) : nullptr; 802 // tdf#130414 it's possible that we were reparented and are no longer inside 803 // our original GtkFixed parent 804 if (pParent && GTK_IS_FIXED(pParent)) 805 { 806 gtk_fixed_move( GTK_FIXED(pParent), 807 m_pWindow, 808 nX - m_pParent->maGeometry.x(), nY - m_pParent->maGeometry.y() ); 809 } 810 return; 811 } 812 #if GTK_CHECK_VERSION(4,0,0) 813 if (GTK_IS_POPOVER(m_pWindow)) 814 { 815 GdkRectangle aRect; 816 aRect.x = nX; 817 aRect.y = nY; 818 aRect.width = 1; 819 aRect.height = 1; 820 gtk_popover_set_pointing_to(GTK_POPOVER(m_pWindow), &aRect); 821 return; 822 } 823 #else 824 gtk_window_move( GTK_WINDOW(m_pWindow), nX, nY ); 825 #endif 826 } 827 828 void GtkSalFrame::widget_set_size_request(tools::Long nWidth, tools::Long nHeight) 829 { 830 gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer), nWidth, nHeight ); 831 #if GTK_CHECK_VERSION(4,0,0) 832 gtk_widget_set_size_request(GTK_WIDGET(m_pDrawingArea), nWidth, nHeight ); 833 #endif 834 } 835 836 void GtkSalFrame::window_resize(tools::Long nWidth, tools::Long nHeight) 837 { 838 m_nWidthRequest = nWidth; 839 m_nHeightRequest = nHeight; 840 if (!GTK_IS_WINDOW(m_pWindow)) 841 { 842 #if GTK_CHECK_VERSION(4,0,0) 843 gtk_widget_set_size_request(GTK_WIDGET(m_pDrawingArea), nWidth, nHeight); 844 #endif 845 return; 846 } 847 gtk_window_set_default_size(GTK_WINDOW(m_pWindow), nWidth, nHeight); 848 #if !GTK_CHECK_VERSION(4,0,0) 849 gtk_window_resize(GTK_WINDOW(m_pWindow), nWidth, nHeight); 850 #endif 851 } 852 853 void GtkSalFrame::resizeWindow( tools::Long nWidth, tools::Long nHeight ) 854 { 855 if( isChild( false ) ) 856 { 857 widget_set_size_request(nWidth, nHeight); 858 } 859 else if( ! isChild( true, false ) ) 860 window_resize(nWidth, nHeight); 861 } 862 863 #if !GTK_CHECK_VERSION(4,0,0) 864 // tdf#124694 GtkFixed takes the max size of all its children as its 865 // preferred size, causing it to not clip its child, but grow instead. 866 867 static void 868 ooo_fixed_get_preferred_height(GtkWidget*, gint *minimum, gint *natural) 869 { 870 *minimum = 0; 871 *natural = 0; 872 } 873 874 static void 875 ooo_fixed_get_preferred_width(GtkWidget*, gint *minimum, gint *natural) 876 { 877 *minimum = 0; 878 *natural = 0; 879 } 880 881 static void 882 ooo_fixed_class_init(gpointer klass_, gpointer) 883 { 884 auto const klass = static_cast<GtkFixedClass *>(klass_); 885 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); 886 widget_class->get_accessible = ooo_fixed_get_accessible; 887 widget_class->get_preferred_height = ooo_fixed_get_preferred_height; 888 widget_class->get_preferred_width = ooo_fixed_get_preferred_width; 889 } 890 891 /* 892 * Always use a sub-class of GtkFixed we can tag for a11y. This allows us to 893 * utilize GAIL for the toplevel window and toolkit implementation incl. 894 * key event listener support .. 895 */ 896 897 GType 898 ooo_fixed_get_type() 899 { 900 static GType type = 0; 901 902 if (!type) { 903 static const GTypeInfo tinfo = 904 { 905 sizeof (GtkFixedClass), 906 nullptr, /* base init */ 907 nullptr, /* base finalize */ 908 ooo_fixed_class_init, /* class init */ 909 nullptr, /* class finalize */ 910 nullptr, /* class data */ 911 sizeof (GtkFixed), /* instance size */ 912 0, /* nb preallocs */ 913 nullptr, /* instance init */ 914 nullptr /* value table */ 915 }; 916 917 type = g_type_register_static( GTK_TYPE_FIXED, "OOoFixed", 918 &tinfo, GTypeFlags(0)); 919 } 920 921 return type; 922 } 923 924 #endif 925 926 void GtkSalFrame::updateScreenNumber() 927 { 928 #if !GTK_CHECK_VERSION(4,0,0) 929 int nScreen = 0; 930 GdkScreen *pScreen = gtk_widget_get_screen( m_pWindow ); 931 if( pScreen ) 932 nScreen = getDisplay()->getSystem()->getScreenMonitorIdx( pScreen, maGeometry.x(), maGeometry.y() ); 933 maGeometry.setScreen(nScreen); 934 #endif 935 } 936 937 GtkWidget *GtkSalFrame::getMouseEventWidget() const 938 { 939 #if !GTK_CHECK_VERSION(4,0,0) 940 return GTK_WIDGET(m_pEventBox); 941 #else 942 return GTK_WIDGET(m_pFixedContainer); 943 #endif 944 } 945 946 static void damaged(void *handle, 947 sal_Int32 nExtentsX, sal_Int32 nExtentsY, 948 sal_Int32 nExtentsWidth, sal_Int32 nExtentsHeight) 949 { 950 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(handle); 951 pThis->damaged(nExtentsX, nExtentsY, nExtentsWidth, nExtentsHeight); 952 } 953 954 #if !GTK_CHECK_VERSION(4,0,0) 955 static void notifyUnref(gpointer data, GObject *) { g_object_unref(data); } 956 #endif 957 958 void GtkSalFrame::InitCommon() 959 { 960 m_pSurface = nullptr; 961 m_nGrabLevel = 0; 962 m_bSalObjectSetPosSize = false; 963 m_nPortalSettingChangedSignalId = 0; 964 m_nSessionClientSignalId = 0; 965 m_pSettingsPortal = nullptr; 966 m_pSessionManager = nullptr; 967 m_pSessionClient = nullptr; 968 969 m_aDamageHandler.handle = this; 970 m_aDamageHandler.damaged = ::damaged; 971 972 #if !GTK_CHECK_VERSION(4,0,0) 973 m_aSmoothScrollIdle.SetInvokeHandler(LINK(this, GtkSalFrame, AsyncScroll)); 974 #endif 975 976 m_pTopLevelGrid = GTK_GRID(gtk_grid_new()); 977 container_add(m_pWindow, GTK_WIDGET(m_pTopLevelGrid)); 978 979 #if !GTK_CHECK_VERSION(4,0,0) 980 m_pEventBox = GTK_EVENT_BOX(gtk_event_box_new()); 981 gtk_widget_add_events( GTK_WIDGET(m_pEventBox), 982 GDK_ALL_EVENTS_MASK ); 983 gtk_widget_set_vexpand(GTK_WIDGET(m_pEventBox), true); 984 gtk_widget_set_hexpand(GTK_WIDGET(m_pEventBox), true); 985 gtk_grid_attach(m_pTopLevelGrid, GTK_WIDGET(m_pEventBox), 0, 0, 1, 1); 986 #endif 987 988 // add the fixed container child, 989 // fixed is needed since we have to position plugin windows 990 #if !GTK_CHECK_VERSION(4,0,0) 991 m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), nullptr )); 992 m_pDrawingArea = m_pFixedContainer; 993 #else 994 m_pOverlay = GTK_OVERLAY(gtk_overlay_new()); 995 #if GTK_CHECK_VERSION(4,9,0) 996 m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), nullptr )); 997 #else 998 m_pFixedContainer = GTK_FIXED(gtk_fixed_new()); 999 #endif 1000 m_pDrawingArea = GTK_DRAWING_AREA(gtk_drawing_area_new()); 1001 #endif 1002 if (GTK_IS_WINDOW(m_pWindow)) 1003 { 1004 Size aDefWindowSize = calcDefaultSize(); 1005 gtk_window_set_default_size(GTK_WINDOW(m_pWindow), aDefWindowSize.Width(), aDefWindowSize.Height()); 1006 } 1007 gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), true); 1008 gtk_widget_set_size_request(GTK_WIDGET(m_pFixedContainer), 1, 1); 1009 #if !GTK_CHECK_VERSION(4,0,0) 1010 gtk_container_add( GTK_CONTAINER(m_pEventBox), GTK_WIDGET(m_pFixedContainer) ); 1011 #else 1012 gtk_widget_set_vexpand(GTK_WIDGET(m_pOverlay), true); 1013 gtk_widget_set_hexpand(GTK_WIDGET(m_pOverlay), true); 1014 gtk_grid_attach(m_pTopLevelGrid, GTK_WIDGET(m_pOverlay), 0, 0, 1, 1); 1015 gtk_overlay_set_child(m_pOverlay, GTK_WIDGET(m_pDrawingArea)); 1016 gtk_overlay_add_overlay(m_pOverlay, GTK_WIDGET(m_pFixedContainer)); 1017 #endif 1018 1019 GtkWidget *pEventWidget = getMouseEventWidget(); 1020 #if !GTK_CHECK_VERSION(4,0,0) 1021 gtk_widget_set_app_paintable(GTK_WIDGET(m_pFixedContainer), true); 1022 gtk_widget_set_redraw_on_allocate(GTK_WIDGET(m_pFixedContainer), false); 1023 #endif 1024 1025 #if GTK_CHECK_VERSION(4,0,0) 1026 m_nSettingChangedSignalId = g_signal_connect(G_OBJECT(gtk_widget_get_display(pEventWidget)), "setting-changed", G_CALLBACK(signalStyleUpdated), this); 1027 #else 1028 // use pEventWidget instead of m_pWindow to avoid infinite event loop under Linux Mint Mate 18.3 1029 g_signal_connect(G_OBJECT(pEventWidget), "style-updated", G_CALLBACK(signalStyleUpdated), this); 1030 #endif 1031 gtk_widget_set_has_tooltip(pEventWidget, true); 1032 // connect signals 1033 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "query-tooltip", G_CALLBACK(signalTooltipQuery), this )); 1034 #if !GTK_CHECK_VERSION(4,0,0) 1035 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-press-event", G_CALLBACK(signalButton), this )); 1036 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-release-event", G_CALLBACK(signalButton), this )); 1037 1038 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "motion-notify-event", G_CALLBACK(signalMotion), this )); 1039 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "leave-notify-event", G_CALLBACK(signalCrossing), this )); 1040 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "enter-notify-event", G_CALLBACK(signalCrossing), this )); 1041 1042 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "scroll-event", G_CALLBACK(signalScroll), this )); 1043 #else 1044 GtkGesture *pClick = gtk_gesture_click_new(); 1045 gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(pClick), 0); 1046 // use GTK_PHASE_TARGET instead of default GTK_PHASE_BUBBLE because I don't 1047 // want click events in gtk widgets inside the overlay, e.g. the font size 1048 // combobox GtkEntry in the toolbar, to be propagated down to this widget 1049 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(pClick), GTK_PHASE_TARGET); 1050 gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pClick)); 1051 g_signal_connect(pClick, "pressed", G_CALLBACK(gesturePressed), this); 1052 g_signal_connect(pClick, "released", G_CALLBACK(gestureReleased), this); 1053 1054 GtkEventController* pMotionController = gtk_event_controller_motion_new(); 1055 g_signal_connect(pMotionController, "motion", G_CALLBACK(signalMotion), this); 1056 g_signal_connect(pMotionController, "enter", G_CALLBACK(signalEnter), this); 1057 g_signal_connect(pMotionController, "leave", G_CALLBACK(signalLeave), this); 1058 gtk_widget_add_controller(pEventWidget, pMotionController); 1059 1060 GtkEventController* pScrollController = gtk_event_controller_scroll_new(GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES); 1061 g_signal_connect(pScrollController, "scroll", G_CALLBACK(signalScroll), this); 1062 gtk_widget_add_controller(pEventWidget, pScrollController); 1063 #endif 1064 1065 #if GTK_CHECK_VERSION(4,0,0) 1066 GtkGesture* pZoomGesture = gtk_gesture_zoom_new(); 1067 gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pZoomGesture)); 1068 #else 1069 GtkGesture* pZoomGesture = gtk_gesture_zoom_new(GTK_WIDGET(pEventWidget)); 1070 g_object_weak_ref(G_OBJECT(pEventWidget), notifyUnref, pZoomGesture); 1071 #endif 1072 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(pZoomGesture), 1073 GTK_PHASE_TARGET); 1074 // Note that the default zoom gesture signal handler needs to run first to setup correct 1075 // scale delta. Otherwise the first "begin" event will always contain scale delta of infinity. 1076 g_signal_connect_after(pZoomGesture, "begin", G_CALLBACK(signalZoomBegin), this); 1077 g_signal_connect_after(pZoomGesture, "update", G_CALLBACK(signalZoomUpdate), this); 1078 g_signal_connect_after(pZoomGesture, "end", G_CALLBACK(signalZoomEnd), this); 1079 1080 #if GTK_CHECK_VERSION(4,0,0) 1081 GtkGesture* pRotateGesture = gtk_gesture_rotate_new(); 1082 gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pRotateGesture)); 1083 #else 1084 GtkGesture* pRotateGesture = gtk_gesture_rotate_new(GTK_WIDGET(pEventWidget)); 1085 g_object_weak_ref(G_OBJECT(pEventWidget), notifyUnref, pRotateGesture); 1086 #endif 1087 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER(pRotateGesture), 1088 GTK_PHASE_TARGET); 1089 g_signal_connect(pRotateGesture, "begin", G_CALLBACK(signalRotateBegin), this); 1090 g_signal_connect(pRotateGesture, "update", G_CALLBACK(signalRotateUpdate), this); 1091 g_signal_connect(pRotateGesture, "end", G_CALLBACK(signalRotateEnd), this); 1092 1093 //Drop Target Stuff 1094 #if GTK_CHECK_VERSION(4,0,0) 1095 GtkDropTargetAsync* pDropTarget = gtk_drop_target_async_new(nullptr, GdkDragAction(GDK_ACTION_ALL)); 1096 g_signal_connect(G_OBJECT(pDropTarget), "drag-enter", G_CALLBACK(signalDragMotion), this); 1097 g_signal_connect(G_OBJECT(pDropTarget), "drag-motion", G_CALLBACK(signalDragMotion), this); 1098 g_signal_connect(G_OBJECT(pDropTarget), "drag-leave", G_CALLBACK(signalDragLeave), this); 1099 g_signal_connect(G_OBJECT(pDropTarget), "drop", G_CALLBACK(signalDragDrop), this); 1100 gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pDropTarget)); 1101 #else 1102 gtk_drag_dest_set(GTK_WIDGET(pEventWidget), GtkDestDefaults(0), nullptr, 0, GdkDragAction(0)); 1103 gtk_drag_dest_set_track_motion(GTK_WIDGET(pEventWidget), true); 1104 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-motion", G_CALLBACK(signalDragMotion), this )); 1105 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-drop", G_CALLBACK(signalDragDrop), this )); 1106 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-data-received", G_CALLBACK(signalDragDropReceived), this )); 1107 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-leave", G_CALLBACK(signalDragLeave), this )); 1108 #endif 1109 1110 #if !GTK_CHECK_VERSION(4,0,0) 1111 //Drag Source Stuff 1112 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-end", G_CALLBACK(signalDragEnd), this )); 1113 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-failed", G_CALLBACK(signalDragFailed), this )); 1114 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-data-delete", G_CALLBACK(signalDragDelete), this )); 1115 m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-data-get", G_CALLBACK(signalDragDataGet), this )); 1116 #endif 1117 1118 #if !GTK_CHECK_VERSION(4,0,0) 1119 g_signal_connect( G_OBJECT(m_pFixedContainer), "draw", G_CALLBACK(signalDraw), this ); 1120 g_signal_connect( G_OBJECT(m_pFixedContainer), "size-allocate", G_CALLBACK(sizeAllocated), this ); 1121 #else 1122 gtk_drawing_area_set_draw_func(m_pDrawingArea, signalDraw, this, nullptr); 1123 g_signal_connect(G_OBJECT(m_pDrawingArea), "resize", G_CALLBACK(sizeAllocated), this); 1124 #endif 1125 1126 g_signal_connect(G_OBJECT(m_pFixedContainer), "realize", G_CALLBACK(signalRealize), this); 1127 1128 #if !GTK_CHECK_VERSION(4,0,0) 1129 GtkGesture *pSwipe = gtk_gesture_swipe_new(pEventWidget); 1130 g_object_weak_ref(G_OBJECT(pEventWidget), notifyUnref, pSwipe); 1131 #else 1132 GtkGesture *pSwipe = gtk_gesture_swipe_new(); 1133 gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pSwipe)); 1134 #endif 1135 g_signal_connect(pSwipe, "swipe", G_CALLBACK(gestureSwipe), this); 1136 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pSwipe), GTK_PHASE_TARGET); 1137 1138 #if !GTK_CHECK_VERSION(4,0,0) 1139 GtkGesture *pLongPress = gtk_gesture_long_press_new(pEventWidget); 1140 g_object_weak_ref(G_OBJECT(pEventWidget), notifyUnref, pLongPress); 1141 #else 1142 GtkGesture *pLongPress = gtk_gesture_long_press_new(); 1143 gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(pLongPress)); 1144 #endif 1145 g_signal_connect(pLongPress, "pressed", G_CALLBACK(gestureLongPress), this); 1146 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pLongPress), GTK_PHASE_TARGET); 1147 1148 #if !GTK_CHECK_VERSION(4,0,0) 1149 g_signal_connect_after( G_OBJECT(m_pWindow), "focus-in-event", G_CALLBACK(signalFocus), this ); 1150 g_signal_connect_after( G_OBJECT(m_pWindow), "focus-out-event", G_CALLBACK(signalFocus), this ); 1151 if (GTK_IS_WINDOW(m_pWindow)) // i.e. not if it's a GtkEventBox which doesn't have the signal 1152 m_nSetFocusSignalId = g_signal_connect( G_OBJECT(m_pWindow), "set-focus", G_CALLBACK(signalSetFocus), this ); 1153 #else 1154 GtkEventController* pFocusController = gtk_event_controller_focus_new(); 1155 g_signal_connect(pFocusController, "enter", G_CALLBACK(signalFocusEnter), this); 1156 g_signal_connect(pFocusController, "leave", G_CALLBACK(signalFocusLeave), this); 1157 gtk_widget_set_focusable(pEventWidget, true); 1158 gtk_widget_add_controller(pEventWidget, pFocusController); 1159 if (GTK_IS_WINDOW(m_pWindow)) // i.e. not if it's a GtkEventBox which doesn't have the property (presumably?) 1160 m_nSetFocusSignalId = g_signal_connect( G_OBJECT(m_pWindow), "notify::focus-widget", G_CALLBACK(signalSetFocus), this ); 1161 #endif 1162 #if !GTK_CHECK_VERSION(4,0,0) 1163 g_signal_connect( G_OBJECT(m_pWindow), "map-event", G_CALLBACK(signalMap), this ); 1164 g_signal_connect( G_OBJECT(m_pWindow), "unmap-event", G_CALLBACK(signalUnmap), this ); 1165 g_signal_connect( G_OBJECT(m_pWindow), "delete-event", G_CALLBACK(signalDelete), this ); 1166 #else 1167 g_signal_connect( G_OBJECT(m_pWindow), "map", G_CALLBACK(signalMap), this ); 1168 g_signal_connect( G_OBJECT(m_pWindow), "unmap", G_CALLBACK(signalUnmap), this ); 1169 if (GTK_IS_WINDOW(m_pWindow)) 1170 g_signal_connect( G_OBJECT(m_pWindow), "close-request", G_CALLBACK(signalDelete), this ); 1171 #endif 1172 #if !GTK_CHECK_VERSION(4,0,0) 1173 g_signal_connect( G_OBJECT(m_pWindow), "configure-event", G_CALLBACK(signalConfigure), this ); 1174 #endif 1175 1176 #if !GTK_CHECK_VERSION(4,0,0) 1177 g_signal_connect( G_OBJECT(m_pWindow), "key-press-event", G_CALLBACK(signalKey), this ); 1178 g_signal_connect( G_OBJECT(m_pWindow), "key-release-event", G_CALLBACK(signalKey), this ); 1179 #else 1180 m_pKeyController = GTK_EVENT_CONTROLLER_KEY(gtk_event_controller_key_new()); 1181 g_signal_connect(m_pKeyController, "key-pressed", G_CALLBACK(signalKeyPressed), this); 1182 g_signal_connect(m_pKeyController, "key-released", G_CALLBACK(signalKeyReleased), this); 1183 gtk_widget_add_controller(pEventWidget, GTK_EVENT_CONTROLLER(m_pKeyController)); 1184 1185 #endif 1186 g_signal_connect( G_OBJECT(m_pWindow), "destroy", G_CALLBACK(signalDestroy), this ); 1187 1188 // init members 1189 m_nKeyModifiers = ModKeyFlags::NONE; 1190 m_bFullscreen = false; 1191 #if GTK_CHECK_VERSION(4,0,0) 1192 m_nState = static_cast<GdkToplevelState>(0); 1193 #else 1194 m_nState = GDK_WINDOW_STATE_WITHDRAWN; 1195 #endif 1196 m_pIMHandler = nullptr; 1197 m_pRegion = nullptr; 1198 m_pDropTarget = nullptr; 1199 m_pDragSource = nullptr; 1200 m_bGeometryIsProvisional = false; 1201 m_bIconSetWhileUnmapped = false; 1202 m_bTooltipBlocked = false; 1203 m_ePointerStyle = static_cast<PointerStyle>(0xffff); 1204 m_pSalMenu = nullptr; 1205 m_nWatcherId = 0; 1206 m_nMenuExportId = 0; 1207 m_nActionGroupExportId = 0; 1208 m_nHudAwarenessId = 0; 1209 1210 #if !GTK_CHECK_VERSION(4,0,0) 1211 gtk_widget_add_events( m_pWindow, 1212 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | 1213 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | 1214 GDK_SCROLL_MASK | GDK_TOUCHPAD_GESTURE_MASK 1215 ); 1216 #endif 1217 1218 // show the widgets 1219 #if !GTK_CHECK_VERSION(4,0,0) 1220 gtk_widget_show_all(GTK_WIDGET(m_pTopLevelGrid)); 1221 #else 1222 gtk_widget_show(GTK_WIDGET(m_pTopLevelGrid)); 1223 #endif 1224 1225 // realize the window, we need an XWindow id 1226 gtk_widget_realize( m_pWindow ); 1227 1228 if (GTK_IS_WINDOW(m_pWindow)) 1229 { 1230 #if !GTK_CHECK_VERSION(4,0,0) 1231 g_signal_connect(G_OBJECT(m_pWindow), "window-state-event", G_CALLBACK(signalWindowState), this); 1232 #else 1233 GdkSurface* gdkWindow = widget_get_surface(m_pWindow); 1234 g_signal_connect(G_OBJECT(gdkWindow), "notify::state", G_CALLBACK(signalWindowState), this); 1235 #endif 1236 } 1237 1238 //system data 1239 m_aSystemData.SetWindowHandle(GetNativeWindowHandle(m_pWindow)); 1240 m_aSystemData.aShellWindow = reinterpret_cast<sal_IntPtr>(this); 1241 m_aSystemData.pSalFrame = this; 1242 m_aSystemData.pWidget = m_pWindow; 1243 m_aSystemData.nScreen = m_nXScreen.getXScreen(); 1244 m_aSystemData.toolkit = SystemEnvData::Toolkit::Gtk; 1245 1246 #if defined(GDK_WINDOWING_X11) 1247 GdkDisplay *pDisplay = getGdkDisplay(); 1248 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay)) 1249 { 1250 m_aSystemData.pDisplay = gdk_x11_display_get_xdisplay(pDisplay); 1251 m_aSystemData.platform = SystemEnvData::Platform::Xcb; 1252 #if !GTK_CHECK_VERSION(4,0,0) 1253 GdkScreen* pScreen = gtk_widget_get_screen(m_pWindow); 1254 GdkVisual* pVisual = gdk_screen_get_system_visual(pScreen); 1255 m_aSystemData.pVisual = gdk_x11_visual_get_xvisual(pVisual); 1256 #endif 1257 } 1258 #endif 1259 #if defined(GDK_WINDOWING_WAYLAND) 1260 if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay)) 1261 { 1262 m_aSystemData.pDisplay = gdk_wayland_display_get_wl_display(pDisplay); 1263 m_aSystemData.platform = SystemEnvData::Platform::Wayland; 1264 } 1265 #endif 1266 1267 m_bGraphics = false; 1268 m_pGraphics = nullptr; 1269 1270 m_nFloatFlags = FloatWinPopupFlags::NONE; 1271 m_bFloatPositioned = false; 1272 1273 m_nWidthRequest = 0; 1274 m_nHeightRequest = 0; 1275 1276 // fake an initial geometry, gets updated via configure event or SetPosSize 1277 if (m_bDefaultPos || m_bDefaultSize) 1278 { 1279 Size aDefSize = calcDefaultSize(); 1280 maGeometry.setPosSize({ -1, -1 }, aDefSize); 1281 maGeometry.setDecorations(0, 0, 0, 0); 1282 } 1283 updateScreenNumber(); 1284 1285 SetIcon(SV_ICON_ID_OFFICE); 1286 } 1287 1288 GtkSalFrame *GtkSalFrame::getFromWindow( GtkWidget *pWindow ) 1289 { 1290 return static_cast<GtkSalFrame *>(g_object_get_data( G_OBJECT( pWindow ), "SalFrame" )); 1291 } 1292 1293 void GtkSalFrame::DisallowCycleFocusOut() 1294 { 1295 if (!m_nSetFocusSignalId) 1296 return; 1297 // don't enable/disable can-focus as control enters and leaves 1298 // embedded native gtk widgets 1299 g_signal_handler_disconnect(G_OBJECT(m_pWindow), m_nSetFocusSignalId); 1300 m_nSetFocusSignalId = 0; 1301 1302 #if !GTK_CHECK_VERSION(4, 0, 0) 1303 // gtk3: set container without can-focus and focus will tab between 1304 // the native embedded widgets using the default gtk handling for 1305 // that 1306 // gtk4: no need because the native widgets are the only 1307 // thing in the overlay and the drawing widget is underneath so 1308 // the natural focus cycle is sufficient 1309 gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), false); 1310 #endif 1311 } 1312 1313 bool GtkSalFrame::IsCycleFocusOutDisallowed() const 1314 { 1315 return m_nSetFocusSignalId == 0; 1316 } 1317 1318 void GtkSalFrame::AllowCycleFocusOut() 1319 { 1320 if (m_nSetFocusSignalId) 1321 return; 1322 #if !GTK_CHECK_VERSION(4,0,0) 1323 // enable/disable can-focus as control enters and leaves 1324 // embedded native gtk widgets 1325 m_nSetFocusSignalId = g_signal_connect(G_OBJECT(m_pWindow), "set-focus", G_CALLBACK(signalSetFocus), this); 1326 #else 1327 m_nSetFocusSignalId = g_signal_connect(G_OBJECT(m_pWindow), "notify::focus-widget", G_CALLBACK(signalSetFocus), this); 1328 #endif 1329 1330 #if !GTK_CHECK_VERSION(4, 0, 0) 1331 // set container without can-focus and focus will tab between 1332 // the native embedded widgets using the default gtk handling for 1333 // that 1334 // gtk4: no need because the native widgets are the only 1335 // thing in the overlay and the drawing widget is underneath so 1336 // the natural focus cycle is sufficient 1337 gtk_widget_set_can_focus(GTK_WIDGET(m_pFixedContainer), true); 1338 #endif 1339 } 1340 1341 namespace 1342 { 1343 enum ColorScheme 1344 { 1345 DEFAULT, 1346 PREFER_DARK, 1347 PREFER_LIGHT 1348 }; 1349 1350 void ReadColorScheme(GDBusProxy* proxy, GVariant** out) 1351 { 1352 g_autoptr (GVariant) ret = 1353 g_dbus_proxy_call_sync(proxy, "Read", 1354 g_variant_new ("(ss)", "org.freedesktop.appearance", "color-scheme"), 1355 G_DBUS_CALL_FLAGS_NONE, G_MAXINT, nullptr, nullptr); 1356 if (!ret) 1357 return; 1358 1359 g_autoptr (GVariant) child = nullptr; 1360 g_variant_get(ret, "(v)", &child); 1361 g_variant_get(child, "v", out); 1362 1363 return; 1364 } 1365 } 1366 1367 void GtkSalFrame::SetColorScheme(GVariant* variant) 1368 { 1369 if (!m_pWindow) 1370 return; 1371 1372 guint32 color_scheme; 1373 1374 switch (officecfg::Office::Common::Misc::Appearance::get()) 1375 { 1376 default: 1377 case 0: // Auto 1378 { 1379 if (variant) 1380 { 1381 color_scheme = g_variant_get_uint32(variant); 1382 if (color_scheme > PREFER_LIGHT) 1383 color_scheme = DEFAULT; 1384 } 1385 else 1386 color_scheme = DEFAULT; 1387 break; 1388 } 1389 case 1: // Light 1390 color_scheme = PREFER_LIGHT; 1391 break; 1392 case 2: // Dark 1393 color_scheme = PREFER_DARK; 1394 break; 1395 } 1396 1397 bool bDarkIconTheme(color_scheme == PREFER_DARK); 1398 GtkSettings* pSettings = gtk_widget_get_settings(m_pWindow); 1399 g_object_set(pSettings, "gtk-application-prefer-dark-theme", bDarkIconTheme, nullptr); 1400 } 1401 1402 bool GtkSalFrame::GetUseDarkMode() const 1403 { 1404 if (!m_pWindow) 1405 return false; 1406 GtkSettings* pSettings = gtk_widget_get_settings(m_pWindow); 1407 gboolean bDarkIconTheme = false; 1408 g_object_get(pSettings, "gtk-application-prefer-dark-theme", &bDarkIconTheme, nullptr); 1409 return bDarkIconTheme; 1410 } 1411 1412 bool GtkSalFrame::GetUseReducedAnimation() const 1413 { 1414 if (!m_pWindow) 1415 return false; 1416 GtkSettings* pSettings = gtk_widget_get_settings(m_pWindow); 1417 gboolean bAnimations; 1418 g_object_get(pSettings, "gtk-enable-animations", &bAnimations, nullptr); 1419 return !bAnimations; 1420 } 1421 1422 static void settings_portal_changed_cb(GDBusProxy*, const char*, const char* signal_name, 1423 GVariant* parameters, gpointer frame) 1424 { 1425 if (g_strcmp0(signal_name, "SettingChanged")) 1426 return; 1427 1428 g_autoptr (GVariant) value = nullptr; 1429 const char *name_space; 1430 const char *name; 1431 g_variant_get(parameters, "(&s&sv)", &name_space, &name, &value); 1432 1433 if (g_strcmp0(name_space, "org.freedesktop.appearance") || 1434 g_strcmp0(name, "color-scheme")) 1435 return; 1436 1437 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 1438 pThis->SetColorScheme(value); 1439 } 1440 1441 void GtkSalFrame::ListenPortalSettings() 1442 { 1443 EnsureSessionBus(); 1444 1445 if (!pSessionBus) 1446 return; 1447 1448 m_pSettingsPortal = g_dbus_proxy_new_sync(pSessionBus, 1449 G_DBUS_PROXY_FLAGS_NONE, 1450 nullptr, 1451 "org.freedesktop.portal.Desktop", 1452 "/org/freedesktop/portal/desktop", 1453 "org.freedesktop.portal.Settings", 1454 nullptr, 1455 nullptr); 1456 1457 UpdateDarkMode(); 1458 1459 if (!m_pSettingsPortal) 1460 return; 1461 1462 m_nPortalSettingChangedSignalId = g_signal_connect(m_pSettingsPortal, "g-signal", G_CALLBACK(settings_portal_changed_cb), this); 1463 } 1464 1465 static void session_client_response(GDBusProxy* client_proxy) 1466 { 1467 g_dbus_proxy_call(client_proxy, 1468 "EndSessionResponse", 1469 g_variant_new ("(bs)", true, ""), 1470 G_DBUS_CALL_FLAGS_NONE, 1471 G_MAXINT, 1472 nullptr, nullptr, nullptr); 1473 } 1474 1475 // unset documents "modify" flag so they won't veto closing 1476 static void clear_modify_and_terminate() 1477 { 1478 css::uno::Reference<css::uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext(); 1479 uno::Reference<frame::XDesktop> xDesktop(frame::Desktop::create(xContext)); 1480 uno::Reference<css::container::XEnumeration> xComponents = xDesktop->getComponents()->createEnumeration(); 1481 while (xComponents->hasMoreElements()) 1482 { 1483 css::uno::Reference<css::util::XModifiable> xModifiable(xComponents->nextElement(), css::uno::UNO_QUERY); 1484 if (xModifiable) 1485 xModifiable->setModified(false); 1486 } 1487 xDesktop->terminate(); 1488 } 1489 1490 static void session_client_signal(GDBusProxy* client_proxy, const char*, const char* signal_name, 1491 GVariant* /*parameters*/, gpointer frame) 1492 { 1493 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 1494 1495 if (g_str_equal (signal_name, "QueryEndSession")) 1496 { 1497 css::uno::Reference<css::uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext(); 1498 uno::Reference<frame::XDesktop2> xDesktop(frame::Desktop::create(xContext)); 1499 1500 bool bModified = false; 1501 1502 // find the XModifiable for this GtkSalFrame 1503 if (UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper(false)) 1504 { 1505 VclPtr<vcl::Window> xThisWindow = pThis->GetWindow(); 1506 css::uno::Reference<css::container::XIndexAccess> xList = xDesktop->getFrames(); 1507 sal_Int32 nFrameCount = xList->getCount(); 1508 for (sal_Int32 i = 0; i < nFrameCount; ++i) 1509 { 1510 css::uno::Reference<css::frame::XFrame> xFrame; 1511 xList->getByIndex(i) >>= xFrame; 1512 if (!xFrame) 1513 continue; 1514 VclPtr<vcl::Window> xWin = pWrapper->GetWindow(xFrame->getContainerWindow()); 1515 if (!xWin) 1516 continue; 1517 if (xWin->GetFrameWindow() != xThisWindow) 1518 continue; 1519 css::uno::Reference<css::frame::XController> xController = xFrame->getController(); 1520 if (!xController) 1521 break; 1522 css::uno::Reference<css::util::XModifiable> xModifiable(xController->getModel(), css::uno::UNO_QUERY); 1523 if (!xModifiable) 1524 break; 1525 bModified = xModifiable->isModified(); 1526 break; 1527 } 1528 } 1529 1530 pThis->SessionManagerInhibit(bModified, APPLICATION_INHIBIT_LOGOUT, VclResId(STR_UNSAVED_DOCUMENTS), 1531 gtk_window_get_icon_name(GTK_WINDOW(pThis->getWindow()))); 1532 1533 session_client_response(client_proxy); 1534 } 1535 else if (g_str_equal (signal_name, "CancelEndSession")) 1536 { 1537 // restore back to uninhibited (to set again if queried), so frames 1538 // that go away before the next logout don't affect that logout 1539 pThis->SessionManagerInhibit(false, APPLICATION_INHIBIT_LOGOUT, VclResId(STR_UNSAVED_DOCUMENTS), 1540 gtk_window_get_icon_name(GTK_WINDOW(pThis->getWindow()))); 1541 } 1542 else if (g_str_equal (signal_name, "EndSession")) 1543 { 1544 session_client_response(client_proxy); 1545 clear_modify_and_terminate(); 1546 } 1547 else if (g_str_equal (signal_name, "Stop")) 1548 { 1549 clear_modify_and_terminate(); 1550 } 1551 } 1552 1553 void GtkSalFrame::ListenSessionManager() 1554 { 1555 EnsureSessionBus(); 1556 1557 if (!pSessionBus) 1558 return; 1559 1560 m_pSessionManager = g_dbus_proxy_new_sync(pSessionBus, 1561 G_DBUS_PROXY_FLAGS_NONE, 1562 nullptr, 1563 "org.gnome.SessionManager", 1564 "/org/gnome/SessionManager", 1565 "org.gnome.SessionManager", 1566 nullptr, 1567 nullptr); 1568 1569 if (!m_pSessionManager) 1570 return; 1571 1572 GVariant* res = g_dbus_proxy_call_sync(m_pSessionManager, 1573 "RegisterClient", 1574 g_variant_new ("(ss)", "org.libreoffice", ""), 1575 G_DBUS_CALL_FLAGS_NONE, 1576 G_MAXINT, 1577 nullptr, 1578 nullptr); 1579 1580 if (!res) 1581 return; 1582 1583 gchar* client_path; 1584 g_variant_get(res, "(o)", &client_path); 1585 g_variant_unref(res); 1586 1587 m_pSessionClient = g_dbus_proxy_new_sync(pSessionBus, 1588 G_DBUS_PROXY_FLAGS_NONE, 1589 nullptr, 1590 "org.gnome.SessionManager", 1591 client_path, 1592 "org.gnome.SessionManager.ClientPrivate", 1593 nullptr, 1594 nullptr); 1595 1596 g_free(client_path); 1597 1598 if (!m_pSessionClient) 1599 return; 1600 1601 m_nSessionClientSignalId = g_signal_connect(m_pSessionClient, "g-signal", G_CALLBACK(session_client_signal), this); 1602 } 1603 1604 void GtkSalFrame::UpdateDarkMode() 1605 { 1606 g_autoptr (GVariant) value = nullptr; 1607 if (m_pSettingsPortal) 1608 ReadColorScheme(m_pSettingsPortal, &value); 1609 SetColorScheme(value); 1610 } 1611 1612 #if GTK_CHECK_VERSION(4,0,0) 1613 static void PopoverClosed(GtkPopover*, GtkSalFrame* pThis) 1614 { 1615 SolarMutexGuard aGuard; 1616 pThis->closePopup(); 1617 } 1618 #endif 1619 1620 void GtkSalFrame::Init( SalFrame* pParent, SalFrameStyleFlags nStyle ) 1621 { 1622 if( nStyle & SalFrameStyleFlags::DEFAULT ) // ensure default style 1623 { 1624 nStyle |= SalFrameStyleFlags::MOVEABLE | SalFrameStyleFlags::SIZEABLE | SalFrameStyleFlags::CLOSEABLE; 1625 nStyle &= ~SalFrameStyleFlags::FLOAT; 1626 } 1627 1628 m_pParent = static_cast<GtkSalFrame*>(pParent); 1629 #if !GTK_CHECK_VERSION(4,0,0) 1630 m_pForeignParent = nullptr; 1631 m_aForeignParentWindow = None; 1632 m_pForeignTopLevel = nullptr; 1633 m_aForeignTopLevelWindow = None; 1634 #endif 1635 m_nStyle = nStyle; 1636 1637 bool bPopup = ((nStyle & SalFrameStyleFlags::FLOAT) && 1638 !(nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION)); 1639 1640 if( nStyle & SalFrameStyleFlags::SYSTEMCHILD ) 1641 { 1642 #if !GTK_CHECK_VERSION(4,0,0) 1643 m_pWindow = gtk_event_box_new(); 1644 #else 1645 m_pWindow = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); 1646 #endif 1647 if( m_pParent ) 1648 { 1649 // insert into container 1650 gtk_fixed_put( m_pParent->getFixedContainer(), 1651 m_pWindow, 0, 0 ); 1652 } 1653 } 1654 else 1655 { 1656 #if !GTK_CHECK_VERSION(4,0,0) 1657 m_pWindow = gtk_window_new(bPopup ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL); 1658 #else 1659 if (!bPopup) 1660 m_pWindow = gtk_window_new(); 1661 else 1662 { 1663 m_pWindow = gtk_popover_new(); 1664 gtk_popover_set_has_arrow(GTK_POPOVER(m_pWindow), false); 1665 g_signal_connect(m_pWindow, "closed", G_CALLBACK(PopoverClosed), this); 1666 } 1667 #endif 1668 1669 #if !GTK_CHECK_VERSION(4,0,0) 1670 // hook up F1 to show help for embedded native gtk widgets 1671 GtkAccelGroup *pGroup = gtk_accel_group_new(); 1672 GClosure* closure = g_cclosure_new(G_CALLBACK(GtkSalFrame::NativeWidgetHelpPressed), GTK_WINDOW(m_pWindow), nullptr); 1673 gtk_accel_group_connect(pGroup, GDK_KEY_F1, static_cast<GdkModifierType>(0), GTK_ACCEL_LOCKED, closure); 1674 gtk_window_add_accel_group(GTK_WINDOW(m_pWindow), pGroup); 1675 #endif 1676 } 1677 1678 g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", this ); 1679 g_object_set_data( G_OBJECT( m_pWindow ), "libo-version", const_cast<char *>(LIBO_VERSION_DOTTED)); 1680 1681 // force wm class hint 1682 if (!isChild()) 1683 { 1684 if (m_pParent) 1685 m_sWMClass = m_pParent->m_sWMClass; 1686 updateWMClass(); 1687 } 1688 1689 if (GTK_IS_WINDOW(m_pWindow)) 1690 { 1691 if (m_pParent) 1692 { 1693 GtkWidget* pTopLevel = widget_get_toplevel(m_pParent->m_pWindow); 1694 #if !GTK_CHECK_VERSION(4,0,0) 1695 if (!isChild()) 1696 gtk_window_set_screen(GTK_WINDOW(m_pWindow), gtk_widget_get_screen(pTopLevel)); 1697 #endif 1698 1699 if (!(m_pParent->m_nStyle & SalFrameStyleFlags::PLUG)) 1700 gtk_window_set_transient_for(GTK_WINDOW(m_pWindow), GTK_WINDOW(pTopLevel)); 1701 m_pParent->m_aChildren.push_back( this ); 1702 gtk_window_group_add_window(gtk_window_get_group(GTK_WINDOW(pTopLevel)), GTK_WINDOW(m_pWindow)); 1703 } 1704 else 1705 { 1706 gtk_window_group_add_window(gtk_window_group_new(), GTK_WINDOW(m_pWindow)); 1707 g_object_unref(gtk_window_get_group(GTK_WINDOW(m_pWindow))); 1708 } 1709 } 1710 else if (GTK_IS_POPOVER(m_pWindow)) 1711 { 1712 assert(m_pParent); 1713 gtk_widget_set_parent(m_pWindow, m_pParent->getMouseEventWidget()); 1714 } 1715 1716 // set window type 1717 bool bDecoHandling = 1718 ! isChild() && 1719 ( ! (nStyle & SalFrameStyleFlags::FLOAT) || 1720 (nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) ); 1721 1722 if( bDecoHandling ) 1723 { 1724 #if !GTK_CHECK_VERSION(4,0,0) 1725 GdkWindowTypeHint eType = GDK_WINDOW_TYPE_HINT_NORMAL; 1726 if( (nStyle & SalFrameStyleFlags::DIALOG) && m_pParent != nullptr ) 1727 eType = GDK_WINDOW_TYPE_HINT_DIALOG; 1728 #endif 1729 if( nStyle & SalFrameStyleFlags::INTRO ) 1730 { 1731 #if !GTK_CHECK_VERSION(4,0,0) 1732 gtk_window_set_role( GTK_WINDOW(m_pWindow), "splashscreen" ); 1733 eType = GDK_WINDOW_TYPE_HINT_SPLASHSCREEN; 1734 #endif 1735 } 1736 else if( nStyle & SalFrameStyleFlags::TOOLWINDOW ) 1737 { 1738 #if !GTK_CHECK_VERSION(4,0,0) 1739 eType = GDK_WINDOW_TYPE_HINT_DIALOG; 1740 gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow), true ); 1741 #endif 1742 } 1743 else if( nStyle & SalFrameStyleFlags::OWNERDRAWDECORATION ) 1744 { 1745 #if !GTK_CHECK_VERSION(4,0,0) 1746 eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; 1747 gtk_window_set_focus_on_map(GTK_WINDOW(m_pWindow), false); 1748 #endif 1749 gtk_window_set_decorated(GTK_WINDOW(m_pWindow), false); 1750 } 1751 #if !GTK_CHECK_VERSION(4,0,0) 1752 gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), eType ); 1753 gtk_window_set_gravity( GTK_WINDOW(m_pWindow), GDK_GRAVITY_STATIC ); 1754 #endif 1755 gtk_window_set_resizable( GTK_WINDOW(m_pWindow), bool(nStyle & SalFrameStyleFlags::SIZEABLE) ); 1756 1757 #if !GTK_CHECK_VERSION(4,0,0) 1758 #if defined(GDK_WINDOWING_WAYLAND) 1759 //rhbz#1392145 under wayland/csd if we've overridden the default widget direction in order to set LibreOffice's 1760 //UI to the configured ui language but the system ui locale is a different text direction, then the toplevel 1761 //built-in close button of the titlebar follows the overridden direction rather than continue in the same 1762 //direction as every other titlebar on the user's desktop. So if they don't match set an explicit 1763 //header bar with the desired 'outside' direction 1764 if ((eType == GDK_WINDOW_TYPE_HINT_NORMAL || eType == GDK_WINDOW_TYPE_HINT_DIALOG) && DLSYM_GDK_IS_WAYLAND_DISPLAY(GtkSalFrame::getGdkDisplay())) 1765 { 1766 const bool bDesktopIsRTL = MsLangId::isRightToLeft(MsLangId::getConfiguredSystemUILanguage()); 1767 const bool bAppIsRTL = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL; 1768 if (bDesktopIsRTL != bAppIsRTL) 1769 { 1770 m_pHeaderBar = GTK_HEADER_BAR(gtk_header_bar_new()); 1771 gtk_widget_set_direction(GTK_WIDGET(m_pHeaderBar), bDesktopIsRTL ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); 1772 gtk_header_bar_set_show_close_button(m_pHeaderBar, true); 1773 gtk_window_set_titlebar(GTK_WINDOW(m_pWindow), GTK_WIDGET(m_pHeaderBar)); 1774 gtk_widget_show(GTK_WIDGET(m_pHeaderBar)); 1775 } 1776 } 1777 #endif 1778 #endif 1779 } 1780 #if !GTK_CHECK_VERSION(4,0,0) 1781 else if( nStyle & SalFrameStyleFlags::FLOAT ) 1782 gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), GDK_WINDOW_TYPE_HINT_POPUP_MENU ); 1783 #endif 1784 1785 InitCommon(); 1786 1787 if (!bPopup) 1788 { 1789 // Enable GMenuModel native menu 1790 attach_menu_model(this); 1791 1792 // Listen to portal settings for e.g. prefer dark theme 1793 ListenPortalSettings(); 1794 1795 // Listen to session manager for e.g. query-end 1796 ListenSessionManager(); 1797 } 1798 } 1799 1800 #if !GTK_CHECK_VERSION(4,0,0) 1801 GdkNativeWindow GtkSalFrame::findTopLevelSystemWindow( GdkNativeWindow ) 1802 { 1803 //FIXME: no findToplevelSystemWindow 1804 return 0; 1805 } 1806 #endif 1807 1808 void GtkSalFrame::Init( SystemParentData* pSysData ) 1809 { 1810 m_pParent = nullptr; 1811 #if !GTK_CHECK_VERSION(4,0,0) 1812 m_aForeignParentWindow = pSysData->aWindow; 1813 m_pForeignParent = nullptr; 1814 m_aForeignTopLevelWindow = findTopLevelSystemWindow(pSysData->aWindow); 1815 m_pForeignTopLevel = gdk_x11_window_foreign_new_for_display( getGdkDisplay(), m_aForeignTopLevelWindow ); 1816 gdk_window_set_events( m_pForeignTopLevel, GDK_STRUCTURE_MASK ); 1817 1818 if( pSysData->nSize > sizeof(pSysData->nSize)+sizeof(pSysData->aWindow) && pSysData->bXEmbedSupport ) 1819 { 1820 m_pWindow = gtk_plug_new_for_display( getGdkDisplay(), pSysData->aWindow ); 1821 gtk_widget_set_can_default(m_pWindow, true); 1822 gtk_widget_set_can_focus(m_pWindow, true); 1823 gtk_widget_set_sensitive(m_pWindow, true); 1824 } 1825 else 1826 { 1827 m_pWindow = gtk_window_new( GTK_WINDOW_POPUP ); 1828 } 1829 #endif 1830 m_nStyle = SalFrameStyleFlags::PLUG; 1831 InitCommon(); 1832 1833 #if !GTK_CHECK_VERSION(4,0,0) 1834 m_pForeignParent = gdk_x11_window_foreign_new_for_display( getGdkDisplay(), m_aForeignParentWindow ); 1835 gdk_window_set_events( m_pForeignParent, GDK_STRUCTURE_MASK ); 1836 #else 1837 (void)pSysData; 1838 #endif 1839 1840 //FIXME: Handling embedded windows, is going to be fun ... 1841 } 1842 1843 void GtkSalFrame::SetExtendedFrameStyle(SalExtStyle) 1844 { 1845 } 1846 1847 SalGraphics* GtkSalFrame::AcquireGraphics() 1848 { 1849 if( m_bGraphics ) 1850 return nullptr; 1851 1852 if( !m_pGraphics ) 1853 { 1854 m_pGraphics.reset( new GtkSalGraphics( this, m_pWindow ) ); 1855 if (!m_pSurface) 1856 { 1857 AllocateFrame(); 1858 TriggerPaintEvent(); 1859 } 1860 m_pGraphics->setSurface(m_pSurface, m_aFrameSize); 1861 } 1862 m_bGraphics = true; 1863 return m_pGraphics.get(); 1864 } 1865 1866 void GtkSalFrame::ReleaseGraphics( SalGraphics* pGraphics ) 1867 { 1868 (void) pGraphics; 1869 assert( pGraphics == m_pGraphics.get() ); 1870 m_bGraphics = false; 1871 } 1872 1873 bool GtkSalFrame::PostEvent(std::unique_ptr<ImplSVEvent> pData) 1874 { 1875 getDisplay()->SendInternalEvent( this, pData.release() ); 1876 return true; 1877 } 1878 1879 void GtkSalFrame::SetTitle( const OUString& rTitle ) 1880 { 1881 if (m_pWindow && GTK_IS_WINDOW(m_pWindow) && !isChild()) 1882 { 1883 OString sTitle(OUStringToOString(rTitle, RTL_TEXTENCODING_UTF8)); 1884 gtk_window_set_title(GTK_WINDOW(m_pWindow), sTitle.getStr()); 1885 #if !GTK_CHECK_VERSION(4,0,0) 1886 if (m_pHeaderBar) 1887 gtk_header_bar_set_title(m_pHeaderBar, sTitle.getStr()); 1888 #endif 1889 } 1890 } 1891 1892 void GtkSalFrame::SetIcon(const char* appicon) 1893 { 1894 gtk_window_set_icon_name(GTK_WINDOW(m_pWindow), appicon); 1895 1896 #if defined(GDK_WINDOWING_WAYLAND) 1897 if (!DLSYM_GDK_IS_WAYLAND_DISPLAY(getGdkDisplay())) 1898 return; 1899 1900 #if GTK_CHECK_VERSION(4,0,0) 1901 GdkSurface* gdkWindow = gtk_native_get_surface(gtk_widget_get_native(m_pWindow)); 1902 gdk_wayland_toplevel_set_application_id((GDK_TOPLEVEL(gdkWindow)), appicon); 1903 #else 1904 static auto set_application_id = reinterpret_cast<void (*) (GdkWindow*, const char*)>( 1905 dlsym(nullptr, "gdk_wayland_window_set_application_id")); 1906 if (set_application_id) 1907 { 1908 GdkSurface* gdkWindow = widget_get_surface(m_pWindow); 1909 set_application_id(gdkWindow, appicon); 1910 } 1911 #endif 1912 // gdk_wayland_window_set_application_id doesn't seem to work before 1913 // the window is mapped, so set this for real when/if we are mapped 1914 m_bIconSetWhileUnmapped = !gtk_widget_get_mapped(m_pWindow); 1915 #endif 1916 } 1917 1918 void GtkSalFrame::SetIcon( sal_uInt16 nIcon ) 1919 { 1920 if( (m_nStyle & (SalFrameStyleFlags::PLUG|SalFrameStyleFlags::SYSTEMCHILD|SalFrameStyleFlags::FLOAT|SalFrameStyleFlags::INTRO|SalFrameStyleFlags::OWNERDRAWDECORATION)) 1921 || ! m_pWindow ) 1922 return; 1923 1924 gchar* appicon; 1925 1926 if (nIcon == SV_ICON_ID_TEXT) 1927 appicon = g_strdup ("libreoffice-writer"); 1928 else if (nIcon == SV_ICON_ID_SPREADSHEET) 1929 appicon = g_strdup ("libreoffice-calc"); 1930 else if (nIcon == SV_ICON_ID_DRAWING) 1931 appicon = g_strdup ("libreoffice-draw"); 1932 else if (nIcon == SV_ICON_ID_PRESENTATION) 1933 appicon = g_strdup ("libreoffice-impress"); 1934 else if (nIcon == SV_ICON_ID_DATABASE) 1935 appicon = g_strdup ("libreoffice-base"); 1936 else if (nIcon == SV_ICON_ID_FORMULA) 1937 appicon = g_strdup ("libreoffice-math"); 1938 else 1939 appicon = g_strdup ("libreoffice-startcenter"); 1940 1941 SetIcon(appicon); 1942 1943 g_free (appicon); 1944 } 1945 1946 void GtkSalFrame::SetMenu( SalMenu* pSalMenu ) 1947 { 1948 m_pSalMenu = static_cast<GtkSalMenu*>(pSalMenu); 1949 } 1950 1951 SalMenu* GtkSalFrame::GetMenu() 1952 { 1953 return m_pSalMenu; 1954 } 1955 1956 void GtkSalFrame::Center() 1957 { 1958 if (!GTK_IS_WINDOW(m_pWindow)) 1959 return; 1960 #if !GTK_CHECK_VERSION(4,0,0) 1961 if (m_pParent) 1962 gtk_window_set_position(GTK_WINDOW(m_pWindow), GTK_WIN_POS_CENTER_ON_PARENT); 1963 else 1964 gtk_window_set_position(GTK_WINDOW(m_pWindow), GTK_WIN_POS_CENTER); 1965 #endif 1966 } 1967 1968 Size GtkSalFrame::calcDefaultSize() 1969 { 1970 Size aScreenSize(getDisplay()->GetScreenSize(GetDisplayScreen())); 1971 int scale = gtk_widget_get_scale_factor(m_pWindow); 1972 aScreenSize.setWidth( aScreenSize.Width() / scale ); 1973 aScreenSize.setHeight( aScreenSize.Height() / scale ); 1974 return bestmaxFrameSizeForScreenSize(aScreenSize); 1975 } 1976 1977 void GtkSalFrame::SetDefaultSize() 1978 { 1979 Size aDefSize = calcDefaultSize(); 1980 1981 SetPosSize( 0, 0, aDefSize.Width(), aDefSize.Height(), 1982 SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); 1983 1984 if( (m_nStyle & SalFrameStyleFlags::DEFAULT) && m_pWindow ) 1985 gtk_window_maximize( GTK_WINDOW(m_pWindow) ); 1986 } 1987 1988 void GtkSalFrame::Show( bool bVisible, bool /*bNoActivate*/ ) 1989 { 1990 if( !m_pWindow ) 1991 return; 1992 1993 if( bVisible ) 1994 { 1995 getDisplay()->startupNotificationCompleted(); 1996 1997 if( m_bDefaultPos ) 1998 Center(); 1999 if( m_bDefaultSize ) 2000 SetDefaultSize(); 2001 setMinMaxSize(); 2002 2003 if (isFloatGrabWindow() && !getDisplay()->GetCaptureFrame()) 2004 { 2005 m_pParent->grabPointer(true, true, true); 2006 m_pParent->addGrabLevel(); 2007 } 2008 2009 #if defined(GDK_WINDOWING_WAYLAND) && !GTK_CHECK_VERSION(4,0,0) 2010 /* 2011 rhbz#1334915, gnome#779143, tdf#100158 2012 https://gitlab.gnome.org/GNOME/gtk/-/issues/767 2013 2014 before gdk_wayland_window_set_application_id was available gtk 2015 under wayland lacked a way to change the app_id of a window, so 2016 brute force everything as a startcenter when initially shown to at 2017 least get the default LibreOffice icon and not the broken app icon 2018 */ 2019 static bool bAppIdImmutable = DLSYM_GDK_IS_WAYLAND_DISPLAY(getGdkDisplay()) && 2020 !dlsym(nullptr, "gdk_wayland_window_set_application_id"); 2021 if (bAppIdImmutable) 2022 { 2023 OString sOrigName(g_get_prgname()); 2024 g_set_prgname("libreoffice-startcenter"); 2025 gtk_widget_show(m_pWindow); 2026 g_set_prgname(sOrigName.getStr()); 2027 } 2028 else 2029 { 2030 gtk_widget_show(m_pWindow); 2031 } 2032 #else 2033 gtk_widget_show(m_pWindow); 2034 #endif 2035 2036 if( isFloatGrabWindow() ) 2037 { 2038 m_nFloats++; 2039 if (!getDisplay()->GetCaptureFrame()) 2040 { 2041 grabPointer(true, true, true); 2042 addGrabLevel(); 2043 } 2044 // #i44068# reset parent's IM context 2045 if( m_pParent ) 2046 m_pParent->EndExtTextInput(EndExtTextInputFlags::NONE); 2047 } 2048 } 2049 else 2050 { 2051 if( isFloatGrabWindow() ) 2052 { 2053 m_nFloats--; 2054 if (!getDisplay()->GetCaptureFrame()) 2055 { 2056 removeGrabLevel(); 2057 grabPointer(false, true, false); 2058 m_pParent->removeGrabLevel(); 2059 bool bParentIsFloatGrabWindow = m_pParent->isFloatGrabWindow(); 2060 m_pParent->grabPointer(bParentIsFloatGrabWindow, true, bParentIsFloatGrabWindow); 2061 } 2062 } 2063 gtk_widget_hide( m_pWindow ); 2064 if( m_pIMHandler ) 2065 m_pIMHandler->focusChanged( false ); 2066 } 2067 } 2068 2069 void GtkSalFrame::setMinMaxSize() 2070 { 2071 /* #i34504# metacity (and possibly others) do not treat 2072 * _NET_WM_STATE_FULLSCREEN and max_width/height independently; 2073 * whether they should is undefined. So don't set the max size hint 2074 * for a full screen window. 2075 */ 2076 if( !m_pWindow || isChild() ) 2077 return; 2078 2079 #if !GTK_CHECK_VERSION(4, 0, 0) 2080 GdkGeometry aGeo; 2081 int aHints = 0; 2082 if( m_nStyle & SalFrameStyleFlags::SIZEABLE ) 2083 { 2084 if( m_aMinSize.Width() && m_aMinSize.Height() && ! m_bFullscreen ) 2085 { 2086 aGeo.min_width = m_aMinSize.Width(); 2087 aGeo.min_height = m_aMinSize.Height(); 2088 aHints |= GDK_HINT_MIN_SIZE; 2089 } 2090 if( m_aMaxSize.Width() && m_aMaxSize.Height() && ! m_bFullscreen ) 2091 { 2092 aGeo.max_width = m_aMaxSize.Width(); 2093 aGeo.max_height = m_aMaxSize.Height(); 2094 aHints |= GDK_HINT_MAX_SIZE; 2095 } 2096 } 2097 else 2098 { 2099 if (!m_bFullscreen && m_nWidthRequest && m_nHeightRequest) 2100 { 2101 aGeo.min_width = m_nWidthRequest; 2102 aGeo.min_height = m_nHeightRequest; 2103 aHints |= GDK_HINT_MIN_SIZE; 2104 2105 aGeo.max_width = m_nWidthRequest; 2106 aGeo.max_height = m_nHeightRequest; 2107 aHints |= GDK_HINT_MAX_SIZE; 2108 } 2109 } 2110 2111 if( m_bFullscreen && m_aMaxSize.Width() && m_aMaxSize.Height() ) 2112 { 2113 aGeo.max_width = m_aMaxSize.Width(); 2114 aGeo.max_height = m_aMaxSize.Height(); 2115 aHints |= GDK_HINT_MAX_SIZE; 2116 } 2117 if( aHints ) 2118 { 2119 gtk_window_set_geometry_hints( GTK_WINDOW(m_pWindow), 2120 nullptr, 2121 &aGeo, 2122 GdkWindowHints( aHints ) ); 2123 } 2124 #endif 2125 } 2126 2127 void GtkSalFrame::SetMaxClientSize( tools::Long nWidth, tools::Long nHeight ) 2128 { 2129 if( ! isChild() ) 2130 { 2131 m_aMaxSize = Size( nWidth, nHeight ); 2132 setMinMaxSize(); 2133 } 2134 } 2135 void GtkSalFrame::SetMinClientSize( tools::Long nWidth, tools::Long nHeight ) 2136 { 2137 if( ! isChild() ) 2138 { 2139 m_aMinSize = Size( nWidth, nHeight ); 2140 if( m_pWindow ) 2141 { 2142 widget_set_size_request(nWidth, nHeight); 2143 setMinMaxSize(); 2144 } 2145 } 2146 } 2147 2148 void GtkSalFrame::AllocateFrame() 2149 { 2150 basegfx::B2IVector aFrameSize( maGeometry.width(), maGeometry.height() ); 2151 if (m_pSurface && m_aFrameSize.getX() == aFrameSize.getX() && 2152 m_aFrameSize.getY() == aFrameSize.getY() ) 2153 return; 2154 2155 if( aFrameSize.getX() == 0 ) 2156 aFrameSize.setX( 1 ); 2157 if( aFrameSize.getY() == 0 ) 2158 aFrameSize.setY( 1 ); 2159 2160 if (m_pSurface) 2161 cairo_surface_destroy(m_pSurface); 2162 2163 m_pSurface = surface_create_similar_surface(widget_get_surface(m_pWindow), 2164 CAIRO_CONTENT_COLOR_ALPHA, 2165 aFrameSize.getX(), 2166 aFrameSize.getY()); 2167 m_aFrameSize = aFrameSize; 2168 2169 cairo_surface_set_user_data(m_pSurface, SvpSalGraphics::getDamageKey(), &m_aDamageHandler, nullptr); 2170 SAL_INFO("vcl.gtk3", "allocated Frame size of " << maGeometry.width() << " x " << maGeometry.height()); 2171 2172 if (m_pGraphics) 2173 m_pGraphics->setSurface(m_pSurface, m_aFrameSize); 2174 } 2175 2176 void GtkSalFrame::SetPosSize( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, sal_uInt16 nFlags ) 2177 { 2178 if( !m_pWindow || isChild( true, false ) ) 2179 return; 2180 2181 if( (nFlags & ( SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT )) && 2182 (nWidth > 0 && nHeight > 0 ) // sometimes stupid things happen 2183 ) 2184 { 2185 m_bDefaultSize = false; 2186 2187 maGeometry.setSize({ nWidth, nHeight }); 2188 2189 if (isChild(false) || GTK_IS_POPOVER(m_pWindow)) 2190 widget_set_size_request(nWidth, nHeight); 2191 else if( ! ( m_nState & GDK_TOPLEVEL_STATE_MAXIMIZED ) ) 2192 window_resize(nWidth, nHeight); 2193 2194 setMinMaxSize(); 2195 } 2196 else if( m_bDefaultSize ) 2197 SetDefaultSize(); 2198 2199 m_bDefaultSize = false; 2200 2201 if( nFlags & ( SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ) ) 2202 { 2203 if( m_pParent ) 2204 { 2205 if( AllSettings::GetLayoutRTL() ) 2206 nX = m_pParent->maGeometry.width()-m_nWidthRequest-1-nX; 2207 nX += m_pParent->maGeometry.x(); 2208 nY += m_pParent->maGeometry.y(); 2209 } 2210 2211 if (nFlags & SAL_FRAME_POSSIZE_X) 2212 maGeometry.setX(nX); 2213 if (nFlags & SAL_FRAME_POSSIZE_Y) 2214 maGeometry.setY(nY); 2215 m_bGeometryIsProvisional = true; 2216 2217 m_bDefaultPos = false; 2218 2219 moveWindow(maGeometry.x(), maGeometry.y()); 2220 2221 updateScreenNumber(); 2222 } 2223 else if( m_bDefaultPos ) 2224 Center(); 2225 2226 m_bDefaultPos = false; 2227 } 2228 2229 void GtkSalFrame::GetClientSize( tools::Long& rWidth, tools::Long& rHeight ) 2230 { 2231 if( m_pWindow && !(m_nState & GDK_TOPLEVEL_STATE_MINIMIZED) ) 2232 { 2233 rWidth = maGeometry.width(); 2234 rHeight = maGeometry.height(); 2235 } 2236 else 2237 rWidth = rHeight = 0; 2238 } 2239 2240 void GtkSalFrame::GetWorkArea( AbsoluteScreenPixelRectangle& rRect ) 2241 { 2242 GdkRectangle aRect; 2243 #if !GTK_CHECK_VERSION(4, 0, 0) 2244 GdkScreen *pScreen = gtk_widget_get_screen(m_pWindow); 2245 AbsoluteScreenPixelRectangle aRetRect; 2246 int max = gdk_screen_get_n_monitors (pScreen); 2247 for (int i = 0; i < max; ++i) 2248 { 2249 gdk_screen_get_monitor_workarea(pScreen, i, &aRect); 2250 AbsoluteScreenPixelRectangle aMonitorRect(aRect.x, aRect.y, aRect.x+aRect.width, aRect.y+aRect.height); 2251 aRetRect.Union(aMonitorRect); 2252 } 2253 rRect = aRetRect; 2254 #else 2255 GdkDisplay* pDisplay = gtk_widget_get_display(m_pWindow); 2256 GdkSurface* gdkWindow = widget_get_surface(m_pWindow); 2257 GdkMonitor* pMonitor = gdk_display_get_monitor_at_surface(pDisplay, gdkWindow); 2258 gdk_monitor_get_geometry(pMonitor, &aRect); 2259 rRect = AbsoluteScreenPixelRectangle(aRect.x, aRect.y, aRect.x+aRect.width, aRect.y+aRect.height); 2260 #endif 2261 } 2262 2263 SalFrame* GtkSalFrame::GetParent() const 2264 { 2265 return m_pParent; 2266 } 2267 2268 void GtkSalFrame::SetWindowState(const vcl::WindowData* pState) 2269 { 2270 if( ! m_pWindow || ! pState || isChild( true, false ) ) 2271 return; 2272 2273 const vcl::WindowDataMask nMaxGeometryMask = vcl::WindowDataMask::PosSize | 2274 vcl::WindowDataMask::MaximizedX | vcl::WindowDataMask::MaximizedY | 2275 vcl::WindowDataMask::MaximizedWidth | vcl::WindowDataMask::MaximizedHeight; 2276 2277 if( (pState->mask() & vcl::WindowDataMask::State) && 2278 ! ( m_nState & GDK_TOPLEVEL_STATE_MAXIMIZED ) && 2279 (pState->state() & vcl::WindowState::Maximized) && 2280 (pState->mask() & nMaxGeometryMask) == nMaxGeometryMask ) 2281 { 2282 resizeWindow(pState->width(), pState->height()); 2283 moveWindow(pState->x(), pState->y()); 2284 m_bDefaultPos = m_bDefaultSize = false; 2285 2286 updateScreenNumber(); 2287 2288 m_nState = GdkToplevelState(m_nState | GDK_TOPLEVEL_STATE_MAXIMIZED); 2289 m_aRestorePosSize = pState->posSize(); 2290 } 2291 else if (pState->mask() & vcl::WindowDataMask::PosSize) 2292 { 2293 sal_uInt16 nPosSizeFlags = 0; 2294 tools::Long nX = pState->x() - (m_pParent ? m_pParent->maGeometry.x() : 0); 2295 tools::Long nY = pState->y() - (m_pParent ? m_pParent->maGeometry.y() : 0); 2296 if (pState->mask() & vcl::WindowDataMask::X) 2297 nPosSizeFlags |= SAL_FRAME_POSSIZE_X; 2298 else 2299 nX = maGeometry.x() - (m_pParent ? m_pParent->maGeometry.x() : 0); 2300 if (pState->mask() & vcl::WindowDataMask::Y) 2301 nPosSizeFlags |= SAL_FRAME_POSSIZE_Y; 2302 else 2303 nY = maGeometry.y() - (m_pParent ? m_pParent->maGeometry.y() : 0); 2304 if (pState->mask() & vcl::WindowDataMask::Width) 2305 nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH; 2306 if (pState->mask() & vcl::WindowDataMask::Height) 2307 nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT; 2308 SetPosSize(nX, nY, pState->width(), pState->height(), nPosSizeFlags); 2309 } 2310 2311 if (pState->mask() & vcl::WindowDataMask::State && !isChild()) 2312 { 2313 if (pState->state() & vcl::WindowState::Maximized) 2314 gtk_window_maximize( GTK_WINDOW(m_pWindow) ); 2315 else 2316 gtk_window_unmaximize( GTK_WINDOW(m_pWindow) ); 2317 /* #i42379# there is no rollup state in GDK; and rolled up windows are 2318 * (probably depending on the WM) reported as iconified. If we iconify a 2319 * window here that was e.g. a dialog, then it will be unmapped but still 2320 * not be displayed in the task list, so it's an iconified window that 2321 * the user cannot get out of this state. So do not set the iconified state 2322 * on windows with a parent (that is transient frames) since these tend 2323 * to not be represented in an icon task list. 2324 */ 2325 bool bMinimize = pState->state() & vcl::WindowState::Minimized && !m_pParent; 2326 #if GTK_CHECK_VERSION(4, 0, 0) 2327 if (bMinimize) 2328 gtk_window_minimize(GTK_WINDOW(m_pWindow)); 2329 else 2330 gtk_window_unminimize(GTK_WINDOW(m_pWindow)); 2331 #else 2332 if (bMinimize) 2333 gtk_window_iconify(GTK_WINDOW(m_pWindow)); 2334 else 2335 gtk_window_deiconify(GTK_WINDOW(m_pWindow)); 2336 #endif 2337 } 2338 TriggerPaintEvent(); 2339 } 2340 2341 namespace 2342 { 2343 void GetPosAndSize(GtkWindow *pWindow, tools::Long& rX, tools::Long &rY, tools::Long &rWidth, tools::Long &rHeight) 2344 { 2345 gint width, height; 2346 #if !GTK_CHECK_VERSION(4, 0, 0) 2347 gint root_x, root_y; 2348 gtk_window_get_position(GTK_WINDOW(pWindow), &root_x, &root_y); 2349 rX = root_x; 2350 rY = root_y; 2351 2352 gtk_window_get_size(GTK_WINDOW(pWindow), &width, &height); 2353 #else 2354 rX = 0; 2355 rY = 0; 2356 gtk_window_get_default_size(GTK_WINDOW(pWindow), &width, &height); 2357 #endif 2358 rWidth = width; 2359 rHeight = height; 2360 } 2361 2362 tools::Rectangle GetPosAndSize(GtkWindow *pWindow) 2363 { 2364 tools::Long nX, nY, nWidth, nHeight; 2365 GetPosAndSize(pWindow, nX, nY, nWidth, nHeight); 2366 return tools::Rectangle(nX, nY, nX + nWidth, nY + nHeight); 2367 } 2368 } 2369 2370 bool GtkSalFrame::GetWindowState(vcl::WindowData* pState) 2371 { 2372 pState->setState(vcl::WindowState::Normal); 2373 pState->setMask(vcl::WindowDataMask::PosSizeState); 2374 2375 // rollup ? gtk 2.2 does not seem to support the shaded state 2376 if( m_nState & GDK_TOPLEVEL_STATE_MINIMIZED ) 2377 pState->rState() |= vcl::WindowState::Minimized; 2378 if( m_nState & GDK_TOPLEVEL_STATE_MAXIMIZED ) 2379 { 2380 pState->rState() |= vcl::WindowState::Maximized; 2381 pState->setPosSize(m_aRestorePosSize); 2382 tools::Rectangle aPosSize = GetPosAndSize(GTK_WINDOW(m_pWindow)); 2383 pState->SetMaximizedX(aPosSize.Left()); 2384 pState->SetMaximizedY(aPosSize.Top()); 2385 pState->SetMaximizedWidth(aPosSize.GetWidth()); 2386 pState->SetMaximizedHeight(aPosSize.GetHeight()); 2387 pState->rMask() |= vcl::WindowDataMask::MaximizedX | 2388 vcl::WindowDataMask::MaximizedY | 2389 vcl::WindowDataMask::MaximizedWidth | 2390 vcl::WindowDataMask::MaximizedHeight; 2391 } 2392 else 2393 pState->setPosSize(GetPosAndSize(GTK_WINDOW(m_pWindow))); 2394 2395 return true; 2396 } 2397 2398 void GtkSalFrame::SetScreen( unsigned int nNewScreen, SetType eType, tools::Rectangle const *pSize ) 2399 { 2400 if( !m_pWindow ) 2401 return; 2402 2403 if (maGeometry.screen() == nNewScreen && eType == SetType::RetainSize) 2404 return; 2405 2406 #if !GTK_CHECK_VERSION(4, 0, 0) 2407 int nX = maGeometry.x(), nY = maGeometry.y(), 2408 nWidth = maGeometry.width(), nHeight = maGeometry.height(); 2409 GdkScreen *pScreen = nullptr; 2410 GdkRectangle aNewMonitor; 2411 2412 bool bSpanAllScreens = nNewScreen == static_cast<unsigned int>(-1); 2413 bool bSpanMonitorsWhenFullscreen = bSpanAllScreens && getDisplay()->getSystem()->GetDisplayScreenCount() > 1; 2414 gint nMonitor = -1; 2415 if (bSpanMonitorsWhenFullscreen) //span all screens 2416 { 2417 pScreen = gtk_widget_get_screen( m_pWindow ); 2418 aNewMonitor.x = 0; 2419 aNewMonitor.y = 0; 2420 aNewMonitor.width = gdk_screen_get_width(pScreen); 2421 aNewMonitor.height = gdk_screen_get_height(pScreen); 2422 } 2423 else 2424 { 2425 bool bSameMonitor = false; 2426 2427 if (!bSpanAllScreens) 2428 { 2429 pScreen = getDisplay()->getSystem()->getScreenMonitorFromIdx( nNewScreen, nMonitor ); 2430 if (!pScreen) 2431 { 2432 g_warning ("Attempt to move GtkSalFrame to invalid screen %d => " 2433 "fallback to current\n", nNewScreen); 2434 } 2435 } 2436 2437 if (!pScreen) 2438 { 2439 pScreen = gtk_widget_get_screen( m_pWindow ); 2440 bSameMonitor = true; 2441 } 2442 2443 // Heavy lifting, need to move screen ... 2444 if( pScreen != gtk_widget_get_screen( m_pWindow )) 2445 gtk_window_set_screen( GTK_WINDOW( m_pWindow ), pScreen ); 2446 2447 gint nOldMonitor = gdk_screen_get_monitor_at_window( 2448 pScreen, widget_get_surface( m_pWindow ) ); 2449 if (bSameMonitor) 2450 nMonitor = nOldMonitor; 2451 2452 #if OSL_DEBUG_LEVEL > 1 2453 if( nMonitor == nOldMonitor ) 2454 g_warning( "An apparently pointless SetScreen - should we elide it ?" ); 2455 #endif 2456 2457 GdkRectangle aOldMonitor; 2458 gdk_screen_get_monitor_geometry( pScreen, nOldMonitor, &aOldMonitor ); 2459 gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aNewMonitor ); 2460 2461 nX = aNewMonitor.x + nX - aOldMonitor.x; 2462 nY = aNewMonitor.y + nY - aOldMonitor.y; 2463 } 2464 2465 bool bResize = false; 2466 bool bVisible = gtk_widget_get_mapped( m_pWindow ); 2467 if( bVisible ) 2468 Show( false ); 2469 2470 if( eType == SetType::Fullscreen ) 2471 { 2472 nX = aNewMonitor.x; 2473 nY = aNewMonitor.y; 2474 nWidth = aNewMonitor.width; 2475 nHeight = aNewMonitor.height; 2476 bResize = true; 2477 2478 // #i110881# for the benefit of compiz set a max size here 2479 // else setting to fullscreen fails for unknown reasons 2480 m_aMaxSize.setWidth( aNewMonitor.width ); 2481 m_aMaxSize.setHeight( aNewMonitor.height ); 2482 } 2483 2484 if( pSize && eType == SetType::UnFullscreen ) 2485 { 2486 nX = pSize->Left(); 2487 nY = pSize->Top(); 2488 nWidth = pSize->GetWidth(); 2489 nHeight = pSize->GetHeight(); 2490 bResize = true; 2491 } 2492 2493 if (bResize) 2494 { 2495 // temporarily re-sizeable 2496 if( !(m_nStyle & SalFrameStyleFlags::SIZEABLE) ) 2497 gtk_window_set_resizable( GTK_WINDOW(m_pWindow), true ); 2498 window_resize(nWidth, nHeight); 2499 } 2500 2501 gtk_window_move(GTK_WINDOW(m_pWindow), nX, nY); 2502 2503 GdkFullscreenMode eMode = 2504 bSpanMonitorsWhenFullscreen ? GDK_FULLSCREEN_ON_ALL_MONITORS : GDK_FULLSCREEN_ON_CURRENT_MONITOR; 2505 2506 gdk_window_set_fullscreen_mode(widget_get_surface(m_pWindow), eMode); 2507 2508 GtkWidget* pMenuBarContainerWidget = m_pSalMenu ? m_pSalMenu->GetMenuBarContainerWidget() : nullptr; 2509 if( eType == SetType::Fullscreen ) 2510 { 2511 if (pMenuBarContainerWidget) 2512 gtk_widget_hide(pMenuBarContainerWidget); 2513 if (bSpanMonitorsWhenFullscreen) 2514 gtk_window_fullscreen(GTK_WINDOW(m_pWindow)); 2515 else 2516 gtk_window_fullscreen_on_monitor(GTK_WINDOW(m_pWindow), pScreen, nMonitor); 2517 2518 } 2519 else if( eType == SetType::UnFullscreen ) 2520 { 2521 if (pMenuBarContainerWidget) 2522 gtk_widget_show(pMenuBarContainerWidget); 2523 gtk_window_unfullscreen( GTK_WINDOW( m_pWindow ) ); 2524 } 2525 2526 if( eType == SetType::UnFullscreen && 2527 !(m_nStyle & SalFrameStyleFlags::SIZEABLE) ) 2528 gtk_window_set_resizable( GTK_WINDOW( m_pWindow ), FALSE ); 2529 2530 // FIXME: we should really let gtk+ handle our widget hierarchy ... 2531 if( m_pParent && gtk_widget_get_screen( m_pParent->m_pWindow ) != pScreen ) 2532 SetParent( nullptr ); 2533 2534 std::list< GtkSalFrame* > aChildren = m_aChildren; 2535 for (auto const& child : aChildren) 2536 child->SetScreen( nNewScreen, SetType::RetainSize ); 2537 2538 m_bDefaultPos = m_bDefaultSize = false; 2539 updateScreenNumber(); 2540 2541 if( bVisible ) 2542 Show( true ); 2543 2544 #else 2545 (void)pSize; // assume restore will restore the original size without our help 2546 2547 bool bSpanMonitorsWhenFullscreen = nNewScreen == static_cast<unsigned int>(-1); 2548 2549 GdkFullscreenMode eMode = 2550 bSpanMonitorsWhenFullscreen ? GDK_FULLSCREEN_ON_ALL_MONITORS : GDK_FULLSCREEN_ON_CURRENT_MONITOR; 2551 2552 g_object_set(widget_get_surface(m_pWindow), "fullscreen-mode", eMode, nullptr); 2553 2554 GtkWidget* pMenuBarContainerWidget = m_pSalMenu ? m_pSalMenu->GetMenuBarContainerWidget() : nullptr; 2555 if (eType == SetType::Fullscreen) 2556 { 2557 if (!(m_nStyle & SalFrameStyleFlags::SIZEABLE)) 2558 { 2559 // temp make it resizable, restore when unfullscreened 2560 gtk_window_set_resizable(GTK_WINDOW(m_pWindow), true); 2561 } 2562 2563 if (pMenuBarContainerWidget) 2564 gtk_widget_hide(pMenuBarContainerWidget); 2565 if (bSpanMonitorsWhenFullscreen) 2566 gtk_window_fullscreen(GTK_WINDOW(m_pWindow)); 2567 else 2568 { 2569 GdkDisplay* pDisplay = gtk_widget_get_display(m_pWindow); 2570 GListModel* pList = gdk_display_get_monitors(pDisplay); 2571 GdkMonitor* pMonitor = static_cast<GdkMonitor*>(g_list_model_get_item(pList, nNewScreen)); 2572 if (!pMonitor) 2573 pMonitor = gdk_display_get_monitor_at_surface(pDisplay, widget_get_surface(m_pWindow)); 2574 gtk_window_fullscreen_on_monitor(GTK_WINDOW(m_pWindow), pMonitor); 2575 } 2576 } 2577 else if (eType == SetType::UnFullscreen) 2578 { 2579 if (pMenuBarContainerWidget) 2580 gtk_widget_show(pMenuBarContainerWidget); 2581 gtk_window_unfullscreen(GTK_WINDOW(m_pWindow)); 2582 2583 if (!(m_nStyle & SalFrameStyleFlags::SIZEABLE)) 2584 { 2585 // restore temp resizability 2586 gtk_window_set_resizable(GTK_WINDOW(m_pWindow), false); 2587 } 2588 } 2589 2590 for (auto const& child : m_aChildren) 2591 child->SetScreen(nNewScreen, SetType::RetainSize); 2592 2593 m_bDefaultPos = m_bDefaultSize = false; 2594 updateScreenNumber(); 2595 #endif 2596 } 2597 2598 void GtkSalFrame::SetScreenNumber( unsigned int nNewScreen ) 2599 { 2600 SetScreen( nNewScreen, SetType::RetainSize ); 2601 } 2602 2603 void GtkSalFrame::updateWMClass() 2604 { 2605 if (!DLSYM_GDK_IS_X11_DISPLAY(getGdkDisplay())) 2606 return; 2607 2608 if (!gtk_widget_get_realized(m_pWindow)) 2609 return; 2610 2611 OString aResClass = OUStringToOString(m_sWMClass, RTL_TEXTENCODING_ASCII_US); 2612 const char *pResClass = !aResClass.isEmpty() ? aResClass.getStr() : 2613 SalGenericSystem::getFrameClassName(); 2614 XClassHint* pClass = XAllocClassHint(); 2615 OString aResName = SalGenericSystem::getFrameResName(); 2616 pClass->res_name = const_cast<char*>(aResName.getStr()); 2617 pClass->res_class = const_cast<char*>(pResClass); 2618 Display *display = gdk_x11_display_get_xdisplay(getGdkDisplay()); 2619 XSetClassHint( display, 2620 GtkSalFrame::GetNativeWindowHandle(m_pWindow), 2621 pClass ); 2622 XFree( pClass ); 2623 } 2624 2625 void GtkSalFrame::SetApplicationID( const OUString &rWMClass ) 2626 { 2627 if( rWMClass != m_sWMClass && ! isChild() ) 2628 { 2629 m_sWMClass = rWMClass; 2630 updateWMClass(); 2631 2632 for (auto const& child : m_aChildren) 2633 child->SetApplicationID(rWMClass); 2634 } 2635 } 2636 2637 void GtkSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nScreen ) 2638 { 2639 m_bFullscreen = bFullScreen; 2640 2641 if( !m_pWindow || isChild() ) 2642 return; 2643 2644 if( bFullScreen ) 2645 { 2646 m_aRestorePosSize = GetPosAndSize(GTK_WINDOW(m_pWindow)); 2647 SetScreen( nScreen, SetType::Fullscreen ); 2648 } 2649 else 2650 { 2651 SetScreen( nScreen, SetType::UnFullscreen, 2652 !m_aRestorePosSize.IsEmpty() ? &m_aRestorePosSize : nullptr ); 2653 m_aRestorePosSize = tools::Rectangle(); 2654 } 2655 } 2656 2657 void GtkSalFrame::SessionManagerInhibit(bool bStart, ApplicationInhibitFlags eType, std::u16string_view sReason, const char* application_id) 2658 { 2659 guint nWindow(0); 2660 std::optional<Display*> aDisplay; 2661 2662 if (DLSYM_GDK_IS_X11_DISPLAY(getGdkDisplay())) 2663 { 2664 nWindow = GtkSalFrame::GetNativeWindowHandle(m_pWindow); 2665 aDisplay = gdk_x11_display_get_xdisplay(getGdkDisplay()); 2666 } 2667 2668 m_SessionManagerInhibitor.inhibit(bStart, sReason, eType, 2669 nWindow, aDisplay, application_id); 2670 } 2671 2672 void GtkSalFrame::StartPresentation( bool bStart ) 2673 { 2674 SessionManagerInhibit(bStart, APPLICATION_INHIBIT_IDLE, u"presentation", nullptr); 2675 } 2676 2677 void GtkSalFrame::SetAlwaysOnTop( bool bOnTop ) 2678 { 2679 #if !GTK_CHECK_VERSION(4, 0, 0) 2680 if( m_pWindow ) 2681 gtk_window_set_keep_above( GTK_WINDOW( m_pWindow ), bOnTop ); 2682 #else 2683 (void)bOnTop; 2684 #endif 2685 } 2686 2687 static guint32 nLastUserInputTime = GDK_CURRENT_TIME; 2688 2689 guint32 GtkSalFrame::GetLastInputEventTime() 2690 { 2691 return nLastUserInputTime; 2692 } 2693 2694 void GtkSalFrame::UpdateLastInputEventTime(guint32 nUserInputTime) 2695 { 2696 //gtk3 can generate a synthetic crossing event with a useless 0 2697 //(GDK_CURRENT_TIME) timestamp on showing a menu from the main 2698 //menubar, which is unhelpful, so ignore the 0 timestamps 2699 if (nUserInputTime == GDK_CURRENT_TIME) 2700 return; 2701 nLastUserInputTime = nUserInputTime; 2702 } 2703 2704 void GtkSalFrame::ToTop( SalFrameToTop nFlags ) 2705 { 2706 if( !m_pWindow ) 2707 return; 2708 2709 if( isChild( false ) ) 2710 GrabFocus(); 2711 else if( gtk_widget_get_mapped( m_pWindow ) ) 2712 { 2713 auto nTimestamp = GetLastInputEventTime(); 2714 #ifdef GDK_WINDOWING_X11 2715 GdkDisplay *pDisplay = GtkSalFrame::getGdkDisplay(); 2716 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay)) 2717 nTimestamp = gdk_x11_display_get_user_time(pDisplay); 2718 #endif 2719 if (!(nFlags & SalFrameToTop::GrabFocusOnly)) 2720 gtk_window_present_with_time(GTK_WINDOW(m_pWindow), nTimestamp); 2721 #if !GTK_CHECK_VERSION(4, 0, 0) 2722 else 2723 gdk_window_focus(widget_get_surface(m_pWindow), nTimestamp); 2724 #endif 2725 GrabFocus(); 2726 } 2727 else 2728 { 2729 if( nFlags & SalFrameToTop::RestoreWhenMin ) 2730 gtk_window_present( GTK_WINDOW(m_pWindow) ); 2731 } 2732 } 2733 2734 void GtkSalFrame::SetPointer( PointerStyle ePointerStyle ) 2735 { 2736 if( !m_pWindow || ePointerStyle == m_ePointerStyle ) 2737 return; 2738 2739 m_ePointerStyle = ePointerStyle; 2740 GdkCursor *pCursor = getDisplay()->getCursor( ePointerStyle ); 2741 widget_set_cursor(GTK_WIDGET(m_pWindow), pCursor); 2742 } 2743 2744 void GtkSalFrame::grabPointer( bool bGrab, bool bKeyboardAlso, bool bOwnerEvents ) 2745 { 2746 if (bGrab) 2747 { 2748 // tdf#135779 move focus back inside usual input window out of any 2749 // other gtk widgets before grabbing the pointer 2750 GrabFocus(); 2751 } 2752 2753 static const char* pEnv = getenv( "SAL_NO_MOUSEGRABS" ); 2754 if (pEnv && *pEnv) 2755 return; 2756 2757 if (!m_pWindow) 2758 return; 2759 2760 #if !GTK_CHECK_VERSION(4, 0, 0) 2761 GdkSeat* pSeat = gdk_display_get_default_seat(getGdkDisplay()); 2762 if (bGrab) 2763 { 2764 GdkSeatCapabilities eCapability = bKeyboardAlso ? GDK_SEAT_CAPABILITY_ALL : GDK_SEAT_CAPABILITY_ALL_POINTING; 2765 gdk_seat_grab(pSeat, widget_get_surface(getMouseEventWidget()), eCapability, 2766 bOwnerEvents, nullptr, nullptr, nullptr, nullptr); 2767 } 2768 else 2769 { 2770 gdk_seat_ungrab(pSeat); 2771 } 2772 #else 2773 (void)bKeyboardAlso; 2774 (void)bOwnerEvents; 2775 #endif 2776 } 2777 2778 void GtkSalFrame::CaptureMouse( bool bCapture ) 2779 { 2780 getDisplay()->CaptureMouse( bCapture ? this : nullptr ); 2781 } 2782 2783 void GtkSalFrame::SetPointerPos( tools::Long nX, tools::Long nY ) 2784 { 2785 #if !GTK_CHECK_VERSION(4, 0, 0) 2786 GtkSalFrame* pFrame = this; 2787 while( pFrame && pFrame->isChild( false ) ) 2788 pFrame = pFrame->m_pParent; 2789 if( ! pFrame ) 2790 return; 2791 2792 GdkScreen *pScreen = gtk_widget_get_screen(pFrame->m_pWindow); 2793 GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); 2794 2795 /* when the application tries to center the mouse in the dialog the 2796 * window isn't mapped already. So use coordinates relative to the root window. 2797 */ 2798 unsigned int nWindowLeft = maGeometry.x() + nX; 2799 unsigned int nWindowTop = maGeometry.y() + nY; 2800 2801 GdkDeviceManager* pManager = gdk_display_get_device_manager(pDisplay); 2802 gdk_device_warp(gdk_device_manager_get_client_pointer(pManager), pScreen, nWindowLeft, nWindowTop); 2803 2804 // #i38648# ask for the next motion hint 2805 gint x, y; 2806 GdkModifierType mask; 2807 gdk_window_get_pointer( widget_get_surface(pFrame->m_pWindow) , &x, &y, &mask ); 2808 #else 2809 (void)nX; 2810 (void)nY; 2811 #endif 2812 } 2813 2814 void GtkSalFrame::Flush() 2815 { 2816 gdk_display_flush( getGdkDisplay() ); 2817 } 2818 2819 void GtkSalFrame::KeyCodeToGdkKey(const vcl::KeyCode& rKeyCode, 2820 guint* pGdkKeyCode, GdkModifierType *pGdkModifiers) 2821 { 2822 if ( pGdkKeyCode == nullptr || pGdkModifiers == nullptr ) 2823 return; 2824 2825 // Get GDK key modifiers 2826 GdkModifierType nModifiers = GdkModifierType(0); 2827 2828 if ( rKeyCode.IsShift() ) 2829 nModifiers = static_cast<GdkModifierType>( nModifiers | GDK_SHIFT_MASK ); 2830 2831 if ( rKeyCode.IsMod1() ) 2832 nModifiers = static_cast<GdkModifierType>( nModifiers | GDK_CONTROL_MASK ); 2833 2834 if ( rKeyCode.IsMod2() ) 2835 nModifiers = static_cast<GdkModifierType>( nModifiers | GDK_ALT_MASK ); 2836 2837 *pGdkModifiers = nModifiers; 2838 2839 // Get GDK keycode. 2840 guint nKeyCode = 0; 2841 2842 guint nCode = rKeyCode.GetCode(); 2843 2844 if ( nCode >= KEY_0 && nCode <= KEY_9 ) 2845 nKeyCode = ( nCode - KEY_0 ) + GDK_KEY_0; 2846 else if ( nCode >= KEY_A && nCode <= KEY_Z ) 2847 nKeyCode = ( nCode - KEY_A ) + GDK_KEY_A; 2848 else if ( nCode >= KEY_F1 && nCode <= KEY_F26 ) 2849 nKeyCode = ( nCode - KEY_F1 ) + GDK_KEY_F1; 2850 else 2851 { 2852 switch (nCode) 2853 { 2854 case KEY_DOWN: nKeyCode = GDK_KEY_Down; break; 2855 case KEY_UP: nKeyCode = GDK_KEY_Up; break; 2856 case KEY_LEFT: nKeyCode = GDK_KEY_Left; break; 2857 case KEY_RIGHT: nKeyCode = GDK_KEY_Right; break; 2858 case KEY_HOME: nKeyCode = GDK_KEY_Home; break; 2859 case KEY_END: nKeyCode = GDK_KEY_End; break; 2860 case KEY_PAGEUP: nKeyCode = GDK_KEY_Page_Up; break; 2861 case KEY_PAGEDOWN: nKeyCode = GDK_KEY_Page_Down; break; 2862 case KEY_RETURN: nKeyCode = GDK_KEY_Return; break; 2863 case KEY_ESCAPE: nKeyCode = GDK_KEY_Escape; break; 2864 case KEY_TAB: nKeyCode = GDK_KEY_Tab; break; 2865 case KEY_BACKSPACE: nKeyCode = GDK_KEY_BackSpace; break; 2866 case KEY_SPACE: nKeyCode = GDK_KEY_space; break; 2867 case KEY_INSERT: nKeyCode = GDK_KEY_Insert; break; 2868 case KEY_DELETE: nKeyCode = GDK_KEY_Delete; break; 2869 case KEY_ADD: nKeyCode = GDK_KEY_plus; break; 2870 case KEY_SUBTRACT: nKeyCode = GDK_KEY_minus; break; 2871 case KEY_MULTIPLY: nKeyCode = GDK_KEY_asterisk; break; 2872 case KEY_DIVIDE: nKeyCode = GDK_KEY_slash; break; 2873 case KEY_POINT: nKeyCode = GDK_KEY_period; break; 2874 case KEY_COMMA: nKeyCode = GDK_KEY_comma; break; 2875 case KEY_LESS: nKeyCode = GDK_KEY_less; break; 2876 case KEY_GREATER: nKeyCode = GDK_KEY_greater; break; 2877 case KEY_EQUAL: nKeyCode = GDK_KEY_equal; break; 2878 case KEY_FIND: nKeyCode = GDK_KEY_Find; break; 2879 case KEY_CONTEXTMENU: nKeyCode = GDK_KEY_Menu; break; 2880 case KEY_HELP: nKeyCode = GDK_KEY_Help; break; 2881 case KEY_UNDO: nKeyCode = GDK_KEY_Undo; break; 2882 case KEY_REPEAT: nKeyCode = GDK_KEY_Redo; break; 2883 case KEY_DECIMAL: nKeyCode = GDK_KEY_KP_Decimal; break; 2884 case KEY_TILDE: nKeyCode = GDK_KEY_asciitilde; break; 2885 case KEY_QUOTELEFT: nKeyCode = GDK_KEY_quoteleft; break; 2886 case KEY_BRACKETLEFT: nKeyCode = GDK_KEY_bracketleft; break; 2887 case KEY_BRACKETRIGHT: nKeyCode = GDK_KEY_bracketright; break; 2888 case KEY_SEMICOLON: nKeyCode = GDK_KEY_semicolon; break; 2889 case KEY_QUOTERIGHT: nKeyCode = GDK_KEY_quoteright; break; 2890 case KEY_RIGHTCURLYBRACKET: nKeyCode = GDK_KEY_braceright; break; 2891 case KEY_NUMBERSIGN: nKeyCode = GDK_KEY_numbersign; break; 2892 case KEY_XF86FORWARD: nKeyCode = GDK_KEY_Forward; break; 2893 case KEY_XF86BACK: nKeyCode = GDK_KEY_Back; break; 2894 case KEY_COLON: nKeyCode = GDK_KEY_colon; break; 2895 2896 // Special cases 2897 case KEY_COPY: nKeyCode = GDK_KEY_Copy; break; 2898 case KEY_CUT: nKeyCode = GDK_KEY_Cut; break; 2899 case KEY_PASTE: nKeyCode = GDK_KEY_Paste; break; 2900 case KEY_OPEN: nKeyCode = GDK_KEY_Open; break; 2901 } 2902 } 2903 2904 *pGdkKeyCode = nKeyCode; 2905 } 2906 2907 OUString GtkSalFrame::GetKeyName( sal_uInt16 nKeyCode ) 2908 { 2909 guint nGtkKeyCode; 2910 GdkModifierType nGtkModifiers; 2911 KeyCodeToGdkKey(nKeyCode, &nGtkKeyCode, &nGtkModifiers ); 2912 2913 gchar* pName = gtk_accelerator_get_label(nGtkKeyCode, nGtkModifiers); 2914 OUString aRet = OStringToOUString(pName, RTL_TEXTENCODING_UTF8); 2915 g_free(pName); 2916 return aRet; 2917 } 2918 2919 GdkDisplay *GtkSalFrame::getGdkDisplay() 2920 { 2921 return GetGtkSalData()->GetGdkDisplay(); 2922 } 2923 2924 GtkSalDisplay *GtkSalFrame::getDisplay() 2925 { 2926 return GetGtkSalData()->GetGtkDisplay(); 2927 } 2928 2929 SalFrame::SalPointerState GtkSalFrame::GetPointerState() 2930 { 2931 SalPointerState aState; 2932 #if !GTK_CHECK_VERSION(4, 0, 0) 2933 GdkScreen* pScreen; 2934 gint x, y; 2935 GdkModifierType aMask; 2936 gdk_display_get_pointer( getGdkDisplay(), &pScreen, &x, &y, &aMask ); 2937 aState.maPos = Point( x - maGeometry.x(), y - maGeometry.y() ); 2938 aState.mnState = GetMouseModCode( aMask ); 2939 #endif 2940 return aState; 2941 } 2942 2943 KeyIndicatorState GtkSalFrame::GetIndicatorState() 2944 { 2945 KeyIndicatorState nState = KeyIndicatorState::NONE; 2946 2947 #if !GTK_CHECK_VERSION(4, 0, 0) 2948 GdkKeymap *pKeyMap = gdk_keymap_get_for_display(getGdkDisplay()); 2949 2950 if (gdk_keymap_get_caps_lock_state(pKeyMap)) 2951 nState |= KeyIndicatorState::CAPSLOCK; 2952 if (gdk_keymap_get_num_lock_state(pKeyMap)) 2953 nState |= KeyIndicatorState::NUMLOCK; 2954 if (gdk_keymap_get_scroll_lock_state(pKeyMap)) 2955 nState |= KeyIndicatorState::SCROLLLOCK; 2956 #endif 2957 2958 return nState; 2959 } 2960 2961 void GtkSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode ) 2962 { 2963 g_warning ("missing simulate keypress %d", nKeyCode); 2964 } 2965 2966 void GtkSalFrame::SetInputContext( SalInputContext* pContext ) 2967 { 2968 if( ! pContext ) 2969 return; 2970 2971 if( ! (pContext->mnOptions & InputContextFlags::Text) ) 2972 return; 2973 2974 // create a new im context 2975 if( ! m_pIMHandler ) 2976 m_pIMHandler.reset( new IMHandler( this ) ); 2977 } 2978 2979 void GtkSalFrame::EndExtTextInput( EndExtTextInputFlags nFlags ) 2980 { 2981 if( m_pIMHandler ) 2982 m_pIMHandler->endExtTextInput( nFlags ); 2983 } 2984 2985 bool GtkSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , vcl::KeyCode& ) 2986 { 2987 // not supported yet 2988 return false; 2989 } 2990 2991 LanguageType GtkSalFrame::GetInputLanguage() 2992 { 2993 return LANGUAGE_DONTKNOW; 2994 } 2995 2996 void GtkSalFrame::UpdateSettings( AllSettings& rSettings ) 2997 { 2998 if( ! m_pWindow ) 2999 return; 3000 3001 GtkSalGraphics* pGraphics = m_pGraphics.get(); 3002 bool bFreeGraphics = false; 3003 if( ! pGraphics ) 3004 { 3005 pGraphics = static_cast<GtkSalGraphics*>(AcquireGraphics()); 3006 if ( !pGraphics ) 3007 { 3008 SAL_WARN("vcl.gtk3", "Could not get graphics - unable to update settings"); 3009 return; 3010 } 3011 bFreeGraphics = true; 3012 } 3013 3014 pGraphics->UpdateSettings( rSettings ); 3015 3016 if( bFreeGraphics ) 3017 ReleaseGraphics( pGraphics ); 3018 } 3019 3020 void GtkSalFrame::Beep() 3021 { 3022 gdk_display_beep( getGdkDisplay() ); 3023 } 3024 3025 const SystemEnvData* GtkSalFrame::GetSystemData() const 3026 { 3027 return &m_aSystemData; 3028 } 3029 3030 void GtkSalFrame::ResolveWindowHandle(SystemEnvData& rData) const 3031 { 3032 if (!rData.pWidget) 3033 return; 3034 SAL_WARN("vcl.gtk3", "its undesirable to need the NativeWindowHandle, see tdf#139609"); 3035 rData.SetWindowHandle(GetNativeWindowHandle(static_cast<GtkWidget*>(rData.pWidget))); 3036 } 3037 3038 void GtkSalFrame::SetParent( SalFrame* pNewParent ) 3039 { 3040 GtkWindow* pWindow = GTK_IS_WINDOW(m_pWindow) ? GTK_WINDOW(m_pWindow) : nullptr; 3041 if (m_pParent) 3042 { 3043 if (pWindow && GTK_IS_WINDOW(m_pParent->m_pWindow)) 3044 gtk_window_group_remove_window(gtk_window_get_group(GTK_WINDOW(m_pParent->m_pWindow)), pWindow); 3045 m_pParent->m_aChildren.remove(this); 3046 } 3047 m_pParent = static_cast<GtkSalFrame*>(pNewParent); 3048 if (m_pParent) 3049 { 3050 m_pParent->m_aChildren.push_back(this); 3051 if (pWindow && GTK_IS_WINDOW(m_pParent->m_pWindow)) 3052 gtk_window_group_add_window(gtk_window_get_group(GTK_WINDOW(m_pParent->m_pWindow)), pWindow); 3053 } 3054 if (!isChild() && pWindow) 3055 gtk_window_set_transient_for( pWindow, 3056 (m_pParent && ! m_pParent->isChild(true,false)) ? GTK_WINDOW(m_pParent->m_pWindow) : nullptr 3057 ); 3058 } 3059 3060 void GtkSalFrame::SetPluginParent( SystemParentData* ) 3061 { 3062 //FIXME: no SetPluginParent impl. for gtk3 3063 } 3064 3065 void GtkSalFrame::ResetClipRegion() 3066 { 3067 #if !GTK_CHECK_VERSION(4, 0, 0) 3068 if( m_pWindow ) 3069 gdk_window_shape_combine_region( widget_get_surface( m_pWindow ), nullptr, 0, 0 ); 3070 #endif 3071 } 3072 3073 void GtkSalFrame::BeginSetClipRegion( sal_uInt32 ) 3074 { 3075 if( m_pRegion ) 3076 cairo_region_destroy( m_pRegion ); 3077 m_pRegion = cairo_region_create(); 3078 } 3079 3080 void GtkSalFrame::UnionClipRegion( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) 3081 { 3082 if( m_pRegion ) 3083 { 3084 GdkRectangle aRect; 3085 aRect.x = nX; 3086 aRect.y = nY; 3087 aRect.width = nWidth; 3088 aRect.height = nHeight; 3089 cairo_region_union_rectangle( m_pRegion, &aRect ); 3090 } 3091 } 3092 3093 void GtkSalFrame::EndSetClipRegion() 3094 { 3095 #if !GTK_CHECK_VERSION(4, 0, 0) 3096 if( m_pWindow && m_pRegion ) 3097 gdk_window_shape_combine_region( widget_get_surface(m_pWindow), m_pRegion, 0, 0 ); 3098 #endif 3099 } 3100 3101 void GtkSalFrame::PositionByToolkit(const tools::Rectangle& rRect, FloatWinPopupFlags nFlags) 3102 { 3103 if ( ImplGetSVData()->maNWFData.mbCanDetermineWindowPosition && 3104 // tdf#152155 cannot determine window positions of popup listboxes on sidebar 3105 nFlags != LISTBOX_FLOATWINPOPUPFLAGS ) 3106 { 3107 return; 3108 } 3109 3110 m_aFloatRect = rRect; 3111 m_nFloatFlags = nFlags; 3112 m_bFloatPositioned = true; 3113 } 3114 3115 void GtkSalFrame::SetModal(bool bModal) 3116 { 3117 if (!m_pWindow) 3118 return; 3119 gtk_window_set_modal(GTK_WINDOW(m_pWindow), bModal); 3120 } 3121 3122 bool GtkSalFrame::GetModal() const 3123 { 3124 if (!m_pWindow) 3125 return false; 3126 return gtk_window_get_modal(GTK_WINDOW(m_pWindow)); 3127 } 3128 3129 gboolean GtkSalFrame::signalTooltipQuery(GtkWidget*, gint /*x*/, gint /*y*/, 3130 gboolean /*keyboard_mode*/, GtkTooltip *tooltip, 3131 gpointer frame) 3132 { 3133 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3134 if (pThis->m_aTooltip.isEmpty() || pThis->m_bTooltipBlocked) 3135 return false; 3136 gtk_tooltip_set_text(tooltip, 3137 OUStringToOString(pThis->m_aTooltip, RTL_TEXTENCODING_UTF8).getStr()); 3138 GdkRectangle aHelpArea; 3139 aHelpArea.x = pThis->m_aHelpArea.Left(); 3140 aHelpArea.y = pThis->m_aHelpArea.Top(); 3141 aHelpArea.width = pThis->m_aHelpArea.GetWidth(); 3142 aHelpArea.height = pThis->m_aHelpArea.GetHeight(); 3143 if (AllSettings::GetLayoutRTL()) 3144 aHelpArea.x = pThis->maGeometry.width()-aHelpArea.width-1-aHelpArea.x; 3145 gtk_tooltip_set_tip_area(tooltip, &aHelpArea); 3146 return true; 3147 } 3148 3149 bool GtkSalFrame::ShowTooltip(const OUString& rHelpText, const tools::Rectangle& rHelpArea) 3150 { 3151 m_aTooltip = rHelpText; 3152 m_aHelpArea = rHelpArea; 3153 gtk_widget_trigger_tooltip_query(getMouseEventWidget()); 3154 return true; 3155 } 3156 3157 void GtkSalFrame::BlockTooltip() 3158 { 3159 m_bTooltipBlocked = true; 3160 } 3161 3162 void GtkSalFrame::UnblockTooltip() 3163 { 3164 m_bTooltipBlocked = false; 3165 } 3166 3167 void GtkSalFrame::HideTooltip() 3168 { 3169 m_aTooltip.clear(); 3170 GtkWidget* pEventWidget = getMouseEventWidget(); 3171 gtk_widget_trigger_tooltip_query(pEventWidget); 3172 } 3173 3174 namespace 3175 { 3176 void set_pointing_to(GtkPopover *pPopOver, vcl::Window* pParent, const tools::Rectangle& rHelpArea, const SalFrameGeometry& rGeometry) 3177 { 3178 GdkRectangle aRect; 3179 aRect.x = FloatingWindow::ImplConvertToAbsPos(pParent, rHelpArea).Left() - rGeometry.x(); 3180 aRect.y = rHelpArea.Top(); 3181 aRect.width = 1; 3182 aRect.height = 1; 3183 3184 GtkPositionType ePos = gtk_popover_get_position(pPopOver); 3185 switch (ePos) 3186 { 3187 case GTK_POS_BOTTOM: 3188 case GTK_POS_TOP: 3189 aRect.width = rHelpArea.GetWidth(); 3190 break; 3191 case GTK_POS_RIGHT: 3192 case GTK_POS_LEFT: 3193 aRect.height = rHelpArea.GetHeight(); 3194 break; 3195 } 3196 3197 gtk_popover_set_pointing_to(pPopOver, &aRect); 3198 } 3199 } 3200 3201 void* GtkSalFrame::ShowPopover(const OUString& rHelpText, vcl::Window* pParent, const tools::Rectangle& rHelpArea, QuickHelpFlags nFlags) 3202 { 3203 #if !GTK_CHECK_VERSION(4, 0, 0) 3204 GtkWidget *pWidget = gtk_popover_new(getMouseEventWidget()); 3205 #else 3206 GtkWidget *pWidget = gtk_popover_new(); 3207 gtk_widget_set_parent(pWidget, getMouseEventWidget()); 3208 #endif 3209 OString sUTF = OUStringToOString(rHelpText, RTL_TEXTENCODING_UTF8); 3210 GtkWidget *pLabel = gtk_label_new(sUTF.getStr()); 3211 #if !GTK_CHECK_VERSION(4, 0, 0) 3212 gtk_container_add(GTK_CONTAINER(pWidget), pLabel); 3213 #else 3214 gtk_popover_set_child(GTK_POPOVER(pWidget), pLabel); 3215 #endif 3216 3217 if (nFlags & QuickHelpFlags::Top) 3218 gtk_popover_set_position(GTK_POPOVER(pWidget), GTK_POS_BOTTOM); 3219 else if (nFlags & QuickHelpFlags::Bottom) 3220 gtk_popover_set_position(GTK_POPOVER(pWidget), GTK_POS_TOP); 3221 else if (nFlags & QuickHelpFlags::Left) 3222 gtk_popover_set_position(GTK_POPOVER(pWidget), GTK_POS_RIGHT); 3223 else if (nFlags & QuickHelpFlags::Right) 3224 gtk_popover_set_position(GTK_POPOVER(pWidget), GTK_POS_LEFT); 3225 3226 set_pointing_to(GTK_POPOVER(pWidget), pParent, rHelpArea, maGeometry); 3227 3228 #if !GTK_CHECK_VERSION(4, 0, 0) 3229 gtk_popover_set_modal(GTK_POPOVER(pWidget), false); 3230 #else 3231 gtk_popover_set_autohide(GTK_POPOVER(pWidget), false); 3232 #endif 3233 3234 gtk_widget_show(pLabel); 3235 gtk_widget_show(pWidget); 3236 3237 return pWidget; 3238 } 3239 3240 bool GtkSalFrame::UpdatePopover(void* nId, const OUString& rHelpText, vcl::Window* pParent, const tools::Rectangle& rHelpArea) 3241 { 3242 GtkWidget *pWidget = static_cast<GtkWidget*>(nId); 3243 3244 set_pointing_to(GTK_POPOVER(pWidget), pParent, rHelpArea, maGeometry); 3245 3246 #if !GTK_CHECK_VERSION(4, 0, 0) 3247 GtkWidget *pLabel = gtk_bin_get_child(GTK_BIN(pWidget)); 3248 #else 3249 GtkWidget *pLabel = gtk_popover_get_child(GTK_POPOVER(pWidget)); 3250 #endif 3251 OString sUTF = OUStringToOString(rHelpText, RTL_TEXTENCODING_UTF8); 3252 gtk_label_set_text(GTK_LABEL(pLabel), sUTF.getStr()); 3253 3254 return true; 3255 } 3256 3257 bool GtkSalFrame::HidePopover(void* nId) 3258 { 3259 GtkWidget *pWidget = static_cast<GtkWidget*>(nId); 3260 #if !GTK_CHECK_VERSION(4, 0, 0) 3261 gtk_widget_destroy(pWidget); 3262 #else 3263 g_clear_pointer(&pWidget, gtk_widget_unparent); 3264 #endif 3265 return true; 3266 } 3267 3268 void GtkSalFrame::addGrabLevel() 3269 { 3270 #if !GTK_CHECK_VERSION(4, 0, 0) 3271 if (m_nGrabLevel == 0) 3272 gtk_grab_add(getMouseEventWidget()); 3273 #endif 3274 ++m_nGrabLevel; 3275 } 3276 3277 void GtkSalFrame::removeGrabLevel() 3278 { 3279 if (m_nGrabLevel > 0) 3280 { 3281 --m_nGrabLevel; 3282 #if !GTK_CHECK_VERSION(4, 0, 0) 3283 if (m_nGrabLevel == 0) 3284 gtk_grab_remove(getMouseEventWidget()); 3285 #endif 3286 } 3287 } 3288 3289 void GtkSalFrame::closePopup() 3290 { 3291 if (!m_nFloats) 3292 return; 3293 ImplSVData* pSVData = ImplGetSVData(); 3294 if (!pSVData->mpWinData->mpFirstFloat) 3295 return; 3296 if (pSVData->mpWinData->mpFirstFloat->ImplGetFrame() != this) 3297 return; 3298 pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll); 3299 } 3300 3301 #if !GTK_CHECK_VERSION(4, 0, 0) 3302 namespace 3303 { 3304 //tdf#117981 translate embedded video window mouse events to parent coordinates 3305 void translate_coords(GdkWindow* pSourceWindow, GtkWidget* pTargetWidget, int& rEventX, int& rEventY) 3306 { 3307 gpointer user_data=nullptr; 3308 gdk_window_get_user_data(pSourceWindow, &user_data); 3309 GtkWidget* pRealEventWidget = static_cast<GtkWidget*>(user_data); 3310 if (pRealEventWidget) 3311 { 3312 gtk_coord fX(0.0), fY(0.0); 3313 gtk_widget_translate_coordinates(pRealEventWidget, pTargetWidget, rEventX, rEventY, &fX, &fY); 3314 rEventX = fX; 3315 rEventY = fY; 3316 } 3317 } 3318 } 3319 #endif 3320 3321 void GtkSalFrame::GrabFocus() 3322 { 3323 GtkWidget* pGrabWidget; 3324 #if !GTK_CHECK_VERSION(4, 0, 0) 3325 if (GTK_IS_EVENT_BOX(m_pWindow)) 3326 pGrabWidget = GTK_WIDGET(m_pWindow); 3327 else 3328 pGrabWidget = GTK_WIDGET(m_pFixedContainer); 3329 // m_nSetFocusSignalId is 0 for the DisallowCycleFocusOut case where 3330 // we don't allow focus to enter the toplevel, but expect it to 3331 // stay in some embedded native gtk widget 3332 if (!gtk_widget_get_can_focus(pGrabWidget) && m_nSetFocusSignalId) 3333 gtk_widget_set_can_focus(pGrabWidget, true); 3334 #else 3335 pGrabWidget = GTK_WIDGET(m_pFixedContainer); 3336 #endif 3337 if (!gtk_widget_has_focus(pGrabWidget)) 3338 { 3339 gtk_widget_grab_focus(pGrabWidget); 3340 if (m_pIMHandler) 3341 m_pIMHandler->focusChanged(true); 3342 } 3343 } 3344 3345 bool GtkSalFrame::DrawingAreaButton(SalEvent nEventType, int nEventX, int nEventY, int nButton, guint32 nTime, guint nState) 3346 { 3347 UpdateLastInputEventTime(nTime); 3348 3349 SalMouseEvent aEvent; 3350 switch(nButton) 3351 { 3352 case 1: aEvent.mnButton = MOUSE_LEFT; break; 3353 case 2: aEvent.mnButton = MOUSE_MIDDLE; break; 3354 case 3: aEvent.mnButton = MOUSE_RIGHT; break; 3355 default: return false; 3356 } 3357 3358 aEvent.mnTime = nTime; 3359 aEvent.mnX = nEventX; 3360 aEvent.mnY = nEventY; 3361 aEvent.mnCode = GetMouseModCode(nState); 3362 3363 if( AllSettings::GetLayoutRTL() ) 3364 aEvent.mnX = maGeometry.width()-1-aEvent.mnX; 3365 3366 CallCallbackExc(nEventType, &aEvent); 3367 3368 return true; 3369 } 3370 3371 #if !GTK_CHECK_VERSION(4, 0, 0) 3372 3373 void GtkSalFrame::UpdateGeometryFromEvent(int x_root, int y_root, int nEventX, int nEventY) 3374 { 3375 //tdf#151509 don't overwrite geometry for system children 3376 if (m_nStyle & SalFrameStyleFlags::SYSTEMCHILD) 3377 return; 3378 3379 int frame_x = x_root - nEventX; 3380 int frame_y = y_root - nEventY; 3381 if (m_bGeometryIsProvisional || frame_x != maGeometry.x() || frame_y != maGeometry.y()) 3382 { 3383 m_bGeometryIsProvisional = false; 3384 maGeometry.setPos({ frame_x, frame_y }); 3385 ImplSVData* pSVData = ImplGetSVData(); 3386 if (pSVData->maNWFData.mbCanDetermineWindowPosition) 3387 CallCallbackExc(SalEvent::Move, nullptr); 3388 } 3389 } 3390 3391 gboolean GtkSalFrame::signalButton(GtkWidget*, GdkEventButton* pEvent, gpointer frame) 3392 { 3393 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3394 GtkWidget* pEventWidget = pThis->getMouseEventWidget(); 3395 bool bDifferentEventWindow = pEvent->window != widget_get_surface(pEventWidget); 3396 3397 if (pEvent->type == GDK_BUTTON_PRESS) 3398 { 3399 // tdf#120764 It isn't allowed under wayland to have two visible popups that share 3400 // the same top level parent. The problem is that since gtk 3.24 tooltips are also 3401 // implemented as popups, which means that we cannot show any popup if there is a 3402 // visible tooltip. In fact, gtk will hide the tooltip by itself after this handler, 3403 // in case of a button press event. But if we intend to show a popup on button press 3404 // it will be too late, so just do it here: 3405 pThis->HideTooltip(); 3406 3407 // focus on click 3408 if (!bDifferentEventWindow) 3409 pThis->GrabFocus(); 3410 } 3411 3412 SalEvent nEventType = SalEvent::NONE; 3413 switch( pEvent->type ) 3414 { 3415 case GDK_BUTTON_PRESS: 3416 nEventType = SalEvent::MouseButtonDown; 3417 break; 3418 case GDK_BUTTON_RELEASE: 3419 nEventType = SalEvent::MouseButtonUp; 3420 break; 3421 default: 3422 return false; 3423 } 3424 3425 vcl::DeletionListener aDel( pThis ); 3426 3427 if (pThis->isFloatGrabWindow()) 3428 { 3429 //rhbz#1505379 if the window that got the event isn't our one, or there's none 3430 //of our windows under the mouse then close this popup window 3431 if (bDifferentEventWindow || 3432 gdk_device_get_window_at_position(pEvent->device, nullptr, nullptr) == nullptr) 3433 { 3434 if (pEvent->type == GDK_BUTTON_PRESS) 3435 pThis->closePopup(); 3436 else if (pEvent->type == GDK_BUTTON_RELEASE) 3437 return true; 3438 } 3439 } 3440 3441 int nEventX = pEvent->x; 3442 int nEventY = pEvent->y; 3443 3444 if (bDifferentEventWindow) 3445 translate_coords(pEvent->window, pEventWidget, nEventX, nEventY); 3446 3447 if (!aDel.isDeleted()) 3448 pThis->UpdateGeometryFromEvent(pEvent->x_root, pEvent->y_root, nEventX, nEventY); 3449 3450 bool bRet = false; 3451 if (!aDel.isDeleted()) 3452 { 3453 bRet = pThis->DrawingAreaButton(nEventType, 3454 nEventX, 3455 nEventY, 3456 pEvent->button, 3457 pEvent->time, 3458 pEvent->state); 3459 } 3460 3461 return bRet; 3462 } 3463 #else 3464 void GtkSalFrame::gesturePressed(GtkGestureClick* pGesture, int /*n_press*/, gdouble x, gdouble y, gpointer frame) 3465 { 3466 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3467 pThis->gestureButton(pGesture, SalEvent::MouseButtonDown, x, y); 3468 } 3469 3470 void GtkSalFrame::gestureReleased(GtkGestureClick* pGesture, int /*n_press*/, gdouble x, gdouble y, gpointer frame) 3471 { 3472 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3473 pThis->gestureButton(pGesture, SalEvent::MouseButtonUp, x, y); 3474 } 3475 3476 void GtkSalFrame::gestureButton(GtkGestureClick* pGesture, SalEvent nEventType, gdouble x, gdouble y) 3477 { 3478 GdkEvent* pEvent = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pGesture)); 3479 GdkModifierType eType = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pGesture)); 3480 int nButton = gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(pGesture)); 3481 DrawingAreaButton(nEventType, x, y, nButton, gdk_event_get_time(pEvent), eType); 3482 } 3483 #endif 3484 3485 #if !GTK_CHECK_VERSION(4, 0, 0) 3486 void GtkSalFrame::LaunchAsyncScroll(GdkEvent const * pEvent) 3487 { 3488 //if we don't match previous pending states, flush that queue now 3489 if (!m_aPendingScrollEvents.empty() && pEvent->scroll.state != m_aPendingScrollEvents.back()->scroll.state) 3490 { 3491 m_aSmoothScrollIdle.Stop(); 3492 m_aSmoothScrollIdle.Invoke(); 3493 assert(m_aPendingScrollEvents.empty()); 3494 } 3495 //add scroll event to queue 3496 m_aPendingScrollEvents.push_back(gdk_event_copy(pEvent)); 3497 if (!m_aSmoothScrollIdle.IsActive()) 3498 m_aSmoothScrollIdle.Start(); 3499 } 3500 #endif 3501 3502 void GtkSalFrame::DrawingAreaScroll(double delta_x, double delta_y, int nEventX, int nEventY, guint32 nTime, guint nState) 3503 { 3504 SalWheelMouseEvent aEvent; 3505 3506 aEvent.mnTime = nTime; 3507 aEvent.mnX = nEventX; 3508 // --- RTL --- (mirror mouse pos) 3509 if (AllSettings::GetLayoutRTL()) 3510 aEvent.mnX = maGeometry.width() - 1 - aEvent.mnX; 3511 aEvent.mnY = nEventY; 3512 aEvent.mnCode = GetMouseModCode(nState); 3513 3514 // rhbz#1344042 "Traditionally" in gtk3 we took a single up/down event as 3515 // equating to 3 scroll lines and a delta of 120. So scale the delta here 3516 // by 120 where a single mouse wheel click is an incoming delta_x of 1 3517 // and divide that by 40 to get the number of scroll lines 3518 if (delta_x != 0.0) 3519 { 3520 aEvent.mnDelta = -delta_x * 120; 3521 aEvent.mnNotchDelta = aEvent.mnDelta < 0 ? -1 : +1; 3522 if (aEvent.mnDelta == 0) 3523 aEvent.mnDelta = aEvent.mnNotchDelta; 3524 aEvent.mbHorz = true; 3525 aEvent.mnScrollLines = std::abs(aEvent.mnDelta) / 40.0; 3526 CallCallbackExc(SalEvent::WheelMouse, &aEvent); 3527 } 3528 3529 if (delta_y != 0.0) 3530 { 3531 aEvent.mnDelta = -delta_y * 120; 3532 aEvent.mnNotchDelta = aEvent.mnDelta < 0 ? -1 : +1; 3533 if (aEvent.mnDelta == 0) 3534 aEvent.mnDelta = aEvent.mnNotchDelta; 3535 aEvent.mbHorz = false; 3536 aEvent.mnScrollLines = std::abs(aEvent.mnDelta) / 40.0; 3537 CallCallbackExc(SalEvent::WheelMouse, &aEvent); 3538 } 3539 } 3540 3541 #if !GTK_CHECK_VERSION(4, 0, 0) 3542 IMPL_LINK_NOARG(GtkSalFrame, AsyncScroll, Timer *, void) 3543 { 3544 assert(!m_aPendingScrollEvents.empty()); 3545 3546 GdkEvent* pEvent = m_aPendingScrollEvents.back(); 3547 auto nEventX = pEvent->scroll.x; 3548 auto nEventY = pEvent->scroll.y; 3549 auto nTime = pEvent->scroll.time; 3550 auto nState = pEvent->scroll.state; 3551 3552 double delta_x(0.0), delta_y(0.0); 3553 for (auto pSubEvent : m_aPendingScrollEvents) 3554 { 3555 delta_x += pSubEvent->scroll.delta_x; 3556 delta_y += pSubEvent->scroll.delta_y; 3557 gdk_event_free(pSubEvent); 3558 } 3559 m_aPendingScrollEvents.clear(); 3560 3561 DrawingAreaScroll(delta_x, delta_y, nEventX, nEventY, nTime, nState); 3562 } 3563 #endif 3564 3565 #if !GTK_CHECK_VERSION(4, 0, 0) 3566 SalWheelMouseEvent GtkSalFrame::GetWheelEvent(const GdkEventScroll& rEvent) 3567 { 3568 SalWheelMouseEvent aEvent; 3569 3570 aEvent.mnTime = rEvent.time; 3571 aEvent.mnX = static_cast<sal_uLong>(rEvent.x); 3572 aEvent.mnY = static_cast<sal_uLong>(rEvent.y); 3573 aEvent.mnCode = GetMouseModCode(rEvent.state); 3574 3575 switch (rEvent.direction) 3576 { 3577 case GDK_SCROLL_UP: 3578 aEvent.mnDelta = 120; 3579 aEvent.mnNotchDelta = 1; 3580 aEvent.mnScrollLines = 3; 3581 aEvent.mbHorz = false; 3582 break; 3583 3584 case GDK_SCROLL_DOWN: 3585 aEvent.mnDelta = -120; 3586 aEvent.mnNotchDelta = -1; 3587 aEvent.mnScrollLines = 3; 3588 aEvent.mbHorz = false; 3589 break; 3590 3591 case GDK_SCROLL_LEFT: 3592 aEvent.mnDelta = 120; 3593 aEvent.mnNotchDelta = 1; 3594 aEvent.mnScrollLines = 3; 3595 aEvent.mbHorz = true; 3596 break; 3597 3598 case GDK_SCROLL_RIGHT: 3599 aEvent.mnDelta = -120; 3600 aEvent.mnNotchDelta = -1; 3601 aEvent.mnScrollLines = 3; 3602 aEvent.mbHorz = true; 3603 break; 3604 default: 3605 break; 3606 } 3607 3608 return aEvent; 3609 } 3610 3611 gboolean GtkSalFrame::signalScroll(GtkWidget*, GdkEvent* pInEvent, gpointer frame) 3612 { 3613 GdkEventScroll& rEvent = pInEvent->scroll; 3614 3615 UpdateLastInputEventTime(rEvent.time); 3616 3617 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3618 3619 if (rEvent.direction == GDK_SCROLL_SMOOTH) 3620 { 3621 pThis->LaunchAsyncScroll(pInEvent); 3622 return true; 3623 } 3624 3625 //if we have smooth scrolling previous pending states, flush that queue now 3626 if (!pThis->m_aPendingScrollEvents.empty()) 3627 { 3628 pThis->m_aSmoothScrollIdle.Stop(); 3629 pThis->m_aSmoothScrollIdle.Invoke(); 3630 assert(pThis->m_aPendingScrollEvents.empty()); 3631 } 3632 3633 SalWheelMouseEvent aEvent(GetWheelEvent(rEvent)); 3634 3635 // --- RTL --- (mirror mouse pos) 3636 if (AllSettings::GetLayoutRTL()) 3637 aEvent.mnX = pThis->maGeometry.width() - 1 - aEvent.mnX; 3638 3639 pThis->CallCallbackExc(SalEvent::WheelMouse, &aEvent); 3640 3641 return true; 3642 } 3643 #else 3644 gboolean GtkSalFrame::signalScroll(GtkEventControllerScroll* pController, double delta_x, double delta_y, gpointer frame) 3645 { 3646 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3647 3648 GdkEvent* pEvent = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController)); 3649 GdkModifierType eState = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pController)); 3650 3651 auto nTime = gdk_event_get_time(pEvent); 3652 3653 UpdateLastInputEventTime(nTime); 3654 3655 double nEventX(0.0), nEventY(0.0); 3656 gdk_event_get_position(pEvent, &nEventX, &nEventY); 3657 3658 pThis->DrawingAreaScroll(delta_x, delta_y, nEventX, nEventY, nTime, eState); 3659 3660 return true; 3661 } 3662 3663 gboolean GtkSalFrame::event_controller_scroll_forward(GtkEventControllerScroll* pController, double delta_x, double delta_y) 3664 { 3665 return GtkSalFrame::signalScroll(pController, delta_x, delta_y, this); 3666 } 3667 3668 #endif 3669 3670 void GtkSalFrame::gestureSwipe(GtkGestureSwipe* gesture, gdouble velocity_x, gdouble velocity_y, gpointer frame) 3671 { 3672 gdouble x, y; 3673 GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); 3674 //I feel I want the first point of the sequence, not the last point which 3675 //the docs say this gives, but for the moment assume we start and end 3676 //within the same vcl window 3677 if (gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y)) 3678 { 3679 SalGestureSwipeEvent aEvent; 3680 aEvent.mnVelocityX = velocity_x; 3681 aEvent.mnVelocityY = velocity_y; 3682 aEvent.mnX = x; 3683 aEvent.mnY = y; 3684 3685 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3686 pThis->CallCallbackExc(SalEvent::GestureSwipe, &aEvent); 3687 } 3688 } 3689 3690 void GtkSalFrame::gestureLongPress(GtkGestureLongPress* gesture, gdouble x, gdouble y, gpointer frame) 3691 { 3692 GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture)); 3693 if (gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y)) 3694 { 3695 SalGestureLongPressEvent aEvent; 3696 aEvent.mnX = x; 3697 aEvent.mnY = y; 3698 3699 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3700 pThis->CallCallbackExc(SalEvent::GestureLongPress, &aEvent); 3701 } 3702 } 3703 3704 void GtkSalFrame::DrawingAreaMotion(int nEventX, int nEventY, guint32 nTime, guint nState) 3705 { 3706 UpdateLastInputEventTime(nTime); 3707 3708 SalMouseEvent aEvent; 3709 aEvent.mnTime = nTime; 3710 aEvent.mnX = nEventX; 3711 aEvent.mnY = nEventY; 3712 aEvent.mnCode = GetMouseModCode(nState); 3713 aEvent.mnButton = 0; 3714 3715 if( AllSettings::GetLayoutRTL() ) 3716 aEvent.mnX = maGeometry.width() - 1 - aEvent.mnX; 3717 3718 CallCallbackExc(SalEvent::MouseMove, &aEvent); 3719 } 3720 3721 #if GTK_CHECK_VERSION(4, 0, 0) 3722 void GtkSalFrame::signalMotion(GtkEventControllerMotion *pController, double x, double y, gpointer frame) 3723 { 3724 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3725 GdkEvent* pEvent = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController)); 3726 GdkModifierType eType = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pController)); 3727 pThis->DrawingAreaMotion(x, y, gdk_event_get_time(pEvent), eType); 3728 } 3729 #else 3730 gboolean GtkSalFrame::signalMotion( GtkWidget*, GdkEventMotion* pEvent, gpointer frame ) 3731 { 3732 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3733 GtkWidget* pEventWidget = pThis->getMouseEventWidget(); 3734 bool bDifferentEventWindow = pEvent->window != widget_get_surface(pEventWidget); 3735 3736 //If a menu, e.g. font name dropdown, is open, then under wayland moving the 3737 //mouse in the top left corner of the toplevel window in a 3738 //0,0,float-width,float-height area generates motion events which are 3739 //delivered to the dropdown 3740 if (pThis->isFloatGrabWindow() && bDifferentEventWindow) 3741 return true; 3742 3743 vcl::DeletionListener aDel( pThis ); 3744 3745 int nEventX = pEvent->x; 3746 int nEventY = pEvent->y; 3747 3748 if (bDifferentEventWindow) 3749 translate_coords(pEvent->window, pEventWidget, nEventX, nEventY); 3750 3751 pThis->UpdateGeometryFromEvent(pEvent->x_root, pEvent->y_root, nEventX, nEventY); 3752 3753 if (!aDel.isDeleted()) 3754 pThis->DrawingAreaMotion(nEventX, nEventY, pEvent->time, pEvent->state); 3755 3756 if (!aDel.isDeleted()) 3757 { 3758 // ask for the next hint 3759 gint x, y; 3760 GdkModifierType mask; 3761 gdk_window_get_pointer( widget_get_surface(GTK_WIDGET(pThis->m_pWindow)), &x, &y, &mask ); 3762 } 3763 3764 return true; 3765 } 3766 #endif 3767 3768 void GtkSalFrame::DrawingAreaCrossing(SalEvent nEventType, int nEventX, int nEventY, guint32 nTime, guint nState) 3769 { 3770 UpdateLastInputEventTime(nTime); 3771 3772 SalMouseEvent aEvent; 3773 aEvent.mnTime = nTime; 3774 aEvent.mnX = nEventX; 3775 aEvent.mnY = nEventY; 3776 aEvent.mnCode = GetMouseModCode(nState); 3777 aEvent.mnButton = 0; 3778 3779 if (AllSettings::GetLayoutRTL()) 3780 aEvent.mnX = maGeometry.width()-1-aEvent.mnX; 3781 3782 CallCallbackExc(nEventType, &aEvent); 3783 } 3784 3785 #if GTK_CHECK_VERSION(4, 0, 0) 3786 void GtkSalFrame::signalEnter(GtkEventControllerMotion *pController, double x, double y, gpointer frame) 3787 { 3788 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3789 GdkEvent* pEvent = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController)); 3790 GdkModifierType eType = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pController)); 3791 pThis->DrawingAreaCrossing(SalEvent::MouseMove, x, y, pEvent ? gdk_event_get_time(pEvent) : GDK_CURRENT_TIME, eType); 3792 } 3793 3794 void GtkSalFrame::signalLeave(GtkEventControllerMotion *pController, gpointer frame) 3795 { 3796 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3797 GdkEvent* pEvent = gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController)); 3798 GdkModifierType eType = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(pController)); 3799 pThis->DrawingAreaCrossing(SalEvent::MouseLeave, -1, -1, pEvent ? gdk_event_get_time(pEvent) : GDK_CURRENT_TIME, eType); 3800 } 3801 #else 3802 gboolean GtkSalFrame::signalCrossing( GtkWidget*, GdkEventCrossing* pEvent, gpointer frame ) 3803 { 3804 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3805 pThis->DrawingAreaCrossing((pEvent->type == GDK_ENTER_NOTIFY) ? SalEvent::MouseMove : SalEvent::MouseLeave, 3806 pEvent->x, 3807 pEvent->y, 3808 pEvent->time, 3809 pEvent->state); 3810 return true; 3811 } 3812 #endif 3813 3814 cairo_t* GtkSalFrame::getCairoContext() const 3815 { 3816 cairo_t* cr = cairo_create(m_pSurface); 3817 assert(cr); 3818 return cr; 3819 } 3820 3821 void GtkSalFrame::damaged(sal_Int32 nExtentsX, sal_Int32 nExtentsY, 3822 sal_Int32 nExtentsWidth, sal_Int32 nExtentsHeight) const 3823 { 3824 #if OSL_DEBUG_LEVEL > 0 3825 if (dumpframes) 3826 { 3827 static int frame; 3828 OString tmp("/tmp/frame" + OString::number(frame++) + ".png"); 3829 cairo_t* cr = getCairoContext(); 3830 cairo_surface_write_to_png(cairo_get_target(cr), tmp.getStr()); 3831 cairo_destroy(cr); 3832 } 3833 #endif 3834 3835 // quite a bit of noise in RTL mode with negative widths 3836 if (nExtentsWidth <= 0 || nExtentsHeight <= 0) 3837 return; 3838 3839 #if !GTK_CHECK_VERSION(4, 0, 0) 3840 gtk_widget_queue_draw_area(GTK_WIDGET(m_pDrawingArea), 3841 nExtentsX, nExtentsY, 3842 nExtentsWidth, nExtentsHeight); 3843 #else 3844 gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea)); 3845 (void)nExtentsX; 3846 (void)nExtentsY; 3847 #endif 3848 } 3849 3850 // blit our backing cairo surface to the target cairo context 3851 void GtkSalFrame::DrawingAreaDraw(cairo_t *cr) 3852 { 3853 cairo_set_source_surface(cr, m_pSurface, 0, 0); 3854 cairo_paint(cr); 3855 } 3856 3857 #if !GTK_CHECK_VERSION(4, 0, 0) 3858 gboolean GtkSalFrame::signalDraw(GtkWidget*, cairo_t *cr, gpointer frame) 3859 { 3860 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3861 pThis->DrawingAreaDraw(cr); 3862 return false; 3863 } 3864 #else 3865 void GtkSalFrame::signalDraw(GtkDrawingArea*, cairo_t *cr, int /*width*/, int /*height*/, gpointer frame) 3866 { 3867 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3868 pThis->DrawingAreaDraw(cr); 3869 } 3870 #endif 3871 3872 void GtkSalFrame::DrawingAreaResized(GtkWidget* pWidget, int nWidth, int nHeight) 3873 { 3874 // ignore size-allocations that occur during configuring an embedded SalObject 3875 if (m_bSalObjectSetPosSize) 3876 return; 3877 maGeometry.setSize({ nWidth, nHeight }); 3878 bool bRealized = gtk_widget_get_realized(pWidget); 3879 if (bRealized) 3880 AllocateFrame(); 3881 CallCallbackExc( SalEvent::Resize, nullptr ); 3882 if (bRealized) 3883 TriggerPaintEvent(); 3884 } 3885 3886 #if !GTK_CHECK_VERSION(4, 0, 0) 3887 void GtkSalFrame::sizeAllocated(GtkWidget* pWidget, GdkRectangle *pAllocation, gpointer frame) 3888 { 3889 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3890 pThis->DrawingAreaResized(pWidget, pAllocation->width, pAllocation->height); 3891 } 3892 #else 3893 void GtkSalFrame::sizeAllocated(GtkWidget* pWidget, int nWidth, int nHeight, gpointer frame) 3894 { 3895 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3896 pThis->DrawingAreaResized(pWidget, nWidth, nHeight); 3897 } 3898 #endif 3899 3900 #if !GTK_CHECK_VERSION(4, 0, 0) 3901 namespace { 3902 3903 void swapDirection(GdkGravity& gravity) 3904 { 3905 if (gravity == GDK_GRAVITY_NORTH_WEST) 3906 gravity = GDK_GRAVITY_NORTH_EAST; 3907 else if (gravity == GDK_GRAVITY_NORTH_EAST) 3908 gravity = GDK_GRAVITY_NORTH_WEST; 3909 else if (gravity == GDK_GRAVITY_SOUTH_WEST) 3910 gravity = GDK_GRAVITY_SOUTH_EAST; 3911 else if (gravity == GDK_GRAVITY_SOUTH_EAST) 3912 gravity = GDK_GRAVITY_SOUTH_WEST; 3913 } 3914 3915 } 3916 #endif 3917 3918 void GtkSalFrame::signalRealize(GtkWidget*, gpointer frame) 3919 { 3920 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3921 pThis->AllocateFrame(); 3922 if (pThis->m_bSalObjectSetPosSize) 3923 return; 3924 pThis->TriggerPaintEvent(); 3925 3926 #if !GTK_CHECK_VERSION(4, 0, 0) 3927 if (!pThis->m_bFloatPositioned) 3928 return; 3929 3930 static auto window_move_to_rect = reinterpret_cast<void (*) (GdkWindow*, const GdkRectangle*, GdkGravity, 3931 GdkGravity, GdkAnchorHints, gint, gint)>( 3932 dlsym(nullptr, "gdk_window_move_to_rect")); 3933 if (!window_move_to_rect) 3934 return; 3935 3936 GdkGravity rect_anchor = GDK_GRAVITY_SOUTH_WEST, menu_anchor = GDK_GRAVITY_NORTH_WEST; 3937 3938 if (pThis->m_nFloatFlags & FloatWinPopupFlags::Left) 3939 { 3940 rect_anchor = GDK_GRAVITY_NORTH_WEST; 3941 menu_anchor = GDK_GRAVITY_NORTH_EAST; 3942 } 3943 else if (pThis->m_nFloatFlags & FloatWinPopupFlags::Up) 3944 { 3945 rect_anchor = GDK_GRAVITY_NORTH_WEST; 3946 menu_anchor = GDK_GRAVITY_SOUTH_WEST; 3947 } 3948 else if (pThis->m_nFloatFlags & FloatWinPopupFlags::Right) 3949 { 3950 rect_anchor = GDK_GRAVITY_NORTH_EAST; 3951 } 3952 3953 VclPtr<vcl::Window> pVclParent = pThis->GetWindow()->GetParent(); 3954 if (pVclParent->GetOutDev()->HasMirroredGraphics() && pVclParent->IsRTLEnabled()) 3955 { 3956 swapDirection(rect_anchor); 3957 swapDirection(menu_anchor); 3958 } 3959 3960 AbsoluteScreenPixelRectangle aFloatRect = FloatingWindow::ImplConvertToAbsPos(pVclParent, pThis->m_aFloatRect); 3961 switch (gdk_window_get_window_type(widget_get_surface(pThis->m_pParent->m_pWindow))) 3962 { 3963 case GDK_WINDOW_TOPLEVEL: 3964 break; 3965 case GDK_WINDOW_CHILD: 3966 { 3967 // See tdf#152155 for an example 3968 gtk_coord nX(0), nY(0.0); 3969 gtk_widget_translate_coordinates(pThis->m_pParent->m_pWindow, widget_get_toplevel(pThis->m_pParent->m_pWindow), 0, 0, &nX, &nY); 3970 aFloatRect.Move(nX, nY); 3971 break; 3972 } 3973 default: 3974 { 3975 // See tdf#154072 for an example 3976 aFloatRect.Move(-pThis->m_pParent->maGeometry.x(), -pThis->m_pParent->maGeometry.y()); 3977 break; 3978 } 3979 } 3980 3981 GdkRectangle rect {static_cast<int>(aFloatRect.Left()), static_cast<int>(aFloatRect.Top()), 3982 static_cast<int>(aFloatRect.GetWidth()), static_cast<int>(aFloatRect.GetHeight())}; 3983 3984 GdkSurface* gdkWindow = widget_get_surface(pThis->m_pWindow); 3985 window_move_to_rect(gdkWindow, &rect, rect_anchor, menu_anchor, static_cast<GdkAnchorHints>(GDK_ANCHOR_FLIP | GDK_ANCHOR_SLIDE), 0, 0); 3986 #endif 3987 } 3988 3989 #if !GTK_CHECK_VERSION(4, 0, 0) 3990 gboolean GtkSalFrame::signalConfigure(GtkWidget*, GdkEventConfigure* pEvent, gpointer frame) 3991 { 3992 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 3993 3994 bool bMoved = false; 3995 int x = pEvent->x, y = pEvent->y; 3996 3997 /* #i31785# claims we cannot trust the x,y members of the event; 3998 * they are e.g. not set correctly on maximize/demaximize; 3999 * yet the gdkdisplay-x11.c code handling configure_events has 4000 * done this XTranslateCoordinates work since the day ~zero. 4001 */ 4002 if (pThis->m_bGeometryIsProvisional || x != pThis->maGeometry.x() || y != pThis->maGeometry.y() ) 4003 { 4004 bMoved = true; 4005 pThis->m_bGeometryIsProvisional = false; 4006 pThis->maGeometry.setPos({ x, y }); 4007 } 4008 4009 // update decoration hints 4010 GdkRectangle aRect; 4011 gdk_window_get_frame_extents( widget_get_surface(GTK_WIDGET(pThis->m_pWindow)), &aRect ); 4012 pThis->maGeometry.setTopDecoration(y - aRect.y); 4013 pThis->maGeometry.setBottomDecoration(aRect.y + aRect.height - y - pEvent->height); 4014 pThis->maGeometry.setLeftDecoration(x - aRect.x); 4015 pThis->maGeometry.setRightDecoration(aRect.x + aRect.width - x - pEvent->width); 4016 pThis->updateScreenNumber(); 4017 4018 if (bMoved) 4019 { 4020 ImplSVData* pSVData = ImplGetSVData(); 4021 if (pSVData->maNWFData.mbCanDetermineWindowPosition) 4022 pThis->CallCallbackExc(SalEvent::Move, nullptr); 4023 } 4024 4025 return false; 4026 } 4027 #endif 4028 4029 void GtkSalFrame::queue_draw() 4030 { 4031 gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea)); 4032 } 4033 4034 void GtkSalFrame::TriggerPaintEvent() 4035 { 4036 //Under gtk2 we can basically paint directly into the XWindow and on 4037 //additional "expose-event" events we can re-render the missing pieces 4038 // 4039 //Under gtk3 we have to keep our own buffer up to date and flush it into 4040 //the given cairo context on "draw". So we emit a paint event on 4041 //opportune resize trigger events to initially fill our backbuffer and then 4042 //keep it up to date with our direct paints and tell gtk those regions 4043 //have changed and then blit them into the provided cairo context when 4044 //we get the "draw" 4045 // 4046 //The other alternative was to always paint everything on "draw", but 4047 //that duplicates the amount of drawing and is hideously slow 4048 SAL_INFO("vcl.gtk3", "force painting" << 0 << "," << 0 << " " << maGeometry.width() << "x" << maGeometry.height()); 4049 SalPaintEvent aPaintEvt(0, 0, maGeometry.width(), maGeometry.height(), true); 4050 CallCallbackExc(SalEvent::Paint, &aPaintEvt); 4051 queue_draw(); 4052 } 4053 4054 void GtkSalFrame::DrawingAreaFocusInOut(SalEvent nEventType) 4055 { 4056 SalGenericInstance* pSalInstance = GetGenericInstance(); 4057 4058 // check if printers have changed (analogous to salframe focus handler) 4059 pSalInstance->updatePrinterUpdate(); 4060 4061 if (nEventType == SalEvent::LoseFocus) 4062 m_nKeyModifiers = ModKeyFlags::NONE; 4063 4064 if (m_pIMHandler) 4065 { 4066 bool bFocusInAnotherGtkWidget = false; 4067 if (GTK_IS_WINDOW(m_pWindow)) 4068 { 4069 GtkWidget* pFocusWindow = gtk_window_get_focus(GTK_WINDOW(m_pWindow)); 4070 bFocusInAnotherGtkWidget = pFocusWindow && pFocusWindow != GTK_WIDGET(m_pFixedContainer); 4071 } 4072 if (!bFocusInAnotherGtkWidget) 4073 m_pIMHandler->focusChanged(nEventType == SalEvent::GetFocus); 4074 } 4075 4076 // ask for changed printers like generic implementation 4077 if (nEventType == SalEvent::GetFocus && pSalInstance->isPrinterInit()) 4078 pSalInstance->updatePrinterUpdate(); 4079 4080 CallCallbackExc(nEventType, nullptr); 4081 } 4082 4083 #if !GTK_CHECK_VERSION(4, 0, 0) 4084 gboolean GtkSalFrame::signalFocus( GtkWidget*, GdkEventFocus* pEvent, gpointer frame ) 4085 { 4086 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4087 4088 SalGenericInstance *pSalInstance = GetGenericInstance(); 4089 4090 // check if printers have changed (analogous to salframe focus handler) 4091 pSalInstance->updatePrinterUpdate(); 4092 4093 if( !pEvent->in ) 4094 pThis->m_nKeyModifiers = ModKeyFlags::NONE; 4095 4096 if( pThis->m_pIMHandler ) 4097 { 4098 bool bFocusInAnotherGtkWidget = false; 4099 if (GTK_IS_WINDOW(pThis->m_pWindow)) 4100 { 4101 GtkWidget* pFocusWindow = gtk_window_get_focus(GTK_WINDOW(pThis->m_pWindow)); 4102 bFocusInAnotherGtkWidget = pFocusWindow && pFocusWindow != GTK_WIDGET(pThis->m_pFixedContainer); 4103 } 4104 if (!bFocusInAnotherGtkWidget) 4105 pThis->m_pIMHandler->focusChanged( pEvent->in != 0 ); 4106 } 4107 4108 // ask for changed printers like generic implementation 4109 if( pEvent->in && pSalInstance->isPrinterInit() ) 4110 pSalInstance->updatePrinterUpdate(); 4111 4112 // FIXME: find out who the hell steals the focus from our frame 4113 // while we have the pointer grabbed, this should not come from 4114 // the window manager. Is this an event that was still queued ? 4115 // The focus does not seem to get set inside our process 4116 // in the meantime do not propagate focus get/lose if floats are open 4117 if( m_nFloats == 0 ) 4118 { 4119 GtkWidget* pGrabWidget; 4120 if (GTK_IS_EVENT_BOX(pThis->m_pWindow)) 4121 pGrabWidget = GTK_WIDGET(pThis->m_pWindow); 4122 else 4123 pGrabWidget = GTK_WIDGET(pThis->m_pFixedContainer); 4124 bool bHasFocus = gtk_widget_has_focus(pGrabWidget); 4125 pThis->CallCallbackExc(bHasFocus ? SalEvent::GetFocus : SalEvent::LoseFocus, nullptr); 4126 } 4127 4128 return false; 4129 } 4130 #else 4131 void GtkSalFrame::signalFocusEnter(GtkEventControllerFocus*, gpointer frame) 4132 { 4133 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4134 pThis->DrawingAreaFocusInOut(SalEvent::GetFocus); 4135 } 4136 4137 void GtkSalFrame::signalFocusLeave(GtkEventControllerFocus*, gpointer frame) 4138 { 4139 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4140 pThis->DrawingAreaFocusInOut(SalEvent::LoseFocus); 4141 } 4142 #endif 4143 4144 // change of focus between native widgets within the toplevel 4145 #if !GTK_CHECK_VERSION(4, 0, 0) 4146 void GtkSalFrame::signalSetFocus(GtkWindow*, GtkWidget* pWidget, gpointer frame) 4147 #else 4148 void GtkSalFrame::signalSetFocus(GtkWindow*, GParamSpec*, gpointer frame) 4149 #endif 4150 { 4151 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4152 4153 GtkWidget* pGrabWidget = GTK_WIDGET(pThis->m_pFixedContainer); 4154 4155 GtkWidget* pTopLevel = widget_get_toplevel(pGrabWidget); 4156 // see commentary in GtkSalObjectWidgetClip::Show 4157 if (pTopLevel && g_object_get_data(G_OBJECT(pTopLevel), "g-lo-BlockFocusChange")) 4158 return; 4159 4160 #if GTK_CHECK_VERSION(4, 0, 0) 4161 GtkWidget* pWidget = gtk_window_get_focus(GTK_WINDOW(pThis->m_pWindow)); 4162 #endif 4163 4164 // tdf#129634 interpret losing focus as focus passing explicitly to another widget 4165 bool bLoseFocus = pWidget && pWidget != pGrabWidget; 4166 4167 // do not propagate focus get/lose if floats are open 4168 pThis->CallCallbackExc(bLoseFocus ? SalEvent::LoseFocus : SalEvent::GetFocus, nullptr); 4169 4170 #if !GTK_CHECK_VERSION(4, 0, 0) 4171 gtk_widget_set_can_focus(GTK_WIDGET(pThis->m_pFixedContainer), !bLoseFocus); 4172 #endif 4173 } 4174 4175 void GtkSalFrame::WindowMap() 4176 { 4177 if (m_bIconSetWhileUnmapped) 4178 SetIcon(gtk_window_get_icon_name(GTK_WINDOW(m_pWindow))); 4179 4180 CallCallbackExc( SalEvent::Resize, nullptr ); 4181 TriggerPaintEvent(); 4182 } 4183 4184 void GtkSalFrame::WindowUnmap() 4185 { 4186 CallCallbackExc( SalEvent::Resize, nullptr ); 4187 4188 if (m_bFloatPositioned) 4189 { 4190 // Unrealize is needed for cases where we reuse the same popup 4191 // (e.g. the font name control), making the realize signal fire 4192 // again on next show. 4193 gtk_widget_unrealize(m_pWindow); 4194 m_bFloatPositioned = false; 4195 } 4196 } 4197 4198 #if GTK_CHECK_VERSION(4, 0, 0) 4199 void GtkSalFrame::signalMap(GtkWidget*, gpointer frame) 4200 { 4201 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4202 pThis->WindowMap(); 4203 } 4204 4205 void GtkSalFrame::signalUnmap(GtkWidget*, gpointer frame) 4206 { 4207 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4208 pThis->WindowUnmap(); 4209 } 4210 #else 4211 gboolean GtkSalFrame::signalMap(GtkWidget*, GdkEvent*, gpointer frame) 4212 { 4213 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4214 pThis->WindowMap(); 4215 return false; 4216 } 4217 4218 gboolean GtkSalFrame::signalUnmap(GtkWidget*, GdkEvent*, gpointer frame) 4219 { 4220 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4221 pThis->WindowUnmap(); 4222 return false; 4223 } 4224 #endif 4225 4226 #if !GTK_CHECK_VERSION(4, 0, 0) 4227 4228 static bool key_forward(GdkEventKey* pEvent, GtkWindow* pDest) 4229 { 4230 gpointer pClass = g_type_class_ref(GTK_TYPE_WINDOW); 4231 GtkWidgetClass* pWindowClass = GTK_WIDGET_CLASS(pClass); 4232 bool bHandled = pEvent->type == GDK_KEY_PRESS 4233 ? pWindowClass->key_press_event(GTK_WIDGET(pDest), pEvent) 4234 : pWindowClass->key_release_event(GTK_WIDGET(pDest), pEvent); 4235 g_type_class_unref(pClass); 4236 return bHandled; 4237 } 4238 4239 static bool activate_menubar_mnemonic(GtkWidget* pWidget, guint nKeyval) 4240 { 4241 const char* pLabel = gtk_menu_item_get_label(GTK_MENU_ITEM(pWidget)); 4242 gunichar cAccelChar = 0; 4243 if (!pango_parse_markup(pLabel, -1, '_', nullptr, nullptr, &cAccelChar, nullptr)) 4244 return false; 4245 if (!cAccelChar) 4246 return false; 4247 auto nMnemonicKeyval = gdk_keyval_to_lower(gdk_unicode_to_keyval(cAccelChar)); 4248 if (nKeyval == nMnemonicKeyval) 4249 return gtk_widget_mnemonic_activate(pWidget, false); 4250 return false; 4251 } 4252 4253 bool GtkSalFrame::HandleMenubarMnemonic(guint eState, guint nKeyval) 4254 { 4255 bool bUsedInMenuBar = false; 4256 if (eState & GDK_ALT_MASK) 4257 { 4258 if (GtkWidget* pMenuBar = m_pSalMenu ? m_pSalMenu->GetMenuBarWidget() : nullptr) 4259 { 4260 GList* pChildren = gtk_container_get_children(GTK_CONTAINER(pMenuBar)); 4261 for (GList* pChild = g_list_first(pChildren); pChild; pChild = g_list_next(pChild)) 4262 { 4263 bUsedInMenuBar = activate_menubar_mnemonic(static_cast<GtkWidget*>(pChild->data), nKeyval); 4264 if (bUsedInMenuBar) 4265 break; 4266 } 4267 g_list_free(pChildren); 4268 } 4269 } 4270 return bUsedInMenuBar; 4271 } 4272 4273 gboolean GtkSalFrame::signalKey(GtkWidget* pWidget, GdkEventKey* pEvent, gpointer frame) 4274 { 4275 UpdateLastInputEventTime(pEvent->time); 4276 4277 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4278 4279 bool bFocusInAnotherGtkWidget = false; 4280 4281 VclPtr<vcl::Window> xTopLevelInterimWindow; 4282 4283 if (GTK_IS_WINDOW(pThis->m_pWindow)) 4284 { 4285 GtkWidget* pFocusWindow = gtk_window_get_focus(GTK_WINDOW(pThis->m_pWindow)); 4286 bFocusInAnotherGtkWidget = pFocusWindow && pFocusWindow != GTK_WIDGET(pThis->m_pFixedContainer); 4287 if (bFocusInAnotherGtkWidget) 4288 { 4289 if (!gtk_widget_get_realized(pFocusWindow)) 4290 return true; 4291 4292 // if the focus is not in our main widget, see if there is a handler 4293 // for this key stroke in GtkWindow first 4294 if (key_forward(pEvent, GTK_WINDOW(pThis->m_pWindow))) 4295 return true; 4296 4297 // Is focus inside an InterimItemWindow? In which case find that 4298 // InterimItemWindow and send unconsumed keystrokes to it to 4299 // support ctrl-q etc shortcuts. Only bother to search for the 4300 // InterimItemWindow if it is a toplevel that fills its frame, or 4301 // the keystroke is sufficiently special its worth passing on, 4302 // e.g. F6 to switch between task-panels or F5 to close a navigator 4303 if (pThis->IsCycleFocusOutDisallowed() || IsFunctionKeyVal(pEvent->keyval)) 4304 { 4305 GtkWidget* pSearch = pFocusWindow; 4306 while (pSearch) 4307 { 4308 void* pData = g_object_get_data(G_OBJECT(pSearch), "InterimWindowGlue"); 4309 if (pData) 4310 { 4311 xTopLevelInterimWindow = static_cast<vcl::Window*>(pData); 4312 break; 4313 } 4314 pSearch = gtk_widget_get_parent(pSearch); 4315 } 4316 } 4317 } 4318 } 4319 4320 if (pThis->isFloatGrabWindow()) 4321 return signalKey(pWidget, pEvent, pThis->m_pParent); 4322 4323 vcl::DeletionListener aDel( pThis ); 4324 4325 if (!bFocusInAnotherGtkWidget && pThis->m_pIMHandler && pThis->m_pIMHandler->handleKeyEvent(pEvent)) 4326 return true; 4327 4328 bool bStopProcessingKey = false; 4329 4330 // handle modifiers 4331 if( pEvent->keyval == GDK_KEY_Shift_L || pEvent->keyval == GDK_KEY_Shift_R || 4332 pEvent->keyval == GDK_KEY_Control_L || pEvent->keyval == GDK_KEY_Control_R || 4333 pEvent->keyval == GDK_KEY_Alt_L || pEvent->keyval == GDK_KEY_Alt_R || 4334 pEvent->keyval == GDK_KEY_Meta_L || pEvent->keyval == GDK_KEY_Meta_R || 4335 pEvent->keyval == GDK_KEY_Super_L || pEvent->keyval == GDK_KEY_Super_R ) 4336 { 4337 sal_uInt16 nModCode = GetKeyModCode( pEvent->state ); 4338 ModKeyFlags nExtModMask = ModKeyFlags::NONE; 4339 sal_uInt16 nModMask = 0; 4340 // pressing just the ctrl key leads to a keysym of XK_Control but 4341 // the event state does not contain ControlMask. In the release 4342 // event it's the other way round: it does contain the Control mask. 4343 // The modifier mode therefore has to be adapted manually. 4344 switch( pEvent->keyval ) 4345 { 4346 case GDK_KEY_Control_L: 4347 nExtModMask = ModKeyFlags::LeftMod1; 4348 nModMask = KEY_MOD1; 4349 break; 4350 case GDK_KEY_Control_R: 4351 nExtModMask = ModKeyFlags::RightMod1; 4352 nModMask = KEY_MOD1; 4353 break; 4354 case GDK_KEY_Alt_L: 4355 nExtModMask = ModKeyFlags::LeftMod2; 4356 nModMask = KEY_MOD2; 4357 break; 4358 case GDK_KEY_Alt_R: 4359 nExtModMask = ModKeyFlags::RightMod2; 4360 nModMask = KEY_MOD2; 4361 break; 4362 case GDK_KEY_Shift_L: 4363 nExtModMask = ModKeyFlags::LeftShift; 4364 nModMask = KEY_SHIFT; 4365 break; 4366 case GDK_KEY_Shift_R: 4367 nExtModMask = ModKeyFlags::RightShift; 4368 nModMask = KEY_SHIFT; 4369 break; 4370 // Map Meta/Super to MOD3 modifier on all Unix systems 4371 // except macOS 4372 case GDK_KEY_Meta_L: 4373 case GDK_KEY_Super_L: 4374 nExtModMask = ModKeyFlags::LeftMod3; 4375 nModMask = KEY_MOD3; 4376 break; 4377 case GDK_KEY_Meta_R: 4378 case GDK_KEY_Super_R: 4379 nExtModMask = ModKeyFlags::RightMod3; 4380 nModMask = KEY_MOD3; 4381 break; 4382 } 4383 4384 SalKeyModEvent aModEvt; 4385 aModEvt.mbDown = pEvent->type == GDK_KEY_PRESS; 4386 4387 if( pEvent->type == GDK_KEY_RELEASE ) 4388 { 4389 aModEvt.mnModKeyCode = pThis->m_nKeyModifiers; 4390 aModEvt.mnCode = nModCode & ~nModMask; 4391 pThis->m_nKeyModifiers &= ~nExtModMask; 4392 } 4393 else 4394 { 4395 aModEvt.mnCode = nModCode | nModMask; 4396 pThis->m_nKeyModifiers |= nExtModMask; 4397 aModEvt.mnModKeyCode = pThis->m_nKeyModifiers; 4398 } 4399 4400 pThis->CallCallbackExc( SalEvent::KeyModChange, &aModEvt ); 4401 } 4402 else 4403 { 4404 bool bRestoreDisallowCycleFocusOut = false; 4405 4406 VclPtr<vcl::Window> xOrigFrameFocusWin; 4407 VclPtr<vcl::Window> xOrigFocusWin; 4408 if (xTopLevelInterimWindow) 4409 { 4410 // Focus is inside an InterimItemWindow so send unconsumed 4411 // keystrokes to by setting it as the mpFocusWin 4412 VclPtr<vcl::Window> xVclWindow = pThis->GetWindow(); 4413 ImplFrameData* pFrameData = xVclWindow->ImplGetWindowImpl()->mpFrameData; 4414 xOrigFrameFocusWin = pFrameData->mpFocusWin; 4415 pFrameData->mpFocusWin = xTopLevelInterimWindow; 4416 4417 ImplSVData* pSVData = ImplGetSVData(); 4418 xOrigFocusWin = pSVData->mpWinData->mpFocusWin; 4419 pSVData->mpWinData->mpFocusWin = xTopLevelInterimWindow; 4420 4421 if (pEvent->keyval == GDK_KEY_F6 && pThis->IsCycleFocusOutDisallowed()) 4422 { 4423 // For F6, allow the focus to leave the InterimItemWindow 4424 pThis->AllowCycleFocusOut(); 4425 bRestoreDisallowCycleFocusOut = true; 4426 } 4427 } 4428 4429 bStopProcessingKey = pThis->doKeyCallback(pEvent->state, 4430 pEvent->keyval, 4431 pEvent->hardware_keycode, 4432 pEvent->group, 4433 sal_Unicode(gdk_keyval_to_unicode( pEvent->keyval )), 4434 (pEvent->type == GDK_KEY_PRESS), 4435 false); 4436 4437 // tdf#144846 If this is registered as a menubar mnemonic then ensure 4438 // that any other widget won't be considered as a candidate by taking 4439 // over the task of launch the menubar menu outself 4440 // The code was moved here from its original position at beginning 4441 // of this function in order to resolve tdf#146174. 4442 if (!bStopProcessingKey && // module key handler did not process key 4443 pEvent->type == GDK_KEY_PRESS && // module key handler handles only GDK_KEY_PRESS 4444 GTK_IS_WINDOW(pThis->m_pWindow) && 4445 pThis->HandleMenubarMnemonic(pEvent->state, pEvent->keyval)) 4446 { 4447 return true; 4448 } 4449 4450 if (!aDel.isDeleted()) 4451 { 4452 pThis->m_nKeyModifiers = ModKeyFlags::NONE; 4453 4454 if (xTopLevelInterimWindow) 4455 { 4456 // Focus was inside an InterimItemWindow, restore the original 4457 // focus win, unless the focus was changed away from the 4458 // InterimItemWindow which should only be possible with F6 4459 VclPtr<vcl::Window> xVclWindow = pThis->GetWindow(); 4460 ImplFrameData* pFrameData = xVclWindow->ImplGetWindowImpl()->mpFrameData; 4461 if (pFrameData->mpFocusWin == xTopLevelInterimWindow) 4462 pFrameData->mpFocusWin = xOrigFrameFocusWin; 4463 4464 ImplSVData* pSVData = ImplGetSVData(); 4465 if (pSVData->mpWinData->mpFocusWin == xTopLevelInterimWindow) 4466 pSVData->mpWinData->mpFocusWin = xOrigFocusWin; 4467 4468 if (bRestoreDisallowCycleFocusOut) 4469 { 4470 // undo the above AllowCycleFocusOut for F6 4471 pThis->DisallowCycleFocusOut(); 4472 } 4473 } 4474 } 4475 4476 } 4477 4478 if (!bFocusInAnotherGtkWidget && !aDel.isDeleted() && pThis->m_pIMHandler) 4479 pThis->m_pIMHandler->updateIMSpotLocation(); 4480 4481 return bStopProcessingKey; 4482 } 4483 #else 4484 4485 bool GtkSalFrame::DrawingAreaKey(GtkEventControllerKey* pController, SalEvent nEventType, guint keyval, guint keycode, guint state) 4486 { 4487 guint32 nTime = gdk_event_get_time(gtk_event_controller_get_current_event(GTK_EVENT_CONTROLLER(pController))); 4488 UpdateLastInputEventTime(nTime); 4489 4490 bool bFocusInAnotherGtkWidget = false; 4491 4492 VclPtr<vcl::Window> xTopLevelInterimWindow; 4493 4494 if (GTK_IS_WINDOW(m_pWindow)) 4495 { 4496 GtkWidget* pFocusWindow = gtk_window_get_focus(GTK_WINDOW(m_pWindow)); 4497 bFocusInAnotherGtkWidget = pFocusWindow && pFocusWindow != GTK_WIDGET(m_pFixedContainer); 4498 if (bFocusInAnotherGtkWidget) 4499 { 4500 if (!gtk_widget_get_realized(pFocusWindow)) 4501 return true; 4502 // if the focus is not in our main widget, see if there is a handler 4503 // for this key stroke in GtkWindow first 4504 bool bHandled = gtk_event_controller_key_forward(pController, m_pWindow); 4505 if (bHandled) 4506 return true; 4507 4508 // Is focus inside an InterimItemWindow? In which case find that 4509 // InterimItemWindow and send unconsumed keystrokes to it to 4510 // support ctrl-q etc shortcuts. Only bother to search for the 4511 // InterimItemWindow if it is a toplevel that fills its frame, or 4512 // the keystroke is sufficiently special its worth passing on, 4513 // e.g. F6 to switch between task-panels or F5 to close a navigator 4514 if (IsCycleFocusOutDisallowed() || IsFunctionKeyVal(keyval)) 4515 { 4516 GtkWidget* pSearch = pFocusWindow; 4517 while (pSearch) 4518 { 4519 void* pData = g_object_get_data(G_OBJECT(pSearch), "InterimWindowGlue"); 4520 if (pData) 4521 { 4522 xTopLevelInterimWindow = static_cast<vcl::Window*>(pData); 4523 break; 4524 } 4525 pSearch = gtk_widget_get_parent(pSearch); 4526 } 4527 } 4528 } 4529 } 4530 4531 vcl::DeletionListener aDel(this); 4532 4533 bool bStopProcessingKey = false; 4534 4535 // handle modifiers 4536 if( keyval == GDK_KEY_Shift_L || keyval == GDK_KEY_Shift_R || 4537 keyval == GDK_KEY_Control_L || keyval == GDK_KEY_Control_R || 4538 keyval == GDK_KEY_Alt_L || keyval == GDK_KEY_Alt_R || 4539 keyval == GDK_KEY_Meta_L || keyval == GDK_KEY_Meta_R || 4540 keyval == GDK_KEY_Super_L || keyval == GDK_KEY_Super_R ) 4541 { 4542 sal_uInt16 nModCode = GetKeyModCode(state); 4543 ModKeyFlags nExtModMask = ModKeyFlags::NONE; 4544 sal_uInt16 nModMask = 0; 4545 // pressing just the ctrl key leads to a keysym of XK_Control but 4546 // the event state does not contain ControlMask. In the release 4547 // event it's the other way round: it does contain the Control mask. 4548 // The modifier mode therefore has to be adapted manually. 4549 switch (keyval) 4550 { 4551 case GDK_KEY_Control_L: 4552 nExtModMask = ModKeyFlags::LeftMod1; 4553 nModMask = KEY_MOD1; 4554 break; 4555 case GDK_KEY_Control_R: 4556 nExtModMask = ModKeyFlags::RightMod1; 4557 nModMask = KEY_MOD1; 4558 break; 4559 case GDK_KEY_Alt_L: 4560 nExtModMask = ModKeyFlags::LeftMod2; 4561 nModMask = KEY_MOD2; 4562 break; 4563 case GDK_KEY_Alt_R: 4564 nExtModMask = ModKeyFlags::RightMod2; 4565 nModMask = KEY_MOD2; 4566 break; 4567 case GDK_KEY_Shift_L: 4568 nExtModMask = ModKeyFlags::LeftShift; 4569 nModMask = KEY_SHIFT; 4570 break; 4571 case GDK_KEY_Shift_R: 4572 nExtModMask = ModKeyFlags::RightShift; 4573 nModMask = KEY_SHIFT; 4574 break; 4575 // Map Meta/Super to MOD3 modifier on all Unix systems 4576 // except macOS 4577 case GDK_KEY_Meta_L: 4578 case GDK_KEY_Super_L: 4579 nExtModMask = ModKeyFlags::LeftMod3; 4580 nModMask = KEY_MOD3; 4581 break; 4582 case GDK_KEY_Meta_R: 4583 case GDK_KEY_Super_R: 4584 nExtModMask = ModKeyFlags::RightMod3; 4585 nModMask = KEY_MOD3; 4586 break; 4587 } 4588 4589 SalKeyModEvent aModEvt; 4590 aModEvt.mbDown = nEventType == SalEvent::KeyInput; 4591 4592 if (!aModEvt.mbDown) 4593 { 4594 aModEvt.mnModKeyCode = m_nKeyModifiers; 4595 aModEvt.mnCode = nModCode & ~nModMask; 4596 m_nKeyModifiers &= ~nExtModMask; 4597 } 4598 else 4599 { 4600 aModEvt.mnCode = nModCode | nModMask; 4601 m_nKeyModifiers |= nExtModMask; 4602 aModEvt.mnModKeyCode = m_nKeyModifiers; 4603 } 4604 4605 CallCallbackExc(SalEvent::KeyModChange, &aModEvt); 4606 } 4607 else 4608 { 4609 bool bRestoreDisallowCycleFocusOut = false; 4610 4611 VclPtr<vcl::Window> xOrigFrameFocusWin; 4612 VclPtr<vcl::Window> xOrigFocusWin; 4613 if (xTopLevelInterimWindow) 4614 { 4615 // Focus is inside an InterimItemWindow so send unconsumed 4616 // keystrokes to by setting it as the mpFocusWin 4617 VclPtr<vcl::Window> xVclWindow = GetWindow(); 4618 ImplFrameData* pFrameData = xVclWindow->ImplGetWindowImpl()->mpFrameData; 4619 xOrigFrameFocusWin = pFrameData->mpFocusWin; 4620 pFrameData->mpFocusWin = xTopLevelInterimWindow; 4621 4622 ImplSVData* pSVData = ImplGetSVData(); 4623 xOrigFocusWin = pSVData->mpWinData->mpFocusWin; 4624 pSVData->mpWinData->mpFocusWin = xTopLevelInterimWindow; 4625 4626 if (keyval == GDK_KEY_F6 && IsCycleFocusOutDisallowed()) 4627 { 4628 // For F6, allow the focus to leave the InterimItemWindow 4629 AllowCycleFocusOut(); 4630 bRestoreDisallowCycleFocusOut = true; 4631 } 4632 } 4633 4634 4635 bStopProcessingKey = doKeyCallback(state, 4636 keyval, 4637 keycode, 4638 0, // group 4639 sal_Unicode(gdk_keyval_to_unicode(keyval)), 4640 nEventType == SalEvent::KeyInput, 4641 false); 4642 4643 if (!aDel.isDeleted()) 4644 { 4645 m_nKeyModifiers = ModKeyFlags::NONE; 4646 4647 if (xTopLevelInterimWindow) 4648 { 4649 // Focus was inside an InterimItemWindow, restore the original 4650 // focus win, unless the focus was changed away from the 4651 // InterimItemWindow which should only be possible with F6 4652 VclPtr<vcl::Window> xVclWindow = GetWindow(); 4653 ImplFrameData* pFrameData = xVclWindow->ImplGetWindowImpl()->mpFrameData; 4654 if (pFrameData->mpFocusWin == xTopLevelInterimWindow) 4655 pFrameData->mpFocusWin = xOrigFrameFocusWin; 4656 4657 ImplSVData* pSVData = ImplGetSVData(); 4658 if (pSVData->mpWinData->mpFocusWin == xTopLevelInterimWindow) 4659 pSVData->mpWinData->mpFocusWin = xOrigFocusWin; 4660 4661 if (bRestoreDisallowCycleFocusOut) 4662 { 4663 // undo the above AllowCycleFocusOut for F6 4664 DisallowCycleFocusOut(); 4665 } 4666 } 4667 } 4668 } 4669 4670 if (m_pIMHandler) 4671 m_pIMHandler->updateIMSpotLocation(); 4672 4673 return bStopProcessingKey; 4674 } 4675 4676 gboolean GtkSalFrame::signalKeyPressed(GtkEventControllerKey* pController, guint keyval, guint keycode, GdkModifierType state, gpointer frame) 4677 { 4678 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4679 return pThis->DrawingAreaKey(pController, SalEvent::KeyInput, keyval, keycode, state); 4680 } 4681 4682 gboolean GtkSalFrame::signalKeyReleased(GtkEventControllerKey* pController, guint keyval, guint keycode, GdkModifierType state, gpointer frame) 4683 { 4684 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4685 return pThis->DrawingAreaKey(pController, SalEvent::KeyUp, keyval, keycode, state); 4686 } 4687 #endif 4688 4689 bool GtkSalFrame::WindowCloseRequest() 4690 { 4691 CallCallbackExc(SalEvent::Close, nullptr); 4692 return true; 4693 } 4694 4695 #if GTK_CHECK_VERSION(4, 0, 0) 4696 gboolean GtkSalFrame::signalDelete(GtkWidget*, gpointer frame) 4697 { 4698 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4699 return pThis->WindowCloseRequest(); 4700 } 4701 #else 4702 gboolean GtkSalFrame::signalDelete(GtkWidget*, GdkEvent*, gpointer frame) 4703 { 4704 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4705 return pThis->WindowCloseRequest(); 4706 } 4707 #endif 4708 4709 const cairo_font_options_t* GtkSalFrame::get_font_options() 4710 { 4711 GtkWidget* pWidget = getMouseEventWidget(); 4712 #if GTK_CHECK_VERSION(4, 0, 0) 4713 PangoContext* pContext = gtk_widget_get_pango_context(pWidget); 4714 assert(pContext); 4715 return pango_cairo_context_get_font_options(pContext); 4716 #else 4717 return gdk_screen_get_font_options(gtk_widget_get_screen(pWidget)); 4718 #endif 4719 } 4720 4721 #if GTK_CHECK_VERSION(4, 0, 0) 4722 void GtkSalFrame::signalStyleUpdated(GtkWidget*, const gchar* /*pSetting*/, gpointer frame) 4723 #else 4724 void GtkSalFrame::signalStyleUpdated(GtkWidget*, gpointer frame) 4725 #endif 4726 { 4727 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4728 4729 // note: settings changed for multiple frames is avoided in winproc.cxx ImplHandleSettings 4730 GtkSalFrame::getDisplay()->SendInternalEvent( pThis, nullptr, SalEvent::SettingsChanged ); 4731 4732 // a plausible alternative might be to send SalEvent::FontChanged if pSetting starts with "gtk-xft" 4733 4734 // fire off font-changed when the system cairo font hints change 4735 GtkInstance *pInstance = GetGtkInstance(); 4736 const cairo_font_options_t* pLastCairoFontOptions = pInstance->GetLastSeenCairoFontOptions(); 4737 const cairo_font_options_t* pCurrentCairoFontOptions = pThis->get_font_options(); 4738 bool bFontSettingsChanged = true; 4739 if (pLastCairoFontOptions && pCurrentCairoFontOptions) 4740 bFontSettingsChanged = !cairo_font_options_equal(pLastCairoFontOptions, pCurrentCairoFontOptions); 4741 else if (!pLastCairoFontOptions && !pCurrentCairoFontOptions) 4742 bFontSettingsChanged = false; 4743 if (bFontSettingsChanged) 4744 { 4745 pInstance->ResetLastSeenCairoFontOptions(pCurrentCairoFontOptions); 4746 GtkSalFrame::getDisplay()->SendInternalEvent( pThis, nullptr, SalEvent::FontChanged ); 4747 } 4748 } 4749 4750 #if !GTK_CHECK_VERSION(4, 0, 0) 4751 gboolean GtkSalFrame::signalWindowState( GtkWidget*, GdkEvent* pEvent, gpointer frame ) 4752 { 4753 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4754 if( (pThis->m_nState & GDK_TOPLEVEL_STATE_MINIMIZED) != (pEvent->window_state.new_window_state & GDK_TOPLEVEL_STATE_MINIMIZED) ) 4755 { 4756 GtkSalFrame::getDisplay()->SendInternalEvent( pThis, nullptr, SalEvent::Resize ); 4757 pThis->TriggerPaintEvent(); 4758 } 4759 4760 if ((pEvent->window_state.new_window_state & GDK_TOPLEVEL_STATE_MAXIMIZED) && 4761 !(pThis->m_nState & GDK_TOPLEVEL_STATE_MAXIMIZED)) 4762 { 4763 pThis->m_aRestorePosSize = GetPosAndSize(GTK_WINDOW(pThis->m_pWindow)); 4764 } 4765 4766 if ((pEvent->window_state.new_window_state & GDK_WINDOW_STATE_WITHDRAWN) && 4767 !(pThis->m_nState & GDK_WINDOW_STATE_WITHDRAWN)) 4768 { 4769 if (pThis->isFloatGrabWindow()) 4770 pThis->closePopup(); 4771 } 4772 4773 pThis->m_nState = pEvent->window_state.new_window_state; 4774 4775 return false; 4776 } 4777 #else 4778 void GtkSalFrame::signalWindowState(GdkToplevel* pSurface, GParamSpec*, gpointer frame) 4779 { 4780 GdkToplevelState eNewWindowState = gdk_toplevel_get_state(pSurface); 4781 4782 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4783 if( (pThis->m_nState & GDK_TOPLEVEL_STATE_MINIMIZED) != (eNewWindowState & GDK_TOPLEVEL_STATE_MINIMIZED) ) 4784 { 4785 GtkSalFrame::getDisplay()->SendInternalEvent( pThis, nullptr, SalEvent::Resize ); 4786 pThis->TriggerPaintEvent(); 4787 } 4788 4789 if ((eNewWindowState & GDK_TOPLEVEL_STATE_MAXIMIZED) && 4790 !(pThis->m_nState & GDK_TOPLEVEL_STATE_MAXIMIZED)) 4791 { 4792 pThis->m_aRestorePosSize = GetPosAndSize(GTK_WINDOW(pThis->m_pWindow)); 4793 } 4794 4795 pThis->m_nState = eNewWindowState; 4796 } 4797 #endif 4798 4799 namespace 4800 { 4801 bool handleSignalZoom(GtkGesture* gesture, GdkEventSequence* sequence, gpointer frame, 4802 GestureEventZoomType eEventType) 4803 { 4804 gdouble x = 0; 4805 gdouble y = 0; 4806 gtk_gesture_get_point(gesture, sequence, &x, &y); 4807 4808 SalGestureZoomEvent aEvent; 4809 aEvent.meEventType = eEventType; 4810 aEvent.mnX = x; 4811 aEvent.mnY = y; 4812 aEvent.mfScaleDelta = gtk_gesture_zoom_get_scale_delta(GTK_GESTURE_ZOOM(gesture)); 4813 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4814 pThis->CallCallbackExc(SalEvent::GestureZoom, &aEvent); 4815 return true; 4816 } 4817 4818 bool handleSignalRotate(GtkGesture* gesture, GdkEventSequence* sequence, gpointer frame, 4819 GestureEventRotateType eEventType) 4820 { 4821 gdouble x = 0; 4822 gdouble y = 0; 4823 gtk_gesture_get_point(gesture, sequence, &x, &y); 4824 4825 SalGestureRotateEvent aEvent; 4826 aEvent.meEventType = eEventType; 4827 aEvent.mnX = x; 4828 aEvent.mnY = y; 4829 aEvent.mfAngleDelta = gtk_gesture_rotate_get_angle_delta(GTK_GESTURE_ROTATE(gesture)); 4830 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 4831 pThis->CallCallbackExc(SalEvent::GestureRotate, &aEvent); 4832 return true; 4833 } 4834 } 4835 4836 bool GtkSalFrame::signalZoomBegin(GtkGesture* gesture, GdkEventSequence* sequence, gpointer frame) 4837 { 4838 return handleSignalZoom(gesture, sequence, frame, GestureEventZoomType::Begin); 4839 } 4840 4841 bool GtkSalFrame::signalZoomUpdate(GtkGesture* gesture, GdkEventSequence* sequence, gpointer frame) 4842 { 4843 return handleSignalZoom(gesture, sequence, frame, GestureEventZoomType::Update); 4844 } 4845 4846 bool GtkSalFrame::signalZoomEnd(GtkGesture* gesture, GdkEventSequence* sequence, gpointer frame) 4847 { 4848 return handleSignalZoom(gesture, sequence, frame, GestureEventZoomType::End); 4849 } 4850 4851 bool GtkSalFrame::signalRotateBegin(GtkGesture* gesture, GdkEventSequence* sequence, 4852 gpointer frame) 4853 { 4854 return handleSignalRotate(gesture, sequence, frame, GestureEventRotateType::Begin); 4855 } 4856 4857 bool GtkSalFrame::signalRotateUpdate(GtkGesture* gesture, GdkEventSequence* sequence, 4858 gpointer frame) 4859 { 4860 return handleSignalRotate(gesture, sequence, frame, GestureEventRotateType::Update); 4861 } 4862 4863 bool GtkSalFrame::signalRotateEnd(GtkGesture* gesture, GdkEventSequence* sequence, 4864 gpointer frame) 4865 { 4866 return handleSignalRotate(gesture, sequence, frame, GestureEventRotateType::End); 4867 } 4868 4869 namespace 4870 { 4871 GdkDragAction VclToGdk(sal_Int8 dragOperation) 4872 { 4873 GdkDragAction eRet(static_cast<GdkDragAction>(0)); 4874 if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_COPY) 4875 eRet = static_cast<GdkDragAction>(eRet | GDK_ACTION_COPY); 4876 if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_MOVE) 4877 eRet = static_cast<GdkDragAction>(eRet | GDK_ACTION_MOVE); 4878 if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_LINK) 4879 eRet = static_cast<GdkDragAction>(eRet | GDK_ACTION_LINK); 4880 return eRet; 4881 } 4882 4883 sal_Int8 GdkToVcl(GdkDragAction dragOperation) 4884 { 4885 sal_Int8 nRet(0); 4886 if (dragOperation & GDK_ACTION_COPY) 4887 nRet |= css::datatransfer::dnd::DNDConstants::ACTION_COPY; 4888 if (dragOperation & GDK_ACTION_MOVE) 4889 nRet |= css::datatransfer::dnd::DNDConstants::ACTION_MOVE; 4890 if (dragOperation & GDK_ACTION_LINK) 4891 nRet |= css::datatransfer::dnd::DNDConstants::ACTION_LINK; 4892 return nRet; 4893 } 4894 } 4895 4896 namespace 4897 { 4898 GdkDragAction getPreferredDragAction(sal_Int8 dragOperation) 4899 { 4900 GdkDragAction eAct(static_cast<GdkDragAction>(0)); 4901 4902 if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_MOVE) 4903 eAct = GDK_ACTION_MOVE; 4904 else if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_COPY) 4905 eAct = GDK_ACTION_COPY; 4906 else if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_LINK) 4907 eAct = GDK_ACTION_LINK; 4908 4909 return eAct; 4910 } 4911 } 4912 4913 static bool g_DropSuccessSet = false; 4914 static bool g_DropSuccess = false; 4915 4916 namespace { 4917 4918 #if GTK_CHECK_VERSION(4, 0, 0) 4919 4920 void read_drop_async_completed(GObject* source, GAsyncResult* res, gpointer user_data) 4921 { 4922 GdkDrop* drop = GDK_DROP(source); 4923 read_transfer_result* pRes = static_cast<read_transfer_result*>(user_data); 4924 4925 GInputStream* pResult = gdk_drop_read_finish(drop, res, nullptr, nullptr); 4926 4927 if (!pResult) 4928 { 4929 pRes->bDone = true; 4930 g_main_context_wakeup(nullptr); 4931 return; 4932 } 4933 4934 pRes->aVector.resize(read_transfer_result::BlockSize); 4935 4936 g_input_stream_read_async(pResult, 4937 pRes->aVector.data(), 4938 pRes->aVector.size(), 4939 G_PRIORITY_DEFAULT, 4940 nullptr, 4941 read_transfer_result::read_block_async_completed, 4942 user_data); 4943 } 4944 #endif 4945 4946 class GtkDropTargetDropContext : public cppu::WeakImplHelper<css::datatransfer::dnd::XDropTargetDropContext> 4947 { 4948 #if !GTK_CHECK_VERSION(4, 0, 0) 4949 GdkDragContext *m_pContext; 4950 guint m_nTime; 4951 #else 4952 GdkDrop* m_pDrop; 4953 #endif 4954 public: 4955 #if !GTK_CHECK_VERSION(4, 0, 0) 4956 GtkDropTargetDropContext(GdkDragContext* pContext, guint nTime) 4957 : m_pContext(pContext) 4958 , m_nTime(nTime) 4959 #else 4960 GtkDropTargetDropContext(GdkDrop* pDrop) 4961 : m_pDrop(pDrop) 4962 #endif 4963 { 4964 } 4965 4966 // XDropTargetDropContext 4967 virtual void SAL_CALL acceptDrop(sal_Int8 dragOperation) override 4968 { 4969 #if !GTK_CHECK_VERSION(4, 0, 0) 4970 gdk_drag_status(m_pContext, getPreferredDragAction(dragOperation), m_nTime); 4971 #else 4972 GdkDragAction eDragAction = getPreferredDragAction(dragOperation); 4973 gdk_drop_status(m_pDrop, 4974 static_cast<GdkDragAction>(eDragAction | gdk_drop_get_actions(m_pDrop)), 4975 eDragAction); 4976 #endif 4977 } 4978 4979 virtual void SAL_CALL rejectDrop() override 4980 { 4981 #if !GTK_CHECK_VERSION(4, 0, 0) 4982 gdk_drag_status(m_pContext, static_cast<GdkDragAction>(0), m_nTime); 4983 #else 4984 gdk_drop_status(m_pDrop, gdk_drop_get_actions(m_pDrop), static_cast<GdkDragAction>(0)); 4985 #endif 4986 } 4987 4988 virtual void SAL_CALL dropComplete(sal_Bool bSuccess) override 4989 { 4990 #if !GTK_CHECK_VERSION(4, 0, 0) 4991 gtk_drag_finish(m_pContext, bSuccess, false, m_nTime); 4992 #else 4993 // should we do something better here 4994 gdk_drop_finish(m_pDrop, bSuccess 4995 ? gdk_drop_get_actions(m_pDrop) 4996 : static_cast<GdkDragAction>(0)); 4997 #endif 4998 if (GtkInstDragSource::g_ActiveDragSource) 4999 { 5000 g_DropSuccessSet = true; 5001 g_DropSuccess = bSuccess; 5002 } 5003 } 5004 }; 5005 5006 } 5007 5008 class GtkDnDTransferable : public GtkTransferable 5009 { 5010 #if !GTK_CHECK_VERSION(4, 0, 0) 5011 GdkDragContext *m_pContext; 5012 guint m_nTime; 5013 GtkWidget *m_pWidget; 5014 GtkInstDropTarget* m_pDropTarget; 5015 #else 5016 GdkDrop* m_pDrop; 5017 #endif 5018 #if !GTK_CHECK_VERSION(4, 0, 0) 5019 GMainLoop *m_pLoop; 5020 GtkSelectionData *m_pData; 5021 #endif 5022 public: 5023 #if !GTK_CHECK_VERSION(4, 0, 0) 5024 GtkDnDTransferable(GdkDragContext *pContext, guint nTime, GtkWidget *pWidget, GtkInstDropTarget *pDropTarget) 5025 : m_pContext(pContext) 5026 , m_nTime(nTime) 5027 , m_pWidget(pWidget) 5028 , m_pDropTarget(pDropTarget) 5029 #else 5030 GtkDnDTransferable(GdkDrop *pDrop) 5031 : m_pDrop(pDrop) 5032 #endif 5033 #if !GTK_CHECK_VERSION(4, 0, 0) 5034 , m_pLoop(nullptr) 5035 , m_pData(nullptr) 5036 #endif 5037 { 5038 } 5039 5040 virtual css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor) override 5041 { 5042 css::datatransfer::DataFlavor aFlavor(rFlavor); 5043 if (aFlavor.MimeType == "text/plain;charset=utf-16") 5044 aFlavor.MimeType = "text/plain;charset=utf-8"; 5045 5046 auto it = m_aMimeTypeToGtkType.find(aFlavor.MimeType); 5047 if (it == m_aMimeTypeToGtkType.end()) 5048 return css::uno::Any(); 5049 5050 css::uno::Any aRet; 5051 5052 #if !GTK_CHECK_VERSION(4, 0, 0) 5053 /* like gtk_clipboard_wait_for_contents run a sub loop 5054 * waiting for drag-data-received triggered from 5055 * gtk_drag_get_data 5056 */ 5057 { 5058 m_pLoop = g_main_loop_new(nullptr, true); 5059 m_pDropTarget->SetFormatConversionRequest(this); 5060 5061 gtk_drag_get_data(m_pWidget, m_pContext, it->second, m_nTime); 5062 5063 if (g_main_loop_is_running(m_pLoop)) 5064 main_loop_run(m_pLoop); 5065 5066 g_main_loop_unref(m_pLoop); 5067 m_pLoop = nullptr; 5068 m_pDropTarget->SetFormatConversionRequest(nullptr); 5069 } 5070 5071 if (aFlavor.MimeType == "text/plain;charset=utf-8") 5072 { 5073 OUString aStr; 5074 gchar *pText = reinterpret_cast<gchar*>(gtk_selection_data_get_text(m_pData)); 5075 if (pText) 5076 aStr = OStringToOUString(pText, RTL_TEXTENCODING_UTF8); 5077 g_free(pText); 5078 aRet <<= aStr.replaceAll("\r\n", "\n"); 5079 } 5080 else 5081 { 5082 gint length(0); 5083 const guchar *rawdata = gtk_selection_data_get_data_with_length(m_pData, 5084 &length); 5085 // seen here was rawhide == nullptr and length set to -1 5086 if (rawdata) 5087 { 5088 css::uno::Sequence<sal_Int8> aSeq(reinterpret_cast<const sal_Int8*>(rawdata), length); 5089 aRet <<= aSeq; 5090 } 5091 } 5092 5093 gtk_selection_data_free(m_pData); 5094 #else 5095 SalInstance* pInstance = GetSalInstance(); 5096 read_transfer_result aRes; 5097 const char *mime_types[] = { it->second.getStr(), nullptr }; 5098 5099 gdk_drop_read_async(m_pDrop, 5100 mime_types, 5101 G_PRIORITY_DEFAULT, 5102 nullptr, 5103 read_drop_async_completed, 5104 &aRes); 5105 5106 while (!aRes.bDone) 5107 pInstance->DoYield(true, false); 5108 5109 if (aFlavor.MimeType == "text/plain;charset=utf-8") 5110 aRet <<= aRes.get_as_string(); 5111 else 5112 aRet <<= aRes.get_as_sequence(); 5113 #endif 5114 return aRet; 5115 } 5116 5117 virtual std::vector<css::datatransfer::DataFlavor> getTransferDataFlavorsAsVector() override 5118 { 5119 #if !GTK_CHECK_VERSION(4, 0, 0) 5120 std::vector<GdkAtom> targets; 5121 for (GList* l = gdk_drag_context_list_targets(m_pContext); l; l = l->next) 5122 targets.push_back(static_cast<GdkAtom>(l->data)); 5123 return GtkTransferable::getTransferDataFlavorsAsVector(targets.data(), targets.size()); 5124 #else 5125 GdkContentFormats* pFormats = gdk_drop_get_formats(m_pDrop); 5126 gsize n_targets; 5127 const char * const *targets = gdk_content_formats_get_mime_types(pFormats, &n_targets); 5128 return GtkTransferable::getTransferDataFlavorsAsVector(targets, n_targets); 5129 #endif 5130 } 5131 5132 #if !GTK_CHECK_VERSION(4, 0, 0) 5133 void LoopEnd(GtkSelectionData *pData) 5134 { 5135 m_pData = pData; 5136 g_main_loop_quit(m_pLoop); 5137 } 5138 #endif 5139 }; 5140 5141 // For LibreOffice internal D&D we provide the Transferable without Gtk 5142 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this 5143 GtkInstDragSource* GtkInstDragSource::g_ActiveDragSource; 5144 5145 #if GTK_CHECK_VERSION(4, 0, 0) 5146 5147 gboolean GtkSalFrame::signalDragDrop(GtkDropTargetAsync* context, GdkDrop* drop, double x, double y, gpointer frame) 5148 { 5149 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 5150 if (!pThis->m_pDropTarget) 5151 return false; 5152 return pThis->m_pDropTarget->signalDragDrop(context, drop, x, y); 5153 } 5154 #else 5155 gboolean GtkSalFrame::signalDragDrop(GtkWidget* pWidget, GdkDragContext* context, gint x, gint y, guint time, gpointer frame) 5156 { 5157 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 5158 if (!pThis->m_pDropTarget) 5159 return false; 5160 return pThis->m_pDropTarget->signalDragDrop(pWidget, context, x, y, time); 5161 } 5162 #endif 5163 5164 #if !GTK_CHECK_VERSION(4, 0, 0) 5165 gboolean GtkInstDropTarget::signalDragDrop(GtkWidget* pWidget, GdkDragContext* context, gint x, gint y, guint time) 5166 #else 5167 gboolean GtkInstDropTarget::signalDragDrop(GtkDropTargetAsync* context, GdkDrop* drop, double x, double y) 5168 #endif 5169 { 5170 // remove the deferred dragExit, as we'll do a drop 5171 #ifndef NDEBUG 5172 bool res = 5173 #endif 5174 g_idle_remove_by_data(this); 5175 #ifndef NDEBUG 5176 #if !GTK_CHECK_VERSION(4, 0, 0) 5177 assert(res); 5178 #else 5179 (void)res; 5180 #endif 5181 #endif 5182 5183 css::datatransfer::dnd::DropTargetDropEvent aEvent; 5184 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(this); 5185 #if !GTK_CHECK_VERSION(4, 0, 0) 5186 aEvent.Context = new GtkDropTargetDropContext(context, time); 5187 #else 5188 aEvent.Context = new GtkDropTargetDropContext(drop); 5189 #endif 5190 aEvent.LocationX = x; 5191 aEvent.LocationY = y; 5192 #if !GTK_CHECK_VERSION(4, 0, 0) 5193 aEvent.DropAction = GdkToVcl(gdk_drag_context_get_selected_action(context)); 5194 #else 5195 aEvent.DropAction = GdkToVcl(getPreferredDragAction(GdkToVcl(gdk_drop_get_actions(drop)))); 5196 #endif 5197 // ACTION_DEFAULT is documented as... 5198 // 'This means the user did not press any key during the Drag and Drop operation 5199 // and the action that was combined with ACTION_DEFAULT is the system default action' 5200 // in tdf#107031 writer won't insert a link when a heading is dragged from the 5201 // navigator unless this is set. Its unclear really what ACTION_DEFAULT means, 5202 // there is a deprecated 'GDK_ACTION_DEFAULT Means nothing, and should not be used' 5203 // possible equivalent in gtk. 5204 // So (tdf#109227) set ACTION_DEFAULT if no modifier key is held down 5205 #if !GTK_CHECK_VERSION(4,0,0) 5206 aEvent.SourceActions = GdkToVcl(gdk_drag_context_get_actions(context)); 5207 GdkModifierType mask; 5208 gdk_window_get_pointer(widget_get_surface(pWidget), nullptr, nullptr, &mask); 5209 #else 5210 aEvent.SourceActions = GdkToVcl(gdk_drop_get_actions(drop)); 5211 GdkModifierType mask = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(context)); 5212 #endif 5213 if (!(mask & (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) 5214 aEvent.DropAction |= css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT; 5215 5216 css::uno::Reference<css::datatransfer::XTransferable> xTransferable; 5217 // For LibreOffice internal D&D we provide the Transferable without Gtk 5218 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this 5219 if (GtkInstDragSource::g_ActiveDragSource) 5220 xTransferable = GtkInstDragSource::g_ActiveDragSource->GetTransferable(); 5221 else 5222 { 5223 #if GTK_CHECK_VERSION(4,0,0) 5224 xTransferable = new GtkDnDTransferable(drop); 5225 #else 5226 xTransferable = new GtkDnDTransferable(context, time, pWidget, this); 5227 #endif 5228 } 5229 aEvent.Transferable = xTransferable; 5230 5231 fire_drop(aEvent); 5232 5233 return true; 5234 } 5235 5236 namespace { 5237 5238 class GtkDropTargetDragContext : public cppu::WeakImplHelper<css::datatransfer::dnd::XDropTargetDragContext> 5239 { 5240 #if !GTK_CHECK_VERSION(4, 0, 0) 5241 GdkDragContext *m_pContext; 5242 guint m_nTime; 5243 #else 5244 GdkDrop* m_pDrop; 5245 #endif 5246 public: 5247 #if !GTK_CHECK_VERSION(4, 0, 0) 5248 GtkDropTargetDragContext(GdkDragContext *pContext, guint nTime) 5249 : m_pContext(pContext) 5250 , m_nTime(nTime) 5251 #else 5252 GtkDropTargetDragContext(GdkDrop* pDrop) 5253 : m_pDrop(pDrop) 5254 #endif 5255 { 5256 } 5257 5258 virtual void SAL_CALL acceptDrag(sal_Int8 dragOperation) override 5259 { 5260 #if !GTK_CHECK_VERSION(4, 0, 0) 5261 gdk_drag_status(m_pContext, getPreferredDragAction(dragOperation), m_nTime); 5262 #else 5263 gdk_drop_status(m_pDrop, gdk_drop_get_actions(m_pDrop), getPreferredDragAction(dragOperation)); 5264 #endif 5265 } 5266 5267 virtual void SAL_CALL rejectDrag() override 5268 { 5269 #if !GTK_CHECK_VERSION(4, 0, 0) 5270 gdk_drag_status(m_pContext, static_cast<GdkDragAction>(0), m_nTime); 5271 #else 5272 gdk_drop_status(m_pDrop, gdk_drop_get_actions(m_pDrop), static_cast<GdkDragAction>(0)); 5273 #endif 5274 } 5275 }; 5276 5277 } 5278 5279 #if !GTK_CHECK_VERSION(4, 0, 0) 5280 void GtkSalFrame::signalDragDropReceived(GtkWidget* pWidget, GdkDragContext* context, gint x, gint y, GtkSelectionData* data, guint ttype, guint time, gpointer frame) 5281 { 5282 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 5283 if (!pThis->m_pDropTarget) 5284 return; 5285 pThis->m_pDropTarget->signalDragDropReceived(pWidget, context, x, y, data, ttype, time); 5286 } 5287 5288 void GtkInstDropTarget::signalDragDropReceived(GtkWidget* /*pWidget*/, GdkDragContext * /*context*/, gint /*x*/, gint /*y*/, GtkSelectionData* data, guint /*ttype*/, guint /*time*/) 5289 { 5290 /* 5291 * If we get a drop, then we will call like gtk_clipboard_wait_for_contents 5292 * with a loop inside a loop to get the right format, so if this is the 5293 * case return to the outer loop here with a copy of the desired data 5294 * 5295 * don't look at me like that. 5296 */ 5297 if (!m_pFormatConversionRequest) 5298 return; 5299 5300 m_pFormatConversionRequest->LoopEnd(gtk_selection_data_copy(data)); 5301 } 5302 #endif 5303 5304 #if GTK_CHECK_VERSION(4,0,0) 5305 GdkDragAction GtkSalFrame::signalDragMotion(GtkDropTargetAsync *dest, GdkDrop *drop, double x, double y, gpointer frame) 5306 { 5307 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 5308 if (!pThis->m_pDropTarget) 5309 return GdkDragAction(0); 5310 return pThis->m_pDropTarget->signalDragMotion(dest, drop, x, y); 5311 } 5312 #else 5313 gboolean GtkSalFrame::signalDragMotion(GtkWidget *pWidget, GdkDragContext *context, gint x, gint y, guint time, gpointer frame) 5314 { 5315 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 5316 if (!pThis->m_pDropTarget) 5317 return false; 5318 return pThis->m_pDropTarget->signalDragMotion(pWidget, context, x, y, time); 5319 } 5320 #endif 5321 5322 #if !GTK_CHECK_VERSION(4,0,0) 5323 gboolean GtkInstDropTarget::signalDragMotion(GtkWidget *pWidget, GdkDragContext *context, gint x, gint y, guint time) 5324 #else 5325 GdkDragAction GtkInstDropTarget::signalDragMotion(GtkDropTargetAsync *context, GdkDrop *pDrop, double x, double y) 5326 #endif 5327 { 5328 if (!m_bInDrag) 5329 { 5330 #if !GTK_CHECK_VERSION(4,0,0) 5331 GtkWidget* pHighlightWidget = m_pFrame ? GTK_WIDGET(m_pFrame->getFixedContainer()) : pWidget; 5332 gtk_drag_highlight(pHighlightWidget); 5333 #else 5334 GtkWidget* pHighlightWidget = m_pFrame ? GTK_WIDGET(m_pFrame->getFixedContainer()) : 5335 gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(context)); 5336 gtk_widget_set_state_flags(pHighlightWidget, GTK_STATE_FLAG_DROP_ACTIVE, false); 5337 #endif 5338 } 5339 5340 css::datatransfer::dnd::DropTargetDragEnterEvent aEvent; 5341 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(this); 5342 #if !GTK_CHECK_VERSION(4,0,0) 5343 rtl::Reference<GtkDropTargetDragContext> pContext = new GtkDropTargetDragContext(context, time); 5344 #else 5345 rtl::Reference<GtkDropTargetDragContext> pContext = new GtkDropTargetDragContext(pDrop); 5346 #endif 5347 //preliminary accept the Drag and select the preferred action, the fire_* will 5348 //inform the original caller of our choice and the callsite can decide 5349 //to overrule this choice. i.e. typically here we default to ACTION_MOVE 5350 #if !GTK_CHECK_VERSION(4,0,0) 5351 sal_Int8 nSourceActions = GdkToVcl(gdk_drag_context_get_actions(context)); 5352 GdkModifierType mask; 5353 gdk_window_get_pointer(widget_get_surface(pWidget), nullptr, nullptr, &mask); 5354 #else 5355 sal_Int8 nSourceActions = GdkToVcl(gdk_drop_get_actions(pDrop)); 5356 GdkModifierType mask = gtk_event_controller_get_current_event_state(GTK_EVENT_CONTROLLER(context)); 5357 #endif 5358 5359 // tdf#124411 default to move if drag originates within LO itself, default 5360 // to copy if it comes from outside, this is similar to srcAndDestEqual 5361 // in macosx DropTarget::determineDropAction equivalent 5362 sal_Int8 nNewDropAction = GtkInstDragSource::g_ActiveDragSource ? 5363 css::datatransfer::dnd::DNDConstants::ACTION_MOVE : 5364 css::datatransfer::dnd::DNDConstants::ACTION_COPY; 5365 5366 // tdf#109227 if a modifier is held down, default to the matching 5367 // action for that modifier combo, otherwise pick the preferred 5368 // default from the possible source actions 5369 if ((mask & GDK_SHIFT_MASK) && !(mask & GDK_CONTROL_MASK)) 5370 nNewDropAction = css::datatransfer::dnd::DNDConstants::ACTION_MOVE; 5371 else if ((mask & GDK_CONTROL_MASK) && !(mask & GDK_SHIFT_MASK)) 5372 nNewDropAction = css::datatransfer::dnd::DNDConstants::ACTION_COPY; 5373 else if ((mask & GDK_SHIFT_MASK) && (mask & GDK_CONTROL_MASK) ) 5374 nNewDropAction = css::datatransfer::dnd::DNDConstants::ACTION_LINK; 5375 nNewDropAction &= nSourceActions; 5376 5377 GdkDragAction eAction; 5378 if (!(mask & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) && !nNewDropAction) 5379 eAction = getPreferredDragAction(nSourceActions); 5380 else 5381 eAction = getPreferredDragAction(nNewDropAction); 5382 5383 #if !GTK_CHECK_VERSION(4,0,0) 5384 gdk_drag_status(context, eAction, time); 5385 #else 5386 gdk_drop_status(pDrop, 5387 static_cast<GdkDragAction>(eAction | gdk_drop_get_actions(pDrop)), 5388 eAction); 5389 #endif 5390 aEvent.Context = pContext; 5391 aEvent.LocationX = x; 5392 aEvent.LocationY = y; 5393 //under wayland at least, the action selected by gdk_drag_status on the 5394 //context is not immediately available via gdk_drag_context_get_selected_action 5395 //so here we set the DropAction from what we selected on the context, not 5396 //what the context says is selected 5397 aEvent.DropAction = GdkToVcl(eAction); 5398 aEvent.SourceActions = nSourceActions; 5399 5400 if (!m_bInDrag) 5401 { 5402 css::uno::Reference<css::datatransfer::XTransferable> xTransferable; 5403 // For LibreOffice internal D&D we provide the Transferable without Gtk 5404 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this 5405 if (GtkInstDragSource::g_ActiveDragSource) 5406 xTransferable = GtkInstDragSource::g_ActiveDragSource->GetTransferable(); 5407 else 5408 { 5409 #if !GTK_CHECK_VERSION(4,0,0) 5410 xTransferable = new GtkDnDTransferable(context, time, pWidget, this); 5411 #else 5412 xTransferable = new GtkDnDTransferable(pDrop); 5413 #endif 5414 } 5415 aEvent.SupportedDataFlavors = xTransferable->getTransferDataFlavors(); 5416 fire_dragEnter(aEvent); 5417 m_bInDrag = true; 5418 } 5419 else 5420 { 5421 fire_dragOver(aEvent); 5422 } 5423 5424 #if !GTK_CHECK_VERSION(4,0,0) 5425 return true; 5426 #else 5427 return eAction; 5428 #endif 5429 } 5430 5431 #if GTK_CHECK_VERSION(4,0,0) 5432 void GtkSalFrame::signalDragLeave(GtkDropTargetAsync* pDest, GdkDrop* /*drop*/, gpointer frame) 5433 { 5434 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 5435 if (!pThis->m_pDropTarget) 5436 return; 5437 pThis->m_pDropTarget->signalDragLeave(gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(pDest))); 5438 } 5439 #else 5440 void GtkSalFrame::signalDragLeave(GtkWidget* pWidget, GdkDragContext* /*context*/, guint /*time*/, gpointer frame) 5441 { 5442 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 5443 if (!pThis->m_pDropTarget) 5444 return; 5445 pThis->m_pDropTarget->signalDragLeave(pWidget); 5446 } 5447 #endif 5448 5449 static gboolean lcl_deferred_dragExit(gpointer user_data) 5450 { 5451 GtkInstDropTarget* pThis = static_cast<GtkInstDropTarget*>(user_data); 5452 css::datatransfer::dnd::DropTargetEvent aEvent; 5453 aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(pThis); 5454 pThis->fire_dragExit(aEvent); 5455 return false; 5456 } 5457 5458 void GtkInstDropTarget::signalDragLeave(GtkWidget *pWidget) 5459 { 5460 m_bInDrag = false; 5461 5462 GtkWidget* pHighlightWidget = m_pFrame ? GTK_WIDGET(m_pFrame->getFixedContainer()) : pWidget; 5463 #if !GTK_CHECK_VERSION(4,0,0) 5464 gtk_drag_unhighlight(pHighlightWidget); 5465 #else 5466 gtk_widget_unset_state_flags(pHighlightWidget, GTK_STATE_FLAG_DROP_ACTIVE); 5467 #endif 5468 5469 // defer fire_dragExit, since gtk also sends a drag-leave before the drop, while 5470 // LO expect to either handle the drop or the exit... at least in Writer. 5471 // but since we don't know there will be a drop following the leave, defer the 5472 // exit handling to an idle. 5473 g_idle_add(lcl_deferred_dragExit, this); 5474 } 5475 5476 void GtkSalFrame::signalDestroy( GtkWidget* pObj, gpointer frame ) 5477 { 5478 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 5479 if( pObj != pThis->m_pWindow ) 5480 return; 5481 5482 pThis->m_aDamageHandler.damaged = nullptr; 5483 pThis->m_aDamageHandler.handle = nullptr; 5484 if (pThis->m_pSurface) 5485 cairo_surface_set_user_data(pThis->m_pSurface, SvpSalGraphics::getDamageKey(), nullptr, nullptr); 5486 pThis->m_pFixedContainer = nullptr; 5487 pThis->m_pDrawingArea = nullptr; 5488 #if !GTK_CHECK_VERSION(4, 0, 0) 5489 pThis->m_pEventBox = nullptr; 5490 #endif 5491 pThis->m_pTopLevelGrid = nullptr; 5492 pThis->m_pWindow = nullptr; 5493 pThis->m_xFrameWeld.reset(); 5494 pThis->InvalidateGraphics(); 5495 } 5496 5497 // GtkSalFrame::IMHandler 5498 5499 GtkSalFrame::IMHandler::IMHandler( GtkSalFrame* pFrame ) 5500 : m_pFrame(pFrame), 5501 m_nPrevKeyPresses( 0 ), 5502 m_pIMContext( nullptr ), 5503 m_bFocused( true ), 5504 m_bPreeditJustChanged( false ) 5505 { 5506 m_aInputEvent.mpTextAttr = nullptr; 5507 createIMContext(); 5508 } 5509 5510 GtkSalFrame::IMHandler::~IMHandler() 5511 { 5512 // cancel an eventual event posted to begin preedit again 5513 GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SalEvent::ExtTextInput ); 5514 deleteIMContext(); 5515 } 5516 5517 void GtkSalFrame::IMHandler::createIMContext() 5518 { 5519 if( m_pIMContext ) 5520 return; 5521 5522 m_pIMContext = gtk_im_multicontext_new (); 5523 g_signal_connect( m_pIMContext, "commit", 5524 G_CALLBACK (signalIMCommit), this ); 5525 g_signal_connect( m_pIMContext, "preedit_changed", 5526 G_CALLBACK (signalIMPreeditChanged), this ); 5527 g_signal_connect( m_pIMContext, "retrieve_surrounding", 5528 G_CALLBACK (signalIMRetrieveSurrounding), this ); 5529 g_signal_connect( m_pIMContext, "delete_surrounding", 5530 G_CALLBACK (signalIMDeleteSurrounding), this ); 5531 g_signal_connect( m_pIMContext, "preedit_start", 5532 G_CALLBACK (signalIMPreeditStart), this ); 5533 g_signal_connect( m_pIMContext, "preedit_end", 5534 G_CALLBACK (signalIMPreeditEnd), this ); 5535 5536 GetGenericUnixSalData()->ErrorTrapPush(); 5537 im_context_set_client_widget(m_pIMContext, m_pFrame->getMouseEventWidget()); 5538 #if GTK_CHECK_VERSION(4, 0, 0) 5539 gtk_event_controller_key_set_im_context(m_pFrame->m_pKeyController, m_pIMContext); 5540 #endif 5541 gtk_im_context_focus_in( m_pIMContext ); 5542 GetGenericUnixSalData()->ErrorTrapPop(); 5543 m_bFocused = true; 5544 5545 } 5546 5547 void GtkSalFrame::IMHandler::deleteIMContext() 5548 { 5549 if( !m_pIMContext ) 5550 return; 5551 5552 // first give IC a chance to deinitialize 5553 GetGenericUnixSalData()->ErrorTrapPush(); 5554 #if GTK_CHECK_VERSION(4, 0, 0) 5555 gtk_event_controller_key_set_im_context(m_pFrame->m_pKeyController, nullptr); 5556 #endif 5557 im_context_set_client_widget(m_pIMContext, nullptr); 5558 GetGenericUnixSalData()->ErrorTrapPop(); 5559 // destroy old IC 5560 g_object_unref( m_pIMContext ); 5561 m_pIMContext = nullptr; 5562 } 5563 5564 void GtkSalFrame::IMHandler::doCallEndExtTextInput() 5565 { 5566 m_aInputEvent.mpTextAttr = nullptr; 5567 m_pFrame->CallCallbackExc( SalEvent::EndExtTextInput, nullptr ); 5568 } 5569 5570 void GtkSalFrame::IMHandler::updateIMSpotLocation() 5571 { 5572 SalExtTextInputPosEvent aPosEvent; 5573 m_pFrame->CallCallbackExc( SalEvent::ExtTextInputPos, static_cast<void*>(&aPosEvent) ); 5574 GdkRectangle aArea; 5575 aArea.x = aPosEvent.mnX; 5576 aArea.y = aPosEvent.mnY; 5577 aArea.width = aPosEvent.mnWidth; 5578 aArea.height = aPosEvent.mnHeight; 5579 GetGenericUnixSalData()->ErrorTrapPush(); 5580 gtk_im_context_set_cursor_location( m_pIMContext, &aArea ); 5581 GetGenericUnixSalData()->ErrorTrapPop(); 5582 } 5583 5584 void GtkSalFrame::IMHandler::sendEmptyCommit() 5585 { 5586 vcl::DeletionListener aDel( m_pFrame ); 5587 5588 SalExtTextInputEvent aEmptyEv; 5589 aEmptyEv.mpTextAttr = nullptr; 5590 aEmptyEv.maText.clear(); 5591 aEmptyEv.mnCursorPos = 0; 5592 aEmptyEv.mnCursorFlags = 0; 5593 m_pFrame->CallCallbackExc( SalEvent::ExtTextInput, static_cast<void*>(&aEmptyEv) ); 5594 if( ! aDel.isDeleted() ) 5595 m_pFrame->CallCallbackExc( SalEvent::EndExtTextInput, nullptr ); 5596 } 5597 5598 void GtkSalFrame::IMHandler::endExtTextInput( EndExtTextInputFlags /*nFlags*/ ) 5599 { 5600 gtk_im_context_reset ( m_pIMContext ); 5601 5602 if( !m_aInputEvent.mpTextAttr ) 5603 return; 5604 5605 vcl::DeletionListener aDel( m_pFrame ); 5606 // delete preedit in sal (commit an empty string) 5607 sendEmptyCommit(); 5608 if( ! aDel.isDeleted() ) 5609 { 5610 // mark previous preedit state again (will e.g. be sent at focus gain) 5611 m_aInputEvent.mpTextAttr = m_aInputFlags.data(); 5612 if( m_bFocused ) 5613 { 5614 // begin preedit again 5615 GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SalEvent::ExtTextInput ); 5616 } 5617 } 5618 } 5619 5620 void GtkSalFrame::IMHandler::focusChanged( bool bFocusIn ) 5621 { 5622 m_bFocused = bFocusIn; 5623 if( bFocusIn ) 5624 { 5625 GetGenericUnixSalData()->ErrorTrapPush(); 5626 gtk_im_context_focus_in( m_pIMContext ); 5627 GetGenericUnixSalData()->ErrorTrapPop(); 5628 if( m_aInputEvent.mpTextAttr ) 5629 { 5630 sendEmptyCommit(); 5631 // begin preedit again 5632 GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SalEvent::ExtTextInput ); 5633 } 5634 } 5635 else 5636 { 5637 GetGenericUnixSalData()->ErrorTrapPush(); 5638 gtk_im_context_focus_out( m_pIMContext ); 5639 GetGenericUnixSalData()->ErrorTrapPop(); 5640 // cancel an eventual event posted to begin preedit again 5641 GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SalEvent::ExtTextInput ); 5642 } 5643 } 5644 5645 #if !GTK_CHECK_VERSION(4, 0, 0) 5646 bool GtkSalFrame::IMHandler::handleKeyEvent( GdkEventKey* pEvent ) 5647 { 5648 vcl::DeletionListener aDel( m_pFrame ); 5649 5650 if( pEvent->type == GDK_KEY_PRESS ) 5651 { 5652 // Add this key press event to the list of previous key presses 5653 // to which we compare key release events. If a later key release 5654 // event has a matching key press event in this list, we swallow 5655 // the key release because some GTK Input Methods don't swallow it 5656 // for us. 5657 m_aPrevKeyPresses.emplace_back(pEvent ); 5658 m_nPrevKeyPresses++; 5659 5660 // Also pop off the earliest key press event if there are more than 10 5661 // already. 5662 while (m_nPrevKeyPresses > 10) 5663 { 5664 m_aPrevKeyPresses.pop_front(); 5665 m_nPrevKeyPresses--; 5666 } 5667 5668 GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); 5669 5670 // #i51353# update spot location on every key input since we cannot 5671 // know which key may activate a preedit choice window 5672 updateIMSpotLocation(); 5673 if( aDel.isDeleted() ) 5674 return true; 5675 5676 bool bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); 5677 g_object_unref( pRef ); 5678 5679 if( aDel.isDeleted() ) 5680 return true; 5681 5682 m_bPreeditJustChanged = false; 5683 5684 if( bResult ) 5685 return true; 5686 else 5687 { 5688 SAL_WARN_IF( m_nPrevKeyPresses <= 0, "vcl.gtk3", "key press has vanished !" ); 5689 if( ! m_aPrevKeyPresses.empty() ) // sanity check 5690 { 5691 // event was not swallowed, do not filter a following 5692 // key release event 5693 // note: this relies on gtk_im_context_filter_keypress 5694 // returning without calling a handler (in the "not swallowed" 5695 // case ) which might change the previous key press list so 5696 // we would pop the wrong event here 5697 m_aPrevKeyPresses.pop_back(); 5698 m_nPrevKeyPresses--; 5699 } 5700 } 5701 } 5702 5703 // Determine if we got an earlier key press event corresponding to this key release 5704 if (pEvent->type == GDK_KEY_RELEASE) 5705 { 5706 GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); 5707 bool bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); 5708 g_object_unref( pRef ); 5709 5710 if( aDel.isDeleted() ) 5711 return true; 5712 5713 m_bPreeditJustChanged = false; 5714 5715 auto iter = std::find(m_aPrevKeyPresses.begin(), m_aPrevKeyPresses.end(), pEvent); 5716 // If we found a corresponding previous key press event, swallow the release 5717 // and remove the earlier key press from our list 5718 if (iter != m_aPrevKeyPresses.end()) 5719 { 5720 m_aPrevKeyPresses.erase(iter); 5721 m_nPrevKeyPresses--; 5722 return true; 5723 } 5724 5725 if( bResult ) 5726 return true; 5727 } 5728 5729 return false; 5730 } 5731 5732 /* FIXME: 5733 * #122282# still more hacking: some IMEs never start a preedit but simply commit 5734 * in this case we cannot commit a single character. Workaround: do not do the 5735 * single key hack for enter or space if the unicode committed does not match 5736 */ 5737 5738 static bool checkSingleKeyCommitHack( guint keyval, sal_Unicode cCode ) 5739 { 5740 bool bRet = true; 5741 switch( keyval ) 5742 { 5743 case GDK_KEY_KP_Enter: 5744 case GDK_KEY_Return: 5745 if( cCode != '\n' && cCode != '\r' ) 5746 bRet = false; 5747 break; 5748 case GDK_KEY_space: 5749 case GDK_KEY_KP_Space: 5750 if( cCode != ' ' ) 5751 bRet = false; 5752 break; 5753 default: 5754 break; 5755 } 5756 return bRet; 5757 } 5758 5759 void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext* /*pContext*/, gchar* pText, gpointer im_handler ) 5760 { 5761 GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler); 5762 5763 SolarMutexGuard aGuard; 5764 vcl::DeletionListener aDel( pThis->m_pFrame ); 5765 { 5766 const bool bWasPreedit = 5767 (pThis->m_aInputEvent.mpTextAttr != nullptr) || 5768 pThis->m_bPreeditJustChanged; 5769 5770 pThis->m_aInputEvent.mpTextAttr = nullptr; 5771 pThis->m_aInputEvent.maText = OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ); 5772 pThis->m_aInputEvent.mnCursorPos = pThis->m_aInputEvent.maText.getLength(); 5773 pThis->m_aInputEvent.mnCursorFlags = 0; 5774 5775 pThis->m_aInputFlags.clear(); 5776 5777 /* necessary HACK: all keyboard input comes in here as soon as an IMContext is set 5778 * which is logical and consequent. But since even simple input like 5779 * <space> comes through the commit signal instead of signalKey 5780 * and all kinds of windows only implement KeyInput (e.g. PushButtons, 5781 * RadioButtons and a lot of other Controls), will send a single 5782 * KeyInput/KeyUp sequence instead of an ExtText event if there 5783 * never was a preedit and the text is only one character. 5784 * 5785 * In this case there the last ExtText event must have been 5786 * SalEvent::EndExtTextInput, either because of a regular commit 5787 * or because there never was a preedit. 5788 */ 5789 bool bSingleCommit = false; 5790 if( ! bWasPreedit 5791 && pThis->m_aInputEvent.maText.getLength() == 1 5792 && ! pThis->m_aPrevKeyPresses.empty() 5793 ) 5794 { 5795 const PreviousKeyPress& rKP = pThis->m_aPrevKeyPresses.back(); 5796 sal_Unicode aOrigCode = pThis->m_aInputEvent.maText[0]; 5797 5798 if( checkSingleKeyCommitHack( rKP.keyval, aOrigCode ) ) 5799 { 5800 pThis->m_pFrame->doKeyCallback( rKP.state, rKP.keyval, rKP.hardware_keycode, rKP.group, aOrigCode, true, true ); 5801 bSingleCommit = true; 5802 } 5803 } 5804 if( ! bSingleCommit ) 5805 { 5806 pThis->m_pFrame->CallCallbackExc( SalEvent::ExtTextInput, static_cast<void*>(&pThis->m_aInputEvent)); 5807 if( ! aDel.isDeleted() ) 5808 pThis->doCallEndExtTextInput(); 5809 } 5810 if( ! aDel.isDeleted() ) 5811 { 5812 // reset input event 5813 pThis->m_aInputEvent.maText.clear(); 5814 pThis->m_aInputEvent.mnCursorPos = 0; 5815 pThis->updateIMSpotLocation(); 5816 } 5817 } 5818 } 5819 #else 5820 void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext* /*pContext*/, gchar* pText, gpointer im_handler ) 5821 { 5822 GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler); 5823 5824 SolarMutexGuard aGuard; 5825 vcl::DeletionListener aDel( pThis->m_pFrame ); 5826 { 5827 #if 0 5828 const bool bWasPreedit = 5829 (pThis->m_aInputEvent.mpTextAttr != nullptr) || 5830 pThis->m_bPreeditJustChanged; 5831 #endif 5832 5833 pThis->m_aInputEvent.mpTextAttr = nullptr; 5834 pThis->m_aInputEvent.maText = OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ); 5835 pThis->m_aInputEvent.mnCursorPos = pThis->m_aInputEvent.maText.getLength(); 5836 pThis->m_aInputEvent.mnCursorFlags = 0; 5837 5838 pThis->m_aInputFlags.clear(); 5839 5840 /* necessary HACK: all keyboard input comes in here as soon as an IMContext is set 5841 * which is logical and consequent. But since even simple input like 5842 * <space> comes through the commit signal instead of signalKey 5843 * and all kinds of windows only implement KeyInput (e.g. PushButtons, 5844 * RadioButtons and a lot of other Controls), will send a single 5845 * KeyInput/KeyUp sequence instead of an ExtText event if there 5846 * never was a preedit and the text is only one character. 5847 * 5848 * In this case there the last ExtText event must have been 5849 * SalEvent::EndExtTextInput, either because of a regular commit 5850 * or because there never was a preedit. 5851 */ 5852 bool bSingleCommit = false; 5853 #if 0 5854 // TODO this needs a rethink to work again if necessary 5855 if( ! bWasPreedit 5856 && pThis->m_aInputEvent.maText.getLength() == 1 5857 && ! pThis->m_aPrevKeyPresses.empty() 5858 ) 5859 { 5860 const PreviousKeyPress& rKP = pThis->m_aPrevKeyPresses.back(); 5861 sal_Unicode aOrigCode = pThis->m_aInputEvent.maText[0]; 5862 5863 if( checkSingleKeyCommitHack( rKP.keyval, aOrigCode ) ) 5864 { 5865 pThis->m_pFrame->doKeyCallback( rKP.state, rKP.keyval, rKP.hardware_keycode, rKP.group, aOrigCode, true, true ); 5866 bSingleCommit = true; 5867 } 5868 } 5869 #endif 5870 if( ! bSingleCommit ) 5871 { 5872 pThis->m_pFrame->CallCallbackExc( SalEvent::ExtTextInput, static_cast<void*>(&pThis->m_aInputEvent)); 5873 if( ! aDel.isDeleted() ) 5874 pThis->doCallEndExtTextInput(); 5875 } 5876 if( ! aDel.isDeleted() ) 5877 { 5878 // reset input event 5879 pThis->m_aInputEvent.maText.clear(); 5880 pThis->m_aInputEvent.mnCursorPos = 0; 5881 pThis->updateIMSpotLocation(); 5882 } 5883 } 5884 } 5885 #endif 5886 5887 OUString GtkSalFrame::GetPreeditDetails(GtkIMContext* pIMContext, std::vector<ExtTextInputAttr>& rInputFlags, sal_Int32& rCursorPos, sal_uInt8& rCursorFlags) 5888 { 5889 char* pText = nullptr; 5890 PangoAttrList* pAttrs = nullptr; 5891 gint nCursorPos = 0; 5892 5893 gtk_im_context_get_preedit_string( pIMContext, 5894 &pText, 5895 &pAttrs, 5896 &nCursorPos ); 5897 5898 gint nUtf8Len = pText ? strlen(pText) : 0; 5899 OUString sText = pText ? OUString(pText, nUtf8Len, RTL_TEXTENCODING_UTF8) : OUString(); 5900 5901 std::vector<sal_Int32> aUtf16Offsets; 5902 for (sal_Int32 nUtf16Offset = 0; nUtf16Offset < sText.getLength(); sText.iterateCodePoints(&nUtf16Offset)) 5903 aUtf16Offsets.push_back(nUtf16Offset); 5904 5905 sal_Int32 nUtf32Len = aUtf16Offsets.size(); 5906 // from the above loop filling aUtf16Offsets, we know that its size() fits into sal_Int32 5907 aUtf16Offsets.push_back(sText.getLength()); 5908 5909 // sanitize the CurPos which is in utf-32 5910 if (nCursorPos < 0) 5911 nCursorPos = 0; 5912 else if (nCursorPos > nUtf32Len) 5913 nCursorPos = nUtf32Len; 5914 5915 rCursorPos = aUtf16Offsets[nCursorPos]; 5916 rCursorFlags = 0; 5917 5918 rInputFlags.resize(std::max(1, static_cast<int>(sText.getLength())), ExtTextInputAttr::NONE); 5919 5920 PangoAttrIterator *iter = pango_attr_list_get_iterator(pAttrs); 5921 do 5922 { 5923 GSList *attr_list = nullptr; 5924 GSList *tmp_list = nullptr; 5925 gint nUtf8Start, nUtf8End; 5926 ExtTextInputAttr sal_attr = ExtTextInputAttr::NONE; 5927 5928 // docs say... "Get the range of the current segment ... the stored 5929 // return values are signed, not unsigned like the values in 5930 // PangoAttribute", which implies that the units are otherwise the same 5931 // as that of PangoAttribute whose docs state these units are "in 5932 // bytes" 5933 // so this is the utf8 range 5934 pango_attr_iterator_range(iter, &nUtf8Start, &nUtf8End); 5935 5936 // sanitize the utf8 range 5937 nUtf8Start = std::min(nUtf8Start, nUtf8Len); 5938 nUtf8End = std::min(nUtf8End, nUtf8Len); 5939 if (nUtf8Start >= nUtf8End) 5940 continue; 5941 5942 // get the utf32 range 5943 sal_Int32 nUtf32Start = g_utf8_pointer_to_offset(pText, pText + nUtf8Start); 5944 sal_Int32 nUtf32End = g_utf8_pointer_to_offset(pText, pText + nUtf8End); 5945 5946 // sanitize the utf32 range 5947 nUtf32Start = std::min(nUtf32Start, nUtf32Len); 5948 nUtf32End = std::min(nUtf32End, nUtf32Len); 5949 if (nUtf32Start >= nUtf32End) 5950 continue; 5951 5952 tmp_list = attr_list = pango_attr_iterator_get_attrs (iter); 5953 while (tmp_list) 5954 { 5955 PangoAttribute *pango_attr = static_cast<PangoAttribute *>(tmp_list->data); 5956 5957 switch (pango_attr->klass->type) 5958 { 5959 case PANGO_ATTR_BACKGROUND: 5960 sal_attr |= ExtTextInputAttr::Highlight; 5961 rCursorFlags |= EXTTEXTINPUT_CURSOR_INVISIBLE; 5962 break; 5963 case PANGO_ATTR_UNDERLINE: 5964 { 5965 PangoAttrInt* pango_underline = reinterpret_cast<PangoAttrInt*>(pango_attr); 5966 switch (pango_underline->value) 5967 { 5968 case PANGO_UNDERLINE_NONE: 5969 break; 5970 case PANGO_UNDERLINE_DOUBLE: 5971 sal_attr |= ExtTextInputAttr::DoubleUnderline; 5972 break; 5973 default: 5974 sal_attr |= ExtTextInputAttr::Underline; 5975 break; 5976 } 5977 break; 5978 } 5979 case PANGO_ATTR_STRIKETHROUGH: 5980 sal_attr |= ExtTextInputAttr::RedText; 5981 break; 5982 default: 5983 break; 5984 } 5985 pango_attribute_destroy (pango_attr); 5986 tmp_list = tmp_list->next; 5987 } 5988 if (!attr_list) 5989 sal_attr |= ExtTextInputAttr::Underline; 5990 g_slist_free (attr_list); 5991 5992 // Set the sal attributes on our text 5993 // rhbz#1648281 apply over our utf-16 range derived from the input utf-32 range 5994 for (sal_Int32 i = aUtf16Offsets[nUtf32Start]; i < aUtf16Offsets[nUtf32End]; ++i) 5995 { 5996 SAL_WARN_IF(i >= static_cast<int>(rInputFlags.size()), 5997 "vcl.gtk3", "pango attrib out of range. Broken range: " 5998 << aUtf16Offsets[nUtf32Start] << "," << aUtf16Offsets[nUtf32End] << " Legal range: 0," 5999 << rInputFlags.size()); 6000 if (i >= static_cast<int>(rInputFlags.size())) 6001 continue; 6002 rInputFlags[i] |= sal_attr; 6003 } 6004 } while (pango_attr_iterator_next (iter)); 6005 pango_attr_iterator_destroy(iter); 6006 6007 g_free( pText ); 6008 pango_attr_list_unref( pAttrs ); 6009 6010 return sText; 6011 } 6012 6013 void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext* pIMContext, gpointer im_handler ) 6014 { 6015 GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler); 6016 6017 sal_Int32 nCursorPos(0); 6018 sal_uInt8 nCursorFlags(0); 6019 std::vector<ExtTextInputAttr> aInputFlags; 6020 OUString sText = GtkSalFrame::GetPreeditDetails(pIMContext, aInputFlags, nCursorPos, nCursorFlags); 6021 if (sText.isEmpty() && pThis->m_aInputEvent.maText.isEmpty()) 6022 { 6023 // change from nothing to nothing -> do not start preedit 6024 // e.g. this will activate input into a calc cell without 6025 // user input 6026 return; 6027 } 6028 6029 pThis->m_bPreeditJustChanged = true; 6030 6031 bool bEndPreedit = sText.isEmpty() && pThis->m_aInputEvent.mpTextAttr != nullptr; 6032 pThis->m_aInputEvent.maText = sText; 6033 pThis->m_aInputEvent.mnCursorPos = nCursorPos; 6034 pThis->m_aInputEvent.mnCursorFlags = nCursorFlags; 6035 pThis->m_aInputFlags = aInputFlags; 6036 pThis->m_aInputEvent.mpTextAttr = pThis->m_aInputFlags.data(); 6037 6038 SolarMutexGuard aGuard; 6039 vcl::DeletionListener aDel( pThis->m_pFrame ); 6040 6041 pThis->m_pFrame->CallCallbackExc( SalEvent::ExtTextInput, static_cast<void*>(&pThis->m_aInputEvent)); 6042 if( bEndPreedit && ! aDel.isDeleted() ) 6043 pThis->doCallEndExtTextInput(); 6044 if( ! aDel.isDeleted() ) 6045 pThis->updateIMSpotLocation(); 6046 } 6047 6048 void GtkSalFrame::IMHandler::signalIMPreeditStart( GtkIMContext*, gpointer /*im_handler*/ ) 6049 { 6050 } 6051 6052 void GtkSalFrame::IMHandler::signalIMPreeditEnd( GtkIMContext*, gpointer im_handler ) 6053 { 6054 GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler); 6055 6056 pThis->m_bPreeditJustChanged = true; 6057 6058 SolarMutexGuard aGuard; 6059 vcl::DeletionListener aDel( pThis->m_pFrame ); 6060 pThis->doCallEndExtTextInput(); 6061 if( ! aDel.isDeleted() ) 6062 pThis->updateIMSpotLocation(); 6063 } 6064 6065 gboolean GtkSalFrame::IMHandler::signalIMRetrieveSurrounding( GtkIMContext* pContext, gpointer im_handler ) 6066 { 6067 GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler); 6068 6069 SalSurroundingTextRequestEvent aEvt; 6070 aEvt.maText.clear(); 6071 aEvt.mnStart = aEvt.mnEnd = 0; 6072 6073 SolarMutexGuard aGuard; 6074 pThis->m_pFrame->CallCallback(SalEvent::SurroundingTextRequest, &aEvt); 6075 6076 OString sUTF = OUStringToOString(aEvt.maText, RTL_TEXTENCODING_UTF8); 6077 std::u16string_view sCursorText(aEvt.maText.subView(0, aEvt.mnStart)); 6078 gtk_im_context_set_surrounding(pContext, sUTF.getStr(), sUTF.getLength(), 6079 OUStringToOString(sCursorText, RTL_TEXTENCODING_UTF8).getLength()); 6080 return true; 6081 } 6082 6083 gboolean GtkSalFrame::IMHandler::signalIMDeleteSurrounding( GtkIMContext*, gint offset, gint nchars, 6084 gpointer im_handler ) 6085 { 6086 GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler); 6087 6088 // First get the surrounding text 6089 SalSurroundingTextRequestEvent aSurroundingTextEvt; 6090 aSurroundingTextEvt.maText.clear(); 6091 aSurroundingTextEvt.mnStart = aSurroundingTextEvt.mnEnd = 0; 6092 6093 SolarMutexGuard aGuard; 6094 pThis->m_pFrame->CallCallback(SalEvent::SurroundingTextRequest, &aSurroundingTextEvt); 6095 6096 // Turn offset, nchars into a utf-16 selection 6097 Selection aSelection = SalFrame::CalcDeleteSurroundingSelection(aSurroundingTextEvt.maText, 6098 aSurroundingTextEvt.mnStart, 6099 offset, nchars); 6100 Selection aInvalid(SAL_MAX_UINT32, SAL_MAX_UINT32); 6101 if (aSelection == aInvalid) 6102 return false; 6103 6104 SalSurroundingTextSelectionChangeEvent aEvt; 6105 aEvt.mnStart = aSelection.Min(); 6106 aEvt.mnEnd = aSelection.Max(); 6107 6108 pThis->m_pFrame->CallCallback(SalEvent::DeleteSurroundingTextRequest, &aEvt); 6109 6110 aSelection = Selection(aEvt.mnStart, aEvt.mnEnd); 6111 if (aSelection == aInvalid) 6112 return false; 6113 6114 return true; 6115 } 6116 6117 AbsoluteScreenPixelSize GtkSalDisplay::GetScreenSize( int nDisplayScreen ) 6118 { 6119 AbsoluteScreenPixelRectangle aRect = m_pSys->GetDisplayScreenPosSizePixel( nDisplayScreen ); 6120 return AbsoluteScreenPixelSize( aRect.GetWidth(), aRect.GetHeight() ); 6121 } 6122 6123 sal_uIntPtr GtkSalFrame::GetNativeWindowHandle(GtkWidget *pWidget) 6124 { 6125 GdkSurface* pSurface = widget_get_surface(pWidget); 6126 GdkDisplay *pDisplay = getGdkDisplay(); 6127 6128 #if defined(GDK_WINDOWING_X11) 6129 if (DLSYM_GDK_IS_X11_DISPLAY(pDisplay)) 6130 { 6131 return gdk_x11_surface_get_xid(pSurface); 6132 } 6133 #endif 6134 6135 #if defined(GDK_WINDOWING_WAYLAND) 6136 if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay)) 6137 { 6138 return reinterpret_cast<sal_uIntPtr>(gdk_wayland_surface_get_wl_surface(pSurface)); 6139 } 6140 #endif 6141 6142 return 0; 6143 } 6144 6145 void GtkInstDragSource::set_datatransfer(const css::uno::Reference<css::datatransfer::XTransferable>& rTrans, 6146 const css::uno::Reference<css::datatransfer::dnd::XDragSourceListener>& rListener) 6147 { 6148 m_xListener = rListener; 6149 m_xTrans = rTrans; 6150 } 6151 6152 void GtkInstDragSource::setActiveDragSource() 6153 { 6154 // For LibreOffice internal D&D we provide the Transferable without Gtk 6155 // intermediaries as a shortcut, see tdf#100097 for how dbaccess depends on this 6156 g_ActiveDragSource = this; 6157 g_DropSuccessSet = false; 6158 g_DropSuccess = false; 6159 } 6160 6161 #if !GTK_CHECK_VERSION(4, 0, 0) 6162 std::vector<GtkTargetEntry> GtkInstDragSource::FormatsToGtk(const css::uno::Sequence<css::datatransfer::DataFlavor> &rFormats) 6163 { 6164 return m_aConversionHelper.FormatsToGtk(rFormats); 6165 } 6166 #endif 6167 6168 void GtkInstDragSource::startDrag(const datatransfer::dnd::DragGestureEvent& rEvent, 6169 sal_Int8 sourceActions, sal_Int32 /*cursor*/, sal_Int32 /*image*/, 6170 const css::uno::Reference<css::datatransfer::XTransferable>& rTrans, 6171 const css::uno::Reference<css::datatransfer::dnd::XDragSourceListener>& rListener) 6172 { 6173 set_datatransfer(rTrans, rListener); 6174 6175 if (m_pFrame) 6176 { 6177 setActiveDragSource(); 6178 6179 m_pFrame->startDrag(rEvent, rTrans, m_aConversionHelper ,VclToGdk(sourceActions)); 6180 } 6181 else 6182 dragFailed(); 6183 } 6184 6185 void GtkSalFrame::startDrag(const css::datatransfer::dnd::DragGestureEvent& rEvent, 6186 const css::uno::Reference<css::datatransfer::XTransferable>& rTrans, 6187 VclToGtkHelper& rConversionHelper, 6188 GdkDragAction sourceActions) 6189 { 6190 SolarMutexGuard aGuard; 6191 6192 assert(m_pDragSource); 6193 6194 #if GTK_CHECK_VERSION(4, 0, 0) 6195 6196 GdkSeat *pSeat = gdk_display_get_default_seat(getGdkDisplay()); 6197 GdkDrag* pDrag = gdk_drag_begin(widget_get_surface(getMouseEventWidget()), 6198 gdk_seat_get_pointer(pSeat), 6199 transerable_content_new(&rConversionHelper, rTrans.get()), 6200 sourceActions, 6201 rEvent.DragOriginX, rEvent.DragOriginY); 6202 6203 g_signal_connect(G_OBJECT(pDrag), "drop-performed", G_CALLBACK(signalDragEnd), this); 6204 g_signal_connect(G_OBJECT(pDrag), "cancel", G_CALLBACK(signalDragFailed), this); 6205 g_signal_connect(G_OBJECT(pDrag), "dnd-finished", G_CALLBACK(signalDragDelete), this); 6206 6207 #else 6208 6209 auto aFormats = rTrans->getTransferDataFlavors(); 6210 auto aGtkTargets = rConversionHelper.FormatsToGtk(aFormats); 6211 6212 GtkTargetList *pTargetList = gtk_target_list_new(aGtkTargets.data(), aGtkTargets.size()); 6213 6214 gint nDragButton = 1; // default to left button 6215 css::awt::MouseEvent aEvent; 6216 if (rEvent.Event >>= aEvent) 6217 { 6218 if (aEvent.Buttons & css::awt::MouseButton::LEFT ) 6219 nDragButton = 1; 6220 else if (aEvent.Buttons & css::awt::MouseButton::RIGHT) 6221 nDragButton = 3; 6222 else if (aEvent.Buttons & css::awt::MouseButton::MIDDLE) 6223 nDragButton = 2; 6224 } 6225 6226 GdkEvent aFakeEvent; 6227 memset(&aFakeEvent, 0, sizeof(GdkEvent)); 6228 aFakeEvent.type = GDK_BUTTON_PRESS; 6229 aFakeEvent.button.window = widget_get_surface(getMouseEventWidget()); 6230 aFakeEvent.button.time = GDK_CURRENT_TIME; 6231 6232 aFakeEvent.button.device = gtk_get_current_event_device(); 6233 // if no current event to determine device, or (tdf#140272) the device will be unsuitable then find an 6234 // appropriate device to use. 6235 if (!aFakeEvent.button.device || !gdk_device_get_window_at_position(aFakeEvent.button.device, nullptr, nullptr)) 6236 { 6237 GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay()); 6238 GList* pDevices = gdk_device_manager_list_devices(pDeviceManager, GDK_DEVICE_TYPE_MASTER); 6239 for (GList* pEntry = pDevices; pEntry; pEntry = pEntry->next) 6240 { 6241 GdkDevice* pDevice = static_cast<GdkDevice*>(pEntry->data); 6242 if (gdk_device_get_source(pDevice) == GDK_SOURCE_KEYBOARD) 6243 continue; 6244 if (gdk_device_get_window_at_position(pDevice, nullptr, nullptr)) 6245 { 6246 aFakeEvent.button.device = pDevice; 6247 break; 6248 } 6249 } 6250 g_list_free(pDevices); 6251 } 6252 6253 GdkDragContext *pDrag; 6254 if (!aFakeEvent.button.device || !gdk_device_get_window_at_position(aFakeEvent.button.device, nullptr, nullptr)) 6255 pDrag = nullptr; 6256 else 6257 pDrag = gtk_drag_begin_with_coordinates(getMouseEventWidget(), 6258 pTargetList, 6259 sourceActions, 6260 nDragButton, 6261 &aFakeEvent, 6262 rEvent.DragOriginX, 6263 rEvent.DragOriginY); 6264 6265 gtk_target_list_unref(pTargetList); 6266 6267 for (auto &a : aGtkTargets) 6268 g_free(a.target); 6269 #endif 6270 6271 if (!pDrag) 6272 m_pDragSource->dragFailed(); 6273 } 6274 6275 void GtkInstDragSource::dragFailed() 6276 { 6277 if (m_xListener.is()) 6278 { 6279 datatransfer::dnd::DragSourceDropEvent aEv; 6280 aEv.DropAction = datatransfer::dnd::DNDConstants::ACTION_NONE; 6281 aEv.DropSuccess = false; 6282 auto xListener = m_xListener; 6283 m_xListener.clear(); 6284 xListener->dragDropEnd(aEv); 6285 } 6286 } 6287 6288 #if GTK_CHECK_VERSION(4, 0, 0) 6289 void GtkSalFrame::signalDragFailed(GdkDrag* /*drag*/, GdkDragCancelReason /*reason*/, gpointer frame) 6290 #else 6291 gboolean GtkSalFrame::signalDragFailed(GtkWidget* /*widget*/, GdkDragContext* /*context*/, GtkDragResult /*result*/, gpointer frame) 6292 #endif 6293 { 6294 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 6295 if (pThis->m_pDragSource) 6296 pThis->m_pDragSource->dragFailed(); 6297 #if !GTK_CHECK_VERSION(4, 0, 0) 6298 return false; 6299 #endif 6300 } 6301 6302 void GtkInstDragSource::dragDelete() 6303 { 6304 if (m_xListener.is()) 6305 { 6306 datatransfer::dnd::DragSourceDropEvent aEv; 6307 aEv.DropAction = datatransfer::dnd::DNDConstants::ACTION_MOVE; 6308 aEv.DropSuccess = true; 6309 auto xListener = m_xListener; 6310 m_xListener.clear(); 6311 xListener->dragDropEnd(aEv); 6312 } 6313 } 6314 6315 #if GTK_CHECK_VERSION(4, 0, 0) 6316 void GtkSalFrame::signalDragDelete(GdkDrag* /*context*/, gpointer frame) 6317 #else 6318 void GtkSalFrame::signalDragDelete(GtkWidget* /*widget*/, GdkDragContext* /*context*/, gpointer frame) 6319 #endif 6320 { 6321 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 6322 if (!pThis->m_pDragSource) 6323 return; 6324 pThis->m_pDragSource->dragDelete(); 6325 } 6326 6327 #if GTK_CHECK_VERSION(4, 0, 0) 6328 void GtkInstDragSource::dragEnd(GdkDrag* context) 6329 #else 6330 void GtkInstDragSource::dragEnd(GdkDragContext* context) 6331 #endif 6332 { 6333 if (m_xListener.is()) 6334 { 6335 datatransfer::dnd::DragSourceDropEvent aEv; 6336 #if GTK_CHECK_VERSION(4, 0, 0) 6337 aEv.DropAction = GdkToVcl(gdk_drag_get_selected_action(context)); 6338 #else 6339 aEv.DropAction = GdkToVcl(gdk_drag_context_get_selected_action(context)); 6340 #endif 6341 // an internal drop can accept the drop but fail with dropComplete( false ) 6342 // this is different than the GTK API 6343 if (g_DropSuccessSet) 6344 aEv.DropSuccess = g_DropSuccess; 6345 else 6346 aEv.DropSuccess = true; 6347 auto xListener = m_xListener; 6348 m_xListener.clear(); 6349 xListener->dragDropEnd(aEv); 6350 } 6351 g_ActiveDragSource = nullptr; 6352 } 6353 6354 #if GTK_CHECK_VERSION(4, 0, 0) 6355 void GtkSalFrame::signalDragEnd(GdkDrag* context, gpointer frame) 6356 #else 6357 void GtkSalFrame::signalDragEnd(GtkWidget* /*widget*/, GdkDragContext* context, gpointer frame) 6358 #endif 6359 { 6360 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 6361 if (!pThis->m_pDragSource) 6362 return; 6363 pThis->m_pDragSource->dragEnd(context); 6364 } 6365 6366 #if !GTK_CHECK_VERSION(4, 0, 0) 6367 void GtkInstDragSource::dragDataGet(GtkSelectionData *data, guint info) 6368 { 6369 m_aConversionHelper.setSelectionData(m_xTrans, data, info); 6370 } 6371 6372 void GtkSalFrame::signalDragDataGet(GtkWidget* /*widget*/, GdkDragContext* /*context*/, GtkSelectionData *data, guint info, 6373 guint /*time*/, gpointer frame) 6374 { 6375 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); 6376 if (!pThis->m_pDragSource) 6377 return; 6378 pThis->m_pDragSource->dragDataGet(data, info); 6379 } 6380 #endif 6381 6382 bool GtkSalFrame::CallCallbackExc(SalEvent nEvent, const void* pEvent) const 6383 { 6384 SolarMutexGuard aGuard; 6385 bool nRet = false; 6386 try 6387 { 6388 nRet = CallCallback(nEvent, pEvent); 6389 } 6390 catch (...) 6391 { 6392 GetGtkSalData()->setException(std::current_exception()); 6393 } 6394 return nRet; 6395 } 6396 6397 #if !GTK_CHECK_VERSION(4, 0, 0) 6398 void GtkSalFrame::nopaint_container_resize_children(GtkContainer *pContainer) 6399 { 6400 bool bOrigSalObjectSetPosSize = m_bSalObjectSetPosSize; 6401 m_bSalObjectSetPosSize = true; 6402 gtk_container_resize_children(pContainer); 6403 m_bSalObjectSetPosSize = bOrigSalObjectSetPosSize; 6404 } 6405 #endif 6406 6407 GdkEvent* GtkSalFrame::makeFakeKeyPress(GtkWidget* pWidget) 6408 { 6409 #if !GTK_CHECK_VERSION(4, 0, 0) 6410 GdkEvent *event = gdk_event_new(GDK_KEY_PRESS); 6411 event->key.window = GDK_WINDOW(g_object_ref(widget_get_surface(pWidget))); 6412 6413 GdkSeat *seat = gdk_display_get_default_seat(gtk_widget_get_display(pWidget)); 6414 gdk_event_set_device(event, gdk_seat_get_keyboard(seat)); 6415 6416 event->key.send_event = 1 /* TRUE */; 6417 event->key.time = gtk_get_current_event_time(); 6418 event->key.state = 0; 6419 event->key.keyval = 0; 6420 event->key.length = 0; 6421 event->key.string = nullptr; 6422 event->key.hardware_keycode = 0; 6423 event->key.group = 0; 6424 event->key.is_modifier = false; 6425 return event; 6426 #else 6427 (void)pWidget; 6428 return nullptr; 6429 #endif 6430 } 6431 6432 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 6433
