diff options
author | Kevin Walzer <kw@codebykevin.com> | 2018-03-05 03:34:59 (GMT) |
---|---|---|
committer | Kevin Walzer <kw@codebykevin.com> | 2018-03-05 03:34:59 (GMT) |
commit | 6cec0cc76e0280301d5f58e11677a56f04eaf5ef (patch) | |
tree | 8ea6ba859b06e371439b17a38962290157ba08a0 | |
parent | ece57bee6bf7f6ccff0a6d8062db54009a967525 (diff) | |
download | tk-6cec0cc76e0280301d5f58e11677a56f04eaf5ef.zip tk-6cec0cc76e0280301d5f58e11677a56f04eaf5ef.tar.gz tk-6cec0cc76e0280301d5f58e11677a56f04eaf5ef.tar.bz2 |
Implementation of NSServices API for Tk core on macOS
-rw-r--r-- | macosx/tkMacOSXServices.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/macosx/tkMacOSXServices.c b/macosx/tkMacOSXServices.c new file mode 100644 index 0000000..9e78c56 --- /dev/null +++ b/macosx/tkMacOSXServices.c @@ -0,0 +1,244 @@ +/* + * tkMacOSXServices.c -- + * + * This file allows the integration of Tk and the Cocoa NSServices API. + * + * Copyright (c) 2010-2018 Kevin Walzer/WordTech Communications LLC. + * 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 <CoreFoundation/CoreFoundation.h> +#include <CoreServices/CoreServices.h> +#define Cursor FooFoo +#include <Carbon/Carbon.h> +#undef Cursor +#include <tcl.h> +#include <tk.h> +#include <tkInt.h> +#include <tkMacOSXInt.h> +#include <Cocoa/Cocoa.h> +#include <stdio.h> +#include <string.h> + +static Tcl_Interp *ServicesInterp; +char *myExportData[80]; + + +/* These two assist with asynchronous Tcl proc calling. */ +typedef struct Services_Event { + Tcl_Event header; + char script[50000]; +} Services_Event; + +int ServicesEventProc(Tcl_Event *event, int flags) { + Tcl_GlobalEval(ServicesInterp, ((Services_Event *)event)->script); + return 1; +} + + +/* Class declarations for TkService class. */ +@interface TkService : NSView { + +} + ++ (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; + +@end + +/* Class methods. */ +@implementation TclService + ++ (void) initialize { + + NSArray *sendTypes = [NSArray arrayWithObjects:NSStringPboardType, nil]; + NSArray *returnTypes = [NSArray arrayWithObjects:NSStringPboardType, + nil]; + [NSApp registerServicesMenuSendTypes:sendTypes returnTypes:returnTypes]; + NSUpdateDynamicServices(); + return; +} + + +- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType { + + if ([sendType isEqual:NSStringPboardType]) { + 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. */ +- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard types:(NSArray *)types { + NSArray *typesDeclared; + if ([types containsObject:NSStringPboardType] == NO) { + return NO; + } + + Tcl_Eval(Services_Interp,"clipboard get"); + char *copystring; + copystring = Tcl_GetString(Tcl_GetObjResult(myInterp)); + + NSString *writestring = [NSString stringWithUTF8String:copystring]; + typesDeclared = [NSArray arrayWithObject:NSStringPboardType]; + [pboard declareTypes:typesDeclared owner:nil]; + return [pboard setString:writestring forType:NSStringPboardType]; +} + +/* Read return data from clipboard. */ +- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard +{ + NSArray *types; + NSString *returnText; + + types = [pboard types]; + if ( [types containsObject:NSStringPboardType] == NO ) { + return NO; + } + returnText = [pboard stringForType:NSStringPboardType]; + char *returnData = [returnText UTF8String]; + Tcl_Eval(Services_Interp,"clipboard clear; clipboard append %s", returnData); + return YES; +} + + +/* This is the method that actually calls the Tk service; this is the method that must be defined in info.plist */ + +- (void)provideService:(NSPasteboard *)pboard userData:(NSString *)data error:(NSString **)error { + + NSString *pboardString; + NSArray *types = [pboard types]; + Services_Event *event; + + /* Get string from private pasteboard, write to general pasteboard to make available to Tcl service. */ + if ([types containsObject:NSStringPboardType] && + (pboardString = [pboard stringForType:NSStringPboardType])) { + + NSPasteboard *generalpasteboard = [NSPasteboard generalPasteboard]; + [generalpasteboard declareTypes:[NSArray arrayWithObjects:NSStringPboardType, nil] owner:nil]; + [generalpasteboard setString:pboardString forType:NSStringPboardType]; + event = (Services_Event *)ckalloc(sizeof(Services_Event)); + event->header.proc = ServicesEventProc; + strcpy(event->script, "::tk::mac::PerformService"); + Tcl_QueueEvent((Tcl_Event *)event, TCL_QUEUE_TAIL); + + /* Get output from service proc, return to interp, and write to pasteboard. */ + char *output; + output = Tcl_GetString(Tcl_GetObjResult(Services_Interp)); + Tcl_SetResult(Services_Interp, output, NULL); + NSString *serviceOutput = [NSString stringWithUTF8String:output]; + [pboard clearContents]; + [pboard writeObjects:[NSArray arrayWithObject:serviceOutput]]; + + } else { + return; + } + return; +} + +@end + + +//register a specific widget to access the Services menu +int RegisterServiceWidget (ClientData cd, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[]) { + + //set up an autorelease pool + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + //need proper number of args + if(objc != 2) { + Tcl_WrongNumArgs(ip, 1, objv, "path?"); + return TCL_ERROR; + } + + /* Get the object that holds this Tk Window... */ + Rect bounds; + NSRect frame; + Tk_Window path; + path = Tk_NameToWindow(ip, Tcl_GetString(objv[1]), Tk_MainWindow(ip)); + 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 + TclService *serviceview = [[TclService 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]; + [pool release]; + + return TCL_OK; + +} + + +//initalize the package in the tcl interpreter, create tcl commands +int Tclservices_Init (Tcl_Interp *interp) { + + //set up an autorelease pool + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (Tcl_InitStubs(interp, "8.5", 0) == NULL) { + return TCL_ERROR; + } + if (Tk_InitStubs(interp, "8.5", 0) == NULL) { + return TCL_ERROR; + } + + + Tcl_CreateObjCommand(interp, "::tclservices::registerservicewidget", RegisterServiceWidget,(ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); + + + //initialize instance of TclServices to provide service functionality + TclService *service = [[TclService alloc] init]; + myInterp = interp; + [NSApp setServicesProvider:service]; + + + if (Tcl_PkgProvide(interp, "tclservices", "1.0") != TCL_OK) { + return TCL_ERROR; + } + + [pool release]; + + return TCL_OK; + +} + +int Tclservices_SafeInit(Tcl_Interp *interp) { + return Tclservices_Init(interp); +} + + |