This is Controller.m in view mode; [Download] [Up]
{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;\f1\fmodern Ohlfs;}
\paperw13740
\paperh13740
\margl40
\margr40
{\colortbl;\red255\green0\blue0;\red0\green0\blue0;\red255\green109\blue0;\red0\green41\blue0;\red61\green0\blue0;\red0\green255\blue0;}
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i\ulnone\fs28\gray300\fc1\cf1 // Controller.m
\i0\gray0\fc2\cf2 \
\
\b #import
\b0
\gray550\fc3\cf3 "Controller.h"
\gray0\fc2\cf2 \
\b #import
\b0
\gray550\fc3\cf3 "regex.h"
\gray0\fc2\cf2 \
\b #import
\b0 <strings.h>\
\b #import
\b0 <stdlib.h>\
\
\fs36 @implementation Controller
\fs28 \
\
\fs36 - init\
\fs28 \{\
[
\b super
\b0 init];\
itemList = [[List alloc] init];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - free\
\fs28 \{\
[itemList free];\
\b return
\b0 [
\b super
\b0 free];\
\}\
\
\fs36 - appDidInit:sender\
\fs28 \{\
\b static
\b0 NXDefaultsVector RTFSyntaxDefaults = \{\
\{
\gray550\fc3\cf3 "configuration"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 ""
\gray0\fc2\cf2 \},\
\{
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 \}\
\};\
\
\i\gray300\fc1\cf1 // make this object the services delegate
\i0\gray0\fc2\cf2 \
[[NXApp appListener] setServicesDelegate:
\b self
\b0 ];\
\
\i\gray300\fc1\cf1 // setup text objects
\i0\gray0\fc2\cf2 \
[regularExpression setMonoFont:
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ];\
[matchedSampleText setMonoFont:
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ];\
[unmatchedSampleText setMonoFont:
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ];\
\
\i\gray300\fc1\cf1 // register defaults
\i0\gray0\fc2\cf2 \
NXRegisterDefaults(
\gray550\fc3\cf3 "RTFSyntax"
\gray0\fc2\cf2 , RTFSyntaxDefaults);\
\
\i\gray300\fc1\cf1 // read defaults
\i0\gray0\fc2\cf2 \
[defaultConfiguration setStringValue:NXGetDefaultValue(
\gray550\fc3\cf3 "RTFSyntax"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "configuration"
\gray0\fc2\cf2 )];\
\
\i\gray300\fc1\cf1 // open the default configuration (or a new one if there is no default)
\i0\gray0\fc2\cf2 \
\b if
\b0 (!fileOpened && ![
\b self
\b0 openDefaultConfiguration])\
[
\b self
\b0 newConfiguration:
\b self
\b0 ];\
\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - (int)app:sender openFile:(const char *)path type:(const char *)type\
\fs28 \{\
\b if
\b0 (strcmp(type,
\gray550\fc3\cf3 "syntax"
\gray0\fc2\cf2 ))\
\b return
\b0
\gray93\fc4\cf4 NO
\gray0\fc2\cf2 ;\
\
[
\b self
\b0 openNamedConfiguration:path];\
[configurationWindow makeKeyAndOrderFront:
\b self
\b0 ];\
fileOpened =
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ;\
\
\b return
\b0
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ;\
\}\
\
\fs36 - (BOOL)appAcceptsAnotherFile:(Application *)sender\
\fs28 \{\
\b return
\b0
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ;\
\}\
\
\fs36 - previousItem:sender\
\fs28 \{\
\b int
\b0 i = [itemNumber intValue];\
\b if
\b0 (i <=
\gray71\fc5\cf5 1
\gray0\fc2\cf2 )\
\b return
\b0
\b self
\b0 ;\
[
\b self
\b0 saveCurrentItem];\
[
\b self
\b0 displayItem:i
\gray71\fc5\cf5 -1
\gray0\fc2\cf2 ];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - nextItem:sender\
\fs28 \{\
\b int
\b0 i = [itemNumber intValue];\
\
\b if
\b0 (i >= [numberOfItems intValue])\
\b return
\b0
\b self
\b0 ;\
[
\b self
\b0 saveCurrentItem];\
[
\b self
\b0 displayItem:i+
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - deleteItem:sender\
\fs28 \{\
\b int
\b0 i = [itemNumber intValue];\
\
\b if
\b0 ([numberOfItems intValue] ==
\gray71\fc5\cf5 1
\gray0\fc2\cf2 )\
\b return
\b0
\b self
\b0 ;
\i\gray300\fc1\cf1 // can't delete last item (need at least one)
\i0\gray0\fc2\cf2 \
[[itemList removeObjectAt:i
\gray71\fc5\cf5 -1
\gray0\fc2\cf2 ] free];\
[numberOfItems setIntValue:[numberOfItems intValue]-
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
\b if
\b0 (i ==
\gray71\fc5\cf5 1
\gray0\fc2\cf2 )\
[
\b self
\b0 displayItem:
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
\b else
\b0 \
[
\b self
\b0 displayItem:i
\gray71\fc5\cf5 -1
\gray0\fc2\cf2 ];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - insertItem:sender\
\fs28 \{\
\b int
\b0 i = [itemNumber intValue];\
\
[
\b self
\b0 saveCurrentItem];\
[itemList insertObject:[[SyntaxItem alloc] init] at:i
\gray71\fc5\cf5 -1
\gray0\fc2\cf2 ];\
[numberOfItems setIntValue:[numberOfItems intValue]+
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
[
\b self
\b0 displayItem:i];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - (void)saveCurrentItem\
\fs28 \{\
SyntaxItem *item = [itemList objectAt:[itemNumber intValue]-
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
\
[item setRegularExpression:regularExpression];\
[item setMatchedSampleText:matchedSampleText];\
\}\
\
\fs36 - (void)displayItem:(int)i\
\fs28 \{\
SyntaxItem *item = [itemList objectAt:i
\gray71\fc5\cf5 -1
\gray0\fc2\cf2 ];\
\
[itemNumber setIntValue:i];\
\
\b if
\b0 ([item getRegularExpression]) \{\
[regularExpression readText:[[item getRegularExpression] stream]];\
[regularExpression setFont:[[item getRegularExpression] font]];\
[regularExpression display];\
\}
\b else
\b0 \
[regularExpression setText:
\gray550\fc3\cf3 ""
\gray0\fc2\cf2 ];\
\
\b if
\b0 ([item getMatchedSampleText]) \{\
[matchedSampleText readText:[[item getMatchedSampleText] stream]];\
[matchedSampleText setFont:[[item getMatchedSampleText] font]];\
[matchedSampleText setTextColor:[[item getMatchedSampleText] textColor]];\
[matchedSampleText display];\
[matchedColorWell setColor:[matchedSampleText textColor]];\
\}
\b else
\b0 \
[matchedSampleText setText:
\gray550\fc3\cf3 ""
\gray0\fc2\cf2 ];\
\}\
\
\fs36 - (BOOL)openDefaultConfiguration\
\fs28 \{\
\b if
\b0 (![defaultConfiguration stringValue] || !strlen([defaultConfiguration stringValue])) \{\
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc2\cf2 [configurationWindow setTitle:""];\
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc2\cf2
\b return
\b0
\gray93\fc4\cf4 NO
\gray0\fc2\cf2 ;\
\}\
\i\gray300\fc1\cf1 // load default configuration if not already loaded
\i0\gray0\fc2\cf2 \
\b if
\b0 (strcmp([defaultConfiguration stringValue], [configurationWindow title]))\
[
\b self
\b0 openNamedConfiguration:[defaultConfiguration stringValue]];\
\b return
\b0
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ;\
\}\
\
\fs36 - openConfiguration:sender\
\fs28 \{\
\b const
\b0
\b char
\b0 *
\b const
\b0 types[
\gray71\fc5\cf5 2
\gray0\fc2\cf2 ] = \{
\gray550\fc3\cf3 "syntax"
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 \};\
\b id
\b0 openPanel = [OpenPanel new];\
\
[openPanel allowMultipleFiles:
\gray93\fc4\cf4 NO
\gray0\fc2\cf2 ];\
\b if
\b0 (![openPanel runModalForTypes:types])\
\b return
\b0
\b self
\b0 ;\
[
\b self
\b0 openNamedConfiguration:[openPanel filename]];\
[configurationWindow makeKeyAndOrderFront:
\b self
\b0 ];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - (void)openNamedConfiguration:(const char *)name\
\fs28 \{\
NXTypedStream *stream;\
Text *text;\
NXColor color;\
\
[configurationWindow setTitle:name];\
\b if
\b0 (!(stream = NXOpenTypedStreamForFile(name,
\gray93\fc4\cf4 NX_READONLY
\gray0\fc2\cf2 ))) \{\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "Can't open %s."
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 , name);\
[
\b self
\b0 newConfiguration:
\b self
\b0 ];\
\b return
\b0 ;\
\}\
\
text = NXReadObject(stream);\
[unmatchedSampleText readText:[text stream]];\
[unmatchedSampleText setFont:[text font]];\
color = NXReadColor(stream);\
[unmatchedSampleText setTextColor:color];\
[unmatchedSampleText display];\
[unmatchedColorWell setColor:color];\
[text free];\
\
[itemList free];\
itemList = NXReadObject(stream);\
NXCloseTypedStream(stream);\
[numberOfItems setIntValue:[itemList count]];\
\b if
\b0 ([itemList count] <
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ) \{\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "No items in configuration file"
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 );\
\b return
\b0 ;\
\}\
[
\b self
\b0 displayItem:
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
\b return
\b0 ;\
\}\
\
\fs36 - newConfiguration:sender\
\fs28 \{\
[configurationWindow makeKeyAndOrderFront:
\b self
\b0 ];\
[itemList freeObjects];\
[itemList addObject:[[SyntaxItem alloc] init]];\
[numberOfItems setIntValue:
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
[configurationWindow setTitle:
\gray550\fc3\cf3 "Untitled"
\gray0\fc2\cf2 ];\
[
\b self
\b0 displayItem:
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
[unmatchedSampleText setText:
\gray550\fc3\cf3 "unmatched text"
\gray0\fc2\cf2 ];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - saveConfiguration:sender\
\fs28 \{\
NXTypedStream *stream;\
\
[configurationWindow makeKeyAndOrderFront:
\b self
\b0 ];\
\b if
\b0 (!strcmp([configurationWindow title],
\gray550\fc3\cf3 "Untitled"
\gray0\fc2\cf2 ))\
[
\b self
\b0 getConfigurationName];\
\b if
\b0 (!(stream = NXOpenTypedStreamForFile([configurationWindow title],
\gray93\fc4\cf4 NX_WRITEONLY
\gray0\fc2\cf2 ))) \{\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "Can't open %s."
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 , [configurationWindow title]);\
\b return
\b0
\b self
\b0 ;\
\}\
[
\b self
\b0 saveCurrentItem];\
NXWriteRootObject(stream, unmatchedSampleText);\
NXWriteColor(stream, [unmatchedSampleText textColor]);\
NXWriteObject(stream, itemList);\
NXCloseTypedStream(stream);\
\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - revertToSavedConfiguration:sender\
\fs28 \{\
[configurationWindow makeKeyAndOrderFront:
\b self
\b0 ];\
[
\b self
\b0 openNamedConfiguration:[configurationWindow title]];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - (BOOL)getConfigurationName\
\fs28 \{\
\b id
\b0 savePanel = [SavePanel new];\
\
[savePanel setRequiredFileType:
\gray550\fc3\cf3 "syntax"
\gray0\fc2\cf2 ];\
\b if
\b0 (![savePanel runModalForDirectory:
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 file:
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 ])\
\b return
\b0
\gray93\fc4\cf4 NO
\gray0\fc2\cf2 ;\
[configurationWindow setTitle:[savePanel filename]];\
\b return
\b0
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ;\
\}\
\
\fs36 - saveAsConfiguration:sender\
\fs28 \{\
\b if
\b0 ([
\b self
\b0 getConfigurationName])\
[
\b self
\b0 saveConfiguration:
\b self
\b0 ];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - setDefaultConfigurationName:sender\
\fs28 \{\
\b const
\b0
\b char
\b0 *
\b const
\b0 types[
\gray71\fc5\cf5 2
\gray0\fc2\cf2 ] = \{
\gray550\fc3\cf3 "syntax"
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 \};\
\b id
\b0 openPanel = [OpenPanel new];\
\
[openPanel allowMultipleFiles:
\gray93\fc4\cf4 NO
\gray0\fc2\cf2 ];\
\b if
\b0 (![openPanel runModalForTypes:types])\
\b return
\b0
\gray93\fc4\cf4 NO
\gray0\fc2\cf2 ;\
[defaultConfiguration setStringValue:[openPanel filename]];\
NXWriteDefault(
\gray550\fc3\cf3 "RTFSyntax"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "configuration"
\gray0\fc2\cf2 , [openPanel filename]);\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - setMatchedColor:sender\
\fs28 \{\
[matchedSampleText setTextColor:[matchedColorWell color]];\
[matchedSampleText display];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - setUnmatchedColor:sender;\
\fs28 \{\
[unmatchedSampleText setTextColor:[unmatchedColorWell color]];\
[unmatchedSampleText display];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\b #define
\b0
\gray93\fc4\cf4 MAIL_ADDRESS
\gray0\fc2\cf2
\gray550\fc3\cf3 "rgc@cs.umd.edu"
\gray0\fc2\cf2 \
\b #define
\b0
\gray93\fc4\cf4 SUBJECT
\gray0\fc2\cf2
\gray550\fc3\cf3 "Suggestion/Bug for RTFSyntax"
\gray0\fc2\cf2 \
\
\fs36 - suggestion:sender\
\fs28 \{\
\b id
\b0 s = [NXApp appSpeaker];\
\
NXPortFromName(
\gray550\fc3\cf3 "Mail"
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 );
\i\gray300\fc1\cf1 // make sure app is launched
\i0\gray0\fc2\cf2 \
[[NXApp appSpeaker] setSendPort:NXPortFromName(
\gray550\fc3\cf3 "MailSendDemo"
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 )];\
[s performRemoteMethod:
\gray550\fc3\cf3 "setTo:"
\gray0\fc2\cf2 with:
\gray93\fc4\cf4 MAIL_ADDRESS
\gray0\fc2\cf2 length:strlen(
\gray93\fc4\cf4 MAIL_ADDRESS
\gray0\fc2\cf2 )+
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
[s performRemoteMethod:
\gray550\fc3\cf3 "setSubject:"
\gray0\fc2\cf2 with:
\gray93\fc4\cf4 SUBJECT
\gray0\fc2\cf2 length:strlen(
\gray93\fc4\cf4 SUBJECT
\gray0\fc2\cf2 )+
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
\b return
\b0
\b self
\b0 ;\
\}\
\
\b #define
\b0
\gray93\fc4\cf4 ERRBUF
\gray0\fc2\cf2
\gray71\fc5\cf5 100
\gray0\fc2\cf2 \
\
\fs36 - colorSyntax:pasteboard userData:(const char *)userData error:(char **)message\
\fs28 \{\
NXAtom typelist[
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
\b const
\b0 NXAtom *types;\
NXStream *stream;\
Text *text;\
\
\b for
\b0 (types = [pasteboard types]; *types; types++)\
\b if
\b0 (*types == NXRTFPboardType)\
\b break
\b0 ;\
\b if
\b0 (*types ==
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ) \{\
*message =
\gray550\fc3\cf3 "Could not find RTF pasteboard type"
\gray0\fc2\cf2 ;\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 , *message,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 );\
\b return
\b0
\gray93\fc4\cf4 nil
\gray0\fc2\cf2 ;\
\}\
\
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\fc2\cf2 if
\b0 (![
\b self
\b0 openDefaultConfiguration]) \{\
*message =
\gray551\fc3\cf3 "No default configuration."
\gray0\fc2\cf2 ;\
NXRunAlertPanel(
\gray551\fc3\cf3 "Error"
\gray0\fc2\cf2 , *message,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 );\
\b return
\b0
\gray93\fc4\cf4 nil
\gray0\fc2\cf2 ;\
\}\
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc2\cf2 \
\i\gray300\fc1\cf1 // read the rich text from the pasteboard stream and save it in an internal text object
\i0\gray0\fc2\cf2 \
\b if
\b0 (!(stream = [pasteboard readTypeToStream:NXRTFPboardType])) \{\
*message =
\gray550\fc3\cf3 "Could not read RTF data"
\gray0\fc2\cf2 ;\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 , *message,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 );\
\b return
\b0
\gray93\fc4\cf4 nil
\gray0\fc2\cf2 ;\
\}\
text = [[Text alloc] init];\
[text setMonoFont:
\gray93\fc4\cf4 NO
\gray0\fc2\cf2 ];\
NXSeek(stream, 0L,
\gray93\fc4\cf4 NX_FROMSTART
\gray0\fc2\cf2 );\
[text readRichText:stream];\
NXCloseMemory(stream,
\gray93\fc4\cf4 NX_FREEBUFFER
\gray0\fc2\cf2 ); \
\
[
\b self
\b0 colorRTF:text];\
\
\i\gray300\fc1\cf1 // copy results to the pasteboard
\i0\gray0\fc2\cf2 \
typelist[
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ] = NXRTFPboardType;\
[pasteboard declareTypes:typelist num:
\gray71\fc5\cf5 1
\gray0\fc2\cf2 owner:
\b self
\b0 ];\
\b if
\b0 (!(stream = NXOpenMemory(
\gray93\fc4\cf4 NULL
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray93\fc4\cf4 NX_READWRITE
\gray0\fc2\cf2 ))) \{\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "Can't open memory file."
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 );\
[text free];\
\b return
\b0
\b self
\b0 ;\
\}\
[text writeRichText:stream];\
\b if
\b0 (![pasteboard writeType:NXRTFPboardType fromStream:stream])\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "Can't write results to pasteboard."
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 );\
NXCloseMemory(stream,
\gray93\fc4\cf4 NX_FREEBUFFER
\gray0\fc2\cf2 );\
[text free];\
\
\b return
\b0
\b self
\b0 ;\
\}\
\
\fs36 - (void)colorRTF:(Text *)text\
\i\fs28\gray300\fc1\cf1 /*\
Description:\
Color the given text object using the default configuration.\
Uses GNU's regex library (POSIX syntax).\
*/
\i0\gray0\fc2\cf2 \
\{\
\b char
\b0 errbuf[
\gray93\fc4\cf4 ERRBUF
\gray0\fc2\cf2 ];\
\b typedef
\b0
\b struct
\b0 \{\
regex_t re;\
\b int
\b0 begin, end;
\i\gray300\fc1\cf1 // offsets
\i0\gray0\fc2\cf2 \
\gray93\fc4\cf4 BOOL
\gray0\fc2\cf2 compute;
\i\gray300\fc1\cf1 // YES => compute match
\i0\gray0\fc2\cf2 \
\gray93\fc4\cf4 BOOL
\gray0\fc2\cf2 done;
\i\gray300\fc1\cf1 // YES => no more matches
\i0\gray0\fc2\cf2 \
\} Match;\
Match *match;\
\b char
\b0 *s;\
\b int
\b0 length;\
\b int
\b0 i;\
\b int
\b0 skip =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ;\
\b int
\b0 errcode;\
\
\i\gray300\fc1\cf1 // first set text to be like the unmatched sample text
\i0\gray0\fc2\cf2 \
length = [text textLength];\
[text setSel:
\gray71\fc5\cf5 0
\gray0\fc2\cf2 :length];\
[text setSelColor:[unmatchedSampleText textColor]];\
[text setSelFont:[unmatchedSampleText font]];\
[text selectNull];\
\
\i\gray300\fc1\cf1 // allocate the text string and get it
\i0\gray0\fc2\cf2 \
\b if
\b0 (!(s = malloc(length +
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ))) \{\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "malloc() failed."
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 );\
\b return
\b0 ;\
\}\
\b if
\b0 ([text getSubstring:s start:
\gray71\fc5\cf5 0
\gray0\fc2\cf2 length:length] <=
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ) \{\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "Can't get substring."
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 );\
free(s);\
\b return
\b0 ;\
\}\
s[length] =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ;\
\
\i\gray300\fc1\cf1 // allocate the match array
\i0\gray0\fc2\cf2 \
\b if
\b0 (!(match = malloc(
\b sizeof
\b0 (Match) * [itemList count]))) \{\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "malloc() failed."
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 );\
free(s);\
\b return
\b0 ;\
\}\
\
\i\gray300\fc1\cf1 // compile all of the regular expressions
\i0\gray0\fc2\cf2 \
\b for
\b0 (i =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ; i < [itemList count]; i++) \{\
\b id
\b0 item = [itemList objectAt:i];\
\b char
\b0 *reText = [item getRegularExpressionText];\
\b int
\b0 reLength = strlen(reText);\
\b int
\b0 j, k;\
\
\i\gray300\fc1\cf1 // remove newlines
\i0\gray0\fc2\cf2 \
\b for
\b0 (j =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 , k =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ; k < reLength; k++)\
\b if
\b0 (reText[k] != '\\n')\
reText[j++] = reText[k];\
reText[j] = '\\
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ';\
reLength = j;\
\
\i\gray300\fc1\cf1 // convert "\\n"'s to newlines
\i0\gray0\fc2\cf2 \
\b for
\b0 (j =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 , k =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ; k < reLength; k++)\
\b if
\b0 (reText[k] != '\\\\')\
reText[j++] = reText[k];\
\b else
\b0
\b if
\b0 (k+
\gray71\fc5\cf5 1
\gray0\fc2\cf2 == reLength)\
reText[j++] = '\\\\';\
\b else
\b0
\b if
\b0 (reText[k+
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ] == 'n') \{\
reText[j++] = '\\n';\
k++;\
\}
\b else
\b0 \{\
reText[j++] = '\\\\';\
reText[j++] = reText[k+
\gray71\fc5\cf5 1
\gray0\fc2\cf2 ];\
k++;\
\} \
reText[j] = '\\
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ';\
reLength = j;\
\
\i\gray300\fc1\cf1 // make sure regular expression is not empty
\i0\gray0\fc2\cf2 \
\b if
\b0 (!reLength) \{\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "Regular expression %d is empty."
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 , i+
\gray71\fc5\cf5 1
\gray0\fc2\cf2 );\
\b goto
\b0 cleanup;\
\}\
\
\i\gray300\fc1\cf1 // compile regular expression
\i0\gray0\fc2\cf2 \
\b if
\b0 ((errcode = regcomp(&match[i].re, reText,
\gray93\fc4\cf4 REG_EXTENDED
\gray0\fc2\cf2 |
\gray93\fc4\cf4 REG_NEWLINE
\gray0\fc2\cf2 ))) \{\
regerror(errcode, &match[i].re, errbuf,
\gray93\fc4\cf4 ERRBUF
\gray0\fc2\cf2 );\
NXRunAlertPanel(
\gray550\fc3\cf3 "Error"
\gray0\fc2\cf2 ,
\gray550\fc3\cf3 "regcomp() failed -- %s"
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 , errbuf);\
\b goto
\b0 cleanup;\
\}\
match[i].compute =
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ;\
match[i].done =
\gray93\fc4\cf4 NO
\gray0\fc2\cf2 ;\
\}\
\
\b for
\b0 (;;) \{\
\b int
\b0 j;
\i\gray300\fc1\cf1 // index of match[] to use
\i0\gray0\fc2\cf2 \
\
\i\gray300\fc1\cf1 // compute all regular expressions that need it
\i0\gray0\fc2\cf2 \
\b for
\b0 (i =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ; i < [itemList count]; i++)\
\b if
\b0 (!match[i].done && match[i].compute) \{\
regmatch_t rm;\
\b if
\b0 (regexec(&match[i].re, s + skip,
\gray71\fc5\cf5 1
\gray0\fc2\cf2 , &rm,
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ))\
match[i].done =
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ;\
\b else
\b0 \{\
match[i].begin = rm.rm_so + skip;\
match[i].end = rm.rm_eo + skip;\
match[i].compute =
\gray93\fc4\cf4 NO
\gray0\fc2\cf2 ;\
\}\
\}\
\
\i\gray300\fc1\cf1 // find the first (earliest) match using all regular expressions
\i0\gray0\fc2\cf2 \
\i\gray300\fc1\cf1 // if two RE's find match at the same starting offset, choose the longer match
\i0\gray0\fc2\cf2 \
\i\gray300\fc1\cf1 // store the result in j
\i0\gray0\fc2\cf2 \
\b for
\b0 (i =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 , j =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ; i < [itemList count]; i++)\
\b if
\b0 (!match[i].done) \{\
j = i;\
\b break
\b0 ;\
\}\
\b if
\b0 (i == [itemList count])
\i\gray300\fc1\cf1 // no matches
\i0\gray0\fc2\cf2 \
\b break
\b0 ;\
\b for
\b0 (++i; i < [itemList count]; i++)\
\b if
\b0 (!match[i].done && (match[j].begin > match[i].begin || \
(match[j].begin == match[i].begin && match[j].end < match[i].end)))\
j = i;\
\
\i\gray300\fc1\cf1 // reset all matches that started before match[j].end
\i0\gray0\fc2\cf2 \
\b for
\b0 (i =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ; i < [itemList count]; i++)\
\b if
\b0 (!match[i].done && match[i].begin < match[j].end)\
match[i].compute =
\gray93\fc4\cf4 YES
\gray0\fc2\cf2 ;\
\
\i\gray300\fc1\cf1 // color the matching text
\i0\gray0\fc2\cf2 \
\i\gray300\fc1\cf1 // note that these 4 lines take up about 70% of the program's CPU time
\i0\gray0\fc2\cf2 \
[text setSel:match[j].begin :match[j].end];\
[text setSelColor:[[[itemList objectAt:j] getMatchedSampleText] textColor]];\
[text setSelFont:[[[itemList objectAt:j] getMatchedSampleText] font]];\
[text selectNull];\
\
skip = match[j].end;\
\}\
\
\gray587\fc6\cf6 cleanup:
\gray0\fc2\cf2 \
\i\gray300\fc1\cf1 // free the compiled regular expressions and other stuff
\i0\gray0\fc2\cf2 \
\b for
\b0 (i =
\gray71\fc5\cf5 0
\gray0\fc2\cf2 ; i < [itemList count]; i++)\
regfree(&match[i].re);\
free(match);\
\}\
\
\fs36 - showInfo:sender\
\fs28 \{\
\b if
\b0 (!infoPanel)\
infoPanel = [NXApp loadNibSection:
\gray550\fc3\cf3 "InfoPanel.nib"
\gray0\fc2\cf2 owner:
\b self
\b0 ];\
[infoPanel makeKeyAndOrderFront:
\b self
\b0 ];\
\b return
\b0
\b self
\b0 ; \
\}\
\
\b @end
\b0 \
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.