When writing the temporary file to use for highlighting, use NSUTF8StringEncoding...
[macgdbp.git] / Source / BSSourceView.m
1 /*
2 * MacGDBp
3 * Copyright (c) 2007 - 2009, Blue Static <http://www.bluestatic.org>
4 *
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.
8 *
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.
12 *
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
15 */
16
17 #import "BSSourceView.h"
18
19 @interface BSSourceView (Private)
20 - (void)setupViews;
21 - (void)errorHighlightingFile:(NSNotification *)notif;
22 @end
23
24 @implementation BSSourceView
25
26 @synthesize numberView, textView, scrollView, markedLine, delegate, file;
27
28 /**
29 * Initializes the source view with the path of a file
30 */
31 - (id)initWithFrame:(NSRect)frame
32 {
33 if (self = [super initWithFrame:frame])
34 {
35 [self setupViews];
36 [[NSNotificationCenter defaultCenter]
37 addObserver:self
38 selector:@selector(errorHighlightingFile:)
39 name:NSFileHandleReadToEndOfFileCompletionNotification
40 object:nil
41 ];
42 }
43 return self;
44 }
45
46 /**
47 * Dealloc
48 */
49 - (void)dealloc
50 {
51 [file release];
52
53 [numberView removeFromSuperview];
54 [scrollView removeFromSuperview];
55 [textView removeFromSuperview];
56
57 [super dealloc];
58 }
59
60 /**
61 * Sets the file name as well as the text of the source view
62 */
63 - (void)setFile:(NSString *)f
64 {
65 if (file != f)
66 {
67 [file release];
68 file = [f retain];
69 }
70
71 if (![[NSFileManager defaultManager] fileExistsAtPath:f])
72 {
73 [textView setString:@""];
74 return;
75 }
76
77 @try
78 {
79 // Attempt to use the PHP CLI to highlight the source file as HTML
80 NSPipe *outPipe = [NSPipe pipe];
81 NSPipe *errPipe = [NSPipe pipe];
82 NSTask *task = [[NSTask new] autorelease];
83
84 [task setLaunchPath:@"/usr/bin/php"]; // This is the path to the default Leopard PHP executable
85 [task setArguments:[NSArray arrayWithObjects:@"-s", f, nil]];
86 [task setStandardOutput:outPipe];
87 [task setStandardError:errPipe];
88 [task launch];
89
90 [[errPipe fileHandleForReading] readToEndOfFileInBackgroundAndNotify];
91
92 NSData *data = [[outPipe fileHandleForReading] readDataToEndOfFile];
93 NSAttributedString *source = [[NSAttributedString alloc] initWithHTML:data documentAttributes:NULL];
94 [[textView textStorage] setAttributedString:source];
95 [source release];
96 }
97 @catch (NSException *exception)
98 {
99 // If the PHP executable is not available then the NSTask will throw an exception
100 [textView setString:[NSString stringWithContentsOfFile:f]];
101 }
102 }
103
104 /**
105 * Sets the contents of the SourceView via a string rather than loading from a path
106 */
107 - (void)setString:(NSString *)source asFile:(NSString *)path
108 {
109 // create the temp file
110 NSError *error = nil;
111 NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"MacGDBpHighlighter"];
112 [source writeToFile:tmpPath atomically:NO encoding:NSUTF8StringEncoding error:&error];
113 if (error)
114 {
115 [textView setString:source];
116 return;
117 }
118
119 // highlight the temporary file
120 [self setFile:tmpPath];
121
122 // delete the temp file
123 [[NSFileManager defaultManager] removeItemAtPath:tmpPath error:NULL];
124
125 // plop in our fake path so nobody knows the difference
126 if (path != file)
127 {
128 [file release];
129 file = [path copy];
130 }
131 }
132
133 /**
134 * If an error occurs in reading the highlighted PHP source, this will merely set the string
135 */
136 - (void)errorHighlightingFile:(NSNotification *)notif
137 {
138 NSData *data = [[notif userInfo] objectForKey:NSFileHandleNotificationDataItem];
139 if ([data length] > 0) // there's something on stderr, so the PHP CLI failed
140 [textView setString:[NSString stringWithContentsOfFile:file]];
141 }
142
143 /**
144 * Flip the coordinates
145 */
146 - (BOOL)isFlipped
147 {
148 return YES;
149 }
150
151 /**
152 * Tells the text view to scroll to a certain line
153 */
154 - (void)scrollToLine:(int)line
155 {
156 if ([[textView textStorage] length] == 0)
157 return;
158
159 // go through the document until we find the NSRange for the line we want
160 int rangeIndex = 0;
161 for (int i = 0; i < line; i++)
162 {
163 rangeIndex = NSMaxRange([[textView string] lineRangeForRange:NSMakeRange(rangeIndex, 0)]);
164 }
165
166 // now get the true start/end markers for it
167 unsigned lineStart, lineEnd;
168 [[textView string] getLineStart:&lineStart end:NULL contentsEnd:&lineEnd forRange:NSMakeRange(rangeIndex - 1, 0)];
169 [textView scrollRangeToVisible:[[textView string] lineRangeForRange:NSMakeRange(lineStart, lineEnd - lineStart)]];
170 }
171
172 /**
173 * Setup all the subviews for the source metaview
174 */
175 - (void)setupViews
176 {
177 int gutterWidth = 30;
178
179 // setup the line number view
180 NSRect numberFrame = [self bounds];
181 numberFrame.origin = NSMakePoint(0.0, 0.0);
182 numberFrame.size.width = gutterWidth;
183 numberView = [[BSLineNumberView alloc] initWithFrame:numberFrame];
184 [numberView setAutoresizingMask:NSViewHeightSizable];
185 [numberView setSourceView:self];
186 [self addSubview:numberView];
187
188 // create the scroll view
189 NSRect scrollFrame = [self bounds];
190 scrollFrame.origin.x = gutterWidth;
191 scrollFrame.size.width = scrollFrame.size.width - gutterWidth;
192 scrollView = [[NSScrollView alloc] initWithFrame:scrollFrame];
193 [scrollView setHasHorizontalScroller:YES];
194 [scrollView setHasVerticalScroller:YES];
195 [scrollView setAutohidesScrollers:YES];
196 [scrollView setBorderType:NSBezelBorder];
197 [scrollView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
198 [[scrollView contentView] setAutoresizesSubviews:YES];
199 [self addSubview:scrollView];
200
201 // add the text view to the scroll view
202 NSRect textFrame;
203 textFrame.origin = NSMakePoint(0.0, 0.0);
204 textFrame.size = [scrollView contentSize];
205 textView = [[BSSourceViewTextView alloc] initWithFrame:textFrame];
206 [textView setSourceView:self];
207 [textView setEditable:NO];
208 [textView setFont:[NSFont fontWithName:@"Monaco" size:10.0]];
209 [textView setHorizontallyResizable:YES];
210 [textView setVerticallyResizable:YES];
211 [textView setMinSize:textFrame.size];
212 [textView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
213 [[textView textContainer] setContainerSize:NSMakeSize(FLT_MAX, FLT_MAX)];
214 [[textView textContainer] setWidthTracksTextView:NO];
215 [[textView textContainer] setHeightTracksTextView:NO];
216 [textView setAutoresizingMask:NSViewNotSizable];
217 [scrollView setDocumentView:textView];
218 }
219
220 @end