1/* 2 PPGNUstepGlue_DisableMenuKeysDuringTextEntry.m 3 4 Copyright 2014-2018 Josh Freeman 5 http://www.twilightedge.com 6 7 This file is part of PikoPixel for GNUstep. 8 PikoPixel is a graphical application for drawing & editing pixel-art images. 9 10 PikoPixel is free software: you can redistribute it and/or modify it under 11 the terms of the GNU Affero General Public License as published by the 12 Free Software Foundation, either version 3 of the License, or (at your 13 option) any later version approved for PikoPixel by its copyright holder (or 14 an authorized proxy). 15 16 PikoPixel is distributed in the hope that it will be useful, but WITHOUT ANY 17 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18 FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more 19 details. 20 21 You should have received a copy of the GNU Affero General Public License 22 along with this program. If not, see <http://www.gnu.org/licenses/>. 23*/ 24 25// GNUstep workaround to prevent keys which have menu-key-equivalents (such as '0' & <Delete>) 26// from being intercepted while typing text in a textfield. 27 28#ifdef GNUSTEP 29 30#import <Cocoa/Cocoa.h> 31#import "NSObject_PPUtilities.h" 32#import "PPAppBootUtilities.h" 33#import "PPKeyCancellableWindow.h" 34 35 36@implementation NSObject (PPGNUstepGlue_DisableMenuKeysDuringTextEntry) 37 38+ (void) ppGSGlue_DisableMenuKeysDuringTextEntry_InstallPatches 39{ 40 // Patch -[NSWindow performKeyEquivalent:] for all NSWindow subclasses that use textfields 41 // in PikoPixel: NSOpenPanel, NSSavePanel, NSColorPanel, NSPanel, PPKeyCancellableWindow. 42 // This used to be a patch on a single class (NSWindow), but subclass inheritance of 43 // patched methods no longer seems to work on the gcc runtime (perhaps it never worked, or 44 // perhaps it's an issue with recent versions) apparently due to subclasses not updating 45 // their method tables when the patch is installed on a superclass (calling 46 // performKeyEquivalent: on an NSWindow works fine, but calling it on an NSPanel jumps 47 // directly to the original NSWindow method without calling the patch), so workaround is to 48 // manually patch every class where the patched functionality is needed (don't assume it 49 // inherits the patch correctly). 50 // Subclasses should be patched before their parent classes, to prevent swapping methods 51 // that are already swapped in the inherited method table (if patch inheritance is working 52 // correctly). 53 54 macroSwizzleInstanceMethod(NSOpenPanel, performKeyEquivalent:, 55 ppGSPatch_DisableMenuKeysDuringTextEntry_PerformKeyEquivalent:); 56 57 macroSwizzleInstanceMethod(NSSavePanel, performKeyEquivalent:, 58 ppGSPatch_DisableMenuKeysDuringTextEntry_PerformKeyEquivalent:); 59 60 macroSwizzleInstanceMethod(NSColorPanel, performKeyEquivalent:, 61 ppGSPatch_DisableMenuKeysDuringTextEntry_PerformKeyEquivalent:); 62 63 macroSwizzleInstanceMethod(NSPanel, performKeyEquivalent:, 64 ppGSPatch_DisableMenuKeysDuringTextEntry_PerformKeyEquivalent:); 65 66 macroSwizzleInstanceMethod(PPKeyCancellableWindow, performKeyEquivalent:, 67 ppGSPatch_DisableMenuKeysDuringTextEntry_PerformKeyEquivalent:); 68} 69 70+ (void) load 71{ 72 macroPerformNSObjectSelectorAfterAppLoads( 73 ppGSGlue_DisableMenuKeysDuringTextEntry_InstallPatches); 74} 75 76@end 77 78@implementation NSWindow (PPGNUstepGlue_DisableMenuKeysDuringTextEntry) 79 80// PATCH: -[NSWindow performKeyEquivalent:] 81// Override prevents GNUstep from intercepting keypresses that are (non-modifier-key) menu-item 82// equivalents when editing text; For keyDown events, when there's no modifier key pressed, 83// if there's an active fieldEditor (window's firstResponder), the event's passed directly to it 84// via its keyDown: method 85 86- (BOOL) ppGSPatch_DisableMenuKeysDuringTextEntry_PerformKeyEquivalent: (NSEvent *) theEvent 87{ 88 static NSUInteger disallowedFieldEditorModifierFlags = 89 NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask; 90 91 if (([theEvent type] == NSKeyDown) 92 && !([theEvent modifierFlags] & disallowedFieldEditorModifierFlags)) 93 { 94 NSText *fieldEditor = [self fieldEditor: NO forObject: nil]; 95 96 if (fieldEditor && (fieldEditor == [self firstResponder])) 97 { 98 [fieldEditor keyDown: theEvent]; 99 100 return YES; 101 } 102 } 103 104 return [self ppGSPatch_DisableMenuKeysDuringTextEntry_PerformKeyEquivalent: theEvent]; 105} 106 107@end 108 109#endif // GNUSTEP 110 111