Attachment "tkc_inputManager_20110323.patch" to
ticket [3205153fff]
added by
arobert3434
2011-03-23 05:16:08.
Index: tkMacOSXKeyEvent.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/macosx/tkMacOSXKeyEvent.c,v
retrieving revision 1.31
diff -u -p -r1.31 tkMacOSXKeyEvent.c
--- tkMacOSXKeyEvent.c 24 Jan 2011 15:20:50 -0000 1.31
+++ tkMacOSXKeyEvent.c 22 Mar 2011 22:09:47 -0000
@@ -21,6 +21,8 @@
#define TK_MAC_DEBUG_KEYBOARD
#endif
*/
+#define NS_KEYLOG 0
+
static Tk_Window grabWinPtr = NULL;
/* Current grab window, NULL if no grab. */
@@ -28,9 +30,17 @@ static Tk_Window keyboardGrabWinPtr = NU
/* Current keyboard grab window. */
static NSModalSession modalSession = NULL;
+static void setupXEvent(XEvent *xEvent, NSWindow *w, unsigned int state);
+
+static BOOL processingCompose = NO;
+
+static int caret_x = 0, caret_y = 0, caret_height = 0;
+
+
#pragma mark TKApplication(TKKeyEvent)
@implementation TKApplication(TKKeyEvent)
+
- (NSEvent *) tkProcessKeyEvent: (NSEvent *) theEvent
{
#ifdef TK_MAC_DEBUG_EVENTS
@@ -38,11 +48,18 @@ static NSModalSession modalSession = NUL
#endif
NSWindow* w;
NSEventType type = [theEvent type];
- NSUInteger modifiers, len;
+ NSUInteger modifiers, len = 0;
BOOL repeat = NO;
unsigned short keyCode;
NSString *characters = nil, *charactersIgnoringModifiers = nil;
static NSUInteger savedModifiers = 0;
+ static NSMutableArray *nsEvArray;
+
+ if (nsEvArray == nil)
+ {
+ nsEvArray = [[NSMutableArray alloc] initWithCapacity: 1];
+ processingCompose = NO;
+ }
switch (type) {
case NSKeyUp:
@@ -50,12 +67,13 @@ static NSModalSession modalSession = NUL
repeat = [theEvent isARepeat];
characters = [theEvent characters];
charactersIgnoringModifiers = [theEvent charactersIgnoringModifiers];
+ len = [charactersIgnoringModifiers length];
case NSFlagsChanged:
modifiers = [theEvent modifierFlags];
keyCode = [theEvent keyCode];
w = [self windowWithWindowNumber:[theEvent windowNumber]];
#ifdef TK_MAC_DEBUG_EVENTS
- TKLog(@"-[%@(%p) %s] %d %u %@ %@ %u %@", [self class], self, _cmd, repeat, modifiers, characters, charactersIgnoringModifiers, keyCode, w);
+ NSLog(@"-[%@(%p) %s] r=%d mods=%u '%@' '%@' code=%u c=%d %@ %d", [self class], self, _cmd, repeat, modifiers, characters, charactersIgnoringModifiers, keyCode,([charactersIgnoringModifiers length] == 0) ? 0 : [charactersIgnoringModifiers characterAtIndex: 0], w, type);
#endif
break;
@@ -63,123 +81,343 @@ static NSModalSession modalSession = NUL
return theEvent;
}
- unsigned int state = 0;
+ if (!processingCompose) {
+ unsigned int state = 0;
- if (modifiers & NSAlphaShiftKeyMask) {
- state |= LockMask;
- }
- if (modifiers & NSShiftKeyMask) {
- state |= ShiftMask;
- }
- if (modifiers & NSControlKeyMask) {
- state |= ControlMask;
- }
- if (modifiers & NSCommandKeyMask) {
- state |= Mod1Mask; /* command key */
- }
- if (modifiers & NSAlternateKeyMask) {
- state |= Mod2Mask; /* option key */
- }
- if (modifiers & NSNumericPadKeyMask) {
- state |= Mod3Mask;
- }
- if (modifiers & NSFunctionKeyMask) {
- state |= Mod4Mask;
- }
+ if (modifiers & NSAlphaShiftKeyMask) {
+ state |= LockMask;
+ }
+ if (modifiers & NSShiftKeyMask) {
+ state |= ShiftMask;
+ }
+ if (modifiers & NSControlKeyMask) {
+ state |= ControlMask;
+ }
+ if (modifiers & NSCommandKeyMask) {
+ state |= Mod1Mask; /* command key */
+ }
+ if (modifiers & NSAlternateKeyMask) {
+ state |= Mod2Mask; /* option key */
+ }
+ if (modifiers & NSNumericPadKeyMask) {
+ state |= Mod3Mask;
+ }
+ if (modifiers & NSFunctionKeyMask) {
+ state |= Mod4Mask;
+ }
+
+ /*
+ * The focus must be in the FrontWindow on the Macintosh. We then query Tk
+ * to determine the exact Tk window that owns the focus.
+ */
+
+ TkWindow *winPtr = TkMacOSXGetTkWindow(w);
+ Tk_Window tkwin = (Tk_Window) winPtr;
+
+ if (!tkwin) {
+ TkMacOSXDbgMsg("tkwin == NULL");
+ return theEvent;
+ }
+ tkwin = (Tk_Window) winPtr->dispPtr->focusPtr;
+ if (!tkwin) {
+ TkMacOSXDbgMsg("tkwin == NULL");
+ return theEvent;
+ }
+
+ /*
+ * If it's a function key, or we have modifiers other than Shift or Alt,
+ * pass it straight to Tk. Otherwise we'll send for input processing.
+ */
+ int code = ([charactersIgnoringModifiers length] == 0) ?
+ 0 : [charactersIgnoringModifiers characterAtIndex: 0];
+ if (type != NSKeyDown || (code < 0x20 || code > 0x80)
+ || (len > 0 && state && state != Mod2Mask
+ && state != ShiftMask && state != LockMask)) {
+
+ XEvent xEvent;
+ setupXEvent(&xEvent, w, state);
+
+ if (type == NSFlagsChanged) {
+ if (savedModifiers > modifiers) {
+ xEvent.xany.type = KeyRelease;
+ } else {
+ xEvent.xany.type = KeyPress;
+ }
+
+ /*
+ * Use special '-1' to signify a special keycode to our platform
+ * specific code in tkMacOSXKeyboard.c. This is rather like what
+ * happens on Windows.
+ */
+
+ xEvent.xany.send_event = -1;
+
+ /*
+ * Set keycode (which was zero) to the changed modifier
+ */
+
+ xEvent.xkey.keycode = (modifiers ^ savedModifiers);
+ } else {
+ if (type == NSKeyUp || repeat) {
+ xEvent.xany.type = KeyRelease;
+ } else {
+ xEvent.xany.type = KeyPress;
+ }
+
+ if ([characters length] > 0) {
+ xEvent.xkey.keycode = (keyCode << 16) | (UInt16)
+ [characters characterAtIndex:0];
+ if (![characters getCString:xEvent.xkey.trans_chars
+ maxLength:XMaxTransChars encoding:NSUTF8StringEncoding]) {
+ /* prevent SF bug 2907388 here (crash on some composite characters) */
+ //PENDING: we might not need this anymore
+ TkMacOSXDbgMsg("characters too long");
+ return theEvent;
+ }
+ }
+
+ if (len) {
+ xEvent.xkey.nbytes = [charactersIgnoringModifiers characterAtIndex:0];
+ if (len > 1) {
+ TkMacOSXDbgMsg("more than one charactersIgnoringModifiers");
+ }
+ }
+ if (repeat) {
+ Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
+ xEvent.xany.type = KeyPress;
+ xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
+ }
+ }
+ Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
+ savedModifiers = modifiers;
+ return theEvent;
+ } /* if send straight to TK */
+
+ } /* if not processing compose */
+
+ if (type == NSKeyDown) {
+ if (NS_KEYLOG)
+ fprintf (stderr, "keyDown: %s compose sequence.\n",
+ processingCompose == YES ? "Continue" : "Begin");
+ processingCompose = YES;
+ [nsEvArray addObject: theEvent];
+ [[w contentView] interpretKeyEvents: nsEvArray];
+ [nsEvArray removeObject: theEvent];
+ }
- /*
- * The focus must be in the FrontWindow on the Macintosh. We then query Tk
- * to determine the exact Tk window that owns the focus.
- */
+ savedModifiers = modifiers;
- TkWindow *winPtr = TkMacOSXGetTkWindow(w);
- Tk_Window tkwin = (Tk_Window) winPtr;
+ return theEvent;
+}
+@end
- if (!tkwin) {
- TkMacOSXDbgMsg("tkwin == NULL");
- return theEvent;
- }
- tkwin = (Tk_Window) winPtr->dispPtr->focusPtr;
- if (!tkwin) {
- TkMacOSXDbgMsg("tkwin == NULL");
- return theEvent;
+
+
+@implementation TKContentView(TKKeyEvent)
+/* <NSTextInput> implementation (called through interpretKeyEvents:]). */
+
+/* <NSTextInput>: called when done composing;
+ NOTE: also called when we delete over working text, followed immed.
+ by doCommandBySelector: deleteBackward: */
+- (void)insertText: (id)aString
+{
+ int i, len = [(NSString *)aString length];
+ XEvent xEvent;
+
+ if (NS_KEYLOG)
+ NSLog (@"insertText '%@'\tlen = %d", aString, len);
+ processingCompose = NO;
+
+ /* first, clear any working text */
+ if (workingText != nil)
+ [self deleteWorkingText];
+
+ /* now insert the string as keystrokes */
+ setupXEvent(&xEvent, [self window], 0);
+ xEvent.xany.type = KeyPress;
+
+ for (i =0; i<len; i++)
+ {
+ xEvent.xkey.keycode = (UInt16) [aString characterAtIndex: i];
+ [[aString substringWithRange: NSMakeRange(i,1)]
+ getCString: xEvent.xkey.trans_chars
+ maxLength: XMaxTransChars encoding: NSUTF8StringEncoding];
+ xEvent.xkey.nbytes = strlen(xEvent.xkey.trans_chars);
+ Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
}
+}
- XEvent xEvent;
- memset(&xEvent, 0, sizeof(XEvent));
- xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
- xEvent.xany.send_event = false;
- xEvent.xany.display = Tk_Display(tkwin);
- xEvent.xany.window = Tk_WindowId(tkwin);
-
- xEvent.xkey.root = XRootWindow(Tk_Display(tkwin), 0);
- xEvent.xkey.subwindow = None;
- xEvent.xkey.time = TkpGetMS();
- xEvent.xkey.state = state;
- xEvent.xkey.same_screen = true;
- xEvent.xkey.trans_chars[0] = 0;
- xEvent.xkey.nbytes = 0;
-
- if (type == NSFlagsChanged) {
- if (savedModifiers > modifiers) {
- xEvent.xany.type = KeyRelease;
- } else {
- xEvent.xany.type = KeyPress;
- }
+/* <NSTextInput>: inserts display of composing characters */
+- (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
+{
+ NSString *str = [aString respondsToSelector: @selector (string)] ?
+ [aString string] : aString;
+ if (NS_KEYLOG)
+ NSLog (@"setMarkedText '%@' len =%d range %d from %d", str, [str length],
+ selRange.length, selRange.location);
+
+ if (workingText != nil)
+ [self deleteWorkingText];
+ if ([str length] == 0)
+ return;
- /*
- * Use special '-1' to signify a special keycode to our platform
- * specific code in tkMacOSXKeyboard.c. This is rather like what
- * happens on Windows.
- */
-
- xEvent.xany.send_event = -1;
-
- /*
- * Set keycode (which was zero) to the changed modifier
- */
-
- xEvent.xkey.keycode = (modifiers ^ savedModifiers);
- } else {
- if (type == NSKeyUp || repeat) {
- xEvent.xany.type = KeyRelease;
- } else {
- xEvent.xany.type = KeyPress;
- }
+ processingCompose = YES;
+ workingText = [str copy];
-/* prevent SF bug 2907388 here (crash on composite characters) */
-if ([characters length] > 0) {
- xEvent.xkey.keycode = (keyCode << 16) | (UInt16)
- [characters characterAtIndex:0];
- if (![characters getCString:xEvent.xkey.trans_chars
- maxLength:XMaxTransChars encoding:NSUTF8StringEncoding]) {
- TkMacOSXDbgMsg("characters too long");
- return theEvent;
- }
+ //PENDING: insert workingText underlined
}
-/* end workaround */
- len = [charactersIgnoringModifiers length];
- if (len) {
- xEvent.xkey.nbytes = [charactersIgnoringModifiers characterAtIndex:0];
- if (len > 1) {
- TkMacOSXDbgMsg("more than one charactersIgnoringModifiers");
- }
- }
- if (repeat) {
- Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
- xEvent.xany.type = KeyPress;
- xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
- }
+
+/* delete display of composing characters [not in <NSTextInput>] */
+- (void)deleteWorkingText
+{
+ if (workingText == nil)
+ return;
+ if (NS_KEYLOG)
+ NSLog(@"deleteWorkingText len = %d\n", [workingText length]);
+ [workingText release];
+ workingText = nil;
+ processingCompose = NO;
+
+ //PENDING: delete working text
+}
+
+
+- (BOOL)hasMarkedText
+{
+ return workingText != nil;
+}
+
+
+- (NSRange)markedRange
+{
+ NSRange rng = workingText != nil
+ ? NSMakeRange (0, [workingText length]) : NSMakeRange (NSNotFound, 0);
+ if (NS_KEYLOG)
+ NSLog (@"markedRange request");
+ return rng;
+}
+
+
+- (void)unmarkText
+{
+ if (NS_KEYLOG)
+ NSLog (@"unmark (accept) text");
+ [self deleteWorkingText];
+ processingCompose = NO;
+}
+
+
+/* used to position char selection windows, etc. */
+- (NSRect)firstRectForCharacterRange: (NSRange)theRange
+{
+ NSRect rect;
+ NSPoint pt;
+
+ pt.x = caret_x;
+ pt.y = caret_y;
+
+ pt = [self convertPoint: pt toView: nil];
+ pt = [[self window] convertBaseToScreen: pt];
+ pt.y -= caret_height;
+
+ rect.origin = pt;
+ rect.size.width = caret_height;
+ rect.size.height = caret_height;
+ return rect;
+}
+
+
+- (long)conversationIdentifier
+{
+ return (long)self;
+}
+
+
+- (void)doCommandBySelector: (SEL)aSelector
+{
+ if (NS_KEYLOG)
+ NSLog (@"doCommandBySelector: %@", NSStringFromSelector (aSelector));
+ processingCompose = NO;
+ if (aSelector == @selector (deleteBackward:))
+ {
+ /* happens when user backspaces over an ongoing composition:
+ throw a 'delete' into the event queue */
+ XEvent xEvent;
+ setupXEvent(&xEvent, [self window], 0);
+ xEvent.xany.type = KeyPress;
+ xEvent.xkey.nbytes = 1;
+ xEvent.xkey.keycode = (0x33 << 16) | 0x7F;
+ xEvent.xkey.trans_chars[0] = 0x7F;
+ xEvent.xkey.trans_chars[1] = 0x0;
+ Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
}
- Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
- savedModifiers = modifiers;
+}
- return theEvent;
+
+- (NSArray *)validAttributesForMarkedText
+{
+ static NSArray *arr = nil;
+ if (arr == nil) arr = [NSArray new];
+ /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
+ return arr;
+}
+
+
+- (NSRange)selectedRange
+{
+ if (NS_KEYLOG)
+ NSLog (@"selectedRange request");
+ return NSMakeRange (NSNotFound, 0);
+}
+
+
+- (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
+{
+ if (NS_KEYLOG)
+ NSLog (@"characterIndexForPoint request");
+ return 0;
+}
+
+
+- (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
+{
+ static NSAttributedString *str = nil;
+ if (str == nil) str = [NSAttributedString new];
+ if (NS_KEYLOG)
+ NSLog (@"attributedSubstringFromRange request");
+ return str;
}
+/* End <NSTextInput> impl. */
+
@end
+
+/*
+ * Set up basic fields in xevent for keyboard input.
+ */
+static void
+setupXEvent(XEvent *xEvent, NSWindow *w, unsigned int state)
+{
+ TkWindow *winPtr = TkMacOSXGetTkWindow(w);
+ Tk_Window tkwin = (Tk_Window) winPtr;
+
+ memset(xEvent, 0, sizeof(XEvent));
+ xEvent->xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
+ xEvent->xany.send_event = false;
+ xEvent->xany.display = Tk_Display(tkwin);
+ xEvent->xany.window = Tk_WindowId(tkwin);
+
+ xEvent->xkey.root = XRootWindow(Tk_Display(tkwin), 0);
+ xEvent->xkey.subwindow = None;
+ xEvent->xkey.time = TkpGetMS();
+ xEvent->xkey.state = state;
+ xEvent->xkey.same_screen = true;
+ xEvent->xkey.trans_chars[0] = 0;
+ xEvent->xkey.nbytes = 0;
+}
+
#pragma mark -
/*
@@ -346,6 +584,42 @@ Tk_SetCaretPos(
int y,
int height)
{
+ TkCaret *caretPtr = &(((TkWindow *) tkwin)->dispPtr->caret);
+
+ /*
+ * Prevent processing anything if the values haven't changed. Windows only
+ * has one display, so we can do this with statics.
+ */
+
+ if ((caretPtr->winPtr == ((TkWindow *) tkwin))
+ && (caretPtr->x == x) && (caretPtr->y == y)) {
+ return;
+ }
+
+ caretPtr->winPtr = ((TkWindow *) tkwin);
+ caretPtr->x = x;
+ caretPtr->y = y;
+ caretPtr->height = height;
+
+ /*
+ * As in Windows, adjust to the toplevel to get the coords right.
+ */
+
+ while (!Tk_IsTopLevel(tkwin)) {
+ x += Tk_X(tkwin);
+ y += Tk_Y(tkwin);
+ tkwin = Tk_Parent(tkwin);
+ if (tkwin == NULL) {
+ return;
+ }
+ }
+
+ /* But adjust for fact that NS uses flipped view. */
+ y = Tk_Height(tkwin) - y;
+
+ caret_x = x;
+ caret_y = y;
+ caret_height = height;
}
/*
Index: tkMacOSXPrivate.h
===================================================================
RCS file: /cvsroot/tktoolkit/tk/macosx/tkMacOSXPrivate.h,v
retrieving revision 1.12
diff -u -p -r1.12 tkMacOSXPrivate.h
--- tkMacOSXPrivate.h 7 Jul 2009 08:08:18 -0000 1.12
+++ tkMacOSXPrivate.h 22 Mar 2011 22:09:47 -0000
@@ -331,13 +331,18 @@ VISIBILITY_HIDDEN
@end
VISIBILITY_HIDDEN
-@interface TKContentView : NSView {
+@interface TKContentView : NSView <NSTextInput> {
@private
id _savedSubviews;
BOOL _subviewsSetAside;
+ NSString *workingText;
}
@end
+@interface TKContentView(TKKeyEvent)
+- (void) deleteWorkingText;
+@end
+
VISIBILITY_HIDDEN
@interface TKWindow : NSWindow
@end