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