summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Walzer <kw@codebykevin.com>2018-03-05 03:34:59 (GMT)
committerKevin Walzer <kw@codebykevin.com>2018-03-05 03:34:59 (GMT)
commit6cec0cc76e0280301d5f58e11677a56f04eaf5ef (patch)
tree8ea6ba859b06e371439b17a38962290157ba08a0
parentece57bee6bf7f6ccff0a6d8062db54009a967525 (diff)
downloadtk-6cec0cc76e0280301d5f58e11677a56f04eaf5ef.zip
tk-6cec0cc76e0280301d5f58e11677a56f04eaf5ef.tar.gz
tk-6cec0cc76e0280301d5f58e11677a56f04eaf5ef.tar.bz2
Implementation of NSServices API for Tk core on macOS
-rw-r--r--macosx/tkMacOSXServices.c244
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);
+}
+
+