17

One of my projects is an testing app where a student shouldn't be able to easily look up words while they're typing.

It's relatively easy to turn off automatic spelling checking in a NSTextView via setContinuousSpellCheckingEnabled: and setAutomaticSpellingCorrectionEnabled:.

I just discovered that it's very trivial for students to simply tap with three fingers upon any selected word in any app and up pops a helpful window containing a dictionary, thesaurus and even a Wikipedia entry if the word can be found there.

What Lookup functionality looks like

This is great functionality for 99% of MacOS apps but not appropriate for my testing app.

Now after a few months, Apple has provided me with a (undocumented and subtle) solution that works for 10.8 only and I may eventually provide it in the answers down below, but I need to have a solution that works for 10.7 as well (which is where this functionality came in).

There are three possible plans of attack on this problem but I'm not sure how to approach any of these three:

1)

I need to block this Lookup functionality from happening in this text view.

2)

I've already tried to delete the dictionary preferences (if they exist; if the user never opened Dictionary.app, there are no preferences) and the dictionary cache files (in "~/Library/Cache", but this doesn't seem to improve the situation.

3)

Or is there a way to be able to detect the Trackpad setting that says "Use Lookup when doing a three fingered tap"? It's probably in some com.apple.*.plist somewhere or detectable via "defaults" but I'm not certain where.

EDIT:

Only a bit of time left to hopefully solve this problem and award a bounty. Here is the approach that I was attempting with "defaults":

defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadThreeFingerTapGesture -bool false
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadThreeFingerDoubleTapGesture -bool false

But I'm not 100% certain these are the correct gestures/keywords to type in. And even after typing them in (and verifying they were correctly saved via "defaults read com.apple.driver.AppleBluetoothMultitouch.trackpad"), the dictionary LookUp window still appears.

Now here is the only thing that works, but it works only under MacOS 10.8 (which is where these methods were exposed/brought in). Simply override these two methods in your NSTextView subclass:

- (void)quickLookWithEvent:(NSEvent *)event;
- (void)quickLookPreviewItems:(id)sender;
Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215

3 Answers3

5

I suspect that the dictionary lookup is implemented in one of two ways, as a Service or via the NSTextInputClient protocol. So, I would override -validRequestorForSendType:returnType: in your text subclass to see if it's invoked for a three-finger tap. Anyway, it sounds like for your app you want to return NO for all services. You should also override writeSelectionToPasteboard:types: to return NO.

If that doesn't do it, try overriding the methods of NSTextInputClient to see if they're being called. In particular, the -attributedString and -attributedSubstringForProposedRange:actualRange: methods would be how the dictionary lookup is obtaining the text. Making both of those return nil should prevent it from working. Mind you, the NSTextInputClient protocol is central to the use of input methods and the press-and-hold access to characters with accents and diacritics, so you may break that. I don't know if you'll be able to distinguish between requests from a normal input method vs. from the dictionary lookup gesture.

If all else fails, you can implement a custom text view rather than using NSTextView. It's not trivial, but you'll have full control over its behavior.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Overriding "`validRequestorForSendType:returnType:`" didn't hit breakpoints or NSLogs when doing a three fingered tap. But the following paragraph -- overriding "`attributeSubstringForProposedRange:`" & "`attributedString`" both hit when doing a TFT Lookup. If the first call fails, then "`attributedString`" is the fallback method that's called. Returning NULL for both results in a "`NSInternalInconsistencyException`" thrown from "`LUTSMDocumentTextAccessor`", but the app keeps going with no crash or subsequent bad behavior. This is looking *very* promising, Ken. I wonder what the downside is? – Michael Dautermann Mar 18 '13 at 06:38
  • The only downsides I can think of are A) there's an exception being thrown and usually when exceptions get thrown, there's a subsequent crash... unless something catches it. Perhaps there's some place in the Lookup architecture is catching it? B) I tested all the text & limited formatting capability I make available to my app's users and I couldn't see the two "`attributedString`" methods being called for anything aside from three fingered tap lookup. I'm hoping that I'm not accidentally disabling something in the app by returning NULL from those two methods. – Michael Dautermann Mar 18 '13 at 06:59
  • 1
    Is the exception from your process? The methods in question have to do with input methods, which actually run in a separate process. You can try to override some of the other `NSTextInputClient` methods to see if you can convince the dictionary lookup IM to not even expect it can obtain a string. Another thought, you can override `-[NSView inputContext]` to return `nil` to disable IMs entirely. As to downsides, have you tried Asian IMs? Have you tried press-and-hold entry of accented characters? IMs such as those are what I would expect might break. – Ken Thomases Mar 18 '13 at 15:34
  • I'll be trying out Asian input methods in the morning (before the bounty period runs out :-) and see how it looks. The exception appears in the Xcode console while running the app from Xcode, so would that count as a child process or a service process that isn't the main (app) process? – Michael Dautermann Mar 19 '13 at 04:37
  • That may be an exception in your app process. Check the Console.app. Logged messages there show the sender. If it is in your process, then something must have caught it or your process would have terminated. The question is what. Also, if the three-finger tap is entirely processed within an app's process, as opposed to being a separate program which interacts with the target app as an input method, then there should be some way to intercept the event. Have you tried trapping the `NSResponder` gesture methods? – Ken Thomases Mar 19 '13 at 10:54
  • An obvious thing to try to avoid the exception is to return the empty string `@""` instead of `nil` from those `NSTextInputClient` methods. – Ken Thomases Mar 19 '13 at 14:40
  • Okay, with Asian & Hebrew (right to left) input methods nothing seems to break with the input clients, so that's good. My current workaround is to return the attributed substring of "`NSMakeRange(0,1)`" because returning an empty string causes other (private or undocumented?) methods to be called for LookUp to figure out which word is selected. Returning that one character still throws a "`Lookup: Unhandled exception 'NSRangeException' caught in -[LULookupDefinitionModule showDefinitionByHotKey]]`" line in the Xcode console, though. Oh, if only I could swizzle "`showDefinitionByHotKey`" away. – Michael Dautermann Mar 19 '13 at 15:17
  • Thanks for the bounty. Can you get a backtrace from the exception? That may reveal the entry point for where the lookup begins. – Ken Thomases Mar 19 '13 at 16:09
1

This answer to a similar question fixes the problem without throwing exceptions with the trade off that it use a private API: https://stackoverflow.com/a/20618984/959140

Simply override -(void)quickLookWithEvent:(NSEvent *)event in your subclass and don't do anything with the event.

Community
  • 1
  • 1
Robin Andersson
  • 5,150
  • 3
  • 25
  • 44
0

When the Dictionary screen pops up, go to File (or maybe it was edit) / Preferences and uncheck all the options so that no actual dictionary options are checked, and close the app, and it no longer shows up - for the most part - once in a while while trying to use the mousepad to select a string of text for copy and/or cutting, it shows up, but not constantly like before...

TriumphST
  • 1,194
  • 1
  • 10
  • 17