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];
42 -(void)selectLineByNumber:(int)line {
44 NSString *data = [self string];
45 unsigned int i, startIndex, lineEndIndex;
56 for (i = 1; i <= line; i++) {
58 [data getLineStart:&startIndex end:&lineEndIndex contentsEnd:nil forRange:aRange];
59 aRange.location = lineEndIndex;
63 aRange.location = startIndex;
64 aRange.length = lineEndIndex - startIndex;
66 [self setSelectedRange:aRange];
67 [self scrollRangeToVisible:aRange];
76 - (void)keyDown:(NSEvent *)event {
78 NSRange selectedRange;
80 if (![defaults boolForKey:@"enableSyntaxAnalysis"]) {
81 [super keyDown:event];
85 if ([[event characters] isEqual:@"\033"]) {
87 if ([event modifierFlags] || [event isARepeat]) {
93 } else if ([[event characters] isEqual:@"/"]) {
95 selectedRange = [self selectedRange];
96 if (selectedRange.location < 2 || selectedRange.length > 0) {
97 [super keyDown:event];
101 if ([[[self string] substringWithRange:NSMakeRange(selectedRange.location - 1, 1)] isEqual:@"<"]) {
102 if([self completeAfterSlash]) {
110 if ([event modifierFlags] & NSFunctionKeyMask) {
111 [self didChangeText];
118 [super keyDown:event];
124 - (BOOL)completeAfterSlash {
126 int location = [self selectedRange].location - 1;
128 [self calculateTagStackAtLocation:location];
134 NSTextStorage *storage = [self textStorage];
135 [storage beginEditing];
136 [storage deleteCharactersInRange:NSMakeRange(location, 1)];
137 [storage endEditing];
139 [self setSelectedRange:NSMakeRange(location, 0)];
149 - (void)flashRange:(NSRange)range {
153 tagNameRect = [self firstRectForCharacterRange:range];
155 tagNameRect.origin = [[self window] convertScreenToBase:tagNameRect.origin];
157 tagNameRect = [self convertRect:tagNameRect fromView:[[self window] contentView]];
161 [[[NSColor selectedControlColor] colorWithAlphaComponent:0.75] set];
163 NSFrameRectWithWidth(tagNameRect, 2);
165 [NSBezierPath fillRect:tagNameRect];
167 [[self window] flushWindow];
171 [self setNeedsDisplay:YES];
176 -(NSString *)calculateTagStack {
178 return [self calculateTagStackAtLocation:[self selectedRange].location];
182 -(BOOL)checkWellFormed {
185 NSData *data = [XMLUtils getDataWithEncodingFromString:[self string]];
187 parser = XML_ParserCreate(NULL);
190 NSLog(@"Unable to allocate expat parser in XMLTextView:checkWellFormed!");
194 result = XML_Parse(parser, [data bytes], [data length], 1);
197 [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)];
202 XML_ParserFree(parser);
211 errorLine = errorColumn = 0;
213 [errorString release];
223 -(void)setError:(NSString *)errstring atLine:(int)line atColumn:(int)column {
226 errorColumn = column;
229 [errorString release];
230 errorString = errstring;
240 -(NSString *)calculateTagStackAtLocation:(int)location {
244 NSRange selectedRange;
245 NSMutableString *mystack;
247 if (![[NSUserDefaults standardUserDefaults] boolForKey:@"enableSyntaxAnalysis"]) {
251 buffer = [[self string] lossyCString];
252 selectedRange = [self selectedRange];
258 resultstack = findCompletion(buffer, strlen(buffer), location, &stackresult, tagpositions);
260 mystack = [NSMutableString stringWithCapacity:1000];
262 for (i = 0; resultstack && *(resultstack[i]); i++) {
263 if (*(resultstack[i+1])) {
264 [mystack appendFormat:@"%s/", resultstack[i]];
267 [mystack appendFormat:@"%s", resultstack[i]];
269 [mystack appendFormat:@"<%s>", resultstack[i]];
278 switch (stackresult) {
281 [mystack appendString:@" (In tag)"];
285 [mystack appendString:@" (In comment section)"];
288 [mystack appendString:@" (In CDATA section)"];
292 [mystack appendString:@" (No open tags)"];
296 [mystack appendString:@" (Tags are balanced)"];
305 - (void)complete:(id)sender {
307 NSRange selectedRange, tagNameRange;
308 NSString *tagName = nil;
311 // data = [self string];
313 selectedRange = [self selectedRange];
314 location = selectedRange.location;
316 /* May not have a selection, and insertion point must be preceded by at
317 * least one tag, which means at least 3 characters must be to its left
319 if (selectedRange.length > 0 || location < 3 || stackresult) {
325 for (i = 0; resultstack && *(resultstack[i]); i++) {
326 if (!*(resultstack[i+1])) {
327 tagName = [NSString stringWithCString:resultstack[i]];
328 tagNameRange = NSMakeRange(tagpositions[i*2], tagpositions[i*2+1]- tagpositions[i*2]);
333 [self insertText:[NSString stringWithFormat:@"</%@>", tagName]];
334 [self setSelectedRange:selectedRange];
336 [self flashRange:tagNameRange];
337 [self calculateTagStack];