3 * Copyright (c) 2007 - 2011, Blue Static <http://www.bluestatic.org>
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 #import "BSSourceView.h"
19 #import "BSLineNumberRulerView.h"
20 #import "BSSourceViewTextView.h"
22 @interface BSSourceView (Private)
24 - (void)errorHighlightingFile:(NSNotification*)notif;
25 - (void)setPlainTextStringFromFile:(NSString*)filePath;
28 @implementation BSSourceView
30 @synthesize textView = textView_;
31 @synthesize scrollView = scrollView_;
32 @synthesize markers = markers_;
33 @synthesize markedLine = markedLine_;
34 @synthesize delegate = delegate_;
35 @synthesize file = file_;
38 * Initializes the source view with the path of a file
40 - (id)initWithFrame:(NSRect)frame
42 if (self = [super initWithFrame:frame]) {
44 [[NSNotificationCenter defaultCenter]
46 selector:@selector(errorHighlightingFile:)
47 name:NSFileHandleReadToEndOfFileCompletionNotification
61 [scrollView_ removeFromSuperview];
62 [textView_ removeFromSuperview];
67 - (void)setMarkers:(NSSet*)markers {
69 markers_ = [markers copy];
71 [ruler_ setNeedsDisplay:YES];
74 - (void)setMarkedLine:(NSUInteger)markedLine {
75 markedLine_ = markedLine;
76 [ruler_ setNeedsDisplay:YES];
80 * Sets the file name as well as the text of the source view
82 - (void)setFile:(NSString*)f
89 if (![[NSFileManager defaultManager] fileExistsAtPath:f]) {
90 [textView_ setString:@""];
95 // Attempt to use the PHP CLI to highlight the source file as HTML
96 NSPipe* outPipe = [NSPipe pipe];
97 NSPipe* errPipe = [NSPipe pipe];
98 NSTask* task = [[NSTask new] autorelease];
100 [task setLaunchPath:@"/usr/bin/php"]; // This is the path to the default Leopard PHP executable
101 [task setArguments:[NSArray arrayWithObjects:@"-s", f, nil]];
102 [task setStandardOutput:outPipe];
103 [task setStandardError:errPipe];
106 [[errPipe fileHandleForReading] readToEndOfFileInBackgroundAndNotify];
108 NSData* data = [[outPipe fileHandleForReading] readDataToEndOfFile];
109 NSAttributedString* source = [[NSAttributedString alloc] initWithHTML:data documentAttributes:NULL];
110 [[textView_ textStorage] setAttributedString:source];
112 } @catch (NSException* exception) {
113 // If the PHP executable is not available then the NSTask will throw an exception
114 [self setPlainTextStringFromFile:f];
117 [ruler_ performLayout];
121 * Sets the contents of the SourceView via a string rather than loading from a path
123 - (void)setString:(NSString*)source asFile:(NSString*)path
125 // create the temp file
126 NSError* error = nil;
127 NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"MacGDBpHighlighter"];
128 [source writeToFile:tmpPath atomically:NO encoding:NSUTF8StringEncoding error:&error];
130 [textView_ setString:source];
134 // highlight the temporary file
135 [self setFile:tmpPath];
137 // delete the temp file
138 [[NSFileManager defaultManager] removeItemAtPath:tmpPath error:NULL];
140 // plop in our fake path so nobody knows the difference
146 [ruler_ performLayout];
150 * If an error occurs in reading the highlighted PHP source, this will merely set the string
152 - (void)errorHighlightingFile:(NSNotification*)notif
154 NSData* data = [[notif userInfo] objectForKey:NSFileHandleNotificationDataItem];
155 if ([data length] > 0 && file_) // there's something on stderr, so the PHP CLI failed
156 [self setPlainTextStringFromFile:file_];
160 * Flip the coordinates
168 * Tells the text view to scroll to a certain line
170 - (void)scrollToLine:(NSUInteger)line
172 if ([[textView_ textStorage] length] == 0)
175 // go through the document until we find the NSRange for the line we want
176 NSUInteger rangeIndex = 0;
177 for (NSUInteger i = 0; i < line; i++) {
178 rangeIndex = NSMaxRange([[textView_ string] lineRangeForRange:NSMakeRange(rangeIndex, 0)]);
181 // now get the true start/end markers for it
182 NSUInteger lineStart, lineEnd;
183 [[textView_ string] getLineStart:&lineStart
186 forRange:NSMakeRange(rangeIndex - 1, 0)];
187 [textView_ scrollRangeToVisible:[[textView_ string]
188 lineRangeForRange:NSMakeRange(lineStart, lineEnd - lineStart)]];
189 [scrollView_ setNeedsDisplay:YES];
193 * Setup all the subviews for the source metaview
197 // Create the scroll view.
198 scrollView_ = [[[NSScrollView alloc] initWithFrame:[self bounds]] autorelease];
199 [scrollView_ setHasHorizontalScroller:YES];
200 [scrollView_ setHasVerticalScroller:YES];
201 [scrollView_ setAutohidesScrollers:YES];
202 [scrollView_ setBorderType:NSBezelBorder];
203 [scrollView_ setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
204 [[scrollView_ contentView] setAutoresizesSubviews:YES];
205 [self addSubview:scrollView_];
207 // add the text view to the scroll view
209 textFrame.origin = NSMakePoint(0.0, 0.0);
210 textFrame.size = [scrollView_ contentSize];
211 textView_ = [[[BSSourceViewTextView alloc] initWithFrame:textFrame] autorelease];
212 [textView_ setSourceView:self];
213 [textView_ setEditable:NO];
214 [textView_ setFont:[NSFont fontWithName:@"Monaco" size:10.0]];
215 [textView_ setHorizontallyResizable:YES];
216 [textView_ setVerticallyResizable:YES];
217 [textView_ setMinSize:textFrame.size];
218 [textView_ setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
219 [[textView_ textContainer] setContainerSize:NSMakeSize(FLT_MAX, FLT_MAX)];
220 [[textView_ textContainer] setWidthTracksTextView:NO];
221 [[textView_ textContainer] setHeightTracksTextView:NO];
222 [textView_ setAutoresizingMask:NSViewNotSizable];
223 [scrollView_ setDocumentView:textView_];
226 ruler_ = [[[BSLineNumberRulerView alloc] initWithSourceView:self] autorelease];
227 [scrollView_ setVerticalRulerView:ruler_];
228 [scrollView_ setHasHorizontalRuler:NO];
229 [scrollView_ setHasVerticalRuler:YES];
230 [scrollView_ setRulersVisible:YES];
232 NSArray* types = [NSArray arrayWithObject:NSFilenamesPboardType];
233 [self registerForDraggedTypes:types];
237 * Gets the plain-text representation of the file at |filePath| and sets the
238 * contents in the source view.
240 - (void)setPlainTextStringFromFile:(NSString*)filePath
242 NSError* error = nil;
243 NSString* contents = [NSString stringWithContentsOfFile:filePath
244 encoding:NSUTF8StringEncoding
247 NSLog(@"Error reading file at %@: %@", filePath, error);
250 [textView_ setString:contents];
253 // Drag Handlers ///////////////////////////////////////////////////////////////
256 * Validates an initiated drag operation.
258 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
260 if ([delegate_ respondsToSelector:@selector(sourceView:acceptsDropOfFile:)])
261 return NSDragOperationCopy;
262 return NSDragOperationNone;
266 * Performs a dragging operation of files to set the contents of the file.
268 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
270 NSPasteboard* pboard = [sender draggingPasteboard];
271 if ([[pboard types] containsObject:NSFilenamesPboardType]) {
272 NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
274 NSString* filename = [files objectAtIndex:0];
275 if ([delegate_ respondsToSelector:@selector(sourceView:acceptsDropOfFile:)] &&
276 [delegate_ sourceView:self acceptsDropOfFile:filename]) {
277 [self setFile:filename];