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