Feature request: Support services

Feature request: Support services

Post by David Phil » Tue, 14 Dec 2004 17:21:11

ac OS X has a "Services" hierarchical menu, with an extensible list of commands
that operate on the selection. Supporting it is trivial, but the Codewarrior IDE doesn't.

It would be nice if it did. It would be nice if every program on the Mac that has editable
text did. Many of the default services are useful for a programmer:

* Opening URLs

* Speaking text: so you can compare on-screen data to paper in your hand

* Expression evaluation: select an arithmetic expression, and type: <Command><Shift>-<Star>
(That is, command shift 8), and Script Editor will evaluate the expression.

It is very nice to be able to type: 5+7/8, and have the computer tell you 5.875, for example
when entering paper sizes.

Script Editor will evaluate even moderately complex expressions like:

set a to 4 + 5
set b to 6 + 7
a + b

Here is sample code to support services, assuming TextEdit (I don't know the actual
API used by Codewarrior IDE, but the concepts are clear.)

// InitServiceEventHandlers - At application init time:
static void InitServiceEventHandlers(void){
static EventTypeSpec serviceCopySpec = {kEventClassService, kEventServiceCopy};
static EventTypeSpec servicePasteSpec = {kEventClassService, kEventServicePaste};
static EventTypeSpec serviceGetTypesSpec = {kEventClassService, kEventServiceGetTypes};

InstallApplicationEventHandler(NewEventHandlerUPP(DoServiceCopy), 1, &serviceCopySpec, NULL, NULL);
InstallApplicationEventHandler(NewEventHandlerUPP(DoServicePaste), 1, &servicePasteSpec, NULL, NULL);
InstallApplicationEventHandler(NewEventHandlerUPP(DoServiceGetTypes), 1, &serviceGetTypesSpec, NULL, NULL);

// DoServiceGetTypes - just tells the OS that you are interested in TEXT
static pascal OSStatus DoServiceGetTypes(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void * inUserData){
#pragma unused(inHandlerCallRef, inUserData)
CFMutableArrayRef copyRef = NULL;
CFMutableArrayRef pasteRef = NULL;
static CFStringRef textType = NULL;
if(NULL == textType){
textType = CFSTR("TEXT");

GetEventParameter(inEvent, kEventParamServiceCopyTypes, typeCFMutableArrayRef, NULL, sizeof copyRef, NULL, ©Ref);
if(NULL != copyRef){
CFArrayAppendValue(copyRef, textType);
GetEventParameter(inEvent, kEventParamServicePasteTypes, typeCFMutableArrayRef, NULL, sizeof pasteRef, NULL, &pasteRef);
if(NULL != pasteRef){
CFArrayAppendValue(pasteRef, textType);

return noErr;

// DoServiceCopy - send the selection of the current TEHandle to the service.
static pascal OSStatus DoServiceCopy(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void * inUserData){
#pragma unused(inHandlerCallRef, inUserData)
OSStatus err = noErr;
ScrapRef scrapRef;
long len = SomeToCut();
if(0 < len && noErr == GetEventParameter(inEvent, kEventParamScrapRef, typeScrapRef, NULL, sizeof scrapRef, NULL, &scrapRef)){
Handle h; // copy to scrap provided
SignedByte state;
h = (**GetTEHandle()).hText;
state = HGetState(h);
err = ClearScrap(&scrapRef);
if(noErr == err){ err = PutScrapFlavor(scrapRef, 'TEXT', 0, len, &(*h)[(**GetTEHandle()).selStart]); }
HSetState(h, state);
return err;
return eventNotHandledErr;

// DoServicePaste - replace the current selection by the text returned

Feature request: Support services

Post by David Phil » Tue, 21 Dec 2004 14:21:31

In article

the above ClearScrap() line is almost certainly a bug: You already get
the s *** object ready to PutScrapFlavor data into. Calling ClearS ***
on it would modify it into an object that the caller will have
difficulty recognizing. Although this code works either way (Otherwise I
wouldn't have posted it) I think it is better without the ClearScrap()

David Phillip Oster