Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / unx / gtk / window / gtksalframe.cxx
bloba28c144675f7c1d17c57c011062e55fa8f9ba269
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <unx/gtk/gtkframe.hxx>
21 #include <unx/gtk/gtkdata.hxx>
22 #include <unx/gtk/gtkinst.hxx>
23 #include <unx/gtk/gtkgdi.hxx>
24 #include <vcl/help.hxx>
25 #include <vcl/keycodes.hxx>
26 #include <vcl/layout.hxx>
27 #include <unx/wmadaptor.hxx>
28 #include <unx/sm.hxx>
29 #include <unx/salbmp.h>
30 #include <generic/genprn.h>
31 #include <generic/geninst.h>
32 #include <headless/svpgdi.hxx>
33 #include <osl/file.hxx>
34 #include <rtl/bootstrap.hxx>
35 #include <rtl/process.h>
36 #include <vcl/floatwin.hxx>
37 #include <vcl/svapp.hxx>
38 #include <vcl/window.hxx>
39 #include <vcl/settings.hxx>
41 #if !GTK_CHECK_VERSION(3,0,0)
42 # include <unx/x11/xlimits.hxx>
43 #endif
44 #if defined(ENABLE_DBUS) && defined(ENABLE_GIO)
45 # include <unx/gtk/gtksalmenu.hxx>
46 #endif
47 #if defined ENABLE_GMENU_INTEGRATION // defined in gtksalmenu.hxx above
48 # include <unx/gtk/hudawareness.h>
49 #endif
51 #include <gtk/gtk.h>
52 #include <prex.h>
53 #include <X11/Xatom.h>
54 #include <gdk/gdkx.h>
55 #include <postx.h>
57 #include <dlfcn.h>
58 #include <vcl/salbtype.hxx>
59 #include <vcl/bitmapex.hxx>
60 #include <impbmp.hxx>
61 #include <svids.hrc>
62 #include <sal/macros.h>
64 #include <basegfx/range/b2ibox.hxx>
65 #include <basegfx/vector/b2ivector.hxx>
67 #include <algorithm>
68 #include <glib/gprintf.h>
70 #if OSL_DEBUG_LEVEL > 1
71 # include <cstdio>
72 #endif
74 #include <comphelper/processfactory.hxx>
75 #include <comphelper/sequenceashashmap.hxx>
76 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
77 #include <com/sun/star/accessibility/AccessibleRole.hpp>
78 #include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
79 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
80 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
81 #include <com/sun/star/frame/Desktop.hpp>
82 #include <com/sun/star/frame/ModuleManager.hpp>
83 #include <com/sun/star/frame/XFrame.hpp>
84 #include <com/sun/star/util/URLTransformer.hpp>
86 #if GTK_CHECK_VERSION(3,0,0)
87 # include <gdk/gdkkeysyms-compat.h>
88 #endif
90 #ifdef ENABLE_DBUS
91 #include <dbus/dbus-glib.h>
93 #define GSM_DBUS_SERVICE "org.gnome.SessionManager"
94 #define GSM_DBUS_PATH "/org/gnome/SessionManager"
95 #define GSM_DBUS_INTERFACE "org.gnome.SessionManager"
96 #endif
98 #include <config_folders.h>
100 #if GTK_CHECK_VERSION(3,0,0)
101 #define IS_WIDGET_REALIZED gtk_widget_get_realized
102 #define IS_WIDGET_MAPPED gtk_widget_get_mapped
103 #else
104 #define IS_WIDGET_REALIZED GTK_WIDGET_REALIZED
105 #define IS_WIDGET_MAPPED GTK_WIDGET_MAPPED
106 #endif
108 #if !GTK_CHECK_VERSION(3,0,0)
109 #define GDK_IS_X11_DISPLAY(foo) (true)
110 #endif
112 using namespace com::sun::star;
114 int GtkSalFrame::m_nFloats = 0;
116 #if defined ENABLE_GMENU_INTEGRATION
117 static GDBusConnection* pSessionBus = NULL;
118 #endif
120 static sal_uInt16 GetKeyModCode( guint state )
122 sal_uInt16 nCode = 0;
123 if( (state & GDK_SHIFT_MASK) )
124 nCode |= KEY_SHIFT;
125 if( (state & GDK_CONTROL_MASK) )
126 nCode |= KEY_MOD1;
127 if( (state & GDK_MOD1_MASK) )
128 nCode |= KEY_MOD2;
130 // Map Meta/Super keys to MOD3 modifier on all Unix systems
131 // except Mac OS X
132 if ( (state & GDK_META_MASK ) || ( state & GDK_SUPER_MASK ) )
133 nCode |= KEY_MOD3;
134 return nCode;
137 static sal_uInt16 GetMouseModCode( guint state )
139 sal_uInt16 nCode = GetKeyModCode( state );
140 if( (state & GDK_BUTTON1_MASK) )
141 nCode |= MOUSE_LEFT;
142 if( (state & GDK_BUTTON2_MASK) )
143 nCode |= MOUSE_MIDDLE;
144 if( (state & GDK_BUTTON3_MASK) )
145 nCode |= MOUSE_RIGHT;
147 return nCode;
150 static sal_uInt16 GetKeyCode( guint keyval )
152 sal_uInt16 nCode = 0;
153 if( keyval >= GDK_0 && keyval <= GDK_9 )
154 nCode = KEY_0 + (keyval-GDK_0);
155 else if( keyval >= GDK_KP_0 && keyval <= GDK_KP_9 )
156 nCode = KEY_0 + (keyval-GDK_KP_0);
157 else if( keyval >= GDK_A && keyval <= GDK_Z )
158 nCode = KEY_A + (keyval-GDK_A );
159 else if( keyval >= GDK_a && keyval <= GDK_z )
160 nCode = KEY_A + (keyval-GDK_a );
161 else if( keyval >= GDK_F1 && keyval <= GDK_F26 )
163 #if !GTK_CHECK_VERSION(3,0,0)
164 if( GetGtkSalData()->GetGtkDisplay()->IsNumLockFromXS() )
166 nCode = KEY_F1 + (keyval-GDK_F1);
168 else
169 #endif
171 switch( keyval )
173 // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx
174 case GDK_L2:
175 #if !GTK_CHECK_VERSION(3,0,0)
176 if( GetGtkSalData()->GetGtkDisplay()->GetServerVendor() == vendor_sun )
177 nCode = KEY_REPEAT;
178 else
179 #endif
180 nCode = KEY_F12;
181 break;
182 case GDK_L3: nCode = KEY_PROPERTIES; break;
183 case GDK_L4: nCode = KEY_UNDO; break;
184 case GDK_L6: nCode = KEY_COPY; break; // KEY_F16
185 case GDK_L8: nCode = KEY_PASTE; break; // KEY_F18
186 case GDK_L10: nCode = KEY_CUT; break; // KEY_F20
187 default:
188 nCode = KEY_F1 + (keyval-GDK_F1); break;
192 else
194 switch( keyval )
196 case GDK_KP_Down:
197 case GDK_Down: nCode = KEY_DOWN; break;
198 case GDK_KP_Up:
199 case GDK_Up: nCode = KEY_UP; break;
200 case GDK_KP_Left:
201 case GDK_Left: nCode = KEY_LEFT; break;
202 case GDK_KP_Right:
203 case GDK_Right: nCode = KEY_RIGHT; break;
204 case GDK_KP_Begin:
205 case GDK_KP_Home:
206 case GDK_Begin:
207 case GDK_Home: nCode = KEY_HOME; break;
208 case GDK_KP_End:
209 case GDK_End: nCode = KEY_END; break;
210 case GDK_KP_Page_Up:
211 case GDK_Page_Up: nCode = KEY_PAGEUP; break;
212 case GDK_KP_Page_Down:
213 case GDK_Page_Down: nCode = KEY_PAGEDOWN; break;
214 case GDK_KP_Enter:
215 case GDK_Return: nCode = KEY_RETURN; break;
216 case GDK_Escape: nCode = KEY_ESCAPE; break;
217 case GDK_ISO_Left_Tab:
218 case GDK_KP_Tab:
219 case GDK_Tab: nCode = KEY_TAB; break;
220 case GDK_BackSpace: nCode = KEY_BACKSPACE; break;
221 case GDK_KP_Space:
222 case GDK_space: nCode = KEY_SPACE; break;
223 case GDK_KP_Insert:
224 case GDK_Insert: nCode = KEY_INSERT; break;
225 case GDK_KP_Delete:
226 case GDK_Delete: nCode = KEY_DELETE; break;
227 case GDK_plus:
228 case GDK_KP_Add: nCode = KEY_ADD; break;
229 case GDK_minus:
230 case GDK_KP_Subtract: nCode = KEY_SUBTRACT; break;
231 case GDK_asterisk:
232 case GDK_KP_Multiply: nCode = KEY_MULTIPLY; break;
233 case GDK_slash:
234 case GDK_KP_Divide: nCode = KEY_DIVIDE; break;
235 case GDK_period: nCode = KEY_POINT; break;
236 case GDK_decimalpoint: nCode = KEY_POINT; break;
237 case GDK_comma: nCode = KEY_COMMA; break;
238 case GDK_less: nCode = KEY_LESS; break;
239 case GDK_greater: nCode = KEY_GREATER; break;
240 case GDK_KP_Equal:
241 case GDK_equal: nCode = KEY_EQUAL; break;
242 case GDK_Find: nCode = KEY_FIND; break;
243 case GDK_Menu: nCode = KEY_CONTEXTMENU;break;
244 case GDK_Help: nCode = KEY_HELP; break;
245 case GDK_Undo: nCode = KEY_UNDO; break;
246 case GDK_Redo: nCode = KEY_REPEAT; break;
247 case GDK_KP_Decimal:
248 case GDK_KP_Separator: nCode = KEY_DECIMAL; break;
249 case GDK_asciitilde: nCode = KEY_TILDE; break;
250 case GDK_leftsinglequotemark:
251 case GDK_quoteleft: nCode = KEY_QUOTELEFT; break;
252 case GDK_bracketleft: nCode = KEY_BRACKETLEFT; break;
253 case GDK_bracketright: nCode = KEY_BRACKETRIGHT; break;
254 case GDK_semicolon: nCode = KEY_SEMICOLON; break;
255 case GDK_quoteright: nCode = KEY_QUOTERIGHT; break;
256 // some special cases, also see saldisp.cxx
257 // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000
258 case 0x1000FF02: // apXK_Copy
259 nCode = KEY_COPY;
260 break;
261 case 0x1000FF03: // apXK_Cut
262 nCode = KEY_CUT;
263 break;
264 case 0x1000FF04: // apXK_Paste
265 nCode = KEY_PASTE;
266 break;
267 case 0x1000FF14: // apXK_Repeat
268 nCode = KEY_REPEAT;
269 break;
270 // Exit, Save
271 // - - - - - - - - - - - - - - D E C - - - - - - - - - - - - - 0x1000
272 case 0x1000FF00:
273 nCode = KEY_DELETE;
274 break;
275 // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000
276 case 0x1000FF73: // hpXK_DeleteChar
277 nCode = KEY_DELETE;
278 break;
279 case 0x1000FF74: // hpXK_BackTab
280 case 0x1000FF75: // hpXK_KP_BackTab
281 nCode = KEY_TAB;
282 break;
283 // - - - - - - - - - - - - - - I B M - - - - - - - - - - - - -
284 // - - - - - - - - - - - - - - O S F - - - - - - - - - - - - - 0x1004
285 case 0x1004FF02: // osfXK_Copy
286 nCode = KEY_COPY;
287 break;
288 case 0x1004FF03: // osfXK_Cut
289 nCode = KEY_CUT;
290 break;
291 case 0x1004FF04: // osfXK_Paste
292 nCode = KEY_PASTE;
293 break;
294 case 0x1004FF07: // osfXK_BackTab
295 nCode = KEY_TAB;
296 break;
297 case 0x1004FF08: // osfXK_BackSpace
298 nCode = KEY_BACKSPACE;
299 break;
300 case 0x1004FF1B: // osfXK_Escape
301 nCode = KEY_ESCAPE;
302 break;
303 // Up, Down, Left, Right, PageUp, PageDown
304 // - - - - - - - - - - - - - - S C O - - - - - - - - - - - - -
305 // - - - - - - - - - - - - - - S G I - - - - - - - - - - - - - 0x1007
306 // - - - - - - - - - - - - - - S N I - - - - - - - - - - - - -
307 // - - - - - - - - - - - - - - S U N - - - - - - - - - - - - - 0x1005
308 case 0x1005FF10: // SunXK_F36
309 nCode = KEY_F11;
310 break;
311 case 0x1005FF11: // SunXK_F37
312 nCode = KEY_F12;
313 break;
314 case 0x1005FF70: // SunXK_Props
315 nCode = KEY_PROPERTIES;
316 break;
317 case 0x1005FF71: // SunXK_Front
318 nCode = KEY_FRONT;
319 break;
320 case 0x1005FF72: // SunXK_Copy
321 nCode = KEY_COPY;
322 break;
323 case 0x1005FF73: // SunXK_Open
324 nCode = KEY_OPEN;
325 break;
326 case 0x1005FF74: // SunXK_Paste
327 nCode = KEY_PASTE;
328 break;
329 case 0x1005FF75: // SunXK_Cut
330 nCode = KEY_CUT;
331 break;
335 return nCode;
338 static guint GetKeyValFor(GdkKeymap* pKeyMap, guint16 hardware_keycode, guint8 group)
340 guint updated_keyval = 0;
341 gdk_keymap_translate_keyboard_state(pKeyMap, hardware_keycode,
342 (GdkModifierType)0, group, &updated_keyval, NULL, NULL, NULL);
343 return updated_keyval;
346 // F10 means either KEY_F10 or KEY_MENU, which has to be decided
347 // in the independent part.
348 struct KeyAlternate
350 sal_uInt16 nKeyCode;
351 sal_Unicode nCharCode;
352 KeyAlternate() : nKeyCode( 0 ), nCharCode( 0 ) {}
353 KeyAlternate( sal_uInt16 nKey, sal_Unicode nChar = 0 ) : nKeyCode( nKey ), nCharCode( nChar ) {}
356 inline KeyAlternate
357 GetAlternateKeyCode( const sal_uInt16 nKeyCode )
359 KeyAlternate aAlternate;
361 switch( nKeyCode )
363 case KEY_F10: aAlternate = KeyAlternate( KEY_MENU );break;
364 case KEY_F24: aAlternate = KeyAlternate( KEY_SUBTRACT, '-' );break;
367 return aAlternate;
370 #if GTK_CHECK_VERSION(3,0,0)
372 namespace {
373 /// Decouple SalFrame lifetime from damagetracker lifetime
374 struct DamageTracker : public basebmp::IBitmapDeviceDamageTracker
376 DamageTracker(GtkSalFrame& rFrame) : m_rFrame(rFrame)
379 virtual ~DamageTracker() {}
381 virtual void damaged(const basegfx::B2IBox& rDamageRect) const SAL_OVERRIDE
383 m_rFrame.damaged(rDamageRect);
386 GtkSalFrame& m_rFrame;
390 static bool dumpframes = false;
391 #endif
393 void GtkSalFrame::doKeyCallback( guint state,
394 guint keyval,
395 guint16 hardware_keycode,
396 guint8 group,
397 guint32 time,
398 sal_Unicode aOrigCode,
399 bool bDown,
400 bool bSendRelease
403 SalKeyEvent aEvent;
405 aEvent.mnTime = time;
406 aEvent.mnCharCode = aOrigCode;
407 aEvent.mnRepeat = 0;
409 vcl::DeletionListener aDel( this );
411 #if GTK_CHECK_VERSION(3,0,0)
412 #if 0
413 // shift-zero forces a re-draw and event is swallowed
414 if (keyval == GDK_0)
416 fprintf( stderr, "force widget_queue_draw\n");
417 gtk_widget_queue_draw (m_pWindow);
418 return;
420 else if (keyval == GDK_1)
422 fprintf( stderr, "force repaint all\n");
423 TriggerPaintEvent();
424 return;
426 else if (keyval == GDK_2)
428 dumpframes = !dumpframes;
429 fprintf(stderr, "toggle dump frames to %d\n", dumpframes);
430 return;
432 #endif
433 #endif
436 * #i42122# translate all keys with Ctrl and/or Alt to group 0 else
437 * shortcuts (e.g. Ctrl-o) will not work but be inserted by the
438 * application
440 * #i52338# do this for all keys that the independent part has no key code
441 * for
443 * fdo#41169 rather than use group 0, detect if there is a group which can
444 * be used to input Latin text and use that if possible
446 aEvent.mnCode = GetKeyCode( keyval );
447 if( aEvent.mnCode == 0 )
449 gint best_group = SAL_MAX_INT32;
451 // Try and find Latin layout
452 GdkKeymap* keymap = gdk_keymap_get_default();
453 GdkKeymapKey *keys;
454 gint n_keys;
455 if (gdk_keymap_get_entries_for_keyval(keymap, GDK_A, &keys, &n_keys))
457 // Find the lowest group that supports Latin layout
458 for (gint i = 0; i < n_keys; ++i)
460 if (keys[i].level != 0 && keys[i].level != 1)
461 continue;
462 best_group = std::min(best_group, keys[i].group);
463 if (best_group == 0)
464 break;
466 g_free(keys);
469 //Unavailable, go with original group then I suppose
470 if (best_group == SAL_MAX_INT32)
471 best_group = group;
473 guint updated_keyval = GetKeyValFor(keymap, hardware_keycode, best_group);
474 aEvent.mnCode = GetKeyCode(updated_keyval);
477 aEvent.mnCode |= GetKeyModCode( state );
479 if( bDown )
481 bool bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent );
482 // #i46889# copy AlternatKeyCode handling from generic plugin
483 if( ! bHandled )
485 KeyAlternate aAlternate = GetAlternateKeyCode( aEvent.mnCode );
486 if( aAlternate.nKeyCode )
488 aEvent.mnCode = aAlternate.nKeyCode;
489 if( aAlternate.nCharCode )
490 aEvent.mnCharCode = aAlternate.nCharCode;
491 bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent );
494 if( bSendRelease && ! aDel.isDeleted() )
496 CallCallback( SALEVENT_KEYUP, &aEvent );
499 else
500 CallCallback( SALEVENT_KEYUP, &aEvent );
503 GtkSalFrame::GraphicsHolder::~GraphicsHolder()
505 delete pGraphics;
508 GtkSalFrame::GtkSalFrame( SalFrame* pParent, sal_uLong nStyle )
509 : m_nXScreen( getDisplay()->GetDefaultXScreen() )
511 getDisplay()->registerFrame( this );
512 m_bDefaultPos = true;
513 m_bDefaultSize = ( (nStyle & SAL_FRAME_STYLE_SIZEABLE) && ! pParent );
514 m_bWindowIsGtkPlug = false;
515 #if defined(ENABLE_DBUS) && defined(ENABLE_GIO)
516 m_pLastSyncedDbusMenu = NULL;
517 #endif
518 Init( pParent, nStyle );
521 GtkSalFrame::GtkSalFrame( SystemParentData* pSysData )
522 : m_nXScreen( getDisplay()->GetDefaultXScreen() )
524 getDisplay()->registerFrame( this );
525 // permanently ignore errors from our unruly children ...
526 GetGenericData()->ErrorTrapPush();
527 m_bDefaultPos = true;
528 m_bDefaultSize = true;
529 #if defined(ENABLE_DBUS) && defined(ENABLE_GIO)
530 m_pLastSyncedDbusMenu = NULL;
531 #endif
532 Init( pSysData );
535 #ifdef ENABLE_GMENU_INTEGRATION
537 static void
538 gdk_x11_window_set_utf8_property (GdkWindow *window,
539 const gchar *name,
540 const gchar *value)
542 #if !GTK_CHECK_VERSION(3,0,0)
543 GdkDisplay* display = gdk_window_get_display (window);
545 if (value != NULL)
547 XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
548 GDK_WINDOW_XID (window),
549 gdk_x11_get_xatom_by_name_for_display (display, name),
550 gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
551 PropModeReplace, reinterpret_cast<guchar const *>(value), strlen (value));
553 else
555 XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
556 GDK_WINDOW_XID (window),
557 gdk_x11_get_xatom_by_name_for_display (display, name));
559 #endif
562 // AppMenu watch functions.
564 static void ObjectDestroyedNotify( gpointer data )
566 if ( data ) {
567 g_object_unref( data );
571 #if defined(ENABLE_DBUS) && defined(ENABLE_GIO)
572 void GtkSalFrame::EnsureDbusMenuSynced()
574 GtkSalMenu* pSalMenu = static_cast<GtkSalMenu*>(GetMenu());
575 if(m_pLastSyncedDbusMenu != pSalMenu) {
576 m_pLastSyncedDbusMenu = pSalMenu;
577 static_cast<GtkSalMenu*>(pSalMenu)->Activate();
580 #endif
582 static void hud_activated( gboolean hud_active, gpointer user_data )
584 if ( hud_active )
586 SolarMutexGuard aGuard;
587 GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data );
588 GtkSalMenu* pSalMenu = reinterpret_cast< GtkSalMenu* >( pSalFrame->GetMenu() );
590 if ( pSalMenu )
591 pSalMenu->UpdateFull();
595 static void activate_uno(GSimpleAction *action, GVariant*, gpointer)
597 uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
599 uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
601 uno::Reference < css::frame::XFrame > xFrame(xDesktop->getActiveFrame());
602 if (!xFrame.is())
603 xFrame = uno::Reference < css::frame::XFrame >(xDesktop, uno::UNO_QUERY);
605 if (!xFrame.is())
606 return;
608 uno::Reference< css::frame::XDispatchProvider > xDispatchProvider(xFrame, uno::UNO_QUERY);
609 if (!xDispatchProvider.is())
610 return;
612 gchar *strval = NULL;
613 g_object_get(action, "name", &strval, NULL);
614 if (!strval)
615 return;
617 if (strcmp(strval, "New") == 0)
619 uno::Reference<frame::XModuleManager2> xModuleManager(frame::ModuleManager::create(xContext));
620 OUString aModuleId(xModuleManager->identify(xFrame));
621 if (aModuleId.isEmpty())
622 return;
624 comphelper::SequenceAsHashMap lModuleDescription(xModuleManager->getByName(aModuleId));
625 OUString sFactoryService;
626 lModuleDescription[OUString("ooSetupFactoryEmptyDocumentURL")] >>= sFactoryService;
627 if (sFactoryService.isEmpty())
628 return;
630 uno::Sequence < css::beans::PropertyValue > args(0);
631 xDesktop->loadComponentFromURL(sFactoryService, OUString("_blank"), 0, args);
632 return;
635 OUString sCommand(".uno:");
636 sCommand += OUString(strval, strlen(strval), RTL_TEXTENCODING_UTF8);
637 g_free(strval);
639 css::util::URL aCommand;
640 aCommand.Complete = sCommand;
641 uno::Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create(xContext);
642 xParser->parseStrict(aCommand);
644 uno::Reference< css::frame::XDispatch > xDisp = xDispatchProvider->queryDispatch(aCommand, OUString(), 0);
646 if (!xDisp.is())
647 return;
649 xDisp->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >());
652 static const GActionEntry app_entries[] = {
653 { "OptionsTreeDialog", activate_uno, NULL, NULL, NULL, {0} },
654 { "About", activate_uno, NULL, NULL, NULL, {0} },
655 { "HelpIndex", activate_uno, NULL, NULL, NULL, {0} },
656 { "Quit", activate_uno, NULL, NULL, NULL, {0} },
657 { "New", activate_uno, NULL, NULL, NULL, {0} }
660 gboolean ensure_dbus_setup( gpointer data )
662 GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( data );
663 GdkWindow* gdkWindow = widget_get_window( pSalFrame->getWindow() );
665 if ( gdkWindow != NULL && g_object_get_data( G_OBJECT( gdkWindow ), "g-lo-menubar" ) == NULL )
667 // Get a DBus session connection.
668 if(!pSessionBus)
669 pSessionBus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
670 if( !pSessionBus )
671 return FALSE;
673 // Create menu model and action group attached to this frame.
674 GMenuModel* pMenuModel = G_MENU_MODEL( g_lo_menu_new() );
675 GActionGroup* pActionGroup = reinterpret_cast<GActionGroup*>(g_lo_action_group_new( static_cast< gpointer >( pSalFrame ) ));
677 // Generate menu paths.
678 ::Window windowId = GDK_WINDOW_XID( gdkWindow );
679 gchar* aDBusWindowPath = g_strdup_printf( "/org/libreoffice/window/%lu", windowId );
680 gchar* aDBusMenubarPath = g_strdup_printf( "/org/libreoffice/window/%lu/menus/menubar", windowId );
682 // Set window properties.
683 g_object_set_data_full( G_OBJECT( gdkWindow ), "g-lo-menubar", pMenuModel, ObjectDestroyedNotify );
684 g_object_set_data_full( G_OBJECT( gdkWindow ), "g-lo-action-group", pActionGroup, ObjectDestroyedNotify );
686 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_ID", "org.libreoffice" );
687 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_UNIQUE_BUS_NAME", g_dbus_connection_get_unique_name( pSessionBus ) );
688 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APPLICATION_OBJECT_PATH", "/org/libreoffice" );
689 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_WINDOW_OBJECT_PATH", aDBusWindowPath );
690 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_MENUBAR_OBJECT_PATH", aDBusMenubarPath );
692 // Publish the menu model and the action group.
693 SAL_INFO("vcl.unity", "exporting menu model at " << pMenuModel << " for window " << windowId);
694 pSalFrame->m_nMenuExportId = g_dbus_connection_export_menu_model (pSessionBus, aDBusMenubarPath, pMenuModel, NULL);
695 SAL_INFO("vcl.unity", "exporting action group at " << pActionGroup << " for window " << windowId);
696 pSalFrame->m_nActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, aDBusWindowPath, pActionGroup, NULL);
697 pSalFrame->m_nHudAwarenessId = hud_awareness_register( pSessionBus, aDBusMenubarPath, hud_activated, pSalFrame, NULL, NULL );
699 // fdo#70885 we don't want app menu under Unity
700 bool bDesktopIsUnity = (SalGetDesktopEnvironment() == "UNITY");
702 if (!bDesktopIsUnity)
703 gdk_x11_window_set_utf8_property( gdkWindow, "_GTK_APP_MENU_OBJECT_PATH", "/org/libreoffice/menus/appmenu" );
705 //app menu, to-do translations, block normal menus when active, honor use appmenu settings
706 ResMgr* pMgr = ImplGetResMgr();
707 if( pMgr && !bDesktopIsUnity )
709 GMenu *menu = g_menu_new ();
710 GMenuItem* item;
712 GMenu *firstsubmenu = g_menu_new ();
714 OString sNew(OUStringToOString(ResId(SV_BUTTONTEXT_NEW, *pMgr).toString(),
715 RTL_TEXTENCODING_UTF8).replaceFirst("~", "_"));
717 item = g_menu_item_new(sNew.getStr(), "app.New");
718 g_menu_append_item( firstsubmenu, item );
719 g_object_unref(item);
721 g_menu_append_section( menu, NULL, G_MENU_MODEL(firstsubmenu));
722 g_object_unref(firstsubmenu);
724 GMenu *secondsubmenu = g_menu_new ();
726 OString sPreferences(OUStringToOString(ResId(SV_STDTEXT_PREFERENCES, *pMgr).toString(),
727 RTL_TEXTENCODING_UTF8).replaceFirst("~", "_"));
729 item = g_menu_item_new(sPreferences.getStr(), "app.OptionsTreeDialog");
730 g_menu_append_item( secondsubmenu, item );
731 g_object_unref(item);
733 g_menu_append_section( menu, NULL, G_MENU_MODEL(secondsubmenu));
734 g_object_unref(secondsubmenu);
736 GMenu *thirdsubmenu = g_menu_new ();
738 OString sHelp(OUStringToOString(ResId(SV_BUTTONTEXT_HELP, *pMgr).toString(),
739 RTL_TEXTENCODING_UTF8).replaceFirst("~", "_"));
741 item = g_menu_item_new(sHelp.getStr(), "app.HelpIndex");
742 g_menu_append_item( thirdsubmenu, item );
743 g_object_unref(item);
745 OString sAbout(OUStringToOString(ResId(SV_STDTEXT_ABOUT, *pMgr).toString(),
746 RTL_TEXTENCODING_UTF8).replaceFirst("~", "_"));
748 item = g_menu_item_new(sAbout.getStr(), "app.About");
749 g_menu_append_item( thirdsubmenu, item );
750 g_object_unref(item);
752 OString sQuit(OUStringToOString(ResId(SV_MENU_MAC_QUITAPP, *pMgr).toString(),
753 RTL_TEXTENCODING_UTF8).replaceFirst("~", "_"));
755 item = g_menu_item_new(sQuit.getStr(), "app.Quit");
756 g_menu_append_item( thirdsubmenu, item );
757 g_object_unref(item);
758 g_menu_append_section( menu, NULL, G_MENU_MODEL(thirdsubmenu));
759 g_object_unref(thirdsubmenu);
761 GSimpleActionGroup *group = g_simple_action_group_new ();
762 #if GLIB_CHECK_VERSION(2,38,0) // g_simple_action_group_add_entries is deprecated since 2.38
763 g_action_map_add_action_entries (G_ACTION_MAP (group), app_entries, G_N_ELEMENTS (app_entries), NULL);
764 #else
765 g_simple_action_group_add_entries (group, app_entries, G_N_ELEMENTS (app_entries), NULL);
766 #endif
767 GActionGroup* pAppActionGroup = G_ACTION_GROUP(group);
769 pSalFrame->m_nAppActionGroupExportId = g_dbus_connection_export_action_group( pSessionBus, "/org/libreoffice", pAppActionGroup, NULL);
770 g_object_unref(pAppActionGroup);
771 pSalFrame->m_nAppMenuExportId = g_dbus_connection_export_menu_model (pSessionBus, "/org/libreoffice/menus/appmenu", G_MENU_MODEL (menu), NULL);
772 g_object_unref(menu);
775 g_free( aDBusMenubarPath );
776 g_free( aDBusWindowPath );
779 return FALSE;
782 void on_registrar_available( GDBusConnection * /*connection*/,
783 const gchar * /*name*/,
784 const gchar * /*name_owner*/,
785 gpointer user_data )
787 SolarMutexGuard aGuard;
789 GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data );
791 SalMenu* pSalMenu = pSalFrame->GetMenu();
793 if ( pSalMenu != NULL )
795 GtkSalMenu* pGtkSalMenu = static_cast<GtkSalMenu*>(pSalMenu);
796 pGtkSalMenu->Display( true );
797 pGtkSalMenu->UpdateFull();
801 // This is called when the registrar becomes unavailable. It shows the menubar.
802 void on_registrar_unavailable( GDBusConnection * /*connection*/,
803 const gchar * /*name*/,
804 gpointer user_data )
806 SolarMutexGuard aGuard;
808 SAL_INFO("vcl.unity", "on_registrar_unavailable");
810 //pSessionBus = NULL;
811 GtkSalFrame* pSalFrame = static_cast< GtkSalFrame* >( user_data );
813 SalMenu* pSalMenu = pSalFrame->GetMenu();
815 if ( pSalMenu ) {
816 GtkSalMenu* pGtkSalMenu = static_cast< GtkSalMenu* >( pSalMenu );
817 pGtkSalMenu->Display( false );
820 #endif
822 void GtkSalFrame::EnsureAppMenuWatch()
824 #ifdef ENABLE_GMENU_INTEGRATION
825 if ( !m_nWatcherId )
827 // Get a DBus session connection.
828 if ( pSessionBus == NULL )
830 pSessionBus = g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, NULL );
832 if ( pSessionBus == NULL )
833 return;
836 // Publish the menu only if AppMenu registrar is available.
837 m_nWatcherId = g_bus_watch_name_on_connection( pSessionBus,
838 "com.canonical.AppMenu.Registrar",
839 G_BUS_NAME_WATCHER_FLAGS_NONE,
840 on_registrar_available,
841 on_registrar_unavailable,
842 static_cast<GtkSalFrame*>(this),
843 NULL );
846 //ensure_dbus_setup( this );
847 #else
848 (void) this; // loplugin:staticmethods
849 #endif
852 void GtkSalFrame::InvalidateGraphics()
854 for (unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); ++i)
856 if( !m_aGraphics[i].pGraphics )
857 continue;
858 #if !GTK_CHECK_VERSION(3,0,0)
859 m_aGraphics[i].pGraphics->SetDrawable( None, m_nXScreen );
860 m_aGraphics[i].pGraphics->SetWindow(NULL);
861 #endif
862 m_aGraphics[i].bInUse = false;
866 GtkSalFrame::~GtkSalFrame()
868 InvalidateGraphics();
870 if( m_pParent )
871 m_pParent->m_aChildren.remove( this );
873 getDisplay()->deregisterFrame( this );
875 if( m_pRegion )
877 #if GTK_CHECK_VERSION(3,0,0)
878 cairo_region_destroy( m_pRegion );
879 #else
880 gdk_region_destroy( m_pRegion );
881 #endif
884 #if !GTK_CHECK_VERSION(3,0,0)
885 if( m_hBackgroundPixmap )
887 XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(),
888 widget_get_xid(m_pWindow),
889 None );
890 XFreePixmap( getDisplay()->GetDisplay(), m_hBackgroundPixmap );
892 #endif
894 if( m_pIMHandler )
895 delete m_pIMHandler;
897 if( m_pFixedContainer )
898 gtk_widget_destroy( GTK_WIDGET( m_pFixedContainer ) );
900 SolarMutexGuard aGuard;
901 #if defined ENABLE_GMENU_INTEGRATION
902 if(m_nWatcherId)
903 g_bus_unwatch_name(m_nWatcherId);
904 #endif
905 if( m_pWindow )
907 g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", NULL );
909 #if defined ENABLE_GMENU_INTEGRATION
910 if ( pSessionBus )
912 if ( m_nHudAwarenessId )
913 hud_awareness_unregister( pSessionBus, m_nHudAwarenessId );
914 if ( m_nMenuExportId )
915 g_dbus_connection_unexport_menu_model( pSessionBus, m_nMenuExportId );
916 if ( m_nAppMenuExportId )
917 g_dbus_connection_unexport_menu_model( pSessionBus, m_nAppMenuExportId );
918 if ( m_nActionGroupExportId )
919 g_dbus_connection_unexport_action_group( pSessionBus, m_nActionGroupExportId );
920 if ( m_nAppActionGroupExportId )
921 g_dbus_connection_unexport_action_group( pSessionBus, m_nAppActionGroupExportId );
923 #endif
924 gtk_widget_destroy( m_pWindow );
927 if( m_pForeignParent )
928 g_object_unref( G_OBJECT( m_pForeignParent ) );
929 if( m_pForeignTopLevel )
930 g_object_unref( G_OBJECT( m_pForeignTopLevel) );
933 void GtkSalFrame::moveWindow( long nX, long nY )
935 if( isChild( false, true ) )
937 if( m_pParent )
938 gtk_fixed_move( m_pParent->getFixedContainer(),
939 m_pWindow,
940 nX - m_pParent->maGeometry.nX, nY - m_pParent->maGeometry.nY );
942 else
943 gtk_window_move( GTK_WINDOW(m_pWindow), nX, nY );
946 void GtkSalFrame::widget_set_size_request(long nWidth, long nHeight)
948 gint nOrigwidth, nOrigheight;
949 gtk_window_get_size(GTK_WINDOW(m_pWindow), &nOrigwidth, &nOrigheight);
950 #if !GTK_CHECK_VERSION(3,0,0)
951 if (nWidth > nOrigwidth || nHeight > nOrigheight)
953 m_bPaintsBlocked = true;
955 #endif
956 gtk_widget_set_size_request(m_pWindow, nWidth, nHeight );
959 void GtkSalFrame::window_resize(long nWidth, long nHeight)
961 gint nOrigwidth, nOrigheight;
962 gtk_window_get_size(GTK_WINDOW(m_pWindow), &nOrigwidth, &nOrigheight);
963 #if !GTK_CHECK_VERSION(3,0,0)
964 if (nWidth > nOrigwidth || nHeight > nOrigheight)
966 m_bPaintsBlocked = true;
968 #endif
969 gtk_window_resize(GTK_WINDOW(m_pWindow), nWidth, nHeight);
972 void GtkSalFrame::resizeWindow( long nWidth, long nHeight )
974 if( isChild( false, true ) )
976 widget_set_size_request(nWidth, nHeight);
978 else if( ! isChild( true, false ) )
979 window_resize(nWidth, nHeight);
982 #if GTK_CHECK_VERSION(3,2,0)
984 static void
985 ooo_fixed_class_init(GtkFixedClass *klass)
987 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
988 widget_class->get_accessible = ooo_fixed_get_accessible;
991 #endif
994 * Always use a sub-class of GtkFixed we can tag for a11y. This allows us to
995 * utilize GAIL for the toplevel window and toolkit implementation incl.
996 * key event listener support ..
999 GType
1000 ooo_fixed_get_type()
1002 static GType type = 0;
1004 if (!type) {
1005 static const GTypeInfo tinfo =
1007 sizeof (GtkFixedClass),
1008 nullptr, /* base init */
1009 nullptr, /* base finalize */
1010 #if GTK_CHECK_VERSION(3,2,0)
1011 reinterpret_cast<GClassInitFunc>(ooo_fixed_class_init), /* class init */
1012 #else
1013 nullptr, /* class init */
1014 #endif
1015 nullptr, /* class finalize */
1016 NULL, /* class data */
1017 sizeof (GtkFixed), /* instance size */
1018 0, /* nb preallocs */
1019 (GInstanceInitFunc) NULL, /* instance init */
1020 NULL /* value table */
1023 type = g_type_register_static( GTK_TYPE_FIXED, "OOoFixed",
1024 &tinfo, (GTypeFlags) 0);
1027 return type;
1030 void GtkSalFrame::updateScreenNumber()
1032 int nScreen = 0;
1033 GdkScreen *pScreen = gtk_widget_get_screen( m_pWindow );
1034 if( pScreen )
1035 nScreen = getDisplay()->getSystem()->getScreenMonitorIdx( pScreen, maGeometry.nX, maGeometry.nY );
1036 maGeometry.nDisplayScreenNumber = nScreen;
1039 void GtkSalFrame::InitCommon()
1041 // connect signals
1042 g_signal_connect( G_OBJECT(m_pWindow), "style-set", G_CALLBACK(signalStyleSet), this );
1043 g_signal_connect( G_OBJECT(m_pWindow), "button-press-event", G_CALLBACK(signalButton), this );
1044 g_signal_connect( G_OBJECT(m_pWindow), "button-release-event", G_CALLBACK(signalButton), this );
1045 #if GTK_CHECK_VERSION(3,0,0)
1046 g_signal_connect( G_OBJECT(m_pWindow), "draw", G_CALLBACK(signalDraw), this );
1047 g_signal_connect( G_OBJECT(m_pWindow), "size-allocate", G_CALLBACK(sizeAllocated), this );
1048 // g_signal_connect( G_OBJECT(m_pWindow), "state-flags-changed", G_CALLBACK(signalFlagsChanged), this );
1049 #if GTK_CHECK_VERSION(3,14,0)
1050 GtkGesture *pSwipe = gtk_gesture_swipe_new(m_pWindow);
1051 g_signal_connect(pSwipe, "swipe", G_CALLBACK(gestureSwipe), this);
1052 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pSwipe), GTK_PHASE_TARGET);
1053 g_object_weak_ref(G_OBJECT(m_pWindow), reinterpret_cast<GWeakNotify>(g_object_unref), pSwipe);
1055 GtkGesture *pLongPress = gtk_gesture_long_press_new(m_pWindow);
1056 g_signal_connect(pLongPress, "pressed", G_CALLBACK(gestureLongPress), this);
1057 gtk_event_controller_set_propagation_phase(GTK_EVENT_CONTROLLER (pLongPress), GTK_PHASE_TARGET);
1058 g_object_weak_ref(G_OBJECT(m_pWindow), reinterpret_cast<GWeakNotify>(g_object_unref), pLongPress);
1060 #endif
1062 #else
1063 g_signal_connect( G_OBJECT(m_pWindow), "expose-event", G_CALLBACK(signalExpose), this );
1064 #endif
1065 g_signal_connect( G_OBJECT(m_pWindow), "focus-in-event", G_CALLBACK(signalFocus), this );
1066 g_signal_connect( G_OBJECT(m_pWindow), "focus-out-event", G_CALLBACK(signalFocus), this );
1067 g_signal_connect( G_OBJECT(m_pWindow), "map-event", G_CALLBACK(signalMap), this );
1068 g_signal_connect( G_OBJECT(m_pWindow), "unmap-event", G_CALLBACK(signalUnmap), this );
1069 g_signal_connect( G_OBJECT(m_pWindow), "configure-event", G_CALLBACK(signalConfigure), this );
1070 g_signal_connect( G_OBJECT(m_pWindow), "motion-notify-event", G_CALLBACK(signalMotion), this );
1071 g_signal_connect( G_OBJECT(m_pWindow), "key-press-event", G_CALLBACK(signalKey), this );
1072 g_signal_connect( G_OBJECT(m_pWindow), "key-release-event", G_CALLBACK(signalKey), this );
1073 g_signal_connect( G_OBJECT(m_pWindow), "delete-event", G_CALLBACK(signalDelete), this );
1074 g_signal_connect( G_OBJECT(m_pWindow), "window-state-event", G_CALLBACK(signalWindowState), this );
1075 g_signal_connect( G_OBJECT(m_pWindow), "scroll-event", G_CALLBACK(signalScroll), this );
1076 g_signal_connect( G_OBJECT(m_pWindow), "leave-notify-event", G_CALLBACK(signalCrossing), this );
1077 g_signal_connect( G_OBJECT(m_pWindow), "enter-notify-event", G_CALLBACK(signalCrossing), this );
1078 g_signal_connect( G_OBJECT(m_pWindow), "visibility-notify-event", G_CALLBACK(signalVisibility), this );
1079 g_signal_connect( G_OBJECT(m_pWindow), "destroy", G_CALLBACK(signalDestroy), this );
1081 // init members
1082 m_pCurrentCursor = NULL;
1083 m_nKeyModifiers = 0;
1084 m_bFullscreen = false;
1085 m_bSpanMonitorsWhenFullscreen = false;
1086 m_nState = GDK_WINDOW_STATE_WITHDRAWN;
1087 m_nVisibility = GDK_VISIBILITY_FULLY_OBSCURED;
1088 m_bSendModChangeOnRelease = false;
1089 m_pIMHandler = NULL;
1090 m_hBackgroundPixmap = None;
1091 m_nSavedScreenSaverTimeout = 0;
1092 m_nGSMCookie = 0;
1093 m_nExtStyle = 0;
1094 m_pRegion = NULL;
1095 m_ePointerStyle = static_cast<PointerStyle>(0xffff);
1096 m_bSetFocusOnMap = false;
1097 m_pSalMenu = NULL;
1098 m_nWatcherId = 0;
1099 m_nMenuExportId = 0;
1100 m_nAppMenuExportId = 0;
1101 m_nActionGroupExportId = 0;
1102 m_nAppActionGroupExportId = 0;
1103 m_nHudAwarenessId = 0;
1105 gtk_widget_set_app_paintable( m_pWindow, TRUE );
1106 /*non-X11 displays won't show anything at all without double-buffering
1107 enabled*/
1108 if (GDK_IS_X11_DISPLAY(getGdkDisplay()))
1109 gtk_widget_set_double_buffered( m_pWindow, FALSE );
1110 gtk_widget_set_redraw_on_allocate( m_pWindow, FALSE );
1112 gtk_widget_add_events( m_pWindow,
1113 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1114 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1115 GDK_VISIBILITY_NOTIFY_MASK | GDK_SCROLL_MASK
1118 // add the fixed container child,
1119 // fixed is needed since we have to position plugin windows
1120 m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), NULL ));
1121 gtk_container_add( GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pFixedContainer) );
1123 // show the widgets
1124 gtk_widget_show( GTK_WIDGET(m_pFixedContainer) );
1126 // realize the window, we need an XWindow id
1127 gtk_widget_realize( m_pWindow );
1129 //system data
1130 m_aSystemData.nSize = sizeof( SystemEnvData );
1131 #if !GTK_CHECK_VERSION(3,0,0)
1132 GtkSalDisplay* pDisp = GetGtkSalData()->GetGtkDisplay();
1133 m_aSystemData.pDisplay = pDisp->GetDisplay();
1134 m_aSystemData.pVisual = pDisp->GetVisual( m_nXScreen ).GetVisual();
1135 m_aSystemData.nDepth = pDisp->GetVisual( m_nXScreen ).GetDepth();
1136 m_aSystemData.aColormap = pDisp->GetColormap( m_nXScreen ).GetXColormap();
1137 m_aSystemData.aWindow = widget_get_xid(m_pWindow);
1138 m_aSystemData.aShellWindow = m_aSystemData.aWindow;
1139 #else
1140 static int nWindow = 0;
1141 m_aSystemData.aWindow = nWindow;
1142 m_aSystemData.aShellWindow = nWindow;
1143 ++nWindow;
1144 #endif
1145 m_aSystemData.pSalFrame = this;
1146 m_aSystemData.pWidget = m_pWindow;
1147 m_aSystemData.nScreen = m_nXScreen.getXScreen();
1148 m_aSystemData.pAppContext = NULL;
1149 m_aSystemData.pShellWidget = m_aSystemData.pWidget;
1151 // fake an initial geometry, gets updated via configure event or SetPosSize
1152 if( m_bDefaultPos || m_bDefaultSize )
1154 Size aDefSize = calcDefaultSize();
1155 maGeometry.nX = -1;
1156 maGeometry.nY = -1;
1157 maGeometry.nWidth = aDefSize.Width();
1158 maGeometry.nHeight = aDefSize.Height();
1159 if( m_pParent )
1161 // approximation
1162 maGeometry.nTopDecoration = m_pParent->maGeometry.nTopDecoration;
1163 maGeometry.nBottomDecoration = m_pParent->maGeometry.nBottomDecoration;
1164 maGeometry.nLeftDecoration = m_pParent->maGeometry.nLeftDecoration;
1165 maGeometry.nRightDecoration = m_pParent->maGeometry.nRightDecoration;
1167 else
1169 maGeometry.nTopDecoration = 0;
1170 maGeometry.nBottomDecoration = 0;
1171 maGeometry.nLeftDecoration = 0;
1172 maGeometry.nRightDecoration = 0;
1175 else
1177 resizeWindow( maGeometry.nWidth, maGeometry.nHeight );
1178 moveWindow( maGeometry.nX, maGeometry.nY );
1180 updateScreenNumber();
1182 SetIcon(1);
1184 #if !GTK_CHECK_VERSION(3,0,0)
1185 m_nWorkArea = pDisp->getWMAdaptor()->getCurrentWorkArea();
1186 /* #i64117# gtk sets a nice background pixmap
1187 * but we actually don't really want that, so save
1188 * some time on the Xserver as well as prevent
1189 * some paint issues
1191 XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(),
1192 widget_get_xid(m_pWindow),
1193 m_hBackgroundPixmap );
1194 #endif
1197 /* Sadly gtk_window_set_accept_focus exists only since gtk 2.4
1198 * for achieving the same effect we will remove the WM_TAKE_FOCUS
1199 * protocol from the window and set the input hint to false.
1200 * But gtk_window_set_accept_focus needs to be called before
1201 * window realization whereas the removal obviously can only happen
1202 * after realization.
1205 #if !GTK_CHECK_VERSION(3,0,0)
1206 extern "C" {
1207 typedef void(*setAcceptFn)( GtkWindow*, gboolean );
1208 static setAcceptFn p_gtk_window_set_accept_focus = NULL;
1209 static bool bGetAcceptFocusFn = true;
1211 typedef void(*setUserTimeFn)( GdkWindow*, guint32 );
1212 static setUserTimeFn p_gdk_x11_window_set_user_time = NULL;
1213 static bool bGetSetUserTimeFn = true;
1215 #endif
1217 static void lcl_set_accept_focus( GtkWindow* pWindow, gboolean bAccept, bool bBeforeRealize )
1219 #if !GTK_CHECK_VERSION(3,0,0)
1220 if( bGetAcceptFocusFn )
1222 bGetAcceptFocusFn = false;
1223 p_gtk_window_set_accept_focus = reinterpret_cast<setAcceptFn>(osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_window_set_accept_focus" ));
1225 if( p_gtk_window_set_accept_focus && bBeforeRealize )
1226 p_gtk_window_set_accept_focus( pWindow, bAccept );
1227 else if( ! bBeforeRealize )
1229 Display* pDisplay = GetGtkSalData()->GetGtkDisplay()->GetDisplay();
1230 ::Window aWindow = widget_get_xid(GTK_WIDGET(pWindow));
1231 XWMHints* pHints = XGetWMHints( pDisplay, aWindow );
1232 if( ! pHints )
1234 pHints = XAllocWMHints();
1235 pHints->flags = 0;
1237 pHints->flags |= InputHint;
1238 pHints->input = bAccept ? True : False;
1239 XSetWMHints( pDisplay, aWindow, pHints );
1240 XFree( pHints );
1242 if (GetGtkSalData()->GetGtkDisplay()->getWMAdaptor()->getWindowManagerName() == "compiz")
1243 return;
1245 /* remove WM_TAKE_FOCUS protocol; this would usually be the
1246 * right thing, but gtk handles it internally whereas we
1247 * want to handle it ourselves (as to sometimes not get
1248 * the focus)
1250 Atom* pProtocols = NULL;
1251 int nProtocols = 0;
1252 XGetWMProtocols( pDisplay,
1253 aWindow,
1254 &pProtocols, &nProtocols );
1255 if( pProtocols )
1257 bool bSet = false;
1258 Atom nTakeFocus = XInternAtom( pDisplay, "WM_TAKE_FOCUS", True );
1259 if( nTakeFocus )
1261 for( int i = 0; i < nProtocols; i++ )
1263 if( pProtocols[i] == nTakeFocus )
1265 for( int n = i; n < nProtocols-1; n++ )
1266 pProtocols[n] = pProtocols[n+1];
1267 nProtocols--;
1268 i--;
1269 bSet = true;
1273 if( bSet )
1274 XSetWMProtocols( pDisplay, aWindow, pProtocols, nProtocols );
1275 XFree( pProtocols );
1278 #else
1279 gtk_window_set_accept_focus(pWindow, bAccept);
1280 (void)bBeforeRealize;
1281 #endif
1284 #if !GTK_CHECK_VERSION(3,0,0)
1285 static void lcl_set_user_time( GtkWindow* i_pWindow, guint32 i_nTime )
1287 if( bGetSetUserTimeFn )
1289 bGetSetUserTimeFn = false;
1290 p_gdk_x11_window_set_user_time = reinterpret_cast<setUserTimeFn>(osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gdk_x11_window_set_user_time" ));
1292 bool bSet = false;
1293 if( p_gdk_x11_window_set_user_time )
1295 GdkWindow* pWin = widget_get_window(GTK_WIDGET(i_pWindow));
1296 if( pWin ) // only if the window is realized.
1298 p_gdk_x11_window_set_user_time( pWin, i_nTime );
1299 bSet = true;
1302 if( !bSet )
1304 Display* pDisplay = GetGtkSalData()->GetGtkDisplay()->GetDisplay();
1305 Atom nUserTime = XInternAtom( pDisplay, "_NET_WM_USER_TIME", True );
1306 if( nUserTime )
1308 XChangeProperty( pDisplay, widget_get_xid(GTK_WIDGET(i_pWindow)),
1309 nUserTime, XA_CARDINAL, 32,
1310 PropModeReplace, reinterpret_cast<unsigned char*>(&i_nTime), 1 );
1314 #endif
1316 GtkSalFrame *GtkSalFrame::getFromWindow( GtkWindow *pWindow )
1318 return static_cast<GtkSalFrame *>(g_object_get_data( G_OBJECT( pWindow ), "SalFrame" ));
1321 void GtkSalFrame::Init( SalFrame* pParent, sal_uLong nStyle )
1323 if( nStyle & SAL_FRAME_STYLE_DEFAULT ) // ensure default style
1325 nStyle |= SAL_FRAME_STYLE_MOVEABLE | SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_CLOSEABLE;
1326 nStyle &= ~SAL_FRAME_STYLE_FLOAT;
1329 m_pParent = static_cast<GtkSalFrame*>(pParent);
1330 m_pForeignParent = NULL;
1331 m_aForeignParentWindow = None;
1332 m_pForeignTopLevel = NULL;
1333 m_aForeignTopLevelWindow = None;
1334 m_nStyle = nStyle;
1336 GtkWindowType eWinType = ( (nStyle & SAL_FRAME_STYLE_FLOAT) &&
1337 ! (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|
1338 SAL_FRAME_STYLE_FLOAT_FOCUSABLE))
1340 ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL;
1342 if( nStyle & SAL_FRAME_STYLE_SYSTEMCHILD )
1344 m_pWindow = gtk_event_box_new();
1345 if( m_pParent )
1347 // insert into container
1348 gtk_fixed_put( m_pParent->getFixedContainer(),
1349 m_pWindow, 0, 0 );
1353 else
1355 m_pWindow = gtk_widget_new( GTK_TYPE_WINDOW, "type", eWinType,
1356 "visible", FALSE, NULL );
1358 g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", this );
1359 g_object_set_data( G_OBJECT( m_pWindow ), "libo-version", (gpointer)LIBO_VERSION_DOTTED);
1361 // force wm class hint
1362 m_nExtStyle = ~0;
1363 if (m_pParent)
1364 m_sWMClass = m_pParent->m_sWMClass;
1365 SetExtendedFrameStyle( 0 );
1367 if( m_pParent && m_pParent->m_pWindow && ! isChild() )
1368 gtk_window_set_screen( GTK_WINDOW(m_pWindow), gtk_window_get_screen( GTK_WINDOW(m_pParent->m_pWindow) ) );
1370 // set window type
1371 bool bDecoHandling =
1372 ! isChild() &&
1373 ( ! (nStyle & SAL_FRAME_STYLE_FLOAT) ||
1374 (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) );
1376 if( bDecoHandling )
1378 GdkWindowTypeHint eType = GDK_WINDOW_TYPE_HINT_NORMAL;
1379 if( (nStyle & SAL_FRAME_STYLE_DIALOG) && m_pParent != 0 )
1380 eType = GDK_WINDOW_TYPE_HINT_DIALOG;
1381 if( (nStyle & SAL_FRAME_STYLE_INTRO) )
1383 gtk_window_set_role( GTK_WINDOW(m_pWindow), "splashscreen" );
1384 eType = GDK_WINDOW_TYPE_HINT_SPLASHSCREEN;
1386 else if( (nStyle & SAL_FRAME_STYLE_TOOLWINDOW ) )
1388 eType = GDK_WINDOW_TYPE_HINT_UTILITY;
1389 gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow), true );
1391 else if( (nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) )
1393 eType = GDK_WINDOW_TYPE_HINT_TOOLBAR;
1394 lcl_set_accept_focus( GTK_WINDOW(m_pWindow), false, true );
1396 else if( (nStyle & SAL_FRAME_STYLE_FLOAT_FOCUSABLE) )
1398 eType = GDK_WINDOW_TYPE_HINT_UTILITY;
1400 #if !GTK_CHECK_VERSION(3,0,0)
1401 if( (nStyle & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN )
1402 && getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() )
1404 eType = GDK_WINDOW_TYPE_HINT_TOOLBAR;
1405 gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), true );
1407 #endif
1408 gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), eType );
1409 gtk_window_set_gravity( GTK_WINDOW(m_pWindow), GDK_GRAVITY_STATIC );
1410 if( m_pParent && ! (m_pParent->m_nStyle & SAL_FRAME_STYLE_PLUG) )
1411 gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), GTK_WINDOW(m_pParent->m_pWindow) );
1413 else if( (nStyle & SAL_FRAME_STYLE_FLOAT) )
1415 gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), GDK_WINDOW_TYPE_HINT_UTILITY );
1417 if( m_pParent )
1418 m_pParent->m_aChildren.push_back( this );
1420 InitCommon();
1422 #if !GTK_CHECK_VERSION(3,0,0)
1423 if( eWinType == GTK_WINDOW_TOPLEVEL )
1425 #ifdef ENABLE_GMENU_INTEGRATION
1426 // Enable DBus native menu if available.
1427 ensure_dbus_setup( this );
1428 #endif
1430 guint32 nUserTime = 0;
1431 if( (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_TOOLWINDOW)) == 0 )
1433 nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window);
1435 lcl_set_user_time(GTK_WINDOW(m_pWindow), nUserTime);
1437 #endif
1439 if( bDecoHandling )
1441 gtk_window_set_resizable( GTK_WINDOW(m_pWindow), (nStyle & SAL_FRAME_STYLE_SIZEABLE) != 0 );
1442 if( ( (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION)) ) )
1443 lcl_set_accept_focus( GTK_WINDOW(m_pWindow), false, false );
1447 GdkNativeWindow GtkSalFrame::findTopLevelSystemWindow( GdkNativeWindow aWindow )
1449 #if !GTK_CHECK_VERSION(3,0,0)
1450 ::Window aRoot, aParent;
1451 ::Window* pChildren;
1452 unsigned int nChildren;
1453 bool bBreak = false;
1456 pChildren = NULL;
1457 nChildren = 0;
1458 aParent = aRoot = None;
1459 XQueryTree( getDisplay()->GetDisplay(), aWindow,
1460 &aRoot, &aParent, &pChildren, &nChildren );
1461 XFree( pChildren );
1462 if( aParent != aRoot )
1463 aWindow = aParent;
1464 int nCount = 0;
1465 Atom* pProps = XListProperties( getDisplay()->GetDisplay(),
1466 aWindow,
1467 &nCount );
1468 for( int i = 0; i < nCount && ! bBreak; ++i )
1469 bBreak = (pProps[i] == XA_WM_HINTS);
1470 if( pProps )
1471 XFree( pProps );
1472 } while( aParent != aRoot && ! bBreak );
1474 return aWindow;
1475 #else
1476 (void)aWindow;
1477 //FIXME: no findToplevelSystemWindow
1478 return 0;
1479 #endif
1482 void GtkSalFrame::Init( SystemParentData* pSysData )
1484 m_pParent = NULL;
1485 m_aForeignParentWindow = (GdkNativeWindow)pSysData->aWindow;
1486 m_pForeignParent = NULL;
1487 m_aForeignTopLevelWindow = findTopLevelSystemWindow( (GdkNativeWindow)pSysData->aWindow );
1488 m_pForeignTopLevel = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignTopLevelWindow );
1489 gdk_window_set_events( m_pForeignTopLevel, GDK_STRUCTURE_MASK );
1491 if( pSysData->nSize > sizeof(pSysData->nSize)+sizeof(pSysData->aWindow) && pSysData->bXEmbedSupport )
1493 #if GTK_CHECK_VERSION(3,0,0)
1494 m_pWindow = gtk_plug_new_for_display( getGdkDisplay(), pSysData->aWindow );
1495 #else
1496 m_pWindow = gtk_plug_new( pSysData->aWindow );
1497 #endif
1498 m_bWindowIsGtkPlug = true;
1499 widget_set_can_default( m_pWindow, true );
1500 widget_set_can_focus( m_pWindow, true );
1501 gtk_widget_set_sensitive( m_pWindow, true );
1503 else
1505 m_pWindow = gtk_window_new( GTK_WINDOW_POPUP );
1506 m_bWindowIsGtkPlug = false;
1508 m_nStyle = SAL_FRAME_STYLE_PLUG;
1509 InitCommon();
1511 m_pForeignParent = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignParentWindow );
1512 gdk_window_set_events( m_pForeignParent, GDK_STRUCTURE_MASK );
1514 #if !GTK_CHECK_VERSION(3,0,0)
1515 int x_ret, y_ret;
1516 unsigned int w, h, bw, d;
1517 ::Window aRoot;
1518 XGetGeometry( getDisplay()->GetDisplay(), pSysData->aWindow,
1519 &aRoot, &x_ret, &y_ret, &w, &h, &bw, &d );
1520 maGeometry.nWidth = w;
1521 maGeometry.nHeight = h;
1522 window_resize(w, h);
1523 gtk_window_move( GTK_WINDOW(m_pWindow), 0, 0 );
1524 if( ! m_bWindowIsGtkPlug )
1526 XReparentWindow( getDisplay()->GetDisplay(),
1527 widget_get_xid(m_pWindow),
1528 (::Window)pSysData->aWindow,
1529 0, 0 );
1531 #else
1532 //FIXME: Handling embedded windows, is going to be fun ...
1533 #endif
1536 void GtkSalFrame::askForXEmbedFocus( sal_Int32 i_nTimeCode )
1538 #if !GTK_CHECK_VERSION(3,0,0)
1539 XEvent aEvent;
1541 memset( &aEvent, 0, sizeof(aEvent) );
1542 aEvent.xclient.window = m_aForeignParentWindow;
1543 aEvent.xclient.type = ClientMessage;
1544 aEvent.xclient.message_type = getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED );
1545 aEvent.xclient.format = 32;
1546 aEvent.xclient.data.l[0] = i_nTimeCode ? i_nTimeCode : CurrentTime;
1547 aEvent.xclient.data.l[1] = 3; // XEMBED_REQUEST_FOCUS
1548 aEvent.xclient.data.l[2] = 0;
1549 aEvent.xclient.data.l[3] = 0;
1550 aEvent.xclient.data.l[4] = 0;
1552 GetGenericData()->ErrorTrapPush();
1553 XSendEvent( getDisplay()->GetDisplay(),
1554 m_aForeignParentWindow,
1555 False, NoEventMask, &aEvent );
1556 GetGenericData()->ErrorTrapPop();
1557 #else
1558 (void) this; // loplugin:staticmethods
1559 (void)i_nTimeCode;
1560 //FIXME: no askForXEmbedFocus for gtk3 yet
1561 #endif
1564 void GtkSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle )
1566 if( nStyle != m_nExtStyle && ! isChild() )
1568 m_nExtStyle = nStyle;
1569 updateWMClass();
1573 SalGraphics* GtkSalFrame::AcquireGraphics()
1575 if( m_pWindow )
1577 for( int i = 0; i < nMaxGraphics; i++ )
1579 if( ! m_aGraphics[i].bInUse )
1581 m_aGraphics[i].bInUse = true;
1582 if( ! m_aGraphics[i].pGraphics )
1584 #if GTK_CHECK_VERSION(3,0,0)
1585 m_aGraphics[i].pGraphics = new GtkSalGraphics( this, m_pWindow );
1586 if( !m_aFrame.get() )
1588 AllocateFrame();
1589 TriggerPaintEvent();
1591 m_aGraphics[i].pGraphics->setDevice( m_aFrame );
1592 #else // common case:
1593 m_aGraphics[i].pGraphics = new GtkSalGraphics( this, m_pWindow, m_nXScreen );
1594 #endif
1596 return m_aGraphics[i].pGraphics;
1601 return NULL;
1604 void GtkSalFrame::ReleaseGraphics( SalGraphics* pGraphics )
1606 for( int i = 0; i < nMaxGraphics; i++ )
1608 if( m_aGraphics[i].pGraphics == pGraphics )
1610 m_aGraphics[i].bInUse = false;
1611 break;
1616 bool GtkSalFrame::PostEvent( void* pData )
1618 getDisplay()->SendInternalEvent( this, pData );
1619 return true;
1622 void GtkSalFrame::SetTitle( const OUString& rTitle )
1624 m_aTitle = rTitle;
1625 if( m_pWindow && ! isChild() )
1626 gtk_window_set_title( GTK_WINDOW(m_pWindow), OUStringToOString( rTitle, RTL_TEXTENCODING_UTF8 ).getStr() );
1629 static inline sal_uInt8 *
1630 getRow( BitmapBuffer *pBuffer, sal_uLong nRow )
1632 if( BMP_SCANLINE_ADJUSTMENT( pBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN )
1633 return pBuffer->mpBits + nRow * pBuffer->mnScanlineSize;
1634 else
1635 return pBuffer->mpBits + ( pBuffer->mnHeight - nRow - 1 ) * pBuffer->mnScanlineSize;
1638 static GdkPixbuf *
1639 bitmapToPixbuf( SalBitmap *pSalBitmap, SalBitmap *pSalAlpha )
1641 g_return_val_if_fail( pSalBitmap != NULL, NULL );
1642 g_return_val_if_fail( pSalAlpha != NULL, NULL );
1644 BitmapBuffer *pBitmap = pSalBitmap->AcquireBuffer( BITMAP_READ_ACCESS );
1645 g_return_val_if_fail( pBitmap != NULL, NULL );
1646 g_return_val_if_fail( pBitmap->mnBitCount == 24 || pBitmap->mnBitCount == 32, NULL );
1648 BitmapBuffer *pAlpha = pSalAlpha->AcquireBuffer( BITMAP_READ_ACCESS );
1649 g_return_val_if_fail( pAlpha != NULL, NULL );
1650 g_return_val_if_fail( pAlpha->mnBitCount == 8, NULL );
1652 Size aSize = pSalBitmap->GetSize();
1653 g_return_val_if_fail( pSalAlpha->GetSize() == aSize, NULL );
1655 int nX, nY;
1656 guchar *pPixbufData = static_cast<guchar *>(g_malloc (4 * aSize.Width() * aSize.Height() ));
1657 guchar *pDestData = pPixbufData;
1659 for( nY = 0; nY < pBitmap->mnHeight; nY++ )
1661 sal_uInt8 *pData = getRow( pBitmap, nY );
1662 sal_uInt8 *pAlphaData = getRow( pAlpha, nY );
1664 for( nX = 0; nX < pBitmap->mnWidth; nX++ )
1666 BitmapColor aColor;
1667 if (pBitmap->mnFormat == BMP_FORMAT_24BIT_TC_BGR)
1669 aColor = BitmapColor(pData[2], pData[1], pData[0]);
1670 pData += 3;
1672 else if (pBitmap->mnFormat == BMP_FORMAT_24BIT_TC_RGB)
1674 aColor = BitmapColor(pData[0], pData[1], pData[2]);
1675 pData += 3;
1677 else
1679 pBitmap->maColorMask.GetColorFor32Bit(aColor, pData);
1680 pData += 4;
1682 *pDestData++ = aColor.GetRed();
1683 *pDestData++ = aColor.GetGreen();
1684 *pDestData++ = aColor.GetBlue();
1685 *pDestData++ = 255 - *pAlphaData++;
1689 pSalBitmap->ReleaseBuffer( pBitmap, BITMAP_READ_ACCESS );
1690 pSalAlpha->ReleaseBuffer( pAlpha, BITMAP_READ_ACCESS );
1692 return gdk_pixbuf_new_from_data( pPixbufData,
1693 GDK_COLORSPACE_RGB, true, 8,
1694 aSize.Width(), aSize.Height(),
1695 aSize.Width() * 4,
1696 reinterpret_cast<GdkPixbufDestroyNotify>(g_free),
1697 NULL );
1700 void GtkSalFrame::SetIcon( sal_uInt16 nIcon )
1702 if( (m_nStyle & (SAL_FRAME_STYLE_PLUG|SAL_FRAME_STYLE_SYSTEMCHILD|SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_INTRO|SAL_FRAME_STYLE_OWNERDRAWDECORATION))
1703 || ! m_pWindow )
1704 return;
1706 if( !ImplGetResMgr() )
1707 return;
1709 GdkPixbuf *pBuf;
1710 GList *pIcons = NULL;
1712 sal_uInt16 nOffsets[2] = { SV_ICON_SMALL_START, SV_ICON_LARGE_START };
1713 sal_uInt16 nIndex;
1715 for( nIndex = 0; nIndex < sizeof(nOffsets)/ sizeof(sal_uInt16); nIndex++ )
1717 // #i44723# workaround gcc temporary problem
1718 ResId aResId( nOffsets[nIndex] + nIcon, *ImplGetResMgr() );
1719 BitmapEx aIcon( aResId );
1721 // #i81083# convert to 24bit/8bit alpha bitmap
1722 Bitmap aBmp = aIcon.GetBitmap();
1723 if( aBmp.GetBitCount() != 24 || ! aIcon.IsAlpha() )
1725 if( aBmp.GetBitCount() != 24 )
1726 aBmp.Convert( BMP_CONVERSION_24BIT );
1727 AlphaMask aMask;
1728 if( ! aIcon.IsAlpha() )
1730 switch( aIcon.GetTransparentType() )
1732 case TRANSPARENT_NONE:
1734 sal_uInt8 nTrans = 0;
1735 aMask = AlphaMask( aBmp.GetSizePixel(), &nTrans );
1737 break;
1738 case TRANSPARENT_COLOR:
1739 aMask = AlphaMask( aBmp.CreateMask( aIcon.GetTransparentColor() ) );
1740 break;
1741 case TRANSPARENT_BITMAP:
1742 aMask = AlphaMask( aIcon.GetMask() );
1743 break;
1744 default:
1745 OSL_FAIL( "unhandled transparent type" );
1746 break;
1749 else
1750 aMask = aIcon.GetAlpha();
1751 aIcon = BitmapEx( aBmp, aMask );
1754 ImpBitmap *pIconImpBitmap = aIcon.ImplGetBitmapImpBitmap();
1755 ImpBitmap *pIconImpMask = aIcon.ImplGetMaskImpBitmap();
1757 if( pIconImpBitmap && pIconImpMask )
1759 SalBitmap *pIconBitmap =
1760 pIconImpBitmap->ImplGetSalBitmap();
1761 SalBitmap *pIconMask =
1762 pIconImpMask->ImplGetSalBitmap();
1764 if( ( pBuf = bitmapToPixbuf( pIconBitmap, pIconMask ) ) )
1765 pIcons = g_list_prepend( pIcons, pBuf );
1769 gtk_window_set_icon_list( GTK_WINDOW(m_pWindow), pIcons );
1771 g_list_foreach( pIcons, reinterpret_cast<GFunc>(g_object_unref), NULL );
1772 g_list_free( pIcons );
1775 void GtkSalFrame::SetMenu( SalMenu* pSalMenu )
1777 // if(m_pSalMenu)
1778 // {
1779 // static_cast<GtkSalMenu*>(m_pSalMenu)->DisconnectFrame();
1780 // }
1781 m_pSalMenu = pSalMenu;
1784 SalMenu* GtkSalFrame::GetMenu()
1786 return m_pSalMenu;
1789 void GtkSalFrame::DrawMenuBar()
1793 void GtkSalFrame::Center()
1795 long nX, nY;
1797 if( m_pParent )
1799 nX = ((long)m_pParent->maGeometry.nWidth - (long)maGeometry.nWidth)/2;
1800 nY = ((long)m_pParent->maGeometry.nHeight - (long)maGeometry.nHeight)/2;
1802 else
1804 GdkScreen *pScreen = NULL;
1805 gint px, py;
1806 GdkModifierType nMask;
1807 gdk_display_get_pointer( getGdkDisplay(), &pScreen, &px, &py, &nMask );
1808 if( !pScreen )
1809 pScreen = gtk_widget_get_screen( m_pWindow );
1811 gint nMonitor;
1812 nMonitor = gdk_screen_get_monitor_at_point( pScreen, px, py );
1814 GdkRectangle aMonitor;
1815 gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aMonitor );
1817 nX = aMonitor.x + (aMonitor.width - (long)maGeometry.nWidth)/2;
1818 nY = aMonitor.y + (aMonitor.height - (long)maGeometry.nHeight)/2;
1820 SetPosSize( nX, nY, 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y );
1823 Size GtkSalFrame::calcDefaultSize()
1825 return bestmaxFrameSizeForScreenSize(getDisplay()->GetScreenSize(GetDisplayScreen()));
1828 void GtkSalFrame::SetDefaultSize()
1830 Size aDefSize = calcDefaultSize();
1832 SetPosSize( 0, 0, aDefSize.Width(), aDefSize.Height(),
1833 SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT );
1835 if( (m_nStyle & SAL_FRAME_STYLE_DEFAULT) && m_pWindow )
1836 gtk_window_maximize( GTK_WINDOW(m_pWindow) );
1839 static void initClientId()
1841 #if !GTK_CHECK_VERSION(3,0,0)
1842 static bool bOnce = false;
1843 if (!bOnce)
1845 bOnce = true;
1846 const OString& rID = SessionManagerClient::getSessionID();
1847 if (!rID.isEmpty())
1848 gdk_set_sm_client_id(rID.getStr());
1850 #else
1851 // No session management support for gtk3+ - this is now legacy.
1852 #endif
1855 void GtkSalFrame::Show( bool bVisible, bool bNoActivate )
1857 if( m_pWindow )
1859 #if !GTK_CHECK_VERSION(3,0,0)
1860 if( m_pParent && (m_pParent->m_nStyle & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN)
1861 && getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() )
1862 gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), bVisible );
1863 #endif
1864 if( bVisible )
1866 initClientId();
1867 getDisplay()->startupNotificationCompleted();
1869 if( m_bDefaultPos )
1870 Center();
1871 if( m_bDefaultSize )
1872 SetDefaultSize();
1873 setMinMaxSize();
1875 #if !GTK_CHECK_VERSION(3,0,0)
1876 // #i45160# switch to desktop where a dialog with parent will appear
1877 if( m_pParent && m_pParent->m_nWorkArea != m_nWorkArea && IS_WIDGET_MAPPED(m_pParent->m_pWindow) )
1878 getDisplay()->getWMAdaptor()->switchToWorkArea( m_pParent->m_nWorkArea );
1879 #endif
1881 if( isFloatGrabWindow() &&
1882 m_pParent &&
1883 m_nFloats == 0 &&
1884 ! getDisplay()->GetCaptureFrame() )
1886 /* #i63086#
1887 * outsmart Metacity's "focus:mouse" mode
1888 * which insists on taking the focus from the document
1889 * to the new float. Grab focus to parent frame BEFORE
1890 * showing the float (cannot grab it to the float
1891 * before show).
1893 m_pParent->grabPointer( true, true );
1896 #if !GTK_CHECK_VERSION(3,0,0)
1897 guint32 nUserTime = 0;
1898 if( ! bNoActivate && (m_nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_TOOLWINDOW)) == 0 )
1899 nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window);
1901 //For these floating windows we don't want the main window to lose focus, and metacity has...
1902 // metacity-2.24.0/src/core/window.c
1904 // if ((focus_window != NULL) && XSERVER_TIME_IS_BEFORE (compare, focus_window->net_wm_user_time))
1905 // "compare" window focus prevented by other activity
1907 // where "compare" is this window
1909 // which leads to...
1911 // /* This happens for error dialogs or alerts; these need to remain on
1912 // * top, but it would be confusing to have its ancestor remain
1913 // * focused.
1914 // */
1915 // if (meta_window_is_ancestor_of_transient (focus_window, window))
1916 // "The focus window %s is an ancestor of the newly mapped "
1917 // "window %s which isn't being focused. Unfocusing the "
1918 // "ancestor.\n",
1920 // i.e. having a time < that of the toplevel frame means that the toplevel frame gets unfocused.
1921 // awesome.
1922 if( nUserTime == 0 )
1924 nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window);
1926 lcl_set_user_time(GTK_WINDOW(m_pWindow), nUserTime );
1927 #endif
1929 if( ! bNoActivate && (m_nStyle & SAL_FRAME_STYLE_TOOLWINDOW) )
1930 m_bSetFocusOnMap = true;
1932 gtk_widget_show( m_pWindow );
1934 if( isFloatGrabWindow() )
1936 m_nFloats++;
1937 if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 1 )
1939 grabPointer(true, true);
1940 GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this;
1941 pKeyboardFrame->grabKeyboard(true);
1943 // #i44068# reset parent's IM context
1944 if( m_pParent )
1945 m_pParent->EndExtTextInput(0);
1947 if( m_bWindowIsGtkPlug )
1948 askForXEmbedFocus( 0 );
1950 else
1952 if( isFloatGrabWindow() )
1954 m_nFloats--;
1955 if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 0)
1957 GtkSalFrame *pKeyboardFrame = m_pParent ? m_pParent : this;
1958 pKeyboardFrame->grabKeyboard(false);
1959 grabPointer(false);
1962 gtk_widget_hide( m_pWindow );
1963 if( m_pIMHandler )
1964 m_pIMHandler->focusChanged( false );
1965 // flush here; there may be a very seldom race between
1966 // the display connection used for clipboard and our connection
1967 Flush();
1969 CallCallback( SALEVENT_RESIZE, NULL );
1970 TriggerPaintEvent();
1974 void GtkSalFrame::setMinMaxSize()
1976 /* #i34504# metacity (and possibly others) do not treat
1977 * _NET_WM_STATE_FULLSCREEN and max_width/height independently;
1978 * whether they should is undefined. So don't set the max size hint
1979 * for a full screen window.
1981 if( m_pWindow && ! isChild() )
1983 GdkGeometry aGeo;
1984 int aHints = 0;
1985 if( m_nStyle & SAL_FRAME_STYLE_SIZEABLE )
1987 if( m_aMinSize.Width() && m_aMinSize.Height() && ! m_bFullscreen )
1989 aGeo.min_width = m_aMinSize.Width();
1990 aGeo.min_height = m_aMinSize.Height();
1991 aHints |= GDK_HINT_MIN_SIZE;
1993 if( m_aMaxSize.Width() && m_aMaxSize.Height() && ! m_bFullscreen )
1995 aGeo.max_width = m_aMaxSize.Width();
1996 aGeo.max_height = m_aMaxSize.Height();
1997 aHints |= GDK_HINT_MAX_SIZE;
2000 else
2002 if( ! m_bFullscreen )
2004 aGeo.min_width = maGeometry.nWidth;
2005 aGeo.min_height = maGeometry.nHeight;
2006 aHints |= GDK_HINT_MIN_SIZE;
2008 aGeo.max_width = maGeometry.nWidth;
2009 aGeo.max_height = maGeometry.nHeight;
2010 aHints |= GDK_HINT_MAX_SIZE;
2013 if( m_bFullscreen && m_aMaxSize.Width() && m_aMaxSize.Height() )
2015 aGeo.max_width = m_aMaxSize.Width();
2016 aGeo.max_height = m_aMaxSize.Height();
2017 aHints |= GDK_HINT_MAX_SIZE;
2019 if( aHints )
2021 gtk_window_set_geometry_hints( GTK_WINDOW(m_pWindow),
2022 NULL,
2023 &aGeo,
2024 GdkWindowHints( aHints ) );
2029 void GtkSalFrame::SetMaxClientSize( long nWidth, long nHeight )
2031 if( ! isChild() )
2033 m_aMaxSize = Size( nWidth, nHeight );
2034 // Show does a setMinMaxSize
2035 if( IS_WIDGET_MAPPED( m_pWindow ) )
2036 setMinMaxSize();
2039 void GtkSalFrame::SetMinClientSize( long nWidth, long nHeight )
2041 if( ! isChild() )
2043 m_aMinSize = Size( nWidth, nHeight );
2044 if( m_pWindow )
2046 widget_set_size_request(nWidth, nHeight );
2047 // Show does a setMinMaxSize
2048 if( IS_WIDGET_MAPPED( m_pWindow ) )
2049 setMinMaxSize();
2054 // FIXME: we should really be an SvpSalFrame sub-class, and
2055 // share their AllocateFrame !
2056 void GtkSalFrame::AllocateFrame()
2058 #if GTK_CHECK_VERSION(3,0,0)
2059 basegfx::B2IVector aFrameSize( maGeometry.nWidth, maGeometry.nHeight );
2060 if( ! m_aFrame.get() || m_aFrame->getSize() != aFrameSize )
2062 if( aFrameSize.getX() == 0 )
2063 aFrameSize.setX( 1 );
2064 if( aFrameSize.getY() == 0 )
2065 aFrameSize.setY( 1 );
2066 int cairo_stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, aFrameSize.getX());
2067 m_aFrame = basebmp::createBitmapDevice(aFrameSize, true,
2068 basebmp::FORMAT_THIRTYTWO_BIT_TC_MASK_BGRX, cairo_stride);
2069 m_aFrame->setDamageTracker(
2070 basebmp::IBitmapDeviceDamageTrackerSharedPtr(new DamageTracker(*this)) );
2071 SAL_INFO("vcl.gtk3", "allocated m_aFrame size of " << maGeometry.nWidth << " x " << maGeometry.nHeight);
2073 #if OSL_DEBUG_LEVEL > 0 // set background to orange
2074 m_aFrame->clear( basebmp::Color( 255, 127, 0 ) );
2075 #endif
2077 // update device in existing graphics
2078 for( unsigned int i = 0; i < SAL_N_ELEMENTS( m_aGraphics ); ++i )
2080 if( !m_aGraphics[i].pGraphics )
2081 continue;
2082 m_aGraphics[i].pGraphics->setDevice( m_aFrame );
2085 #endif
2088 void GtkSalFrame::SetPosSize( long nX, long nY, long nWidth, long nHeight, sal_uInt16 nFlags )
2090 if( !m_pWindow || isChild( true, false ) )
2091 return;
2093 bool bSized = false, bMoved = false;
2095 if( (nFlags & ( SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT )) &&
2096 (nWidth > 0 && nHeight > 0 ) // sometimes stupid things happen
2099 m_bDefaultSize = false;
2101 if( (unsigned long)nWidth != maGeometry.nWidth || (unsigned long)nHeight != maGeometry.nHeight )
2102 bSized = true;
2103 maGeometry.nWidth = nWidth;
2104 maGeometry.nHeight = nHeight;
2106 if( isChild( false, true ) )
2107 widget_set_size_request(nWidth, nHeight);
2108 else if( ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) )
2109 window_resize(nWidth, nHeight);
2110 setMinMaxSize();
2112 else if( m_bDefaultSize )
2113 SetDefaultSize();
2115 m_bDefaultSize = false;
2117 if( nFlags & ( SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ) )
2119 if( m_pParent )
2121 if( AllSettings::GetLayoutRTL() )
2122 nX = m_pParent->maGeometry.nWidth-maGeometry.nWidth-1-nX;
2123 nX += m_pParent->maGeometry.nX;
2124 nY += m_pParent->maGeometry.nY;
2127 if( nX != maGeometry.nX || nY != maGeometry.nY )
2128 bMoved = true;
2129 maGeometry.nX = nX;
2130 maGeometry.nY = nY;
2132 m_bDefaultPos = false;
2134 moveWindow( maGeometry.nX, maGeometry.nY );
2136 updateScreenNumber();
2138 else if( m_bDefaultPos )
2139 Center();
2141 m_bDefaultPos = false;
2143 if( bSized )
2144 AllocateFrame();
2146 if( bSized && ! bMoved )
2147 CallCallback( SALEVENT_RESIZE, NULL );
2148 else if( bMoved && ! bSized )
2149 CallCallback( SALEVENT_MOVE, NULL );
2150 else if( bMoved && bSized )
2151 CallCallback( SALEVENT_MOVERESIZE, NULL );
2153 if (bSized)
2154 TriggerPaintEvent();
2157 void GtkSalFrame::GetClientSize( long& rWidth, long& rHeight )
2159 if( m_pWindow && !(m_nState & GDK_WINDOW_STATE_ICONIFIED) )
2161 rWidth = maGeometry.nWidth;
2162 rHeight = maGeometry.nHeight;
2164 else
2165 rWidth = rHeight = 0;
2168 void GtkSalFrame::GetWorkArea( Rectangle& rRect )
2170 #if !GTK_CHECK_VERSION(3,0,0)
2171 rRect = GetGtkSalData()->GetGtkDisplay()->getWMAdaptor()->getWorkArea( 0 );
2172 #else
2173 GdkScreen *pScreen = gtk_window_get_screen(GTK_WINDOW(m_pWindow));
2174 Rectangle aRetRect;
2175 int max = gdk_screen_get_n_monitors (pScreen);
2176 for (int i = 0; i < max; ++i)
2178 GdkRectangle aRect;
2179 gdk_screen_get_monitor_workarea(pScreen, i, &aRect);
2180 Rectangle aMonitorRect(aRect.x, aRect.y, aRect.x+aRect.width, aRect.y+aRect.height);
2181 aRetRect.Union(aMonitorRect);
2183 rRect = aRetRect;
2184 #endif
2187 SalFrame* GtkSalFrame::GetParent() const
2189 return m_pParent;
2192 void GtkSalFrame::SetWindowState( const SalFrameState* pState )
2194 if( ! m_pWindow || ! pState || isChild( true, false ) )
2195 return;
2197 const sal_uLong nMaxGeometryMask =
2198 WINDOWSTATE_MASK_X | WINDOWSTATE_MASK_Y |
2199 WINDOWSTATE_MASK_WIDTH | WINDOWSTATE_MASK_HEIGHT |
2200 WINDOWSTATE_MASK_MAXIMIZED_X | WINDOWSTATE_MASK_MAXIMIZED_Y |
2201 WINDOWSTATE_MASK_MAXIMIZED_WIDTH | WINDOWSTATE_MASK_MAXIMIZED_HEIGHT;
2203 if( (pState->mnMask & WINDOWSTATE_MASK_STATE) &&
2204 ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) &&
2205 (pState->mnState & WINDOWSTATE_STATE_MAXIMIZED) &&
2206 (pState->mnMask & nMaxGeometryMask) == nMaxGeometryMask )
2208 resizeWindow( pState->mnWidth, pState->mnHeight );
2209 moveWindow( pState->mnX, pState->mnY );
2210 m_bDefaultPos = m_bDefaultSize = false;
2212 maGeometry.nX = pState->mnMaximizedX;
2213 maGeometry.nY = pState->mnMaximizedY;
2214 maGeometry.nWidth = pState->mnMaximizedWidth;
2215 maGeometry.nHeight = pState->mnMaximizedHeight;
2216 updateScreenNumber();
2218 m_nState = GdkWindowState( m_nState | GDK_WINDOW_STATE_MAXIMIZED );
2219 m_aRestorePosSize = Rectangle( Point( pState->mnX, pState->mnY ),
2220 Size( pState->mnWidth, pState->mnHeight ) );
2221 CallCallback( SALEVENT_RESIZE, NULL );
2223 else if( pState->mnMask & (WINDOWSTATE_MASK_X | WINDOWSTATE_MASK_Y |
2224 WINDOWSTATE_MASK_WIDTH | WINDOWSTATE_MASK_HEIGHT ) )
2226 sal_uInt16 nPosSizeFlags = 0;
2227 long nX = pState->mnX - (m_pParent ? m_pParent->maGeometry.nX : 0);
2228 long nY = pState->mnY - (m_pParent ? m_pParent->maGeometry.nY : 0);
2229 if( pState->mnMask & WINDOWSTATE_MASK_X )
2230 nPosSizeFlags |= SAL_FRAME_POSSIZE_X;
2231 else
2232 nX = maGeometry.nX - (m_pParent ? m_pParent->maGeometry.nX : 0);
2233 if( pState->mnMask & WINDOWSTATE_MASK_Y )
2234 nPosSizeFlags |= SAL_FRAME_POSSIZE_Y;
2235 else
2236 nY = maGeometry.nY - (m_pParent ? m_pParent->maGeometry.nY : 0);
2237 if( pState->mnMask & WINDOWSTATE_MASK_WIDTH )
2238 nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH;
2239 if( pState->mnMask & WINDOWSTATE_MASK_HEIGHT )
2240 nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT;
2241 SetPosSize( nX, nY, pState->mnWidth, pState->mnHeight, nPosSizeFlags );
2243 if( pState->mnMask & WINDOWSTATE_MASK_STATE && ! isChild() )
2245 if( pState->mnState & WINDOWSTATE_STATE_MAXIMIZED )
2246 gtk_window_maximize( GTK_WINDOW(m_pWindow) );
2247 else
2248 gtk_window_unmaximize( GTK_WINDOW(m_pWindow) );
2249 /* #i42379# there is no rollup state in GDK; and rolled up windows are
2250 * (probably depending on the WM) reported as iconified. If we iconify a
2251 * window here that was e.g. a dialog, then it will be unmapped but still
2252 * not be displayed in the task list, so it's an iconified window that
2253 * the user cannot get out of this state. So do not set the iconified state
2254 * on windows with a parent (that is transient frames) since these tend
2255 * to not be represented in an icon task list.
2257 if( (pState->mnState & WINDOWSTATE_STATE_MINIMIZED)
2258 && ! m_pParent )
2259 gtk_window_iconify( GTK_WINDOW(m_pWindow) );
2260 else
2261 gtk_window_deiconify( GTK_WINDOW(m_pWindow) );
2263 TriggerPaintEvent();
2266 bool GtkSalFrame::GetWindowState( SalFrameState* pState )
2268 pState->mnState = WINDOWSTATE_STATE_NORMAL;
2269 pState->mnMask = WINDOWSTATE_MASK_STATE;
2270 // rollup ? gtk 2.2 does not seem to support the shaded state
2271 if( (m_nState & GDK_WINDOW_STATE_ICONIFIED) )
2272 pState->mnState |= WINDOWSTATE_STATE_MINIMIZED;
2273 if( m_nState & GDK_WINDOW_STATE_MAXIMIZED )
2275 pState->mnState |= WINDOWSTATE_STATE_MAXIMIZED;
2276 pState->mnX = m_aRestorePosSize.Left();
2277 pState->mnY = m_aRestorePosSize.Top();
2278 pState->mnWidth = m_aRestorePosSize.GetWidth();
2279 pState->mnHeight = m_aRestorePosSize.GetHeight();
2280 pState->mnMaximizedX = maGeometry.nX;
2281 pState->mnMaximizedY = maGeometry.nY;
2282 pState->mnMaximizedWidth = maGeometry.nWidth;
2283 pState->mnMaximizedHeight = maGeometry.nHeight;
2284 pState->mnMask |= WINDOWSTATE_MASK_MAXIMIZED_X |
2285 WINDOWSTATE_MASK_MAXIMIZED_Y |
2286 WINDOWSTATE_MASK_MAXIMIZED_WIDTH |
2287 WINDOWSTATE_MASK_MAXIMIZED_HEIGHT;
2289 else
2291 pState->mnX = maGeometry.nX;
2292 pState->mnY = maGeometry.nY;
2293 pState->mnWidth = maGeometry.nWidth;
2294 pState->mnHeight = maGeometry.nHeight;
2296 pState->mnMask |= WINDOWSTATE_MASK_X |
2297 WINDOWSTATE_MASK_Y |
2298 WINDOWSTATE_MASK_WIDTH |
2299 WINDOWSTATE_MASK_HEIGHT;
2301 return true;
2304 typedef enum {
2305 SET_RETAIN_SIZE,
2306 SET_FULLSCREEN,
2307 SET_UN_FULLSCREEN
2308 } SetType;
2310 void GtkSalFrame::SetScreen( unsigned int nNewScreen, int eType, Rectangle *pSize )
2312 if( !m_pWindow )
2313 return;
2315 if (maGeometry.nDisplayScreenNumber == nNewScreen && eType == SET_RETAIN_SIZE)
2316 return;
2318 GdkScreen *pScreen = NULL;
2319 GdkRectangle aNewMonitor;
2321 bool bSpanAllScreens = nNewScreen == (unsigned int)-1;
2322 m_bSpanMonitorsWhenFullscreen = bSpanAllScreens && getDisplay()->getSystem()->GetDisplayScreenCount() > 1;
2324 if (m_bSpanMonitorsWhenFullscreen) //span all screens
2326 pScreen = gtk_widget_get_screen( m_pWindow );
2327 aNewMonitor.x = 0;
2328 aNewMonitor.y = 0;
2329 aNewMonitor.width = gdk_screen_get_width(pScreen);
2330 aNewMonitor.height = gdk_screen_get_height(pScreen);
2332 else
2334 gint nMonitor;
2335 bool bSameMonitor = false;
2337 if (!bSpanAllScreens)
2339 pScreen = getDisplay()->getSystem()->getScreenMonitorFromIdx( nNewScreen, nMonitor );
2340 if (!pScreen)
2342 g_warning ("Attempt to move GtkSalFrame to invalid screen %d => "
2343 "fallback to current\n", nNewScreen);
2347 if (!pScreen)
2349 pScreen = gtk_widget_get_screen( m_pWindow );
2350 bSameMonitor = true;
2353 // Heavy lifting, need to move screen ...
2354 if( pScreen != gtk_widget_get_screen( m_pWindow ))
2355 gtk_window_set_screen( GTK_WINDOW( m_pWindow ), pScreen );
2357 gint nOldMonitor = gdk_screen_get_monitor_at_window(
2358 pScreen, widget_get_window( m_pWindow ) );
2359 if (bSameMonitor)
2360 nMonitor = nOldMonitor;
2362 #if OSL_DEBUG_LEVEL > 1
2363 if( nMonitor == nOldMonitor )
2364 g_warning( "An apparently pointless SetScreen - should we elide it ?" );
2365 #endif
2367 GdkRectangle aOldMonitor;
2368 gdk_screen_get_monitor_geometry( pScreen, nOldMonitor, &aOldMonitor );
2369 gdk_screen_get_monitor_geometry( pScreen, nMonitor, &aNewMonitor );
2371 maGeometry.nX = aNewMonitor.x + maGeometry.nX - aOldMonitor.x;
2372 maGeometry.nY = aNewMonitor.y + maGeometry.nY - aOldMonitor.y;
2375 bool bResize = false;
2376 bool bVisible = IS_WIDGET_MAPPED( m_pWindow );
2377 if( bVisible )
2378 Show( false );
2380 if( eType == SET_FULLSCREEN )
2382 maGeometry.nX = aNewMonitor.x;
2383 maGeometry.nY = aNewMonitor.y;
2384 maGeometry.nWidth = aNewMonitor.width;
2385 maGeometry.nHeight = aNewMonitor.height;
2386 m_nStyle |= SAL_FRAME_STYLE_PARTIAL_FULLSCREEN;
2387 bResize = true;
2389 // #i110881# for the benefit of compiz set a max size here
2390 // else setting to fullscreen fails for unknown reasons
2391 m_aMaxSize.Width() = aNewMonitor.width;
2392 m_aMaxSize.Height() = aNewMonitor.height;
2395 if( pSize && eType == SET_UN_FULLSCREEN )
2397 maGeometry.nX = pSize->Left();
2398 maGeometry.nY = pSize->Top();
2399 maGeometry.nWidth = pSize->GetWidth();
2400 maGeometry.nHeight = pSize->GetHeight();
2401 m_nStyle &= ~SAL_FRAME_STYLE_PARTIAL_FULLSCREEN;
2402 bResize = true;
2405 if (bResize)
2407 // temporarily re-sizeable
2408 if( !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) )
2409 gtk_window_set_resizable( GTK_WINDOW(m_pWindow), TRUE );
2410 window_resize(maGeometry.nWidth, maGeometry.nHeight);
2411 //I wonder if we should instead leave maGeometry alone and rely on
2412 //configure-event to trigger signalConfigure and set it there
2413 AllocateFrame();
2414 TriggerPaintEvent();
2417 gtk_window_move( GTK_WINDOW( m_pWindow ), maGeometry.nX, maGeometry.nY );
2419 #if !GTK_CHECK_VERSION(3,0,0)
2420 // _NET_WM_STATE_FULLSCREEN (Metacity <-> KWin)
2421 if( ! getDisplay()->getWMAdaptor()->isLegacyPartialFullscreen() )
2422 #endif
2424 #if GTK_CHECK_VERSION(3,8,0)
2425 gdk_window_set_fullscreen_mode( widget_get_window(m_pWindow), m_bSpanMonitorsWhenFullscreen
2426 ? GDK_FULLSCREEN_ON_ALL_MONITORS : GDK_FULLSCREEN_ON_CURRENT_MONITOR );
2427 #endif
2428 if( eType == SET_FULLSCREEN )
2429 gtk_window_fullscreen( GTK_WINDOW( m_pWindow ) );
2430 else if( eType == SET_UN_FULLSCREEN )
2431 gtk_window_unfullscreen( GTK_WINDOW( m_pWindow ) );
2434 if( eType == SET_UN_FULLSCREEN &&
2435 !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) )
2436 gtk_window_set_resizable( GTK_WINDOW( m_pWindow ), FALSE );
2438 // FIXME: we should really let gtk+ handle our widget hierarchy ...
2439 if( m_pParent && gtk_widget_get_screen( m_pParent->m_pWindow ) != pScreen )
2440 SetParent( NULL );
2441 std::list< GtkSalFrame* > aChildren = m_aChildren;
2442 for( std::list< GtkSalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it )
2443 (*it)->SetScreen( nNewScreen, SET_RETAIN_SIZE );
2445 m_bDefaultPos = m_bDefaultSize = false;
2446 updateScreenNumber();
2447 CallCallback( SALEVENT_MOVERESIZE, NULL );
2449 if( bVisible )
2450 Show( true );
2453 void GtkSalFrame::SetScreenNumber( unsigned int nNewScreen )
2455 SetScreen( nNewScreen, SET_RETAIN_SIZE );
2458 void GtkSalFrame::updateWMClass()
2460 OString aResClass = OUStringToOString(m_sWMClass, RTL_TEXTENCODING_ASCII_US);
2461 const char *pResClass = !aResClass.isEmpty() ? aResClass.getStr() :
2462 SalGenericSystem::getFrameClassName();
2463 Display *display;
2465 if (!getDisplay()->IsX11Display())
2466 return;
2468 #if GTK_CHECK_VERSION(3,0,0)
2469 display = GDK_DISPLAY_XDISPLAY(getGdkDisplay());
2470 #else
2471 display = getDisplay()->GetDisplay();
2472 #endif
2474 if( IS_WIDGET_REALIZED( m_pWindow ) )
2476 XClassHint* pClass = XAllocClassHint();
2477 OString aResName = SalGenericSystem::getFrameResName();
2478 pClass->res_name = const_cast<char*>(aResName.getStr());
2479 pClass->res_class = const_cast<char*>(pResClass);
2480 XSetClassHint( display,
2481 widget_get_xid(m_pWindow),
2482 pClass );
2483 XFree( pClass );
2487 void GtkSalFrame::SetApplicationID( const OUString &rWMClass )
2489 if( rWMClass != m_sWMClass && ! isChild() )
2491 m_sWMClass = rWMClass;
2492 updateWMClass();
2494 for( std::list< GtkSalFrame* >::iterator it = m_aChildren.begin(); it != m_aChildren.end(); ++it )
2495 (*it)->SetApplicationID(rWMClass);
2499 void GtkSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nScreen )
2501 m_bFullscreen = bFullScreen;
2503 if( !m_pWindow || isChild() )
2504 return;
2506 if( bFullScreen )
2508 m_aRestorePosSize = Rectangle( Point( maGeometry.nX, maGeometry.nY ),
2509 Size( maGeometry.nWidth, maGeometry.nHeight ) );
2510 SetScreen( nScreen, SET_FULLSCREEN );
2512 else
2514 SetScreen( nScreen, SET_UN_FULLSCREEN,
2515 !m_aRestorePosSize.IsEmpty() ? &m_aRestorePosSize : NULL );
2516 m_aRestorePosSize = Rectangle();
2520 /* definitions from xautolock.c (pl15) */
2521 #define XAUTOLOCK_DISABLE 1
2522 #define XAUTOLOCK_ENABLE 2
2524 void GtkSalFrame::setAutoLock( bool bLock )
2526 if( isChild() || !getDisplay()->IsX11Display() )
2527 return;
2529 GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(m_pWindow) );
2530 GdkDisplay *pDisplay = gdk_screen_get_display( pScreen );
2531 GdkWindow *pRootWin = gdk_screen_get_root_window( pScreen );
2533 Atom nAtom = XInternAtom( GDK_DISPLAY_XDISPLAY( pDisplay ),
2534 "XAUTOLOCK_MESSAGE", False );
2536 int nMessage = bLock ? XAUTOLOCK_ENABLE : XAUTOLOCK_DISABLE;
2538 XChangeProperty( GDK_DISPLAY_XDISPLAY( pDisplay ),
2539 GDK_WINDOW_XID( pRootWin ),
2540 nAtom, XA_INTEGER,
2541 8, PropModeReplace,
2542 reinterpret_cast<unsigned char*>(&nMessage),
2543 sizeof( nMessage ) );
2546 #ifdef ENABLE_DBUS
2547 /** cookie is returned as an unsigned integer */
2548 static guint
2549 dbus_inhibit_gsm (const gchar *appname,
2550 const gchar *reason,
2551 guint xid)
2553 gboolean res;
2554 guint cookie;
2555 GError *error = NULL;
2556 DBusGProxy *proxy = NULL;
2558 /* get the DBUS session connection */
2559 DBusGConnection *session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
2560 if (error != NULL) {
2561 g_debug ("DBUS cannot connect : %s", error->message);
2562 g_error_free (error);
2563 return -1;
2566 /* get the proxy with gnome-session-manager */
2567 proxy = dbus_g_proxy_new_for_name (session_connection,
2568 GSM_DBUS_SERVICE,
2569 GSM_DBUS_PATH,
2570 GSM_DBUS_INTERFACE);
2571 if (proxy == NULL) {
2572 g_debug ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE);
2573 return -1;
2576 res = dbus_g_proxy_call (proxy,
2577 "Inhibit", &error,
2578 G_TYPE_STRING, appname,
2579 G_TYPE_UINT, xid,
2580 G_TYPE_STRING, reason,
2581 G_TYPE_UINT, 8, //Inhibit the session being marked as idle
2582 G_TYPE_INVALID,
2583 G_TYPE_UINT, &cookie,
2584 G_TYPE_INVALID);
2586 /* check the return value */
2587 if (! res) {
2588 cookie = -1;
2589 g_debug ("Inhibit method failed");
2592 /* check the error value */
2593 if (error != NULL) {
2594 g_debug ("Inhibit problem : %s", error->message);
2595 g_error_free (error);
2596 cookie = -1;
2599 g_object_unref (G_OBJECT (proxy));
2600 return cookie;
2603 static void
2604 dbus_uninhibit_gsm (guint cookie)
2606 gboolean res;
2607 GError *error = NULL;
2608 DBusGProxy *proxy = NULL;
2609 DBusGConnection *session_connection = NULL;
2611 if (cookie == guint(-1)) {
2612 g_debug ("Invalid cookie");
2613 return;
2616 /* get the DBUS session connection */
2617 session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
2618 if (error) {
2619 g_debug ("DBUS cannot connect : %s", error->message);
2620 g_error_free (error);
2621 return;
2624 /* get the proxy with gnome-session-manager */
2625 proxy = dbus_g_proxy_new_for_name (session_connection,
2626 GSM_DBUS_SERVICE,
2627 GSM_DBUS_PATH,
2628 GSM_DBUS_INTERFACE);
2629 if (proxy == NULL) {
2630 g_debug ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE);
2631 return;
2634 res = dbus_g_proxy_call (proxy,
2635 "Uninhibit",
2636 &error,
2637 G_TYPE_UINT, cookie,
2638 G_TYPE_INVALID,
2639 G_TYPE_INVALID);
2641 /* check the return value */
2642 if (! res) {
2643 g_debug ("Uninhibit method failed");
2646 /* check the error value */
2647 if (error != NULL) {
2648 g_debug ("Uninhibit problem : %s", error->message);
2649 g_error_free (error);
2650 cookie = -1;
2652 g_object_unref (G_OBJECT (proxy));
2654 #endif
2656 void GtkSalFrame::StartPresentation( bool bStart )
2658 setAutoLock( !bStart );
2660 if( !getDisplay()->IsX11Display() )
2661 return;
2663 #if !GTK_CHECK_VERSION(3,0,0)
2664 Display *pDisplay = GDK_DISPLAY_XDISPLAY( getGdkDisplay() );
2666 int nTimeout, nInterval, bPreferBlanking, bAllowExposures;
2667 XGetScreenSaver( pDisplay, &nTimeout, &nInterval,
2668 &bPreferBlanking, &bAllowExposures );
2669 #endif
2670 if( bStart )
2672 #if !GTK_CHECK_VERSION(3,0,0)
2673 if ( nTimeout )
2675 m_nSavedScreenSaverTimeout = nTimeout;
2676 XResetScreenSaver( pDisplay );
2677 XSetScreenSaver( pDisplay, 0, nInterval,
2678 bPreferBlanking, bAllowExposures );
2680 #endif
2681 #ifdef ENABLE_DBUS
2682 m_nGSMCookie = dbus_inhibit_gsm(g_get_application_name(), "presentation",
2683 widget_get_xid(m_pWindow));
2684 #endif
2686 else
2688 #if !GTK_CHECK_VERSION(3,0,0)
2689 if( m_nSavedScreenSaverTimeout )
2690 XSetScreenSaver( pDisplay, m_nSavedScreenSaverTimeout,
2691 nInterval, bPreferBlanking,
2692 bAllowExposures );
2693 #endif
2694 m_nSavedScreenSaverTimeout = 0;
2695 #ifdef ENABLE_DBUS
2696 dbus_uninhibit_gsm(m_nGSMCookie);
2697 #endif
2701 void GtkSalFrame::SetAlwaysOnTop( bool bOnTop )
2703 if( m_pWindow )
2704 gtk_window_set_keep_above( GTK_WINDOW( m_pWindow ), bOnTop );
2707 void GtkSalFrame::ToTop( sal_uInt16 nFlags )
2709 if( m_pWindow )
2711 if( isChild( false, true ) )
2712 gtk_widget_grab_focus( m_pWindow );
2713 else if( IS_WIDGET_MAPPED( m_pWindow ) )
2715 if( ! (nFlags & SAL_FRAME_TOTOP_GRABFOCUS_ONLY) )
2716 gtk_window_present( GTK_WINDOW(m_pWindow) );
2717 else
2719 #if !GTK_CHECK_VERSION(3,0,0)
2720 guint32 nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window);
2721 #else
2722 guint32 nUserTime = GDK_CURRENT_TIME;
2723 #endif
2724 gdk_window_focus( widget_get_window(m_pWindow), nUserTime );
2726 #if !GTK_CHECK_VERSION(3,0,0)
2727 /* need to do an XSetInputFocus here because
2728 * gdk_window_focus will ask a EWMH compliant WM to put the focus
2729 * to our window - which it of course won't since our input hint
2730 * is set to false.
2732 if( (m_nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_FLOAT_FOCUSABLE)) )
2734 // sad but true: this can cause an XError, we need to catch that
2735 // to do this we need to synchronize with the XServer
2736 GetGenericData()->ErrorTrapPush();
2737 XSetInputFocus( getDisplay()->GetDisplay(), widget_get_xid(m_pWindow), RevertToParent, CurrentTime );
2738 // fdo#46687 - an XSync should not be necessary - but for some reason it is.
2739 XSync( getDisplay()->GetDisplay(), False );
2740 GetGenericData()->ErrorTrapPop();
2742 #endif
2744 else
2746 if( nFlags & SAL_FRAME_TOTOP_RESTOREWHENMIN )
2747 gtk_window_present( GTK_WINDOW(m_pWindow) );
2752 void GtkSalFrame::SetPointer( PointerStyle ePointerStyle )
2754 if( m_pWindow && ePointerStyle != m_ePointerStyle )
2756 m_ePointerStyle = ePointerStyle;
2757 GdkCursor *pCursor = getDisplay()->getCursor( ePointerStyle );
2758 gdk_window_set_cursor( widget_get_window(m_pWindow), pCursor );
2759 m_pCurrentCursor = pCursor;
2761 // #i80791# use grabPointer the same way as CaptureMouse, respective float grab
2762 if( getDisplay()->MouseCaptured( this ) )
2763 grabPointer( true, false );
2764 else if( m_nFloats > 0 )
2765 grabPointer( true, true );
2769 void GtkSalFrame::grabPointer( bool bGrab, bool bOwnerEvents )
2771 static const char* pEnv = getenv( "SAL_NO_MOUSEGRABS" );
2772 if (pEnv && *pEnv)
2773 return;
2775 if (!m_pWindow)
2776 return;
2778 const int nMask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2780 #if GTK_CHECK_VERSION(3,0,0)
2781 GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay());
2782 GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager);
2783 if (bGrab)
2784 gdk_device_grab(pPointer, widget_get_window(m_pWindow), GDK_OWNERSHIP_NONE, bOwnerEvents, (GdkEventMask) nMask, m_pCurrentCursor, GDK_CURRENT_TIME);
2785 else
2786 gdk_device_ungrab(pPointer, GDK_CURRENT_TIME);
2787 #else
2788 if( bGrab )
2790 bool bUseGdkGrab = true;
2791 const std::list< SalFrame* >& rFrames = getDisplay()->getFrames();
2792 for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it )
2794 const GtkSalFrame* pFrame = static_cast< const GtkSalFrame* >(*it);
2795 if( pFrame->m_bWindowIsGtkPlug )
2797 bUseGdkGrab = false;
2798 break;
2801 if( bUseGdkGrab )
2803 gdk_pointer_grab( widget_get_window( m_pWindow ), bOwnerEvents,
2804 (GdkEventMask) nMask, NULL, m_pCurrentCursor,
2805 GDK_CURRENT_TIME );
2807 else
2809 // FIXME: for some unknown reason gdk_pointer_grab does not
2810 // really produce owner events for GtkPlug windows
2811 // the cause is yet unknown
2813 // this is of course a bad hack, especially as we cannot
2814 // set the right cursor this way
2815 XGrabPointer( getDisplay()->GetDisplay(),
2816 widget_get_xid( m_pWindow ),
2817 bOwnerEvents,
2818 PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
2819 GrabModeAsync,
2820 GrabModeAsync,
2821 None,
2822 None,
2823 CurrentTime
2827 else
2829 // Two GdkDisplays may be open
2830 gdk_display_pointer_ungrab( getGdkDisplay(), GDK_CURRENT_TIME);
2832 #endif
2835 void GtkSalFrame::grabKeyboard( bool bGrab )
2837 if (!m_pWindow)
2838 return;
2840 #if GTK_CHECK_VERSION(3,0,0)
2841 GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay());
2842 GdkDevice* pPointer = gdk_device_manager_get_client_pointer(pDeviceManager);
2843 GdkDevice* pKeyboard = gdk_device_get_associated_device(pPointer);
2844 if (bGrab)
2846 gdk_device_grab(pKeyboard, widget_get_window(m_pWindow), GDK_OWNERSHIP_NONE,
2847 true, (GdkEventMask)(GDK_KEY_PRESS | GDK_KEY_RELEASE), NULL, GDK_CURRENT_TIME);
2849 else
2851 gdk_device_ungrab(pKeyboard, GDK_CURRENT_TIME);
2853 #else
2854 if( bGrab )
2856 gdk_keyboard_grab(widget_get_window(m_pWindow), true,
2857 GDK_CURRENT_TIME);
2859 else
2861 gdk_keyboard_ungrab(GDK_CURRENT_TIME);
2863 #endif
2866 void GtkSalFrame::CaptureMouse( bool bCapture )
2868 getDisplay()->CaptureMouse( bCapture ? this : NULL );
2871 void GtkSalFrame::SetPointerPos( long nX, long nY )
2873 GtkSalFrame* pFrame = this;
2874 while( pFrame && pFrame->isChild( false, true ) )
2875 pFrame = pFrame->m_pParent;
2876 if( ! pFrame )
2877 return;
2879 GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(pFrame->m_pWindow) );
2880 GdkDisplay *pDisplay = gdk_screen_get_display( pScreen );
2882 /* when the application tries to center the mouse in the dialog the
2883 * window isn't mapped already. So use coordinates relative to the root window.
2885 unsigned int nWindowLeft = maGeometry.nX + nX;
2886 unsigned int nWindowTop = maGeometry.nY + nY;
2888 XWarpPointer( GDK_DISPLAY_XDISPLAY (pDisplay), None,
2889 GDK_WINDOW_XID (gdk_screen_get_root_window( pScreen ) ),
2890 0, 0, 0, 0, nWindowLeft, nWindowTop);
2891 // #i38648# ask for the next motion hint
2892 gint x, y;
2893 GdkModifierType mask;
2894 gdk_window_get_pointer( widget_get_window(pFrame->m_pWindow) , &x, &y, &mask );
2897 void GtkSalFrame::Flush()
2899 #if GTK_CHECK_VERSION(3,0,0)
2900 gdk_display_flush( getGdkDisplay() );
2901 #else
2902 XFlush (GDK_DISPLAY_XDISPLAY (getGdkDisplay()));
2903 #endif
2906 void GtkSalFrame::Sync()
2908 gdk_display_sync( getGdkDisplay() );
2911 #ifndef GDK_Open
2912 #define GDK_Open 0x1008ff6b
2913 #endif
2914 #ifndef GDK_Paste
2915 #define GDK_Paste 0x1008ff6d
2916 #endif
2917 #ifndef GDK_Copy
2918 #define GDK_Copy 0x1008ff57
2919 #endif
2920 #ifndef GDK_Cut
2921 #define GDK_Cut 0x1008ff58
2922 #endif
2924 void GtkSalFrame::KeyCodeToGdkKey(const vcl::KeyCode& rKeyCode,
2925 guint* pGdkKeyCode, GdkModifierType *pGdkModifiers)
2927 if ( pGdkKeyCode == NULL || pGdkModifiers == NULL )
2928 return;
2930 // Get GDK key modifiers
2931 GdkModifierType nModifiers = (GdkModifierType) 0;
2933 if ( rKeyCode.IsShift() )
2934 nModifiers = (GdkModifierType) ( nModifiers | GDK_SHIFT_MASK );
2936 if ( rKeyCode.IsMod1() )
2937 nModifiers = (GdkModifierType) ( nModifiers | GDK_CONTROL_MASK );
2939 if ( rKeyCode.IsMod2() )
2940 nModifiers = (GdkModifierType) ( nModifiers | GDK_MOD1_MASK );
2942 *pGdkModifiers = nModifiers;
2944 // Get GDK keycode.
2945 guint nKeyCode = 0;
2947 guint nCode = rKeyCode.GetCode();
2949 if ( nCode >= KEY_0 && nCode <= KEY_9 )
2950 nKeyCode = ( nCode - KEY_0 ) + GDK_0;
2951 else if ( nCode >= KEY_A && nCode <= KEY_Z )
2952 nKeyCode = ( nCode - KEY_A ) + GDK_A;
2953 else if ( nCode >= KEY_F1 && nCode <= KEY_F26 )
2954 nKeyCode = ( nCode - KEY_F1 ) + GDK_F1;
2955 else
2957 switch( nCode )
2959 case KEY_DOWN: nKeyCode = GDK_Down; break;
2960 case KEY_UP: nKeyCode = GDK_Up; break;
2961 case KEY_LEFT: nKeyCode = GDK_Left; break;
2962 case KEY_RIGHT: nKeyCode = GDK_Right; break;
2963 case KEY_HOME: nKeyCode = GDK_Home; break;
2964 case KEY_END: nKeyCode = GDK_End; break;
2965 case KEY_PAGEUP: nKeyCode = GDK_Page_Up; break;
2966 case KEY_PAGEDOWN: nKeyCode = GDK_Page_Down; break;
2967 case KEY_RETURN: nKeyCode = GDK_Return; break;
2968 case KEY_ESCAPE: nKeyCode = GDK_Escape; break;
2969 case KEY_TAB: nKeyCode = GDK_Tab; break;
2970 case KEY_BACKSPACE: nKeyCode = GDK_BackSpace; break;
2971 case KEY_SPACE: nKeyCode = GDK_space; break;
2972 case KEY_INSERT: nKeyCode = GDK_Insert; break;
2973 case KEY_DELETE: nKeyCode = GDK_Delete; break;
2974 case KEY_ADD: nKeyCode = GDK_plus; break;
2975 case KEY_SUBTRACT: nKeyCode = GDK_minus; break;
2976 case KEY_MULTIPLY: nKeyCode = GDK_asterisk; break;
2977 case KEY_DIVIDE: nKeyCode = GDK_slash; break;
2978 case KEY_POINT: nKeyCode = GDK_period; break;
2979 case KEY_COMMA: nKeyCode = GDK_comma; break;
2980 case KEY_LESS: nKeyCode = GDK_less; break;
2981 case KEY_GREATER: nKeyCode = GDK_greater; break;
2982 case KEY_EQUAL: nKeyCode = GDK_equal; break;
2983 case KEY_FIND: nKeyCode = GDK_Find; break;
2984 case KEY_CONTEXTMENU: nKeyCode = GDK_Menu; break;
2985 case KEY_HELP: nKeyCode = GDK_Help; break;
2986 case KEY_UNDO: nKeyCode = GDK_Undo; break;
2987 case KEY_REPEAT: nKeyCode = GDK_Redo; break;
2988 case KEY_DECIMAL: nKeyCode = GDK_KP_Decimal; break;
2989 case KEY_TILDE: nKeyCode = GDK_asciitilde; break;
2990 case KEY_QUOTELEFT: nKeyCode = GDK_quoteleft; break;
2991 case KEY_BRACKETLEFT: nKeyCode = GDK_bracketleft; break;
2992 case KEY_BRACKETRIGHT: nKeyCode = GDK_bracketright; break;
2993 case KEY_SEMICOLON: nKeyCode = GDK_semicolon; break;
2994 case KEY_QUOTERIGHT: nKeyCode = GDK_quoteright; break;
2996 // Special cases
2997 case KEY_COPY: nKeyCode = GDK_Copy; break;
2998 case KEY_CUT: nKeyCode = GDK_Cut; break;
2999 case KEY_PASTE: nKeyCode = GDK_Paste; break;
3000 case KEY_OPEN: nKeyCode = GDK_Open; break;
3004 *pGdkKeyCode = nKeyCode;
3007 OUString GtkSalFrame::GetKeyName( sal_uInt16 nKeyCode )
3009 #if !GTK_CHECK_VERSION(3,0,0)
3010 return getDisplay()->GetKeyName( nKeyCode );
3011 #else
3012 guint nGtkKeyCode;
3013 GdkModifierType nGtkModifiers;
3014 KeyCodeToGdkKey(nKeyCode, &nGtkKeyCode, &nGtkModifiers );
3016 gchar* pName = gtk_accelerator_get_label(nGtkKeyCode, nGtkModifiers);
3017 OUString aRet(pName, rtl_str_getLength(pName), RTL_TEXTENCODING_UTF8);
3018 g_free(pName);
3019 return aRet;
3020 #endif
3023 GdkDisplay *GtkSalFrame::getGdkDisplay()
3025 return GetGtkSalData()->GetGdkDisplay();
3028 GtkSalDisplay *GtkSalFrame::getDisplay()
3030 return GetGtkSalData()->GetGtkDisplay();
3033 SalFrame::SalPointerState GtkSalFrame::GetPointerState()
3035 SalPointerState aState;
3036 GdkScreen* pScreen;
3037 gint x, y;
3038 GdkModifierType aMask;
3039 gdk_display_get_pointer( getGdkDisplay(), &pScreen, &x, &y, &aMask );
3040 aState.maPos = Point( x - maGeometry.nX, y - maGeometry.nY );
3041 aState.mnState = GetMouseModCode( aMask );
3042 return aState;
3045 KeyIndicatorState GtkSalFrame::GetIndicatorState()
3047 #if !GTK_CHECK_VERSION(3,0,0)
3048 return GetGtkSalData()->GetGtkDisplay()->GetIndicatorState();
3049 #else
3050 g_warning ("missing get indicator state");
3051 return KeyIndicatorState::NONE;
3052 #endif
3055 void GtkSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode )
3057 #if !GTK_CHECK_VERSION(3,0,0)
3058 GetGtkSalData()->GetGtkDisplay()->SimulateKeyPress(nKeyCode);
3059 #else
3060 g_warning ("missing simulate keypress %d", nKeyCode);
3061 #endif
3064 void GtkSalFrame::SetInputContext( SalInputContext* pContext )
3066 if( ! pContext )
3067 return;
3069 if( ! (pContext->mnOptions & InputContextFlags::Text) )
3070 return;
3072 // create a new im context
3073 if( ! m_pIMHandler )
3074 m_pIMHandler = new IMHandler( this );
3077 void GtkSalFrame::EndExtTextInput( sal_uInt16 nFlags )
3079 if( m_pIMHandler )
3080 m_pIMHandler->endExtTextInput( nFlags );
3083 bool GtkSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , vcl::KeyCode& )
3085 // not supported yet
3086 return false;
3089 LanguageType GtkSalFrame::GetInputLanguage()
3091 return LANGUAGE_DONTKNOW;
3094 void GtkSalFrame::UpdateSettings( AllSettings& rSettings )
3096 if( ! m_pWindow )
3097 return;
3099 GtkSalGraphics* pGraphics = static_cast<GtkSalGraphics*>(m_aGraphics[0].pGraphics);
3100 bool bFreeGraphics = false;
3101 if( ! pGraphics )
3103 pGraphics = static_cast<GtkSalGraphics*>(AcquireGraphics());
3104 if ( !pGraphics )
3106 SAL_WARN("vcl", "Could not get graphics - unable to update settings");
3107 return;
3109 bFreeGraphics = true;
3112 pGraphics->updateSettings( rSettings );
3114 if( bFreeGraphics )
3115 ReleaseGraphics( pGraphics );
3118 void GtkSalFrame::Beep()
3120 gdk_display_beep( getGdkDisplay() );
3123 const SystemEnvData* GtkSalFrame::GetSystemData() const
3125 return &m_aSystemData;
3128 void GtkSalFrame::SetParent( SalFrame* pNewParent )
3130 if( m_pParent )
3131 m_pParent->m_aChildren.remove( this );
3132 m_pParent = static_cast<GtkSalFrame*>(pNewParent);
3133 if( m_pParent )
3134 m_pParent->m_aChildren.push_back( this );
3135 if( ! isChild() )
3136 gtk_window_set_transient_for( GTK_WINDOW(m_pWindow),
3137 (m_pParent && ! m_pParent->isChild(true,false)) ? GTK_WINDOW(m_pParent->m_pWindow) : NULL
3141 #if !GTK_CHECK_VERSION(3,0,0)
3143 void GtkSalFrame::createNewWindow( ::Window aNewParent, bool bXEmbed, SalX11Screen nXScreen )
3145 bool bWasVisible = m_pWindow && IS_WIDGET_MAPPED(m_pWindow);
3146 if( bWasVisible )
3147 Show( false );
3149 if( (int)nXScreen.getXScreen() >= getDisplay()->GetXScreenCount() )
3150 nXScreen = m_nXScreen;
3152 SystemParentData aParentData;
3153 aParentData.aWindow = aNewParent;
3154 aParentData.bXEmbedSupport = bXEmbed;
3155 if( aNewParent == None )
3157 aNewParent = getDisplay()->GetRootWindow(nXScreen);
3158 aParentData.aWindow = None;
3159 aParentData.bXEmbedSupport = false;
3161 else
3163 // is new parent a root window ?
3164 Display* pDisp = getDisplay()->GetDisplay();
3165 int nScreens = getDisplay()->GetXScreenCount();
3166 for( int i = 0; i < nScreens; i++ )
3168 if( aNewParent == RootWindow( pDisp, i ) )
3170 nXScreen = SalX11Screen( i );
3171 aParentData.aWindow = None;
3172 aParentData.bXEmbedSupport = false;
3173 break;
3178 // free xrender resources
3179 for( unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); i++ )
3180 if( m_aGraphics[i].bInUse )
3181 m_aGraphics[i].pGraphics->SetDrawable( None, m_nXScreen );
3183 // first deinit frame
3184 if( m_pIMHandler )
3186 delete m_pIMHandler;
3187 m_pIMHandler = NULL;
3189 if( m_pRegion )
3191 gdk_region_destroy( m_pRegion );
3193 if( m_pFixedContainer )
3194 gtk_widget_destroy( GTK_WIDGET(m_pFixedContainer) );
3195 if( m_pWindow )
3196 gtk_widget_destroy( m_pWindow );
3197 if( m_pForeignParent )
3198 g_object_unref( G_OBJECT( m_pForeignParent ) );
3199 if( m_pForeignTopLevel )
3200 g_object_unref( G_OBJECT( m_pForeignTopLevel ) );
3202 // init new window
3203 m_bDefaultPos = m_bDefaultSize = false;
3204 if( aParentData.aWindow != None )
3206 m_nStyle |= SAL_FRAME_STYLE_PLUG;
3207 Init( &aParentData );
3209 else
3211 m_nStyle &= ~SAL_FRAME_STYLE_PLUG;
3212 Init( (m_pParent && m_pParent->m_nXScreen == m_nXScreen) ? m_pParent : NULL, m_nStyle );
3215 // update graphics
3216 for( unsigned int i = 0; i < SAL_N_ELEMENTS(m_aGraphics); i++ )
3218 if( m_aGraphics[i].bInUse )
3220 m_aGraphics[i].pGraphics->SetDrawable( widget_get_xid(m_pWindow), m_nXScreen );
3221 m_aGraphics[i].pGraphics->SetWindow( m_pWindow );
3225 if( ! m_aTitle.isEmpty() )
3226 SetTitle( m_aTitle );
3228 if( bWasVisible )
3229 Show( true );
3231 std::list< GtkSalFrame* > aChildren = m_aChildren;
3232 m_aChildren.clear();
3233 for( std::list< GtkSalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it )
3234 (*it)->createNewWindow( None, false, m_nXScreen );
3236 // FIXME: SalObjects
3238 #endif
3240 bool GtkSalFrame::SetPluginParent( SystemParentData* pSysParent )
3242 #if !GTK_CHECK_VERSION(3,0,0)
3243 GetGenericData()->ErrorTrapPush(); // permanantly ignore unruly children's errors
3244 createNewWindow( pSysParent->aWindow, (pSysParent->nSize > sizeof(long)) && pSysParent->bXEmbedSupport, m_nXScreen );
3245 return true;
3246 #else
3247 (void)pSysParent;
3248 //FIXME: no SetPluginParent impl. for gtk3
3249 return false;
3250 #endif
3253 void GtkSalFrame::ResetClipRegion()
3255 if( m_pWindow )
3256 gdk_window_shape_combine_region( widget_get_window( m_pWindow ), NULL, 0, 0 );
3259 void GtkSalFrame::BeginSetClipRegion( sal_uLong )
3261 #if GTK_CHECK_VERSION(3,0,0)
3262 if( m_pRegion )
3263 cairo_region_destroy( m_pRegion );
3264 m_pRegion = cairo_region_create();
3265 #else
3266 if( m_pRegion )
3267 gdk_region_destroy( m_pRegion );
3268 m_pRegion = gdk_region_new();
3269 #endif
3272 void GtkSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight )
3274 if( m_pRegion )
3276 GdkRectangle aRect;
3277 aRect.x = nX;
3278 aRect.y = nY;
3279 aRect.width = nWidth;
3280 aRect.height = nHeight;
3281 #if GTK_CHECK_VERSION(3,0,0)
3282 cairo_region_union_rectangle( m_pRegion, &aRect );
3283 #else
3284 gdk_region_union_with_rect( m_pRegion, &aRect );
3285 #endif
3289 void GtkSalFrame::EndSetClipRegion()
3291 if( m_pWindow && m_pRegion )
3292 gdk_window_shape_combine_region( widget_get_window(m_pWindow), m_pRegion, 0, 0 );
3295 #if !GTK_CHECK_VERSION(3,0,0)
3296 bool GtkSalFrame::Dispatch( const XEvent* pEvent )
3298 bool bContinueDispatch = true;
3300 if( pEvent->type == PropertyNotify )
3302 vcl_sal::WMAdaptor* pAdaptor = getDisplay()->getWMAdaptor();
3303 Atom nDesktopAtom = pAdaptor->getAtom( vcl_sal::WMAdaptor::NET_WM_DESKTOP );
3304 if( pEvent->xproperty.atom == nDesktopAtom &&
3305 pEvent->xproperty.state == PropertyNewValue )
3307 m_nWorkArea = pAdaptor->getWindowWorkArea( widget_get_xid(m_pWindow) );
3310 else if( pEvent->type == ConfigureNotify )
3312 if( m_pForeignParent && pEvent->xconfigure.window == m_aForeignParentWindow )
3314 bContinueDispatch = false;
3315 gtk_window_resize( GTK_WINDOW(m_pWindow), pEvent->xconfigure.width, pEvent->xconfigure.height );
3316 if( ( sal::static_int_cast< int >(maGeometry.nWidth) !=
3317 pEvent->xconfigure.width ) ||
3318 ( sal::static_int_cast< int >(maGeometry.nHeight) !=
3319 pEvent->xconfigure.height ) )
3321 maGeometry.nWidth = pEvent->xconfigure.width;
3322 maGeometry.nHeight = pEvent->xconfigure.height;
3323 setMinMaxSize();
3324 getDisplay()->SendInternalEvent( this, NULL, SALEVENT_RESIZE );
3327 else if( m_pForeignTopLevel && pEvent->xconfigure.window == m_aForeignTopLevelWindow )
3329 bContinueDispatch = false;
3330 // update position
3331 int x = 0, y = 0;
3332 ::Window aChild;
3333 XTranslateCoordinates( getDisplay()->GetDisplay(),
3334 widget_get_xid(m_pWindow),
3335 getDisplay()->GetRootWindow( getDisplay()->GetDefaultXScreen() ),
3336 0, 0,
3337 &x, &y,
3338 &aChild );
3339 if( x != maGeometry.nX || y != maGeometry.nY )
3341 maGeometry.nX = x;
3342 maGeometry.nY = y;
3343 getDisplay()->SendInternalEvent( this, NULL, SALEVENT_MOVE );
3347 else if( pEvent->type == ClientMessage &&
3348 pEvent->xclient.message_type == getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED ) &&
3349 pEvent->xclient.window == widget_get_xid(m_pWindow) &&
3350 m_bWindowIsGtkPlug
3353 // FIXME: this should not be necessary, GtkPlug should do this
3354 // transparently for us
3355 if( pEvent->xclient.data.l[1] == 1 || // XEMBED_WINDOW_ACTIVATE
3356 pEvent->xclient.data.l[1] == 2 // XEMBED_WINDOW_DEACTIVATE
3359 GdkEventFocus aEvent;
3360 aEvent.type = GDK_FOCUS_CHANGE;
3361 aEvent.window = widget_get_window( m_pWindow );
3362 aEvent.send_event = gint8(TRUE);
3363 aEvent.in = gint16(pEvent->xclient.data.l[1] == 1);
3364 signalFocus( m_pWindow, &aEvent, this );
3368 return bContinueDispatch;
3370 #endif
3372 gboolean GtkSalFrame::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer frame )
3374 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3376 SalMouseEvent aEvent;
3377 sal_uInt16 nEventType = 0;
3378 switch( pEvent->type )
3380 case GDK_BUTTON_PRESS:
3381 nEventType = SALEVENT_MOUSEBUTTONDOWN;
3382 break;
3383 case GDK_BUTTON_RELEASE:
3384 nEventType = SALEVENT_MOUSEBUTTONUP;
3385 break;
3386 default:
3387 return false;
3389 switch( pEvent->button )
3391 case 1: aEvent.mnButton = MOUSE_LEFT; break;
3392 case 2: aEvent.mnButton = MOUSE_MIDDLE; break;
3393 case 3: aEvent.mnButton = MOUSE_RIGHT; break;
3394 default: return false;
3396 aEvent.mnTime = pEvent->time;
3397 aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX;
3398 aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY;
3399 aEvent.mnCode = GetMouseModCode( pEvent->state );
3401 bool bClosePopups = false;
3402 if( pEvent->type == GDK_BUTTON_PRESS &&
3403 (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) == 0
3406 if( m_nFloats > 0 )
3408 // close popups if user clicks outside our application
3409 gint x, y;
3410 bClosePopups = (gdk_display_get_window_at_pointer( GtkSalFrame::getGdkDisplay(), &x, &y ) == NULL);
3412 /* #i30306# release implicit pointer grab if no popups are open; else
3413 * Drag cannot grab the pointer and will fail.
3415 if( m_nFloats < 1 || bClosePopups )
3416 gdk_display_pointer_ungrab( GtkSalFrame::getGdkDisplay(), GDK_CURRENT_TIME );
3419 if( pThis->m_bWindowIsGtkPlug &&
3420 pEvent->type == GDK_BUTTON_PRESS &&
3421 pEvent->button == 1 )
3423 pThis->askForXEmbedFocus( pEvent->time );
3426 // --- RTL --- (mirror mouse pos)
3427 if( AllSettings::GetLayoutRTL() )
3428 aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX;
3430 vcl::DeletionListener aDel( pThis );
3432 pThis->CallCallback( nEventType, &aEvent );
3434 if( ! aDel.isDeleted() )
3436 if( bClosePopups )
3438 ImplSVData* pSVData = ImplGetSVData();
3439 if ( pSVData->maWinData.mpFirstFloat )
3441 static const char* pEnv = getenv( "SAL_FLOATWIN_NOAPPFOCUSCLOSE" );
3442 if ( !(pSVData->maWinData.mpFirstFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose) && !(pEnv && *pEnv) )
3443 pSVData->maWinData.mpFirstFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
3447 if( ! aDel.isDeleted() )
3449 int frame_x = (int)(pEvent->x_root - pEvent->x);
3450 int frame_y = (int)(pEvent->y_root - pEvent->y);
3451 if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY )
3453 pThis->maGeometry.nX = frame_x;
3454 pThis->maGeometry.nY = frame_y;
3455 pThis->CallCallback( SALEVENT_MOVE, NULL );
3460 return true;
3463 #if GTK_CHECK_VERSION(3,0,0)
3464 void GtkSalFrame::signalFlagsChanged( GtkWidget* , GtkStateFlags state, gpointer frame )
3466 //TO-DO: This isn't as helpful as I'd like it to be. The color selector puts the main
3467 //windows into the backdrop, disabling everything, and the floating navigator window
3468 //is also problematic.
3469 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3471 bool bOldBackDrop = state & GTK_STATE_FLAG_BACKDROP;
3472 bool bNewBackDrop = (gtk_widget_get_state_flags(GTK_WIDGET(pThis->m_pWindow)) & GTK_STATE_FLAG_BACKDROP);
3473 if (bNewBackDrop && !bOldBackDrop)
3475 pThis->GetWindow()->Disable();
3477 else if (bOldBackDrop && !bNewBackDrop)
3479 pThis->GetWindow()->Enable();
3482 #endif
3484 gboolean GtkSalFrame::signalScroll( GtkWidget*, GdkEvent* pEvent, gpointer frame )
3486 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3487 GdkEventScroll* pSEvent = reinterpret_cast<GdkEventScroll*>(pEvent);
3489 #if GTK_CHECK_VERSION(3,0,0)
3490 //TODO: do something less feeble here
3491 if (pSEvent->direction == GDK_SCROLL_SMOOTH)
3492 return true;
3493 #endif
3495 static sal_uLong nLines = 0;
3496 if( ! nLines )
3498 char* pEnv = getenv( "SAL_WHEELLINES" );
3499 nLines = pEnv ? atoi( pEnv ) : 3;
3500 if( nLines > 10 )
3501 nLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
3504 bool bNeg = (pSEvent->direction == GDK_SCROLL_DOWN || pSEvent->direction == GDK_SCROLL_RIGHT );
3505 SalWheelMouseEvent aEvent;
3506 aEvent.mnTime = pSEvent->time;
3507 aEvent.mnX = (sal_uLong)pSEvent->x;
3508 aEvent.mnY = (sal_uLong)pSEvent->y;
3509 aEvent.mnDelta = bNeg ? -120 : 120;
3510 aEvent.mnNotchDelta = bNeg ? -1 : 1;
3511 aEvent.mnScrollLines = nLines;
3512 aEvent.mnCode = GetMouseModCode( pSEvent->state );
3513 aEvent.mbHorz = (pSEvent->direction == GDK_SCROLL_LEFT || pSEvent->direction == GDK_SCROLL_RIGHT);
3515 // --- RTL --- (mirror mouse pos)
3516 if( AllSettings::GetLayoutRTL() )
3517 aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX;
3519 pThis->CallCallback( SALEVENT_WHEELMOUSE, &aEvent );
3521 return true;
3524 #if GTK_CHECK_VERSION(3,14,0)
3525 void GtkSalFrame::gestureSwipe(GtkGestureSwipe* gesture, gdouble velocity_x, gdouble velocity_y, gpointer frame)
3527 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3529 SalSwipeEvent aEvent;
3530 aEvent.mnVelocityX = velocity_x;
3531 aEvent.mnVelocityY = velocity_y;
3533 gdouble x, y;
3534 GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture));
3535 //I feel I want the first point of the sequence, not the last point which
3536 //the docs say this gives, but for the moment assume we start and end
3537 //within the same vcl window
3538 gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y);
3539 aEvent.mnX = x;
3540 aEvent.mnY = y;
3542 pThis->CallCallback(SALEVENT_SWIPE, &aEvent);
3545 void GtkSalFrame::gestureLongPress(GtkGestureLongPress* gesture, gpointer frame)
3547 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3549 if(pThis)
3551 SalLongPressEvent aEvent;
3553 gdouble x, y;
3554 GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence(GTK_GESTURE_SINGLE(gesture));
3555 gtk_gesture_get_point(GTK_GESTURE(gesture), sequence, &x, &y);
3556 aEvent.mnX = x;
3557 aEvent.mnY = y;
3559 pThis->CallCallback(SALEVENT_LONGPRESS, &aEvent);
3563 #endif
3565 gboolean GtkSalFrame::signalMotion( GtkWidget*, GdkEventMotion* pEvent, gpointer frame )
3567 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3569 SalMouseEvent aEvent;
3570 aEvent.mnTime = pEvent->time;
3571 aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX;
3572 aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY;
3573 aEvent.mnCode = GetMouseModCode( pEvent->state );
3574 aEvent.mnButton = 0;
3576 // --- RTL --- (mirror mouse pos)
3577 if( AllSettings::GetLayoutRTL() )
3578 aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX;
3580 vcl::DeletionListener aDel( pThis );
3582 pThis->CallCallback( SALEVENT_MOUSEMOVE, &aEvent );
3584 if( ! aDel.isDeleted() )
3586 int frame_x = (int)(pEvent->x_root - pEvent->x);
3587 int frame_y = (int)(pEvent->y_root - pEvent->y);
3588 if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY )
3590 pThis->maGeometry.nX = frame_x;
3591 pThis->maGeometry.nY = frame_y;
3592 pThis->CallCallback( SALEVENT_MOVE, NULL );
3595 if( ! aDel.isDeleted() )
3597 // ask for the next hint
3598 gint x, y;
3599 GdkModifierType mask;
3600 gdk_window_get_pointer( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &x, &y, &mask );
3604 return true;
3607 gboolean GtkSalFrame::signalCrossing( GtkWidget*, GdkEventCrossing* pEvent, gpointer frame )
3609 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3610 SalMouseEvent aEvent;
3611 aEvent.mnTime = pEvent->time;
3612 aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX;
3613 aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY;
3614 aEvent.mnCode = GetMouseModCode( pEvent->state );
3615 aEvent.mnButton = 0;
3617 pThis->CallCallback( (pEvent->type == GDK_ENTER_NOTIFY) ? SALEVENT_MOUSEMOVE : SALEVENT_MOUSELEAVE, &aEvent );
3619 return true;
3622 #if GTK_CHECK_VERSION(3,0,0)
3624 cairo_t* GtkSalFrame::getCairoContext() const
3626 cairo_t* cr = SvpSalGraphics::createCairoContext(m_aFrame);
3627 assert(cr);
3628 return cr;
3631 void GtkSalFrame::damaged (const basegfx::B2IBox& rDamageRect)
3633 #if OSL_DEBUG_LEVEL > 1
3634 long long area = rDamageRect.getWidth() * rDamageRect.getHeight();
3635 if( area > 32 * 1024 )
3637 fprintf( stderr, "bitmap damaged %d %d (%dx%d) area %lld widget\n",
3638 (int) rDamageRect.getMinX(),
3639 (int) rDamageRect.getMinY(),
3640 (int) rDamageRect.getWidth(),
3641 (int) rDamageRect.getHeight(),
3642 area );
3644 #endif
3646 if (dumpframes)
3648 static int frame;
3649 OString tmp("/tmp/frame" + OString::number(frame++) + ".png");
3650 cairo_t* cr = getCairoContext();
3651 cairo_surface_write_to_png(cairo_get_target(cr), tmp.getStr());
3652 cairo_destroy(cr);
3655 gtk_widget_queue_draw_area(m_pWindow,
3656 rDamageRect.getMinX(),
3657 rDamageRect.getMinY(),
3658 rDamageRect.getWidth(),
3659 rDamageRect.getHeight());
3662 // blit our backing basebmp buffer to the target cairo context cr
3663 gboolean GtkSalFrame::signalDraw( GtkWidget*, cairo_t *cr, gpointer frame )
3665 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3667 cairo_save(cr);
3669 cairo_t* source = pThis->getCairoContext();
3670 cairo_surface_t *pSurface = cairo_get_target(source);
3672 cairo_set_operator( cr, CAIRO_OPERATOR_OVER );
3673 cairo_set_source_surface(cr, pSurface, 0, 0);
3674 cairo_paint(cr);
3676 cairo_destroy(source);
3678 cairo_restore(cr);
3680 cairo_surface_flush(cairo_get_target(cr));
3682 return false;
3685 void GtkSalFrame::sizeAllocated(GtkWidget*, GdkRectangle *pAllocation, gpointer frame)
3687 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3689 bool bSized = false;
3691 if( pThis->m_bFullscreen || (pThis->m_nStyle & (SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_PLUG)) == SAL_FRAME_STYLE_SIZEABLE )
3693 if( pAllocation->width != (int)pThis->maGeometry.nWidth || pAllocation->height != (int)pThis->maGeometry.nHeight )
3695 bSized = true;
3696 pThis->maGeometry.nWidth = pAllocation->width;
3697 pThis->maGeometry.nHeight = pAllocation->height;
3701 if( bSized )
3703 pThis->AllocateFrame();
3704 pThis->CallCallback( SALEVENT_RESIZE, nullptr );
3705 pThis->TriggerPaintEvent();
3709 gboolean GtkSalFrame::signalConfigure(GtkWidget*, GdkEventConfigure* pEvent, gpointer frame)
3711 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3712 pThis->m_bPaintsBlocked = false;
3714 bool bMoved = false;
3715 int x = pEvent->x, y = pEvent->y;
3717 /* HACK: during sizing/moving a toolbar pThis->maGeometry is actually
3718 * already exact; even worse: due to the asynchronicity of configure
3719 * events the borderwindow which would evaluate this event
3720 * would size/move based on wrong data if we would actually evaluate
3721 * this event. So let's swallow it.
3723 if( (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) &&
3724 GtkSalFrame::getDisplay()->GetCaptureFrame() == pThis )
3725 return false;
3727 /* #i31785# claims we cannot trust the x,y members of the event;
3728 * they are e.g. not set correctly on maximize/demaximize;
3729 * yet the gdkdisplay-x11.c code handling configure_events has
3730 * done this XTranslateCoordinates work since the day ~zero.
3732 if( x != pThis->maGeometry.nX || y != pThis->maGeometry.nY )
3734 bMoved = true;
3735 pThis->maGeometry.nX = x;
3736 pThis->maGeometry.nY = y;
3739 // update decoration hints
3740 if( ! (pThis->m_nStyle & SAL_FRAME_STYLE_PLUG) )
3742 GdkRectangle aRect;
3743 gdk_window_get_frame_extents( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &aRect );
3744 pThis->maGeometry.nTopDecoration = y - aRect.y;
3745 pThis->maGeometry.nBottomDecoration = aRect.y + aRect.height - y - pEvent->height;
3746 pThis->maGeometry.nLeftDecoration = x - aRect.x;
3747 pThis->maGeometry.nRightDecoration = aRect.x + aRect.width - x - pEvent->width;
3749 else
3751 pThis->maGeometry.nTopDecoration =
3752 pThis->maGeometry.nBottomDecoration =
3753 pThis->maGeometry.nLeftDecoration =
3754 pThis->maGeometry.nRightDecoration = 0;
3757 pThis->updateScreenNumber();
3759 if (bMoved)
3760 pThis->CallCallback(SALEVENT_MOVE, nullptr);
3762 return false;
3764 #else
3765 gboolean GtkSalFrame::signalExpose( GtkWidget*, GdkEventExpose* pEvent, gpointer frame )
3767 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3768 pThis->m_bPaintsBlocked = false;
3770 struct SalPaintEvent aEvent( pEvent->area.x, pEvent->area.y, pEvent->area.width, pEvent->area.height );
3772 pThis->CallCallback( SALEVENT_PAINT, &aEvent );
3774 return false;
3777 gboolean GtkSalFrame::signalConfigure( GtkWidget*, GdkEventConfigure* pEvent, gpointer frame )
3779 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3780 pThis->m_bPaintsBlocked = false;
3782 bool bMoved = false, bSized = false;
3783 int x = pEvent->x, y = pEvent->y;
3785 /* HACK: during sizing/moving a toolbar pThis->maGeometry is actually
3786 * already exact; even worse: due to the asynchronicity of configure
3787 * events the borderwindow which would evaluate this event
3788 * would size/move based on wrong data if we would actually evaluate
3789 * this event. So let's swallow it.
3791 if( (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) &&
3792 GtkSalFrame::getDisplay()->GetCaptureFrame() == pThis )
3793 return false;
3795 /* #i31785# claims we cannot trust the x,y members of the event;
3796 * they are e.g. not set correctly on maximize/demaximize;
3797 * yet the gdkdisplay-x11.c code handling configure_events has
3798 * done this XTranslateCoordinates work since the day ~zero.
3800 if( x != pThis->maGeometry.nX || y != pThis->maGeometry.nY )
3802 bMoved = true;
3803 pThis->maGeometry.nX = x;
3804 pThis->maGeometry.nY = y;
3806 /* #i86302#
3807 * for non sizeable windows we set the min and max hint for the window manager to
3808 * achieve correct sizing. However this is asynchronous and e.g. on Compiz
3809 * it sometimes happens that the window gets resized to another size (some default)
3810 * if we update the size here, subsequent setMinMaxSize will use this wrong size
3811 * - which is not good since the window manager will now size the window back to this
3812 * wrong size at some point.
3814 if( pThis->m_bFullscreen || (pThis->m_nStyle & (SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_PLUG)) == SAL_FRAME_STYLE_SIZEABLE )
3816 if( pEvent->width != (int)pThis->maGeometry.nWidth || pEvent->height != (int)pThis->maGeometry.nHeight )
3818 bSized = true;
3819 pThis->maGeometry.nWidth = pEvent->width;
3820 pThis->maGeometry.nHeight = pEvent->height;
3824 // update decoration hints
3825 if( ! (pThis->m_nStyle & SAL_FRAME_STYLE_PLUG) )
3827 GdkRectangle aRect;
3828 gdk_window_get_frame_extents( widget_get_window(GTK_WIDGET(pThis->m_pWindow)), &aRect );
3829 pThis->maGeometry.nTopDecoration = y - aRect.y;
3830 pThis->maGeometry.nBottomDecoration = aRect.y + aRect.height - y - pEvent->height;
3831 pThis->maGeometry.nLeftDecoration = x - aRect.x;
3832 pThis->maGeometry.nRightDecoration = aRect.x + aRect.width - x - pEvent->width;
3834 else
3836 pThis->maGeometry.nTopDecoration =
3837 pThis->maGeometry.nBottomDecoration =
3838 pThis->maGeometry.nLeftDecoration =
3839 pThis->maGeometry.nRightDecoration = 0;
3842 pThis->updateScreenNumber();
3843 if( bSized )
3844 pThis->AllocateFrame();
3846 if( bMoved && bSized )
3847 pThis->CallCallback( SALEVENT_MOVERESIZE, nullptr );
3848 else if( bMoved )
3849 pThis->CallCallback( SALEVENT_MOVE, nullptr );
3850 else if( bSized )
3851 pThis->CallCallback( SALEVENT_RESIZE, nullptr );
3853 if (bSized)
3854 pThis->TriggerPaintEvent();
3855 return false;
3858 #endif // GTK_CHECK_VERSION(3,0,0)
3860 void GtkSalFrame::TriggerPaintEvent()
3862 //Under gtk2 we can basically paint directly into the XWindow and on
3863 //additional "expose-event" events we can re-render the missing pieces
3865 //Under gtk3 we have to keep our own buffer up to date and flush it into
3866 //the given cairo context on "draw". So we emit a paint event on
3867 //opportune resize trigger events to initially fill our backbuffer and then
3868 //keep it up to date with our direct paints and tell gtk those regions
3869 //have changed and then blit them into the provided cairo context when
3870 //we get the "draw"
3872 //The other alternative was to always paint everything on "draw", but
3873 //that duplicates the amount of drawing and is hideously slow
3874 #if GTK_CHECK_VERSION(3,0,0)
3875 SAL_INFO("vcl.gtk3", "force painting" << 0 << "," << 0 << " " << maGeometry.nWidth << "x" << maGeometry.nHeight);
3876 SalPaintEvent aPaintEvt(0, 0, maGeometry.nWidth, maGeometry.nHeight, true);
3877 CallCallback(SALEVENT_PAINT, &aPaintEvt);
3878 gtk_widget_queue_draw(m_pWindow);
3879 #endif
3882 gboolean GtkSalFrame::signalFocus( GtkWidget*, GdkEventFocus* pEvent, gpointer frame )
3884 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3886 SalGenericInstance *pSalInstance =
3887 static_cast< SalGenericInstance* >(GetSalData()->m_pInstance);
3889 // check if printers have changed (analogous to salframe focus handler)
3890 pSalInstance->updatePrinterUpdate();
3892 if( !pEvent->in )
3894 pThis->m_nKeyModifiers = 0;
3895 pThis->m_bSendModChangeOnRelease = false;
3898 if( pThis->m_pIMHandler )
3899 pThis->m_pIMHandler->focusChanged( pEvent->in );
3901 // ask for changed printers like generic implementation
3902 if( pEvent->in && pSalInstance->isPrinterInit() )
3903 pSalInstance->updatePrinterUpdate();
3905 // FIXME: find out who the hell steals the focus from our frame
3906 // while we have the pointer grabbed, this should not come from
3907 // the window manager. Is this an event that was still queued ?
3908 // The focus does not seem to get set inside our process
3910 // in the meantime do not propagate focus get/lose if floats are open
3911 if( m_nFloats == 0 )
3912 pThis->CallCallback( pEvent->in ? SALEVENT_GETFOCUS : SALEVENT_LOSEFOCUS, NULL );
3914 return false;
3917 #if !GTK_CHECK_VERSION(3,8,0)
3918 static OString getDisplayString()
3920 int nParams = rtl_getAppCommandArgCount();
3921 OUString aParam;
3922 for( int i = 0; i < nParams; i++ )
3924 rtl_getAppCommandArg( i, &aParam.pData );
3925 if( i < nParams-1 && (aParam == "-display" || aParam == "--display" ) )
3927 rtl_getAppCommandArg( i+1, &aParam.pData );
3928 return OUStringToOString( aParam, osl_getThreadTextEncoding() );
3931 return OString();
3933 #endif
3935 gboolean GtkSalFrame::signalMap( GtkWidget *pWidget, GdkEvent*, gpointer frame )
3937 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3939 #if !GTK_CHECK_VERSION(3,8,0)
3940 //Spawn off a helper program that will attempt to set this fullscreen
3941 //window to span all displays.
3942 if (pThis->m_bFullscreen && pThis->m_bSpanMonitorsWhenFullscreen)
3944 GdkWindow* gdkwin = widget_get_window(pThis->m_pWindow);
3945 if (gdkwin)
3947 OUString sProgramURL( "$BRAND_BASE_DIR/" LIBO_LIBEXEC_FOLDER "/xid-fullscreen-on-all-monitors");
3948 rtl::Bootstrap::expandMacros(sProgramURL);
3949 OUString sProgram;
3950 if (osl::FileBase::getSystemPathFromFileURL(sProgramURL, sProgram) == osl::File::E_None)
3952 OString sFinalProgram(OUStringToOString(sProgram, osl_getThreadTextEncoding())
3953 + " " + OString::number((int)GDK_WINDOW_XID(gdkwin)));
3954 OString sDisplay(getDisplayString());
3955 if (!sDisplay.isEmpty())
3957 sFinalProgram += "--display " + sDisplay;
3959 int returnValue = system(sFinalProgram.getStr());
3960 (void)returnValue;
3964 #endif
3966 bool bSetFocus = pThis->m_bSetFocusOnMap;
3967 pThis->m_bSetFocusOnMap = false;
3969 #if !GTK_CHECK_VERSION(3,0,0)
3970 if( bSetFocus )
3972 GetGenericData()->ErrorTrapPush();
3973 XSetInputFocus( GtkSalFrame::getDisplay()->GetDisplay(),
3974 widget_get_xid(pWidget),
3975 RevertToParent, CurrentTime );
3976 XSync( GtkSalFrame::getDisplay()->GetDisplay(), False );
3977 GetGenericData()->ErrorTrapPop();
3979 #else
3980 (void)pWidget; (void)bSetFocus;
3981 //FIXME: no set input focus ...
3982 #endif
3984 pThis->CallCallback( SALEVENT_RESIZE, NULL );
3985 pThis->TriggerPaintEvent();
3987 return false;
3990 gboolean GtkSalFrame::signalUnmap( GtkWidget*, GdkEvent*, gpointer frame )
3992 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
3994 pThis->CallCallback( SALEVENT_RESIZE, NULL );
3996 return false;
3999 gboolean GtkSalFrame::signalKey( GtkWidget*, GdkEventKey* pEvent, gpointer frame )
4001 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
4003 vcl::DeletionListener aDel( pThis );
4005 if( pThis->m_pIMHandler )
4007 if( pThis->m_pIMHandler->handleKeyEvent( pEvent ) )
4008 return true;
4011 // handle modifiers
4012 if( pEvent->keyval == GDK_Shift_L || pEvent->keyval == GDK_Shift_R ||
4013 pEvent->keyval == GDK_Control_L || pEvent->keyval == GDK_Control_R ||
4014 pEvent->keyval == GDK_Alt_L || pEvent->keyval == GDK_Alt_R ||
4015 pEvent->keyval == GDK_Meta_L || pEvent->keyval == GDK_Meta_R ||
4016 pEvent->keyval == GDK_Super_L || pEvent->keyval == GDK_Super_R )
4018 SalKeyModEvent aModEvt;
4020 sal_uInt16 nModCode = GetKeyModCode( pEvent->state );
4022 aModEvt.mnModKeyCode = 0; // emit no MODKEYCHANGE events
4023 if( pEvent->type == GDK_KEY_PRESS && !pThis->m_nKeyModifiers )
4024 pThis->m_bSendModChangeOnRelease = true;
4026 else if( pEvent->type == GDK_KEY_RELEASE &&
4027 pThis->m_bSendModChangeOnRelease )
4029 aModEvt.mnModKeyCode = pThis->m_nKeyModifiers;
4030 pThis->m_nKeyModifiers = 0;
4033 sal_uInt16 nExtModMask = 0;
4034 sal_uInt16 nModMask = 0;
4035 // pressing just the ctrl key leads to a keysym of XK_Control but
4036 // the event state does not contain ControlMask. In the release
4037 // event its the other way round: it does contain the Control mask.
4038 // The modifier mode therefore has to be adapted manually.
4039 switch( pEvent->keyval )
4041 case GDK_Control_L:
4042 nExtModMask = MODKEY_LMOD1;
4043 nModMask = KEY_MOD1;
4044 break;
4045 case GDK_Control_R:
4046 nExtModMask = MODKEY_RMOD1;
4047 nModMask = KEY_MOD1;
4048 break;
4049 case GDK_Alt_L:
4050 nExtModMask = MODKEY_LMOD2;
4051 nModMask = KEY_MOD2;
4052 break;
4053 case GDK_Alt_R:
4054 nExtModMask = MODKEY_RMOD2;
4055 nModMask = KEY_MOD2;
4056 break;
4057 case GDK_Shift_L:
4058 nExtModMask = MODKEY_LSHIFT;
4059 nModMask = KEY_SHIFT;
4060 break;
4061 case GDK_Shift_R:
4062 nExtModMask = MODKEY_RSHIFT;
4063 nModMask = KEY_SHIFT;
4064 break;
4065 // Map Meta/Super to MOD3 modifier on all Unix systems
4066 // except Mac OS X
4067 case GDK_Meta_L:
4068 case GDK_Super_L:
4069 nExtModMask = MODKEY_LMOD3;
4070 nModMask = KEY_MOD3;
4071 break;
4072 case GDK_Meta_R:
4073 case GDK_Super_R:
4074 nExtModMask = MODKEY_RMOD3;
4075 nModMask = KEY_MOD3;
4076 break;
4078 if( pEvent->type == GDK_KEY_RELEASE )
4080 nModCode &= ~nModMask;
4081 pThis->m_nKeyModifiers &= ~nExtModMask;
4083 else
4085 nModCode |= nModMask;
4086 pThis->m_nKeyModifiers |= nExtModMask;
4089 aModEvt.mnCode = nModCode;
4090 aModEvt.mnTime = pEvent->time;
4092 pThis->CallCallback( SALEVENT_KEYMODCHANGE, &aModEvt );
4095 else
4097 pThis->doKeyCallback( pEvent->state,
4098 pEvent->keyval,
4099 pEvent->hardware_keycode,
4100 pEvent->group,
4101 pEvent->time,
4102 sal_Unicode(gdk_keyval_to_unicode( pEvent->keyval )),
4103 (pEvent->type == GDK_KEY_PRESS),
4104 false );
4105 if( ! aDel.isDeleted() )
4106 pThis->m_bSendModChangeOnRelease = false;
4109 if( !aDel.isDeleted() && pThis->m_pIMHandler )
4110 pThis->m_pIMHandler->updateIMSpotLocation();
4112 return true;
4115 gboolean GtkSalFrame::signalDelete( GtkWidget*, GdkEvent*, gpointer frame )
4117 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
4119 #if GTK_CHECK_VERSION(3,0,0)
4120 //If we went into the backdrop we disabled the toplevel window, if we
4121 //receive a delete here, re-enable so we can process it
4122 bool bBackDrop = (gtk_widget_get_state_flags(GTK_WIDGET(pThis->m_pWindow)) & GTK_STATE_FLAG_BACKDROP);
4123 if (bBackDrop)
4124 pThis->GetWindow()->Enable();
4125 #endif
4127 pThis->CallCallback( SALEVENT_CLOSE, NULL );
4129 return true;
4132 void GtkSalFrame::signalStyleSet( GtkWidget*, GtkStyle* pPrevious, gpointer frame )
4134 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
4136 // every frame gets an initial style set on creation
4137 // do not post these as the whole application tends to
4138 // redraw itself to adjust to the new style
4139 // where there IS no new style resulting in tremendous unnecessary flickering
4140 if( pPrevious != NULL )
4142 // signalStyleSet does NOT usually have the gdk lock
4143 // so post user event to safely dispatch the SALEVENT_SETTINGSCHANGED
4144 // note: settings changed for multiple frames is avoided in winproc.cxx ImplHandleSettings
4145 GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_SETTINGSCHANGED );
4146 GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_FONTCHANGED );
4149 #if !GTK_CHECK_VERSION(3,0,0)
4150 /* #i64117# gtk sets a nice background pixmap
4151 * but we actually don't really want that, so save
4152 * some time on the Xserver as well as prevent
4153 * some paint issues
4155 GdkWindow* pWin = widget_get_window(GTK_WIDGET(pThis->getWindow()));
4156 if( pWin )
4158 ::Window aWin = GDK_WINDOW_XWINDOW(pWin);
4159 if( aWin != None )
4160 XSetWindowBackgroundPixmap( GtkSalFrame::getDisplay()->GetDisplay(),
4161 aWin,
4162 pThis->m_hBackgroundPixmap );
4164 if( ! pThis->m_pParent )
4166 // signalize theme changed for NWF caches
4167 // FIXME: should be called only once for a style change
4168 GtkSalGraphics::bThemeChanged = true;
4170 #endif
4173 gboolean GtkSalFrame::signalWindowState( GtkWidget*, GdkEvent* pEvent, gpointer frame )
4175 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
4176 if( (pThis->m_nState & GDK_WINDOW_STATE_ICONIFIED) != (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_ICONIFIED ) )
4178 GtkSalFrame::getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_RESIZE );
4179 pThis->TriggerPaintEvent();
4182 if( (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_MAXIMIZED) &&
4183 ! (pThis->m_nState & GDK_WINDOW_STATE_MAXIMIZED) )
4185 pThis->m_aRestorePosSize =
4186 Rectangle( Point( pThis->maGeometry.nX, pThis->maGeometry.nY ),
4187 Size( pThis->maGeometry.nWidth, pThis->maGeometry.nHeight ) );
4189 pThis->m_nState = pEvent->window_state.new_window_state;
4191 #if OSL_DEBUG_LEVEL > 1
4192 if( (pEvent->window_state.changed_mask & GDK_WINDOW_STATE_FULLSCREEN) )
4194 fprintf( stderr, "window %p %s full screen state\n",
4195 pThis,
4196 (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_FULLSCREEN) ? "enters" : "leaves");
4198 #endif
4200 return false;
4203 gboolean GtkSalFrame::signalVisibility( GtkWidget*, GdkEventVisibility* pEvent, gpointer frame )
4205 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
4206 pThis->m_nVisibility = pEvent->state;
4207 return true;
4210 void GtkSalFrame::signalDestroy( GtkWidget* pObj, gpointer frame )
4212 GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
4213 if( pObj == pThis->m_pWindow )
4215 pThis->m_pFixedContainer = NULL;
4216 pThis->m_pWindow = NULL;
4217 pThis->InvalidateGraphics();
4221 // GtkSalFrame::IMHandler
4223 GtkSalFrame::IMHandler::IMHandler( GtkSalFrame* pFrame )
4224 : m_pFrame(pFrame),
4225 m_nPrevKeyPresses( 0 ),
4226 m_pIMContext( NULL ),
4227 m_bFocused( true ),
4228 m_bPreeditJustChanged( false )
4230 m_aInputEvent.mpTextAttr = NULL;
4231 createIMContext();
4234 GtkSalFrame::IMHandler::~IMHandler()
4236 // cancel an eventual event posted to begin preedit again
4237 GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT );
4238 deleteIMContext();
4241 void GtkSalFrame::IMHandler::createIMContext()
4243 if( ! m_pIMContext )
4245 m_pIMContext = gtk_im_multicontext_new ();
4246 g_signal_connect( m_pIMContext, "commit",
4247 G_CALLBACK (signalIMCommit), this );
4248 g_signal_connect( m_pIMContext, "preedit_changed",
4249 G_CALLBACK (signalIMPreeditChanged), this );
4250 g_signal_connect( m_pIMContext, "retrieve_surrounding",
4251 G_CALLBACK (signalIMRetrieveSurrounding), this );
4252 g_signal_connect( m_pIMContext, "delete_surrounding",
4253 G_CALLBACK (signalIMDeleteSurrounding), this );
4254 g_signal_connect( m_pIMContext, "preedit_start",
4255 G_CALLBACK (signalIMPreeditStart), this );
4256 g_signal_connect( m_pIMContext, "preedit_end",
4257 G_CALLBACK (signalIMPreeditEnd), this );
4259 GetGenericData()->ErrorTrapPush();
4260 gtk_im_context_set_client_window( m_pIMContext, widget_get_window(GTK_WIDGET(m_pFrame->m_pWindow)) );
4261 gtk_im_context_focus_in( m_pIMContext );
4262 GetGenericData()->ErrorTrapPop();
4263 m_bFocused = true;
4267 void GtkSalFrame::IMHandler::deleteIMContext()
4269 if( m_pIMContext )
4271 // first give IC a chance to deinitialize
4272 GetGenericData()->ErrorTrapPush();
4273 gtk_im_context_set_client_window( m_pIMContext, NULL );
4274 GetGenericData()->ErrorTrapPop();
4275 // destroy old IC
4276 g_object_unref( m_pIMContext );
4277 m_pIMContext = NULL;
4281 void GtkSalFrame::IMHandler::doCallEndExtTextInput()
4283 m_aInputEvent.mpTextAttr = NULL;
4284 m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL );
4287 void GtkSalFrame::IMHandler::updateIMSpotLocation()
4289 SalExtTextInputPosEvent aPosEvent;
4290 m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent );
4291 GdkRectangle aArea;
4292 aArea.x = aPosEvent.mnX;
4293 aArea.y = aPosEvent.mnY;
4294 aArea.width = aPosEvent.mnWidth;
4295 aArea.height = aPosEvent.mnHeight;
4296 GetGenericData()->ErrorTrapPush();
4297 gtk_im_context_set_cursor_location( m_pIMContext, &aArea );
4298 GetGenericData()->ErrorTrapPop();
4301 void GtkSalFrame::IMHandler::sendEmptyCommit()
4303 vcl::DeletionListener aDel( m_pFrame );
4305 SalExtTextInputEvent aEmptyEv;
4306 aEmptyEv.mnTime = 0;
4307 aEmptyEv.mpTextAttr = 0;
4308 aEmptyEv.maText.clear();
4309 aEmptyEv.mnCursorPos = 0;
4310 aEmptyEv.mnCursorFlags = 0;
4311 aEmptyEv.mbOnlyCursor = False;
4312 m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&aEmptyEv );
4313 if( ! aDel.isDeleted() )
4314 m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL );
4317 void GtkSalFrame::IMHandler::endExtTextInput( sal_uInt16 /*nFlags*/ )
4319 gtk_im_context_reset ( m_pIMContext );
4321 if( m_aInputEvent.mpTextAttr )
4323 vcl::DeletionListener aDel( m_pFrame );
4324 // delete preedit in sal (commit an empty string)
4325 sendEmptyCommit();
4326 if( ! aDel.isDeleted() )
4328 // mark previous preedit state again (will e.g. be sent at focus gain)
4329 m_aInputEvent.mpTextAttr = &m_aInputFlags[0];
4330 if( m_bFocused )
4332 // begin preedit again
4333 GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT );
4339 void GtkSalFrame::IMHandler::focusChanged( bool bFocusIn )
4341 m_bFocused = bFocusIn;
4342 if( bFocusIn )
4344 GetGenericData()->ErrorTrapPush();
4345 gtk_im_context_focus_in( m_pIMContext );
4346 GetGenericData()->ErrorTrapPop();
4347 if( m_aInputEvent.mpTextAttr )
4349 sendEmptyCommit();
4350 // begin preedit again
4351 GtkSalFrame::getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT );
4354 else
4356 GetGenericData()->ErrorTrapPush();
4357 gtk_im_context_focus_out( m_pIMContext );
4358 GetGenericData()->ErrorTrapPop();
4359 // cancel an eventual event posted to begin preedit again
4360 GtkSalFrame::getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT );
4364 bool GtkSalFrame::IMHandler::handleKeyEvent( GdkEventKey* pEvent )
4366 vcl::DeletionListener aDel( m_pFrame );
4368 if( pEvent->type == GDK_KEY_PRESS )
4370 // Add this key press event to the list of previous key presses
4371 // to which we compare key release events. If a later key release
4372 // event has a matching key press event in this list, we swallow
4373 // the key release because some GTK Input Methods don't swallow it
4374 // for us.
4375 m_aPrevKeyPresses.push_back( PreviousKeyPress(pEvent) );
4376 m_nPrevKeyPresses++;
4378 // Also pop off the earliest key press event if there are more than 10
4379 // already.
4380 while (m_nPrevKeyPresses > 10)
4382 m_aPrevKeyPresses.pop_front();
4383 m_nPrevKeyPresses--;
4386 GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) );
4388 // #i51353# update spot location on every key input since we cannot
4389 // know which key may activate a preedit choice window
4390 updateIMSpotLocation();
4391 if( aDel.isDeleted() )
4392 return true;
4394 gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent );
4395 g_object_unref( pRef );
4397 if( aDel.isDeleted() )
4398 return true;
4400 m_bPreeditJustChanged = false;
4402 if( bResult )
4403 return true;
4404 else
4406 DBG_ASSERT( m_nPrevKeyPresses > 0, "key press has vanished !" );
4407 if( ! m_aPrevKeyPresses.empty() ) // sanity check
4409 // event was not swallowed, do not filter a following
4410 // key release event
4411 // note: this relies on gtk_im_context_filter_keypress
4412 // returning without calling a handler (in the "not swallowed"
4413 // case ) which might change the previous key press list so
4414 // we would pop the wrong event here
4415 m_aPrevKeyPresses.pop_back();
4416 m_nPrevKeyPresses--;
4421 // Determine if we got an earlier key press event corresponding to this key release
4422 if (pEvent->type == GDK_KEY_RELEASE)
4424 GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) );
4425 gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent );
4426 g_object_unref( pRef );
4428 if( aDel.isDeleted() )
4429 return true;
4431 m_bPreeditJustChanged = false;
4433 std::list<PreviousKeyPress>::iterator iter = m_aPrevKeyPresses.begin();
4434 std::list<PreviousKeyPress>::iterator iter_end = m_aPrevKeyPresses.end();
4435 while (iter != iter_end)
4437 // If we found a corresponding previous key press event, swallow the release
4438 // and remove the earlier key press from our list
4439 if (*iter == pEvent)
4441 m_aPrevKeyPresses.erase(iter);
4442 m_nPrevKeyPresses--;
4443 return true;
4445 ++iter;
4448 if( bResult )
4449 return true;
4452 return false;
4455 /* FIXME:
4456 * #122282# still more hacking: some IMEs never start a preedit but simply commit
4457 * in this case we cannot commit a single character. Workaround: do not do the
4458 * single key hack for enter or space if the unicode committed does not match
4461 static bool checkSingleKeyCommitHack( guint keyval, sal_Unicode cCode )
4463 bool bRet = true;
4464 switch( keyval )
4466 case GDK_KP_Enter:
4467 case GDK_Return:
4468 if( cCode != '\n' && cCode != '\r' )
4469 bRet = false;
4470 break;
4471 case GDK_space:
4472 case GDK_KP_Space:
4473 if( cCode != ' ' )
4474 bRet = false;
4475 break;
4476 default:
4477 break;
4479 return bRet;
4482 void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext* pContext, gchar* pText, gpointer im_handler )
4484 GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler);
4486 SolarMutexGuard aGuard;
4487 vcl::DeletionListener aDel( pThis->m_pFrame );
4489 const bool bWasPreedit =
4490 (pThis->m_aInputEvent.mpTextAttr != 0) ||
4491 pThis->m_bPreeditJustChanged;
4493 pThis->m_aInputEvent.mnTime = 0;
4494 pThis->m_aInputEvent.mpTextAttr = 0;
4495 pThis->m_aInputEvent.maText = OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 );
4496 pThis->m_aInputEvent.mnCursorPos = pThis->m_aInputEvent.maText.getLength();
4497 pThis->m_aInputEvent.mnCursorFlags = 0;
4498 pThis->m_aInputEvent.mbOnlyCursor = False;
4500 pThis->m_aInputFlags.clear();
4502 /* necessary HACK: all keyboard input comes in here as soon as a IMContext is set
4503 * which is logical and consequent. But since even simple input like
4504 * <space> comes through the commit signal instead of signalKey
4505 * and all kinds of windows only implement KeyInput (e.g. PushButtons,
4506 * RadioButtons and a lot of other Controls), will send a single
4507 * KeyInput/KeyUp sequence instead of an ExtText event if there
4508 * never was a preedit and the text is only one character.
4510 * In this case there the last ExtText event must have been
4511 * SALEVENT_ENDEXTTEXTINPUT, either because of a regular commit
4512 * or because there never was a preedit.
4514 bool bSingleCommit = false;
4515 if( ! bWasPreedit
4516 && pThis->m_aInputEvent.maText.getLength() == 1
4517 && ! pThis->m_aPrevKeyPresses.empty()
4520 const PreviousKeyPress& rKP = pThis->m_aPrevKeyPresses.back();
4521 sal_Unicode aOrigCode = pThis->m_aInputEvent.maText[0];
4523 if( checkSingleKeyCommitHack( rKP.keyval, aOrigCode ) )
4525 pThis->m_pFrame->doKeyCallback( rKP.state, rKP.keyval, rKP.hardware_keycode, rKP.group, rKP.time, aOrigCode, true, true );
4526 bSingleCommit = true;
4529 if( ! bSingleCommit )
4531 pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent);
4532 if( ! aDel.isDeleted() )
4533 pThis->doCallEndExtTextInput();
4535 if( ! aDel.isDeleted() )
4537 // reset input event
4538 pThis->m_aInputEvent.maText.clear();
4539 pThis->m_aInputEvent.mnCursorPos = 0;
4540 pThis->updateIMSpotLocation();
4543 #ifdef SOLARIS
4544 // #i51356# workaround a solaris IIIMP bug
4545 // in case of partial commits the preedit changed signal
4546 // and commit signal come in wrong order
4547 if( ! aDel.isDeleted() )
4548 signalIMPreeditChanged( pContext, im_handler );
4549 #else
4550 (void) pContext;
4551 #endif
4554 void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext*, gpointer im_handler )
4556 GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler);
4558 char* pText = NULL;
4559 PangoAttrList* pAttrs = NULL;
4560 gint nCursorPos = 0;
4562 gtk_im_context_get_preedit_string( pThis->m_pIMContext,
4563 &pText,
4564 &pAttrs,
4565 &nCursorPos );
4566 if( pText && ! *pText ) // empty string
4568 // change from nothing to nothing -> do not start preedit
4569 // e.g. this will activate input into a calc cell without
4570 // user input
4571 if( pThis->m_aInputEvent.maText.getLength() == 0 )
4573 g_free( pText );
4574 pango_attr_list_unref( pAttrs );
4575 return;
4579 pThis->m_bPreeditJustChanged = true;
4581 bool bEndPreedit = (!pText || !*pText) && pThis->m_aInputEvent.mpTextAttr != NULL;
4582 pThis->m_aInputEvent.mnTime = 0;
4583 pThis->m_aInputEvent.maText = pText ? OUString( pText, strlen(pText), RTL_TEXTENCODING_UTF8 ) : OUString();
4584 pThis->m_aInputEvent.mnCursorPos = nCursorPos;
4585 pThis->m_aInputEvent.mnCursorFlags = 0;
4586 pThis->m_aInputEvent.mbOnlyCursor = False;
4588 pThis->m_aInputFlags = std::vector<sal_uInt16>( std::max( 1, (int)pThis->m_aInputEvent.maText.getLength() ), 0 );
4590 PangoAttrIterator *iter = pango_attr_list_get_iterator(pAttrs);
4593 GSList *attr_list = NULL;
4594 GSList *tmp_list = NULL;
4595 gint start, end;
4596 guint sal_attr = 0;
4598 pango_attr_iterator_range (iter, &start, &end);
4599 if (end == G_MAXINT)
4600 end = pText ? strlen (pText) : 0;
4601 if (end == start)
4602 continue;
4604 start = g_utf8_pointer_to_offset (pText, pText + start);
4605 end = g_utf8_pointer_to_offset (pText, pText + end);
4607 tmp_list = attr_list = pango_attr_iterator_get_attrs (iter);
4608 while (tmp_list)
4610 PangoAttribute *pango_attr = static_cast<PangoAttribute *>(tmp_list->data);
4612 switch (pango_attr->klass->type)
4614 case PANGO_ATTR_BACKGROUND:
4615 sal_attr |= (EXTTEXTINPUT_ATTR_HIGHLIGHT | EXTTEXTINPUT_CURSOR_INVISIBLE);
4616 break;
4617 case PANGO_ATTR_UNDERLINE:
4618 sal_attr |= EXTTEXTINPUT_ATTR_UNDERLINE;
4619 break;
4620 case PANGO_ATTR_STRIKETHROUGH:
4621 sal_attr |= EXTTEXTINPUT_ATTR_REDTEXT;
4622 break;
4623 default:
4624 break;
4626 pango_attribute_destroy (pango_attr);
4627 tmp_list = tmp_list->next;
4629 if (sal_attr == 0)
4630 sal_attr |= EXTTEXTINPUT_ATTR_UNDERLINE;
4631 g_slist_free (attr_list);
4633 // Set the sal attributes on our text
4634 for (int i = start; i < end; ++i)
4636 SAL_WARN_IF(i >= static_cast<int>(pThis->m_aInputFlags.size()),
4637 "vcl.gtk", "pango attrib out of range. Broken range: "
4638 << start << "," << end << " Legal range: 0,"
4639 << pThis->m_aInputFlags.size());
4640 if (i >= static_cast<int>(pThis->m_aInputFlags.size()))
4641 continue;
4642 pThis->m_aInputFlags[i] |= sal_attr;
4644 } while (pango_attr_iterator_next (iter));
4645 pango_attr_iterator_destroy(iter);
4647 pThis->m_aInputEvent.mpTextAttr = &pThis->m_aInputFlags[0];
4649 g_free( pText );
4650 pango_attr_list_unref( pAttrs );
4652 SolarMutexGuard aGuard;
4653 vcl::DeletionListener aDel( pThis->m_pFrame );
4655 pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent);
4656 if( bEndPreedit && ! aDel.isDeleted() )
4657 pThis->doCallEndExtTextInput();
4658 if( ! aDel.isDeleted() )
4659 pThis->updateIMSpotLocation();
4662 void GtkSalFrame::IMHandler::signalIMPreeditStart( GtkIMContext*, gpointer /*im_handler*/ )
4666 void GtkSalFrame::IMHandler::signalIMPreeditEnd( GtkIMContext*, gpointer im_handler )
4668 GtkSalFrame::IMHandler* pThis = static_cast<GtkSalFrame::IMHandler*>(im_handler);
4670 pThis->m_bPreeditJustChanged = true;
4672 SolarMutexGuard aGuard;
4673 vcl::DeletionListener aDel( pThis->m_pFrame );
4674 pThis->doCallEndExtTextInput();
4675 if( ! aDel.isDeleted() )
4676 pThis->updateIMSpotLocation();
4679 uno::Reference<accessibility::XAccessibleEditableText>
4680 FindFocus(uno::Reference< accessibility::XAccessibleContext > xContext)
4682 if (!xContext.is())
4683 uno::Reference< accessibility::XAccessibleEditableText >();
4685 uno::Reference<accessibility::XAccessibleStateSet> xState = xContext->getAccessibleStateSet();
4686 if (xState.is())
4688 if (xState->contains(accessibility::AccessibleStateType::FOCUSED))
4689 return uno::Reference<accessibility::XAccessibleEditableText>(xContext, uno::UNO_QUERY);
4692 for (sal_Int32 i = 0; i < xContext->getAccessibleChildCount(); ++i)
4694 uno::Reference< accessibility::XAccessible > xChild = xContext->getAccessibleChild(i);
4695 if (!xChild.is())
4696 continue;
4697 uno::Reference< accessibility::XAccessibleContext > xChildContext = xChild->getAccessibleContext();
4698 if (!xChildContext.is())
4699 continue;
4700 uno::Reference< accessibility::XAccessibleEditableText > xText = FindFocus(xChildContext);
4701 if (xText.is())
4702 return xText;
4704 return uno::Reference< accessibility::XAccessibleEditableText >();
4707 static uno::Reference<accessibility::XAccessibleEditableText> lcl_GetxText(vcl::Window *pFocusWin)
4709 uno::Reference<accessibility::XAccessibleEditableText> xText;
4712 uno::Reference< accessibility::XAccessible > xAccessible( pFocusWin->GetAccessible( true ) );
4713 if (xAccessible.is())
4714 xText = FindFocus(xAccessible->getAccessibleContext());
4716 catch(const uno::Exception& e)
4718 SAL_WARN( "vcl.gtk", "Exception in getting input method surrounding text: " << e.Message);
4720 return xText;
4723 gboolean GtkSalFrame::IMHandler::signalIMRetrieveSurrounding( GtkIMContext* pContext, gpointer /*im_handler*/ )
4725 vcl::Window *pFocusWin = Application::GetFocusWindow();
4726 if (!pFocusWin)
4727 return true;
4729 uno::Reference<accessibility::XAccessibleEditableText> xText = lcl_GetxText(pFocusWin);
4730 if (xText.is())
4732 sal_Int32 nPosition = xText->getCaretPosition();
4733 OUString sAllText = xText->getText();
4734 OString sUTF = OUStringToOString(sAllText, RTL_TEXTENCODING_UTF8);
4735 OUString sCursorText(sAllText.copy(0, nPosition));
4736 gtk_im_context_set_surrounding(pContext, sUTF.getStr(), sUTF.getLength(),
4737 OUStringToOString(sCursorText, RTL_TEXTENCODING_UTF8).getLength());
4738 return true;
4741 return false;
4744 gboolean GtkSalFrame::IMHandler::signalIMDeleteSurrounding( GtkIMContext*, gint offset, gint nchars,
4745 gpointer /*im_handler*/ )
4747 vcl::Window *pFocusWin = Application::GetFocusWindow();
4748 if (!pFocusWin)
4749 return true;
4751 uno::Reference<accessibility::XAccessibleEditableText> xText = lcl_GetxText(pFocusWin);
4752 if (xText.is())
4754 sal_Int32 nPosition = xText->getCaretPosition();
4755 // #i111768# range checking
4756 sal_Int32 nDeletePos = nPosition + offset;
4757 sal_Int32 nDeleteEnd = nDeletePos + nchars;
4758 if (nDeletePos < 0)
4759 nDeletePos = 0;
4760 if (nDeleteEnd < 0)
4761 nDeleteEnd = 0;
4762 if (nDeleteEnd > xText->getCharacterCount())
4763 nDeleteEnd = xText->getCharacterCount();
4765 xText->deleteText(nDeletePos, nDeleteEnd);
4766 //tdf91641 adjust cursor if deleted chars shift it forward (normal case)
4767 if (nDeletePos < nPosition)
4769 if (nDeleteEnd <= nPosition)
4770 nPosition = nPosition - (nDeleteEnd - nDeletePos);
4771 else
4772 nPosition = nDeletePos;
4774 if (xText->getCharacterCount() >= nPosition)
4775 xText->setCaretPosition( nPosition );
4777 return true;
4780 return false;
4783 Size GtkSalDisplay::GetScreenSize( int nDisplayScreen )
4785 Rectangle aRect = m_pSys->GetDisplayScreenPosSizePixel( nDisplayScreen );
4786 return Size( aRect.GetWidth(), aRect.GetHeight() );
4789 Window GtkSalFrame::GetX11Window()
4791 return widget_get_xid(m_pWindow);
4794 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */