5 // Created by Marc Liyanage on Fri Aug 02 2002.
6 // Copyright (c) 2002 __MyCompanyName__. All rights reserved.
9 #import "XMLTextView.h"
12 @implementation XMLTextView
17 if (self = [super init]) {
18 [self setRichText:NO];
27 -(void)selectLineByNumber:(int)line {
29 NSString *data = [self string];
30 unsigned int i, startIndex, lineEndIndex;
41 for (i = 1; i <= line; i++) {
43 [data getLineStart:&startIndex end:&lineEndIndex contentsEnd:nil forRange:aRange];
44 aRange.location = lineEndIndex;
48 aRange.location = startIndex;
49 aRange.length = lineEndIndex - startIndex;
51 [self setSelectedRange:aRange];
52 [self scrollRangeToVisible:aRange];
59 -(void)pasteAsRichText:(id)sender {
62 NSLog(@"paste as rich text");
63 // [self pasteAsPlainText:sender];
70 - (void)keyDown:(NSEvent *)event {
72 NSRange selectedRange;
74 if ([[event characters] isEqual:@"\033"]) {
76 if ([event modifierFlags] || [event isARepeat]) {
82 } else if ([[event characters] isEqual:@"/"]) {
84 selectedRange = [self selectedRange];
85 if (selectedRange.location < 2 || selectedRange.length > 0) {
86 [super keyDown:event];
90 if ([[[self string] substringWithRange:NSMakeRange(selectedRange.location - 1, 1)] isEqual:@"<"]) {
91 if([self completeAfterSlash]) {
97 [super keyDown:event];
103 - (BOOL)completeAfterSlash {
105 NSRange selectedRange = [self selectedRange], tagNameRange;
106 NSString *tagName = nil;
108 tagNameRange = [self scanBackwardsForOpeningTagNameInRange:NSMakeRange(0, selectedRange.location - 1)];
110 if (tagNameRange.location == NSNotFound) {
114 tagName = [[self string] substringWithRange:tagNameRange];
116 // NSLog(@"completion!");
118 if (tagName == nil) {
122 [self insertText:[NSString stringWithFormat:@"/%@>", tagName]];
124 [self flashRange:tagNameRange];
130 - (void)flashRange:(NSRange)range {
134 tagNameRect = [self firstRectForCharacterRange:range];
136 tagNameRect.origin = [[self window] convertScreenToBase:tagNameRect.origin];
138 tagNameRect = [self convertRect:tagNameRect fromView:[[self window] contentView]];
142 [[[NSColor selectedControlColor] colorWithAlphaComponent:0.75] set];
144 NSFrameRectWithWidth(tagNameRect, 2);
146 [NSBezierPath fillRect:tagNameRect];
148 [[self window] flushWindow];
152 [self setNeedsDisplay:YES];
159 - (void)complete:(id)sender {
161 NSRange selectedRange, leftRange, rightRange, tagNameRange;
162 NSString *tagName = nil;
163 NSString *data = nil;
166 data = [self string];
168 selectedRange = [self selectedRange];
169 location = selectedRange.location;
171 /* May not have a selection, and insertion point must be preceded by at
172 * least one tag, which means at least 3 characters must be to its left
174 if (selectedRange.length > 0 || location < 3) {
179 /* leftRange is everything from beginning of data to the insertion point.
180 * rightRange is everything after the insertion point to the end of the data
182 leftRange = NSMakeRange(0, location);
183 rightRange = NSMakeRange(location, [data length] - leftRange.length);
187 tagNameRange = [self scanBackwardsForOpeningTagNameInRange:leftRange];
189 if (tagNameRange.location == NSNotFound) {
194 tagName = [data substringWithRange:tagNameRange];
196 [self insertText:[NSString stringWithFormat:@"</%@>", tagName]];
197 [self setSelectedRange:selectedRange];
199 [self flashRange:tagNameRange];
205 -(NSRange)scanBackwardsForOpeningTagNameInRange:(NSRange)scanRange {
207 NSRange tagNameEndRange, tagContentPlusEndRange, leftAngleRange, openingTagRange, slashRange;
208 NSCharacterSet *angleSet = [NSCharacterSet characterSetWithCharactersInString:@"<>"];
209 NSCharacterSet *tagNameDelimiterSet = [NSCharacterSet characterSetWithCharactersInString:@" <>"];
210 NSString *tagName = nil;
211 NSString *data = [self string];
213 NSRange tagNameRange;
216 /* find the first occurence of angle brackets, both opening or closing,
217 * in the range to the left of the insertion point, searching backwards
218 * from the insertion point
220 leftAngleRange = [data rangeOfCharacterFromSet:angleSet options:NSBackwardsSearch range:scanRange];
222 /* abort unless we found something */
223 if (leftAngleRange.location == NSNotFound) {
224 return NSMakeRange(NSNotFound, 0);
227 // NSLog(@"leftbracket: %d %d", leftAngleRange.location, leftAngleRange.length);
230 /* We expect the nearest angle bracket to our left to be a closing one, we will
231 * not try to complete an open tag if we find an opening bracket instead.
233 character = [data substringWithRange:leftAngleRange];
234 if (![character isEqual:@">"]) {
235 return NSMakeRange(NSNotFound, 0);
238 /* shift end of scanRange so it ends after the closing angle bracket we just found */
239 scanRange = NSMakeRange(0, leftAngleRange.location);
242 /* Now search again from the end of that range backward to the beginning of the data
243 * and look for another angle bracket.
245 leftAngleRange = [data rangeOfCharacterFromSet:angleSet options:NSBackwardsSearch range:NSMakeRange(0, scanRange.length - 1)];
247 /* again abort unless we found something */
248 if (leftAngleRange.location == NSNotFound) {
249 return NSMakeRange(NSNotFound, 0);
252 //NSLog(@"leftbracket: %d %d", leftAngleRange.location, leftAngleRange.length);
254 /* This time we expect to see an opening angle bracket */
255 character = [data substringWithRange:leftAngleRange];
256 if (![character isEqual:@"<"]) {
257 return NSMakeRange(NSNotFound, 0);
261 /* This will indicate the range of the entire nearest tag to our left,
262 * we assume that's the opening tag */
263 openingTagRange = NSMakeRange(leftAngleRange.location, (scanRange.length - leftAngleRange.location) + 1);
265 /* check if it is indeed an opening tag, i.e. no slash after the opening bracket */
266 slashRange = NSMakeRange(openingTagRange.location + 1, 1);
267 character = [data substringWithRange:slashRange];
268 if ([character isEqual:@"/"]) {
269 return NSMakeRange(NSNotFound, 0);
272 /* Don't complete processing instructions, comments etc. */
273 if ([character isEqual:@"?"] || [character isEqual:@"!"]) {
274 return NSMakeRange(NSNotFound, 0);
278 /* check if it is a merged start and end tag, i.e. slash before closing bracket */
279 slashRange = NSMakeRange((openingTagRange.location + openingTagRange.length) - 2, 1);
280 character = [data substringWithRange:slashRange];
281 if ([character isEqual:@"/"]) {
282 return NSMakeRange(NSNotFound, 0);
286 tagContentPlusEndRange = NSMakeRange(openingTagRange.location + 1, openingTagRange.length - 1);
288 //NSLog(@"tagcontent: %d %d", tagContentPlusEndRange.location, tagContentPlusEndRange.length);
290 tagNameEndRange = [data rangeOfCharacterFromSet:tagNameDelimiterSet options:0 range:tagContentPlusEndRange];
293 tagNameRange = NSMakeRange(tagContentPlusEndRange.location, tagNameEndRange.location - tagContentPlusEndRange.location);
295 tagName = [data substringWithRange:tagNameRange];
297 if ([tagName length] < 1) {
298 return NSMakeRange(NSNotFound, 0);
304 //NSLog(@"tagrange: %d %d / %@", tagNameEndRange.location, tagNameEndRange.length, tagName);