summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorculler <culler>2019-10-25 17:58:58 (GMT)
committerculler <culler>2019-10-25 17:58:58 (GMT)
commitd4a398392db6a69b9a99d89a9aa8e10b5d423a65 (patch)
treee857a8584a2ec1571b0d6da9d05d1f932a0d8178
parent87f5fe063fabe957d6b3b116952d83b7c575e794 (diff)
parentc7c2b3b52832d1b78f18359507d3b971ec1506e2 (diff)
downloadtk-d4a398392db6a69b9a99d89a9aa8e10b5d423a65.zip
tk-d4a398392db6a69b9a99d89a9aa8e10b5d423a65.tar.gz
tk-d4a398392db6a69b9a99d89a9aa8e10b5d423a65.tar.bz2
Aqua: fix IME behavior; make accent menus work; rework Services; deal with non-BMP pastes.
-rw-r--r--library/entry.tcl24
-rw-r--r--library/text.tcl27
-rw-r--r--library/tk.tcl8
-rw-r--r--library/ttk/entry.tcl26
-rw-r--r--macosx/tkMacOSXClipboard.c23
-rw-r--r--macosx/tkMacOSXInit.c6
-rw-r--r--macosx/tkMacOSXKeyEvent.c271
-rw-r--r--macosx/tkMacOSXKeyboard.c2
-rw-r--r--macosx/tkMacOSXPrivate.h12
-rw-r--r--macosx/tkMacOSXServices.c131
-rw-r--r--macosx/tkMacOSXWindowEvent.c22
11 files changed, 330 insertions, 222 deletions
diff --git a/library/entry.tcl b/library/entry.tcl
index 0cc9ffb..2aab934 100644
--- a/library/entry.tcl
+++ b/library/entry.tcl
@@ -74,11 +74,6 @@ bind Entry <<TraverseIn>> {
# Standard Motif bindings:
-bind Entry <Map> {
- if {[tk windowingsystem] eq "aqua"} {
- ::tk::RegisterServiceWidget %W
- }
-}
bind Entry <1> {
tk::EntryButton1 %W %x
%W selection clear
@@ -277,6 +272,25 @@ bind Entry <Meta-Delete> {
}
}
+# Bindings for IME text input and accents.
+
+bind Entry <<TkStartIMEMarkedText>> {
+ dict set ::tk::Priv(IMETextMark) "%W" [%W index insert]
+}
+bind Entry <<TkEndIMEMarkedText>> {
+ if { [catch {dict get $::tk::Priv(IMETextMark) "%W"} mark] } {
+ bell
+ } else {
+ %W selection range $mark insert
+ }
+}
+bind Entry <<TkClearIMEMarkedText>> {
+ %W delete [dict get $::tk::Priv(IMETextMark) "%W"] [%W index insert]
+}
+bind Entry <<TkAccentBackspace>> {
+ tk::EntryBackspace %W
+}
+
# A few additional bindings of my own.
bind Entry <2> {
diff --git a/library/text.tcl b/library/text.tcl
index 964a5fb..e9c8bfe 100644
--- a/library/text.tcl
+++ b/library/text.tcl
@@ -42,12 +42,6 @@
# Standard Motif bindings:
-bind Text <Map> {
- if {[tk windowingsystem] eq "aqua"} {
- ::tk::RegisterServiceWidget %W
- }
-}
-
bind Text <1> {
tk::TextButton1 %W %x %y
%W tag remove sel 0.0 end
@@ -397,6 +391,26 @@ bind Text <Meta-Delete> {
}
}
+# Bindings for IME text input.
+
+bind Text <<TkStartIMEMarkedText>> {
+ dict set ::tk::Priv(IMETextMark) "%W" [%W index insert]
+}
+bind Text <<TkEndIMEMarkedText>> {
+ if { [catch {dict get $::tk::Priv(IMETextMark) "%W"} mark] } {
+ bell
+ } else {
+ %W tag add IMEmarkedtext $mark insert
+ %W tag configure IMEmarkedtext -underline on
+ }
+}
+bind Text <<TkClearIMEMarkedText>> {
+ %W delete IMEmarkedtext.first IMEmarkedtext.last
+}
+bind Text <<TkAccentBackspace>> {
+ %W delete insert-1c
+}
+
# Macintosh only bindings:
if {[tk windowingsystem] eq "aqua"} {
@@ -1223,7 +1237,6 @@ proc ::tk::TextScanDrag {w x y} {
$w scan dragto $x $y
}
}
-
# ::tk::TextUndoRedoProcessMarks --
#
# This proc is executed after an undo or redo action.
diff --git a/library/tk.tcl b/library/tk.tcl
index 047ea63..bc021d2 100644
--- a/library/tk.tcl
+++ b/library/tk.tcl
@@ -687,11 +687,6 @@ if {[tk windowingsystem] eq "aqua"} {
if {[tk windowingsystem] eq "aqua"} {
- #register to send data to macOS Services
- proc ::tk::RegisterServiceWidget {w} {
- ::tk::mac::registerServiceWidget $w
- }
-
#stub procedures to respond to "do script" Apple Events
proc ::tk::mac::DoScriptFile {file} {
source $file
@@ -701,7 +696,10 @@ if {[tk windowingsystem] eq "aqua"} {
}
}
+# Create a dictionary to store the starting index of the IME marked
+# text in an Entry or Text widget.
+set ::tk::Priv(IMETextMark) [dict create]
# Run the Ttk themed widget set initialization
if {$::ttk::library ne ""} {
diff --git a/library/ttk/entry.tcl b/library/ttk/entry.tcl
index 383eebd..0a61ffa 100644
--- a/library/ttk/entry.tcl
+++ b/library/ttk/entry.tcl
@@ -58,13 +58,6 @@ option add *TEntry.cursor [ttk::cursor text] widgetDefault
# and I'll put it back.
#
-##Bindings to register with macOS Services API.
-bind T.Entry <Map> {
- if {[tk windowingsystem] eq "aqua"} {
- ::tk::RegisterServiceWidget %W
- }
-}
-
## Clipboard events:
#
bind TEntry <<Cut>> { ttk::entry::Cut %W }
@@ -152,6 +145,25 @@ bind TEntry <Control-Key-d> { ttk::entry::Delete %W }
bind TEntry <Control-Key-h> { ttk::entry::Backspace %W }
bind TEntry <Control-Key-k> { %W delete insert end }
+# Bindings for IME text input.
+
+bind TEntry <<TkStartIMEMarkedText>> {
+ dict set ::tk::Priv(IMETextMark) "%W" [%W index insert]
+}
+bind TEntry <<TkEndIMEMarkedText>> {
+ if { [catch {dict get $::tk::Priv(IMETextMark) "%W"} mark] } {
+ bell
+ } else {
+ %W selection range $mark insert
+ }
+}
+bind TEntry <<TkClearIMEMarkedText>> {
+ %W delete [dict get $::tk::Priv(IMETextMark) "%W"] [%W index insert]
+}
+bind Entry <<TkAccentBackspace>> {
+ tk::EntryBackspace %W
+}
+
### Clipboard procedures.
#
diff --git a/macosx/tkMacOSXClipboard.c b/macosx/tkMacOSXClipboard.c
index 452b32f..6717afa 100644
--- a/macosx/tkMacOSXClipboard.c
+++ b/macosx/tkMacOSXClipboard.c
@@ -130,6 +130,7 @@ TkSelGetSelection(
&& selection == dispPtr->clipboardAtom
&& (target == XA_STRING || target == dispPtr->utf8Atom)) {
NSString *string = nil;
+ NSString *clean;
NSPasteboard *pb = [NSPasteboard generalPasteboard];
NSString *type = [pb availableTypeFromArray:[NSArray arrayWithObject:
NSStringPboardType]];
@@ -137,7 +138,27 @@ TkSelGetSelection(
if (type) {
string = [pb stringForType:type];
}
- result = proc(clientData, interp, string ? [string UTF8String] : "");
+ if (string) {
+ /*
+ * Replace all non-BMP characters by the replacement character 0xfffd.
+ * This is a workaround until Tcl supports TCL_UTF_MAX > 3.
+ */
+ int i, j, len = [string length];
+ CFRange all = CFRangeMake(0, len);
+ UniChar *buffer = ckalloc(len*sizeof(UniChar));
+ CFStringGetCharacters((CFStringRef) string, all, buffer);
+ for (i = 0, j = 0 ; j < len ; i++, j++) {
+ if (CFStringIsSurrogateHighCharacter(buffer[j])) {
+ buffer[i] = 0xfffd;
+ j++;
+ } else {
+ buffer[i] = buffer[j];
+ }
+ }
+ clean = (NSString *)CFStringCreateWithCharacters(NULL, buffer, i);
+ ckfree(buffer);
+ result = proc(clientData, interp, [clean UTF8String]);
+ }
} else {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"%s selection doesn't exist or form \"%s\" not defined",
diff --git a/macosx/tkMacOSXInit.c b/macosx/tkMacOSXInit.c
index a787e13..3d15442 100644
--- a/macosx/tkMacOSXInit.c
+++ b/macosx/tkMacOSXInit.c
@@ -413,8 +413,7 @@ TkpInit(
/*
* Initialize the NSServices object here. Apple's docs say to do this
* in applicationDidFinishLaunching, but the Tcl interpreter is not
- * initialized until this function call. Only the main interpreter
- * is allowed to provide services.
+ * initialized until this function call.
*/
TkMacOSXServices_Init(interp);
@@ -435,9 +434,6 @@ TkpInit(
TkMacOSXIconBitmapObjCmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "::tk::mac::GetAppPath",
TkMacOSXGetAppPathCmd, NULL, NULL);
- Tcl_CreateObjCommand(interp, "::tk::mac::registerServiceWidget",
- TkMacOSXRegisterServiceWidgetObjCmd, NULL, NULL);
-
return TCL_OK;
}
diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c
index ac03db2..e71d3ab 100644
--- a/macosx/tkMacOSXKeyEvent.c
+++ b/macosx/tkMacOSXKeyEvent.c
@@ -7,7 +7,7 @@
* Copyright 2001-2009, Apple Inc.
* Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net>
* Copyright (c) 2012 Adrian Robert.
- * Copyright 2015 Marc Culler.
+ * Copyright 2015-2019 Marc Culler.
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -31,13 +31,12 @@ static NSWindow *keyboardGrabNSWindow = nil;
* window. */
static NSModalSession modalSession = nil;
static BOOL processingCompose = NO;
-static BOOL finishedCompose = NO;
+static Tk_Window composeWin = NULL;
static int caret_x = 0, caret_y = 0, caret_height = 0;
-static void setupXEvent(XEvent *xEvent, NSWindow *w,
- unsigned int state);
-static unsigned isFunctionKey(unsigned int code);
+static unsigned short releaseCode;
-unsigned short releaseCode;
+static void setupXEvent(XEvent *xEvent, NSWindow *w, unsigned int state);
+static unsigned isFunctionKey(unsigned int code);
#pragma mark TKApplication(TKKeyEvent)
@@ -210,15 +209,19 @@ unsigned short releaseCode;
}
/*
- * For command key, take input manager's word so things like
- * dvorak / qwerty layout work.
+ * For the command key, take the input manager's word so things
+ * like dvorak / qwerty layout work.
*/
if ((modifiers & NSCommandKeyMask) == NSCommandKeyMask
&& (modifiers & NSAlternateKeyMask) != NSAlternateKeyMask
&& len > 0 && !isFunctionKey(code)) {
- // head off keycode-based translation in tkMacOSXKeyboard.c
- xEvent.xkey.nbytes = [characters length]; //len
+
+ /*
+ * Prevent keycode-based translation in tkMacOSXKeyboard.c
+ */
+
+ xEvent.xkey.nbytes = [characters length];
}
if ([characters length] > 0) {
@@ -242,7 +245,7 @@ unsigned short releaseCode;
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
savedModifiers = modifiers;
return theEvent;
- } /* if send straight to TK */
+ } /* if this is a function key or has modifiers */
} /* if not processing compose */
if (type == NSKeyDown) {
@@ -250,12 +253,29 @@ unsigned short releaseCode;
TKLog(@"keyDown: %s compose sequence.\n",
processingCompose == YES ? "Continue" : "Begin");
}
- processingCompose = YES;
- [nsEvArray addObject: theEvent];
- [[w contentView] interpretKeyEvents: nsEvArray];
- [nsEvArray removeObject: theEvent];
- }
+ /*
+ * Call the interpretKeyEvents method to interpret composition key
+ * strokes. When it detects a complete composition sequence it will
+ * call our implementation of insertText: replacementRange, which
+ * generates a key down XEvent with the appropriate character. In IME
+ * when multiple characters have the same composition sequence and the
+ * chosen character is not the default it may be necessary to hit the
+ * enter key multiple times before the character is accepted and
+ * rendered. We send enter key events until inputText has cleared
+ * the processingCompose flag.
+ */
+
+ processingCompose = YES;
+ while(processingCompose) {
+ [nsEvArray addObject: theEvent];
+ [[w contentView] interpretKeyEvents: nsEvArray];
+ [nsEvArray removeObject: theEvent];
+ if ([theEvent keyCode] != 36) {
+ break;
+ }
+ }
+ }
savedModifiers = modifiers;
return theEvent;
}
@@ -272,25 +292,34 @@ unsigned short releaseCode;
return self;
}
-/* <NSTextInput> implementation (called through interpretKeyEvents:]). */
+/*
+ * Implementation of the NSTextInputClient protocol.
+ */
-/* <NSTextInput>: called when done composing;
- NOTE: also called when we delete over working text, followed immed.
- by doCommandBySelector: deleteBackward: */
+/* [NSTextInputClient inputText: replacementRange:] is called by
+ * interpretKeyEvents when a composition sequence is complete. It is also
+ * called when we delete over working text. In that case the call is followed
+ * immediately by doCommandBySelector: deleteBackward:
+ */
- (void)insertText: (id)aString
+ replacementRange: (NSRange)repRange
{
- int i, len = [(NSString *) aString length];
+ int i, len;
XEvent xEvent;
+ NSString *str;
+
+ str = ([aString isKindOfClass: [NSAttributedString class]]) ?
+ str = [aString string] : aString;
+ len = [str length];
if (NS_KEYLOG) {
TKLog(@"insertText '%@'\tlen = %d", aString, len);
}
processingCompose = NO;
- finishedCompose = YES;
/*
- * First, clear any working text.
+ * Clear any working text.
*/
if (privateWorkingText != nil) {
@@ -298,32 +327,104 @@ unsigned short releaseCode;
}
/*
- * Now insert the string as keystrokes.
+ * Insert the string as a sequence of 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);
+ /*
+ * Apple evidently sets location to 0 to signal that an accented letter has
+ * been selected from the accent menu. An unaccented letter has already
+ * been displayed and we need to erase it before displaying the accented
+ * letter.
+ */
+
+ if (repRange.location == 0) {
+ TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
+ Tk_Window focusWin = (Tk_Window) winPtr->dispPtr->focusPtr;
+ TkSendVirtualEvent(focusWin, "TkAccentBackspace", NULL);
+ }
+
+ /*
+ * NSString represents a non-BMP character as a string of length 2 where
+ * the first character is the high surrogate and the second character is
+ * the low surrogate. We could record this in the XEvent by setting the
+ * keycode to the unicode code point and setting the trans_chars to the
+ * 4-byte UTF-8 string. However, that will not help as long as TCL_UTF_MAX
+ * is set to 3. Until that changes, we just replace non-BMP characters by
+ * the "replacement character" U+FFFD.
+ */
+
+ for (i = 0; i < len; i++) {
+ UniChar nextChar = [str characterAtIndex: i];
+ if (CFStringIsSurrogateHighCharacter(nextChar)) {
+#if 0
+ UniChar lowChar = [str characterAtIndex: ++i];
+ xEvent.xkey.keycode = CFStringGetLongCharacterForSurrogatePair(
+ nextChar, lowChar);
+ xEvent.xkey.nbytes = TkUniCharToUtf(xEvent.xkey.keycode,
+ &xEvent.xkey.trans_chars);
+#else
+ i++;
+ xEvent.xkey.keycode = 0xfffd;
+ strcpy(xEvent.xkey.trans_chars, "\xef\xbf\xbd");
+ xEvent.xkey.nbytes = strlen(xEvent.xkey.trans_chars);
+#endif
+ } else {
+ xEvent.xkey.keycode = (int) nextChar;
+ [[str substringWithRange: NSMakeRange(i,1)]
+ getCString: xEvent.xkey.trans_chars
+ maxLength: XMaxTransChars encoding: NSUTF8StringEncoding];
+ xEvent.xkey.nbytes = strlen(xEvent.xkey.trans_chars);
+ }
xEvent.xany.type = KeyPress;
- releaseCode = (UInt16) [aString characterAtIndex: 0];
+ releaseCode = (UInt16) nextChar;
Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
}
- releaseCode = (UInt16) [aString characterAtIndex: 0];
+ releaseCode = (UInt16) [str characterAtIndex: 0];
+}
+
+/*
+ * This required method is allowed to return nil.
+ */
+
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)theRange
+ actualRange:(NSRangePointer)thePointer
+{
+ return nil;
}
+/*
+ * This method is supposed to insert (or replace selected text with) the string
+ * argument. If the argument is an NSString, it should be displayed with a
+ * distinguishing appearance, e.g underlined.
+ */
-/* <NSTextInput>: inserts display of composing characters */
-- (void)setMarkedText: (id)aString selectedRange: (NSRange)selRange
+- (void)setMarkedText: (id)aString
+ selectedRange: (NSRange)selRange
+ replacementRange: (NSRange)repRange
{
- NSString *str = [aString respondsToSelector: @selector (string)] ?
- [aString string] : aString;
+ TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
+ Tk_Window focusWin = (Tk_Window) winPtr->dispPtr->focusPtr;
+ NSString *temp;
+ NSString *str;
+
+ str = ([aString isKindOfClass: [NSAttributedString class]]) ?
+ str = [aString string] : aString;
+
+ if (focusWin) {
+
+ /*
+ * Remember the widget where the composition is happening, in case it
+ * gets defocussed during the composition.
+ */
+
+ composeWin = focusWin;
+ } else {
+ return;
+ }
if (NS_KEYLOG) {
TKLog(@"setMarkedText '%@' len =%lu range %lu from %lu", str,
(unsigned long) [str length], (unsigned long) selRange.length,
@@ -333,17 +434,23 @@ unsigned short releaseCode;
if (privateWorkingText != nil) {
[self deleteWorkingText];
}
+
if ([str length] == 0) {
return;
}
- processingCompose = YES;
- privateWorkingText = [str copy];
+ /*
+ * Use our insertText method to display the marked text.
+ */
- //PENDING: insert workingText underlined
+ TkSendVirtualEvent(focusWin, "TkStartIMEMarkedText", NULL);
+ temp = [str copy];
+ [self insertText: temp replacementRange:repRange];
+ privateWorkingText = temp;
+ processingCompose = YES;
+ TkSendVirtualEvent(focusWin, "TkEndIMEMarkedText", NULL);
}
-
- (BOOL)hasMarkedText
{
return privateWorkingText != nil;
@@ -362,23 +469,34 @@ unsigned short releaseCode;
return rng;
}
+- (void)cancelComposingText
+{
+ if (NS_KEYLOG) {
+ TKLog(@"cancelComposingText");
+ }
+ [self deleteWorkingText];
+ processingCompose = NO;
+}
- (void)unmarkText
{
if (NS_KEYLOG) {
- TKLog(@"unmark (accept) text");
+ TKLog(@"unmarkText");
}
[self deleteWorkingText];
processingCompose = NO;
}
-/* used to position char selection windows, etc. */
+/*
+ * Called by the system to get a position for popup character selection windows
+ * such as a Character Palette, or a selection menu for IME.
+ */
- (NSRect)firstRectForCharacterRange: (NSRange)theRange
+ actualRange: (NSRangePointer)thePointer
{
NSRect rect;
NSPoint pt;
-
pt.x = caret_x;
pt.y = caret_y;
@@ -387,7 +505,7 @@ unsigned short releaseCode;
pt.y -= caret_height;
rect.origin = pt;
- rect.size.width = caret_height;
+ rect.size.width = 0;
rect.size.height = caret_height;
return rect;
}
@@ -398,7 +516,6 @@ unsigned short releaseCode;
return (NSInteger) self;
}
-
- (void)doCommandBySelector: (SEL)aSelector
{
if (NS_KEYLOG) {
@@ -406,54 +523,41 @@ unsigned short releaseCode;
}
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);
+ TkWindow *winPtr = TkMacOSXGetTkWindow([self window]);
+ Tk_Window focusWin = (Tk_Window) winPtr->dispPtr->focusPtr;
+ TkSendVirtualEvent(focusWin, "TkAccentBackspace", NULL);
}
}
-
- (NSArray *)validAttributesForMarkedText
{
static NSArray *arr = nil;
-
if (arr == nil) {
- arr = [NSArray new];
+ arr = [[NSArray alloc] initWithObjects:
+ NSUnderlineStyleAttributeName,
+ NSUnderlineColorAttributeName,
+ nil];
+ [arr retain];
}
- /* [[NSArray arrayWithObject: NSUnderlineStyleAttributeName] retain]; */
return arr;
}
-
- (NSRange)selectedRange
{
if (NS_KEYLOG) {
TKLog(@"selectedRange request");
}
- return NSMakeRange(NSNotFound, 0);
+ return NSMakeRange(0, 0);
}
-
- (NSUInteger)characterIndexForPoint: (NSPoint)thePoint
{
if (NS_KEYLOG) {
TKLog(@"characterIndexForPoint request");
}
- return 0;
+ return NSNotFound;
}
-
- (NSAttributedString *)attributedSubstringFromRange: (NSRange)theRange
{
static NSAttributedString *str = nil;
@@ -465,28 +569,37 @@ unsigned short releaseCode;
}
return str;
}
-/* End <NSTextInput> impl. */
+/* End of NSTextInputClient implementation. */
@synthesize needsRedisplay = _needsRedisplay;
@end
@implementation TKContentView(TKKeyEvent)
-/* delete display of composing characters [not in <NSTextInput>] */
+
+/*
+ * Tell the widget to erase the displayed composing characters. This
+ * is not part of the NSTextInputClient protocol.
+ */
+
- (void)deleteWorkingText
{
if (privateWorkingText == nil) {
return;
- }
- if (NS_KEYLOG) {
- TKLog(@"deleteWorkingText len = %lu\n",
- (unsigned long)[privateWorkingText length]);
- }
- [privateWorkingText release];
- privateWorkingText = nil;
- processingCompose = NO;
+ } else {
+
+ if (NS_KEYLOG) {
+ TKLog(@"deleteWorkingText len = %lu\n",
+ (unsigned long)[privateWorkingText length]);
+ }
- //PENDING: delete working text
+ [privateWorkingText release];
+ privateWorkingText = nil;
+ processingCompose = NO;
+ if (composeWin) {
+ TkSendVirtualEvent(composeWin, "TkClearIMEMarkedText", NULL);
+ }
+ }
}
@end
diff --git a/macosx/tkMacOSXKeyboard.c b/macosx/tkMacOSXKeyboard.c
index 3272657..ef4a4e2 100644
--- a/macosx/tkMacOSXKeyboard.c
+++ b/macosx/tkMacOSXKeyboard.c
@@ -803,7 +803,7 @@ TkpGetKeySym(
/* If nbytes has been set, it's not a function key, but a regular key that
has been translated in tkMacOSXKeyEvent.c; just use that. */
if (eventPtr->xkey.nbytes) {
- return eventPtr->xkey.keycode & 0xFFFF;
+ return eventPtr->xkey.keycode;
}
/*
diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h
index cb80dd8..7aa6840 100644
--- a/macosx/tkMacOSXPrivate.h
+++ b/macosx/tkMacOSXPrivate.h
@@ -340,7 +340,12 @@ VISIBILITY_HIDDEN
@end
VISIBILITY_HIDDEN
-@interface TKContentView : NSView <NSTextInput>
+/*
+ * Subclass TKContentView from NSTextInputClient to enable composition and
+ * input from the Character Palette.
+ */
+
+@interface TKContentView : NSView <NSTextInputClient>
{
@private
NSString *privateWorkingText;
@@ -354,13 +359,8 @@ VISIBILITY_HIDDEN
@end
@interface TKContentView(TKWindowEvent)
-- (void) drawRect: (NSRect) rect;
- (void) generateExposeEvents: (HIShapeRef) shape;
- (void) tkToolbarButton: (id) sender;
-- (BOOL) isOpaque;
-- (BOOL) wantsDefaultClipping;
-- (BOOL) acceptsFirstResponder;
-- (void) keyDown: (NSEvent *) theEvent;
@end
@interface NSWindow(TKWm)
diff --git a/macosx/tkMacOSXServices.c b/macosx/tkMacOSXServices.c
index f1e5951..e92158f 100644
--- a/macosx/tkMacOSXServices.c
+++ b/macosx/tkMacOSXServices.c
@@ -4,20 +4,18 @@
* This file allows the integration of Tk and the Cocoa NSServices API.
*
* Copyright (c) 2010-2019 Kevin Walzer/WordTech Communications LLC.
+ * Copyright (c) 2019 Marc Culler.
* Copyright (c) 2010 Adrian Robert.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
-#include <CoreServices/CoreServices.h>
#include <tkInt.h>
#include <tkMacOSXInt.h>
-static Tcl_Interp *ServicesInterp;
-
/*
- * Event proc which calls the PerformService procedure
+ * Event proc which calls the PerformService procedure.
*/
static int
@@ -25,22 +23,28 @@ ServicesEventProc(
Tcl_Event *event,
int flags)
{
- Tcl_GlobalEval(ServicesInterp, "::tk::mac::PerformService");
+ TkMainInfo *info = TkGetMainInfoList();
+ Tcl_GlobalEval(info->interp, "::tk::mac::PerformService");
return 1;
}
/*
- * Class declarations for TkService class.
+ * The Wish application can send the current selection in the Tk clipboard
+ * to other applications which accept messages of type NSString. The TkService
+ * object provides this service via its provideService method. (The method
+ * must be specified in the application's Info.plist file for this to work.)
*/
-@interface TkService : NSView {
+@interface TkService : NSObject {
}
+ (void) initialize;
-- (void)provideService:(NSPasteboard *)pboard userData:(NSString *)data error:(NSString **)error;
-- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType;
-- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types;
+- (void) provideService:(NSPasteboard *)pboard
+ userData:(NSString *)data
+ error:(NSString **)error;
+- (BOOL) writeSelectionToPasteboard:(NSPasteboard *)pboard
+ types:(NSArray *)types;
@end
@@ -57,32 +61,8 @@ ServicesEventProc(
return;
}
-
-- (id)validRequestorForSendType:(NSString *)sendType
- returnType:(NSString *)returnType
-{
- if ([sendType isEqualToString:@"NSStringPboardType"] ||
- [sendType isEqualToString:@"NSPasteboardTypeString"]) {
- return self;
- }
- return [super validRequestorForSendType:sendType returnType:returnType];
-}
-
-/*
- * Make sure the view accepts events.
- */
-
-- (BOOL)acceptsFirstResponder
-{
- return YES;
-}
-- (BOOL)becomeFirstResponder
-{
- return YES;
-}
-
/*
- * Get selected text, copy to pasteboard.
+ * Get the current Tk selection and copy it to the system pasteboard.
*/
- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
@@ -90,6 +70,7 @@ ServicesEventProc(
{
NSArray *typesDeclared = nil;
NSString *pboardType = nil;
+ TkMainInfo *info = TkGetMainInfoList();
for (NSString *typeString in types) {
if ([typeString isEqualToString:@"NSStringPboardType"] ||
@@ -102,9 +83,9 @@ ServicesEventProc(
if (!typesDeclared) {
return NO;
}
- Tcl_Eval(ServicesInterp, "selection get");
+ Tcl_Eval(info->interp, "selection get");
- char *copystring = Tcl_GetString(Tcl_GetObjResult(ServicesInterp));
+ char *copystring = Tcl_GetString(Tcl_GetObjResult(info->interp));
NSString *writestring = [NSString stringWithUTF8String:copystring];
[pboard declareTypes:typesDeclared owner:nil];
@@ -112,8 +93,8 @@ ServicesEventProc(
}
/*
- * This is the method that actually calls the Tk service; this is the method
- * that must be defined in info.plist.
+ * This is the method that actually calls the Tk service; it must be specified
+ * in Info.plist.
*/
- (void)provideService:(NSPasteboard *)pboard
@@ -125,8 +106,8 @@ ServicesEventProc(
Tcl_Event *event;
/*
- * Get string from private pasteboard, write to general pasteboard to make
- * available to Tcl service.
+ * Get a string from the private pasteboard and copy it to the general
+ * pasteboard to make it available to other applications.
*/
for (NSString *typeString in types) {
@@ -150,69 +131,9 @@ ServicesEventProc(
@end
/*
- * Register a specific widget to access the Services menu.
- */
-
-int
-TkMacOSXRegisterServiceWidgetObjCmd(
- ClientData cd,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const objv[])
-{
- /*
- * Need proper number of args.
- */
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "path?");
- return TCL_ERROR;
- }
- /*
- * Get the object that holds this Tk Window...
- */
-
- Rect bounds;
- NSRect frame;
- Tk_Window path = Tk_NameToWindow(interp,
- Tcl_GetString(objv[1]), Tk_MainWindow(interp));
-
- if (path == NULL) {
- return TCL_ERROR;
- }
-
- Tk_MakeWindowExist(path);
- Tk_MapWindow(path);
- Drawable d = Tk_WindowId(path);
-
- /*
- * Get NSView from Tk window and add subview.
- */
-
- TkService *serviceview = [[TkService alloc] init];
- NSView *view = TkMacOSXGetRootControl(d);
-
- if ([serviceview superview] != view) {
- [view addSubview:serviceview];
- }
- TkMacOSXWinBounds((TkWindow*)path, &bounds);
-
- /*
- * Hack to make sure subview is set to take up entire geometry of window.
- */
-
- frame = NSMakeRect(bounds.left, bounds.top, 100000, 100000);
- frame.origin.y = 0;
- if (!NSEqualRects(frame, [serviceview frame])) {
- [serviceview setFrame:frame];
- }
- [serviceview release];
- return TCL_OK;
-}
-
-/*
- * Initalize the package in the Tcl interpreter, create Tcl commands.
+ * Instantiate a TkService object and register it with the NSApplication.
+ * This is called exactly one time from TkpInit.
*/
int
@@ -220,12 +141,10 @@ TkMacOSXServices_Init(
Tcl_Interp *interp)
{
/*
- * Initialize instance of TclServices to provide service functionality.
+ * Initialize an instance of TkService and register it with the NSApp.
*/
TkService *service = [[TkService alloc] init];
-
- ServicesInterp = interp;
[NSApp setServicesProvider:service];
return TCL_OK;
}
diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c
index 2666939..fe6981d 100644
--- a/macosx/tkMacOSXWindowEvent.c
+++ b/macosx/tkMacOSXWindowEvent.c
@@ -1186,6 +1186,11 @@ RedisplayView(
return YES;
}
+/*
+ * This keyDown method does nothing, which is a huge improvement over the
+ * default keyDown method which beeps every time a key is pressed.
+ */
+
- (void) keyDown: (NSEvent *) theEvent
{
#ifdef TK_MAC_DEBUG_EVENTS
@@ -1193,6 +1198,23 @@ RedisplayView(
#endif
}
+/*
+ * When the services menu is opened this is called for each Responder in
+ * the Responder chain until a service provider is found. The TkContentView
+ * should be the first (and generally only) Responder in the chain. We
+ * return the TkServices object that was created in TkpInit.
+ */
+
+- (id)validRequestorForSendType:(NSString *)sendType
+ returnType:(NSString *)returnType
+{
+ if ([sendType isEqualToString:@"NSStringPboardType"] ||
+ [sendType isEqualToString:@"NSPasteboardTypeString"]) {
+ return [NSApp servicesProvider];
+ }
+ return [super validRequestorForSendType:sendType returnType:returnType];
+}
+
@end
/*