/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include "osx/a11yfactory.h" #include "osx/salframe.h" #include "osx/salframeview.h" #include "osx/salinst.h" #include "quartz/salgdi.h" #include "quartz/utils.h" #define WHEEL_EVENT_FACTOR 1.5 static sal_uInt16 ImplGetModifierMask( unsigned int nMask ) { sal_uInt16 nRet = 0; if( (nMask & NSShiftKeyMask) != 0 ) nRet |= KEY_SHIFT; if( (nMask & NSControlKeyMask) != 0 ) nRet |= KEY_MOD3; if( (nMask & NSAlternateKeyMask) != 0 ) nRet |= KEY_MOD2; if( (nMask & NSCommandKeyMask) != 0 ) nRet |= KEY_MOD1; return nRet; } static sal_uInt16 ImplMapCharCode( sal_Unicode aCode ) { static sal_uInt16 aKeyCodeMap[ 128 ] = { 0, 0, 0, 0, 0, 0, 0, 0, KEY_BACKSPACE, KEY_TAB, KEY_RETURN, 0, 0, KEY_RETURN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_TAB, 0, KEY_ESCAPE, 0, 0, 0, 0, KEY_SPACE, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_MULTIPLY, KEY_ADD, KEY_COMMA, KEY_SUBTRACT, KEY_POINT, KEY_DIVIDE, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, 0, 0, KEY_LESS, KEY_EQUAL, KEY_GREATER, 0, 0, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, 0, 0, 0, 0, 0, KEY_QUOTELEFT, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, 0, 0, 0, KEY_TILDE, KEY_BACKSPACE }; // Note: the mapping 0x7f should by rights be KEY_DELETE // however if you press "backspace" 0x7f is reported // whereas for "delete" 0xf728 gets reported // Note: the mapping of 0x19 to KEY_TAB is because for unknown reasons // tab alone is reported as 0x09 (as expected) but shift-tab is // reported as 0x19 (end of medium) static sal_uInt16 aFunctionKeyCodeMap[ 128 ] = { KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22, KEY_F23, KEY_F24, KEY_F25, KEY_F26, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_INSERT, KEY_DELETE, KEY_HOME, 0, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_UNDO, KEY_REPEAT, KEY_FIND, KEY_HELP, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; sal_uInt16 nKeyCode = 0; if( aCode < SAL_N_ELEMENTS( aKeyCodeMap) ) nKeyCode = aKeyCodeMap[ aCode ]; else if( aCode >= 0xf700 && aCode < 0xf780 ) nKeyCode = aFunctionKeyCodeMap[ aCode - 0xf700 ]; return nKeyCode; } static sal_uInt16 ImplMapKeyCode(sal_uInt16 nKeyCode) { /* http://stackoverflow.com/questions/2080312/where-can-i-find-a-list-of-key-codes-for-use-with-cocoas-nsevent-class/2080324#2080324 /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h */ static sal_uInt16 aKeyCodeMap[ 0x80 ] = { KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X, KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5, KEY_EQUAL, KEY_9, KEY_7, KEY_SUBTRACT, KEY_8, KEY_0, KEY_BRACKETRIGHT, KEY_0, KEY_U, KEY_BRACKETLEFT, KEY_I, KEY_P, KEY_RETURN, KEY_L, KEY_J, KEY_QUOTERIGHT, KEY_K, KEY_SEMICOLON, 0, KEY_COMMA, KEY_DIVIDE, KEY_N, KEY_M, KEY_POINT, KEY_TAB, KEY_SPACE, KEY_QUOTELEFT, KEY_DELETE, 0, KEY_ESCAPE, 0, 0, 0, KEY_CAPSLOCK, 0, 0, 0, 0, 0, 0, KEY_F17, KEY_DECIMAL, 0, KEY_MULTIPLY, 0, KEY_ADD, 0, 0, 0, 0, 0, KEY_DIVIDE, KEY_RETURN, 0, KEY_SUBTRACT, KEY_F18, KEY_F19, KEY_EQUAL, 0, 0, 0, 0, 0, 0, 0, 0, KEY_F20, 0, 0, 0, 0, 0, KEY_F5, KEY_F6, KEY_F7, KEY_F3, KEY_F8, KEY_F9, 0, KEY_F11, 0, KEY_F13, KEY_F16, KEY_F14, 0, KEY_F10, 0, KEY_F12, 0, KEY_F15, KEY_HELP, KEY_HOME, KEY_PAGEUP, KEY_DELETE, KEY_F4, KEY_END, KEY_F2, KEY_PAGEDOWN, KEY_F1, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0 }; if (nKeyCode < SAL_N_ELEMENTS(aKeyCodeMap)) return aKeyCodeMap[nKeyCode]; return 0; } // store the frame the mouse last entered static AquaSalFrame* s_pMouseFrame = nullptr; // store the last pressed button for enter/exit events // which lack that information static sal_uInt16 s_nLastButton = 0; // combinations of keys we need to handle ourselves static const struct ExceptionalKey { const sal_uInt16 nKeyCode; const unsigned int nModifierMask; } aExceptionalKeys[] = { { KEY_D, NSControlKeyMask | NSShiftKeyMask | NSAlternateKeyMask }, { KEY_D, NSCommandKeyMask | NSShiftKeyMask | NSAlternateKeyMask } }; static AquaSalFrame* getMouseContainerFrame() { AquaSalFrame* pDispatchFrame = nullptr; NSArray* aWindows = [NSWindow windowNumbersWithOptions:0]; for(NSUInteger i = 0; i < [aWindows count] && ! pDispatchFrame; i++ ) { NSWindow* pWin = [NSApp windowWithWindowNumber:[[aWindows objectAtIndex:i] integerValue]]; if( pWin && [pWin isMemberOfClass: [SalFrameWindow class]] && [(SalFrameWindow*)pWin containsMouse] ) pDispatchFrame = [(SalFrameWindow*)pWin getSalFrame]; } return pDispatchFrame; } @implementation SalFrameWindow -(id)initWithSalFrame: (AquaSalFrame*)pFrame { mDraggingDestinationHandler = nil; mpFrame = pFrame; NSRect aRect = { { static_cast(pFrame->maGeometry.nX), static_cast(pFrame->maGeometry.nY) }, { static_cast(pFrame->maGeometry.nWidth), static_cast(pFrame->maGeometry.nHeight) } }; pFrame->VCLToCocoa( aRect ); NSWindow* pNSWindow = [super initWithContentRect: aRect styleMask: mpFrame->getStyleMask() backing: NSBackingStoreBuffered defer: NO ]; #if MACOSX_SDK_VERSION < 101000 [pNSWindow useOptimizedDrawing: YES]; // OSX recommendation when there are no overlapping subviews within the receiver #endif // enable OSX>=10.7 fullscreen options if available and useful bool bAllowFullScreen = (SalFrameStyleFlags::NONE == (mpFrame->mnStyle & (SalFrameStyleFlags::DIALOG | SalFrameStyleFlags::TOOLTIP | SalFrameStyleFlags::SYSTEMCHILD | SalFrameStyleFlags::FLOAT | SalFrameStyleFlags::TOOLWINDOW | SalFrameStyleFlags::INTRO))); bAllowFullScreen &= (SalFrameStyleFlags::NONE == (~mpFrame->mnStyle & (SalFrameStyleFlags::SIZEABLE))); bAllowFullScreen &= (mpFrame->mpParent == nullptr); const SEL setCollectionBehavior = @selector(setCollectionBehavior:); if( bAllowFullScreen && [pNSWindow respondsToSelector: setCollectionBehavior]) { const int bMode= (bAllowFullScreen ? NSWindowCollectionBehaviorFullScreenPrimary : NSWindowCollectionBehaviorFullScreenAuxiliary); [pNSWindow performSelector:setCollectionBehavior withObject:reinterpret_cast(static_cast(bMode))]; } // disable OSX>=10.7 window restoration until we support it directly const SEL setRestorable = @selector(setRestorable:); if( [pNSWindow respondsToSelector: setRestorable]) { [pNSWindow performSelector:setRestorable withObject:reinterpret_cast(NO)]; } return (SalFrameWindow *)pNSWindow; } -(AquaSalFrame*)getSalFrame { return mpFrame; } -(void)displayIfNeeded { if( GetSalData() && GetSalData()->mpFirstInstance ) { comphelper::SolarMutex* pMutex = GetSalData()->mpFirstInstance->GetYieldMutex(); if( pMutex ) { pMutex->acquire(); [super displayIfNeeded]; pMutex->release(); } } } -(BOOL)containsMouse { // is this event actually inside that NSWindow ? NSPoint aPt = [NSEvent mouseLocation]; NSRect aFrameRect = [self frame]; BOOL bInRect = NSPointInRect( aPt, aFrameRect ); return bInRect; } -(BOOL)canBecomeKeyWindow { if( (mpFrame->mnStyle & ( SalFrameStyleFlags::FLOAT | SalFrameStyleFlags::TOOLTIP | SalFrameStyleFlags::INTRO )) == SalFrameStyleFlags::NONE ) return YES; if( mpFrame->mnStyle & SalFrameStyleFlags::OWNERDRAWDECORATION ) return YES; if( mpFrame->mbFullScreen ) return YES; return [super canBecomeKeyWindow]; } -(void)windowDidBecomeKey: (NSNotification*)pNotification { (void)pNotification; SolarMutexGuard aGuard; if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) { static const SalFrameStyleFlags nGuessDocument = SalFrameStyleFlags::MOVEABLE| SalFrameStyleFlags::SIZEABLE| SalFrameStyleFlags::CLOSEABLE; if( mpFrame->mpMenu ) mpFrame->mpMenu->setMainMenu(); else if( ! mpFrame->mpParent && ( (mpFrame->mnStyle & nGuessDocument) == nGuessDocument || // set default menu for e.g. help mpFrame->mbFullScreen ) ) // ser default menu for e.g. presentation { AquaSalMenu::setDefaultMenu(); } #if 0 // FIXME: we should disable menus while in modal mode // however from down here there is currently no reliable way to // find out when to do this if( (mpFrame->mpParent && mpFrame->mpParent->GetWindow()->IsInModalMode()) ) AquaSalMenu::enableMainMenu( false ); #endif mpFrame->CallCallback( SALEVENT_GETFOCUS, nullptr ); mpFrame->SendPaintEvent(); // repaint controls as active } } -(void)windowDidResignKey: (NSNotification*)pNotification { (void)pNotification; SolarMutexGuard aGuard; if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) { mpFrame->CallCallback(SALEVENT_LOSEFOCUS, nullptr); mpFrame->SendPaintEvent(); // repaint controls as inactive } } -(void)windowDidChangeScreen: (NSNotification*)pNotification { (void)pNotification; SolarMutexGuard aGuard; if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) mpFrame->screenParametersChanged(); } -(void)windowDidMove: (NSNotification*)pNotification { (void)pNotification; SolarMutexGuard aGuard; if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) { mpFrame->UpdateFrameGeometry(); mpFrame->CallCallback( SALEVENT_MOVE, nullptr ); } } -(void)windowDidResize: (NSNotification*)pNotification { (void)pNotification; SolarMutexGuard aGuard; if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) { mpFrame->UpdateFrameGeometry(); mpFrame->CallCallback( SALEVENT_RESIZE, nullptr ); mpFrame->SendPaintEvent(); } } -(void)windowDidMiniaturize: (NSNotification*)pNotification { (void)pNotification; SolarMutexGuard aGuard; if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) { mpFrame->mbShown = false; mpFrame->UpdateFrameGeometry(); mpFrame->CallCallback( SALEVENT_RESIZE, nullptr ); } } -(void)windowDidDeminiaturize: (NSNotification*)pNotification { (void)pNotification; SolarMutexGuard aGuard; if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) { mpFrame->mbShown = true; mpFrame->UpdateFrameGeometry(); mpFrame->CallCallback( SALEVENT_RESIZE, nullptr ); } } -(BOOL)windowShouldClose: (NSNotification*)pNotification { (void)pNotification; SolarMutexGuard aGuard; BOOL bRet = YES; if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) { // #i84461# end possible input mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, nullptr ); if( AquaSalFrame::isAlive( mpFrame ) ) { mpFrame->CallCallback( SALEVENT_CLOSE, nullptr ); bRet = NO; // application will close the window or not, AppKit shouldn't } } return bRet; } -(void)windowDidEnterFullScreen: (NSNotification*)pNotification { SolarMutexGuard aGuard; if( !mpFrame || !AquaSalFrame::isAlive( mpFrame)) return; mpFrame->mbFullScreen = true; (void)pNotification; } -(void)windowDidExitFullScreen: (NSNotification*)pNotification { SolarMutexGuard aGuard; if( !mpFrame || !AquaSalFrame::isAlive( mpFrame)) return; mpFrame->mbFullScreen = false; (void)pNotification; } -(void)dockMenuItemTriggered: (id)sender { (void)sender; SolarMutexGuard aGuard; if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) mpFrame->ToTop( SAL_FRAME_TOTOP_RESTOREWHENMIN | SAL_FRAME_TOTOP_GRABFOCUS ); } -(css::uno::Reference < css::accessibility::XAccessibleContext >)accessibleContext { return mpFrame -> GetWindow() -> GetAccessible() -> getAccessibleContext(); } -(NSDragOperation)draggingEntered:(id )sender { return [mDraggingDestinationHandler draggingEntered: sender]; } -(NSDragOperation)draggingUpdated:(id )sender { return [mDraggingDestinationHandler draggingUpdated: sender]; } -(void)draggingExited:(id )sender { [mDraggingDestinationHandler draggingExited: sender]; } -(BOOL)prepareForDragOperation:(id )sender { return [mDraggingDestinationHandler prepareForDragOperation: sender]; } -(BOOL)performDragOperation:(id )sender { return [mDraggingDestinationHandler performDragOperation: sender]; } -(void)concludeDragOperation:(id )sender { [mDraggingDestinationHandler concludeDragOperation: sender]; } -(void)registerDraggingDestinationHandler:(id)theHandler { mDraggingDestinationHandler = theHandler; } -(void)unregisterDraggingDestinationHandler:(id)theHandler { (void)theHandler; mDraggingDestinationHandler = nil; } @end @implementation SalFrameView +(void)unsetMouseFrame: (AquaSalFrame*)pFrame { if( pFrame == s_pMouseFrame ) s_pMouseFrame = nullptr; } -(id)initWithSalFrame: (AquaSalFrame*)pFrame { if ((self = [super initWithFrame: [NSWindow contentRectForFrameRect: [pFrame->getNSWindow() frame] styleMask: pFrame->mnStyleMask]]) != nil) { mDraggingDestinationHandler = nil; mpFrame = pFrame; mMarkedRange = NSMakeRange(NSNotFound, 0); mSelectedRange = NSMakeRange(NSNotFound, 0); mpReferenceWrapper = nil; mpMouseEventListener = nil; mpLastSuperEvent = nil; } mfLastMagnifyTime = 0.0; return self; } -(AquaSalFrame*)getSalFrame { return mpFrame; } -(void)resetCursorRects { if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) { // FIXME: does this leak the returned NSCursor of getCurrentCursor ? const NSRect aRect = { NSZeroPoint, NSMakeSize( mpFrame->maGeometry.nWidth, mpFrame->maGeometry.nHeight) }; [self addCursorRect: aRect cursor: mpFrame->getCurrentCursor()]; } } -(BOOL)acceptsFirstResponder { return YES; } -(BOOL)acceptsFirstMouse: (NSEvent*)pEvent { (void)pEvent; return YES; } -(BOOL)isOpaque { if( !mpFrame) return YES; if( !AquaSalFrame::isAlive( mpFrame)) return YES; if( !mpFrame->getClipPath()) return YES; return NO; } // helper class similar to a osl::Guard< comphelper::SolarMutex > for the // SalYieldMutex; the difference is that it only does tryToAcquire instead of // acquire so dreaded deadlocks like #i93512# are prevented class TryGuard { public: TryGuard() { mbGuarded = ImplSalYieldMutexTryToAcquire(); } ~TryGuard() { if( mbGuarded ) ImplSalYieldMutexRelease(); } bool IsGuarded() { return mbGuarded; } private: bool mbGuarded; }; -(void)drawRect: (NSRect)aRect { // HOTFIX: #i93512# prevent deadlocks if any other thread already has the SalYieldMutex TryGuard aTryGuard; if( !aTryGuard.IsGuarded() ) { // NOTE: the mpFrame access below is not guarded yet! // TODO: mpFrame et al need to be guarded by an independent mutex AquaSalGraphics* pGraphics = (mpFrame && AquaSalFrame::isAlive(mpFrame)) ? mpFrame->mpGraphics : nullptr; if( pGraphics ) { // we did not get the mutex so we cannot draw now => request to redraw later // convert the NSRect to a CGRect for Refreshrect() const CGRect aCGRect = {{aRect.origin.x,aRect.origin.y},{aRect.size.width,aRect.size.height}}; pGraphics->RefreshRect( aCGRect ); } return; } if( mpFrame && AquaSalFrame::isAlive( mpFrame ) ) { if( mpFrame->mpGraphics ) { mpFrame->mpGraphics->UpdateWindow( aRect ); if( mpFrame->getClipPath() ) [mpFrame->getNSWindow() invalidateShadow]; } } } -(void)sendMouseEventToFrame: (NSEvent*)pEvent button:(sal_uInt16)nButton eventtype:(sal_uInt16)nEvent { SolarMutexGuard aGuard; AquaSalFrame* pDispatchFrame = AquaSalFrame::GetCaptureFrame(); bool bIsCaptured = false; if( pDispatchFrame ) { bIsCaptured = true; if( nEvent == SALEVENT_MOUSELEAVE ) // no leave events if mouse is captured nEvent = SALEVENT_MOUSEMOVE; } else if( s_pMouseFrame ) pDispatchFrame = s_pMouseFrame; else pDispatchFrame = mpFrame; /* #i81645# Cocoa reports mouse events while a button is pressed to the window in which it was first pressed. This is reasonable and fine and gets one around most cases where on other platforms one uses CaptureMouse or XGrabPointer, however vcl expects mouse events to occur in the window the mouse is over, unless the mouse is explicitly captured. So we need to find the window the mouse is actually over for conformance with other platforms. */ if( ! bIsCaptured && nButton && pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) ) { // is this event actually inside that NSWindow ? NSPoint aPt = [NSEvent mouseLocation]; NSRect aFrameRect = [pDispatchFrame->getNSWindow() frame]; if ( ! NSPointInRect( aPt, aFrameRect ) ) { // no, it is not // now we need to find the one it may be in /* #i93756# we ant to get enumerate the application windows in z-order to check if any contains the mouse. This could be elegantly done with this code: // use NSApp to check windows in ZOrder whether they contain the mouse pointer NSWindow* pWindow = [NSApp makeWindowsPerform: @selector(containsMouse) inOrder: YES]; if( pWindow && [pWindow isMemberOfClass: [SalFrameWindow class]] ) pDispatchFrame = [(SalFrameWindow*)pWindow getSalFrame]; However if a non SalFrameWindow is on screen (like e.g. the file dialog) it can be hit with the containsMouse selector, which it doesn't support. Sadly NSApplication:makeWindowsPerform does not check (for performance reasons I assume) whether a window supports a selector before sending it. */ AquaSalFrame* pMouseFrame = getMouseContainerFrame(); if( pMouseFrame ) pDispatchFrame = pMouseFrame; } } if( pDispatchFrame && AquaSalFrame::isAlive( pDispatchFrame ) ) { pDispatchFrame->mnLastEventTime = static_cast( [pEvent timestamp] * 1000.0 ); pDispatchFrame->mnLastModifierFlags = [pEvent modifierFlags]; NSPoint aPt = [NSEvent mouseLocation]; pDispatchFrame->CocoaToVCL( aPt ); sal_uInt16 nModMask = ImplGetModifierMask( [pEvent modifierFlags] ); // #i82284# emulate ctrl left if( nModMask == KEY_MOD3 && nButton == MOUSE_LEFT ) { nModMask = 0; nButton = MOUSE_RIGHT; } SalMouseEvent aEvent; aEvent.mnTime = pDispatchFrame->mnLastEventTime; aEvent.mnX = static_cast(aPt.x) - pDispatchFrame->maGeometry.nX; aEvent.mnY = static_cast(aPt.y) - pDispatchFrame->maGeometry.nY; aEvent.mnButton = nButton; aEvent.mnCode = aEvent.mnButton | nModMask; // --- RTL --- (mirror mouse pos) if( AllSettings::GetLayoutRTL() ) aEvent.mnX = pDispatchFrame->maGeometry.nWidth-1-aEvent.mnX; pDispatchFrame->CallCallback( nEvent, &aEvent ); } } -(void)mouseDown: (NSEvent*)pEvent { if ( mpMouseEventListener != nil && [mpMouseEventListener respondsToSelector: @selector(mouseDown:)]) { [mpMouseEventListener mouseDown: [pEvent copyWithZone: nullptr]]; } s_nLastButton = MOUSE_LEFT; [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SALEVENT_MOUSEBUTTONDOWN]; } -(void)mouseDragged: (NSEvent*)pEvent { if ( mpMouseEventListener != nil && [mpMouseEventListener respondsToSelector: @selector(mouseDragged:)]) { [mpMouseEventListener mouseDragged: [pEvent copyWithZone: nullptr]]; } s_nLastButton = MOUSE_LEFT; [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SALEVENT_MOUSEMOVE]; } -(void)mouseUp: (NSEvent*)pEvent { s_nLastButton = 0; [self sendMouseEventToFrame:pEvent button:MOUSE_LEFT eventtype:SALEVENT_MOUSEBUTTONUP]; } -(void)mouseMoved: (NSEvent*)pEvent { s_nLastButton = 0; [self sendMouseEventToFrame:pEvent button:0 eventtype:SALEVENT_MOUSEMOVE]; } -(void)mouseEntered: (NSEvent*)pEvent { s_pMouseFrame = mpFrame; // #i107215# the only mouse events we get when inactive are enter/exit // actually we would like to have all of them, but better none than some if( [NSApp isActive] ) [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SALEVENT_MOUSEMOVE]; } -(void)mouseExited: (NSEvent*)pEvent { if( s_pMouseFrame == mpFrame ) s_pMouseFrame = nullptr; // #i107215# the only mouse events we get when inactive are enter/exit // actually we would like to have all of them, but better none than some if( [NSApp isActive] ) [self sendMouseEventToFrame:pEvent button:s_nLastButton eventtype:SALEVENT_MOUSELEAVE]; } -(void)rightMouseDown: (NSEvent*)pEvent { s_nLastButton = MOUSE_RIGHT; [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SALEVENT_MOUSEBUTTONDOWN]; } -(void)rightMouseDragged: (NSEvent*)pEvent { s_nLastButton = MOUSE_RIGHT; [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SALEVENT_MOUSEMOVE]; } -(void)rightMouseUp: (NSEvent*)pEvent { s_nLastButton = 0; [self sendMouseEventToFrame:pEvent button:MOUSE_RIGHT eventtype:SALEVENT_MOUSEBUTTONUP]; } -(void)otherMouseDown: (NSEvent*)pEvent { if( [pEvent buttonNumber] == 2 ) { s_nLastButton = MOUSE_MIDDLE; [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SALEVENT_MOUSEBUTTONDOWN]; } else s_nLastButton = 0; } -(void)otherMouseDragged: (NSEvent*)pEvent { if( [pEvent buttonNumber] == 2 ) { s_nLastButton = MOUSE_MIDDLE; [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SALEVENT_MOUSEMOVE]; } else s_nLastButton = 0; } -(void)otherMouseUp: (NSEvent*)pEvent { s_nLastButton = 0; if( [pEvent buttonNumber] == 2 ) [self sendMouseEventToFrame:pEvent button:MOUSE_MIDDLE eventtype:SALEVENT_MOUSEBUTTONUP]; } - (void)magnifyWithEvent: (NSEvent*)pEvent { SolarMutexGuard aGuard; // TODO: ?? -(float)magnification; if( AquaSalFrame::isAlive( mpFrame ) ) { const NSTimeInterval fMagnifyTime = [pEvent timestamp]; mpFrame->mnLastEventTime = static_cast( fMagnifyTime * 1000.0 ); mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; // check if this is a new series of magnify events static const NSTimeInterval fMaxDiffTime = 0.3; const bool bNewSeries = (fMagnifyTime - mfLastMagnifyTime > fMaxDiffTime); if( bNewSeries ) mfMagnifyDeltaSum = 0.0; mfMagnifyDeltaSum += [pEvent magnification]; mfLastMagnifyTime = [pEvent timestamp]; // TODO: change to 0.1 when CommandWheelMode::ZOOM handlers allow finer zooming control static const float fMagnifyFactor = 0.25*500; // steps are 500 times smaller for -magnification static const float fMinMagnifyStep = 15.0 / fMagnifyFactor; if( fabs(mfMagnifyDeltaSum) <= fMinMagnifyStep ) return; // adapt NSEvent-sensitivity to application expectations // TODO: rather make CommandWheelMode::ZOOM handlers smarter const float fDeltaZ = mfMagnifyDeltaSum * fMagnifyFactor; int nDeltaZ = FRound( fDeltaZ ); if( !nDeltaZ ) { // handle new series immediately if( !bNewSeries ) return; nDeltaZ = (fDeltaZ >= 0.0) ? +1 : -1; } // eventually give credit for delta sum mfMagnifyDeltaSum -= nDeltaZ / fMagnifyFactor; NSPoint aPt = [NSEvent mouseLocation]; mpFrame->CocoaToVCL( aPt ); SalWheelMouseEvent aEvent; aEvent.mnTime = mpFrame->mnLastEventTime; aEvent.mnX = static_cast(aPt.x) - mpFrame->maGeometry.nX; aEvent.mnY = static_cast(aPt.y) - mpFrame->maGeometry.nY; aEvent.mnCode = ImplGetModifierMask( mpFrame->mnLastModifierFlags ); aEvent.mnCode |= KEY_MOD1; // we want zooming, no scrolling aEvent.mbDeltaIsPixel = TRUE; // --- RTL --- (mirror mouse pos) if( AllSettings::GetLayoutRTL() ) aEvent.mnX = mpFrame->maGeometry.nWidth-1-aEvent.mnX; aEvent.mnDelta = nDeltaZ; aEvent.mnNotchDelta = (nDeltaZ >= 0) ? +1 : -1; if( aEvent.mnDelta == 0 ) aEvent.mnDelta = aEvent.mnNotchDelta; aEvent.mbHorz = FALSE; aEvent.mnScrollLines = nDeltaZ; if( aEvent.mnScrollLines == 0 ) aEvent.mnScrollLines = 1; mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); } } - (void)rotateWithEvent: (NSEvent*)pEvent { //Rotation : -(float)rotation; // TODO: create new CommandType so rotation is available to the applications (void)pEvent; } - (void)swipeWithEvent: (NSEvent*)pEvent { SolarMutexGuard aGuard; if( AquaSalFrame::isAlive( mpFrame ) ) { mpFrame->mnLastEventTime = static_cast( [pEvent timestamp] * 1000.0 ); mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; // merge pending scroll wheel events CGFloat dX = 0.0; CGFloat dY = 0.0; for(;;) { dX += [pEvent deltaX]; dY += [pEvent deltaY]; NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSScrollWheelMask untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ]; if( !pNextEvent ) break; pEvent = pNextEvent; } NSPoint aPt = [NSEvent mouseLocation]; mpFrame->CocoaToVCL( aPt ); SalWheelMouseEvent aEvent; aEvent.mnTime = mpFrame->mnLastEventTime; aEvent.mnX = static_cast(aPt.x) - mpFrame->maGeometry.nX; aEvent.mnY = static_cast(aPt.y) - mpFrame->maGeometry.nY; aEvent.mnCode = ImplGetModifierMask( mpFrame->mnLastModifierFlags ); aEvent.mbDeltaIsPixel = TRUE; // --- RTL --- (mirror mouse pos) if( AllSettings::GetLayoutRTL() ) aEvent.mnX = mpFrame->maGeometry.nWidth-1-aEvent.mnX; if( dX != 0.0 ) { aEvent.mnDelta = static_cast(floor(dX)); aEvent.mnNotchDelta = (dX < 0) ? -1 : +1; if( aEvent.mnDelta == 0 ) aEvent.mnDelta = aEvent.mnNotchDelta; aEvent.mbHorz = TRUE; aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL; mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); } if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame )) { aEvent.mnDelta = static_cast(floor(dY)); aEvent.mnNotchDelta = (dY < 0) ? -1 : +1; if( aEvent.mnDelta == 0 ) aEvent.mnDelta = aEvent.mnNotchDelta; aEvent.mbHorz = FALSE; aEvent.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL; mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); } } } -(void)scrollWheel: (NSEvent*)pEvent { SolarMutexGuard aGuard; if( AquaSalFrame::isAlive( mpFrame ) ) { mpFrame->mnLastEventTime = static_cast( [pEvent timestamp] * 1000.0 ); mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; // merge pending scroll wheel events CGFloat dX = 0.0; CGFloat dY = 0.0; for(;;) { dX += [pEvent deltaX]; dY += [pEvent deltaY]; NSEvent* pNextEvent = [NSApp nextEventMatchingMask: NSScrollWheelMask untilDate: nil inMode: NSDefaultRunLoopMode dequeue: YES ]; if( !pNextEvent ) break; pEvent = pNextEvent; } NSPoint aPt = [NSEvent mouseLocation]; mpFrame->CocoaToVCL( aPt ); SalWheelMouseEvent aEvent; aEvent.mnTime = mpFrame->mnLastEventTime; aEvent.mnX = static_cast(aPt.x) - mpFrame->maGeometry.nX; aEvent.mnY = static_cast(aPt.y) - mpFrame->maGeometry.nY; aEvent.mnCode = ImplGetModifierMask( mpFrame->mnLastModifierFlags ); aEvent.mbDeltaIsPixel = FALSE; // --- RTL --- (mirror mouse pos) if( AllSettings::GetLayoutRTL() ) aEvent.mnX = mpFrame->maGeometry.nWidth-1-aEvent.mnX; if( dX != 0.0 ) { aEvent.mnDelta = static_cast(floor(dX)); aEvent.mnNotchDelta = (dX < 0) ? -1 : +1; if( aEvent.mnDelta == 0 ) aEvent.mnDelta = aEvent.mnNotchDelta; aEvent.mbHorz = TRUE; aEvent.mnScrollLines = fabs(dX) / WHEEL_EVENT_FACTOR; if( aEvent.mnScrollLines == 0 ) aEvent.mnScrollLines = 1; mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); } if( dY != 0.0 && AquaSalFrame::isAlive( mpFrame ) ) { aEvent.mnDelta = static_cast(floor(dY)); aEvent.mnNotchDelta = (dY < 0) ? -1 : +1; if( aEvent.mnDelta == 0 ) aEvent.mnDelta = aEvent.mnNotchDelta; aEvent.mbHorz = FALSE; aEvent.mnScrollLines = fabs(dY) / WHEEL_EVENT_FACTOR; if( aEvent.mnScrollLines == 0 ) aEvent.mnScrollLines = 1; mpFrame->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); } } } -(void)keyDown: (NSEvent*)pEvent { SolarMutexGuard aGuard; if( AquaSalFrame::isAlive( mpFrame ) ) { mpLastEvent = pEvent; mbInKeyInput = true; mbNeedSpecialKeyHandle = false; mbKeyHandled = false; mpFrame->mnLastEventTime = static_cast( [pEvent timestamp] * 1000.0 ); mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; if( ! [self handleKeyDownException: pEvent] ) { NSArray* pArray = [NSArray arrayWithObject: pEvent]; [self interpretKeyEvents: pArray]; } mbInKeyInput = false; } } -(BOOL)handleKeyDownException:(NSEvent*)pEvent { // check for a very special set of modified characters NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers]; if( pUnmodifiedString && [pUnmodifiedString length] == 1 ) { /* #i103102# key events with command and alternate don't make it through interpretKeyEvents (why?). Try to dispatch them here first, if not successful continue normally */ if( (mpFrame->mnLastModifierFlags & (NSAlternateKeyMask | NSCommandKeyMask)) == (NSAlternateKeyMask | NSCommandKeyMask) ) { if( [self sendSingleCharacter: mpLastEvent] ) return YES; } unichar keyChar = [pUnmodifiedString characterAtIndex: 0]; sal_uInt16 nKeyCode = ImplMapCharCode( keyChar ); // Caution: should the table grow to more than 5 or 6 entries, // we must consider moving it to a kind of hash map const unsigned int nExceptions = SAL_N_ELEMENTS( aExceptionalKeys ); for( unsigned int i = 0; i < nExceptions; i++ ) { if( nKeyCode == aExceptionalKeys[i].nKeyCode && (mpFrame->mnLastModifierFlags & aExceptionalKeys[i].nModifierMask) == aExceptionalKeys[i].nModifierMask ) { [self sendKeyInputAndReleaseToFrame: nKeyCode character: 0]; return YES; } } } return NO; } -(void)flagsChanged: (NSEvent*)pEvent { SolarMutexGuard aGuard; if( AquaSalFrame::isAlive( mpFrame ) ) { mpFrame->mnLastEventTime = static_cast( [pEvent timestamp] * 1000.0 ); mpFrame->mnLastModifierFlags = [pEvent modifierFlags]; } } -(void)insertText:(id)aString replacementRange:(NSRange)replacementRange { (void) replacementRange; // FIXME: surely it must be used SolarMutexGuard aGuard; if( AquaSalFrame::isAlive( mpFrame ) ) { NSString* pInsert = nil; if( [aString isMemberOfClass: [NSAttributedString class]] ) pInsert = [aString string]; else pInsert = aString; int nLen = 0; if( pInsert && ( nLen = [pInsert length] ) > 0 ) { OUString aInsertString( GetOUString( pInsert ) ); // aCharCode initializer is safe since aInsertString will at least contain '\0' sal_Unicode aCharCode = *aInsertString.getStr(); if( nLen == 1 && aCharCode < 0x80 && aCharCode > 0x1f && ! [self hasMarkedText ] ) { sal_uInt16 nKeyCode = ImplMapCharCode( aCharCode ); unsigned int nLastModifiers = mpFrame->mnLastModifierFlags; // #i99567# // find out the unmodified key code // sanity check if( mpLastEvent && ( [mpLastEvent type] == NSKeyDown || [mpLastEvent type] == NSKeyUp ) ) { // get unmodified string NSString* pUnmodifiedString = [mpLastEvent charactersIgnoringModifiers]; if( pUnmodifiedString && [pUnmodifiedString length] == 1 ) { // map the unmodified key code unichar keyChar = [pUnmodifiedString characterAtIndex: 0]; nKeyCode = ImplMapCharCode( keyChar ); } nLastModifiers = [mpLastEvent modifierFlags]; } // #i99567# // applications and vcl's edit fields ignore key events with ALT // however we're at a place where we know text should be inserted // so it seems we need to strip the Alt modifier here if( (nLastModifiers & (NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask)) == NSAlternateKeyMask ) { nLastModifiers = 0; } [self sendKeyInputAndReleaseToFrame: nKeyCode character: aCharCode modifiers: nLastModifiers]; } else { SalExtTextInputEvent aEvent; aEvent.mnTime = mpFrame->mnLastEventTime; aEvent.maText = aInsertString; aEvent.mpTextAttr = nullptr; aEvent.mnCursorPos = aInsertString.getLength(); aEvent.mnCursorFlags = 0; aEvent.mbOnlyCursor = FALSE; mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, &aEvent ); if( AquaSalFrame::isAlive( mpFrame ) ) mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, nullptr ); } } else { SalExtTextInputEvent aEvent; aEvent.mnTime = mpFrame->mnLastEventTime; aEvent.maText.clear(); aEvent.mpTextAttr = nullptr; aEvent.mnCursorPos = 0; aEvent.mnCursorFlags = 0; aEvent.mbOnlyCursor = FALSE; mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, &aEvent ); if( AquaSalFrame::isAlive( mpFrame ) ) mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, nullptr ); } mbKeyHandled = true; [self unmarkText]; } } -(void)insertTab: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_TAB character: '\t' modifiers: 0]; } -(void)insertBacktab: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: (KEY_TAB | KEY_SHIFT) character: '\t' modifiers: 0]; } -(void)moveLeft: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: 0]; } -(void)moveLeftAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_LEFT character: 0 modifiers: NSShiftKeyMask]; } -(void)moveBackwardAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_BACKWARD character: 0 modifiers: 0]; } -(void)moveRight: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: 0]; } -(void)moveRightAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_RIGHT character: 0 modifiers: NSShiftKeyMask]; } -(void)moveForwardAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_FORWARD character: 0 modifiers: 0]; } -(void)moveWordLeft: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_BACKWARD character: 0 modifiers: 0]; } -(void)moveWordBackward: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_BACKWARD character: 0 modifiers: 0]; } -(void)moveWordBackwardAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_BACKWARD character: 0 modifiers: 0]; } -(void)moveWordLeftAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_BACKWARD character: 0 modifiers: 0]; } -(void)moveWordRight: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_FORWARD character: 0 modifiers: 0]; } -(void)moveWordForward: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_WORD_FORWARD character: 0 modifiers: 0]; } -(void)moveWordForwardAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_FORWARD character: 0 modifiers: 0]; } -(void)moveWordRightAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD_FORWARD character: 0 modifiers: 0]; } -(void)moveToEndOfLine: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_LINE character: 0 modifiers: 0]; } -(void)moveToRightEndOfLine: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_LINE character: 0 modifiers: 0]; } -(void)moveToEndOfLineAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_LINE character: 0 modifiers: 0]; } -(void)moveToRightEndOfLineAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_LINE character: 0 modifiers: 0]; } -(void)moveToBeginningOfLine: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; } -(void)moveToLeftEndOfLine: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; } -(void)moveToBeginningOfLineAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; } -(void)moveToLeftEndOfLineAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; } -(void)moveToEndOfParagraph: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)moveToEndOfParagraphAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)moveParagraphForward: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)moveParagraphForwardAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)moveToBeginningOfParagraph: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)moveParagraphBackward: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)moveToBeginningOfParagraphAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)moveParagraphBackwardAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)moveToEndOfDocument: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0 modifiers: 0]; } -(void)scrollToEndOfDocument: (id)aSender { (void)aSender; // this is not exactly what we should do, but it makes "End" and "Shift-End" behave consistent [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_END_OF_DOCUMENT character: 0 modifiers: 0]; } -(void)moveToEndOfDocumentAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_END_OF_DOCUMENT character: 0 modifiers: 0]; } -(void)moveToBeginningOfDocument: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0 modifiers: 0]; } -(void)scrollToBeginningOfDocument: (id)aSender { (void)aSender; // this is not exactly what we should do, but it makes "Home" and "Shift-Home" behave consistent [self sendKeyInputAndReleaseToFrame: css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT character: 0 modifiers: 0]; } -(void)moveToBeginningOfDocumentAndModifySelection: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT character: 0 modifiers: 0]; } -(void)moveUp: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_UP character: 0 modifiers: 0]; } -(void)moveDown: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_DOWN character: 0 modifiers: 0]; } -(void)insertNewline: (id)aSender { (void)aSender; // #i91267# make enter and shift-enter work by evaluating the modifiers [self sendKeyInputAndReleaseToFrame: KEY_RETURN character: '\n' modifiers: mpFrame->mnLastModifierFlags]; } -(void)deleteBackward: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0]; } -(void)deleteForward: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_DELETE character: 0x7f modifiers: 0]; } -(void)deleteBackwardByDecomposingPreviousCharacter: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_BACKSPACE character: '\b' modifiers: 0]; } -(void)deleteWordBackward: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_WORD_BACKWARD character: 0 modifiers: 0]; } -(void)deleteWordForward: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_WORD_FORWARD character: 0 modifiers: 0]; } -(void)deleteToBeginningOfLine: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_BEGIN_OF_LINE character: 0 modifiers: 0]; } -(void)deleteToEndOfLine: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_END_OF_LINE character: 0 modifiers: 0]; } -(void)deleteToBeginningOfParagraph: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_BEGIN_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)deleteToEndOfParagraph: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::DELETE_TO_END_OF_PARAGRAPH character: 0 modifiers: 0]; } -(void)insertLineBreak: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::INSERT_LINEBREAK character: 0 modifiers: 0]; } -(void)insertParagraphSeparator: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::INSERT_PARAGRAPH character: 0 modifiers: 0]; } -(void)selectWord: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_WORD character: 0 modifiers: 0]; } -(void)selectLine: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_LINE character: 0 modifiers: 0]; } -(void)selectParagraph: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_PARAGRAPH character: 0 modifiers: 0]; } -(void)selectAll: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: css::awt::Key::SELECT_ALL character: 0 modifiers: 0]; } -(void)cancelOperation: (id)aSender { (void)aSender; [self sendKeyInputAndReleaseToFrame: KEY_ESCAPE character: 0x1b modifiers: 0]; } -(void)noop: (id)aSender { (void)aSender; if( ! mbKeyHandled ) { if( ! [self sendSingleCharacter:mpLastEvent] ) { /* prevent recursion */ if( mpLastEvent != mpLastSuperEvent && [NSApp respondsToSelector: @selector(sendSuperEvent:)] ) { id pLastSuperEvent = mpLastSuperEvent; mpLastSuperEvent = mpLastEvent; [NSApp performSelector:@selector(sendSuperEvent:) withObject: mpLastEvent]; mpLastSuperEvent = pLastSuperEvent; std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent ); if( it != GetSalData()->maKeyEventAnswer.end() ) it->second = true; } } } } -(BOOL)sendKeyInputAndReleaseToFrame: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar { return [self sendKeyInputAndReleaseToFrame: nKeyCode character: aChar modifiers: mpFrame->mnLastModifierFlags]; } -(BOOL)sendKeyInputAndReleaseToFrame: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod { return [self sendKeyToFrameDirect: nKeyCode character: aChar modifiers: nMod] || [self sendSingleCharacter: mpLastEvent]; } -(BOOL)sendKeyToFrameDirect: (sal_uInt16)nKeyCode character: (sal_Unicode)aChar modifiers: (unsigned int)nMod { SolarMutexGuard aGuard; long nRet = 0; if( AquaSalFrame::isAlive( mpFrame ) ) { SalKeyEvent aEvent; aEvent.mnTime = mpFrame->mnLastEventTime; aEvent.mnCode = nKeyCode | ImplGetModifierMask( nMod ); aEvent.mnCharCode = aChar; aEvent.mnRepeat = FALSE; nRet = mpFrame->CallCallback( SALEVENT_KEYINPUT, &aEvent ); std::map< NSEvent*, bool >::iterator it = GetSalData()->maKeyEventAnswer.find( mpLastEvent ); if( it != GetSalData()->maKeyEventAnswer.end() ) it->second = nRet != 0; if( AquaSalFrame::isAlive( mpFrame ) ) mpFrame->CallCallback( SALEVENT_KEYUP, &aEvent ); } return nRet ? YES : NO; } -(BOOL)sendSingleCharacter: (NSEvent *)pEvent { NSString* pUnmodifiedString = [pEvent charactersIgnoringModifiers]; if( pUnmodifiedString && [pUnmodifiedString length] == 1 ) { unichar keyChar = [pUnmodifiedString characterAtIndex: 0]; sal_uInt16 nKeyCode = ImplMapCharCode( keyChar ); if (nKeyCode == 0) { sal_uInt16 nOtherKeyCode = [pEvent keyCode]; nKeyCode = ImplMapKeyCode(nOtherKeyCode); } if( nKeyCode != 0 ) { // don't send unicodes in the private use area if( keyChar >= 0xf700 && keyChar < 0xf780 ) keyChar = 0; BOOL bRet = [self sendKeyToFrameDirect: nKeyCode character: keyChar modifiers: mpFrame->mnLastModifierFlags]; mbInKeyInput = false; return bRet; } } return NO; } // NSTextInput/NSTextInputClient protocol - (NSArray *)validAttributesForMarkedText { return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, nil]; } - (BOOL)hasMarkedText { BOOL bHasMarkedText; bHasMarkedText = ( mMarkedRange.location != NSNotFound ) && ( mMarkedRange.length != 0 ); // hack to check keys like "Control-j" if( mbInKeyInput ) { mbNeedSpecialKeyHandle = true; } // FIXME: // #i106901# // if we come here outside of mbInKeyInput, this is likely to be because // of the keyboard viewer. For unknown reasons having no marked range // in this case causes a crash. So we say we have a marked range anyway // This is a hack, since it is not understood what a) causes that crash // and b) why we should have a marked range at this point. if( ! mbInKeyInput ) bHasMarkedText = YES; return bHasMarkedText; } - (NSRange)markedRange { // FIXME: // #i106901# // if we come here outside of mbInKeyInput, this is likely to be because // of the keyboard viewer. For unknown reasons having no marked range // in this case causes a crash. So we say we have a marked range anyway // This is a hack, since it is not understood what a) causes that crash // and b) why we should have a marked range at this point. if( ! mbInKeyInput ) return NSMakeRange( 0, 0 ); return [self hasMarkedText] ? mMarkedRange : NSMakeRange( NSNotFound, 0 ); } - (NSRange)selectedRange { return mSelectedRange; } - (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange replacementRange:(NSRange)replacementRange { (void) replacementRange; // FIXME - use it! SolarMutexGuard aGuard; if( ![aString isKindOfClass:[NSAttributedString class]] ) aString = [[[NSAttributedString alloc] initWithString:aString] autorelease]; NSRange rangeToReplace = [self hasMarkedText] ? [self markedRange] : [self selectedRange]; if( rangeToReplace.location == NSNotFound ) { mMarkedRange = NSMakeRange( selRange.location, [aString length] ); mSelectedRange = NSMakeRange( selRange.location, selRange.length ); } else { mMarkedRange = NSMakeRange( rangeToReplace.location, [aString length] ); mSelectedRange = NSMakeRange( rangeToReplace.location + selRange.location, selRange.length ); } int len = [aString length]; SalExtTextInputEvent aInputEvent; aInputEvent.mnTime = mpFrame->mnLastEventTime; aInputEvent.mbOnlyCursor = FALSE; if( len > 0 ) { NSString *pString = [aString string]; OUString aInsertString( GetOUString( pString ) ); std::vector aInputFlags = std::vector( std::max( 1, len ), 0 ); for ( int i = 0; i < len; i++ ) { unsigned int nUnderlineValue; NSRange effectiveRange; effectiveRange = NSMakeRange(i, 1); nUnderlineValue = [[aString attribute:NSUnderlineStyleAttributeName atIndex:i effectiveRange:&effectiveRange] unsignedIntValue]; switch (nUnderlineValue & 0xff) { case NSUnderlineStyleSingle: aInputFlags[i] = EXTTEXTINPUT_ATTR_UNDERLINE; break; case NSUnderlineStyleThick: aInputFlags[i] = EXTTEXTINPUT_ATTR_UNDERLINE | EXTTEXTINPUT_ATTR_HIGHLIGHT; break; case NSUnderlineStyleDouble: aInputFlags[i] = EXTTEXTINPUT_ATTR_BOLDUNDERLINE; break; default: aInputFlags[i] = EXTTEXTINPUT_ATTR_HIGHLIGHT; break; } } aInputEvent.maText = aInsertString; aInputEvent.mnCursorPos = selRange.location; aInputEvent.mpTextAttr = &aInputFlags[0]; mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, static_cast(&aInputEvent) ); } else { aInputEvent.maText.clear(); aInputEvent.mnCursorPos = 0; aInputEvent.mnCursorFlags = 0; aInputEvent.mpTextAttr = nullptr; mpFrame->CallCallback( SALEVENT_EXTTEXTINPUT, static_cast(&aInputEvent) ); mpFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, nullptr ); } mbKeyHandled= true; } - (void)unmarkText { mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0); } - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { (void) aRange; (void) actualRange; // FIXME - Implement return nil; } - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint { (void)thePoint; // FIXME return 0; } - (NSInteger)conversationIdentifier { return reinterpret_cast(self); } - (void)doCommandBySelector:(SEL)aSelector { if( AquaSalFrame::isAlive( mpFrame ) ) { #if OSL_DEBUG_LEVEL > 1 // fprintf( stderr, "SalFrameView: doCommandBySelector %s\n", (char*)aSelector ); #endif if( (mpFrame->mnICOptions & InputContextFlags::Text) && aSelector != nullptr && [self respondsToSelector: aSelector] ) { [self performSelector: aSelector]; } else { [self sendSingleCharacter:mpLastEvent]; } } mbKeyHandled = true; } -(void)clearLastEvent { mpLastEvent = nil; } - (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { // FIXME - These should probably be used? (void) aRange; (void) actualRange; SolarMutexGuard aGuard; SalExtTextInputPosEvent aPosEvent; mpFrame->CallCallback( SALEVENT_EXTTEXTINPUTPOS, static_cast(&aPosEvent) ); NSRect rect; rect.origin.x = aPosEvent.mnX + mpFrame->maGeometry.nX; rect.origin.y = aPosEvent.mnY + mpFrame->maGeometry.nY + 4; // add some space for underlines rect.size.width = aPosEvent.mnWidth; rect.size.height = aPosEvent.mnHeight; mpFrame->VCLToCocoa( rect ); return rect; } -(id)parentAttribute { return reinterpret_cast(mpFrame->getNSWindow()); //TODO: odd cast really needed for fdo#74121? } -(css::accessibility::XAccessibleContext *)accessibleContext { if ( !mpReferenceWrapper ) { // some frames never become visible .. vcl::Window *pWindow = mpFrame -> GetWindow(); if ( ! pWindow ) return nil; mpReferenceWrapper = new ReferenceWrapper; mpReferenceWrapper -> rAccessibleContext = pWindow -> /*GetAccessibleChildWindow( 0 ) ->*/ GetAccessible() -> getAccessibleContext(); [ AquaA11yFactory insertIntoWrapperRepository: self forAccessibleContext: mpReferenceWrapper -> rAccessibleContext ]; } return [ super accessibleContext ]; } -(NSWindow*)windowForParent { return mpFrame->getNSWindow(); } -(void)registerMouseEventListener: (id)theListener { mpMouseEventListener = theListener; } -(void)unregisterMouseEventListener: (id)theListener { (void)theListener; mpMouseEventListener = nil; } -(NSDragOperation)draggingEntered:(id )sender { return [mDraggingDestinationHandler draggingEntered: sender]; } -(NSDragOperation)draggingUpdated:(id )sender { return [mDraggingDestinationHandler draggingUpdated: sender]; } -(void)draggingExited:(id )sender { [mDraggingDestinationHandler draggingExited: sender]; } -(BOOL)prepareForDragOperation:(id )sender { return [mDraggingDestinationHandler prepareForDragOperation: sender]; } -(BOOL)performDragOperation:(id )sender { return [mDraggingDestinationHandler performDragOperation: sender]; } -(void)concludeDragOperation:(id )sender { [mDraggingDestinationHandler concludeDragOperation: sender]; } -(void)registerDraggingDestinationHandler:(id)theHandler { mDraggingDestinationHandler = theHandler; } -(void)unregisterDraggingDestinationHandler:(id)theHandler { (void)theHandler; mDraggingDestinationHandler = nil; } @end /* vim:set shiftwidth=4 softtabstop=4 expandtab: */