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
54 - (void)setMarkers:(NSSet*)markers {
55 markers_ = [markers copy];
56 [ruler_ setNeedsDisplay:YES];
59 - (void)setMarkedLine:(NSUInteger)markedLine {
60 markedLine_ = markedLine;
61 [ruler_ setNeedsDisplay:YES];
65 * Reads the contents of file at |f| and sets the source viewer and filename
68 - (void)setFile:(NSString*)f
72 if (![[NSFileManager defaultManager] fileExistsAtPath:f]) {
73 [textView_ setString:@""];
77 [self setSource:f completionHandler:nil];
81 * Sets the contents of the SourceView to |source| representing the file at |path|.
83 - (void)setString:(NSString*)source asFile:(NSString*)path
87 // Write the source out as a temporary file so it can be highlighted.
89 NSString* tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"MacGDBpHighlighter"];
90 [source writeToFile:tmpPath atomically:NO encoding:NSUTF8StringEncoding error:&error];
92 [textView_ setString:source];
96 [self setSource:tmpPath completionHandler:^{
97 [[NSFileManager defaultManager] removeItemAtPath:tmpPath error:NULL];
102 * Flip the coordinates
110 * Tells the text view to scroll to a certain line
112 - (void)scrollToLine:(NSUInteger)line
114 if ([[textView_ textStorage] length] == 0)
117 // go through the document until we find the NSRange for the line we want
118 NSUInteger rangeIndex = 0;
119 for (NSUInteger i = 0; i < line; i++) {
120 rangeIndex = NSMaxRange([[textView_ string] lineRangeForRange:NSMakeRange(rangeIndex, 0)]);
123 // now get the true start/end markers for it
124 NSUInteger lineStart, lineEnd;
125 [[textView_ string] getLineStart:&lineStart
128 forRange:NSMakeRange(rangeIndex - 1, 0)];
129 [textView_ scrollRangeToVisible:[[textView_ string]
130 lineRangeForRange:NSMakeRange(lineStart, lineEnd - lineStart)]];
131 [scrollView_ setNeedsDisplay:YES];
135 * Setup all the subviews for the source metaview
139 // Create the scroll view.
140 scrollView_ = [[NSScrollView alloc] initWithFrame:[self bounds]];
141 [scrollView_ setHasHorizontalScroller:YES];
142 [scrollView_ setHasVerticalScroller:YES];
143 [scrollView_ setAutohidesScrollers:YES];
144 [scrollView_ setBorderType:NSBezelBorder];
145 [scrollView_ setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
146 [[scrollView_ contentView] setAutoresizesSubviews:YES];
147 [self addSubview:scrollView_];
149 // add the text view to the scroll view
151 textFrame.origin = NSMakePoint(0.0, 0.0);
152 textFrame.size = [scrollView_ contentSize];
153 textView_ = [[BSSourceViewTextView alloc] initWithFrame:textFrame];
154 [textView_ setSourceView:self];
155 [textView_ setEditable:NO];
156 [textView_ setFont:[NSFont fontWithName:@"Monaco" size:10.0]];
157 [textView_ setHorizontallyResizable:YES];
158 [textView_ setVerticallyResizable:YES];
159 [textView_ setMinSize:textFrame.size];
160 [textView_ setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
161 [[textView_ textContainer] setContainerSize:NSMakeSize(FLT_MAX, FLT_MAX)];
162 [[textView_ textContainer] setWidthTracksTextView:NO];
163 [[textView_ textContainer] setHeightTracksTextView:NO];
164 [textView_ setAutoresizingMask:NSViewNotSizable];
165 [scrollView_ setDocumentView:textView_];
168 ruler_ = [[BSLineNumberRulerView alloc] initWithSourceView:self];
169 [scrollView_ setVerticalRulerView:ruler_];
170 [scrollView_ setHasHorizontalRuler:NO];
171 [scrollView_ setHasVerticalRuler:YES];
172 [scrollView_ setRulersVisible:YES];
174 NSArray* types = [NSArray arrayWithObject:NSFilenamesPboardType];
175 [self registerForDraggedTypes:types];
179 * Reads the contents of |filePath| and sets it as the displayed text, after
180 * attempting to highlight it using the PHP binary.
182 - (void)setSource:(NSString*)filePath completionHandler:(void(^)(void))handler
185 // Attempt to use the PHP CLI to highlight the source file as HTML
186 NSPipe* outPipe = [NSPipe pipe];
187 NSPipe* errPipe = [NSPipe pipe];
188 NSTask* task = [[NSTask alloc] init];
190 [task setLaunchPath:@"/usr/bin/php"]; // This is the path to the default Leopard PHP executable
191 [task setArguments:@[ @"-s", filePath ]];
192 [task setStandardOutput:outPipe];
193 [task setStandardError:errPipe];
194 [task setTerminationHandler:^(NSTask*) {
195 NSMutableAttributedString* source;
197 if (task.terminationStatus == 0) {
198 NSData* data = [[outPipe fileHandleForReading] readDataToEndOfFile];
200 [[NSMutableAttributedString alloc] initWithHTML:data
201 options:@{ NSCharacterEncodingDocumentAttribute : @(NSUTF8StringEncoding) }
202 documentAttributes:nil];
204 // PHP uses in the highlighted output, which should be converted
205 // back to normal spaces.
206 NSMutableString* stringData = [source mutableString];
207 [stringData replaceOccurrencesOfString:@"\u00A0" withString:@" " options:0 range:NSMakeRange(0, stringData.length)];
209 // Override the default font from Courier.
210 NSFont* menloFont = [NSFont fontWithName:@"Menlo" size:12];
212 [source addAttributes:@{ NSFontAttributeName : menloFont }
213 range:NSMakeRange(0, source.length)];
216 NSLog(@"Failed to highlight PHP file %@: %@", filePath, [[errPipe fileHandleForReading] readDataToEndOfFile]);
219 dispatch_async(dispatch_get_main_queue(), ^{
221 [[self->textView_ textStorage] setAttributedString:source];
223 [self setPlainTextStringFromFile:filePath];
226 [self->ruler_ performLayout];
233 } @catch (NSException* exception) {
234 // If the PHP executable is not available then the NSTask will throw an exception
235 [self setPlainTextStringFromFile:filePath];
240 * Gets the plain-text representation of the file at |filePath| and sets the
241 * contents in the source view.
243 - (void)setPlainTextStringFromFile:(NSString*)filePath
245 NSError* error = nil;
246 NSString* contents = [NSString stringWithContentsOfFile:filePath
247 encoding:NSUTF8StringEncoding
250 NSLog(@"Error reading file at %@: %@", filePath, error);
253 [textView_ setString:contents];
256 // Drag Handlers ///////////////////////////////////////////////////////////////
259 * Validates an initiated drag operation.
261 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender
263 return NSDragOperationCopy;
267 * Performs a dragging operation of files to set the contents of the file.
269 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
271 NSPasteboard* pboard = [sender draggingPasteboard];
272 if ([[pboard types] containsObject:NSFilenamesPboardType]) {
273 NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
275 NSString* filename = [files objectAtIndex:0];
276 [self setFile:filename];