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
{
29 BSSourceViewTextView
* _textView
;
30 BSLineNumberRulerView
* _ruler
;
31 NSScrollView
* _scrollView
;
33 // Line numbers to mark.
34 NSSet
<NSNumber
*>* _markers
;
39 - (id)initWithFrame
:(NSRect
)frame
41 if (self = [super initWithFrame
:frame
]) {
47 - (void)setMarkers
:(NSSet
*)markers
{
48 _markers
= [markers copy
];
49 [_ruler setNeedsDisplay
:YES
];
52 - (void)setMarkedLine
:(NSUInteger
)markedLine
{
53 _markedLine
= markedLine
;
54 [_ruler setNeedsDisplay
:YES
];
55 [_textView setNeedsDisplay
:YES
];
59 * Reads the contents of file at |f| and sets the source viewer and filename
62 - (void)setFile
:(NSString
*)f
66 if (![[NSFileManager defaultManager
] fileExistsAtPath
:f
]) {
67 [_textView setString
:@
""];
71 [self setSource
:f completionHandler
:nil];
75 * Sets the contents of the SourceView to |source| representing the file at |path|.
77 - (void)setString
:(NSString
*)source asFile
:(NSString
*)path
81 // Write the source out as a temporary file so it can be highlighted.
83 NSString
* tmpPath
= [NSTemporaryDirectory() stringByAppendingPathComponent
:@
"MacGDBpHighlighter"];
84 [source writeToFile
:tmpPath atomically
:NO encoding
:NSUTF8StringEncoding error
:&error
];
86 [_textView setString
:source
];
90 [self setSource
:tmpPath completionHandler
:^
{
91 [[NSFileManager defaultManager
] removeItemAtPath
:tmpPath error
:NULL
];
96 * Flip the coordinates
104 * Tells the text view to scroll to a certain line
106 - (void)scrollToLine
:(NSUInteger
)line
108 if ([[_textView textStorage
] length
] == 0)
111 // go through the document until we find the NSRange for the line we want
112 NSUInteger rangeIndex
= 0;
113 for (NSUInteger i
= 0; i
< line
; i
++) {
114 rangeIndex
= NSMaxRange([[_textView string
] lineRangeForRange
:NSMakeRange(rangeIndex
, 0)]);
117 // now get the true start/end markers for it
118 NSUInteger lineStart
, lineEnd
;
119 [[_textView string
] getLineStart
:&lineStart
122 forRange
:NSMakeRange(rangeIndex
- 1, 0)];
123 [_textView scrollRangeToVisible
:[[_textView string
]
124 lineRangeForRange
:NSMakeRange(lineStart
, lineEnd
- lineStart
)]];
128 * Returns the preferred font for source views.
130 + (NSFont
*)sourceFont
132 static NSFont
* font
= nil;
134 font
= [NSFont fontWithName
:@
"Menlo" size
:12];
136 font
= [NSFont fontWithName
:@
"Monaco" size
:12.0];
142 * Setup all the subviews for the source metaview
146 // Create the scroll view.
147 _scrollView
= [[NSScrollView alloc
] initWithFrame
:[self bounds
]];
148 [_scrollView setHasHorizontalScroller
:YES
];
149 [_scrollView setHasVerticalScroller
:YES
];
150 [_scrollView setAutohidesScrollers
:YES
];
151 [_scrollView setBorderType
:NSBezelBorder
];
152 [_scrollView setAutoresizingMask
:(NSViewWidthSizable | NSViewHeightSizable
)];
153 [[_scrollView contentView
] setAutoresizesSubviews
:YES
];
154 [self addSubview
:_scrollView
];
156 // add the text view to the scroll view
158 textFrame.origin
= NSMakePoint(0.0, 0.0);
159 textFrame.size
= [_scrollView contentSize
];
160 _textView
= [[BSSourceViewTextView alloc
] initWithFrame
:textFrame
];
161 [_textView setSourceView
:self];
162 [_textView setEditable
:NO
];
163 [_textView setFont
:[[self class] sourceFont
]];
164 [_textView setHorizontallyResizable
:YES
];
165 [_textView setVerticallyResizable
:YES
];
166 [_textView setMinSize
:textFrame.size
];
167 [_textView setMaxSize
:NSMakeSize(FLT_MAX
, FLT_MAX
)];
168 [[_textView textContainer
] setContainerSize
:NSMakeSize(FLT_MAX
, FLT_MAX
)];
169 [[_textView textContainer
] setWidthTracksTextView
:NO
];
170 [[_textView textContainer
] setHeightTracksTextView
:NO
];
171 [_textView setAutoresizingMask
:NSViewNotSizable
];
172 [_scrollView setDocumentView
:_textView
];
175 _ruler
= [[BSLineNumberRulerView alloc
] initWithSourceView
:self];
176 [_scrollView setVerticalRulerView
:_ruler
];
177 [_scrollView setHasHorizontalRuler
:NO
];
178 [_scrollView setHasVerticalRuler
:YES
];
179 [_scrollView setRulersVisible
:YES
];
181 [self registerForDraggedTypes
:@
[ NSFilenamesPboardType
]];
184 NSString
* ColorHEXStringINIDirective(NSString
* directive
, NSColor
* color
) {
185 CGFloat red
, green
, blue
, alpha
;
186 color
= [color colorUsingColorSpaceName
:NSCalibratedRGBColorSpace
];
187 [color getRed
:&red green
:&green blue
:&blue alpha
:&alpha
];
188 return [NSString stringWithFormat
:@
"%@=\"#%02x%02x%02x\"", directive
, (UInt8
)(red
* 255), (UInt8
)(green
* 255), (UInt8
)(blue
* 255)];
192 * Reads the contents of |filePath| and sets it as the displayed text, after
193 * attempting to highlight it using the PHP binary.
195 - (void)setSource
:(NSString
*)filePath completionHandler
:(void(^
)(void))handler
198 // Attempt to use the PHP CLI to highlight the source file as HTML
199 NSPipe
* outPipe
= [NSPipe pipe
];
200 NSPipe
* errPipe
= [NSPipe pipe
];
201 NSTask
* task
= [[NSTask alloc
] init
];
203 [task setLaunchPath
:@
"/usr/bin/php"]; // This is the path to the default Leopard PHP executable
204 [task setArguments
:@
[
205 @
"--syntax-highlight",
206 @
"--define", ColorHEXStringINIDirective(@
"highlight.string", [NSColor systemRedColor
]),
207 @
"--define", ColorHEXStringINIDirective(@
"highlight.comment", [NSColor systemOrangeColor
]),
208 @
"--define", ColorHEXStringINIDirective(@
"highlight.default", [NSColor systemBlueColor
]),
209 @
"--define", ColorHEXStringINIDirective(@
"highlight.html", [NSColor systemGrayColor
]),
212 [task setStandardOutput
:outPipe
];
213 [task setStandardError
:errPipe
];
214 [task setTerminationHandler
:^
(NSTask
* taskBlock
) {
215 if (task.terminationStatus
!= 0) {
216 NSLog(@
"Failed to highlight PHP file %@. Termination status=%d. stderr: %@",
217 filePath
, taskBlock.terminationStatus
, [[errPipe fileHandleForReading
] readDataToEndOfFile
]);
222 // Start reading the stdout pipe immediately. This is separate from the
223 // terminiationHandler, since a large file could be greater than
225 NSMutableAttributedString
* source
;
226 NSData
* data
= [[outPipe fileHandleForReading
] readDataToEndOfFile
];
228 source
= [[NSMutableAttributedString alloc
] initWithHTML
:data
229 options
:@
{ NSCharacterEncodingDocumentAttribute
: @
(NSUTF8StringEncoding
) }
230 documentAttributes
:nil];
232 // PHP uses in the highlighted output, which should be converted
233 // back to normal spaces.
234 NSMutableString
* stringData
= [source mutableString
];
235 [stringData replaceOccurrencesOfString
:@
"\u00A0" withString
:@
" " options
:0 range
:NSMakeRange(0, stringData.length
)];
237 // Override the default font from Courier.
238 [source addAttributes
:@
{ NSFontAttributeName
: [[self class] sourceFont
] }
239 range
:NSMakeRange(0, source.length
)];
243 [[_textView textStorage
] setAttributedString
:source
];
245 [self setPlainTextStringFromFile
:filePath
];
248 [_ruler performLayout
];
253 } @catch (NSException
* exception
) {
254 // If the PHP executable is not available then the NSTask will throw an exception
255 NSLog(@
"Failed to highlight file: %@", exception
);
256 [self setPlainTextStringFromFile
:filePath
];
261 * Gets the plain-text representation of the file at |filePath| and sets the
262 * contents in the source view.
264 - (void)setPlainTextStringFromFile
:(NSString
*)filePath
266 NSError
* error
= nil;
267 NSString
* contents
= [NSString stringWithContentsOfFile
:filePath
268 encoding
:NSUTF8StringEncoding
271 NSLog(@
"Error reading file at %@: %@", filePath
, error
);
272 if ([_delegate respondsToSelector
:@selector(error
:whileHighlightingFile
:)]) {
273 [_delegate error
:error whileHighlightingFile
:filePath
];
277 [_textView setString
:contents
];
280 // Drag Handlers ///////////////////////////////////////////////////////////////
283 * Validates an initiated drag operation.
285 - (NSDragOperation
)draggingEntered
:(id<NSDraggingInfo
>)sender
287 return NSDragOperationCopy
;
291 * Performs a dragging operation of files to set the contents of the file.
293 - (BOOL)performDragOperation
:(id<NSDraggingInfo
>)sender
295 NSPasteboard
* pboard
= [sender draggingPasteboard
];
296 if ([[pboard types
] containsObject
:NSFilenamesPboardType
]) {
297 NSArray
* files
= [pboard propertyListForType
:NSFilenamesPboardType
];
299 NSString
* filename
= [files objectAtIndex
:0];
300 [self setFile
:filename
];