xref: /core/vcl/osx/salframeview.mm (revision ea9904c8)
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 *   Licensed to the Apache Software Foundation (ASF) under one or more
12 *   contributor license agreements. See the NOTICE file distributed
13 *   with this work for additional information regarding copyright
14 *   ownership. The ASF licenses this file to you under the Apache
15 *   License, Version 2.0 (the "License"); you may not use this file
16 *   except in compliance with the License. You may obtain a copy of
17 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include <memory>
23
24#include <basegfx/numeric/ftools.hxx>
25#include <sal/macros.h>
26#include <tools/helpers.hxx>
27#include <tools/long.hxx>
28#include <vcl/event.hxx>
29#include <vcl/inputctx.hxx>
30#include <vcl/settings.hxx>
31#include <vcl/svapp.hxx>
32#include <vcl/window.hxx>
33#include <vcl/commandevent.hxx>
34
35#include <osx/a11yfactory.h>
36#include <osx/salframe.h>
37#include <osx/salframeview.h>
38#include <osx/salinst.h>
39#include <quartz/salgdi.h>
40#include <quartz/utils.h>
41
42#if HAVE_FEATURE_SKIA
43#include <vcl/skia/SkiaHelper.hxx>
44#endif
45
46#define WHEEL_EVENT_FACTOR 1.5
47
48static sal_uInt16 ImplGetModifierMask( unsigned int nMask )
49{
50    sal_uInt16 nRet = 0;
51    if( (nMask & NSEventModifierFlagShift) != 0 )
52        nRet |= KEY_SHIFT;
53    if( (nMask & NSEventModifierFlagControl) != 0 )
54        nRet |= KEY_MOD3;
55    if( (nMask & NSEventModifierFlagOption) != 0 )
56        nRet |= KEY_MOD2;
57    if( (nMask & NSEventModifierFlagCommand) != 0 )
58        nRet |= KEY_MOD1;
59    return nRet;
60}
61
62static sal_uInt16 ImplMapCharCode( sal_Unicode aCode )
63{
64    static sal_uInt16 aKeyCodeMap[ 128 ] =
65    {
66                    0,                0,                0,                0,                0,                0,                0,                0,
67        KEY_BACKSPACE,          KEY_TAB,       KEY_RETURN,                0,                0,       KEY_RETURN,                0,                0,
68                    0,                0,                0,                0,                0,                0,                0,                0,
69                    0,          KEY_TAB,                0,       KEY_ESCAPE,                0,                0,                0,                0,
70            KEY_SPACE,                0,                0,                0,                0,                0,                0,                0,
71                    0,                0,     KEY_MULTIPLY,          KEY_ADD,        KEY_COMMA,     KEY_SUBTRACT,        KEY_POINT,       KEY_DIVIDE,
72                KEY_0,            KEY_1,            KEY_2,            KEY_3,            KEY_4,            KEY_5,            KEY_6,            KEY_7,
73                KEY_8,            KEY_9,                0,                0,         KEY_LESS,        KEY_EQUAL,      KEY_GREATER,                0,
74                    0,            KEY_A,            KEY_B,            KEY_C,            KEY_D,            KEY_E,            KEY_F,            KEY_G,
75                KEY_H,            KEY_I,            KEY_J,            KEY_K,            KEY_L,            KEY_M,            KEY_N,            KEY_O,
76                KEY_P,            KEY_Q,            KEY_R,            KEY_S,            KEY_T,            KEY_U,            KEY_V,            KEY_W,
77                KEY_X,            KEY_Y,            KEY_Z,                0,                0,                0,                0,                0,
78        KEY_QUOTELEFT,            KEY_A,            KEY_B,            KEY_C,            KEY_D,            KEY_E,            KEY_F,            KEY_G,
79                KEY_H,            KEY_I,            KEY_J,            KEY_K,            KEY_L,            KEY_M,            KEY_N,            KEY_O,
80                KEY_P,            KEY_Q,            KEY_R,            KEY_S,            KEY_T,            KEY_U,            KEY_V,            KEY_W,
81                KEY_X,            KEY_Y,            KEY_Z,                0,                0,                0,        KEY_TILDE,    KEY_BACKSPACE
82    };
83
84    // Note: the mapping 0x7f should by rights be KEY_DELETE
85    // however if you press "backspace" 0x7f is reported
86    // whereas for "delete" 0xf728 gets reported
87
88    // Note: the mapping of 0x19 to KEY_TAB is because for unknown reasons
89    // tab alone is reported as 0x09 (as expected) but shift-tab is
90    // reported as 0x19 (end of medium)
91
92    static sal_uInt16 aFunctionKeyCodeMap[ 128 ] =
93    {
94            KEY_UP,         KEY_DOWN,         KEY_LEFT,        KEY_RIGHT,           KEY_F1,           KEY_F2,           KEY_F3,           KEY_F4,
95            KEY_F5,           KEY_F6,           KEY_F7,           KEY_F8,           KEY_F9,          KEY_F10,          KEY_F11,          KEY_F12,
96           KEY_F13,          KEY_F14,          KEY_F15,          KEY_F16,          KEY_F17,          KEY_F18,          KEY_F19,          KEY_F20,
97           KEY_F21,          KEY_F22,          KEY_F23,          KEY_F24,          KEY_F25,          KEY_F26,                0,                0,
98                 0,                0,                0,                0,                0,                0,                0,       KEY_INSERT,
99        KEY_DELETE,         KEY_HOME,                0,          KEY_END,        KEY_PAGEUP,    KEY_PAGEDOWN,                0,                0,
100                 0,                0,                0,                0,                 0,        KEY_MENU,                0,                0,
101                 0,                0,                0,                0,                 0,               0,                0,                0,
102                 0,                0,                0,         KEY_UNDO,        KEY_REPEAT,        KEY_FIND,         KEY_HELP,                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                 0,                0,                0,                0,                 0,               0,                0,                0,
108                 0,                0,                0,                0,                 0,               0,                0,                0,
109                 0,                0,                0,                0,                 0,               0,                0,                0
110    };
111
112    sal_uInt16 nKeyCode = 0;
113    if( aCode < SAL_N_ELEMENTS( aKeyCodeMap)  )
114        nKeyCode = aKeyCodeMap[ aCode ];
115    else if( aCode >= 0xf700 && aCode < 0xf780 )
116        nKeyCode = aFunctionKeyCodeMap[ aCode - 0xf700 ];
117    return nKeyCode;
118}
119
120static sal_uInt16 ImplMapKeyCode(sal_uInt16 nKeyCode)
121{
122    /*
123      http://stackoverflow.com/questions/2080312/where-can-i-find-a-list-of-key-codes-for-use-with-cocoas-nsevent-class/2080324#2080324
124      /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
125     */
126
127    static sal_uInt16 aKeyCodeMap[ 0x80 ] =
128    {
129            KEY_A,            KEY_S,            KEY_D,            KEY_F,            KEY_H,            KEY_G,            KEY_Z,            KEY_X,
130            KEY_C,            KEY_V,                0,            KEY_B,            KEY_Q,            KEY_W,            KEY_E,            KEY_R,
131            KEY_Y,            KEY_T,            KEY_1,            KEY_2,            KEY_3,            KEY_4,            KEY_6,            KEY_5,
132        KEY_EQUAL,            KEY_9,            KEY_7,     KEY_SUBTRACT,            KEY_8,            KEY_0, KEY_BRACKETRIGHT, KEY_RIGHTCURLYBRACKET,
133            KEY_U,  KEY_BRACKETLEFT,            KEY_I,            KEY_P,       KEY_RETURN,            KEY_L,            KEY_J,   KEY_QUOTERIGHT,
134            KEY_K,    KEY_SEMICOLON,                0,        KEY_COMMA,       KEY_DIVIDE,            KEY_N,            KEY_M,        KEY_POINT,
135          KEY_TAB,        KEY_SPACE,    KEY_QUOTELEFT,       KEY_DELETE,                0,       KEY_ESCAPE,                0,                0,
136                0,     KEY_CAPSLOCK,                0,                0,                0,                0,                0,                0,
137          KEY_F17,      KEY_DECIMAL,                0,     KEY_MULTIPLY,                0,          KEY_ADD,                0,                0,
138                0,                0,                0,       KEY_DIVIDE,       KEY_RETURN,                0,     KEY_SUBTRACT,          KEY_F18,
139          KEY_F19,        KEY_EQUAL,                0,                0,                0,                0,                0,                0,
140                0,                0,          KEY_F20,                0,                0,                0,                0,                0,
141           KEY_F5,           KEY_F6,           KEY_F7,           KEY_F3,           KEY_F8,           KEY_F9,                0,          KEY_F11,
142                0,          KEY_F13,          KEY_F16,          KEY_F14,                0,          KEY_F10,                0,          KEY_F12,
143                0,          KEY_F15,         KEY_HELP,         KEY_HOME,       KEY_PAGEUP,       KEY_DELETE,           KEY_F4,          KEY_END,
144           KEY_F2,     KEY_PAGEDOWN,           KEY_F1,         KEY_LEFT,        KEY_RIGHT,         KEY_DOWN,           KEY_UP,                0
145    };
146
147    if (nKeyCode < SAL_N_ELEMENTS(aKeyCodeMap))
148        return aKeyCodeMap[nKeyCode];
149    return 0;
150}
151
152// store the frame the mouse last entered
153static AquaSalFrame* s_pMouseFrame = nullptr;
154// store the last pressed button for enter/exit events
155// which lack that information
156static sal_uInt16 s_nLastButton = 0;
157
158static AquaSalFrame* getMouseContainerFrame()
159{
160    AquaSalFrame* pDispatchFrame = nullptr;
161    NSArray* aWindows = [NSWindow windowNumbersWithOptions:0];
162    for(NSUInteger i = 0; i < [aWindows count] && ! pDispatchFrame; i++ )
163    {
164        NSWindow* pWin = [NSApp windowWithWindowNumber:[[aWindows objectAtIndex:i] integerValue]];
165        if( pWin && [pWin isMemberOfClass: [SalFrameWindow class]] && [static_cast<SalFrameWindow*>(pWin) containsMouse] )
166            pDispatchFrame = [static_cast<SalFrameWindow*>(pWin) getSalFrame];
167    }
168    return pDispatchFrame;
169}
170
171static NSArray *getMergedAccessibilityChildren(NSArray *pDefaultChildren, NSArray *pUnignoredChildrenToAdd)
172{
173    NSArray *pRet = pDefaultChildren;
174
175    if (pUnignoredChildrenToAdd && [pUnignoredChildrenToAdd count])
176    {
177        NSMutableArray *pNewChildren = [NSMutableArray arrayWithCapacity:(pRet ? [pRet count] : 0) + 1];
178        if (pNewChildren)
179        {
180            if (pRet)
181                [pNewChildren addObjectsFromArray:pRet];
182
183            for (AquaA11yWrapper *pWrapper : pUnignoredChildrenToAdd)
184            {
185                if (pWrapper && ![pNewChildren containsObject:pWrapper])
186                    [pNewChildren addObject:pWrapper];
187            }
188
189            pRet = pNewChildren;
190        }
191        else
192        {
193            pRet = pUnignoredChildrenToAdd;
194        }
195    }
196
197    return pRet;
198}
199
200// Update ImplGetSVData()->mpWinData->mbIsLiveResize
201static void updateWinDataInLiveResize(bool bInLiveResize)
202{
203    ImplSVData* pSVData = ImplGetSVData();
204    assert( pSVData );
205    if ( pSVData )
206    {
207        if ( pSVData->mpWinData->mbIsLiveResize != bInLiveResize )
208        {
209            pSVData->mpWinData->mbIsLiveResize = bInLiveResize;
210            Scheduler::Wakeup();
211        }
212    }
213}
214
215@interface NSResponder (SalFrameWindow)
216-(BOOL)accessibilityIsIgnored;
217@end
218
219@implementation SalFrameWindow
220-(id)initWithSalFrame: (AquaSalFrame*)pFrame
221{
222    mDraggingDestinationHandler = nil;
223    mbInWindowDidResize = NO;
224    mpLiveResizeTimer = nil;
225    mpFrame = pFrame;
226    NSRect aRect = { { static_cast<CGFloat>(pFrame->maGeometry.x()), static_cast<CGFloat>(pFrame->maGeometry.y()) },
227                     { static_cast<CGFloat>(pFrame->maGeometry.width()), static_cast<CGFloat>(pFrame->maGeometry.height()) } };
228    pFrame->VCLToCocoa( aRect );
229    NSWindow* pNSWindow = [super initWithContentRect: aRect
230                                 styleMask: mpFrame->getStyleMask()
231                                 backing: NSBackingStoreBuffered
232                                 defer: Application::IsHeadlessModeEnabled()];
233
234    // Disallow full-screen mode on macOS >= 10.11 where it is enabled by default. We don't want it
235    // for now as it will just be confused with LibreOffice's home-grown full-screen concept, with
236    // which it has nothing to do, and one can get into all kinds of weird states by using them
237    // intermixedly.
238
239    // Ideally we should use the system full-screen mode and adapt the code for the home-grown thing
240    // to be in sync with that instead. (And we would then not need the button to get out of
241    // full-screen mode, as the normal way to get out of it is to either click on the green bubble
242    // again, or invoke the keyboard command again.)
243
244    // (Confusingly, at the moment the home-grown full-screen mode is bound to Cmd+Shift+F, which is
245    // the keyboard command normally used in apps to get in and out of the system full-screen mode.)
246
247    // Disabling system full-screen mode makes the green button on the title bar (on macOS >= 10.11)
248    // show a plus sign instead, and clicking it becomes identical to double-clicking the title bar,
249    // i.e. it maximizes / unmaximises the window. Sure, that state can also be confused with LO's
250    // home-grown full-screen mode. Oh well.
251
252    [pNSWindow setCollectionBehavior: NSWindowCollectionBehaviorFullScreenNone];
253
254    // Disable window restoration until we support it directly
255    [pNSWindow setRestorable: NO];
256
257    // tdf#137468: Restrict to 24-bit RGB as that is all that we can
258    // handle anyway. HDR is far off in the future for LibreOffice.
259    [pNSWindow setDynamicDepthLimit: NO];
260    [pNSWindow setDepthLimit: NSWindowDepthTwentyfourBitRGB];
261
262    return static_cast<SalFrameWindow *>(pNSWindow);
263}
264
265-(void)clearLiveResizeTimer
266{
267    if ( mpLiveResizeTimer )
268    {
269        [mpLiveResizeTimer invalidate];
270        [mpLiveResizeTimer release];
271        mpLiveResizeTimer = nil;
272    }
273}
274
275-(void)dealloc
276{
277    [self clearLiveResizeTimer];
278    [super dealloc];
279}
280
281-(AquaSalFrame*)getSalFrame
282{
283    return mpFrame;
284}
285
286-(void)displayIfNeeded
287{
288    if( GetSalData() && GetSalData()->mpInstance )
289    {
290        SolarMutexGuard aGuard;
291        [super displayIfNeeded];
292    }
293}
294
295-(BOOL)containsMouse
296{
297    // is this event actually inside that NSWindow ?
298    NSPoint aPt = [NSEvent mouseLocation];
299    NSRect aFrameRect = [self frame];
300    bool bInRect = NSPointInRect( aPt, aFrameRect );
301    return bInRect;
302}
303
304-(BOOL)canBecomeKeyWindow
305{
306    if( (mpFrame->mnStyle &
307            ( SalFrameStyleFlags::FLOAT                 |
308              SalFrameStyleFlags::TOOLTIP               |
309              SalFrameStyleFlags::INTRO
310            )) == SalFrameStyleFlags::NONE )
311        return YES;
312    if( mpFrame->mnStyle & SalFrameStyleFlags::OWNERDRAWDECORATION )
313        return YES;
314    if( mpFrame->mbFullScreen )
315        return YES;
316    return [super canBecomeKeyWindow];
317}
318
319-(void)windowDidBecomeKey: (NSNotification*)pNotification
320{
321    (void)pNotification;
322    SolarMutexGuard aGuard;
323
324    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
325    {
326        static const SalFrameStyleFlags nGuessDocument = SalFrameStyleFlags::MOVEABLE|
327                                            SalFrameStyleFlags::SIZEABLE|
328                                            SalFrameStyleFlags::CLOSEABLE;
329
330        // Reset dark mode colors in HITheme controls after printing
331        // In dark mode, after an NSPrintOperation has completed, macOS draws
332        // HITheme controls with light mode colors so reset all dark mode
333        // colors when an NSWindow gains focus.
334        mpFrame->UpdateDarkMode();
335
336        if( mpFrame->mpMenu )
337            mpFrame->mpMenu->setMainMenu();
338        else if( ! mpFrame->mpParent &&
339                 ( (mpFrame->mnStyle & nGuessDocument) == nGuessDocument || // set default menu for e.g. help
340                    mpFrame->mbFullScreen ) )                               // set default menu for e.g. presentation
341        {
342            AquaSalMenu::setDefaultMenu();
343        }
344        mpFrame->CallCallback( SalEvent::GetFocus, nullptr );
345        mpFrame->SendPaintEvent(); // repaint controls as active
346    }
347
348    // Prevent the same native input method popup that was cancelled in a
349    // previous call to [self windowDidResignKey:] from reappearing
350    [self endExtTextInput];
351}
352
353-(void)windowDidResignKey: (NSNotification*)pNotification
354{
355    (void)pNotification;
356    SolarMutexGuard aGuard;
357
358    // Commit any uncommitted text and cancel the native input method session
359    // whenever a window loses focus like in Safari, Firefox, and Excel
360    [self endExtTextInput];
361
362    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
363    {
364        mpFrame->CallCallback(SalEvent::LoseFocus, nullptr);
365        mpFrame->SendPaintEvent(); // repaint controls as inactive
366    }
367}
368
369-(void)windowDidChangeScreen: (NSNotification*)pNotification
370{
371    (void)pNotification;
372    SolarMutexGuard aGuard;
373
374    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
375        mpFrame->screenParametersChanged();
376}
377
378-(void)windowDidMove: (NSNotification*)pNotification
379{
380    (void)pNotification;
381    SolarMutexGuard aGuard;
382
383    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
384    {
385        mpFrame->UpdateFrameGeometry();
386        mpFrame->CallCallback( SalEvent::Move, nullptr );
387    }
388}
389
390-(void)windowDidResize: (NSNotification*)pNotification
391{
392    SolarMutexGuard aGuard;
393
394    if ( mbInWindowDidResize )
395        return;
396
397    mbInWindowDidResize = YES;
398
399    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
400    {
401        mpFrame->UpdateFrameGeometry();
402        mpFrame->CallCallback( SalEvent::Resize, nullptr );
403
404        updateWinDataInLiveResize( [self inLiveResize] );
405        if ( ImplGetSVData()->mpWinData->mbIsLiveResize )
406        {
407#if HAVE_FEATURE_SKIA
408            // Related: tdf#152703 Eliminate empty window with Skia/Metal while resizing
409            // The window will clear its background so when Skia/Metal is
410            // enabled, explicitly flush the Skia graphics to the window
411            // during live resizing or else nothing will be drawn until after
412            // live resizing has ended.
413            // Also, flushing during [self windowDidResize:] eliminates flicker
414            // by forcing this window's SkSurface to recreate its underlying
415            // CAMetalLayer with the new size. Flushing in
416            // [self displayIfNeeded] does not eliminate flicker so apparently
417            // [self windowDidResize:] is called earlier.
418            if ( SkiaHelper::isVCLSkiaEnabled() )
419            {
420                AquaSalGraphics* pGraphics = mpFrame->mpGraphics;
421                if ( pGraphics )
422                    pGraphics->Flush();
423            }
424#endif
425
426            // tdf#152703 Force relayout during live resizing of window
427            // During a live resize, macOS floods the application with
428            // windowDidResize: notifications so sending a paint event does
429            // not trigger redrawing with the new size.
430            // Instead, force relayout by dispatching all pending internal
431            // events and firing any pending timers.
432            // Also, Application::Reschedule() can potentially display a
433            // modal dialog which will cause a hang so temporarily disable
434            // live resize by clamping the window's minimum and maximum sizes
435            // to the current frame size which in Application::Reschedule().
436            NSRect aFrame = [self frame];
437            NSSize aMinSize = [self minSize];
438            NSSize aMaxSize = [self maxSize];
439            [self setMinSize:aFrame.size];
440            [self setMaxSize:aFrame.size];
441            Application::Reschedule( true );
442            [self setMinSize:aMinSize];
443            [self setMaxSize:aMaxSize];
444
445            if ( ImplGetSVData()->mpWinData->mbIsLiveResize )
446            {
447                // tdf#152703 Force repaint after live resizing ends
448                // Repost this notification so that this selector will be called
449                // at least once after live resizing ends
450                if ( !mpLiveResizeTimer )
451                {
452                    mpLiveResizeTimer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(windowDidResizeWithTimer:) userInfo:pNotification repeats:YES];
453                    if ( mpLiveResizeTimer )
454                    {
455                        [mpLiveResizeTimer retain];
456
457                        // The timer won't fire without a call to
458                        // Application::Reschedule() unless we copy the fix for
459                        // #i84055# from vcl/osx/saltimer.cxx and add the timer
460                        // to the NSEventTrackingRunLoopMode run loop mode
461                        [[NSRunLoop currentRunLoop] addTimer:mpLiveResizeTimer forMode:NSEventTrackingRunLoopMode];
462                    }
463                }
464            }
465        }
466        else
467        {
468            [self clearLiveResizeTimer];
469        }
470
471        // tdf#158461 eliminate flicker during live resizing
472        // When using Skia/Metal, the window content will flicker while
473        // live resizing a window if we don't send a paint event.
474        mpFrame->SendPaintEvent();
475    }
476
477    mbInWindowDidResize = NO;
478}
479
480-(void)windowDidMiniaturize: (NSNotification*)pNotification
481{
482    (void)pNotification;
483    SolarMutexGuard aGuard;
484
485    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
486    {
487        mpFrame->mbShown = false;
488        mpFrame->UpdateFrameGeometry();
489        mpFrame->CallCallback( SalEvent::Resize, nullptr );
490    }
491}
492
493-(void)windowDidDeminiaturize: (NSNotification*)pNotification
494{
495    (void)pNotification;
496    SolarMutexGuard aGuard;
497
498    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
499    {
500        mpFrame->mbShown = true;
501        mpFrame->UpdateFrameGeometry();
502        mpFrame->CallCallback( SalEvent::Resize, nullptr );
503    }
504}
505
506-(BOOL)windowShouldClose: (NSNotification*)pNotification
507{
508    (void)pNotification;
509    SolarMutexGuard aGuard;
510
511    bool bRet = true;
512    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
513    {
514        // #i84461# end possible input
515        [self endExtTextInput];
516        if( AquaSalFrame::isAlive( mpFrame ) )
517        {
518            mpFrame->CallCallback( SalEvent::Close, nullptr );
519            bRet = false; // application will close the window or not, AppKit shouldn't
520            AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
521            assert( pTimer );
522            pTimer->handleWindowShouldClose();
523        }
524    }
525
526    return bRet;
527}
528
529-(void)windowDidEnterFullScreen: (NSNotification*)pNotification
530{
531    SolarMutexGuard aGuard;
532
533    if( !mpFrame || !AquaSalFrame::isAlive( mpFrame))
534        return;
535    mpFrame->mbFullScreen = true;
536    (void)pNotification;
537}
538
539-(void)windowDidExitFullScreen: (NSNotification*)pNotification
540{
541    SolarMutexGuard aGuard;
542
543    if( !mpFrame || !AquaSalFrame::isAlive( mpFrame))
544        return;
545    mpFrame->mbFullScreen = false;
546    (void)pNotification;
547}
548
549-(void)windowDidChangeBackingProperties:(NSNotification *)pNotification
550{
551    (void)pNotification;
552#if HAVE_FEATURE_SKIA
553    SolarMutexGuard aGuard;
554
555    sal::aqua::resetWindowScaling();
556
557    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
558    {
559        // tdf#147342 Notify Skia that the window's backing properties changed
560        if ( SkiaHelper::isVCLSkiaEnabled() )
561        {
562            AquaSalGraphics* pGraphics = mpFrame->mpGraphics;
563            if ( pGraphics )
564                pGraphics->WindowBackingPropertiesChanged();
565        }
566    }
567#endif
568}
569
570-(void)windowWillStartLiveResize:(NSNotification *)pNotification
571{
572    SolarMutexGuard aGuard;
573
574    updateWinDataInLiveResize(true);
575}
576
577-(void)windowDidEndLiveResize:(NSNotification *)pNotification
578{
579    SolarMutexGuard aGuard;
580
581    updateWinDataInLiveResize(false);
582}
583
584-(void)dockMenuItemTriggered: (id)sender
585{
586    (void)sender;
587    SolarMutexGuard aGuard;
588
589    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
590        mpFrame->ToTop( SalFrameToTop::RestoreWhenMin | SalFrameToTop::GrabFocus );
591}
592
593-(css::uno::Reference < css::accessibility::XAccessibleContext >)accessibleContext
594{
595    return mpFrame -> GetWindow() -> GetAccessible() -> getAccessibleContext();
596}
597
598-(BOOL)isIgnoredWindow
599{
600    SolarMutexGuard aGuard;
601
602    // Treat tooltip windows as ignored
603    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
604        return (mpFrame->mnStyle & SalFrameStyleFlags::TOOLTIP) != SalFrameStyleFlags::NONE;
605    return YES;
606}
607
608-(id)accessibilityApplicationFocusedUIElement
609{
610    return [self accessibilityFocusedUIElement];
611}
612
613-(id)accessibilityFocusedUIElement
614{
615    // Treat tooltip windows as ignored
616    if ([self isIgnoredWindow])
617        return nil;
618
619    return [super accessibilityFocusedUIElement];
620}
621
622-(BOOL)accessibilityIsIgnored
623{
624    // Treat tooltip windows as ignored
625    if ([self isIgnoredWindow])
626        return YES;
627
628    return [super accessibilityIsIgnored];
629}
630
631-(BOOL)isAccessibilityElement
632{
633    return ![self accessibilityIsIgnored];
634}
635
636-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
637{
638  return [mDraggingDestinationHandler draggingEntered: sender];
639}
640
641-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
642{
643  return [mDraggingDestinationHandler draggingUpdated: sender];
644}
645
646-(void)draggingExited:(id <NSDraggingInfo>)sender
647{
648  [mDraggingDestinationHandler draggingExited: sender];
649}
650
651-(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
652{
653  return [mDraggingDestinationHandler prepareForDragOperation: sender];
654}
655
656-(BOOL)performDragOperation:(id <NSDraggingInfo>)sender
657{
658  return [mDraggingDestinationHandler performDragOperation: sender];
659}
660
661-(void)concludeDragOperation:(id <NSDraggingInfo>)sender
662{
663  [mDraggingDestinationHandler concludeDragOperation: sender];
664}
665
666-(void)registerDraggingDestinationHandler:(id)theHandler
667{
668  mDraggingDestinationHandler = theHandler;
669}
670
671-(void)unregisterDraggingDestinationHandler:(id)theHandler
672{
673    (void)theHandler;
674    mDraggingDestinationHandler = nil;
675}
676
677-(void)endExtTextInput
678{
679    [self endExtTextInput:EndExtTextInputFlags::Complete];
680}
681
682-(void)endExtTextInput:(EndExtTextInputFlags)nFlags
683{
684    SalFrameView *pView = static_cast<SalFrameView*>([self firstResponder]);
685    if (pView && [pView isKindOfClass:[SalFrameView class]])
686        [pView endExtTextInput:nFlags];
687}
688
689-(void)windowDidResizeWithTimer:(NSTimer *)pTimer
690{
691    if ( pTimer )
692        [self windowDidResize:[pTimer userInfo]];
693}
694
695@end
696
697@implementation SalFrameView
698+(void)unsetMouseFrame: (AquaSalFrame*)pFrame
699{
700    if( pFrame == s_pMouseFrame )
701        s_pMouseFrame = nullptr;
702}
703
704-(id)initWithSalFrame: (AquaSalFrame*)pFrame
705{
706    if ((self = [super initWithFrame: [NSWindow contentRectForFrameRect: [pFrame->getNSWindow() frame] styleMask: pFrame->mnStyleMask]]) != nil)
707    {
708        mDraggingDestinationHandler = nil;
709        mpFrame = pFrame;
710        mpChildWrapper = nil;
711        mbNeedChildWrapper = NO;
712        mpLastEvent = nil;
713        mMarkedRange = NSMakeRange(NSNotFound, 0);
714        mSelectedRange = NSMakeRange(NSNotFound, 0);
715        mpMouseEventListener = nil;
716        mpLastSuperEvent = nil;
717        mfLastMagnifyTime = 0.0;
718
719        mbInEndExtTextInput = NO;
720        mbInCommitMarkedText = NO;
721        mpLastMarkedText = nil;
722        mbTextInputWantsNonRepeatKeyDown = NO;
723    }
724
725    return self;
726}
727
728-(void)dealloc
729{
730    [self clearLastEvent];
731    [self clearLastMarkedText];
732    [self revokeWrapper];
733
734    [super dealloc];
735}
736
737-(AquaSalFrame*)getSalFrame
738{
739    return mpFrame;
740}
741
742-(void)resetCursorRects
743{
744    if( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
745    {
746        // FIXME: does this leak the returned NSCursor of getCurrentCursor ?
747        const NSRect aRect = { NSZeroPoint, NSMakeSize(mpFrame->maGeometry.width(), mpFrame->maGeometry.height()) };
748        [self addCursorRect: aRect cursor: mpFrame->getCurrentCursor()];
749    }
750}
751
752-(BOOL)acceptsFirstResponder
753{
754    return YES;
755}
756
757-(BOOL)acceptsFirstMouse: (NSEvent*)pEvent
758{
759    (void)pEvent;
760    return YES;
761}
762
763-(BOOL)isOpaque
764{
765    if( !mpFrame)
766        return YES;
767    if( !AquaSalFrame::isAlive( mpFrame))
768        return YES;
769    if( !mpFrame->getClipPath())
770        return YES;
771    return NO;
772}
773
774-(void)drawRect: (NSRect)aRect
775{
776    ImplSVData* pSVData = ImplGetSVData();
777    assert( pSVData );
778    if ( !pSVData )
779        return;
780
781    SolarMutexGuard aGuard;
782    if (!mpFrame || !AquaSalFrame::isAlive(mpFrame))
783        return;
784
785    updateWinDataInLiveResize([self inLiveResize]);
786
787    AquaSalGraphics* pGraphics = mpFrame->mpGraphics;
788    if (pGraphics)
789    {
790        pGraphics->UpdateWindow(aRect);
791        if (mpFrame->getClipPath())
792            [mpFrame->getNSWindow() invalidateShadow];
793    }
794}
795
796-(void)sendMouseEventToFrame: (NSEvent*)pEvent button:(sal_uInt16)nButton eventtype:(SalEvent)nEvent
797{
798    SolarMutexGuard aGuard;
799
800    AquaSalFrame* pDispatchFrame = AquaSalFrame::GetCaptureFrame();
801    bool bIsCaptured = false;
802    if( pDispatchFrame )
803    {
804        bIsCaptured = true;
805        if( nEvent == SalEvent::MouseLeave ) // no leave events if mouse is captured
806            nEvent = SalEvent::MouseMove;
807    }
808    else if( s_pMouseFrame )
809        pDispatchFrame = s_pMouseFrame;
810    else
811        pDispatchFrame = mpFrame;
812
813    /* #i81645# Cocoa reports mouse events while a button is pressed
814       to the window in which it was first pressed. This is reasonable and fine and
815       gets one around most cases where on other platforms one uses CaptureMouse or XGrabPointer,
816       however vcl expects mouse events to occur in the window the mouse is over, unless the
817       mouse is explicitly captured. So we need to find the window the mouse is actually
818       over for conformance with other platforms.
819    */
820    if( ! bIsCaptured && nButton && pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) )
821    {
822        // is this event actually inside that NSWindow ?
823        NSPoint aPt = [NSEvent mouseLocation];
824        NSRect aFrameRect = [pDispatchFrame->getNSWindow() frame];
825
826        if ( ! NSPointInRect( aPt, aFrameRect ) )
827        {
828            // no, it is not
829            // now we need to find the one it may be in
830            /* #i93756# we ant to get enumerate the application windows in z-order
831               to check if any contains the mouse. This could be elegantly done with this
832               code:
833
834               // use NSApp to check windows in ZOrder whether they contain the mouse pointer
835               NSWindow* pWindow = [NSApp makeWindowsPerform: @selector(containsMouse) inOrder: YES];
836               if( pWindow && [pWindow isMemberOfClass: [SalFrameWindow class]] )
837                   pDispatchFrame = [(SalFrameWindow*)pWindow getSalFrame];
838
839               However if a non SalFrameWindow is on screen (like e.g. the file dialog)
840               it can be hit with the containsMouse selector, which it doesn't support.
841               Sadly NSApplication:makeWindowsPerform does not check (for performance reasons
842               I assume) whether a window supports a selector before sending it.
843            */
844            AquaSalFrame* pMouseFrame = getMouseContainerFrame();
845            if( pMouseFrame )
846                pDispatchFrame = pMouseFrame;
847        }
848    }
849
850    if( pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) )
851    {
852        pDispatchFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
853        pDispatchFrame->mnLastModifierFlags = [pEvent modifierFlags];
854
855        NSPoint aPt = [NSEvent mouseLocation];
856        pDispatchFrame->CocoaToVCL( aPt );
857
858        sal_uInt16 nModMask = ImplGetModifierMask( [pEvent modifierFlags] );
859        // #i82284# emulate ctrl left
860        if( nModMask == KEY_MOD3 && nButton == MOUSE_LEFT )
861        {
862            nModMask    = 0;
863            nButton     = MOUSE_RIGHT;
864        }
865
866        SalMouseEvent aEvent;
867        aEvent.mnTime   = pDispatchFrame->mnLastEventTime;
868        aEvent.mnX = static_cast<tools::Long>(aPt.x) - pDispatchFrame->maGeometry.x();
869        aEvent.mnY = static_cast<tools::Long>(aPt.y) - pDispatchFrame->maGeometry.y();
870        aEvent.mnButton = nButton;
871        aEvent.mnCode   =  aEvent.mnButton | nModMask;
872
873        if( AllSettings::GetLayoutRTL() )
874            aEvent.mnX = pDispatchFrame->maGeometry.width() - 1 - aEvent.mnX;
875
876        pDispatchFrame->CallCallback( nEvent, &aEvent );
877
878        // tdf#155266 force flush after scrolling
879        if (nButton == MOUSE_LEFT && nEvent == SalEvent::MouseMove)
880            mpFrame->mbForceFlush = true;
881    }
882}
883
884-(void)mouseDown: (NSEvent*)pEvent
885{
886    if ( mpMouseEventListener != nil &&
887        [mpMouseEventListener respondsToSelector: @selector(mouseDown:)])
888    {
889        [mpMouseEventListener mouseDown: [pEvent copyWithZone: nullptr]];
890    }
891
892    s_nLastButton = MOUSE_LEFT;
893    [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SalEvent::MouseButtonDown];
894}
895
896-(void)mouseDragged: (NSEvent*)pEvent
897{
898    if ( mpMouseEventListener != nil &&
899         [mpMouseEventListener respondsToSelector: @selector(mouseDragged:)])
900    {
901        [mpMouseEventListener mouseDragged: [pEvent copyWithZone: nullptr]];
902    }
903    s_nLastButton = MOUSE_LEFT;
904    [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SalEvent::MouseMove];
905}
906
907-(void)mouseUp: (NSEvent*)pEvent
908{
909    s_nLastButton = 0;
910    [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SalEvent::MouseButtonUp];
911}
912
913-(void)mouseMoved: (NSEvent*)pEvent
914{
915    s_nLastButton = 0;
916    [self sendMouseEventToFrame:pEvent button:0 eventtype:SalEvent::MouseMove];
917}
918
919-(void)mouseEntered: (NSEvent*)pEvent
920{
921    s_pMouseFrame = mpFrame;
922
923    // #i107215# the only mouse events we get when inactive are enter/exit
924    // actually we would like to have all of them, but better none than some
925    if( [NSApp isActive] )
926        [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SalEvent::MouseMove];
927}
928
929-(void)mouseExited: (NSEvent*)pEvent
930{
931    if( s_pMouseFrame == mpFrame )
932        s_pMouseFrame = nullptr;
933
934    // #i107215# the only mouse events we get when inactive are enter/exit
935    // actually we would like to have all of them, but better none than some
936    if( [NSApp isActive] )
937        [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SalEvent::MouseLeave];
938}
939
940-(void)rightMouseDown: (NSEvent*)pEvent
941{
942    s_nLastButton = MOUSE_RIGHT;
943    [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SalEvent::MouseButtonDown];
944}
945
946-(void)rightMouseDragged: (NSEvent*)pEvent
947{
948    s_nLastButton = MOUSE_RIGHT;
949    [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SalEvent::MouseMove];
950}
951
952-(void)rightMouseUp: (NSEvent*)pEvent
953{
954    s_nLastButton = 0;
955    [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SalEvent::MouseButtonUp];
956}
957
958-(void)otherMouseDown: (NSEvent*)pEvent
959{
960    if( [pEvent buttonNumber] == 2 )
961    {
962        s_nLastButton = MOUSE_MIDDLE;
963        [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SalEvent::MouseButtonDown];
964    }
965    else
966        s_nLastButton = 0;
967}
968
969-(void)otherMouseDragged: (NSEvent*)pEvent
970{
971    if( [pEvent buttonNumber] == 2 )
972    {
973        s_nLastButton = MOUSE_MIDDLE;
974        [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SalEvent::MouseMove];
975    }
976    else
977        s_nLastButton = 0;
978}
979
980-(void)otherMouseUp: (NSEvent*)pEvent
981{
982    s_nLastButton = 0;
983    if( [pEvent buttonNumber] == 2 )
984        [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SalEvent::MouseButtonUp];
985}
986
987- (void)magnifyWithEvent: (NSEvent*)pEvent
988{
989    SolarMutexGuard aGuard;
990
991    // TODO: ??  -(float)magnification;
992    if( AquaSalFrame::isAlive( mpFrame ) )
993    {
994        const NSTimeInterval fMagnifyTime = [pEvent timestamp];
995        mpFrame->mnLastEventTime = static_cast<sal_uInt64>( fMagnifyTime * 1000.0 );
996        mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
997
998        // check if this is a new series of magnify events
999        static const NSTimeInterval fMaxDiffTime = 0.3;
1000        const bool bNewSeries = (fMagnifyTime - mfLastMagnifyTime > fMaxDiffTime);
1001
1002        if( bNewSeries )
1003            mfMagnifyDeltaSum = 0.0;
1004        mfMagnifyDeltaSum += [pEvent magnification];
1005
1006        mfLastMagnifyTime = [pEvent timestamp];
1007// TODO: change to 0.1 when CommandWheelMode::ZOOM handlers allow finer zooming control
1008        static const float fMagnifyFactor = 0.25*500; // steps are 500 times smaller for -magnification
1009        static const float fMinMagnifyStep = 15.0 / fMagnifyFactor;
1010        if( fabs(mfMagnifyDeltaSum) <= fMinMagnifyStep )
1011            return;
1012
1013        // adapt NSEvent-sensitivity to application expectations
1014        // TODO: rather make CommandWheelMode::ZOOM handlers smarter
1015        const float fDeltaZ = mfMagnifyDeltaSum * fMagnifyFactor;
1016        int nDeltaZ = basegfx::fround<int>( fDeltaZ );
1017        if( !nDeltaZ )
1018        {
1019            // handle new series immediately
1020            if( !bNewSeries )
1021                return;
1022            nDeltaZ = (fDeltaZ >= 0.0) ? +1 : -1;
1023        }
1024        // eventually give credit for delta sum
1025        mfMagnifyDeltaSum -= nDeltaZ / fMagnifyFactor;
1026
1027        NSPoint aPt = [NSEvent mouseLocation];
1028        mpFrame->CocoaToVCL( aPt );
1029
1030        SalWheelMouseEvent aEvent;
1031        aEvent.mnTime           = mpFrame->mnLastEventTime;
1032        aEvent.mnX = static_cast<tools::Long>(aPt.x) - mpFrame->maGeometry.x();
1033        aEvent.mnY = static_cast<tools::Long>(aPt.y) - mpFrame->maGeometry.y();
1034        aEvent.mnCode           = ImplGetModifierMask( mpFrame->mnLastModifierFlags );
1035        aEvent.mnCode           |= KEY_MOD1; // we want zooming, no scrolling
1036        aEvent.mbDeltaIsPixel   = true;
1037
1038        if( AllSettings::GetLayoutRTL() )
1039            aEvent.mnX = mpFrame->maGeometry.width() - 1 - aEvent.mnX;
1040
1041        aEvent.mnDelta = nDeltaZ;
1042        aEvent.mnNotchDelta = (nDeltaZ >= 0) ? +1 : -1;
1043        if( aEvent.mnDelta == 0 )
1044            aEvent.mnDelta = aEvent.mnNotchDelta;
1045        aEvent.mbHorz = false;
1046        sal_uInt32 nScrollLines = nDeltaZ;
1047        if (nScrollLines == 0)
1048            nScrollLines = 1;
1049        aEvent.mnScrollLines = nScrollLines;
1050        mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1051    }
1052}
1053
1054- (void)rotateWithEvent: (NSEvent*)pEvent
1055{
1056    //Rotation : -(float)rotation;
1057    // TODO: create new CommandType so rotation is available to the applications
1058    (void)pEvent;
1059}
1060
1061- (void)swipeWithEvent: (NSEvent*)pEvent
1062{
1063    SolarMutexGuard aGuard;
1064
1065    if( AquaSalFrame::isAlive( mpFrame ) )
1066    {
1067        mpFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
1068        mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
1069
1070        // merge pending scroll wheel events
1071        CGFloat dX = 0.0;
1072        CGFloat dY = 0.0;
1073        for(;;)
1074        {
1075            dX += [pEvent deltaX];
1076            dY += [pEvent deltaY];
1077            NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSEventMaskScrollWheel
1078            untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ];
1079            if( !pNextEvent )
1080                break;
1081            pEvent = pNextEvent;
1082        }
1083
1084        NSPoint aPt = [NSEvent mouseLocation];
1085        mpFrame->CocoaToVCL( aPt );
1086
1087        SalWheelMouseEvent aEvent;
1088        aEvent.mnTime           = mpFrame->mnLastEventTime;
1089        aEvent.mnX = static_cast<tools::Long>(aPt.x) - mpFrame->maGeometry.x();
1090        aEvent.mnY = static_cast<tools::Long>(aPt.y) - mpFrame->maGeometry.y();
1091        aEvent.mnCode           = ImplGetModifierMask( mpFrame->mnLastModifierFlags );
1092        aEvent.mbDeltaIsPixel   = true;
1093
1094        if( AllSettings::GetLayoutRTL() )
1095            aEvent.mnX = mpFrame->maGeometry.width() - 1 - aEvent.mnX;
1096
1097        if( dX != 0.0 )
1098        {
1099            aEvent.mnDelta = static_cast<tools::Long>(dX < 0 ? floor(dX) : ceil(dX));
1100            aEvent.mnNotchDelta = (dX < 0) ? -1 : +1;
1101            if( aEvent.mnDelta == 0 )
1102                aEvent.mnDelta = aEvent.mnNotchDelta;
1103            aEvent.mbHorz = true;
1104            aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
1105            mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1106        }
1107        if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame ))
1108        {
1109            aEvent.mnDelta = static_cast<tools::Long>(dY < 0 ? floor(dY) : ceil(dY));
1110            aEvent.mnNotchDelta = (dY < 0) ? -1 : +1;
1111            if( aEvent.mnDelta == 0 )
1112                aEvent.mnDelta = aEvent.mnNotchDelta;
1113            aEvent.mbHorz = false;
1114            aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
1115            mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1116        }
1117    }
1118}
1119
1120-(void)scrollWheel: (NSEvent*)pEvent
1121{
1122    SolarMutexGuard aGuard;
1123
1124    if( AquaSalFrame::isAlive( mpFrame ) )
1125    {
1126        mpFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
1127        mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
1128
1129        // merge pending scroll wheel events
1130        CGFloat dX = 0.0;
1131        CGFloat dY = 0.0;
1132        for(;;)
1133        {
1134            dX += [pEvent deltaX];
1135            dY += [pEvent deltaY];
1136            NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSEventMaskScrollWheel
1137                untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ];
1138            if( !pNextEvent )
1139                break;
1140            pEvent = pNextEvent;
1141        }
1142
1143        NSPoint aPt = [NSEvent mouseLocation];
1144        mpFrame->CocoaToVCL( aPt );
1145
1146        SalWheelMouseEvent aEvent;
1147        aEvent.mnTime         = mpFrame->mnLastEventTime;
1148        aEvent.mnX = static_cast<tools::Long>(aPt.x) - mpFrame->maGeometry.x();
1149        aEvent.mnY = static_cast<tools::Long>(aPt.y) - mpFrame->maGeometry.y();
1150        aEvent.mnCode         = ImplGetModifierMask( mpFrame->mnLastModifierFlags );
1151        aEvent.mbDeltaIsPixel = false;
1152
1153        if( AllSettings::GetLayoutRTL() )
1154            aEvent.mnX = mpFrame->maGeometry.width() - 1 - aEvent.mnX;
1155
1156        if( dX != 0.0 )
1157        {
1158            aEvent.mnDelta = static_cast<tools::Long>(dX < 0 ? floor(dX) : ceil(dX));
1159            aEvent.mnNotchDelta = (dX < 0) ? -1 : +1;
1160            if( aEvent.mnDelta == 0 )
1161                aEvent.mnDelta = aEvent.mnNotchDelta;
1162            aEvent.mbHorz = true;
1163            sal_uInt32 nScrollLines = fabs(dX) / WHEEL_EVENT_FACTOR;
1164            if (nScrollLines == 0)
1165                nScrollLines = 1;
1166            aEvent.mnScrollLines = nScrollLines;
1167
1168            mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1169        }
1170        if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame ) )
1171        {
1172            aEvent.mnDelta = static_cast<tools::Long>(dY < 0 ? floor(dY) : ceil(dY));
1173            aEvent.mnNotchDelta = (dY < 0) ? -1 : +1;
1174            if( aEvent.mnDelta == 0 )
1175                aEvent.mnDelta = aEvent.mnNotchDelta;
1176            aEvent.mbHorz = false;
1177            sal_uInt32 nScrollLines = fabs(dY) / WHEEL_EVENT_FACTOR;
1178            if (nScrollLines == 0)
1179                nScrollLines = 1;
1180            aEvent.mnScrollLines = nScrollLines;
1181
1182            mpFrame->CallCallback( SalEvent::WheelMouse, &aEvent );
1183        }
1184
1185        // tdf#155266 force flush after scrolling
1186        mpFrame->mbForceFlush = true;
1187    }
1188}
1189
1190
1191-(void)keyDown: (NSEvent*)pEvent
1192{
1193    SolarMutexGuard aGuard;
1194
1195    if( AquaSalFrame::isAlive( mpFrame ) )
1196    {
1197        // Retain the event as it will be released sometime before a key up
1198        // event is dispatched
1199        [self clearLastEvent];
1200        mpLastEvent = [pEvent retain];
1201
1202        mbInKeyInput = true;
1203        mbNeedSpecialKeyHandle = false;
1204        mbKeyHandled = false;
1205
1206        mpFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
1207        mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
1208
1209        if( ! [self handleKeyDownException: pEvent] )
1210        {
1211            sal_uInt16 nKeyCode = ImplMapKeyCode( [pEvent keyCode] );
1212            if ( nKeyCode == KEY_DELETE && mbTextInputWantsNonRepeatKeyDown )
1213            {
1214                // tdf#42437 Enable press-and-hold special character input method
1215                // Emulate the press-and-hold behavior of the TextEdit
1216                // application by deleting the marked text when only the
1217                // Delete key is pressed and keep the marked text when the
1218                // Backspace key or Fn-Delete keys are pressed.
1219                if ( [pEvent keyCode] == 51 )
1220                {
1221                    [self deleteTextInputWantsNonRepeatKeyDown];
1222                }
1223                else
1224                {
1225                    [self unmarkText];
1226                    mbKeyHandled = true;
1227                    mbInKeyInput = false;
1228                }
1229
1230                [self endExtTextInput];
1231                return;
1232            }
1233
1234            NSArray* pArray = [NSArray arrayWithObject: pEvent];
1235            [self interpretKeyEvents: pArray];
1236
1237            // Handle repeat key events by explicitly inserting the text if
1238            // -[NSResponder interpretKeyEvents:] does not insert or mark any
1239            // text. Note: do not do this step if there is uncommitted text.
1240            // Related: tdf#42437 Skip special press-and-hold handling for action keys
1241            // Pressing and holding action keys such as arrow keys must not be
1242            // handled like pressing and holding a character key as it will
1243            // insert unexpected text.
1244            if ( !mbKeyHandled && !mpLastMarkedText && mpLastEvent && [mpLastEvent type] == NSEventTypeKeyDown && [mpLastEvent isARepeat] )
1245            {
1246                NSString *pChars = [mpLastEvent characters];
1247                if ( pChars )
1248                    [self insertText:pChars replacementRange:NSMakeRange( 0, [pChars length] )];
1249            }
1250            // tdf#42437 Enable press-and-hold special character input method
1251            // Emulate the press-and-hold behavior of the TextEdit application
1252            // by committing an empty string for key down events dispatched
1253            // while the special character input method popup is displayed.
1254            else if ( mpLastMarkedText && mbTextInputWantsNonRepeatKeyDown && mpLastEvent && [mpLastEvent type] == NSEventTypeKeyDown && ![mpLastEvent isARepeat] )
1255            {
1256                // If the escape or return key is pressed, unmark the text to
1257                // skip deletion of marked text
1258                if ( nKeyCode == KEY_ESCAPE || nKeyCode == KEY_RETURN )
1259                    [self unmarkText];
1260                [self insertText:[NSString string] replacementRange:NSMakeRange( NSNotFound, 0 )];
1261            }
1262        }
1263
1264        mbInKeyInput = false;
1265    }
1266}
1267
1268-(BOOL)handleKeyDownException:(NSEvent*)pEvent
1269{
1270    // check for a very special set of modified characters
1271    NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers];
1272
1273    if( pUnmodifiedString && [pUnmodifiedString length] == 1 )
1274    {
1275        /* #i103102# key events with command and alternate don't make it through
1276           interpretKeyEvents (why?). Try to dispatch them here first,
1277           if not successful continue normally
1278        */
1279        if( (mpFrame->mnLastModifierFlags & (NSEventModifierFlagOption | NSEventModifierFlagCommand))
1280                    == (NSEventModifierFlagOption | NSEventModifierFlagCommand) )
1281        {
1282            if( [self sendSingleCharacter: mpLastEvent] )
1283                return YES;
1284        }
1285    }
1286    return NO;
1287}
1288
1289-(void)flagsChanged: (NSEvent*)pEvent
1290{
1291    SolarMutexGuard aGuard;
1292
1293    if( AquaSalFrame::isAlive( mpFrame ) )
1294    {
1295        mpFrame->mnLastEventTime = static_cast<sal_uInt64>( [pEvent timestamp] * 1000.0 );
1296        mpFrame->mnLastModifierFlags = [pEvent modifierFlags];
1297    }
1298}
1299
1300-(void)insertText:(id)aString replacementRange:(NSRange)replacementRange
1301{
1302    (void) replacementRange; // FIXME: surely it must be used
1303
1304    SolarMutexGuard aGuard;
1305
1306    [self deleteTextInputWantsNonRepeatKeyDown];
1307
1308    // Ignore duplicate events that are sometimes posted during cancellation
1309    // of the native input method session. This usually happens when
1310    // [self endExtTextInput] is called from [self windowDidBecomeKey:] and,
1311    // if the native input method popup, that was cancelled in a
1312    // previous call to [self windowDidResignKey:], has reappeared. In such
1313    // cases, the native input context posts the reappearing popup's
1314    // uncommitted text.
1315    if (mbInEndExtTextInput && !mbInCommitMarkedText)
1316        return;
1317
1318    if( AquaSalFrame::isAlive( mpFrame ) )
1319    {
1320        NSString* pInsert = nil;
1321        if( [aString isKindOfClass: [NSAttributedString class]] )
1322            pInsert = [aString string];
1323        else
1324            pInsert = aString;
1325
1326        int nLen = 0;
1327        if( pInsert && ( nLen = [pInsert length] ) > 0 )
1328        {
1329            OUString aInsertString( GetOUString( pInsert ) );
1330             // aCharCode initializer is safe since aInsertString will at least contain '\0'
1331            sal_Unicode aCharCode = *aInsertString.getStr();
1332
1333            if( nLen == 1 &&
1334                aCharCode < 0x80 &&
1335                aCharCode > 0x1f &&
1336                ! [self hasMarkedText ]
1337                )
1338            {
1339                sal_uInt16 nKeyCode = ImplMapCharCode( aCharCode );
1340                unsigned int nLastModifiers = mpFrame->mnLastModifierFlags;
1341
1342                // #i99567#
1343                // find out the unmodified key code
1344
1345                // sanity check
1346                if( mpLastEvent && ( [mpLastEvent type] == NSEventTypeKeyDown || [mpLastEvent type] == NSEventTypeKeyUp ) )
1347                {
1348                    // get unmodified string
1349                    NSString* pUnmodifiedString = [mpLastEvent charactersIgnoringModifiers];
1350                    if( pUnmodifiedString && [pUnmodifiedString length] == 1 )
1351                    {
1352                        // map the unmodified key code
1353                        unichar keyChar = [pUnmodifiedString characterAtIndex: 0];
1354                        nKeyCode = ImplMapCharCode( keyChar );
1355                    }
1356                    nLastModifiers = [mpLastEvent modifierFlags];
1357
1358                }
1359                // #i99567#
1360                // applications and vcl's edit fields ignore key events with ALT
1361                // however we're at a place where we know text should be inserted
1362                // so it seems we need to strip the Alt modifier here
1363                if( (nLastModifiers & (NSEventModifierFlagControl | NSEventModifierFlagOption | NSEventModifierFlagCommand))
1364                    == NSEventModifierFlagOption )
1365                {
1366                    nLastModifiers = 0;
1367                }
1368                [self sendKeyInputAndReleaseToFrame: nKeyCode character: aCharCode modifiers: nLastModifiers];
1369            }
1370            else
1371            {
1372                SalExtTextInputEvent aEvent;
1373                aEvent.maText           = aInsertString;
1374                aEvent.mpTextAttr       = nullptr;
1375                aEvent.mnCursorPos      = aInsertString.getLength();
1376                aEvent.mnCursorFlags    = 0;
1377                mpFrame->CallCallback( SalEvent::ExtTextInput, &aEvent );
1378                if( AquaSalFrame::isAlive( mpFrame ) )
1379                    mpFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
1380            }
1381        }
1382        else
1383        {
1384            SalExtTextInputEvent aEvent;
1385            aEvent.maText.clear();
1386            aEvent.mpTextAttr       = nullptr;
1387            aEvent.mnCursorPos      = 0;
1388            aEvent.mnCursorFlags    = 0;
1389            mpFrame->CallCallback( SalEvent::ExtTextInput, &aEvent );
1390            if( AquaSalFrame::isAlive( mpFrame ) )
1391                mpFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
1392
1393        }
1394        [self unmarkText];
1395    }
1396
1397    // Mark event as handled even if the frame isn't valid like is done in
1398    // [self setMarkedText:selectedRange:replacementRange:] and
1399    // [self doCommandBySelector:]
1400    mbKeyHandled = true;
1401}
1402
1403-(void)insertTab: (id)aSender
1404{
1405    (void)aSender;
1406    [self sendKeyInputAndReleaseToFrame: KEY_TAB character: '\t' modifiers: 0];
1407}
1408
1409-(void)insertBacktab: (id)aSender
1410{
1411    (void)aSender;
1412    [self sendKeyInputAndReleaseToFrame: (KEY_TAB | KEY_SHIFT) character: '\t' modifiers: 0];
1413}
1414
1415-(void)moveLeft: (id)aSender
1416{
1417    (void)aSender;
1418    [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: 0];
1419}
1420
1421-(void)moveLeftAndModifySelection: (id)aSender
1422{
1423    (void)aSender;
1424    [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: NSEventModifierFlagShift];
1425}
1426
1427-(void)moveBackwardAndModifySelection: (id)aSender
1428{
1429    (void)aSender;
1430    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_BACKWARD character: 0  modifiers: 0];
1431}
1432
1433-(void)moveRight: (id)aSender
1434{
1435    (void)aSender;
1436    [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: 0];
1437}
1438
1439-(void)moveRightAndModifySelection: (id)aSender
1440{
1441    (void)aSender;
1442    [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: NSEventModifierFlagShift];
1443}
1444
1445-(void)moveForwardAndModifySelection: (id)aSender
1446{
1447    (void)aSender;
1448    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_FORWARD character: 0  modifiers: 0];
1449}
1450
1451-(void)moveWordLeft: (id)aSender
1452{
1453    (void)aSender;
1454    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_BACKWARD character: 0  modifiers: 0];
1455}
1456
1457-(void)moveWordBackward: (id)aSender
1458{
1459    (void)aSender;
1460    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_BACKWARD character: 0  modifiers: 0];
1461}
1462
1463-(void)moveWordBackwardAndModifySelection: (id)aSender
1464{
1465    (void)aSender;
1466    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_BACKWARD character: 0  modifiers: 0];
1467}
1468
1469-(void)moveWordLeftAndModifySelection: (id)aSender
1470{
1471    (void)aSender;
1472    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_BACKWARD character: 0  modifiers: 0];
1473}
1474
1475-(void)moveWordRight: (id)aSender
1476{
1477    (void)aSender;
1478    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_FORWARD character: 0  modifiers: 0];
1479}
1480
1481-(void)moveWordForward: (id)aSender
1482{
1483    (void)aSender;
1484    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_FORWARD character: 0  modifiers: 0];
1485}
1486
1487-(void)moveWordForwardAndModifySelection: (id)aSender
1488{
1489    (void)aSender;
1490    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_FORWARD character: 0  modifiers: 0];
1491}
1492
1493-(void)moveWordRightAndModifySelection: (id)aSender
1494{
1495    (void)aSender;
1496    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_FORWARD character: 0  modifiers: 0];
1497}
1498
1499-(void)moveToEndOfLine: (id)aSender
1500{
1501    (void)aSender;
1502    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_LINE character: 0  modifiers: 0];
1503}
1504
1505-(void)moveToRightEndOfLine: (id)aSender
1506{
1507    (void)aSender;
1508    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_LINE character: 0  modifiers: 0];
1509}
1510
1511-(void)moveToEndOfLineAndModifySelection: (id)aSender
1512{
1513    (void)aSender;
1514    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_LINE character: 0  modifiers: 0];
1515}
1516
1517-(void)moveToRightEndOfLineAndModifySelection: (id)aSender
1518{
1519    (void)aSender;
1520    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_LINE character: 0  modifiers: 0];
1521}
1522
1523-(void)moveToBeginningOfLine: (id)aSender
1524{
1525    (void)aSender;
1526    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1527}
1528
1529-(void)moveToLeftEndOfLine: (id)aSender
1530{
1531    (void)aSender;
1532    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1533}
1534
1535-(void)moveToBeginningOfLineAndModifySelection: (id)aSender
1536{
1537    (void)aSender;
1538    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1539}
1540
1541-(void)moveToLeftEndOfLineAndModifySelection: (id)aSender
1542{
1543    (void)aSender;
1544    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1545}
1546
1547-(void)moveToEndOfParagraph: (id)aSender
1548{
1549    (void)aSender;
1550    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1551}
1552
1553-(void)moveToEndOfParagraphAndModifySelection: (id)aSender
1554{
1555    (void)aSender;
1556    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1557}
1558
1559-(void)moveParagraphForward: (id)aSender
1560{
1561    (void)aSender;
1562    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1563}
1564
1565-(void)moveParagraphForwardAndModifySelection: (id)aSender
1566{
1567    (void)aSender;
1568    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1569}
1570
1571-(void)moveToBeginningOfParagraph: (id)aSender
1572{
1573    (void)aSender;
1574    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1575}
1576
1577-(void)moveParagraphBackward: (id)aSender
1578{
1579    (void)aSender;
1580    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1581}
1582
1583-(void)moveToBeginningOfParagraphAndModifySelection: (id)aSender
1584{
1585    (void)aSender;
1586    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1587}
1588
1589-(void)moveParagraphBackwardAndModifySelection: (id)aSender
1590{
1591    (void)aSender;
1592    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1593}
1594
1595-(void)moveToEndOfDocument: (id)aSender
1596{
1597    (void)aSender;
1598    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0  modifiers: 0];
1599}
1600
1601-(void)scrollToEndOfDocument: (id)aSender
1602{
1603    (void)aSender;
1604    // this is not exactly what we should do, but it makes "End" and "Shift-End" behave consistent
1605    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0  modifiers: 0];
1606}
1607
1608-(void)moveToEndOfDocumentAndModifySelection: (id)aSender
1609{
1610    (void)aSender;
1611    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_DOCUMENT character: 0  modifiers: 0];
1612}
1613
1614-(void)moveToBeginningOfDocument: (id)aSender
1615{
1616    (void)aSender;
1617    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0  modifiers: 0];
1618}
1619
1620-(void)scrollToBeginningOfDocument: (id)aSender
1621{
1622    (void)aSender;
1623    // this is not exactly what we should do, but it makes "Home" and "Shift-Home" behave consistent
1624    [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0  modifiers: 0];
1625}
1626
1627-(void)moveToBeginningOfDocumentAndModifySelection: (id)aSender
1628{
1629    (void)aSender;
1630    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT character: 0  modifiers: 0];
1631}
1632
1633-(void)moveUp: (id)aSender
1634{
1635    (void)aSender;
1636    [self sendKeyInputAndReleaseToFrame: KEY_UP character: 0 modifiers: 0];
1637}
1638
1639-(void)moveDown: (id)aSender
1640{
1641    (void)aSender;
1642    [self sendKeyInputAndReleaseToFrame: KEY_DOWN character: 0 modifiers: 0];
1643}
1644
1645-(void)insertNewline: (id)aSender
1646{
1647    (void)aSender;
1648    // #i91267# make enter and shift-enter work by evaluating the modifiers
1649    [self sendKeyInputAndReleaseToFrame: KEY_RETURN character: '\n' modifiers: mpFrame->mnLastModifierFlags];
1650}
1651
1652-(void)deleteBackward: (id)aSender
1653{
1654    (void)aSender;
1655    [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0];
1656}
1657
1658-(void)deleteForward: (id)aSender
1659{
1660    (void)aSender;
1661    [self sendKeyInputAndReleaseToFrame: KEY_DELETE character: 0x7f modifiers: 0];
1662}
1663
1664-(void)deleteBackwardByDecomposingPreviousCharacter: (id)aSender
1665{
1666    (void)aSender;
1667    [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0];
1668}
1669
1670-(void)deleteWordBackward: (id)aSender
1671{
1672    (void)aSender;
1673    [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_WORD_BACKWARD character: 0  modifiers: 0];
1674}
1675
1676-(void)deleteWordForward: (id)aSender
1677{
1678    (void)aSender;
1679    [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_WORD_FORWARD character: 0  modifiers: 0];
1680}
1681
1682-(void)deleteToBeginningOfLine: (id)aSender
1683{
1684    (void)aSender;
1685    [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_BEGIN_OF_LINE character: 0  modifiers: 0];
1686}
1687
1688-(void)deleteToEndOfLine: (id)aSender
1689{
1690    (void)aSender;
1691    [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_END_OF_LINE character: 0  modifiers: 0];
1692}
1693
1694-(void)deleteToBeginningOfParagraph: (id)aSender
1695{
1696    (void)aSender;
1697    [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_BEGIN_OF_PARAGRAPH character: 0  modifiers: 0];
1698}
1699
1700-(void)deleteToEndOfParagraph: (id)aSender
1701{
1702    (void)aSender;
1703    [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_END_OF_PARAGRAPH character: 0  modifiers: 0];
1704}
1705
1706-(void)insertLineBreak: (id)aSender
1707{
1708    (void)aSender;
1709    [self sendKeyInputAndReleaseToFrame: css::awt::Key::INSERT_LINEBREAK character: 0  modifiers: 0];
1710}
1711
1712-(void)insertParagraphSeparator: (id)aSender
1713{
1714    (void)aSender;
1715    [self sendKeyInputAndReleaseToFrame: css::awt::Key::INSERT_PARAGRAPH character: 0  modifiers: 0];
1716}
1717
1718-(void)selectWord: (id)aSender
1719{
1720    (void)aSender;
1721    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD character: 0  modifiers: 0];
1722}
1723
1724-(void)selectLine: (id)aSender
1725{
1726    (void)aSender;
1727    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_LINE character: 0  modifiers: 0];
1728}
1729
1730-(void)selectParagraph: (id)aSender
1731{
1732    (void)aSender;
1733    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_PARAGRAPH character: 0  modifiers: 0];
1734}
1735
1736-(void)selectAll: (id)aSender
1737{
1738    (void)aSender;
1739    [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_ALL character: 0  modifiers: 0];
1740}
1741
1742-(void)cancelOperation: (id)aSender
1743{
1744    (void)aSender;
1745    [self sendKeyInputAndReleaseToFrame: KEY_ESCAPE character: 0x1b modifiers: 0];
1746}
1747
1748-(void)noop: (id)aSender
1749{
1750    (void)aSender;
1751    if( ! mbKeyHandled )
1752    {
1753        if( ! [self sendSingleCharacter:mpLastEvent] )
1754        {
1755            /* prevent recursion */
1756            if( mpLastEvent != mpLastSuperEvent && [NSApp respondsToSelector: @selector(sendSuperEvent:)] )
1757            {
1758                id pLastSuperEvent = mpLastSuperEvent;
1759                mpLastSuperEvent = mpLastEvent;
1760                [NSApp performSelector:@selector(sendSuperEvent:) withObject: mpLastEvent];
1761                mpLastSuperEvent = pLastSuperEvent;
1762
1763                std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent );
1764                if( it != GetSalData()->maKeyEventAnswer.end() )
1765                    it->second = true;
1766            }
1767        }
1768    }
1769}
1770
1771-(BOOL)sendKeyInputAndReleaseToFrame: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar
1772{
1773    return [self sendKeyInputAndReleaseToFrame: nKeyCode character: aChar modifiers: mpFrame->mnLastModifierFlags];
1774}
1775
1776-(BOOL)sendKeyInputAndReleaseToFrame: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod
1777{
1778    return [self sendKeyToFrameDirect: nKeyCode character: aChar modifiers: nMod] ||
1779           [self sendSingleCharacter: mpLastEvent];
1780}
1781
1782-(BOOL)sendKeyToFrameDirect: (sal_uInt16)nKeyCode  character: (sal_Unicode)aChar modifiers: (unsigned int)nMod
1783{
1784    SolarMutexGuard aGuard;
1785
1786    bool nRet = false;
1787    if( AquaSalFrame::isAlive( mpFrame ) )
1788    {
1789        SalKeyEvent aEvent;
1790        aEvent.mnCode           = nKeyCode | ImplGetModifierMask( nMod );
1791        aEvent.mnCharCode       = aChar;
1792        aEvent.mnRepeat         = FALSE;
1793        nRet = mpFrame->CallCallback( SalEvent::KeyInput, &aEvent );
1794        std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent );
1795        if( it != GetSalData()->maKeyEventAnswer.end() )
1796            it->second = nRet;
1797        if( AquaSalFrame::isAlive( mpFrame ) )
1798            mpFrame->CallCallback( SalEvent::KeyUp, &aEvent );
1799    }
1800    return nRet;
1801}
1802
1803
1804-(BOOL)sendSingleCharacter: (NSEvent *)pEvent
1805{
1806    NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers];
1807
1808    if( pUnmodifiedString && [pUnmodifiedString length] == 1 )
1809    {
1810        unichar keyChar = [pUnmodifiedString characterAtIndex: 0];
1811        sal_uInt16 nKeyCode = ImplMapCharCode( keyChar );
1812        if (nKeyCode == 0)
1813        {
1814            sal_uInt16 nOtherKeyCode = [pEvent keyCode];
1815            nKeyCode = ImplMapKeyCode(nOtherKeyCode);
1816        }
1817        if( nKeyCode != 0 )
1818        {
1819            // don't send code points in the private use area
1820            if( keyChar >= 0xf700 && keyChar < 0xf780 )
1821                keyChar = 0;
1822            bool bRet = [self sendKeyToFrameDirect: nKeyCode character: keyChar modifiers: mpFrame->mnLastModifierFlags];
1823            mbInKeyInput = false;
1824
1825            return bRet;
1826        }
1827    }
1828    return NO;
1829}
1830
1831
1832// NSTextInput/NSTextInputClient protocol
1833- (NSArray *)validAttributesForMarkedText
1834{
1835    return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, nil];
1836}
1837
1838- (BOOL)hasMarkedText
1839{
1840    bool bHasMarkedText;
1841
1842    bHasMarkedText = ( mMarkedRange.location != NSNotFound ) &&
1843                     ( mMarkedRange.length != 0 );
1844    // hack to check keys like "Control-j"
1845    if( mbInKeyInput )
1846    {
1847        mbNeedSpecialKeyHandle = true;
1848    }
1849
1850    // FIXME:
1851    // #i106901#
1852    // if we come here outside of mbInKeyInput, this is likely to be because
1853    // of the keyboard viewer. For unknown reasons having no marked range
1854    // in this case causes a crash. So we say we have a marked range anyway
1855    // This is a hack, since it is not understood what a) causes that crash
1856    // and b) why we should have a marked range at this point.
1857    if( ! mbInKeyInput )
1858        bHasMarkedText = true;
1859
1860    return bHasMarkedText;
1861}
1862
1863- (NSRange)markedRange
1864{
1865    // FIXME:
1866    // #i106901#
1867    // if we come here outside of mbInKeyInput, this is likely to be because
1868    // of the keyboard viewer. For unknown reasons having no marked range
1869    // in this case causes a crash. So we say we have a marked range anyway
1870    // This is a hack, since it is not understood what a) causes that crash
1871    // and b) why we should have a marked range at this point. Stop the native
1872    // input method popup from appearing in the bottom left corner of the
1873    // screen by returning the marked range if is valid when called outside of
1874    // mbInKeyInput. If a zero length range is returned, macOS won't call
1875    // [self firstRectForCharacterRange:actualRange:] for any newly appended
1876    // uncommitted text.
1877    if( ! mbInKeyInput )
1878        return mMarkedRange.location != NSNotFound ? mMarkedRange : NSMakeRange( 0, 0 );
1879
1880    return [self hasMarkedText] ? mMarkedRange : NSMakeRange( NSNotFound, 0 );
1881}
1882
1883- (NSRange)selectedRange
1884{
1885    // tdf#42437 Enable press-and-hold special character input method
1886    // Always return a valid range location. If the range location is
1887    // NSNotFound, -[NSResponder interpretKeyEvents:] will not call
1888    // [self firstRectForCharacterRange:actualRange:] and will not display the
1889    // special character input method popup.
1890    return ( mSelectedRange.location == NSNotFound ? NSMakeRange( 0, 0 ) : mSelectedRange );
1891}
1892
1893- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange replacementRange:(NSRange)replacementRange
1894{
1895    (void) replacementRange; // FIXME - use it!
1896
1897    SolarMutexGuard aGuard;
1898
1899    [self deleteTextInputWantsNonRepeatKeyDown];
1900
1901    if( ![aString isKindOfClass:[NSAttributedString class]] )
1902        aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
1903
1904    // Reset cached state
1905    [self unmarkText];
1906
1907    int len = [aString length];
1908    SalExtTextInputEvent aInputEvent;
1909    if( len > 0 ) {
1910        // Set the marked and selected ranges to the marked text and selected
1911        // range parameters
1912        mMarkedRange = NSMakeRange( 0, [aString length] );
1913        if (selRange.location == NSNotFound || selRange.location >= mMarkedRange.length)
1914             mSelectedRange = NSMakeRange( NSNotFound, 0 );
1915        else
1916             mSelectedRange = NSMakeRange( selRange.location, selRange.location + selRange.length > mMarkedRange.length ? mMarkedRange.length - selRange.location : selRange.length );
1917
1918        // If we are going to post uncommitted text, cache the string parameter
1919        // as is needed in both [self endExtTextInput] and
1920        // [self attributedSubstringForProposedRange:actualRange:]
1921        mpLastMarkedText = [aString retain];
1922
1923        NSString *pString = [aString string];
1924        OUString aInsertString( GetOUString( pString ) );
1925        std::vector<ExtTextInputAttr> aInputFlags( std::max( 1, len ), ExtTextInputAttr::NONE );
1926        int nSelectionStart = (mSelectedRange.location == NSNotFound ? len : mSelectedRange.location);
1927        int nSelectionEnd = (mSelectedRange.location == NSNotFound ? len : mSelectedRange.location + selRange.length);
1928        for ( int i = 0; i < len; i++ )
1929        {
1930            // Highlight all characters in the selected range. Normally
1931            // uncommitted text is underlined but when an item is selected in
1932            // the native input method popup or selecting a subblock of
1933            // uncommitted text using the left or right arrow keys, the
1934            // selection range is set and the selected range is either
1935            // highlighted like in Excel or is bold underlined like in
1936            // Safari. Highlighting the selected range was chosen because
1937            // using bold and double underlines can get clipped making the
1938            // selection range indistinguishable from the rest of the
1939            // uncommitted text.
1940            if (i >= nSelectionStart && i < nSelectionEnd)
1941            {
1942                aInputFlags[i] = ExtTextInputAttr::Highlight;
1943                continue;
1944            }
1945
1946            unsigned int nUnderlineValue;
1947            NSRange effectiveRange;
1948
1949            effectiveRange = NSMakeRange(i, 1);
1950            nUnderlineValue = [[aString attribute:NSUnderlineStyleAttributeName atIndex:i effectiveRange:&effectiveRange] unsignedIntValue];
1951
1952            switch (nUnderlineValue & 0xff) {
1953            case NSUnderlineStyleSingle:
1954                aInputFlags[i] = ExtTextInputAttr::Underline;
1955                break;
1956            case NSUnderlineStyleThick:
1957                aInputFlags[i] = ExtTextInputAttr::BoldUnderline;
1958                break;
1959            case NSUnderlineStyleDouble:
1960                aInputFlags[i] = ExtTextInputAttr::DoubleUnderline;
1961                break;
1962            default:
1963                aInputFlags[i] = ExtTextInputAttr::Highlight;
1964                break;
1965            }
1966        }
1967
1968        aInputEvent.maText = aInsertString;
1969        aInputEvent.mnCursorPos = nSelectionStart;
1970        aInputEvent.mnCursorFlags = 0;
1971        aInputEvent.mpTextAttr = aInputFlags.data();
1972        mpFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void *>(&aInputEvent) );
1973    } else {
1974        aInputEvent.maText.clear();
1975        aInputEvent.mnCursorPos = 0;
1976        aInputEvent.mnCursorFlags = 0;
1977        aInputEvent.mpTextAttr = nullptr;
1978        mpFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void *>(&aInputEvent) );
1979        mpFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
1980    }
1981    mbKeyHandled= true;
1982}
1983
1984- (void)unmarkText
1985{
1986    [self clearLastMarkedText];
1987
1988    mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0);
1989}
1990
1991- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
1992{
1993    (void) aRange;
1994    (void) actualRange;
1995
1996    // FIXME - Implement
1997    return nil;
1998}
1999
2000- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
2001{
2002    (void)thePoint;
2003    // FIXME
2004    return 0;
2005}
2006
2007- (NSInteger)conversationIdentifier
2008{
2009    return reinterpret_cast<long>(self);
2010}
2011
2012- (void)doCommandBySelector:(SEL)aSelector
2013{
2014    if( AquaSalFrame::isAlive( mpFrame ) )
2015    {
2016        if( (mpFrame->mnICOptions & InputContextFlags::Text) &&
2017            aSelector != nullptr && [self respondsToSelector: aSelector] )
2018        {
2019            [self performSelector: aSelector];
2020        }
2021        else
2022        {
2023            [self sendSingleCharacter:mpLastEvent];
2024        }
2025    }
2026
2027    mbKeyHandled = true;
2028}
2029
2030-(void)clearLastEvent
2031{
2032    if (mpLastEvent)
2033    {
2034        [mpLastEvent release];
2035        mpLastEvent = nil;
2036    }
2037}
2038
2039-(void)clearLastMarkedText
2040{
2041    if (mpLastMarkedText)
2042    {
2043        [mpLastMarkedText release];
2044        mpLastMarkedText = nil;
2045    }
2046
2047    mbTextInputWantsNonRepeatKeyDown = NO;
2048}
2049
2050- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
2051{
2052     // FIXME - These should probably be used?
2053    (void) aRange;
2054    (void) actualRange;
2055
2056    SolarMutexGuard aGuard;
2057
2058    // tdf#42437 Enable press-and-hold special character input method
2059    // Some text entry controls, such as Writer comments or the cell editor in
2060    // Calc's Formula Bar, need to have an input method session open or else
2061    // the returned position won't be anywhere near the text cursor. So,
2062    // dispatch an empty SalEvent::ExtTextInput event, fetch the position,
2063    // and then dispatch a SalEvent::EndExtTextInput event.
2064    NSString *pNewMarkedText = nullptr;
2065    NSString *pChars = [mpLastEvent characters];
2066
2067    // tdf#158124 KEY_DELETE events do not need an ExtTextInput event
2068    // When using various Japanese input methods, the last event will be a
2069    // repeating key down event with a single delete character while the
2070    // Backspace key, Delete key, or Fn-Delete keys are pressed. These key
2071    // events are now ignored since setting mbTextInputWantsNonRepeatKeyDown
2072    // to YES for these events will trigger an assert or crash when saving a
2073    // .docx document.
2074    bool bNeedsExtTextInput = ( pChars && mbInKeyInput && !mpLastMarkedText && mpLastEvent && [mpLastEvent type] == NSEventTypeKeyDown && [mpLastEvent isARepeat] && ImplMapKeyCode( [mpLastEvent keyCode] ) != KEY_DELETE );
2075    if ( bNeedsExtTextInput )
2076    {
2077        // tdf#154708 Preserve selection for repeating Shift-arrow on Japanese keyboard
2078        // Skip the posting of SalEvent::ExtTextInput and
2079        // SalEvent::EndExtTextInput events for private use area characters.
2080        NSUInteger nLen = [pChars length];
2081        auto const pBuf = std::make_unique<unichar[]>( nLen + 1 );
2082        NSUInteger nBufLen = 0;
2083        for ( NSUInteger i = 0; i < nLen; i++ )
2084        {
2085            unichar aChar = [pChars characterAtIndex:i];
2086            if ( aChar >= 0xf700 && aChar < 0xf780 )
2087                continue;
2088
2089            pBuf[nBufLen++] = aChar;
2090        }
2091        pBuf[nBufLen] = 0;
2092
2093        pNewMarkedText = [NSString stringWithCharacters:pBuf.get() length:nBufLen];
2094        if (!pNewMarkedText || ![pNewMarkedText length])
2095            bNeedsExtTextInput = false;
2096    }
2097
2098    if ( bNeedsExtTextInput )
2099    {
2100        SalExtTextInputEvent aInputEvent;
2101        aInputEvent.maText.clear();
2102        aInputEvent.mnCursorPos = 0;
2103        aInputEvent.mnCursorFlags = 0;
2104        aInputEvent.mpTextAttr = nullptr;
2105        if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
2106            mpFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void *>(&aInputEvent) );
2107    }
2108
2109    SalExtTextInputPosEvent aPosEvent;
2110    if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
2111        mpFrame->CallCallback( SalEvent::ExtTextInputPos, static_cast<void *>(&aPosEvent) );
2112
2113    if ( bNeedsExtTextInput )
2114    {
2115        if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
2116            mpFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
2117
2118        // tdf#42437 Enable press-and-hold special character input method
2119        // Emulate the press-and-hold behavior of the TextEdit application by
2120        // setting the marked text to the last key down event's characters. The
2121        // characters will already have been committed by the special character
2122        // input method so set the mbTextInputWantsNonRepeatKeyDown flag to
2123        // indicate that the characters need to be deleted if the input method
2124        // replaces the committed characters.
2125        if ( pNewMarkedText )
2126        {
2127            [self unmarkText];
2128            mpLastMarkedText = [[NSAttributedString alloc] initWithString:pNewMarkedText];
2129            mSelectedRange = mMarkedRange = NSMakeRange( 0, [mpLastMarkedText length] );
2130            mbTextInputWantsNonRepeatKeyDown = YES;
2131        }
2132    }
2133
2134    NSRect rect;
2135
2136    if ( mpFrame && AquaSalFrame::isAlive( mpFrame ) )
2137    {
2138        rect.origin.x = aPosEvent.mnX + mpFrame->maGeometry.x();
2139        rect.origin.y = aPosEvent.mnY + mpFrame->maGeometry.y() + 4; // add some space for underlines
2140        rect.size.width = aPosEvent.mnWidth;
2141        rect.size.height = aPosEvent.mnHeight;
2142
2143        mpFrame->VCLToCocoa( rect );
2144    }
2145    else
2146    {
2147        rect = NSMakeRect( aPosEvent.mnX, aPosEvent.mnY, aPosEvent.mnWidth, aPosEvent.mnHeight );
2148    }
2149
2150    return rect;
2151}
2152
2153-(id)parentAttribute {
2154    return reinterpret_cast<NSView*>(mpFrame->getNSWindow());
2155        //TODO: odd cast really needed for fdo#74121?
2156}
2157
2158-(css::accessibility::XAccessibleContext *)accessibleContext
2159{
2160    SolarMutexGuard aGuard;
2161
2162    [self insertRegisteredWrapperIntoWrapperRepository];
2163    if (mpChildWrapper)
2164        return [mpChildWrapper accessibleContext];
2165
2166    return nil;
2167}
2168
2169-(NSWindow*)windowForParent
2170{
2171    return mpFrame->getNSWindow();
2172}
2173
2174-(void)registerMouseEventListener: (id)theListener
2175{
2176  mpMouseEventListener = theListener;
2177}
2178
2179-(void)unregisterMouseEventListener: (id)theListener
2180{
2181    (void)theListener;
2182    mpMouseEventListener = nil;
2183}
2184
2185-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
2186{
2187  return [mDraggingDestinationHandler draggingEntered: sender];
2188}
2189
2190-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
2191{
2192  return [mDraggingDestinationHandler draggingUpdated: sender];
2193}
2194
2195-(void)draggingExited:(id <NSDraggingInfo>)sender
2196{
2197  [mDraggingDestinationHandler draggingExited: sender];
2198}
2199
2200-(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
2201{
2202  return [mDraggingDestinationHandler prepareForDragOperation: sender];
2203}
2204
2205-(BOOL)performDragOperation:(id <NSDraggingInfo>)sender
2206{
2207  return [mDraggingDestinationHandler performDragOperation: sender];
2208}
2209
2210-(void)concludeDragOperation:(id <NSDraggingInfo>)sender
2211{
2212  [mDraggingDestinationHandler concludeDragOperation: sender];
2213}
2214
2215-(void)registerDraggingDestinationHandler:(id)theHandler
2216{
2217  mDraggingDestinationHandler = theHandler;
2218}
2219
2220-(void)unregisterDraggingDestinationHandler:(id)theHandler
2221{
2222    (void)theHandler;
2223    mDraggingDestinationHandler = nil;
2224}
2225
2226-(void)endExtTextInput
2227{
2228    [self endExtTextInput:EndExtTextInputFlags::Complete];
2229}
2230
2231-(void)endExtTextInput:(EndExtTextInputFlags)nFlags
2232{
2233    // Prevent recursion from any additional [self insertText:] calls that
2234    // may be called when cancelling the native input method session
2235    if (mbInEndExtTextInput)
2236        return;
2237
2238    mbInEndExtTextInput = YES;
2239
2240    SolarMutexGuard aGuard;
2241
2242    NSTextInputContext *pInputContext = [NSTextInputContext currentInputContext];
2243    if (pInputContext)
2244    {
2245        // Cancel the native input method session
2246        [pInputContext discardMarkedText];
2247
2248        // Commit any uncommitted text. Note: when the delete key is used to
2249        // remove all uncommitted characters, the marked range will be zero
2250        // length but a SalEvent::EndExtTextInput must still be dispatched.
2251        if (mpLastMarkedText && [mpLastMarkedText length] && mMarkedRange.location != NSNotFound && mpFrame && AquaSalFrame::isAlive(mpFrame))
2252        {
2253            // If there is any marked text, SalEvent::EndExtTextInput may leave
2254            // the cursor hidden so commit the marked text to force the cursor
2255            // to be visible.
2256            mbInCommitMarkedText = YES;
2257            if (nFlags & EndExtTextInputFlags::Complete)
2258            {
2259                // Retain the last marked text as it will be released in
2260                // [self insertText:replacementText:]
2261                NSAttributedString *pText = [mpLastMarkedText retain];
2262                [self insertText:pText replacementRange:NSMakeRange(0, [mpLastMarkedText length])];
2263                [pText release];
2264            }
2265            else
2266            {
2267                [self insertText:[NSString string] replacementRange:NSMakeRange(0, 0)];
2268            }
2269            mbInCommitMarkedText = NO;
2270        }
2271
2272        [self unmarkText];
2273
2274        // If a different view is the input context's client, commit that
2275        // view's uncommitted text as well
2276        id<NSTextInputClient> pClient = [pInputContext client];
2277        if (pClient != self)
2278        {
2279            SalFrameView *pView = static_cast<SalFrameView*>(pClient);
2280            if ([pView isKindOfClass:[SalFrameView class]])
2281                [pView endExtTextInput];
2282            else
2283                [pClient unmarkText];
2284        }
2285    }
2286
2287    mbInEndExtTextInput = NO;
2288}
2289
2290-(void)deleteTextInputWantsNonRepeatKeyDown
2291{
2292    SolarMutexGuard aGuard;
2293
2294    // tdf#42437 Enable press-and-hold special character input method
2295    // Emulate the press-and-hold behavior of the TextEdit application by
2296    // dispatching backspace events to delete any marked characters. The
2297    // special character input method commits the marked characters so we must
2298    // delete the marked characters before the input method calls
2299    // [self insertText:replacementRange:].
2300    if (mbTextInputWantsNonRepeatKeyDown)
2301    {
2302        if ( mpLastMarkedText )
2303        {
2304            NSString *pChars = [mpLastMarkedText string];
2305            if ( pChars )
2306            {
2307                NSUInteger nLength = [pChars length];
2308                for ( NSUInteger i = 0; i < nLength; i++ )
2309                    [self deleteBackward:self];
2310            }
2311        }
2312
2313        [self unmarkText];
2314    }
2315}
2316
2317-(void)insertRegisteredWrapperIntoWrapperRepository
2318{
2319    SolarMutexGuard aGuard;
2320
2321    if (!mbNeedChildWrapper)
2322        return;
2323
2324    vcl::Window *pWindow = mpFrame->GetWindow();
2325    if (!pWindow)
2326        return;
2327
2328    mbNeedChildWrapper = NO;
2329
2330    ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext > xAccessibleContext( pWindow->GetAccessible()->getAccessibleContext() );
2331    assert(!mpChildWrapper);
2332    mpChildWrapper = [[SalFrameViewA11yWrapper alloc] initWithParent:self accessibleContext:xAccessibleContext];
2333    [AquaA11yFactory insertIntoWrapperRepository:mpChildWrapper forAccessibleContext:xAccessibleContext];
2334}
2335
2336-(void)registerWrapper
2337{
2338    [self revokeWrapper];
2339
2340    mbNeedChildWrapper = YES;
2341}
2342
2343-(void)revokeWrapper
2344{
2345    mbNeedChildWrapper = NO;
2346
2347    if (mpChildWrapper)
2348    {
2349        [AquaA11yFactory revokeWrapper:mpChildWrapper];
2350        [mpChildWrapper setAccessibilityParent:nil];
2351        [mpChildWrapper release];
2352        mpChildWrapper = nil;
2353    }
2354}
2355
2356-(id)accessibilityAttributeValue:(NSString *)pAttribute
2357{
2358    SolarMutexGuard aGuard;
2359
2360    [self insertRegisteredWrapperIntoWrapperRepository];
2361    if (mpChildWrapper)
2362        return [mpChildWrapper accessibilityAttributeValue:pAttribute];
2363    else
2364        return nil;
2365}
2366
2367-(BOOL)accessibilityIsIgnored
2368{
2369    SolarMutexGuard aGuard;
2370
2371    [self insertRegisteredWrapperIntoWrapperRepository];
2372    if (mpChildWrapper)
2373        return [mpChildWrapper accessibilityIsIgnored];
2374    else
2375        return YES;
2376}
2377
2378-(NSArray *)accessibilityAttributeNames
2379{
2380    SolarMutexGuard aGuard;
2381
2382    [self insertRegisteredWrapperIntoWrapperRepository];
2383    if (mpChildWrapper)
2384        return [mpChildWrapper accessibilityAttributeNames];
2385    else
2386        return [NSArray array];
2387}
2388
2389-(BOOL)accessibilityIsAttributeSettable:(NSString *)pAttribute
2390{
2391    SolarMutexGuard aGuard;
2392
2393    [self insertRegisteredWrapperIntoWrapperRepository];
2394    if (mpChildWrapper)
2395        return [mpChildWrapper accessibilityIsAttributeSettable:pAttribute];
2396    else
2397        return NO;
2398}
2399
2400-(NSArray *)accessibilityParameterizedAttributeNames
2401{
2402    SolarMutexGuard aGuard;
2403
2404    [self insertRegisteredWrapperIntoWrapperRepository];
2405    if (mpChildWrapper)
2406        return [mpChildWrapper accessibilityParameterizedAttributeNames];
2407    else
2408        return [NSArray array];
2409}
2410
2411-(BOOL)accessibilitySetOverrideValue:(id)pValue forAttribute:(NSString *)pAttribute
2412{
2413    SolarMutexGuard aGuard;
2414
2415    [self insertRegisteredWrapperIntoWrapperRepository];
2416    if (mpChildWrapper)
2417        return [mpChildWrapper accessibilitySetOverrideValue:pValue forAttribute:pAttribute];
2418    else
2419        return NO;
2420}
2421
2422-(void)accessibilitySetValue:(id)pValue forAttribute:(NSString *)pAttribute
2423{
2424    SolarMutexGuard aGuard;
2425
2426    [self insertRegisteredWrapperIntoWrapperRepository];
2427    if (mpChildWrapper)
2428        [mpChildWrapper accessibilitySetValue:pValue forAttribute:pAttribute];
2429}
2430
2431-(id)accessibilityAttributeValue:(NSString *)pAttribute forParameter:(id)pParameter
2432{
2433    SolarMutexGuard aGuard;
2434
2435    [self insertRegisteredWrapperIntoWrapperRepository];
2436    if (mpChildWrapper)
2437        return [mpChildWrapper accessibilityAttributeValue:pAttribute forParameter:pParameter];
2438    else
2439        return nil;
2440}
2441
2442-(id)accessibilityFocusedUIElement
2443{
2444    SolarMutexGuard aGuard;
2445
2446    [self insertRegisteredWrapperIntoWrapperRepository];
2447    if (mpChildWrapper)
2448        return [mpChildWrapper accessibilityFocusedUIElement];
2449    else
2450        return nil;
2451}
2452
2453-(NSString *)accessibilityActionDescription:(NSString *)pAction
2454{
2455    SolarMutexGuard aGuard;
2456
2457    [self insertRegisteredWrapperIntoWrapperRepository];
2458    if (mpChildWrapper)
2459        return [mpChildWrapper accessibilityActionDescription:pAction];
2460    else
2461        return nil;
2462}
2463
2464-(void)accessibilityPerformAction:(NSString *)pAction
2465{
2466    SolarMutexGuard aGuard;
2467
2468    [self insertRegisteredWrapperIntoWrapperRepository];
2469    if (mpChildWrapper)
2470        [mpChildWrapper accessibilityPerformAction:pAction];
2471}
2472
2473-(NSArray *)accessibilityActionNames
2474{
2475    SolarMutexGuard aGuard;
2476
2477    [self insertRegisteredWrapperIntoWrapperRepository];
2478    if (mpChildWrapper)
2479        return [mpChildWrapper accessibilityActionNames];
2480    else
2481        return [NSArray array];
2482}
2483
2484-(id)accessibilityHitTest:(NSPoint)aPoint
2485{
2486    SolarMutexGuard aGuard;
2487
2488    [self insertRegisteredWrapperIntoWrapperRepository];
2489    if (mpChildWrapper)
2490        return [mpChildWrapper accessibilityHitTest:aPoint];
2491    else
2492        return nil;
2493}
2494
2495-(id)accessibilityParent
2496{
2497    return [self window];
2498}
2499
2500-(NSArray *)accessibilityVisibleChildren
2501{
2502    return [self accessibilityChildren];
2503}
2504
2505-(NSArray *)accessibilitySelectedChildren
2506{
2507    SolarMutexGuard aGuard;
2508
2509    NSArray *pRet = [super accessibilityChildren];
2510
2511    [self insertRegisteredWrapperIntoWrapperRepository];
2512    if (mpChildWrapper)
2513        pRet = getMergedAccessibilityChildren(pRet, [mpChildWrapper accessibilitySelectedChildren]);
2514
2515    return pRet;
2516}
2517
2518-(NSArray *)accessibilityChildren
2519{
2520    SolarMutexGuard aGuard;
2521
2522    NSArray *pRet = [super accessibilityChildren];
2523
2524    [self insertRegisteredWrapperIntoWrapperRepository];
2525    if (mpChildWrapper)
2526        pRet = getMergedAccessibilityChildren(pRet, [mpChildWrapper accessibilityChildren]);
2527
2528    return pRet;
2529}
2530
2531-(NSArray <id<NSAccessibilityElement>> *)accessibilityChildrenInNavigationOrder
2532{
2533    return [self accessibilityChildren];
2534}
2535
2536@end
2537
2538@implementation SalFrameViewA11yWrapper
2539
2540-(id)initWithParent:(SalFrameView *)pParentView accessibleContext:(::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >&)rxAccessibleContext
2541{
2542    [super init];
2543
2544    maReferenceWrapper.rAccessibleContext = rxAccessibleContext;
2545
2546    mpParentView = pParentView;
2547    if (mpParentView)
2548    {
2549        [mpParentView retain];
2550        [self setAccessibilityParent:mpParentView];
2551    }
2552
2553    return self;
2554}
2555
2556-(void)dealloc
2557{
2558    if (mpParentView)
2559        [mpParentView release];
2560
2561    [super dealloc];
2562}
2563
2564-(id)parentAttribute
2565{
2566    if (mpParentView)
2567        return NSAccessibilityUnignoredAncestor(mpParentView);
2568    else
2569        return nil;
2570}
2571
2572-(void)setAccessibilityParent:(id)pObject
2573{
2574    if (mpParentView)
2575    {
2576        [mpParentView release];
2577        mpParentView = nil;
2578    }
2579
2580    if (pObject && [pObject isKindOfClass:[SalFrameView class]])
2581    {
2582        mpParentView = static_cast<SalFrameView *>(pObject);
2583        [mpParentView retain];
2584    }
2585
2586    [super setAccessibilityParent:mpParentView];
2587}
2588
2589-(id)windowAttribute
2590{
2591    if (mpParentView)
2592        return [mpParentView window];
2593    else
2594        return nil;
2595}
2596
2597-(NSWindow *)windowForParent
2598{
2599    return [self windowAttribute];
2600}
2601
2602@end
2603
2604/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2605