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
18 [self setRichText:NO];
20 defaults = [NSUserDefaults standardUserDefaults];
22 // register our two input text views to receive file drags
24 // [xmlView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
25 // [xsltView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
27 NSFont *computerFont = [NSFont fontWithName:@"Courier" size:12.0];
28 [self setFont:computerFont];
30 [self setAllowsUndo:YES];
39 [errorString release];
48 -(void)selectLineByNumber:(int)line {
50 NSString *data = [self string];
51 unsigned int i, startIndex, lineEndIndex;
62 for (i = 1; i <= line; i++) {
64 [data getLineStart:&startIndex end:&lineEndIndex contentsEnd:nil forRange:aRange];
65 aRange.location = lineEndIndex;
69 aRange.location = startIndex;
70 aRange.length = lineEndIndex - startIndex;
72 [self setSelectedRange:aRange];
73 [self scrollRangeToVisible:aRange];
82 - (void)keyDown:(NSEvent *)event {
84 NSRange selectedRange;
86 if (![defaults boolForKey:@"enableSyntaxAnalysis"]) {
87 [super keyDown:event];
91 if ([[event characters] isEqual:@"\033"]) {
93 if ([event modifierFlags] || [event isARepeat]) {
99 } else if ([[event characters] isEqual:@"/"]) {
101 selectedRange = [self selectedRange];
102 if (selectedRange.location < 2 || selectedRange.length > 0) {
103 [super keyDown:event];
107 if ([[[self string] substringWithRange:NSMakeRange(selectedRange.location - 1, 1)] isEqual:@"<"]) {
108 if([self completeAfterSlash]) {
116 if ([event modifierFlags] & NSFunctionKeyMask) {
117 [self didChangeText];
124 [super keyDown:event];
130 - (BOOL)completeAfterSlash {
132 int location = [self selectedRange].location - 1;
134 [self calculateTagStackAtLocation:location];
140 NSTextStorage *storage = [self textStorage];
141 [storage beginEditing];
142 [storage deleteCharactersInRange:NSMakeRange(location, 1)];
143 [storage endEditing];
145 [self setSelectedRange:NSMakeRange(location, 0)];
155 - (void)flashRange:(NSRange)range {
159 tagNameRect = [self firstRectForCharacterRange:range];
161 tagNameRect.origin = [[self window] convertScreenToBase:tagNameRect.origin];
163 tagNameRect = [self convertRect:tagNameRect fromView:[[self window] contentView]];
167 [[[NSColor selectedControlColor] colorWithAlphaComponent:0.75] set];
169 NSFrameRectWithWidth(tagNameRect, 2);
171 [NSBezierPath fillRect:tagNameRect];
173 [[self window] flushWindow];
177 [self setNeedsDisplay:YES];
183 - (void)calculateTagStack {
184 [self calculateTagStackAtLocation:[self selectedRange].location];
188 - (BOOL)checkWellFormed {
191 NSData *data = [XMLUtils getDataWithEncodingFromString:[self string]];
193 parser = XML_ParserCreate(NULL);
196 NSLog(@"Unable to allocate expat parser in XMLTextView:checkWellFormed!");
200 result = XML_Parse(parser, [data bytes], [data length], 1);
203 [self setError:[NSString stringWithFormat:@"%s, line %d, column %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser), XML_GetCurrentColumnNumber(parser)] atLine:XML_GetCurrentLineNumber(parser) atColumn:XML_GetCurrentColumnNumber(parser)];
204 } else if (hasError) {
208 XML_ParserFree(parser);
215 errorLine = errorColumn = 0;
216 [self setValue:[NSNumber numberWithBool:NO] forKey:@"hasError"];
217 [errorString release];
223 - (void)setError:(NSString *)errstring atLine:(int)line atColumn:(int)column {
224 [self setValue:[NSNumber numberWithInt:line] forKey:@"errorLine"];
225 [self setValue:[NSNumber numberWithInt:column] forKey:@"errorColumn"];
226 [self setValue:[NSNumber numberWithBool:YES] forKey:@"hasError"];
227 [self setValue:errstring forKey:@"errorString"];
232 - (void)calculateTagStackAtLocation:(int)location {
236 NSRange selectedRange;
237 NSMutableString *mystack;
239 if (![[NSUserDefaults standardUserDefaults] boolForKey:@"enableSyntaxAnalysis"]) {
243 buffer = [[self string] lossyCString];
244 selectedRange = [self selectedRange];
250 resultstack = findCompletion(buffer, strlen(buffer), location, &stackresult, tagpositions);
252 mystack = [NSMutableString stringWithCapacity:1000];
254 for (i = 0; resultstack && *(resultstack[i]); i++) {
255 if (*(resultstack[i+1])) {
256 [mystack appendFormat:@"%s/", resultstack[i]];
259 [mystack appendFormat:@"%s", resultstack[i]];
261 [mystack appendFormat:@"<%s>", resultstack[i]];
270 switch (stackresult) {
273 [mystack appendString:@" (In tag)"];
277 [mystack appendString:@" (In comment section)"];
280 [mystack appendString:@" (In CDATA section)"];
284 [mystack appendString:@" (No open tags)"];
288 [mystack appendString:@" (Tags are balanced)"];
292 [self setValue:mystack forKey:@"tagStack"];
296 - (void)complete:(id)sender {
298 NSRange selectedRange, tagNameRange;
299 NSString *tagName = nil;
302 // data = [self string];
304 selectedRange = [self selectedRange];
305 location = selectedRange.location;
307 /* May not have a selection, and insertion point must be preceded by at
308 * least one tag, which means at least 3 characters must be to its left
310 if (selectedRange.length > 0 || location < 3 || stackresult) {
316 for (i = 0; resultstack && *(resultstack[i]); i++) {
317 if (!*(resultstack[i+1])) {
318 tagName = [NSString stringWithCString:resultstack[i]];
319 tagNameRange = NSMakeRange(tagpositions[i*2], tagpositions[i*2+1]- tagpositions[i*2]);
324 [self insertText:[NSString stringWithFormat:@"</%@>", tagName]];
325 [self setSelectedRange:selectedRange];
327 [self flashRange:tagNameRange];
328 [self calculateTagStack];