bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / osx / salframeview.mm
blob0ccdb048d1906e745551bbb71db4b729659f7b31
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
20 #include <sal/config.h>
22 #include <sal/macros.h>
23 #include <tools/helpers.hxx>
24 #include <tools/long.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/inputctx.hxx>
27 #include <vcl/settings.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/window.hxx>
30 #include <vcl/commandevent.hxx>
32 #include <osx/a11yfactory.h>
33 #include <osx/salframe.h>
34 #include <osx/salframeview.h>
35 #include <osx/salinst.h>
36 #include <quartz/salgdi.h>
37 #include <quartz/utils.h>
39 #if HAVE_FEATURE_SKIA
40 #include <vcl/skia/SkiaHelper.hxx>
41 #endif
43 #define WHEEL_EVENT_FACTOR 1.5
45 static sal_uInt16 ImplGetModifierMask( unsigned int nMask )
47     sal_uInt16 nRet = 0;
48     if( (nMask & NSEventModifierFlagShift) != 0 )
49         nRet |= KEY_SHIFT;
50     if( (nMask & NSEventModifierFlagControl) != 0 )
51         nRet |= KEY_MOD3;
52     if( (nMask & NSEventModifierFlagOption) != 0 )
53         nRet |= KEY_MOD2;
54     if( (nMask & NSEventModifierFlagCommand) != 0 )
55         nRet |= KEY_MOD1;
56     return nRet;
59 static sal_uInt16 ImplMapCharCode( sal_Unicode aCode )
61     static sal_uInt16 aKeyCodeMap[ 128 ] =
62     {
63                     0,                0,                0,                0,                0,                0,                0,                0,
64         KEY_BACKSPACE,          KEY_TAB,       KEY_RETURN,                0,                0,       KEY_RETURN,                0,                0,
65                     0,                0,                0,                0,                0,                0,                0,                0,
66                     0,          KEY_TAB,                0,       KEY_ESCAPE,                0,                0,                0,                0,
67             KEY_SPACE,                0,                0,                0,                0,                0,                0,                0,
68                     0,                0,     KEY_MULTIPLY,          KEY_ADD,        KEY_COMMA,     KEY_SUBTRACT,        KEY_POINT,       KEY_DIVIDE,
69                 KEY_0,            KEY_1,            KEY_2,            KEY_3,            KEY_4,            KEY_5,            KEY_6,            KEY_7,
70                 KEY_8,            KEY_9,                0,                0,         KEY_LESS,        KEY_EQUAL,      KEY_GREATER,                0,
71                     0,            KEY_A,            KEY_B,            KEY_C,            KEY_D,            KEY_E,            KEY_F,            KEY_G,
72                 KEY_H,            KEY_I,            KEY_J,            KEY_K,            KEY_L,            KEY_M,            KEY_N,            KEY_O,
73                 KEY_P,            KEY_Q,            KEY_R,            KEY_S,            KEY_T,            KEY_U,            KEY_V,            KEY_W,
74                 KEY_X,            KEY_Y,            KEY_Z,                0,                0,                0,                0,                0,
75         KEY_QUOTELEFT,            KEY_A,            KEY_B,            KEY_C,            KEY_D,            KEY_E,            KEY_F,            KEY_G,
76                 KEY_H,            KEY_I,            KEY_J,            KEY_K,            KEY_L,            KEY_M,            KEY_N,            KEY_O,
77                 KEY_P,            KEY_Q,            KEY_R,            KEY_S,            KEY_T,            KEY_U,            KEY_V,            KEY_W,
78                 KEY_X,            KEY_Y,            KEY_Z,                0,                0,                0,        KEY_TILDE,    KEY_BACKSPACE
79     };
81     // Note: the mapping 0x7f should by rights be KEY_DELETE
82     // however if you press "backspace" 0x7f is reported
83     // whereas for "delete" 0xf728 gets reported
85     // Note: the mapping of 0x19 to KEY_TAB is because for unknown reasons
86     // tab alone is reported as 0x09 (as expected) but shift-tab is
87     // reported as 0x19 (end of medium)
89     static sal_uInt16 aFunctionKeyCodeMap[ 128 ] =
90     {
91             KEY_UP,         KEY_DOWN,         KEY_LEFT,        KEY_RIGHT,           KEY_F1,           KEY_F2,           KEY_F3,           KEY_F4,
92             KEY_F5,           KEY_F6,           KEY_F7,           KEY_F8,           KEY_F9,          KEY_F10,          KEY_F11,          KEY_F12,
93            KEY_F13,          KEY_F14,          KEY_F15,          KEY_F16,          KEY_F17,          KEY_F18,          KEY_F19,          KEY_F20,
94            KEY_F21,          KEY_F22,          KEY_F23,          KEY_F24,          KEY_F25,          KEY_F26,                0,                0,
95                  0,                0,                0,                0,                0,                0,                0,       KEY_INSERT,
96         KEY_DELETE,         KEY_HOME,                0,          KEY_END,        KEY_PAGEUP,    KEY_PAGEDOWN,                0,                0,
97                  0,                0,                0,                0,                 0,        KEY_MENU,                0,                0,
98                  0,                0,                0,                0,                 0,               0,                0,                0,
99                  0,                0,                0,         KEY_UNDO,        KEY_REPEAT,        KEY_FIND,         KEY_HELP,                0,
100                  0,                0,                0,                0,                 0,               0,                0,                0,
101                  0,                0,                0,                0,                 0,               0,                0,                0,
102                  0,                0,                0,                0,                 0,               0,                0,                0,
103                  0,                0,                0,                0,                 0,               0,                0,                0,
104                  0,                0,                0,                0,                 0,               0,                0,                0,
105                  0,                0,                0,                0,                 0,               0,                0,                0,
106                  0,                0,                0,                0,                 0,               0,                0,                0
107     };
109     sal_uInt16 nKeyCode = 0;
110     if( aCode < SAL_N_ELEMENTS( aKeyCodeMap)  )
111         nKeyCode = aKeyCodeMap[ aCode ];
112     else if( aCode >= 0xf700 && aCode < 0xf780 )
113         nKeyCode = aFunctionKeyCodeMap[ aCode - 0xf700 ];
114     return nKeyCode;
117 static sal_uInt16 ImplMapKeyCode(sal_uInt16 nKeyCode)
119     /*
120       http://stackoverflow.com/questions/2080312/where-can-i-find-a-list-of-key-codes-for-use-with-cocoas-nsevent-class/2080324#2080324
121       /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
122      */
124     static sal_uInt16 aKeyCodeMap[ 0x80 ] =
125     {
126             KEY_A,            KEY_S,            KEY_D,            KEY_F,            KEY_H,            KEY_G,            KEY_Z,            KEY_X,
127             KEY_C,            KEY_V,                0,            KEY_B,            KEY_Q,            KEY_W,            KEY_E,            KEY_R,
128             KEY_Y,            KEY_T,            KEY_1,            KEY_2,            KEY_3,            KEY_4,            KEY_6,            KEY_5,
129         KEY_EQUAL,            KEY_9,            KEY_7,     KEY_SUBTRACT,            KEY_8,            KEY_0, KEY_BRACKETRIGHT, KEY_RIGHTCURLYBRACKET,
130             KEY_U,  KEY_BRACKETLEFT,            KEY_I,            KEY_P,       KEY_RETURN,            KEY_L,            KEY_J,   KEY_QUOTERIGHT,
131             KEY_K,    KEY_SEMICOLON,                0,        KEY_COMMA,       KEY_DIVIDE,            KEY_N,            KEY_M,        KEY_POINT,
132           KEY_TAB,        KEY_SPACE,    KEY_QUOTELEFT,       KEY_DELETE,                0,       KEY_ESCAPE,                0,                0,
133                 0,     KEY_CAPSLOCK,                0,                0,                0,                0,                0,                0,
134           KEY_F17,      KEY_DECIMAL,                0,     KEY_MULTIPLY,                0,          KEY_ADD,                0,                0,
135                 0,                0,                0,       KEY_DIVIDE,       KEY_RETURN,                0,     KEY_SUBTRACT,          KEY_F18,
136           KEY_F19,        KEY_EQUAL,                0,                0,                0,                0,                0,                0,
137                 0,                0,          KEY_F20,                0,                0,                0,                0,                0,
138            KEY_F5,           KEY_F6,           KEY_F7,           KEY_F3,           KEY_F8,           KEY_F9,                0,          KEY_F11,
139                 0,          KEY_F13,          KEY_F16,          KEY_F14,                0,          KEY_F10,                0,          KEY_F12,
140                 0,          KEY_F15,         KEY_HELP,         KEY_HOME,       KEY_PAGEUP,       KEY_DELETE,           KEY_F4,          KEY_END,
141            KEY_F2,     KEY_PAGEDOWN,           KEY_F1,         KEY_LEFT,        KEY_RIGHT,         KEY_DOWN,           KEY_UP,                0
142     };
144     if (nKeyCode < SAL_N_ELEMENTS(aKeyCodeMap))
145         return aKeyCodeMap[nKeyCode];
146     return 0;
149 // store the frame the mouse last entered
150 static AquaSalFrame* s_pMouseFrame = nullptr;
151 // store the last pressed button for enter/exit events
152 // which lack that information
153 static sal_uInt16 s_nLastButton = 0;
155 static AquaSalFrame* getMouseContainerFrame()
157     AquaSalFrame* pDispatchFrame = nullptr;
158     NSArray* aWindows = [NSWindow windowNumbersWithOptions:0];
159     for(NSUInteger i = 0; i < [aWindows count] && ! pDispatchFrame; i++ )
160     {
161         NSWindow* pWin = [NSApp windowWithWindowNumber:[[aWindows objectAtIndex:i] integerValue]];
162         if( pWin && [pWin isMemberOfClass: [SalFrameWindow class]] && [static_cast<SalFrameWindow*>(pWin) containsMouse] )
163             pDispatchFrame = [static_cast<SalFrameWindow*>(pWin) getSalFrame];
164     }
165     return pDispatchFrame;
168 static NSArray *getMergedAccessibilityChildren(NSArray *pDefaultChildren, NSArray *pUnignoredChildrenToAdd)
170     NSArray *pRet = pDefaultChildren;
172     if (pUnignoredChildrenToAdd && [pUnignoredChildrenToAdd count])
173     {
174         NSMutableArray *pNewChildren = [NSMutableArray arrayWithCapacity:(pRet ? [pRet count] : 0) + 1];
175         if (pNewChildren)
176         {
177             if (pRet)
178                 [pNewChildren addObjectsFromArray:pRet];
180             for (AquaA11yWrapper *pWrapper : pUnignoredChildrenToAdd)
181             {
182                 if (pWrapper && ![pNewChildren containsObject:pWrapper])
183                     [pNewChildren addObject:pWrapper];
184             }
186             pRet = pNewChildren;
187         }
188         else
189         {
190             pRet = pUnignoredChildrenToAdd;
191         }
192     }
194     return pRet;
197 @interface NSResponder (SalFrameWindow)
198 -(BOOL)accessibilityIsIgnored;
199 @end
201 @implementation SalFrameWindow
202 -(id)initWithSalFrame: (AquaSalFrame*)pFrame
204     mDraggingDestinationHandler = nil;
205     mbInLiveResize = NO;
206     mbInWindowDidResize = NO;
207     mpLiveResizeTimer = nil;
208     mpFrame = pFrame;
209     NSRect aRect = { { static_cast<CGFloat>(pFrame->maGeometry.x()), static_cast<CGFloat>(pFrame->maGeometry.y()) },
210                      { static_cast<CGFloat>(pFrame->maGeometry.width()), static_cast<CGFloat>(pFrame->maGeometry.height()) } };
211     pFrame->VCLToCocoa( aRect );
212     NSWindow* pNSWindow = [super initWithContentRect: aRect
213                                  styleMask: mpFrame->getStyleMask()
214                                  backing: NSBackingStoreBuffered
215                                  defer: Application::IsHeadlessModeEnabled()];
217     // Disallow full-screen mode on macOS >= 10.11 where it is enabled by default. We don't want it
218     // for now as it will just be confused with LibreOffice's home-grown full-screen concept, with
219     // which it has nothing to do, and one can get into all kinds of weird states by using them
220     // intermixedly.
222     // Ideally we should use the system full-screen mode and adapt the code for the home-grown thing
223     // to be in sync with that instead. (And we would then not need the button to get out of
224     // full-screen mode, as the normal way to get out of it is to either click on the green bubble
225     // again, or invoke the keyboard command again.)
227     // (Confusingly, at the moment the home-grown full-screen mode is bound to Cmd+Shift+F, which is
228     // the keyboard command normally used in apps to get in and out of the system full-screen mode.)
230     // Disabling system full-screen mode makes the green button on the title bar (on macOS >= 10.11)
231     // show a plus sign instead, and clicking it becomes identical to double-clicking the title bar,
232     // i.e. it maximizes / unmaximises the window. Sure, that state can also be confused with LO's
233     // home-grown full-screen mode. Oh well.
235     [pNSWindow setCollectionBehavior: NSWindowCollectionBehaviorFullScreenNone];
237     // Disable window restoration until we support it directly
238     [pNSWindow setRestorable: NO];
240     // tdf#137468: Restrict to 24-bit RGB as that is all that we can
241     // handle anyway. HDR is far off in the future for LibreOffice.
242     [pNSWindow setDynamicDepthLimit: NO];
243     [pNSWindow setDepthLimit: NSWindowDepthTwentyfourBitRGB];
245     return static_cast<SalFrameWindow *>(pNSWindow);
248 -(void)clearLiveResizeTimer
250     if ( mpLiveResizeTimer )
251     {
252         [mpLiveResizeTimer invalidate];
253         [mpLiveResizeTimer release];
254         mpLiveResizeTimer = nil;
255     }
258 -(void)dealloc
260     [self clearLiveResizeTimer];
261     [super dealloc];
264 -(AquaSalFrame*)getSalFrame
266     return mpFrame;
269 -(void)displayIfNeeded
271     if( GetSalData() && GetSalData()->mpInstance )
272     {
273         SolarMutexGuard aGuard;
274         [super displayIfNeeded];
275     }
278 -(BOOL)containsMouse
280     // is this event actually inside that NSWindow ?
281     NSPoint aPt = [NSEvent mouseLocation];
282     NSRect aFrameRect = [self frame];
283     bool bInRect = NSPointInRect( aPt, aFrameRect );
284     return bInRect;
287 -(BOOL)canBecomeKeyWindow
289     if( (mpFrame->mnStyle &
290             ( SalFrameStyleFlags::FLOAT                 |
291               SalFrameStyleFlags::TOOLTIP               |
292               SalFrameStyleFlags::INTRO
293             )) == SalFrameStyleFlags::NONE )
294         return YES;
295     if( mpFrame->mnStyle & SalFrameStyleFlags::OWNERDRAWDECORATION )
296         return YES;
297     if( mpFrame->mbFullScreen )
298         return YES;
299     return [super canBecomeKeyWindow];
302 -(void)windowDidBecomeKey: (NSNotification*)pNotification
304     (void)pNotification;
305     SolarMutexGuard aGuard;
307     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
308     {
309         static const SalFrameStyleFlags nGuessDocument = SalFrameStyleFlags::MOVEABLE|
310                                             SalFrameStyleFlags::SIZEABLE|
311                                             SalFrameStyleFlags::CLOSEABLE;
313         // Reset dark mode colors in HITheme controls after printing
314         // In dark mode, after an NSPrintOperation has completed, macOS draws
315         // HITheme controls with light mode colors so reset all dark mode
316         // colors when an NSWindow gains focus.
317         mpFrame->UpdateDarkMode();
319         if( mpFrame->mpMenu )
320             mpFrame->mpMenu->setMainMenu();
321         else if( ! mpFrame->mpParent &&
322                  ( (mpFrame->mnStyle & nGuessDocument) == nGuessDocument || // set default menu for e.g. help
323                     mpFrame->mbFullScreen ) )                               // set default menu for e.g. presentation
324         {
325             AquaSalMenu::setDefaultMenu();
326         }
327         mpFrame->CallCallback( SalEvent::GetFocus, nullptr );
328         mpFrame->SendPaintEvent(); // repaint controls as active
329     }
331     // Prevent the same native input method popup that was cancelled in a
332     // previous call to [self windowDidResignKey:] from reappearing
333     [self endExtTextInput];
336 -(void)windowDidResignKey: (NSNotification*)pNotification
338     (void)pNotification;
339     SolarMutexGuard aGuard;
341     // Commit any uncommitted text and cancel the native input method session
342     // whenever a window loses focus like in Safari, Firefox, and Excel
343     [self endExtTextInput];
345     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
346     {
347         mpFrame->CallCallback(SalEvent::LoseFocus, nullptr);
348         mpFrame->SendPaintEvent(); // repaint controls as inactive
349     }
352 -(void)windowDidChangeScreen: (NSNotification*)pNotification
354     (void)pNotification;
355     SolarMutexGuard aGuard;
357     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
358         mpFrame->screenParametersChanged();
361 -(void)windowDidMove: (NSNotification*)pNotification
363     (void)pNotification;
364     SolarMutexGuard aGuard;
366     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
367     {
368         mpFrame->UpdateFrameGeometry();
369         mpFrame->CallCallback( SalEvent::Move, nullptr );
370     }
373 -(void)windowDidResize: (NSNotification*)pNotification
375     SolarMutexGuard aGuard;
377     if ( mbInWindowDidResize )
378         return;
380     mbInWindowDidResize = YES;
382     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
383     {
384         mpFrame->UpdateFrameGeometry();
385         mpFrame->CallCallback( SalEvent::Resize, nullptr );
387         bool bInLiveResize = [self inLiveResize];
388         ImplSVData* pSVData = ImplGetSVData();
389         assert( pSVData );
390         if ( pSVData )
391         {
392             const bool bWasLiveResize = pSVData->mpWinData->mbIsLiveResize;
393             if ( bWasLiveResize != bInLiveResize )
394             {
395                 pSVData->mpWinData->mbIsLiveResize = bInLiveResize;
396                 Scheduler::Wakeup();
397             }
398         }
400         if ( bInLiveResize || mbInLiveResize )
401         {
402             mbInLiveResize = bInLiveResize;
404 #if HAVE_FEATURE_SKIA
405             // Related: tdf#152703 Eliminate empty window with Skia/Metal while resizing
406             // The window will clear its background so when Skia/Metal is
407             // enabled, explicitly flush the Skia graphics to the window
408             // during live resizing or else nothing will be drawn until after
409             // live resizing has ended.
410             // Also, flushing during [self windowDidResize:] eliminates flicker
411             // by forcing this window's SkSurface to recreate its underlying
412             // CAMetalLayer with the new size. Flushing in
413             // [self displayIfNeeded] does not eliminate flicker so apparently
414             // [self windowDidResize:] is called earlier.
415             if ( SkiaHelper::isVCLSkiaEnabled() )
416             {
417                 AquaSalGraphics* pGraphics = mpFrame->mpGraphics;
418                 if ( pGraphics )
419                     pGraphics->Flush();
420             }
421 #endif
423             // tdf#152703 Force relayout during live resizing of window
424             // During a live resize, macOS floods the application with
425             // windowDidResize: notifications so sending a paint event does
426             // not trigger redrawing with the new size.
427             // Instead, force relayout by dispatching all pending internal
428             // events and firing any pending timers.
429             // Also, Application::Reschedule() can potentially display a
430             // modal dialog which will cause a hang so temporarily disable
431             // live resize by clamping the window's minimum and maximum sizes
432             // to the current frame size which in Application::Reschedule().
433             NSRect aFrame = [self frame];
434             NSSize aMinSize = [self minSize];
435             NSSize aMaxSize = [self maxSize];
436             [self setMinSize:aFrame.size];
437             [self setMaxSize:aFrame.size];
438             Application::Reschedule( true );
439             [self setMinSize:aMinSize];
440             [self setMaxSize:aMaxSize];
442             if ( mbInLiveResize )
443             {
444                 // tdf#152703 Force repaint after live resizing ends
445                 // Repost this notification so that this selector will be called
446                 // at least once after live resizing ends
447                 if ( !mpLiveResizeTimer )
448                 {
449                     mpLiveResizeTimer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(windowDidResizeWithTimer:) userInfo:pNotification repeats:YES];
450                     if ( mpLiveResizeTimer )
451                     {
452                         [mpLiveResizeTimer retain];
454                         // The timer won't fire without a call to
455                         // Application::Reschedule() unless we copy the fix for
456                         // #i84055# from vcl/osx/saltimer.cxx and add the timer
457                         // to the NSEventTrackingRunLoopMode run loop mode
458                         [[NSRunLoop currentRunLoop] addTimer:mpLiveResizeTimer forMode:NSEventTrackingRunLoopMode];
459                     }
460                 }
461             }
462         }
463         else
464         {
465             [self clearLiveResizeTimer];
466             mpFrame->SendPaintEvent();
467         }
468     }
470     mbInWindowDidResize = NO;
473 -(void)windowDidMiniaturize: (NSNotification*)pNotification
475     (void)pNotification;
476     SolarMutexGuard aGuard;
478     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
479     {
480         mpFrame->mbShown = false;
481         mpFrame->UpdateFrameGeometry();
482         mpFrame->CallCallback( SalEvent::Resize, nullptr );
483     }
486 -(void)windowDidDeminiaturize: (NSNotification*)pNotification
488     (void)pNotification;
489     SolarMutexGuard aGuard;
491     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
492     {
493         mpFrame->mbShown = true;
494         mpFrame->UpdateFrameGeometry();
495         mpFrame->CallCallback( SalEvent::Resize, nullptr );
496     }
499 -(BOOL)windowShouldClose: (NSNotification*)pNotification
501     (void)pNotification;
502     SolarMutexGuard aGuard;
504     bool bRet = true;
505     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
506     {
507         // #i84461# end possible input
508         [self endExtTextInput];
509         if( AquaSalFrame::isAlive( mpFrame ) )
510         {
511             mpFrame->CallCallback( SalEvent::Close, nullptr );
512             bRet = false; // application will close the window or not, AppKit shouldn't
513             AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
514             assert( pTimer );
515             pTimer->handleWindowShouldClose();
516         }
517     }
519     return bRet;
522 -(void)windowDidEnterFullScreen: (NSNotification*)pNotification
524     SolarMutexGuard aGuard;
526     if( !mpFrame || !AquaSalFrame::isAlive( mpFrame))
527         return;
528     mpFrame->mbFullScreen = true;
529     (void)pNotification;
532 -(void)windowDidExitFullScreen: (NSNotification*)pNotification
534     SolarMutexGuard aGuard;
536     if( !mpFrame || !AquaSalFrame::isAlive( mpFrame))
537         return;
538     mpFrame->mbFullScreen = false;
539     (void)pNotification;
542 -(void)windowDidChangeBackingProperties:(NSNotification *)pNotification
544     (void)pNotification;
545 #if HAVE_FEATURE_SKIA
546     SolarMutexGuard aGuard;
548     sal::aqua::resetWindowScaling();
550     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
551     {
552         // tdf#147342 Notify Skia that the window's backing properties changed
553         if ( SkiaHelper::isVCLSkiaEnabled() )
554         {
555             AquaSalGraphics* pGraphics = mpFrame->mpGraphics;
556             if ( pGraphics )
557                 pGraphics->WindowBackingPropertiesChanged();
558         }
559     }
560 #endif
563 -(void)dockMenuItemTriggered: (id)sender
565     (void)sender;
566     SolarMutexGuard aGuard;
568     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
569         mpFrame->ToTop( SalFrameToTop::RestoreWhenMin | SalFrameToTop::GrabFocus );
572 -(css::uno::Reference < css::accessibility::XAccessibleContext >)accessibleContext
574     return mpFrame -> GetWindow() -> GetAccessible() -> getAccessibleContext();
577 -(BOOL)isIgnoredWindow
579     SolarMutexGuard aGuard;
581     // Treat tooltip windows as ignored
582     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
583         return (mpFrame->mnStyle & SalFrameStyleFlags::TOOLTIP) != SalFrameStyleFlags::NONE;
584     return YES;
587 -(id)accessibilityApplicationFocusedUIElement
589     return [self accessibilityFocusedUIElement];
592 -(id)accessibilityFocusedUIElement
594     // Treat tooltip windows as ignored
595     if ([self isIgnoredWindow])
596         return nil;
598     return [super accessibilityFocusedUIElement];
601 -(BOOL)accessibilityIsIgnored
603     // Treat tooltip windows as ignored
604     if ([self isIgnoredWindow])
605         return YES;
607     return [super accessibilityIsIgnored];
610 -(BOOL)isAccessibilityElement
612     return ![self accessibilityIsIgnored];
615 -(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
617   return [mDraggingDestinationHandler draggingEntered: sender];
620 -(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
622   return [mDraggingDestinationHandler draggingUpdated: sender];
625 -(void)draggingExited:(id <NSDraggingInfo>)sender
627   [mDraggingDestinationHandler draggingExited: sender];
630 -(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
632   return [mDraggingDestinationHandler prepareForDragOperation: sender];
635 -(BOOL)performDragOperation:(id <NSDraggingInfo>)sender
637   return [mDraggingDestinationHandler performDragOperation: sender];
640 -(void)concludeDragOperation:(id <NSDraggingInfo>)sender
642   [mDraggingDestinationHandler concludeDragOperation: sender];
645 -(void)registerDraggingDestinationHandler:(id)theHandler
647   mDraggingDestinationHandler = theHandler;
650 -(void)unregisterDraggingDestinationHandler:(id)theHandler
652     (void)theHandler;
653     mDraggingDestinationHandler = nil;
656 -(void)endExtTextInput
658     [self endExtTextInput:EndExtTextInputFlags::Complete];
661 -(void)endExtTextInput:(EndExtTextInputFlags)nFlags
663     SalFrameView *pView = static_cast<SalFrameView*>([self firstResponder]);
664     if (pView && [pView isKindOfClass:[SalFrameView class]])
665         [pView endExtTextInput:nFlags];
668 -(void)windowDidResizeWithTimer:(NSTimer *)pTimer
670     if ( pTimer )
671         [self windowDidResize:[pTimer userInfo]];
674 @end
676 @implementation SalFrameView
677 +(void)unsetMouseFrame: (AquaSalFrame*)pFrame
679     if( pFrame == s_pMouseFrame )
680         s_pMouseFrame = nullptr;
683 -(id)initWithSalFrame: (AquaSalFrame*)pFrame
685     if ((self = [super initWithFrame: [NSWindow contentRectForFrameRect: [pFrame->getNSWindow() frame] styleMask: pFrame->mnStyleMask]]) != nil)
686     {
687         mDraggingDestinationHandler = nil;
688         mpFrame = pFrame;
689         mpChildWrapper = nil;
690         mbNeedChildWrapper = NO;
691         mpLastEvent = nil;
692         mMarkedRange = NSMakeRange(NSNotFound, 0);
693         mSelectedRange = NSMakeRange(NSNotFound, 0);
694         mpMouseEventListener = nil;
695         mpLastSuperEvent = nil;
696         mfLastMagnifyTime = 0.0;
698         mbInEndExtTextInput = NO;
699         mbInCommitMarkedText = NO;
700         mpLastMarkedText = nil;
701         mbTextInputWantsNonRepeatKeyDown = NO;
702     }
704     return self;
707 -(void)dealloc
709     [self clearLastEvent];
710     [self clearLastMarkedText];
711     [self revokeWrapper];
713     [super dealloc];
716 -(AquaSalFrame*)getSalFrame
718     return mpFrame;
721 -(void)resetCursorRects
723     if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
724     {
725         // FIXME: does this leak the returned NSCursor of getCurrentCursor ?
726         const NSRect aRect = { NSZeroPoint, NSMakeSize(mpFrame->maGeometry.width(), mpFrame->maGeometry.height()) };
727         [self addCursorRect: aRect cursor: mpFrame->getCurrentCursor()];
728     }
731 -(BOOL)acceptsFirstResponder
733     return YES;
736 -(BOOL)acceptsFirstMouse: (NSEvent*)pEvent
738     (void)pEvent;
739     return YES;
742 -(BOOL)isOpaque
744     if( !mpFrame)
745         return YES;
746     if( !AquaSalFrame::isAlive( mpFrame))
747         return YES;
748     if( !mpFrame->getClipPath())
749         return YES;
750     return NO;
753 -(void)drawRect: (NSRect)aRect
755     ImplSVData* pSVData = ImplGetSVData();
756     assert( pSVData );
757     if ( !pSVData )
758         return;
760     SolarMutexGuard aGuard;
761     if (!mpFrame || !AquaSalFrame::isAlive(mpFrame))
762         return;
764     const bool bIsLiveResize = [self inLiveResize];
765     const bool bWasLiveResize = pSVData->mpWinData->mbIsLiveResize;
766     if (bWasLiveResize != bIsLiveResize)
767     {
768         pSVData->mpWinData->mbIsLiveResize = bIsLiveResize;
769         Scheduler::Wakeup();
770     }
772     AquaSalGraphics* pGraphics = mpFrame->mpGraphics;
773     if (pGraphics)
774     {
775         pGraphics->UpdateWindow(aRect);
776         if (mpFrame->getClipPath())
777             [mpFrame->getNSWindow() invalidateShadow];
778     }
781 -(void)sendMouseEventToFrame: (NSEvent*)pEvent button:(sal_uInt16)nButton eventtype:(SalEvent)nEvent
783     SolarMutexGuard aGuard;
785     AquaSalFrame* pDispatchFrame = AquaSalFrame::GetCaptureFrame();
786     bool bIsCaptured = false;
787     if( pDispatchFrame )
788     {
789         bIsCaptured = true;
790         if( nEvent == SalEvent::MouseLeave ) // no leave events if mouse is captured
791             nEvent = SalEvent::MouseMove;
792     }
793     else if( s_pMouseFrame )
794         pDispatchFrame = s_pMouseFrame;
795     else
796         pDispatchFrame = mpFrame;
798     /* #i81645# Cocoa reports mouse events while a button is pressed
799        to the window in which it was first pressed. This is reasonable and fine and
800        gets one around most cases where on other platforms one uses CaptureMouse or XGrabPointer,
801        however vcl expects mouse events to occur in the window the mouse is over, unless the
802        mouse is explicitly captured. So we need to find the window the mouse is actually
803        over for conformance with other platforms.
804     */
805     if( ! bIsCaptured && nButton && pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) )
806     {
807         // is this event actually inside that NSWindow ?
808         NSPoint aPt = [NSEvent mouseLocation];
809         NSRect aFrameRect = [pDispatchFrame->getNSWindow() frame];
811         if ( ! NSPointInRect( aPt, aFrameRect ) )
812         {
813             // no, it is not
814             // now we need to find the one it may be in
815             /* #i93756# we ant to get enumerate the application windows in z-order
816                to check if any contains the mouse. This could be elegantly done with this
817                code:
819                // use NSApp to check windows in ZOrder whether they contain the mouse pointer
820                NSWindow* pWindow = [NSApp makeWindowsPerform: @selector(containsMouse) inOrder: YES];
821                if( pWindow && [pWindow isMemberOfClass: [SalFrameWindow class]] )
822                    pDispatchFrame = [(SalFrameWindow*)pWindow getSalFrame];
824                However if a non SalFrameWindow is on screen (like e.g. the file dialog)
825                it can be hit with the containsMouse selector, which it doesn't support.
826                Sadly NSApplication:makeWindowsPerform does not check (for performance reasons
827                I assume) whether a window supports a selector before sending it.
828             */
829             AquaSalFrame* pMouseFrame = getMouseContainerFrame();
830             if( pMouseFrame )
831                 pDispatchFrame = pMouseFrame;
832         }
833     }
835     if( pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) )
836     {
837         pDispatchFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
838         pDispatchFrame->mnLastModifierFlags = [pEvent modifierFlags];
840         NSPoint aPt = [NSEvent mouseLocation];
841         pDispatchFrame->CocoaToVCL( aPt );
843         sal_uInt16 nModMask = ImplGetModifierMask( [pEvent modifierFlags] );
844         // #i82284# emulate ctrl left
845         if( nModMask == KEY_MOD3 && nButton == MOUSE_LEFT )
846         {
847             nModMask    = 0;
848             nButton     = MOUSE_RIGHT;
849         }
851         SalMouseEvent aEvent;
852         aEvent.mnTime   = pDispatchFrame->mnLastEventTime;
853         aEvent.mnX = static_cast<tools::Long>(aPt.x) - pDispatchFrame->maGeometry.x();
854         aEvent.mnY = static_cast<tools::Long>(aPt.y) - pDispatchFrame->maGeometry.y();
855         aEvent.mnButton = nButton;
856         aEvent.mnCode   =  aEvent.mnButton | nModMask;
858         if( AllSettings::GetLayoutRTL() )
859             aEvent.mnX = pDispatchFrame->maGeometry.width() - 1 - aEvent.mnX;
861         pDispatchFrame->CallCallback( nEvent, &aEvent );
862     }
865 -(void)mouseDown: (NSEvent*)pEvent
867     if ( mpMouseEventListener != nil &&
868         [mpMouseEventListener respondsToSelector: @selector(mouseDown:)])
869     {
870         [mpMouseEventListener mouseDown: [pEvent copyWithZone: nullptr]];
871     }
873     s_nLastButton = MOUSE_LEFT;
874     [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SalEvent::MouseButtonDown];
877 -(void)mouseDragged: (NSEvent*)pEvent
879     if ( mpMouseEventListener != nil &&
880          [mpMouseEventListener respondsToSelector: @selector(mouseDragged:)])
881     {
882         [mpMouseEventListener mouseDragged: [pEvent copyWithZone: nullptr]];
883     }
884     s_nLastButton = MOUSE_LEFT;
885     [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SalEvent::MouseMove];
888 -(void)mouseUp: (NSEvent*)pEvent
890     s_nLastButton = 0;
891     [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SalEvent::MouseButtonUp];
894 -(void)mouseMoved: (NSEvent*)pEvent
896     s_nLastButton = 0;
897     [self sendMouseEventToFrame:pEvent button:0 eventtype:SalEvent::MouseMove];
900 -(void)mouseEntered: (NSEvent*)pEvent
902     s_pMouseFrame = mpFrame;
904     // #i107215# the only mouse events we get when inactive are enter/exit
905     // actually we would like to have all of them, but better none than some
906     if( [NSApp isActive] )
907         [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SalEvent::MouseMove];
910 -(void)mouseExited: (NSEvent*)pEvent
912     if( s_pMouseFrame == mpFrame )
913         s_pMouseFrame = nullptr;
915     // #i107215# the only mouse events we get when inactive are enter/exit
916     // actually we would like to have all of them, but better none than some
917     if( [NSApp isActive] )
918         [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SalEvent::MouseLeave];
921 -(void)rightMouseDown: (NSEvent*)pEvent
923     s_nLastButton = MOUSE_RIGHT;
924     [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SalEvent::MouseButtonDown];
927 -(void)rightMouseDragged: (NSEvent*)pEvent
929     s_nLastButton = MOUSE_RIGHT;
930     [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SalEvent::MouseMove];
933 -(void)rightMouseUp: (NSEvent*)pEvent
935     s_nLastButton = 0;
936     [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SalEvent::MouseButtonUp];
939 -(void)otherMouseDown: (NSEvent*)pEvent
941     if( [pEvent buttonNumber] == 2 )
942     {
943         s_nLastButton = MOUSE_MIDDLE;
944         [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SalEvent::MouseButtonDown];
945     }
946     else
947         s_nLastButton = 0;
950 -(void)otherMouseDragged: (NSEvent*)pEvent
952     if( [pEvent buttonNumber] == 2 )
953     {
954         s_nLastButton = MOUSE_MIDDLE;
955         [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SalEvent::MouseMove];
956     }
957     else
958         s_nLastButton = 0;
961 -(void)otherMouseUp: (NSEvent*)pEvent
963     s_nLastButton = 0;
964     if( [pEvent buttonNumber] == 2 )
965         [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SalEvent::MouseButtonUp];
968 - (void)magnifyWithEvent: (NSEvent*)pEvent
970     SolarMutexGuard aGuard;
972     // TODO: ??  -(float)magnification;
973     if( AquaSalFrame::isAlive( mpFrame ) )
974     {
975         const NSTimeInterval fMagnifyTime = [pEvent timestamp];
976         mpFrame->mnLastEventTime = static_cast<sal_uInt64>( fMagnifyTime * 1000.0 );
977         mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
979         // check if this is a new series of magnify events
980         static const NSTimeInterval fMaxDiffTime = 0.3;
981         const bool bNewSeries = (fMagnifyTime - mfLastMagnifyTime > fMaxDiffTime);
983         if( bNewSeries )
984             mfMagnifyDeltaSum = 0.0;
985         mfMagnifyDeltaSum += [pEvent magnification];
987         mfLastMagnifyTime = [pEvent timestamp];
988 // TODO: change to 0.1 when CommandWheelMode::ZOOM handlers allow finer zooming control
989         static const float fMagnifyFactor = 0.25*500; // steps are 500 times smaller for -magnification
990         static const float fMinMagnifyStep = 15.0 / fMagnifyFactor;
991         if( fabs(mfMagnifyDeltaSum) <= fMinMagnifyStep )
992             return;
994         // adapt NSEvent-sensitivity to application expectations
995         // TODO: rather make CommandWheelMode::ZOOM handlers smarter
996         const float fDeltaZ = mfMagnifyDeltaSum * fMagnifyFactor;
997         int nDeltaZ = FRound( fDeltaZ );
998         if( !nDeltaZ )
999         {
1000             // handle new series immediately
1001             if( !bNewSeries )
1002                 return;
1003             nDeltaZ = (fDeltaZ >= 0.0) ? +1 : -1;
1004         }
1005         // eventually give credit for delta sum
1006         mfMagnifyDeltaSum -= nDeltaZ / fMagnifyFactor;
1008         NSPoint aPt = [NSEvent mouseLocation];
1009         mpFrame->CocoaToVCL( aPt );
1011         SalWheelMouseEvent aEvent;
1012         aEvent.mnTime           = mpFrame->mnLastEventTime;
1013         aEvent.mnX = static_cast<tools::Long>(aPt.x) - mpFrame->maGeometry.x();
1014         aEvent.mnY = static_cast<tools::Long>(aPt.y) - mpFrame->maGeometry.y();
1015         aEvent.mnCode           = ImplGetModifierMask( mpFrame->mnLastModifierFlags );
1016         aEvent.mnCode           |= KEY_MOD1; // we want zooming, no scrolling
1017         aEvent.mbDeltaIsPixel   = true;
1019         if( AllSettings::GetLayoutRTL() )
1020             aEvent.mnX = mpFrame->maGeometry.width() - 1 - aEvent.mnX;
1022         aEvent.mnDelta = nDeltaZ;
1023         aEvent.mnNotchDelta = (nDeltaZ >= 0) ? +1 : -1;
1024         if( aEvent.mnDelta == 0 )
1025             aEvent.mnDelta = aEvent.mnNotchDelta;
1026         aEvent.mbHorz = false;
1027         sal_uInt32 nScrollLines = nDeltaZ;
1028         if (nScrollLines == 0)
1029             nScrollLines = 1;
1030         aEvent.mnScrollLines = nScrollLines;
1031         mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1032     }
1035 - (void)rotateWithEvent: (NSEvent*)pEvent
1037     //Rotation : -(float)rotation;
1038     // TODO: create new CommandType so rotation is available to the applications
1039     (void)pEvent;
1042 - (void)swipeWithEvent: (NSEvent*)pEvent
1044     SolarMutexGuard aGuard;
1046     if( AquaSalFrame::isAlive( mpFrame ) )
1047     {
1048         mpFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
1049         mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
1051         // merge pending scroll wheel events
1052         CGFloat dX = 0.0;
1053         CGFloat dY = 0.0;
1054         for(;;)
1055         {
1056             dX += [pEvent deltaX];
1057             dY += [pEvent deltaY];
1058             NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSEventMaskScrollWheel
1059             untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ];
1060             if( !pNextEvent )
1061                 break;
1062             pEvent = pNextEvent;
1063         }
1065         NSPoint aPt = [NSEvent mouseLocation];
1066         mpFrame->CocoaToVCL( aPt );
1068         SalWheelMouseEvent aEvent;
1069         aEvent.mnTime           = mpFrame->mnLastEventTime;
1070         aEvent.mnX = static_cast<tools::Long>(aPt.x) - mpFrame->maGeometry.x();
1071         aEvent.mnY = static_cast<tools::Long>(aPt.y) - mpFrame->maGeometry.y();
1072         aEvent.mnCode           = ImplGetModifierMask( mpFrame->mnLastModifierFlags );
1073         aEvent.mbDeltaIsPixel   = true;
1075         if( AllSettings::GetLayoutRTL() )
1076             aEvent.mnX = mpFrame->maGeometry.width() - 1 - aEvent.mnX;
1078         if( dX != 0.0 )
1079         {
1080             aEvent.mnDelta = static_cast<tools::Long>(floor(dX));
1081             aEvent.mnNotchDelta = (dX < 0) ? -1 : +1;
1082             if( aEvent.mnDelta == 0 )
1083                 aEvent.mnDelta = aEvent.mnNotchDelta;
1084             aEvent.mbHorz = true;
1085             aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
1086             mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1087         }
1088         if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame ))
1089         {
1090             aEvent.mnDelta = static_cast<tools::Long>(floor(dY));
1091             aEvent.mnNotchDelta = (dY < 0) ? -1 : +1;
1092             if( aEvent.mnDelta == 0 )
1093                 aEvent.mnDelta = aEvent.mnNotchDelta;
1094             aEvent.mbHorz = false;
1095             aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
1096             mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1097         }
1098     }
1101 -(void)scrollWheel: (NSEvent*)pEvent
1103     SolarMutexGuard aGuard;
1105     if( AquaSalFrame::isAlive( mpFrame ) )
1106     {
1107         mpFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
1108         mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
1110         // merge pending scroll wheel events
1111         CGFloat dX = 0.0;
1112         CGFloat dY = 0.0;
1113         for(;;)
1114         {
1115             dX += [pEvent deltaX];
1116             dY += [pEvent deltaY];
1117             NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSEventMaskScrollWheel
1118                 untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ];
1119             if( !pNextEvent )
1120                 break;
1121             pEvent = pNextEvent;
1122         }
1124         NSPoint aPt = [NSEvent mouseLocation];
1125         mpFrame->CocoaToVCL( aPt );
1127         SalWheelMouseEvent aEvent;
1128         aEvent.mnTime         = mpFrame->mnLastEventTime;
1129         aEvent.mnX = static_cast<tools::Long>(aPt.x) - mpFrame->maGeometry.x();
1130         aEvent.mnY = static_cast<tools::Long>(aPt.y) - mpFrame->maGeometry.y();
1131         aEvent.mnCode         = ImplGetModifierMask( mpFrame->mnLastModifierFlags );
1132         aEvent.mbDeltaIsPixel = false;
1134         if( AllSettings::GetLayoutRTL() )
1135             aEvent.mnX = mpFrame->maGeometry.width() - 1 - aEvent.mnX;
1137         if( dX != 0.0 )
1138         {
1139             aEvent.mnDelta = static_cast<tools::Long>(floor(dX));
1140             aEvent.mnNotchDelta = (dX < 0) ? -1 : +1;
1141             if( aEvent.mnDelta == 0 )
1142                 aEvent.mnDelta = aEvent.mnNotchDelta;
1143             aEvent.mbHorz = true;
1144             sal_uInt32 nScrollLines = fabs(dX) / WHEEL_EVENT_FACTOR;
1145             if (nScrollLines == 0)
1146                 nScrollLines = 1;
1147             aEvent.mnScrollLines = nScrollLines;
1149             mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1150         }
1151         if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame ) )
1152         {
1153             aEvent.mnDelta = static_cast<tools::Long>(floor(dY));
1154             aEvent.mnNotchDelta = (dY < 0) ? -1 : +1;
1155             if( aEvent.mnDelta == 0 )
1156                 aEvent.mnDelta = aEvent.mnNotchDelta;
1157             aEvent.mbHorz = false;
1158             sal_uInt32 nScrollLines = fabs(dY) / WHEEL_EVENT_FACTOR;
1159             if (nScrollLines == 0)
1160                 nScrollLines = 1;
1161             aEvent.mnScrollLines = nScrollLines;
1163             mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1164         }
1165     }
1169 -(void)keyDown: (NSEvent*)pEvent
1171     SolarMutexGuard aGuard;
1173     if( AquaSalFrame::isAlive( mpFrame ) )
1174     {
1175         // Retain the event as it will be released sometime before a key up
1176         // event is dispatched
1177         [self clearLastEvent];
1178         mpLastEvent = [pEvent retain];
1180         mbInKeyInput = true;
1181         mbNeedSpecialKeyHandle = false;
1182         mbKeyHandled = false;
1184         mpFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
1185         mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
1187         if( ! [self handleKeyDownException: pEvent] )
1188         {
1189             sal_uInt16 nKeyCode = ImplMapKeyCode( [pEvent keyCode] );
1190             if ( nKeyCode == KEY_DELETE && mbTextInputWantsNonRepeatKeyDown )
1191             {
1192                 // tdf#42437 Enable press-and-hold special character input method
1193                 // Emulate the press-and-hold behavior of the TextEdit
1194                 // application by deleting the marked text when only the
1195                 // Delete key is pressed and keep the marked text when the
1196                 // Backspace key or Fn-Delete keys are pressed.
1197                 if ( [pEvent keyCode] == 51 )
1198                 {
1199                     [self deleteTextInputWantsNonRepeatKeyDown];
1200                 }
1201                 else
1202                 {
1203                     [self unmarkText];
1204                     mbKeyHandled = true;
1205                     mbInKeyInput = false;
1206                 }
1208                 [self endExtTextInput];
1209                 return;
1210             }
1212             NSArray* pArray = [NSArray arrayWithObject: pEvent];
1213             [self interpretKeyEvents: pArray];
1215             // Handle repeat key events by explicitly inserting the text if
1216             // -[NSResponder interpretKeyEvents:] does not insert or mark any
1217             // text. Note: do not do this step if there is uncommitted text.
1218             // Related: tdf#42437 Skip special press-and-hold handling for action keys
1219             // Pressing and holding action keys such as arrow keys must not be
1220             // handled like pressing and holding a character key as it will
1221             // insert unexpected text.
1222             if ( !mbKeyHandled && !mpLastMarkedText && mpLastEvent && [mpLastEvent type] == NSEventTypeKeyDown && [mpLastEvent isARepeat] )
1223             {
1224                 NSString *pChars = [mpLastEvent characters];
1225                 if ( pChars )
1226                     [self insertText:pChars replacementRange:NSMakeRange( 0, [pChars length] )];
1227             }
1228             // tdf#42437 Enable press-and-hold special character input method
1229             // Emulate the press-and-hold behavior of the TextEdit application
1230             // by committing an empty string for key down events dispatched
1231             // while the special character input method popup is displayed.
1232             else if ( mpLastMarkedText && mbTextInputWantsNonRepeatKeyDown && mpLastEvent && [mpLastEvent type] == NSEventTypeKeyDown && ![mpLastEvent isARepeat] )
1233             {
1234                 // If the escape or return key is pressed, unmark the text to
1235                 // skip deletion of marked text
1236                 if ( nKeyCode == KEY_ESCAPE || nKeyCode == KEY_RETURN )
1237                     [self unmarkText];
1238                 [self insertText:[NSString string] replacementRange:NSMakeRange( NSNotFound, 0 )];
1239             }
1240         }
1242         mbInKeyInput = false;
1243     }
1246 -(BOOL)handleKeyDownException:(NSEvent*)pEvent
1248     // check for a very special set of modified characters
1249     NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers];
1251     if( pUnmodifiedString && [pUnmodifiedString length] == 1 )
1252     {
1253         /* #i103102# key events with command and alternate don't make it through
1254            interpretKeyEvents (why?). Try to dispatch them here first,
1255            if not successful continue normally
1256         */
1257         if( (mpFrame->mnLastModifierFlags & (NSEventModifierFlagOption | NSEventModifierFlagCommand))
1258                     == (NSEventModifierFlagOption | NSEventModifierFlagCommand) )
1259         {
1260             if( [self sendSingleCharacter: mpLastEvent] )
1261                 return YES;
1262         }
1263     }
1264     return NO;
1267 -(void)flagsChanged: (NSEvent*)pEvent
1269     SolarMutexGuard aGuard;
1271     if( AquaSalFrame::isAlive( mpFrame ) )
1272     {
1273         mpFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
1274         mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
1275     }
1278 -(void)insertText:(id)aString replacementRange:(NSRange)replacementRange
1280     (void) replacementRange; // FIXME: surely it must be used
1282     SolarMutexGuard aGuard;
1284     [self deleteTextInputWantsNonRepeatKeyDown];
1286     // Ignore duplicate events that are sometimes posted during cancellation
1287     // of the native input method session. This usually happens when
1288     // [self endExtTextInput] is called from [self windowDidBecomeKey:] and,
1289     // if the native input method popup, that was cancelled in a
1290     // previous call to [self windowDidResignKey:], has reappeared. In such
1291     // cases, the native input context posts the reappearing popup's
1292     // uncommitted text.
1293     if (mbInEndExtTextInput && !mbInCommitMarkedText)
1294         return;
1296     if( AquaSalFrame::isAlive( mpFrame ) )
1297     {
1298         NSString* pInsert = nil;
1299         if( [aString isKindOfClass: [NSAttributedString class]] )
1300             pInsert = [aString string];
1301         else
1302             pInsert = aString;
1304         int nLen = 0;
1305         if( pInsert && ( nLen = [pInsert length] ) > 0 )
1306         {
1307             OUString aInsertString( GetOUString( pInsert ) );
1308              // aCharCode initializer is safe since aInsertString will at least contain '\0'
1309             sal_Unicode aCharCode = *aInsertString.getStr();
1311             if( nLen == 1 &&
1312                 aCharCode < 0x80 &&
1313                 aCharCode > 0x1f &&
1314                 ! [self hasMarkedText ]
1315                 )
1316             {
1317                 sal_uInt16 nKeyCode = ImplMapCharCode( aCharCode );
1318                 unsigned int nLastModifiers = mpFrame->mnLastModifierFlags;
1320                 // #i99567#
1321                 // find out the unmodified key code
1323                 // sanity check
1324                 if( mpLastEvent && ( [mpLastEvent type] == NSEventTypeKeyDown || [mpLastEvent type] == NSEventTypeKeyUp ) )
1325                 {
1326                     // get unmodified string
1327                     NSString* pUnmodifiedString = [mpLastEvent charactersIgnoringModifiers];
1328                     if( pUnmodifiedString && [pUnmodifiedString length] == 1 )
1329                     {
1330                         // map the unmodified key code
1331                         unichar keyChar = [pUnmodifiedString characterAtIndex: 0];
1332                         nKeyCode = ImplMapCharCode( keyChar );
1333                     }
1334                     nLastModifiers = [mpLastEvent modifierFlags];
1336                 }
1337                 // #i99567#
1338                 // applications and vcl's edit fields ignore key events with ALT
1339                 // however we're at a place where we know text should be inserted
1340                 // so it seems we need to strip the Alt modifier here
1341                 if( (nLastModifiers & (NSEventModifierFlagControl | NSEventModifierFlagOption | NSEventModifierFlagCommand))
1342                     == NSEventModifierFlagOption )
1343                 {
1344                     nLastModifiers = 0;
1345                 }
1346                 [self sendKeyInputAndReleaseToFrame: nKeyCode character: aCharCode modifiers: nLastModifiers];
1347             }
1348             else
1349             {
1350                 SalExtTextInputEvent aEvent;
1351                 aEvent.maText           = aInsertString;
1352                 aEvent.mpTextAttr       = nullptr;
1353                 aEvent.mnCursorPos      = aInsertString.getLength();
1354                 aEvent.mnCursorFlags    = 0;
1355                 mpFrame->CallCallback( SalEvent::ExtTextInput, &aEvent );
1356                 if( AquaSalFrame::isAlive( mpFrame ) )
1357                     mpFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
1358             }
1359         }
1360         else
1361         {
1362             SalExtTextInputEvent aEvent;
1363             aEvent.maText.clear();
1364             aEvent.mpTextAttr       = nullptr;
1365             aEvent.mnCursorPos      = 0;
1366             aEvent.mnCursorFlags    = 0;
1367             mpFrame->CallCallback( SalEvent::ExtTextInput, &aEvent );
1368             if( AquaSalFrame::isAlive( mpFrame ) )
1369                 mpFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
1371         }
1372         [self unmarkText];
1373     }
1375     // Mark event as handled even if the frame isn't valid like is done in
1376     // [self setMarkedText:selectedRange:replacementRange:] and
1377     // [self doCommandBySelector:]
1378     mbKeyHandled = true;
1381 -(void)insertTab: (id)aSender
1383     (void)aSender;
1384     [self sendKeyInputAndReleaseToFrame: KEY_TAB character: '\t' modifiers: 0];
1387 -(void)insertBacktab: (id)aSender
1389     (void)aSender;
1390     [self sendKeyInputAndReleaseToFrame: (KEY_TAB | KEY_SHIFT) character: '\t' modifiers: 0];
1393 -(void)moveLeft: (id)aSender
1395     (void)aSender;
1396     [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: 0];
1399 -(void)moveLeftAndModifySelection: (id)aSender
1401     (void)aSender;
1402     [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: NSEventModifierFlagShift];
1405 -(void)moveBackwardAndModifySelection: (id)aSender
1407     (void)aSender;
1408     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_BACKWARD character: 0  modifiers: 0];
1411 -(void)moveRight: (id)aSender
1413     (void)aSender;
1414     [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: 0];
1417 -(void)moveRightAndModifySelection: (id)aSender
1419     (void)aSender;
1420     [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: NSEventModifierFlagShift];
1423 -(void)moveForwardAndModifySelection: (id)aSender
1425     (void)aSender;
1426     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_FORWARD character: 0  modifiers: 0];
1429 -(void)moveWordLeft: (id)aSender
1431     (void)aSender;
1432     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_BACKWARD character: 0  modifiers: 0];
1435 -(void)moveWordBackward: (id)aSender
1437     (void)aSender;
1438     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_BACKWARD character: 0  modifiers: 0];
1441 -(void)moveWordBackwardAndModifySelection: (id)aSender
1443     (void)aSender;
1444     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_BACKWARD character: 0  modifiers: 0];
1447 -(void)moveWordLeftAndModifySelection: (id)aSender
1449     (void)aSender;
1450     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_BACKWARD character: 0  modifiers: 0];
1453 -(void)moveWordRight: (id)aSender
1455     (void)aSender;
1456     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_FORWARD character: 0  modifiers: 0];
1459 -(void)moveWordForward: (id)aSender
1461     (void)aSender;
1462     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_FORWARD character: 0  modifiers: 0];
1465 -(void)moveWordForwardAndModifySelection: (id)aSender
1467     (void)aSender;
1468     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_FORWARD character: 0  modifiers: 0];
1471 -(void)moveWordRightAndModifySelection: (id)aSender
1473     (void)aSender;
1474     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_FORWARD character: 0  modifiers: 0];
1477 -(void)moveToEndOfLine: (id)aSender
1479     (void)aSender;
1480     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_LINE character: 0  modifiers: 0];
1483 -(void)moveToRightEndOfLine: (id)aSender
1485     (void)aSender;
1486     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_LINE character: 0  modifiers: 0];
1489 -(void)moveToEndOfLineAndModifySelection: (id)aSender
1491     (void)aSender;
1492     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_LINE character: 0  modifiers: 0];
1495 -(void)moveToRightEndOfLineAndModifySelection: (id)aSender
1497     (void)aSender;
1498     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_LINE character: 0  modifiers: 0];
1501 -(void)moveToBeginningOfLine: (id)aSender
1503     (void)aSender;
1504     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1507 -(void)moveToLeftEndOfLine: (id)aSender
1509     (void)aSender;
1510     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1513 -(void)moveToBeginningOfLineAndModifySelection: (id)aSender
1515     (void)aSender;
1516     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1519 -(void)moveToLeftEndOfLineAndModifySelection: (id)aSender
1521     (void)aSender;
1522     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1525 -(void)moveToEndOfParagraph: (id)aSender
1527     (void)aSender;
1528     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1531 -(void)moveToEndOfParagraphAndModifySelection: (id)aSender
1533     (void)aSender;
1534     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1537 -(void)moveParagraphForward: (id)aSender
1539     (void)aSender;
1540     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1543 -(void)moveParagraphForwardAndModifySelection: (id)aSender
1545     (void)aSender;
1546     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1549 -(void)moveToBeginningOfParagraph: (id)aSender
1551     (void)aSender;
1552     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1555 -(void)moveParagraphBackward: (id)aSender
1557     (void)aSender;
1558     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1561 -(void)moveToBeginningOfParagraphAndModifySelection: (id)aSender
1563     (void)aSender;
1564     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1567 -(void)moveParagraphBackwardAndModifySelection: (id)aSender
1569     (void)aSender;
1570     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1573 -(void)moveToEndOfDocument: (id)aSender
1575     (void)aSender;
1576     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0  modifiers: 0];
1579 -(void)scrollToEndOfDocument: (id)aSender
1581     (void)aSender;
1582     // this is not exactly what we should do, but it makes "End" and "Shift-End" behave consistent
1583     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0  modifiers: 0];
1586 -(void)moveToEndOfDocumentAndModifySelection: (id)aSender
1588     (void)aSender;
1589     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_DOCUMENT character: 0  modifiers: 0];
1592 -(void)moveToBeginningOfDocument: (id)aSender
1594     (void)aSender;
1595     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0  modifiers: 0];
1598 -(void)scrollToBeginningOfDocument: (id)aSender
1600     (void)aSender;
1601     // this is not exactly what we should do, but it makes "Home" and "Shift-Home" behave consistent
1602     [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0  modifiers: 0];
1605 -(void)moveToBeginningOfDocumentAndModifySelection: (id)aSender
1607     (void)aSender;
1608     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT character: 0  modifiers: 0];
1611 -(void)moveUp: (id)aSender
1613     (void)aSender;
1614     [self sendKeyInputAndReleaseToFrame: KEY_UP character: 0 modifiers: 0];
1617 -(void)moveDown: (id)aSender
1619     (void)aSender;
1620     [self sendKeyInputAndReleaseToFrame: KEY_DOWN character: 0 modifiers: 0];
1623 -(void)insertNewline: (id)aSender
1625     (void)aSender;
1626     // #i91267# make enter and shift-enter work by evaluating the modifiers
1627     [self sendKeyInputAndReleaseToFrame: KEY_RETURN character: '\n' modifiers: mpFrame->mnLastModifierFlags];
1630 -(void)deleteBackward: (id)aSender
1632     (void)aSender;
1633     [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0];
1636 -(void)deleteForward: (id)aSender
1638     (void)aSender;
1639     [self sendKeyInputAndReleaseToFrame: KEY_DELETE character: 0x7f modifiers: 0];
1642 -(void)deleteBackwardByDecomposingPreviousCharacter: (id)aSender
1644     (void)aSender;
1645     [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0];
1648 -(void)deleteWordBackward: (id)aSender
1650     (void)aSender;
1651     [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_WORD_BACKWARD character: 0  modifiers: 0];
1654 -(void)deleteWordForward: (id)aSender
1656     (void)aSender;
1657     [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_WORD_FORWARD character: 0  modifiers: 0];
1660 -(void)deleteToBeginningOfLine: (id)aSender
1662     (void)aSender;
1663     [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1666 -(void)deleteToEndOfLine: (id)aSender
1668     (void)aSender;
1669     [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_END_OF_LINE character: 0  modifiers: 0];
1672 -(void)deleteToBeginningOfParagraph: (id)aSender
1674     (void)aSender;
1675     [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1678 -(void)deleteToEndOfParagraph: (id)aSender
1680     (void)aSender;
1681     [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1684 -(void)insertLineBreak: (id)aSender
1686     (void)aSender;
1687     [self sendKeyInputAndReleaseToFrame: css::awt::Key::INSERT_LINEBREAK character: 0  modifiers: 0];
1690 -(void)insertParagraphSeparator: (id)aSender
1692     (void)aSender;
1693     [self sendKeyInputAndReleaseToFrame: css::awt::Key::INSERT_PARAGRAPH character: 0  modifiers: 0];
1696 -(void)selectWord: (id)aSender
1698     (void)aSender;
1699     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD character: 0  modifiers: 0];
1702 -(void)selectLine: (id)aSender
1704     (void)aSender;
1705     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_LINE character: 0  modifiers: 0];
1708 -(void)selectParagraph: (id)aSender
1710     (void)aSender;
1711     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_PARAGRAPH character: 0  modifiers: 0];
1714 -(void)selectAll: (id)aSender
1716     (void)aSender;
1717     [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_ALL character: 0  modifiers: 0];
1720 -(void)cancelOperation: (id)aSender
1722     (void)aSender;
1723     [self sendKeyInputAndReleaseToFrame: KEY_ESCAPE character: 0x1b modifiers: 0];
1726 -(void)noop: (id)aSender
1728     (void)aSender;
1729     if( ! mbKeyHandled )
1730     {
1731         if( ! [self sendSingleCharacter:mpLastEvent] )
1732         {
1733             /* prevent recursion */
1734             if( mpLastEvent != mpLastSuperEvent && [NSApp respondsToSelector: @selector(sendSuperEvent:)] )
1735             {
1736                 id pLastSuperEvent = mpLastSuperEvent;
1737                 mpLastSuperEvent = mpLastEvent;
1738                 [NSApp performSelector:@selector(sendSuperEvent:) withObject: mpLastEvent];
1739                 mpLastSuperEvent = pLastSuperEvent;
1741                 std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent );
1742                 if( it != GetSalData()->maKeyEventAnswer.end() )
1743                     it->second = true;
1744             }
1745         }
1746     }
1749 -(BOOL)sendKeyInputAndReleaseToFrame: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar
1751     return [self sendKeyInputAndReleaseToFrame: nKeyCode character: aChar modifiers: mpFrame->mnLastModifierFlags];
1754 -(BOOL)sendKeyInputAndReleaseToFrame: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod
1756     return [self sendKeyToFrameDirect: nKeyCode character: aChar modifiers: nMod] ||
1757            [self sendSingleCharacter: mpLastEvent];
1760 -(BOOL)sendKeyToFrameDirect: (sal_uInt16)nKeyCode  character: (sal_Unicode)aChar modifiers: (unsigned int)nMod
1762     SolarMutexGuard aGuard;
1764     bool nRet = false;
1765     if( AquaSalFrame::isAlive( mpFrame ) )
1766     {
1767         SalKeyEvent aEvent;
1768         aEvent.mnCode           = nKeyCode | ImplGetModifierMask( nMod );
1769         aEvent.mnCharCode       = aChar;
1770         aEvent.mnRepeat         = FALSE;
1771         nRet = mpFrame->CallCallback( SalEvent::KeyInput, &aEvent );
1772         std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent );
1773         if( it != GetSalData()->maKeyEventAnswer.end() )
1774             it->second = nRet;
1775         if( AquaSalFrame::isAlive( mpFrame ) )
1776             mpFrame->CallCallback( SalEvent::KeyUp, &aEvent );
1777     }
1778     return nRet;
1782 -(BOOL)sendSingleCharacter: (NSEvent *)pEvent
1784     NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers];
1786     if( pUnmodifiedString && [pUnmodifiedString length] == 1 )
1787     {
1788         unichar keyChar = [pUnmodifiedString characterAtIndex: 0];
1789         sal_uInt16 nKeyCode = ImplMapCharCode( keyChar );
1790         if (nKeyCode == 0)
1791         {
1792             sal_uInt16 nOtherKeyCode = [pEvent keyCode];
1793             nKeyCode = ImplMapKeyCode(nOtherKeyCode);
1794         }
1795         if( nKeyCode != 0 )
1796         {
1797             // don't send code points in the private use area
1798             if( keyChar >= 0xf700 && keyChar < 0xf780 )
1799                 keyChar = 0;
1800             bool bRet = [self sendKeyToFrameDirect: nKeyCode character: keyChar modifiers: mpFrame->mnLastModifierFlags];
1801             mbInKeyInput = false;
1803             return bRet;
1804         }
1805     }
1806     return NO;
1810 // NSTextInput/NSTextInputClient protocol
1811 - (NSArray *)validAttributesForMarkedText
1813     return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, nil];
1816 - (BOOL)hasMarkedText
1818     bool bHasMarkedText;
1820     bHasMarkedText = ( mMarkedRange.location != NSNotFound ) &&
1821                      ( mMarkedRange.length != 0 );
1822     // hack to check keys like "Control-j"
1823     if( mbInKeyInput )
1824     {
1825         mbNeedSpecialKeyHandle = true;
1826     }
1828     // FIXME:
1829     // #i106901#
1830     // if we come here outside of mbInKeyInput, this is likely to be because
1831     // of the keyboard viewer. For unknown reasons having no marked range
1832     // in this case causes a crash. So we say we have a marked range anyway
1833     // This is a hack, since it is not understood what a) causes that crash
1834     // and b) why we should have a marked range at this point.
1835     if( ! mbInKeyInput )
1836         bHasMarkedText = true;
1838     return bHasMarkedText;
1841 - (NSRange)markedRange
1843     // FIXME:
1844     // #i106901#
1845     // if we come here outside of mbInKeyInput, this is likely to be because
1846     // of the keyboard viewer. For unknown reasons having no marked range
1847     // in this case causes a crash. So we say we have a marked range anyway
1848     // This is a hack, since it is not understood what a) causes that crash
1849     // and b) why we should have a marked range at this point. Stop the native
1850     // input method popup from appearing in the bottom left corner of the
1851     // screen by returning the marked range if is valid when called outside of
1852     // mbInKeyInput. If a zero length range is returned, macOS won't call
1853     // [self firstRectForCharacterRange:actualRange:] for any newly appended
1854     // uncommitted text.
1855     if( ! mbInKeyInput )
1856         return mMarkedRange.location != NSNotFound ? mMarkedRange : NSMakeRange( 0, 0 );
1858     return [self hasMarkedText] ? mMarkedRange : NSMakeRange( NSNotFound, 0 );
1861 - (NSRange)selectedRange
1863     // tdf#42437 Enable press-and-hold special character input method
1864     // Always return a valid range location. If the range location is
1865     // NSNotFound, -[NSResponder interpretKeyEvents:] will not call
1866     // [self firstRectForCharacterRange:actualRange:] and will not display the
1867     // special character input method popup.
1868     return ( mSelectedRange.location == NSNotFound ? NSMakeRange( 0, 0 ) : mSelectedRange );
1871 - (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange replacementRange:(NSRange)replacementRange
1873     (void) replacementRange; // FIXME - use it!
1875     SolarMutexGuard aGuard;
1877     [self deleteTextInputWantsNonRepeatKeyDown];
1879     if( ![aString isKindOfClass:[NSAttributedString class]] )
1880         aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
1882     // Reset cached state
1883     [self unmarkText];
1885     int len = [aString length];
1886     SalExtTextInputEvent aInputEvent;
1887     if( len > 0 ) {
1888         // Set the marked and selected ranges to the marked text and selected
1889         // range parameters
1890         mMarkedRange = NSMakeRange( 0, [aString length] );
1891         if (selRange.location == NSNotFound || selRange.location >= mMarkedRange.length)
1892              mSelectedRange = NSMakeRange( NSNotFound, 0 );
1893         else
1894              mSelectedRange = NSMakeRange( selRange.location, selRange.location + selRange.length > mMarkedRange.length ? mMarkedRange.length - selRange.location : selRange.length );
1896         // If we are going to post uncommitted text, cache the string parameter
1897         // as is needed in both [self endExtTextInput] and
1898         // [self attributedSubstringForProposedRange:actualRange:]
1899         mpLastMarkedText = [aString retain];
1901         NSString *pString = [aString string];
1902         OUString aInsertString( GetOUString( pString ) );
1903         std::vector<ExtTextInputAttr> aInputFlags( std::max( 1, len ), ExtTextInputAttr::NONE );
1904         int nSelectionStart = (mSelectedRange.location == NSNotFound ? len : mSelectedRange.location);
1905         int nSelectionEnd = (mSelectedRange.location == NSNotFound ? len : mSelectedRange.location + selRange.length);
1906         for ( int i = 0; i < len; i++ )
1907         {
1908             // Highlight all characters in the selected range. Normally
1909             // uncommitted text is underlined but when an item is selected in
1910             // the native input method popup or selecting a subblock of
1911             // uncommitted text using the left or right arrow keys, the
1912             // selection range is set and the selected range is either
1913             // highlighted like in Excel or is bold underlined like in
1914             // Safari. Highlighting the selected range was chosen because
1915             // using bold and double underlines can get clipped making the
1916             // selection range indistinguishable from the rest of the
1917             // uncommitted text.
1918             if (i >= nSelectionStart && i < nSelectionEnd)
1919             {
1920                 aInputFlags[i] = ExtTextInputAttr::Highlight;
1921                 continue;
1922             }
1924             unsigned int nUnderlineValue;
1925             NSRange effectiveRange;
1927             effectiveRange = NSMakeRange(i, 1);
1928             nUnderlineValue = [[aString attribute:NSUnderlineStyleAttributeName atIndex:i effectiveRange:&effectiveRange] unsignedIntValue];
1930             switch (nUnderlineValue & 0xff) {
1931             case NSUnderlineStyleSingle:
1932                 aInputFlags[i] = ExtTextInputAttr::Underline;
1933                 break;
1934             case NSUnderlineStyleThick:
1935                 aInputFlags[i] = ExtTextInputAttr::BoldUnderline;
1936                 break;
1937             case NSUnderlineStyleDouble:
1938                 aInputFlags[i] = ExtTextInputAttr::DoubleUnderline;
1939                 break;
1940             default:
1941                 aInputFlags[i] = ExtTextInputAttr::Highlight;
1942                 break;
1943             }
1944         }
1946         aInputEvent.maText = aInsertString;
1947         aInputEvent.mnCursorPos = nSelectionStart;
1948         aInputEvent.mnCursorFlags = 0;
1949         aInputEvent.mpTextAttr = aInputFlags.data();
1950         mpFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void *>(&aInputEvent) );
1951     } else {
1952         aInputEvent.maText.clear();
1953         aInputEvent.mnCursorPos = 0;
1954         aInputEvent.mnCursorFlags = 0;
1955         aInputEvent.mpTextAttr = nullptr;
1956         mpFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void *>(&aInputEvent) );
1957         mpFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
1958     }
1959     mbKeyHandled= true;
1962 - (void)unmarkText
1964     [self clearLastMarkedText];
1966     mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0);
1969 - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
1971     (void) aRange;
1972     (void) actualRange;
1974     // FIXME - Implement
1975     return nil;
1978 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
1980     (void)thePoint;
1981     // FIXME
1982     return 0;
1985 - (NSInteger)conversationIdentifier
1987     return reinterpret_cast<long>(self);
1990 - (void)doCommandBySelector:(SEL)aSelector
1992     if( AquaSalFrame::isAlive( mpFrame ) )
1993     {
1994         if( (mpFrame->mnICOptions & InputContextFlags::Text) &&
1995             aSelector != nullptr && [self respondsToSelector: aSelector] )
1996         {
1997             [self performSelector: aSelector];
1998         }
1999         else
2000         {
2001             [self sendSingleCharacter:mpLastEvent];
2002         }
2003     }
2005     mbKeyHandled = true;
2008 -(void)clearLastEvent
2010     if (mpLastEvent)
2011     {
2012         [mpLastEvent release];
2013         mpLastEvent = nil;
2014     }
2017 -(void)clearLastMarkedText
2019     if (mpLastMarkedText)
2020     {
2021         [mpLastMarkedText release];
2022         mpLastMarkedText = nil;
2023     }
2025     mbTextInputWantsNonRepeatKeyDown = NO;
2028 - (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
2030      // FIXME - These should probably be used?
2031     (void) aRange;
2032     (void) actualRange;
2034     SolarMutexGuard aGuard;
2036     // tdf#42437 Enable press-and-hold special character input method
2037     // Some text entry controls, such as Writer comments or the cell editor in
2038     // Calc's Formula Bar, need to have an input method session open or else
2039     // the returned position won't be anywhere near the text cursor. So,
2040     // dispatch an empty SalEvent::ExtTextInput event, fetch the position,
2041     // and then dispatch a SalEvent::EndExtTextInput event.
2042     NSString *pNewMarkedText = nullptr;
2043     NSString *pChars = [mpLastEvent characters];
2044     bool bNeedsExtTextInput = ( pChars && mbInKeyInput && !mpLastMarkedText && mpLastEvent && [mpLastEvent type] == NSEventTypeKeyDown && [mpLastEvent isARepeat] );
2045     if ( bNeedsExtTextInput )
2046     {
2047         // tdf#154708 Preserve selection for repeating Shift-arrow on Japanese keyboard
2048         // Skip the posting of SalEvent::ExtTextInput and
2049         // SalEvent::EndExtTextInput events for private use area characters.
2050         NSUInteger nLen = [pChars length];
2051         unichar pBuf[ nLen + 1 ];
2052         NSUInteger nBufLen = 0;
2053         for ( NSUInteger i = 0; i < nLen; i++ )
2054         {
2055             unichar aChar = [pChars characterAtIndex:i];
2056             if ( aChar >= 0xf700 && aChar < 0xf780 )
2057                 continue;
2059             pBuf[nBufLen++] = aChar;
2060         }
2061         pBuf[nBufLen] = 0;
2063         pNewMarkedText = [NSString stringWithCharacters:pBuf length:nBufLen];
2064         if (!pNewMarkedText || ![pNewMarkedText length])
2065             bNeedsExtTextInput = false;
2066     }
2068     if ( bNeedsExtTextInput )
2069     {
2070         SalExtTextInputEvent aInputEvent;
2071         aInputEvent.maText.clear();
2072         aInputEvent.mnCursorPos = 0;
2073         aInputEvent.mnCursorFlags = 0;
2074         aInputEvent.mpTextAttr = nullptr;
2075         if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
2076             mpFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void *>(&aInputEvent) );
2077     }
2079     SalExtTextInputPosEvent aPosEvent;
2080     if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
2081         mpFrame->CallCallback( SalEvent::ExtTextInputPos, static_cast<void *>(&aPosEvent) );
2083     if ( bNeedsExtTextInput )
2084     {
2085         if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
2086             mpFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
2088         // tdf#42437 Enable press-and-hold special character input method
2089         // Emulate the press-and-hold behavior of the TextEdit application by
2090         // setting the marked text to the last key down event's characters. The
2091         // characters will already have been committed by the special character
2092         // input method so set the mbTextInputWantsNonRepeatKeyDown flag to
2093         // indicate that the characters need to be deleted if the input method
2094         // replaces the committed characters.
2095         if ( pNewMarkedText )
2096         {
2097             [self unmarkText];
2098             mpLastMarkedText = [[NSAttributedString alloc] initWithString:pNewMarkedText];
2099             mSelectedRange = mMarkedRange = NSMakeRange( 0, [mpLastMarkedText length] );
2100             mbTextInputWantsNonRepeatKeyDown = YES;
2101         }
2102     }
2104     NSRect rect;
2106     if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
2107     {
2108         rect.origin.x = aPosEvent.mnX + mpFrame->maGeometry.x();
2109         rect.origin.y = aPosEvent.mnY + mpFrame->maGeometry.y() + 4; // add some space for underlines
2110         rect.size.width = aPosEvent.mnWidth;
2111         rect.size.height = aPosEvent.mnHeight;
2113         mpFrame->VCLToCocoa( rect );
2114     }
2115     else
2116     {
2117         rect = NSMakeRect( aPosEvent.mnX, aPosEvent.mnY, aPosEvent.mnWidth, aPosEvent.mnHeight );
2118     }
2120     return rect;
2123 -(id)parentAttribute {
2124     return reinterpret_cast<NSView*>(mpFrame->getNSWindow());
2125         //TODO: odd cast really needed for fdo#74121?
2128 -(css::accessibility::XAccessibleContext *)accessibleContext
2130     SolarMutexGuard aGuard;
2132     [self insertRegisteredWrapperIntoWrapperRepository];
2133     if (mpChildWrapper)
2134         return [mpChildWrapper accessibleContext];
2136     return nil;
2139 -(NSWindow*)windowForParent
2141     return mpFrame->getNSWindow();
2144 -(void)registerMouseEventListener: (id)theListener
2146   mpMouseEventListener = theListener;
2149 -(void)unregisterMouseEventListener: (id)theListener
2151     (void)theListener;
2152     mpMouseEventListener = nil;
2155 -(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
2157   return [mDraggingDestinationHandler draggingEntered: sender];
2160 -(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
2162   return [mDraggingDestinationHandler draggingUpdated: sender];
2165 -(void)draggingExited:(id <NSDraggingInfo>)sender
2167   [mDraggingDestinationHandler draggingExited: sender];
2170 -(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
2172   return [mDraggingDestinationHandler prepareForDragOperation: sender];
2175 -(BOOL)performDragOperation:(id <NSDraggingInfo>)sender
2177   return [mDraggingDestinationHandler performDragOperation: sender];
2180 -(void)concludeDragOperation:(id <NSDraggingInfo>)sender
2182   [mDraggingDestinationHandler concludeDragOperation: sender];
2185 -(void)registerDraggingDestinationHandler:(id)theHandler
2187   mDraggingDestinationHandler = theHandler;
2190 -(void)unregisterDraggingDestinationHandler:(id)theHandler
2192     (void)theHandler;
2193     mDraggingDestinationHandler = nil;
2196 -(void)endExtTextInput
2198     [self endExtTextInput:EndExtTextInputFlags::Complete];
2201 -(void)endExtTextInput:(EndExtTextInputFlags)nFlags
2203     // Prevent recursion from any additional [self insertText:] calls that
2204     // may be called when cancelling the native input method session
2205     if (mbInEndExtTextInput)
2206         return;
2208     mbInEndExtTextInput = YES;
2210     SolarMutexGuard aGuard;
2212     NSTextInputContext *pInputContext = [NSTextInputContext currentInputContext];
2213     if (pInputContext)
2214     {
2215         // Cancel the native input method session
2216         [pInputContext discardMarkedText];
2218         // Commit any uncommitted text. Note: when the delete key is used to
2219         // remove all uncommitted characters, the marked range will be zero
2220         // length but a SalEvent::EndExtTextInput must still be dispatched.
2221         if (mpLastMarkedText && [mpLastMarkedText length] && mMarkedRange.location != NSNotFound && mpFrame && AquaSalFrame::isAlive(mpFrame))
2222         {
2223             // If there is any marked text, SalEvent::EndExtTextInput may leave
2224             // the cursor hidden so commit the marked text to force the cursor
2225             // to be visible.
2226             mbInCommitMarkedText = YES;
2227             if (nFlags & EndExtTextInputFlags::Complete)
2228             {
2229                 // Retain the last marked text as it will be released in
2230                 // [self insertText:replacementText:]
2231                 NSAttributedString *pText = [mpLastMarkedText retain];
2232                 [self insertText:pText replacementRange:NSMakeRange(0, [mpLastMarkedText length])];
2233                 [pText release];
2234             }
2235             else
2236             {
2237                 [self insertText:[NSString string] replacementRange:NSMakeRange(0, 0)];
2238             }
2239             mbInCommitMarkedText = NO;
2240         }
2242         [self unmarkText];
2244         // If a different view is the input context's client, commit that
2245         // view's uncommitted text as well
2246         id<NSTextInputClient> pClient = [pInputContext client];
2247         if (pClient != self)
2248         {
2249             SalFrameView *pView = static_cast<SalFrameView*>(pClient);
2250             if ([pView isKindOfClass:[SalFrameView class]])
2251                 [pView endExtTextInput];
2252             else
2253                 [pClient unmarkText];
2254         }
2255     }
2257     mbInEndExtTextInput = NO;
2260 -(void)deleteTextInputWantsNonRepeatKeyDown
2262     SolarMutexGuard aGuard;
2264     // tdf#42437 Enable press-and-hold special character input method
2265     // Emulate the press-and-hold behavior of the TextEdit application by
2266     // dispatching backspace events to delete any marked characters. The
2267     // special character input method commits the marked characters so we must
2268     // delete the marked characters before the input method calls
2269     // [self insertText:replacementRange:].
2270     if (mbTextInputWantsNonRepeatKeyDown)
2271     {
2272         if ( mpLastMarkedText )
2273         {
2274             NSString *pChars = [mpLastMarkedText string];
2275             if ( pChars )
2276             {
2277                 NSUInteger nLength = [pChars length];
2278                 for ( NSUInteger i = 0; i < nLength; i++ )
2279                     [self deleteBackward:self];
2280             }
2281         }
2283         [self unmarkText];
2284     }
2287 -(void)insertRegisteredWrapperIntoWrapperRepository
2289     SolarMutexGuard aGuard;
2291     if (!mbNeedChildWrapper)
2292         return;
2294     vcl::Window *pWindow = mpFrame->GetWindow();
2295     if (!pWindow)
2296         return;
2298     mbNeedChildWrapper = NO;
2300     ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext > xAccessibleContext( pWindow->GetAccessible()->getAccessibleContext() );
2301     assert(!mpChildWrapper);
2302     mpChildWrapper = [[SalFrameViewA11yWrapper alloc] initWithParent:self accessibleContext:xAccessibleContext];
2303     [AquaA11yFactory insertIntoWrapperRepository:mpChildWrapper forAccessibleContext:xAccessibleContext];
2306 -(void)registerWrapper
2308     [self revokeWrapper];
2310     mbNeedChildWrapper = YES;
2313 -(void)revokeWrapper
2315     mbNeedChildWrapper = NO;
2317     if (mpChildWrapper)
2318     {
2319         [AquaA11yFactory revokeWrapper:mpChildWrapper];
2320         [mpChildWrapper setAccessibilityParent:nil];
2321         [mpChildWrapper release];
2322         mpChildWrapper = nil;
2323     }
2326 -(id)accessibilityAttributeValue:(NSString *)pAttribute
2328     SolarMutexGuard aGuard;
2330     [self insertRegisteredWrapperIntoWrapperRepository];
2331     if (mpChildWrapper)
2332         return [mpChildWrapper accessibilityAttributeValue:pAttribute];
2333     else
2334         return nil;
2337 -(BOOL)accessibilityIsIgnored
2339     SolarMutexGuard aGuard;
2341     [self insertRegisteredWrapperIntoWrapperRepository];
2342     if (mpChildWrapper)
2343         return [mpChildWrapper accessibilityIsIgnored];
2344     else
2345         return YES;
2348 -(NSArray *)accessibilityAttributeNames
2350     SolarMutexGuard aGuard;
2352     [self insertRegisteredWrapperIntoWrapperRepository];
2353     if (mpChildWrapper)
2354         return [mpChildWrapper accessibilityAttributeNames];
2355     else
2356         return [NSArray array];
2359 -(BOOL)accessibilityIsAttributeSettable:(NSString *)pAttribute
2361     SolarMutexGuard aGuard;
2363     [self insertRegisteredWrapperIntoWrapperRepository];
2364     if (mpChildWrapper)
2365         return [mpChildWrapper accessibilityIsAttributeSettable:pAttribute];
2366     else
2367         return NO;
2370 -(NSArray *)accessibilityParameterizedAttributeNames
2372     SolarMutexGuard aGuard;
2374     [self insertRegisteredWrapperIntoWrapperRepository];
2375     if (mpChildWrapper)
2376         return [mpChildWrapper accessibilityParameterizedAttributeNames];
2377     else
2378         return [NSArray array];
2381 -(BOOL)accessibilitySetOverrideValue:(id)pValue forAttribute:(NSString *)pAttribute
2383     SolarMutexGuard aGuard;
2385     [self insertRegisteredWrapperIntoWrapperRepository];
2386     if (mpChildWrapper)
2387         return [mpChildWrapper accessibilitySetOverrideValue:pValue forAttribute:pAttribute];
2388     else
2389         return NO;
2392 -(void)accessibilitySetValue:(id)pValue forAttribute:(NSString *)pAttribute
2394     SolarMutexGuard aGuard;
2396     [self insertRegisteredWrapperIntoWrapperRepository];
2397     if (mpChildWrapper)
2398         [mpChildWrapper accessibilitySetValue:pValue forAttribute:pAttribute];
2401 -(id)accessibilityAttributeValue:(NSString *)pAttribute forParameter:(id)pParameter
2403     SolarMutexGuard aGuard;
2405     [self insertRegisteredWrapperIntoWrapperRepository];
2406     if (mpChildWrapper)
2407         return [mpChildWrapper accessibilityAttributeValue:pAttribute forParameter:pParameter];
2408     else
2409         return nil;
2412 -(id)accessibilityFocusedUIElement
2414     SolarMutexGuard aGuard;
2416     [self insertRegisteredWrapperIntoWrapperRepository];
2417     if (mpChildWrapper)
2418         return [mpChildWrapper accessibilityFocusedUIElement];
2419     else
2420         return nil;
2423 -(NSString *)accessibilityActionDescription:(NSString *)pAction
2425     SolarMutexGuard aGuard;
2427     [self insertRegisteredWrapperIntoWrapperRepository];
2428     if (mpChildWrapper)
2429         return [mpChildWrapper accessibilityActionDescription:pAction];
2430     else
2431         return nil;
2434 -(void)accessibilityPerformAction:(NSString *)pAction
2436     SolarMutexGuard aGuard;
2438     [self insertRegisteredWrapperIntoWrapperRepository];
2439     if (mpChildWrapper)
2440         [mpChildWrapper accessibilityPerformAction:pAction];
2443 -(NSArray *)accessibilityActionNames
2445     SolarMutexGuard aGuard;
2447     [self insertRegisteredWrapperIntoWrapperRepository];
2448     if (mpChildWrapper)
2449         return [mpChildWrapper accessibilityActionNames];
2450     else
2451         return [NSArray array];
2454 -(id)accessibilityHitTest:(NSPoint)aPoint
2456     SolarMutexGuard aGuard;
2458     [self insertRegisteredWrapperIntoWrapperRepository];
2459     if (mpChildWrapper)
2460         return [mpChildWrapper accessibilityHitTest:aPoint];
2461     else
2462         return nil;
2465 -(id)accessibilityParent
2467     return [self window];
2470 -(NSArray *)accessibilityVisibleChildren
2472     return [self accessibilityChildren];
2475 -(NSArray *)accessibilitySelectedChildren
2477     SolarMutexGuard aGuard;
2479     NSArray *pRet = [super accessibilityChildren];
2481     [self insertRegisteredWrapperIntoWrapperRepository];
2482     if (mpChildWrapper)
2483         pRet = getMergedAccessibilityChildren(pRet, [mpChildWrapper accessibilitySelectedChildren]);
2485     return pRet;
2488 -(NSArray *)accessibilityChildren
2490     SolarMutexGuard aGuard;
2492     NSArray *pRet = [super accessibilityChildren];
2494     [self insertRegisteredWrapperIntoWrapperRepository];
2495     if (mpChildWrapper)
2496         pRet = getMergedAccessibilityChildren(pRet, [mpChildWrapper accessibilityChildren]);
2498     return pRet;
2501 -(NSArray <id<NSAccessibilityElement>> *)accessibilityChildrenInNavigationOrder
2503     return [self accessibilityChildren];
2506 @end
2508 @implementation SalFrameViewA11yWrapper
2510 -(id)initWithParent:(SalFrameView *)pParentView accessibleContext:(::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >&)rxAccessibleContext
2512     [super init];
2514     maReferenceWrapper.rAccessibleContext = rxAccessibleContext;
2516     mpParentView = pParentView;
2517     if (mpParentView)
2518     {
2519         [mpParentView retain];
2520         [self setAccessibilityParent:mpParentView];
2521     }
2523     return self;
2526 -(void)dealloc
2528     if (mpParentView)
2529         [mpParentView release];
2531     [super dealloc];
2534 -(id)parentAttribute
2536     if (mpParentView)
2537         return NSAccessibilityUnignoredAncestor(mpParentView);
2538     else
2539         return nil;
2542 -(void)setAccessibilityParent:(id)pObject
2544     if (mpParentView)
2545     {
2546         [mpParentView release];
2547         mpParentView = nil;
2548     }
2550     if (pObject && [pObject isKindOfClass:[SalFrameView class]])
2551     {
2552         mpParentView = (SalFrameView *)pObject;
2553         [mpParentView retain];
2554     }
2556     [super setAccessibilityParent:mpParentView];
2559 -(id)windowAttribute
2561     if (mpParentView)
2562         return [mpParentView window];
2563     else
2564         return nil;
2567 -(NSWindow *)windowForParent
2569     return [self windowAttribute];
2572 @end
2574 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */