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