2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #import <Cocoa/Cocoa.h>
22 #import "HTMLRenderer.h"
23 #import "MyDocument.h"
24 #import "SCTextView.h"
25 #import "SCVirtualMachine.h"
27 #import "GetStringFromUser.h"
29 #include "SC_DirUtils.h"
30 #import "SMLAdvancedFindController.h"
40 # include <sys/param.h>
43 #include "PyrObject.h"
44 #include "PyrKernel.h"
46 #include "VMGlobals.h"
48 bool firstWindow
= true;
49 bool needBlankWindow
= true;
50 extern NSTextView
*gPostView
;
51 extern pthread_mutex_t gLangMutex
;
52 extern bool compiledOK
;
54 bool docCreatedFromLang
= false; //if this is true addDocument is not called
55 bool defaultDocumentUseAutoInOutDent
= true;
57 NSFont
*defaultFont
= nil;
59 @implementation MyDocument
64 defaultFont
= [NSFont fontWithName
: @
"Monaco" size
: 9];
73 pathToSaveAttachments
= nil;
78 // the first one, not the split
79 - (SCTextView
*)makeTextView
81 SCTextView
* aTextView
= [[SCTextView alloc
] initWithFrame
: NSMakeRect(0,0,612,512)];
83 [self initialiseTextViewParams
: aTextView
];
84 [aTextView setFont
: defaultFont
];
85 activeTextView
= aTextView
;
89 - (void)initialiseTextViewParams
: (SCTextView
*)aTextView
91 [aTextView setAutoresizingMask
: 63];
92 [[aTextView textContainer
] setWidthTracksTextView
: YES
];
93 [aTextView setDelegate
: self];
94 [aTextView setAllowsUndo
: YES
];
95 [aTextView setRichText
: isRichText
];
96 [aTextView setSmartInsertDeleteEnabled
: NO
];
97 [aTextView setImportsGraphics
: YES
];
98 [aTextView setLangClassToCall
:@
"CocoaDocument"
99 withKeyDownActionIndex
:4 withKeyUpActionIndex
:5];
100 [aTextView setObjectKeyDownActionIndex
:2 setObjectKeyUpActionIndex
:1];
101 [aTextView setAcceptsFirstResponder
:YES
];
102 [aTextView setUsesAutoInOutdent
:defaultDocumentUseAutoInOutDent
];
107 [self sendSelection
: "addDocument"];
111 - (void)windowControllerDidLoadNib
:(NSWindowController
*) aController
113 [super windowControllerDidLoadNib
:aController
];
116 contentSize
= [scrollView contentSize
];
118 textView
= initTextView
;
120 textView
= [self makeTextView
];
122 NSWindow
*window
= [scrollView window
];
125 mySplitView
= [[NSSplitView alloc
] initWithFrame
:[scrollView frame
]];
126 [window setContentView
: mySplitView
];
128 [mySplitView addSubview
:scrollView
];
130 [scrollView setDocumentView
: textView
];
133 [textView setSelectedRange
: NSMakeRange(0,0)];
137 if (needBlankWindow
) {
138 [[NSDocumentController sharedDocumentController
] newDocument
: nil];
139 needBlankWindow
= false;
143 needBlankWindow
= false;
144 gPostView
= textView
;
146 [[SCVirtualMachine sharedInstance
] start
];
150 [window makeKeyWindow
];
151 [window makeFirstResponder
: textView
];
153 if(!docCreatedFromLang
)[self addDocument
];
156 - (void)openCode
:(id)sender
158 [self sendSelection
: "openCodeFile"];
161 - (void)methodTemplates
: (id)sender
163 [self sendSelection
: "methodTemplates"];
166 - (void)methodReferences
: (id)sender
168 [self sendSelection
: "methodReferences"];
171 - (void)balanceParens
: (id)sender
173 [activeTextView balanceParens
: nil];
176 - (BOOL)textView
:(NSTextView
*)theTextView shouldChangeTextInRange
:(NSRange
)affectedCharRange replacementString
:(NSString
*)replacementString
178 if (replacementString
&& [replacementString length
] > 0) {
179 unichar c
= [replacementString characterAtIndex
: 0];
180 if (strchr(")]}", c
)) {
181 NSString
*string
= [theTextView string
];
183 int length
= [string length
];
184 unichar
* buffer
= (unichar
*)malloc((length
+1) * sizeof(unichar
));
185 [string getCharacters
: buffer
];
187 unsigned int start
, end
;
188 start
= affectedCharRange.location
;
191 bool doMatchBraks
= !blankUnparsedChars(buffer
, end
, true);
195 bool res
= matchBraks(&start
, &end
, buffer
, length
, c
, false);
197 [self flashHighlight
: YES atIndex
: start wait
: 0.33 timesLeft
: 1];
200 [self flashHighlight
: NO atIndex
: affectedCharRange.location wait
: 0.07 timesLeft
: 6];
209 - (void)flashHighlight
: (BOOL)onoff atIndex
: (int)index wait
: (NSTimeInterval
)interval timesLeft
: (int)timesLeft
211 NSLayoutManager
*layoutManager
= [activeTextView layoutManager
];
212 NSRange highlightRange
= NSMakeRange(index
, 1);
215 [layoutManager addTemporaryAttributes
:
216 [NSDictionary dictionaryWithObjectsAndKeys
: [NSColor lightGrayColor
], NSBackgroundColorAttributeName
, nil]
217 forCharacterRange
: highlightRange
];
219 //highlightRange.length++;
220 [layoutManager removeTemporaryAttribute
: NSBackgroundColorAttributeName
221 forCharacterRange
: highlightRange
];
226 SEL sel
= @selector(flashHighlight
:atIndex
:wait
:timesLeft
:);
227 NSMethodSignature
*sig
= [MyDocument instanceMethodSignatureForSelector
: sel
];
228 NSInvocation
*anInvocation
= [NSInvocation invocationWithMethodSignature
: sig
];
229 [anInvocation setTarget
: self];
230 [anInvocation setSelector
: sel
];
231 [anInvocation setArgument
: &onoff atIndex
: 2];
232 [anInvocation setArgument
: &index atIndex
: 3];
233 [anInvocation setArgument
: &interval atIndex
: 4];
234 [anInvocation setArgument
: ×Left atIndex
: 5];
235 [NSTimer scheduledTimerWithTimeInterval
: interval invocation
: anInvocation repeats
: NO
];
240 void SyntaxColorize(NSTextView
* textView
);
242 - (void)syntaxColorize
: (id)sender
244 SyntaxColorize(activeTextView
);
245 [activeTextView didChangeText
];
248 - (void) insertText
: (char*) text length
: (int)length
250 NSRange selectedRange
= [activeTextView selectedRange
];
251 NSString
*string
= [[NSString alloc
] initWithCString
: text length
: length
];
252 if ([activeTextView shouldChangeTextInRange
: selectedRange replacementString
: string
]) {
253 [activeTextView replaceCharactersInRange
: selectedRange withString
: string
];
254 [activeTextView didChangeText
];
260 - (void)wrapParensBegin
:(NSString
*)begStr end
:(NSString
*)endStr
262 NSTextStorage
*textStorage
= [activeTextView textStorage
];
263 NSRange selectedRange
= [activeTextView selectedRange
];
264 NSAttributedString
*originalSelection
= [textStorage attributedSubstringFromRange
: selectedRange
];
265 NSMutableAttributedString
*newString
= [[NSMutableAttributedString alloc
] initWithAttributedString
: originalSelection
];
266 [newString autorelease
];
268 [newString replaceCharactersInRange
: NSMakeRange([newString length
],0) withString
: endStr
];
269 [newString replaceCharactersInRange
: NSMakeRange(0,0) withString
: begStr
];
271 if ([activeTextView shouldChangeTextInRange
: selectedRange replacementString
: [newString string
]]) {
272 [textStorage replaceCharactersInRange
: selectedRange withAttributedString
: newString
];
273 NSRange newSelectedRange
= selectedRange
;
274 if(selectedRange.length
> 0) newSelectedRange.length
+= [begStr length
] + [endStr length
];
277 newSelectedRange.length
= 0;
278 newSelectedRange.location
= newSelectedRange.location
+ [begStr length
];
280 [activeTextView setSelectedRange
: newSelectedRange
];
281 SyntaxColorize(activeTextView
);
282 [activeTextView didChangeText
];
286 - (void)wrapParens
: (int)sender
288 [self wrapParensBegin
: @
"(" end
: @
")"];
291 - (void)wrapSquareBrackets
: (int)sender
293 [self wrapParensBegin
: @
"[" end
: @
"]"];
296 - (void)wrapCurlyBrackets
: (int)sender
298 [self wrapParensBegin
: @
"{" end
: @
"}"];
301 - (void)wrapComments
: (int)sender
303 [self wrapParensBegin
: @
"/*" end
: @
"*/"];
307 - (void)shiftLeft
: (id)sender
309 NSTextStorage
*textStorage
= [activeTextView textStorage
];
310 NSRange selectedRange
= [activeTextView selectedRange
];
311 if (selectedRange.length
<= 0) return;
312 NSAttributedString
*originalSelection
= [textStorage attributedSubstringFromRange
: selectedRange
];
314 NSMutableAttributedString
*newString
= [[NSMutableAttributedString alloc
] initWithAttributedString
: originalSelection
];
315 [newString autorelease
];
317 NSString
*string
= [originalSelection string
];
319 int length
= [string length
];
320 unichar
* buffer
= (unichar
*)malloc((length
+1) * sizeof(unichar
));
321 [string getCharacters
: buffer
];
325 if (buffer
[0] == NSTabCharacter
) {
326 [newString deleteCharactersInRange
: NSMakeRange(0,1)];
329 for (int i
=0; i
<length
-1; ++i
, ++j
) {
330 unichar c
= buffer
[i
];
331 unichar d
= buffer
[i
+1];
332 if (d
== NSTabCharacter
&& (c
== NSNewlineCharacter || c
== NSCarriageReturnCharacter
)) {
333 [newString deleteCharactersInRange
: NSMakeRange(j
,1)];
338 NSRange newSelectedRange
= NSMakeRange(selectedRange.location
, j
);
340 if ([activeTextView shouldChangeTextInRange
: selectedRange replacementString
: [newString string
]]) {
341 [textStorage replaceCharactersInRange
: selectedRange withAttributedString
: newString
];
342 [activeTextView setSelectedRange
: newSelectedRange
];
343 [activeTextView didChangeText
];
347 - (void)shiftRight
: (id)sender
349 NSTextStorage
*textStorage
= [activeTextView textStorage
];
350 NSRange selectedRange
= [activeTextView selectedRange
];
351 if (selectedRange.length
<= 0) return;
352 NSAttributedString
*originalSelection
= [textStorage attributedSubstringFromRange
: selectedRange
];
354 NSString
*tabStr
= [[NSString alloc
] initWithString
: @
"\t"];
355 [tabStr autorelease
];
357 NSMutableAttributedString
*newString
= [[NSMutableAttributedString alloc
] initWithAttributedString
: originalSelection
];
358 [newString autorelease
];
360 NSString
*string
= [originalSelection string
];
362 int length
= [string length
];
363 unichar
* buffer
= (unichar
*)malloc((length
+1) * sizeof(unichar
));
364 [string getCharacters
: buffer
];
366 [newString replaceCharactersInRange
: NSMakeRange(0,0) withString
: tabStr
];
368 for (int i
=0; i
<length
-1; ++i
, ++j
) {
369 unichar c
= buffer
[i
];
370 if (c
== NSNewlineCharacter || c
== NSCarriageReturnCharacter
) {
371 [newString replaceCharactersInRange
: NSMakeRange(j
+1,0) withString
: tabStr
];
376 NSRange newSelectedRange
= NSMakeRange(selectedRange.location
, j
+1);
378 if ([activeTextView shouldChangeTextInRange
: selectedRange replacementString
: [newString string
]]) {
379 [textStorage replaceCharactersInRange
: selectedRange withAttributedString
: newString
];
380 [activeTextView setSelectedRange
: newSelectedRange
];
381 [activeTextView didChangeText
];
385 - (void)commentCode
: (id)sender
387 NSTextStorage
*textStorage
= [activeTextView textStorage
];
388 NSRange selectedRange
= [activeTextView selectedRange
];
389 if (selectedRange.length
<= 0) return;
390 NSAttributedString
*originalSelection
= [textStorage attributedSubstringFromRange
: selectedRange
];
392 NSString
*commentChars
= [[NSString alloc
] initWithString
: @
"//"];
393 [commentChars autorelease
];
395 NSMutableAttributedString
*newString
= [[NSMutableAttributedString alloc
] initWithAttributedString
: originalSelection
];
396 [newString autorelease
];
398 NSString
*string
= [originalSelection string
];
400 int length
= [string length
];
401 unichar
* buffer
= (unichar
*)malloc((length
+1) * sizeof(unichar
));
402 [string getCharacters
: buffer
];
404 [newString replaceCharactersInRange
: NSMakeRange(0,0) withString
: commentChars
];
406 for (int i
=0; i
<length
-1; ++i
, ++j
) {
407 unichar c
= buffer
[i
];
408 if (c
== NSNewlineCharacter || c
== NSCarriageReturnCharacter
) {
409 [newString replaceCharactersInRange
: NSMakeRange(j
+1,0) withString
: commentChars
];
414 NSRange newSelectedRange
= NSMakeRange(selectedRange.location
, j
+1);
416 if ([activeTextView shouldChangeTextInRange
: selectedRange replacementString
: [newString string
]]) {
417 [textStorage replaceCharactersInRange
: selectedRange withAttributedString
: newString
];
418 [activeTextView didChangeText
];
419 [activeTextView setSelectedRange
: newSelectedRange
];
420 SyntaxColorize(activeTextView
);
421 [activeTextView didChangeText
];
425 - (void)uncommentCode
:(id)sender
427 NSTextStorage
*textStorage
= [activeTextView textStorage
];
428 NSRange selectedRange
= [activeTextView selectedRange
];
429 if (selectedRange.length
<= 0) return;
430 NSAttributedString
*originalSelection
= [textStorage attributedSubstringFromRange
: selectedRange
];
432 NSMutableAttributedString
*newString
= [[NSMutableAttributedString alloc
] initWithAttributedString
: originalSelection
];
433 [newString autorelease
];
435 NSString
*string
= [originalSelection string
];
437 int length
= [string length
];
438 if (length
< 2) return;
440 unichar
* buffer
= (unichar
*)malloc((length
+1) * sizeof(unichar
));
441 [string getCharacters
: buffer
];
446 if (buffer
[0] == '/' && buffer
[1] == '/') {
447 [newString deleteCharactersInRange
: NSMakeRange(0,2)];
450 for (; i
<length
-2; ++i
, ++j
) {
451 unichar c
= buffer
[i
];
452 unichar d
= buffer
[i
+1];
453 unichar e
= buffer
[i
+2];
454 if (d
== '/' && e
== '/' && (c
== NSNewlineCharacter || c
== NSCarriageReturnCharacter
)) {
455 [newString deleteCharactersInRange
: NSMakeRange(j
+1,2)];
460 NSRange newSelectedRange
= NSMakeRange(selectedRange.location
, j
+2);
462 if ([activeTextView shouldChangeTextInRange
: selectedRange replacementString
: [newString string
]]) {
463 [textStorage replaceCharactersInRange
: selectedRange withAttributedString
: newString
];
464 [activeTextView didChangeText
];
465 [activeTextView setSelectedRange
: newSelectedRange
];
466 SyntaxColorize(activeTextView
);
467 [activeTextView didChangeText
];
472 - (IBAction
) executeSelection
: (id) sender
474 [self sendSelection
: "interpretPrintCmdLine" ];
477 NSString
* helpFileWithName(NSFileManager
* fileManager
, NSString
* desiredHelpFile
, NSString
* helpFolderPath
)
479 // catch the common case of wanting main help
480 if([desiredHelpFile isEqual
: @
""]) {
481 NSString
* mainHelpPath
= @
"Help/Help.html";
482 if([fileManager fileExistsAtPath
: mainHelpPath
]) {
485 // maybe it's moved or changed format
486 return helpFileWithName(fileManager
, @
"Help", helpFolderPath
);
490 // If called with no folder check in the bundle (if OS X), SC distribution Help
491 // directory, and the System and User Extensions directories.
492 if(!helpFolderPath
) {
493 NSString
* helpFilePath
;
494 NSString
* searchPath
;
496 char resourceDirPath
[MAXPATHLEN
- 32];
497 sc_GetResourceDirectory(resourceDirPath
, MAXPATHLEN
- 32);
498 sc_AppendToPath(resourceDirPath
, PATH_MAX
- 32, "Help/");
499 searchPath
= [NSString stringWithCString
:(const char *) resourceDirPath
];
500 helpFilePath
= helpFileWithName(fileManager
, desiredHelpFile
, searchPath
);
501 if (helpFilePath
) return helpFilePath
;
503 if(!sc_IsStandAlone()) {
504 searchPath
= [NSString stringWithFormat
: @
"%@/Help/",
505 [fileManager currentDirectoryPath
]];
506 helpFilePath
= helpFileWithName(fileManager
, desiredHelpFile
, searchPath
);
507 if (helpFilePath
) return helpFilePath
;
509 searchPath
= @
"/Library/Application Support/SuperCollider/Extensions/";
510 helpFilePath
= helpFileWithName(fileManager
, desiredHelpFile
, searchPath
);
511 if (helpFilePath
) return helpFilePath
;
513 searchPath
= [NSString stringWithFormat
: @
"%@/Library/Application Support/SuperCollider/Extensions/",
515 helpFilePath
= helpFileWithName(fileManager
, desiredHelpFile
, searchPath
);
516 if (helpFilePath
) return helpFilePath
;
521 // the name of the help file we are looking for
522 NSString
* helpFileName
;
524 // substitute for these symbols, as they are not valid filenames on one or more platforms
525 if([desiredHelpFile isEqual
: @
"-"]) {
526 helpFileName
= @
"subtraction";
527 } else if([desiredHelpFile isEqual
: @
"/"]) {
528 helpFileName
= @
"division";
529 } else if([desiredHelpFile isEqual
: @
"**"]) {
530 helpFileName
= @
"exponentiation";
531 } else if([desiredHelpFile isEqual
: @
"%"]) {
532 helpFileName
= @
"modulo";
533 } else if([desiredHelpFile isEqual
: @
"*"]) {
534 helpFileName
= @
"multiplication";
535 } else if([desiredHelpFile isEqual
: @
"+"]) {
536 helpFileName
= @
"addition";
537 } else if([desiredHelpFile isEqual
: @
"<"]) {
538 helpFileName
= @
"lessthan";
539 } else if([desiredHelpFile isEqual
: @
"<="]) {
540 helpFileName
= @
"lessorequalthan";
541 } else if([desiredHelpFile isEqual
: @
">"]) {
542 helpFileName
= @
"greaterthan";
543 } else if([desiredHelpFile isEqual
: @
">="]) {
544 helpFileName
= @
"greaterorequalthan";
546 helpFileName
= desiredHelpFile
;
549 // recurse through this directory until we find it
550 NSString
* helpFilePath
= nil;
551 NSDirectoryEnumerator
* dirEnumerator
= [fileManager enumeratorAtPath
:helpFolderPath
];
554 while (candidate
= [dirEnumerator nextObject
]) {
555 if([[candidate lastPathComponent
] isEqualToString
: @
".svn"]
556 ||
[[candidate lastPathComponent
] isEqualToString
: @
"ignore"]
557 ||
[[candidate lastPathComponent
] isEqualToString
: @
".git"]
558 ||
[[candidate lastPathComponent
] isEqualToString
: @
"_darcs"]
560 [dirEnumerator skipDescendents
];
562 NSString
* trialName
= [helpFolderPath stringByAppendingString
: candidate
];
564 char name
[MAXPATHLEN
];
565 char resolvedName
[MAXPATHLEN
];
566 [trialName getCString
:name
];
567 bool isAlias
= false;
568 sc_ResolveIfAlias(name
, resolvedName
, isAlias
, MAXPATHLEN
);
569 fullName
= [NSString stringWithCString
:resolvedName
];
570 // compare with valid extensions
571 if([[fullName lastPathComponent
] isEqualToString
: [helpFileName stringByAppendingString
: @
".html"]]
572 ||
[[fullName lastPathComponent
] isEqualToString
: [helpFileName stringByAppendingString
: @
".rtf"]]
573 ||
[[fullName lastPathComponent
] isEqualToString
: [helpFileName stringByAppendingString
: @
".rtfd"]]
574 ||
[[fullName lastPathComponent
] isEqualToString
: [helpFileName stringByAppendingString
: @
".scd"]]
575 ||
[[fullName lastPathComponent
] isEqualToString
: [helpFileName stringByAppendingString
: @
".help.scd"]]
576 ||
[[fullName lastPathComponent
] isEqualToString
: [helpFileName stringByAppendingString
: @
".schelp"]]
577 ||
[[fullName lastPathComponent
] isEqualToString
: [helpFileName stringByAppendingString
: @
".help.rtf"]]
578 ||
[[fullName lastPathComponent
] isEqualToString
: [helpFileName stringByAppendingString
: @
".htm"]]
580 helpFilePath
= fullName
;
582 } else if // if that didn't work, check if this is a directory symlink (allow directories to have extensions)
583 ((([[fileManager fileAttributesAtPath
: fullName traverseLink
: NO
] objectForKey
:@
"NSFileType"] == NSFileTypeSymbolicLink
) || isAlias
)){
584 NSString
* linkName
= [[fullName stringByResolvingSymlinksInPath
] stringByAppendingString
: @
"/"];
585 if(([[fileManager fileAttributesAtPath
: linkName traverseLink
: NO
] objectForKey
:@
"NSFileType"] == NSFileTypeDirectory
)) {
586 // if so traverse the link and call helpFileWithName() recursively
587 helpFilePath
= helpFileWithName(fileManager
, desiredHelpFile
, linkName
);
588 if(helpFilePath
) break;
599 -(void)selectRangeStart
:(int)rangeStart size
:(int)rangeSize
602 NSRange range
= NSMakeRange(rangeStart
, rangeSize
);
603 NSString
*nsstring
= [activeTextView string
];
605 post("text view has no string\n");
608 int maxlength
= [nsstring length
];
609 if(maxlength
< 1) range
= NSMakeRange(0, 0);
612 NSUInteger lineStart
, lineEnd
;
614 [nsstring getLineStart
: &lineStart end
: &lineEnd contentsEnd
: nil forRange
: range
];
615 range
= NSMakeRange(lineStart
, lineEnd
- lineStart
);
617 if(rangeStart
< 0) range.location
= 0;
618 if(rangeStart
>= maxlength
) range.location
= maxlength
;
619 if(rangeSize
+ rangeStart
>= maxlength
) {
620 range.length
= maxlength
- range.location
;
623 [activeTextView setSelectedRange
: range
];
624 [activeTextView scrollRangeToVisible
: range
];
627 -(void)selectLine
:(int)linenum
629 NSString
*nsstring
= [activeTextView string
];
630 int length
= [nsstring length
];
631 if(length
< 1) return;
632 NSLayoutManager
*layoutManager
= [activeTextView layoutManager
];
633 unsigned numberOfLines
, index
, numberOfGlyphs
= [layoutManager numberOfGlyphs
];
635 if(linenum
<= 0) linenum
= 1;
636 for (numberOfLines
= 0, index
= 0; index
< numberOfGlyphs
&& (int)numberOfLines
< linenum
; numberOfLines
++) {
638 (void) [layoutManager lineFragmentRectForGlyphAtIndex
:index effectiveRange
:&lineRange
];
639 index
= NSMaxRange(lineRange
);
642 [activeTextView setSelectedRange
: lineRange
];
643 [activeTextView scrollRangeToVisible
: lineRange
];
648 - (IBAction
)selectLineWindow
: (id) sender
650 [[GoToPanel sharedInstance
] orderFrontGotoLinePanel
:nil];
654 - (IBAction
) advancedFindReplaceAction
:(id)sender
656 [[SMLAdvancedFindController sharedInstance
] showAdvancedFindWindow
];
659 - (IBAction
) showHelpFor
: (id) sender
661 [self sendSelection
: "showHelp"];
664 - (IBAction
)showClassBrowser
:(id)sender
{
665 [self sendSelection
: "showClassBrowser"];
669 // Handle a click in a link.
670 - (BOOL) textView
: (NSTextView
*) textView
671 clickedOnLink
: (id) link
672 atIndex
: (unsigned) charIndex
674 NSDocumentController
* docctl
= [NSDocumentController sharedDocumentController
];
675 if (!docctl
) return YES
;
679 // is it a NSURL link or a NSString link?
680 if ([link isKindOfClass
: [NSString
class]])
682 if([link hasPrefix
:@
"SC://"] ||
[link hasPrefix
:@
"sc://"]) { // this means search immediately
683 NSString
*helpPath
= pathOfHelpFileFor([[[link substringFromIndex
:5] stringByDeletingPathExtension
] stringByDeletingPathExtension
]);
685 post("WARNING:\nInvalid hyperlink: '%s' Please repair this.\n", [link cString
]);
688 desiredURL
= [NSURL URLWithString
: helpPath
];
689 } else desiredURL
= [NSURL URLWithString
: [link stringByAddingPercentEscapesUsingEncoding
: NSUTF8StringEncoding
] relativeToURL
: [self fileURL
]];
691 } else if ([link isKindOfClass
: [NSURL
class]])
694 // check for schemes which we'll handle
695 if([[link scheme
] isEqualToString
: @
"SC"] ||
[[link scheme
] isEqualToString
:@
"sc"]) { // this means search immediately
697 NSString
*helpPath
= pathOfHelpFileFor([[[[link relativeString
] substringFromIndex
:5] stringByDeletingPathExtension
] stringByDeletingPathExtension
]);
699 post("WARNING:\nInvalid hyperlink: '%s' Please repair this.\n", [[link relativeString
] cString
]);
702 desiredURL
= [NSURL fileURLWithPath
: helpPath
];
703 } else if (![link scheme
]) { // NULL could be a regular file link that's been edited
704 if([[link relativePath
] hasPrefix
: @
"/"]) {
705 desiredURL
= [NSURL fileURLWithPath
: [link relativeString
]];
707 desiredURL
= [NSURL URLWithString
: [[link relativeString
] stringByAddingPercentEscapesUsingEncoding
: NSUTF8StringEncoding
] relativeToURL
: [self fileURL
]];
709 //desiredURL = [NSURL fileURLWithPath: [link relativeString]];
710 } else if(![[link scheme
] isEqualToString
: @
"file"]) {
711 return NO
; // it's http, etc. so pass it on to Safari or whatever
712 } else desiredURL
= link
; // it's a regular file:// URL
715 MyDocument
*doc
= nil;
716 if([[NSFileManager defaultManager
] fileExistsAtPath
: [desiredURL path
]]) {
718 doc
= (MyDocument
*)[docctl documentForURL
: [desiredURL absoluteURL
]];
719 } else NSLog(@
"file doesn't exist at path");
721 doc
= [docctl openDocumentWithContentsOfURL
: desiredURL display
: true];
723 // it's a bad file:// URL, post a warning and search
724 post("WARNING:\nInvalid hyperlink: '%s' Please repair this.\nSearching help directories for alternative.\n", [[desiredURL path
] cString
]);
725 // delete extension twice in case something.help.rtf
726 NSString
*desiredHelpName
= [[[[desiredURL path
] lastPathComponent
] stringByDeletingPathExtension
] stringByDeletingPathExtension
];
727 NSString
*helpPath
= pathOfHelpFileFor(desiredHelpName
);
729 post("Can't find Help File Document for: '%s'\n", [desiredHelpName cString
]);
732 desiredURL
= [NSURL fileURLWithPath
: helpPath
];
733 if([[NSFileManager defaultManager
] fileExistsAtPath
: [desiredURL path
]]) {
734 doc
= (MyDocument
*)[docctl documentForURL
: [desiredURL absoluteURL
]];
735 } else post("file doesn't exist at path: '%s'\n", [[desiredURL path
] cString
]);
737 doc
= [docctl openDocumentWithContentsOfURL
: desiredURL display
: true];
739 post("Can't open Help File Document: '%s'\n", [[desiredURL path
] cString
]);
745 NSWindow
*window
= [[[doc windowControllers
] objectAtIndex
: 0] window
];
747 post("!! window controller returns nil ? failed to open help file window\n");
750 [window makeKeyAndOrderFront
: nil];
754 // Create a link to a helpfile
756 - (IBAction
) createLink
: (id) sender
760 NSMutableDictionary
*linkAttributes
;
762 selection
= [activeTextView selectedRange
];
763 // check to see if we can make a link
764 if (selection.length
== 0)
766 NSRunAlertPanel (@
"Link to Help file", @
"Please select some text before creating a link.", nil, nil, nil);
768 } else if(![self fileURL
]) {
769 NSRunAlertPanel (@
"Link to Help file", @
"You must save this Document before creating a link.", nil, nil, nil);
773 // Get the text which will go into the link.
775 link
= pathOfHelpFileFor([activeTextView currentlySelectedTextOrLine
: NULL
]);
777 NSRunAlertPanel (@
"Link to Help file", [NSString stringWithFormat
: @
"No help named %@ exists.", [activeTextView currentlySelectedTextOrLine
: NULL
]], nil, nil, nil);
780 BOOL targetInMainHelp
= [[[self fileURL
] path
] rangeOfString
: [NSString stringWithFormat
: @
"%@/Help/", [[NSFileManager defaultManager
] currentDirectoryPath
]]].location
!= NSNotFound
;
781 BOOL linkInMainHelp
= [link rangeOfString
: [NSString stringWithFormat
: @
"%@/Help/", [[NSFileManager defaultManager
] currentDirectoryPath
]]].location
!= NSNotFound
;
782 // if both are in main help, or both are not, produce a relative link, otherwise an SC:// URL, which will call the search system.
783 if((targetInMainHelp
&& linkInMainHelp
) ||
(!targetInMainHelp
&& !linkInMainHelp
)) {
784 link
= pathOfFileRelativeToBaseDir(link
, [[[self fileURL
] path
] stringByDeletingLastPathComponent
]);
785 } else link
= [NSURL URLWithString
: [@
"SC://" stringByAppendingString
: [activeTextView currentlySelectedTextOrLine
: NULL
]]];
787 // Start to build attributes
788 linkAttributes
= [NSMutableDictionary dictionaryWithObject
: link forKey
: NSLinkAttributeName
];
790 // Currently always blue
791 [linkAttributes setObject
: [NSColor blueColor
] forKey
: NSForegroundColorAttributeName
];
793 // Add the attributes. This adds the link attributes to the selected range.
794 [[activeTextView textStorage
] addAttributes
: linkAttributes range
: selection
];
796 // Make sure it saves as HTML as default
797 [self setFileType
: @
"HTML"]; // need to change name to html
798 [self updateChangeCount
: NSChangeDone
];
802 - (void)sendSelection
: (char*) methodName
808 NSRange selectedRange
;
809 NSString
* selection
= [activeTextView currentlySelectedTextOrLine
: &selectedRange
];
810 const char *text
= [selection UTF8String
];
811 int textlength
= strlen(text
);
813 [[SCVirtualMachine sharedInstance
] setCmdLine
: text length
: textlength
];
815 NSRange newSelectedRange
= NSMakeRange(selectedRange.location
+ selectedRange.length
, 0);
816 [activeTextView setSelectedRange
: newSelectedRange
];
818 pthread_mutex_lock(&gLangMutex
);
819 runLibrary(getsym(methodName
));
820 pthread_mutex_unlock(&gLangMutex
);
824 - (NSString
*)windowNibName
826 // Override returning the nib file name of the document
827 // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
828 return @
"MyDocument";
831 - (BOOL)prepareSavePanel
:(NSSavePanel
*)savePanel
{
832 [savePanel setDelegate
: self];
836 - (void)setpathToSaveAttachments
:(NSString
*)newPathToSaveAttachments
{
837 [newPathToSaveAttachments retain
];
838 [pathToSaveAttachments release
];
839 pathToSaveAttachments
= newPathToSaveAttachments
;
842 - (NSString
*)panel
:(id)sender userEnteredFilename
:(NSString
*)filename confirmed
:(BOOL)okFlag
{
844 if([[self fileTypeFromLastRunSavePanel
] isEqualToString
: @
"SC Help (HTML)"] && [[textView textStorage
] containsAttachments
]) {
845 //.../attachments/filename/
846 [self setpathToSaveAttachments
:
847 [[[[sender filename
] stringByDeletingLastPathComponent
] stringByAppendingPathComponent
: @
"attachments"] stringByAppendingPathComponent
: [[[sender filename
] lastPathComponent
] stringByDeletingPathExtension
]]];
856 - (BOOL)writeToFile
:(NSString
*) path ofType
:(NSString
*)aType
859 NSString
* extension
= [path pathExtension
];
860 if ([extension isEqualToString
: @
"sc"] ||
[extension isEqualToString
: @
"txt"] ||
[extension isEqualToString
: @
"scd"] ||
[extension isEqualToString
: @
"quark"]
861 ||
[aType isEqualToString
: @
"NSStringPboardType"])
863 NSString
*text
= [textView string
];
864 success
= [text writeToFile
: path atomically
: YES
];
865 } else if ([extension isEqualToString
: @
"html"] ||
[extension isEqualToString
: @
"htm"]) {
867 [textView setDefaultTabsTo
: 28.0f
]; // set default tabs to preserve layout
869 //[self replaceOldStyleHelpLinksWithHyperLinks];
870 NSTextStorage
*textStorage
= [textView textStorage
];
871 [self convertFileLinksToRelative
: textStorage
]; // clean up any manually edited file:// links
872 NSRange range
= NSMakeRange(0, [textStorage length
]);
873 NSDictionary
*attributes
= [NSDictionary dictionaryWithObject
: NSHTMLTextDocumentType forKey
:NSDocumentTypeDocumentAttribute
];
875 // Check for images etc.
876 if ([textStorage containsAttachments
]) {
877 // this has wrappers for html and all attachments
878 NSFileWrapper
*wrapper
= [textStorage fileWrapperFromRange
: range documentAttributes
: attributes error
: NULL
];
879 success
= wrapper ? YES
: NO
;
880 // if no wrapper bail out...
881 if(!success
) return success
;
883 // now get the html component so we can clean it up and save it under the requested name
884 NSFileWrapper
*indexWrapper
= [[wrapper fileWrappers
] objectForKey
: @
"index.html"];
885 [indexWrapper retain
];
886 [wrapper removeFileWrapper
: indexWrapper
];
887 NSMutableString
*html
= [[NSMutableString alloc
] initWithData
: [indexWrapper regularFileContents
] encoding
: NSUTF8StringEncoding
]; // maybe should check here?
888 // strip out "file:" so as to have nice portable relative urls
889 [html replaceOccurrencesOfString
: @
"file:///" withString
: @
"" options
: NSCaseInsensitiveSearch range
: NSMakeRange(0, [html length
])];
891 // replace "%20" with " " as psycollider doesn't like the former
892 [html replaceOccurrencesOfString
: @
"%20" withString
: @
" " options
: NSCaseInsensitiveSearch range
: NSMakeRange(0, [html length
])];
894 // fix faded autosyntax Colours
895 [html replaceOccurrencesOfString
: @
"color: #0016bd" withString
: @
"color: #0000bf" options
: NSCaseInsensitiveSearch range
: NSMakeRange(0, [html length
])]; // blue
896 [html replaceOccurrencesOfString
: @
"color: #ae1a19" withString
: @
"color: #bf0000" options
: NSCaseInsensitiveSearch range
: NSMakeRange(0, [html length
])]; // red
897 [html replaceOccurrencesOfString
: @
"color: #2b7000" withString
: @
"color: #007300" options
: NSCaseInsensitiveSearch range
: NSMakeRange(0, [html length
])]; // green
900 // NSTextView doesn't like <b> tags within spans, and unfortunately the export outputs bold underlines that way.
901 // The following figures out what the span name is for underlining, and fixes this
902 fixHTMLBoldUnderline(html
);
904 // now save attachments to the final directory
905 NSFileManager
*fm
= [NSFileManager defaultManager
];
906 // check attachments/filename/ exists
907 if(![fm fileExistsAtPath
: [pathToSaveAttachments stringByDeletingLastPathComponent
]]) {
908 success
= [fm createDirectoryAtPath
: [pathToSaveAttachments stringByDeletingLastPathComponent
] attributes
: nil];
909 if(!success
) return success
;
911 if(![fm fileExistsAtPath
: pathToSaveAttachments
]) {
912 success
= [fm createDirectoryAtPath
: pathToSaveAttachments attributes
: nil];
913 if(!success
) return success
;
916 NSEnumerator
*files
= [[wrapper fileWrappers
] objectEnumerator
];
917 NSFileWrapper
*thisFile
;
918 while(thisFile
= [files nextObject
]) {
919 // save relative to pathToSaveAttachments (which we stashed earlier) as the path arg passed in
920 // may not be the requested path, but rather a temp save location
921 NSString
*thisPath
= [pathToSaveAttachments stringByAppendingPathComponent
: [thisFile preferredFilename
]];
922 NSString
*thisFileName
= [thisPath lastPathComponent
];
924 // now check if this is an image and convert to png.
925 NSBitmapImageRep
*imageRep
= [NSBitmapImageRep imageRepWithData
: [thisFile regularFileContents
]];
928 NSData
*pngData
= [imageRep representationUsingType
: NSPNGFileType properties
: nil];
929 NSString
*pngURL
= [[NSString stringWithFormat
:@
"attachments/%@/", [[path lastPathComponent
] stringByDeletingPathExtension
]]
930 stringByAppendingString
: [[thisFileName stringByDeletingPathExtension
] stringByAppendingPathExtension
: @
"png"]];
931 [html replaceOccurrencesOfString
: [NSString stringWithFormat
:@
"<img src=\"%@\" alt=\"%@\">", thisFileName
, thisFileName
]
932 withString
: [NSString stringWithFormat
:@
"<img src=\"%@\" alt=\"%@\">", pngURL
, pngURL
]
933 options
: NSCaseInsensitiveSearch
934 range
: NSMakeRange(0, [html length
])
936 success
= success
&& [pngData writeToFile
: [pathToSaveAttachments stringByAppendingPathComponent
: [pngURL lastPathComponent
]] atomically
:YES
];
937 } else if([[thisFileName pathExtension
] isEqualToString
:@
"pdf"]) {
939 NSString
*correctURL
= [[NSString stringWithFormat
:@
"attachments/%@/", [[path lastPathComponent
] stringByDeletingPathExtension
]] stringByAppendingString
: thisFileName
];
940 [html replaceOccurrencesOfString
: [NSString stringWithFormat
:@
"<img src=\"%@\"", thisFileName
]
941 withString
: [NSString stringWithFormat
:@
"<img src=\"%@\"", correctURL
]
942 options
: NSCaseInsensitiveSearch
943 range
: NSMakeRange(0, [html length
])
945 success
= success
&& [thisFile writeToFile
: thisPath atomically
:YES updateFilenames
: YES
];
948 NSString
*correctURL
= [[NSString stringWithFormat
:@
"attachments/%@/", [[path lastPathComponent
] stringByDeletingPathExtension
]] stringByAppendingString
: thisFileName
];
949 [html replaceOccurrencesOfString
: [NSString stringWithFormat
:@
"<object data=\"%@\">%@</object>", thisFileName
, thisFileName
]
950 withString
: [NSString stringWithFormat
:@
"<object data=\"%@\">%@</object>", correctURL
, correctURL
]
951 options
: NSCaseInsensitiveSearch
952 range
: NSMakeRange(0, [html length
])
954 success
= success
&& [thisFile writeToFile
: thisPath atomically
:YES updateFilenames
: YES
];
959 NSFileWrapper
*htmlWrapper
= [[NSFileWrapper alloc
] initRegularFileWithContents
: [html dataUsingEncoding
: NSUTF8StringEncoding
]];
960 if(!htmlWrapper
) { NSLog(@
"HTML Wrapper init failed."); }
961 success
= success
&& [htmlWrapper writeToFile
: path atomically
:YES updateFilenames
: YES
];
962 if(!success
) return success
;
963 [indexWrapper release
];
964 [htmlWrapper release
];
967 // html with no attachments, way more simpler
968 NSData
*data
= [textStorage dataFromRange
:range documentAttributes
:attributes error
:NULL
];
969 NSMutableString
*html
= [[NSMutableString alloc
] initWithData
: data encoding
: NSUTF8StringEncoding
];
971 fixHTMLBoldUnderline(html
);
972 // fix faded autosyntax Colours
973 [html replaceOccurrencesOfString
: @
"color: #0016bd" withString
: @
"color: #0000bf" options
: NSCaseInsensitiveSearch range
: NSMakeRange(0, [html length
])]; // blue
974 [html replaceOccurrencesOfString
: @
"color: #ae1a19" withString
: @
"color: #bf0000" options
: NSCaseInsensitiveSearch range
: NSMakeRange(0, [html length
])]; // red
975 [html replaceOccurrencesOfString
: @
"color: #2b7000" withString
: @
"color: #007300" options
: NSCaseInsensitiveSearch range
: NSMakeRange(0, [html length
])]; // green
978 data
= [html dataUsingEncoding
: NSUTF8StringEncoding
];
979 success
= data ?
[data writeToFile
: path atomically
: YES
] : NO
;
984 NSTextStorage
*textStorage
= [textView textStorage
];
986 NSRange range
= NSMakeRange(0, [textStorage length
]);
987 NSMutableDictionary
*dict
= [NSMutableDictionary dictionary
];
988 if ([textStorage containsAttachments
]) {
989 if (![extension isEqualToString
: @
"rtfd"]) {
990 path
= [[path stringByDeletingPathExtension
] stringByAppendingPathExtension
: @
"rtfd"];
992 NSFileWrapper
*wrapper
= [textStorage RTFDFileWrapperFromRange
: range documentAttributes
: dict
];
993 success
= wrapper ?
[wrapper writeToFile
: path atomically
:YES updateFilenames
: YES
] : NO
;
995 if ([extension isEqualToString
: @
""]) {
996 path
= [path stringByAppendingPathExtension
: @
"rtf"];
998 NSData
*data
= [textStorage RTFFromRange
: range documentAttributes
: dict
];
999 success
= data ?
[data writeToFile
: path atomically
: YES
] : NO
;
1002 [textView breakUndoCoalescing
];
1006 - (NSDictionary
*)fileAttributesToWriteToFile
:(NSString
*)fullDocumentPath ofType
:(NSString
*)type saveOperation
: (NSSaveOperationType
) saveOperationType
1008 NSMutableDictionary
*attr
= [NSMutableDictionary dictionary
];
1009 NSNumber
*creator
= [NSNumber numberWithInt
: 'SCjm'];
1010 [attr setObject
: creator forKey
: NSFileHFSCreatorCode
];
1014 - (void) loadHTML
:(NSURL
*)url
1018 if (!initTextView
) initTextView
= [self makeTextView
];
1019 NSTextStorage
* text
= [initTextView textStorage
];
1021 [text beginEditing
]; // Bracket with begin/end editing for efficiency
1022 [[text mutableString
] setString
:@
""]; // Empty the document
1024 NSAttributedString
*htmlAttributedString
= [HTMLRenderer attributedStringWithURL
:url
];
1026 if(htmlAttributedString
)
1028 [text setAttributedString
:htmlAttributedString
];
1029 [initTextView setDefaultTabsTo
: 28.0f
];
1037 - (BOOL)readFromFile
:(NSString
*)path ofType
:(NSString
*)aType
1039 NSURL
*url
= [NSURL fileURLWithPath
: path
];
1042 NSString
* extension
= [path pathExtension
];
1044 if (!initTextView
) initTextView
= [self makeTextView
];
1045 NSTextStorage
* text
= [initTextView textStorage
];
1047 if ([extension isEqualToString
: @
"html"] ||
[extension isEqualToString
: @
"htm"])
1050 if (docCreatedFromLang
)
1052 SEL sel
= @selector(loadHTML
:);
1053 NSMethodSignature
*sig
= [MyDocument instanceMethodSignatureForSelector
: sel
];
1054 SCVirtualMachine
* scvm
= [SCVirtualMachine sharedInstance
];
1056 NSInvocation
*anInvocation
= [NSInvocation invocationWithMethodSignature
: sig
];
1057 [anInvocation setTarget
: self];
1058 [anInvocation setSelector
: sel
];
1059 [anInvocation setArgument
:&url atIndex
:2];
1060 [scvm defer
: anInvocation
];
1064 [self loadHTML
:url
];
1066 [self setpathToSaveAttachments
:[[[path stringByDeletingLastPathComponent
] stringByAppendingPathComponent
: @
"attachments"] stringByAppendingPathComponent
: [[path lastPathComponent
] stringByDeletingPathExtension
]]];
1071 [text beginEditing
]; // Bracket with begin/end editing for efficiency
1072 [[text mutableString
] setString
:@
""]; // Empty the document
1076 success
= [text readFromURL
:url options
:nil documentAttributes
:nil error
:&error
];
1077 if(!success
) NSLog(@
"Error opening file: %@", error
);
1079 if ([extension isEqualToString
: @
"sc"] ||
[extension isEqualToString
: @
"scd"]) {
1080 [initTextView setFont
: defaultFont
];
1081 SyntaxColorize(initTextView
);
1085 // in case of html with images, etc.
1086 [self setpathToSaveAttachments
:[[[path stringByDeletingLastPathComponent
] stringByAppendingPathComponent
: @
"attachments"] stringByAppendingPathComponent
: [[path lastPathComponent
] stringByDeletingPathExtension
]]];
1090 - (BOOL) shouldRunSavePanelWithAccessoryView
1095 - (SCTextView
*) textView
;
1100 - (SCTextView
*) textView2
;
1105 - (BOOL)windowShouldClose
:(id)sender
1107 return (textView
!= gPostView
);
1109 extern PyrSymbol
* s_didBecomeKey
;
1110 - (void) windowDidBecomeKey
:(NSNotification
*)aNotification
1112 if(!docCreatedFromLang
){
1113 [self callSCLangWithMethod
: s_didBecomeKey
];
1118 extern PyrSymbol
* s_didResignKey
;
1119 - (void) windowDidResignKey
:(NSNotification
*)aNotification
1122 if(!docCreatedFromLang
){
1123 [self callSCLangWithMethod
: s_didResignKey
];
1127 - (void) callSCLangWithMethod
: (PyrSymbol
*) method
{
1128 pthread_mutex_lock (&gLangMutex
);
1130 struct PyrObject
*scobj
= [self getSCObject
];
1132 SetPtr(scobj
->slots
+ 0, self);
1133 VMGlobals
*g
= gMainVMGlobals
;
1134 g
->canCallOS
= true;
1135 ++g
->sp
; SetObject(g
->sp
, scobj
); // push window obj
1136 runInterpreter(g
, method
, 1);
1137 g
->canCallOS
= false;
1140 pthread_mutex_unlock (&gLangMutex
);
1142 - (void)windowWillClose
:(NSNotification
*)aNotification
1144 //for some reason some Documents created with open do not call windowWillClose
1145 if (textView
== gPostView
) gPostView
= nil;
1146 [self callSCLangWithMethod
: s_closed
];
1147 [self setSCObject
: nil];
1150 //for some reason some Documents created with open do not call windowWillClose
1151 //so that its action is called here: jan.t
1152 [pathToSaveAttachments release
];
1155 - (IBAction
) becomePostWindow
: (id) sender
1157 gPostView
= textView
;
1161 - (BOOL) isDocumentEdited
1163 if ([self textView
] == gPostView ||
!promptToSave
) return false;
1164 return [super isDocumentEdited
];
1167 //////////////////////////////////////
1169 - (void)doToggleRich
{
1170 [self setRichText
:!isRichText
];
1171 //[self setEncoding:NoStringEncoding];
1172 //[self setConverted:NO];
1173 if ([[textView textStorage
] length
] > 0) [[textView window
] setDocumentEdited
: YES
];
1174 //[self setDocumentName:nil];
1177 /* toggleRich: puts up an alert before ultimately calling doToggleRich
1179 - (void)toggleRich
:(id)sender
{
1180 //int length = [[textView textStorage] length];
1182 //NSDictionary *attrs;
1183 [self doToggleRich
];
1185 // If we are rich and any ofthe text attrs have been changed from the default, then put up an alert first...
1186 if (isRichText && (length > 0) && (attrs = [[textView textStorage] attributesAtIndex:0 effectiveRange:&range]) && ((attrs == nil) || (range.length < length) || ![[self defaultTextAttributes:YES] isEqual:attrs])) {
1187 NSBeginAlertSheet(NSLocalizedString(@"Convert document to plain text?", @"Title of alert confirming Make Plain Text"),
1188 NSLocalizedString(@"OK", @"OK"), NSLocalizedString(@"Cancel", @"Button choice allowing user to cancel."), nil, [textView window],
1189 self, NULL, @selector(didEndToggleRichSheet:returnCode:contextInfo:), NULL,
1190 NSLocalizedString(@"Converting will lose fonts, colors, and other text styles in the document.", @"Subtitle of alert confirming Make Plain Text"));
1192 [self doToggleRich];
1198 - (void)didEndToggleRichSheet:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo {
1199 if (returnCode == NSAlertDefaultReturn) [self doToggleRich];
1204 /* Doesn't check to see if the prev value is the same --- Otherwise the first time doesn't work...
1206 - (void)setRichText
:(BOOL)flag
{
1207 NSTextView
*view
= textView
;
1211 //if (!isRichText) [self removeAttachments];
1213 [view setRichText
:isRichText
];
1214 [view setUsesRuler
:isRichText
]; /* If NO, this correctly gets rid of the ruler if it was up */
1215 //if (isRichText && [[Preferences objectForKey:ShowRuler] boolValue]) [view setRulerVisible:YES]; /* Show ruler if rich, and desired */
1216 [view setImportsGraphics
:isRichText
];
1219 NSMutableDictionary
*textAttributes
= [NSMutableDictionary dictionary
];
1220 [textAttributes setObject
: defaultFont forKey
: NSFontAttributeName
];
1222 if ([[textView textStorage
] length
]) {
1223 [[textView textStorage
] setAttributes
:textAttributes range
: NSMakeRange(0, [[textView textStorage
] length
])];
1226 [view setTypingAttributes
:textAttributes
];
1231 /* Menu validation: Arbitrary numbers to determine the state of the menu items whose titles change. Speeds up the validation... Not zero. */
1232 #define TagForFirst 42
1233 #define TagForSecond 43
1235 static void validateToggleItem(NSMenuItem
*aCell
, BOOL useFirst
, NSString
*first
, NSString
*second
) {
1237 if ([aCell tag
] != TagForFirst
) {
1238 [aCell setTitleWithMnemonic
:first
];
1239 [aCell setTag
:TagForFirst
];
1242 if ([aCell tag
] != TagForSecond
) {
1243 [aCell setTitleWithMnemonic
:second
];
1244 [aCell setTag
:TagForSecond
];
1249 - (BOOL)validateMenuItem
:(NSMenuItem
*)aCell
{
1250 SEL action
= [aCell action
];
1251 if (action
== @selector(toggleRich
:)) {
1252 validateToggleItem(aCell
, isRichText
, NSLocalizedString(@
"&Make Plain Text", @
"Menu item to make the current document plain text"), NSLocalizedString(@
"&Make Rich Text", @
"Menu item to make the current document rich text"));
1253 //if (![textView isEditable] || [self hasSheet]) return NO;
1254 } else if (action
== @selector(toggleSplitWindow
:))
1256 [aCell setState
:(split ? NSOnState
: NSOffState
)];
1259 return [super validateMenuItem
: aCell
];
1263 - (void)setSCObject
: (struct PyrObject
*)inObject
1265 mWindowObj
= inObject
;
1268 - (struct PyrObject
*)getSCObject
1273 - (void) closeWindow
1278 - (NSScrollView
*) scrollView
;
1282 - (SCTextView
*) initTextView
;
1284 return initTextView
;
1287 - (SCTextView
*) activeTextView
1289 return activeTextView
;
1292 - (void)setBackgroundColor
:(NSColor
*)color
1294 if ([color brightnessComponent
] < 0.5) {
1295 [textView setInsertionPointColor
: [NSColor whiteColor
]];
1296 [textView2 setInsertionPointColor
: [NSColor whiteColor
]];
1298 [textView setInsertionPointColor
: [NSColor blackColor
]];
1299 [textView2 setInsertionPointColor
: [NSColor blackColor
]];
1302 [textView setBackgroundColor
: color
];
1303 [[self initTextView
] setBackgroundColor
: color
];
1304 [textView2 setBackgroundColor
: color
];
1305 [[self scrollView
] setBackgroundColor
: color
];
1306 [scrollView2 setBackgroundColor
: color
];
1307 [[textView window
] setAlphaValue
: [color alphaComponent
]];
1310 - (void)setSelectedBackgroundColor
:(NSColor
*)color
1312 [textView setSelectedTextAttributes
: [NSDictionary dictionaryWithObject
: color forKey
: NSBackgroundColorAttributeName
]];
1313 [textView2 setSelectedTextAttributes
: [NSDictionary dictionaryWithObject
: color forKey
: NSBackgroundColorAttributeName
]];
1316 - (BOOL)promptToSave
1318 return promptToSave
;
1321 -(void)setPromptToSave
:(BOOL)flag
1323 promptToSave
= flag
;
1325 - (void) keyDown
: (NSEvent
*) event
1329 - (BOOL) handleKeyDown
: (NSEvent
*) event
;
1334 - (void) keyUp
: (NSEvent
*) event
1337 - (void) mouseDown
: (NSEvent
*) event
1342 - (IBAction
)convertHelpLinksToHyperlinks
:(id)sender
1344 [self replaceOldStyleHelpLinksWithHyperLinks
];
1346 - (void)replaceOldStyleHelpLinksWithHyperLinks
1348 NSTextStorage
*textStorage
= [textView textStorage
];
1349 NSRange limitRange
= NSMakeRange(0, [textStorage length
]);
1350 NSRange underlineRange
;
1351 NSMutableArray
*bracketIndices
= [NSMutableArray arrayWithCapacity
: 10];
1352 while (limitRange.length
> 0) {
1353 id isUnderlined
= [textStorage attribute
: NSUnderlineStyleAttributeName
1354 atIndex
: limitRange.location
1355 longestEffectiveRange
: &underlineRange
1356 inRange
: limitRange
];
1358 if(underlineRange.length
<=0) break;
1360 int openBracketIndex
= -1, closeBracketIndex
= -1;
1361 if([[textStorage string
] characterAtIndex
: underlineRange.location
] == '[') {
1362 openBracketIndex
= underlineRange.location
;
1363 } else if(underlineRange.location
> 0) {
1364 if([[textStorage string
] characterAtIndex
: underlineRange.location
- 1] == '[') openBracketIndex
= underlineRange.location
- 1;
1367 if([[textStorage string
] characterAtIndex
: NSMaxRange(underlineRange
) - 1] == ']') {
1368 closeBracketIndex
= NSMaxRange(underlineRange
) - 1;
1369 } else if(NSMaxRange(underlineRange
) < [textStorage length
]) {
1370 if([[textStorage string
] characterAtIndex
: NSMaxRange(underlineRange
)] == ']') closeBracketIndex
= NSMaxRange(underlineRange
);
1372 if(openBracketIndex
>= 0 && closeBracketIndex
> 0) {
1373 [bracketIndices addObject
: [NSNumber numberWithInt
: openBracketIndex
]];
1374 [bracketIndices addObject
: [NSNumber numberWithInt
: closeBracketIndex
]];
1375 //[textStorage beginEditing];
1376 // remove the underline, and bold if there
1377 [textStorage removeAttribute
: NSUnderlineStyleAttributeName range
: underlineRange
];
1378 NSFont
*font
= [textStorage attribute
:NSFontAttributeName atIndex
: underlineRange.location effectiveRange
:NULL
];
1379 font
= [[NSFontManager sharedFontManager
] convertFont
: font toNotHaveTrait
: NSBoldFontMask
];
1380 if(font
) [textStorage addAttribute
: NSFontAttributeName value
: font range
: underlineRange
];
1381 // now make the link
1382 [textView setSelectedRange
: NSMakeRange(openBracketIndex
+ 1, closeBracketIndex
- openBracketIndex
- 1)];
1383 [self createLink
:NULL
];
1384 //[textStorage endEditing];
1389 limitRange
= NSMakeRange(NSMaxRange(underlineRange
), [textStorage length
] - NSMaxRange(underlineRange
));
1394 NSEnumerator
*bracketEnum
= [bracketIndices reverseObjectEnumerator
];
1395 NSNumber
*bracketIndex
;
1396 while ((bracketIndex
= [bracketEnum nextObject
])) {
1397 [textStorage deleteCharactersInRange
: NSMakeRange([bracketIndex intValue
], 1)];
1401 // the text storage converts relative URLs to file:// URLs on open so this converts them back, and cleans up any manually edited links before saving.
1402 - (void)convertFileLinksToRelative
:(NSTextStorage
*)textStorage
1404 //NSTextStorage *textStorage = [textView textStorage];
1405 NSRange limitRange
= NSMakeRange(0, [textStorage length
]);
1407 while (limitRange.length
> 0) {
1408 id link
= [textStorage attribute
: NSLinkAttributeName
1409 atIndex
: limitRange.location
1410 longestEffectiveRange
: &linkRange
1411 inRange
: limitRange
];
1412 if(linkRange.length
<=0) break;
1413 if (link
&& [link isKindOfClass
: [NSURL
class]] && ([[link scheme
] isEqualToString
: @
"file"])) {
1414 NSString
*newLink
= pathOfFileRelativeToBaseDir([link path
], [[[self fileURL
] path
] stringByDeletingLastPathComponent
]);
1415 [textStorage addAttribute
: NSLinkAttributeName value
: newLink range
: linkRange
];
1417 limitRange
= NSMakeRange(NSMaxRange(linkRange
), NSMaxRange(limitRange
) - NSMaxRange(linkRange
));
1422 - (IBAction
)toggleSplitWindow
: (id)sender
1425 textView2
= [[SCTextView alloc
] initWithFrame
: [scrollView frame
]];
1426 [self initialiseTextViewParams
: textView2
];
1427 NSLog(@
"delegate: %@", [textView2 delegate
]);
1428 [textView2 setBackgroundColor
: [textView backgroundColor
]];
1429 [textView2 setSelectedTextAttributes
: [textView selectedTextAttributes
]];
1430 [textView2 setUsesAutoInOutdent
:[textView usesAutoInOutdent
]];
1431 scrollView2
= [[NSScrollView alloc
] initWithFrame
:[scrollView frame
]];
1432 [scrollView2 setHasVerticalScroller
:YES
];
1434 // a bit easier to see
1435 [scrollView setBorderType
:NSBezelBorder
];
1436 [scrollView2 setBorderType
:NSBezelBorder
];
1438 [mySplitView addSubview
:scrollView2
];
1439 [scrollView2 setDocumentView
: textView2
];
1441 [[textView2 layoutManager
] replaceTextStorage
:[textView textStorage
]];
1443 [textView2 release
];
1444 [scrollView2 release
];
1446 [mySplitView adjustSubviews
];
1448 [(NSMenuItem
*)sender setState
:NSOnState
];
1450 [scrollView2 removeFromSuperview
];
1451 [scrollView setBorderType
:NSNoBorder
];
1455 [(NSMenuItem
*)sender setState
:NSOffState
];
1460 - (void)setActiveTextView
:(SCTextView
*)aTextView
1462 activeTextView
= aTextView
;
1465 - (void) setUsesAutoInOutdent
: (bool) flag
;
1467 [textView setUsesAutoInOutdent
:flag
];
1468 [textView2 setUsesAutoInOutdent
:flag
];
1473 NSString
* pathOfHelpFileFor(NSString
* selection
)
1476 NSString
* helpFilePath
= nil;
1478 NSFileManager
* fileManager
= [NSFileManager defaultManager
];
1479 if (!fileManager
) return helpFilePath
; // == NULL
1481 helpFilePath
= helpFileWithName(fileManager
, selection
, nil);
1482 if (helpFilePath
) return helpFilePath
;
1484 return helpFilePath
; // possibly == NULL
1488 void showHelpFor(NSString* selection)
1490 NSDocumentController* docctl = [NSDocumentController sharedDocumentController];
1494 NSString *helpFilePath = pathOfHelpFileFor(selection);
1495 if(!helpFilePath) { // none found
1496 helpFilePath = helpFileWithName([NSFileManager defaultManager], @"Help", nil);
1497 if(!helpFilePath) { // not even Help.help ?
1498 post("\nCan't find help for '%s'\n", [selection cString]);
1503 MyDocument *doc = (MyDocument*)[docctl documentForFileName: helpFilePath];
1505 doc = [docctl openDocumentWithContentsOfURL:[NSURL fileURLWithPath:helpFilePath] display:true error:nil];
1508 post("Can't open Help File Document '%s'\n", [helpFilePath cString]);
1512 NSWindow *window = [[[doc windowControllers] objectAtIndex: 0] window];
1514 post("!! window controller returns nil ? failed to open help file window\n");
1517 [window makeKeyAndOrderFront: nil];
1521 void fixHTMLBoldUnderline(NSMutableString
* html
) {
1522 // NSTextView doesn't like <b> tags within spans, and unfortunately the export outputs bold underlines that way.
1523 // The following figures out what the span name is for underlining, and fixes this
1524 NSRange underlineSpan
= [html rangeOfString
: @
"{text-decoration: underline}"];
1526 if(underlineSpan.location
!= NSNotFound
) {
1528 unsigned lineStart
, nameStart
;
1529 // goto beginning of line
1530 [html getLineStart
: (NSUInteger
*)&lineStart end
: NULL contentsEnd
: NULL forRange
: underlineSpan
];
1531 nameStart
= lineStart
+ 5; // offset for "span."
1532 NSString
*underlineSpanClass
= [html substringWithRange
: NSMakeRange(nameStart
, underlineSpan.location
- 1 - nameStart
)];
1533 //NSLog(@"span class: %@", underlineSpanClass);
1534 NSString
*underlineBoldSpanTag
= [NSString stringWithFormat
: @
"<span class=\"%@\"><b>", underlineSpanClass
];
1535 NSString
*replaceTag
= [NSString stringWithFormat
: @
"<b><span class=\"%@\">", underlineSpanClass
];
1536 //NSLog(@"span tag: %@", underlineBoldSpanTag);
1538 NSRange openTagRange
= [html rangeOfString
: underlineBoldSpanTag options
: NSCaseInsensitiveSearch
]; // finds the first one
1539 while (openTagRange.location
!= NSNotFound
) {
1540 [html replaceCharactersInRange
: openTagRange withString
: replaceTag
];
1541 // now replace the close tag
1542 unsigned int end
= NSMaxRange(openTagRange
);
1543 NSRange closeTagRange
= [html rangeOfString
: @
"</b></span>" options
: NSCaseInsensitiveSearch range
: NSMakeRange(end
, [html length
] - end
)];
1544 [html replaceCharactersInRange
: closeTagRange withString
: @
"</span></b>"];
1545 // see if there's another one
1546 end
= NSMaxRange(closeTagRange
);
1547 openTagRange
= [html rangeOfString
: underlineBoldSpanTag options
: NSCaseInsensitiveSearch range
: NSMakeRange(end
, [html length
] - end
)];
1552 // base is a directory, filepath is a file
1553 NSString
* pathOfFileRelativeToBaseDir(NSString
*filepath
, NSString
*baseDir
) {
1555 if (![filepath isAbsolutePath
]) {
1557 } else if(![baseDir isAbsolutePath
]) {
1560 NSArray
*pathComponents
= [filepath pathComponents
];
1561 NSArray
*baseComponents
= [baseDir pathComponents
];
1562 NSEnumerator
*pathEnumerator
= [pathComponents objectEnumerator
];
1563 NSEnumerator
*baseEnumerator
= [baseComponents objectEnumerator
];
1565 int i
= 0, pathCount
, baseCount
, shortest
;
1566 pathCount
= [pathComponents count
];
1567 baseCount
= [baseComponents count
];
1568 shortest
= pathCount
<= baseCount ? pathCount
: baseCount
;
1570 while((i
< shortest
) && [[pathEnumerator nextObject
] isEqualToString
: [baseEnumerator nextObject
]]) {
1574 NSString
*result
= @
"";
1575 for(int j
=0; j
< (baseCount
- i
); j
++) {
1576 result
= [result stringByAppendingString
: @
"../"];
1578 result
= [result stringByAppendingString
: [NSString pathWithComponents
: [pathComponents subarrayWithRange
: NSMakeRange(i
, pathCount
- i
) ]]];