SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
HexFiend/HFController.m
1 //
2 // HFController.m
3 // HexFiend_2
4 //
5 // Copyright 2007 ridiculous_fish. All rights reserved.
6 //
7
8 #import <HexFiend/HFController.h>
9 #import <HexFiend/HFRepresenter_Internal.h>
10 #import <HexFiend/HFByteArray_Internal.h>
11 #import <HexFiend/HFFullMemoryByteArray.h>
12 #import <HexFiend/HFBTreeByteArray.h>
13 #import <HexFiend/HFFullMemoryByteSlice.h>
14 #import <HexFiend/HFSharedMemoryByteSlice.h>
15 #import <objc/runtime.h>
16 #import <objc/message.h>
17 #import <objc/objc-auto.h>
18
19 /* Used for the anchor range and location */
20 #define NO_SELECTION ULLONG_MAX
21
22 #define VALIDATE_SELECTION() [self _ensureSelectionIsValid]
23
24 #define BENCHMARK_BYTEARRAYS 0
25
26 #define BEGIN_TRANSACTION() NSUInteger token = [self beginPropertyChangeTransaction]
27 #define END_TRANSACTION() [self endPropertyChangeTransaction:token]
28
29 static const CGFloat kScrollMultiplier = (CGFloat)1.5;
30
31 static const CFTimeInterval kPulseDuration = .2;
32
33 static void *KVOContextChangesAreLocked = &KVOContextChangesAreLocked;
34
35 NSString * const HFPrepareForChangeInFileNotification = @"HFPrepareForChangeInFileNotification";
36 NSString * const HFChangeInFileByteArrayKey = @"HFChangeInFileByteArrayKey";
37 NSString * const HFChangeInFileModifiedRangesKey = @"HFChangeInFileModifiedRangesKey";
38 NSString * const HFChangeInFileShouldCancelKey = @"HFChangeInFileShouldCancelKey";
39 NSString * const HFChangeInFileHintKey = @"HFChangeInFileHintKey";
40
41 NSString * const HFControllerDidChangePropertiesNotification = @"HFControllerDidChangePropertiesNotification";
42 NSString * const HFControllerChangedPropertiesKey = @"HFControllerChangedPropertiesKey";
43
44
45 typedef NS_ENUM(NSInteger, HFControllerSelectAction) {
46 eSelectResult,
47 eSelectAfterResult,
48 ePreserveSelection,
49 NUM_SELECTION_ACTIONS
50 };
51
52 @interface HFController (ForwardDeclarations)
53 - (void)_commandInsertByteArrays:(NSArray *)byteArrays inRanges:(NSArray *)ranges withSelectionAction:(HFControllerSelectAction)selectionAction;
54 - (void)_removeUndoManagerNotifications;
55 - (void)_removeAllUndoOperations;
56 - (void)_registerUndoOperationForInsertingByteArrays:(NSArray *)byteArrays inRanges:(NSArray *)ranges withSelectionAction:(HFControllerSelectAction)selectionAction;
57
58 - (void)_updateBytesPerLine;
59 - (void)_updateDisplayedRange;
60 @end
61
62 @interface NSEvent (HFLionStuff)
63 - (CGFloat)scrollingDeltaY;
64 - (BOOL)hasPreciseScrollingDeltas;
65 - (CGFloat)deviceDeltaY;
66 @end
67
68 static inline Class preferredByteArrayClass(void) {
69 return [HFBTreeByteArray class];
70 }
71
72 @implementation HFController
73
74 - (void)_sharedInit {
75 selectedContentsRanges = [[NSMutableArray alloc] initWithObjects:[HFRangeWrapper withRange:HFRangeMake(0, 0)], nil];
76 byteArray = [[preferredByteArrayClass() alloc] init];
77 [byteArray addObserver:self forKeyPath:@"changesAreLocked" options:0 context:KVOContextChangesAreLocked];
78 selectionAnchor = NO_SELECTION;
79 undoOperations = [[NSMutableSet alloc] init];
80 }
81
82 - (instancetype)init {
83 self = [super init];
84 [self _sharedInit];
85 bytesPerLine = 16;
86 bytesPerColumn = 1;
87 _hfflags.editable = YES;
88 _hfflags.antialias = YES;
89 _hfflags.showcallouts = YES;
90 _hfflags.hideNullBytes = NO;
91 _hfflags.selectable = YES;
92 representers = [[NSMutableArray alloc] init];
93 [self setFont:[NSFont fontWithName:HFDEFAULT_FONT size:HFDEFAULT_FONTSIZE]];
94 return self;
95 }
96
97 - (void)dealloc {
98 [representers makeObjectsPerformSelector:@selector(_setController:) withObject:nil];
99 [representers release];
100 [selectedContentsRanges release];
101 [self _removeUndoManagerNotifications];
102 [self _removeAllUndoOperations];
103 [undoOperations release];
104 [undoManager release];
105 [undoCoalescer release];
106 [_font release];
107 [byteArray removeObserver:self forKeyPath:@"changesAreLocked"];
108 [byteArray release];
109 [cachedData release];
110 [additionalPendingTransactions release];
111 [super dealloc];
112 }
113
114 - (void)encodeWithCoder:(NSCoder *)coder {
115 HFASSERT([coder allowsKeyedCoding]);
116 [coder encodeObject:representers forKey:@"HFRepresenters"];
117 [coder encodeInt64:bytesPerLine forKey:@"HFBytesPerLine"];
118 [coder encodeInt64:bytesPerColumn forKey:@"HFBytesPerColumn"];
119 [coder encodeObject:_font forKey:@"HFFont"];
120 [coder encodeDouble:lineHeight forKey:@"HFLineHeight"];
121 [coder encodeBool:_hfflags.antialias forKey:@"HFAntialias"];
122 [coder encodeBool:_hfflags.colorbytes forKey:@"HFColorBytes"];
123 [coder encodeBool:_hfflags.showcallouts forKey:@"HFShowCallouts"];
124 [coder encodeBool:_hfflags.hideNullBytes forKey:@"HFHidesNullBytes"];
125 [coder encodeBool:_hfflags.livereload forKey:@"HFLiveReload"];
126 [coder encodeInt:_hfflags.editMode forKey:@"HFEditMode"];
127 [coder encodeBool:_hfflags.editable forKey:@"HFEditable"];
128 [coder encodeBool:_hfflags.selectable forKey:@"HFSelectable"];
129 }
130
131 - (instancetype)initWithCoder:(NSCoder *)coder {
132 HFASSERT([coder allowsKeyedCoding]);
133 self = [super init];
134 [self _sharedInit];
135 bytesPerLine = (NSUInteger)[coder decodeInt64ForKey:@"HFBytesPerLine"];
136 bytesPerColumn = (NSUInteger)[coder decodeInt64ForKey:@"HFBytesPerColumn"];
137 _font = [[coder decodeObjectForKey:@"HFFont"] retain];
138 lineHeight = (CGFloat)[coder decodeDoubleForKey:@"HFLineHeight"];
139 _hfflags.antialias = [coder decodeBoolForKey:@"HFAntialias"];
140 _hfflags.colorbytes = [coder decodeBoolForKey:@"HFColorBytes"];
141 _hfflags.livereload = [coder decodeBoolForKey:@"HFLiveReload"];
142
143 if ([coder containsValueForKey:@"HFEditMode"])
144 _hfflags.editMode = [coder decodeIntForKey:@"HFEditMode"];
145 else {
146 _hfflags.editMode = ([coder decodeBoolForKey:@"HFOverwriteMode"]
147 ? HFOverwriteMode : HFInsertMode);
148 }
149
150 _hfflags.editable = [coder decodeBoolForKey:@"HFEditable"];
151 _hfflags.selectable = [coder decodeBoolForKey:@"HFSelectable"];
152 _hfflags.hideNullBytes = [coder decodeBoolForKey:@"HFHidesNullBytes"];
153 representers = [[coder decodeObjectForKey:@"HFRepresenters"] retain];
154 return self;
155 }
156
157 - (NSArray *)representers {
158 return [[representers copy] autorelease];
159 }
160
161 - (void)notifyRepresentersOfChanges:(HFControllerPropertyBits)bits {
162 FOREACH(HFRepresenter*, rep, representers) {
163 [rep controllerDidChange:bits];
164 }
165
166 /* Post the HFControllerDidChangePropertiesNotification */
167 NSNumber *number = [[NSNumber alloc] initWithUnsignedInteger:bits];
168 NSDictionary *userInfo = [[NSDictionary alloc] initWithObjects:&number forKeys:(id *)&HFControllerChangedPropertiesKey count:1];
169 [number release];
170 [[NSNotificationCenter defaultCenter] postNotificationName:HFControllerDidChangePropertiesNotification object:self userInfo:userInfo];
171 [userInfo release];
172 }
173
174 - (void)_firePropertyChanges {
175 NSMutableArray *pendingTransactions = additionalPendingTransactions;
176 NSUInteger pendingTransactionCount = [pendingTransactions count];
177 additionalPendingTransactions = nil;
178 HFControllerPropertyBits propertiesToUpdate = propertiesToUpdateInCurrentTransaction;
179 propertiesToUpdateInCurrentTransaction = 0;
180 if (pendingTransactionCount > 0 || propertiesToUpdate != 0) {
181 BEGIN_TRANSACTION();
182 while (pendingTransactionCount--) {
183 HFControllerPropertyBits propertiesInThisTransaction = [pendingTransactions[0] unsignedIntegerValue];
184 [pendingTransactions removeObjectAtIndex:0];
185 HFASSERT(propertiesInThisTransaction != 0);
186 [self notifyRepresentersOfChanges:propertiesInThisTransaction];
187 }
188 [pendingTransactions release];
189 if (propertiesToUpdate) {
190 [self notifyRepresentersOfChanges:propertiesToUpdate];
191 }
192 END_TRANSACTION();
193 }
194 }
195
196 /* Inserts a "fence" so that all prior property change bits will be complete before any new ones */
197 - (void)_insertPropertyChangeFence {
198 if (currentPropertyChangeToken == 0) {
199 HFASSERT(additionalPendingTransactions == nil);
200 /* There can be no prior property changes */
201 HFASSERT(propertiesToUpdateInCurrentTransaction == 0);
202 return;
203 }
204 if (propertiesToUpdateInCurrentTransaction == 0) {
205 /* Nothing to fence */
206 return;
207 }
208 if (additionalPendingTransactions == nil) additionalPendingTransactions = [[NSMutableArray alloc] init];
209 [additionalPendingTransactions addObject:@(propertiesToUpdateInCurrentTransaction)];
210 propertiesToUpdateInCurrentTransaction = 0;
211 }
212
213 - (void)_addPropertyChangeBits:(HFControllerPropertyBits)bits {
214 propertiesToUpdateInCurrentTransaction |= bits;
215 if (currentPropertyChangeToken == 0) {
216 [self _firePropertyChanges];
217 }
218 }
219
220 - (NSUInteger)beginPropertyChangeTransaction {
221 HFASSERT(currentPropertyChangeToken < NSUIntegerMax);
222 return ++currentPropertyChangeToken;
223 }
224
225 - (void)endPropertyChangeTransaction:(NSUInteger)token {
226 if (currentPropertyChangeToken != token) {
227 [NSException raise:NSInvalidArgumentException format:@"endPropertyChangeTransaction passed token %lu, but expected token %lu", (unsigned long)token, (unsigned long)currentPropertyChangeToken];
228 }
229 HFASSERT(currentPropertyChangeToken > 0);
230 if (--currentPropertyChangeToken == 0) [self _firePropertyChanges];
231 }
232
233 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
234 if (context == KVOContextChangesAreLocked) {
235 HFASSERT([keyPath isEqual:@"changesAreLocked"]);
236 [self _addPropertyChangeBits:HFControllerEditable];
237 }
238 else {
239 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
240 }
241 }
242
243 - (void)addRepresenter:(HFRepresenter *)representer {
244 REQUIRE_NOT_NULL(representer);
245 HFASSERT([representers indexOfObjectIdenticalTo:representer] == NSNotFound);
246 HFASSERT([representer controller] == nil);
247 [representer _setController:self];
248 [representers addObject:representer];
249 [representer controllerDidChange: -1];
250 }
251
252 - (void)removeRepresenter:(HFRepresenter *)representer {
253 REQUIRE_NOT_NULL(representer);
254 HFASSERT([representers indexOfObjectIdenticalTo:representer] != NSNotFound);
255 [representers removeObjectIdenticalTo:representer];
256 [representer _setController:nil];
257 }
258
259 - (HFRange)_maximumDisplayedRangeSet {
260 unsigned long long contentsLength = [self contentsLength];
261 HFRange maximumDisplayedRangeSet = HFRangeMake(0, HFRoundUpToNextMultipleSaturate(contentsLength, bytesPerLine));
262 return maximumDisplayedRangeSet;
263 }
264
265 - (unsigned long long)totalLineCount {
266 return HFDivideULLRoundingUp(HFRoundUpToNextMultipleSaturate([self contentsLength] - 1, bytesPerLine), bytesPerLine);
267 }
268
269 - (HFFPRange)displayedLineRange {
270 #if ! NDEBUG
271 HFASSERT(displayedLineRange.location >= 0);
272 HFASSERT(displayedLineRange.length >= 0);
273 HFASSERT(displayedLineRange.location + displayedLineRange.length <= HFULToFP([self totalLineCount]));
274 #endif
275 return displayedLineRange;
276 }
277
278 - (void)setDisplayedLineRange:(HFFPRange)range {
279 #if ! NDEBUG
280 HFASSERT(range.location >= 0);
281 HFASSERT(range.length >= 0);
282 #endif
283 if (range.location + range.length > HFULToFP([self totalLineCount])) {
284 range.location = [self totalLineCount] - range.length;
285 if (range.location < 0) {
286 return;
287 }
288 }
289 if (! HFFPRangeEqualsRange(range, displayedLineRange)) {
290 displayedLineRange = range;
291 [self _addPropertyChangeBits:HFControllerDisplayedLineRange];
292 }
293 }
294
295 - (CGFloat)lineHeight {
296 return lineHeight;
297 }
298
299 - (void)setFont:(NSFont *)val {
300 if (val != _font) {
301 CGFloat priorLineHeight = [self lineHeight];
302
303 [_font release];
304 _font = [val copy];
305
306 NSLayoutManager *manager = [[NSLayoutManager alloc] init];
307 lineHeight = [manager defaultLineHeightForFont:_font];
308 [manager release];
309
310 HFControllerPropertyBits bits = HFControllerFont;
311 if (lineHeight != priorLineHeight) bits |= HFControllerLineHeight;
312 [self _addPropertyChangeBits:bits];
313 [self _insertPropertyChangeFence];
314 [self _addPropertyChangeBits:HFControllerViewSizeRatios];
315 [self _updateDisplayedRange];
316 }
317 }
318
319 - (BOOL)shouldAntialias {
320 return _hfflags.antialias;
321 }
322
323 - (void)setShouldAntialias:(BOOL)antialias {
324 antialias = !! antialias;
325 if (antialias != _hfflags.antialias) {
326 _hfflags.antialias = antialias;
327 [self _addPropertyChangeBits:HFControllerAntialias];
328 }
329 }
330
331 - (BOOL)shouldColorBytes {
332 return _hfflags.colorbytes;
333 }
334
335 - (void)setShouldColorBytes:(BOOL)colorbytes {
336 colorbytes = !! colorbytes;
337 if (colorbytes != _hfflags.colorbytes) {
338 _hfflags.colorbytes = colorbytes;
339 [self _addPropertyChangeBits:HFControllerColorBytes];
340 }
341 }
342
343 - (BOOL)shouldShowCallouts {
344 return _hfflags.showcallouts;
345 }
346
347 - (void)setShouldShowCallouts:(BOOL)showcallouts {
348 showcallouts = !! showcallouts;
349 if (showcallouts != _hfflags.showcallouts) {
350 _hfflags.showcallouts = showcallouts;
351 [self _addPropertyChangeBits:HFControllerShowCallouts];
352 }
353 }
354
355 - (BOOL)shouldHideNullBytes {
356 return _hfflags.hideNullBytes;
357 }
358
359 - (void)setShouldHideNullBytes:(BOOL)hideNullBytes
360 {
361 hideNullBytes = !! hideNullBytes;
362 if (hideNullBytes != _hfflags.hideNullBytes) {
363 _hfflags.hideNullBytes = hideNullBytes;
364 [self _addPropertyChangeBits:HFControllerHideNullBytes];
365 }
366 }
367
368 - (BOOL)shouldLiveReload {
369 return _hfflags.livereload;
370 }
371
372 - (void)setShouldLiveReload:(BOOL)livereload {
373 _hfflags.livereload = !!livereload;
374
375 }
376
377 - (void)setBytesPerColumn:(NSUInteger)val {
378 if (val != bytesPerColumn) {
379 bytesPerColumn = val;
380 [self _addPropertyChangeBits:HFControllerBytesPerColumn];
381 }
382 }
383
384 - (NSUInteger)bytesPerColumn {
385 return bytesPerColumn;
386 }
387
388 - (BOOL)_shouldInvertSelectedRangesByAnchorRange {
389 return _hfflags.selectionInProgress && _hfflags.commandExtendSelection;
390 }
391
392 - (NSArray *)_invertedSelectedContentsRanges {
393 HFASSERT([selectedContentsRanges count] > 0);
394 HFASSERT(selectionAnchorRange.location != NO_SELECTION);
395 if (selectionAnchorRange.length == 0) return [NSArray arrayWithArray:selectedContentsRanges];
396
397 NSArray *cleanedRanges = [HFRangeWrapper organizeAndMergeRanges:selectedContentsRanges];
398 NSMutableArray *result = [NSMutableArray array];
399
400 /* Our algorithm works as follows - add any ranges outside of the selectionAnchorRange, clipped by the selectionAnchorRange. Then extract every "index" in our cleaned selected arrays that are within the selectionAnchorArray. An index is the location where a range starts or stops. Then use those indexes to create the inverted arrays. A range parity of 1 means that we are adding the range. */
401
402 /* Add all the ranges that are outside of selectionAnchorRange, clipping them if necessary */
403 HFASSERT(HFSumDoesNotOverflow(selectionAnchorRange.location, selectionAnchorRange.length));
404 FOREACH(HFRangeWrapper*, outsideWrapper, cleanedRanges) {
405 HFRange range = [outsideWrapper HFRange];
406 if (range.location < selectionAnchorRange.location) {
407 HFRange clippedRange;
408 clippedRange.location = range.location;
409 HFASSERT(MIN(HFMaxRange(range), selectionAnchorRange.location) >= clippedRange.location);
410 clippedRange.length = MIN(HFMaxRange(range), selectionAnchorRange.location) - clippedRange.location;
411 [result addObject:[HFRangeWrapper withRange:clippedRange]];
412 }
413 if (HFMaxRange(range) > HFMaxRange(selectionAnchorRange)) {
414 HFRange clippedRange;
415 clippedRange.location = MAX(range.location, HFMaxRange(selectionAnchorRange));
416 HFASSERT(HFMaxRange(range) >= clippedRange.location);
417 clippedRange.length = HFMaxRange(range) - clippedRange.location;
418 [result addObject:[HFRangeWrapper withRange:clippedRange]];
419 }
420 }
421
422 HFASSERT(HFSumDoesNotOverflow(selectionAnchorRange.location, selectionAnchorRange.length));
423
424 NEW_ARRAY(unsigned long long, partitions, 2*[cleanedRanges count] + 2);
425 NSUInteger partitionCount, partitionIndex = 0;
426
427 partitions[partitionIndex++] = selectionAnchorRange.location;
428 FOREACH(HFRangeWrapper*, wrapper, cleanedRanges) {
429 HFRange range = [wrapper HFRange];
430 if (! HFIntersectsRange(range, selectionAnchorRange)) continue;
431
432 partitions[partitionIndex++] = MAX(selectionAnchorRange.location, range.location);
433 partitions[partitionIndex++] = MIN(HFMaxRange(selectionAnchorRange), HFMaxRange(range));
434 }
435
436 // For some reason, using HFMaxRange confuses the static analyzer
437 partitions[partitionIndex++] = HFSum(selectionAnchorRange.location, selectionAnchorRange.length);
438
439 partitionCount = partitionIndex;
440 HFASSERT((partitionCount % 2) == 0);
441
442 partitionIndex = 0;
443 while (partitionIndex < partitionCount) {
444 HFASSERT(partitionIndex + 1 < partitionCount);
445 HFASSERT(partitions[partitionIndex] <= partitions[partitionIndex + 1]);
446 if (partitions[partitionIndex] < partitions[partitionIndex + 1]) {
447 HFRange range = HFRangeMake(partitions[partitionIndex], partitions[partitionIndex + 1] - partitions[partitionIndex]);
448 [result addObject:[HFRangeWrapper withRange:range]];
449 }
450 partitionIndex += 2;
451 }
452
453 FREE_ARRAY(partitions);
454
455 if ([result count] == 0) [result addObject:[HFRangeWrapper withRange:HFRangeMake(selectionAnchor, 0)]];
456
457 return [HFRangeWrapper organizeAndMergeRanges:result];
458 }
459
460 - (void)_ensureSelectionIsValid {
461 HFASSERT(selectedContentsRanges != nil);
462 HFASSERT([selectedContentsRanges count] > 0);
463 BOOL onlyOneWrapper = ([selectedContentsRanges count] == 1);
464 FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
465 EXPECT_CLASS(wrapper, HFRangeWrapper);
466 HFRange range = [wrapper HFRange];
467 if (!HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength]))){
468 [self setSelectedContentsRanges:@[[HFRangeWrapper withRange:HFRangeMake(0, 0)]]];
469 return;
470 }
471 if (onlyOneWrapper == NO) HFASSERT(range.length > 0); /* If we have more than one wrapper, then none of them should be zero length */
472 }
473 }
474
475 - (void)_setSingleSelectedContentsRange:(HFRange)newSelection {
476 if (!HFRangeIsSubrangeOfRange(newSelection, HFRangeMake(0, [self contentsLength]))) return;
477 BOOL selectionChanged;
478 if ([selectedContentsRanges count] == 1) {
479 selectionChanged = ! HFRangeEqualsRange([selectedContentsRanges[0] HFRange], newSelection);
480 }
481 else {
482 selectionChanged = YES;
483 }
484
485 if (selectionChanged) {
486 [selectedContentsRanges removeAllObjects];
487 [selectedContentsRanges addObject:[HFRangeWrapper withRange:newSelection]];
488 [self _addPropertyChangeBits:HFControllerSelectedRanges];
489 }
490 VALIDATE_SELECTION();
491 }
492
493 - (NSArray *)selectedContentsRanges {
494 VALIDATE_SELECTION();
495 if ([self _shouldInvertSelectedRangesByAnchorRange]) return [self _invertedSelectedContentsRanges];
496 else return [NSArray arrayWithArray:selectedContentsRanges];
497 }
498
499 - (unsigned long long)contentsLength {
500 if (! byteArray) return 0;
501 else return [byteArray length];
502 }
503
504 - (NSData *)dataForRange:(HFRange)range {
505 HFASSERT(range.length <= NSUIntegerMax); // it doesn't make sense to ask for a buffer larger than can be stored in memory
506 HFASSERT(HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength])));
507
508 if(range.length == 0) {
509 // Don't throw out cache for an empty request! Also makes the analyzer happier.
510 return [NSData data];
511 }
512
513 NSUInteger newGenerationIndex = [byteArray changeGenerationCount];
514 if (cachedData == nil || newGenerationIndex != cachedGenerationIndex || ! HFRangeIsSubrangeOfRange(range, cachedRange)) {
515 [cachedData release];
516 cachedGenerationIndex = newGenerationIndex;
517 cachedRange = range;
518 NSUInteger length = ll2l(range.length);
519 unsigned char *data = check_malloc(length);
520 [byteArray copyBytes:data range:range];
521 cachedData = [[NSData alloc] initWithBytesNoCopy:data length:length freeWhenDone:YES];
522 }
523
524 if (HFRangeEqualsRange(range, cachedRange)) {
525 return cachedData;
526 }
527 else {
528 HFASSERT(cachedRange.location <= range.location);
529 NSRange cachedDataSubrange;
530 cachedDataSubrange.location = ll2l(range.location - cachedRange.location);
531 cachedDataSubrange.length = ll2l(range.length);
532 return [cachedData subdataWithRange:cachedDataSubrange];
533 }
534 }
535
536 - (void)copyBytes:(unsigned char *)bytes range:(HFRange)range {
537 HFASSERT(range.length <= NSUIntegerMax); // it doesn't make sense to ask for a buffer larger than can be stored in memory
538 HFASSERT(HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength])));
539 [byteArray copyBytes:bytes range:range];
540 }
541
542 - (void)_updateDisplayedRange {
543 HFRange proposedNewDisplayRange;
544 HFFPRange proposedNewLineRange;
545 HFRange maxRangeSet = [self _maximumDisplayedRangeSet];
546 NSUInteger maxBytesForViewSize = NSUIntegerMax;
547 double maxLines = DBL_MAX;
548 FOREACH(HFRepresenter*, rep, representers) {
549 NSView *view = [rep view];
550 double repMaxLines = [rep maximumAvailableLinesForViewHeight:NSHeight([view frame])];
551 if (repMaxLines != DBL_MAX) {
552 /* bytesPerLine may be ULONG_MAX. We want to compute the smaller of maxBytesForViewSize and ceil(repMaxLines) * bytesPerLine. If the latter expression overflows, the smaller is the former. */
553 NSUInteger repMaxLinesUInt = (NSUInteger)ceil(repMaxLines);
554 NSUInteger maxLinesTimesBytesPerLine = repMaxLinesUInt * bytesPerLine;
555 /* Check if we overflowed */
556 BOOL overflowed = (repMaxLinesUInt != 0 && (maxLinesTimesBytesPerLine / repMaxLinesUInt != bytesPerLine));
557 if (! overflowed) {
558 maxBytesForViewSize = MIN(maxLinesTimesBytesPerLine, maxBytesForViewSize);
559 }
560 }
561 maxLines = MIN(repMaxLines, maxLines);
562 }
563 if (maxLines == DBL_MAX) {
564 proposedNewDisplayRange = HFRangeMake(0, 0);
565 proposedNewLineRange = (HFFPRange){0, 0};
566 }
567 else {
568 unsigned long long maximumDisplayedBytes = MIN(maxRangeSet.length, maxBytesForViewSize);
569 HFASSERT(HFMaxRange(maxRangeSet) >= maximumDisplayedBytes);
570
571 proposedNewDisplayRange.location = MIN(HFMaxRange(maxRangeSet) - maximumDisplayedBytes, displayedContentsRange.location);
572 proposedNewDisplayRange.location -= proposedNewDisplayRange.location % bytesPerLine;
573 proposedNewDisplayRange.length = MIN(HFMaxRange(maxRangeSet) - proposedNewDisplayRange.location, maxBytesForViewSize);
574 if (maxBytesForViewSize % bytesPerLine != 0) {
575 NSLog(@"Bad max bytes: %lu (%lu)", (unsigned long)maxBytesForViewSize, (unsigned long)bytesPerLine);
576 }
577 if (HFMaxRange(maxRangeSet) != ULLONG_MAX && (HFMaxRange(maxRangeSet) - proposedNewDisplayRange.location) % bytesPerLine != 0) {
578 NSLog(@"Bad max range minus: %llu (%lu)", HFMaxRange(maxRangeSet) - proposedNewDisplayRange.location, (unsigned long)bytesPerLine);
579 }
580
581 long double lastLine = HFULToFP([self totalLineCount]);
582 proposedNewLineRange.length = MIN(maxLines, lastLine);
583 proposedNewLineRange.location = MIN(displayedLineRange.location, lastLine - proposedNewLineRange.length);
584 }
585 HFASSERT(HFRangeIsSubrangeOfRange(proposedNewDisplayRange, maxRangeSet));
586 HFASSERT(proposedNewDisplayRange.location % bytesPerLine == 0);
587 if (! HFRangeEqualsRange(proposedNewDisplayRange, displayedContentsRange) || ! HFFPRangeEqualsRange(proposedNewLineRange, displayedLineRange)) {
588 displayedContentsRange = proposedNewDisplayRange;
589 displayedLineRange = proposedNewLineRange;
590 [self _addPropertyChangeBits:HFControllerDisplayedLineRange];
591 }
592 }
593
594 - (void)_ensureVisibilityOfLocation:(unsigned long long)location {
595 HFASSERT(location <= [self contentsLength]);
596 unsigned long long lineInt = location / bytesPerLine;
597 long double line = HFULToFP(lineInt);
598 HFASSERT(line >= 0);
599 line = MIN(line, HFULToFP([self totalLineCount]) - 1);
600 HFFPRange lineRange = [self displayedLineRange];
601 HFFPRange newLineRange = lineRange;
602 if (line < lineRange.location) {
603 newLineRange.location = line;
604 }
605 else if (line >= lineRange.location + lineRange.length) {
606 HFASSERT(lineRange.location + lineRange.length >= 1);
607 newLineRange.location = lineRange.location + (line - (lineRange.location + lineRange.length - 1));
608 }
609 [self setDisplayedLineRange:newLineRange];
610 }
611
612 - (void)maximizeVisibilityOfContentsRange:(HFRange)range {
613 HFASSERT(HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength])));
614
615 // Find the minimum move necessary to make range visible
616 HFFPRange displayRange = [self displayedLineRange];
617 HFFPRange newDisplayRange = displayRange;
618 unsigned long long startLine = range.location / bytesPerLine;
619 unsigned long long endLine = HFDivideULLRoundingUp(HFRoundUpToNextMultipleSaturate(HFMaxRange(range), bytesPerLine), bytesPerLine);
620 HFASSERT(endLine > startLine || endLine == ULLONG_MAX);
621 long double linesInRange = HFULToFP(endLine - startLine);
622 long double linesToDisplay = MIN(displayRange.length, linesInRange);
623 HFASSERT(linesToDisplay <= linesInRange);
624 long double linesToMoveDownToMakeLastLineVisible = HFULToFP(endLine) - (displayRange.location + displayRange.length);
625 long double linesToMoveUpToMakeFirstLineVisible = displayRange.location - HFULToFP(startLine);
626 //HFASSERT(linesToMoveUpToMakeFirstLineVisible <= 0 || linesToMoveDownToMakeLastLineVisible <= 0);
627 // in general, we expect either linesToMoveUpToMakeFirstLineVisible to be <= zero, or linesToMoveDownToMakeLastLineVisible to be <= zero. However, if the available space is smaller than one line, then that won't be true.
628 if (linesToMoveDownToMakeLastLineVisible > 0) {
629 newDisplayRange.location += linesToMoveDownToMakeLastLineVisible;
630 }
631 else if (linesToMoveUpToMakeFirstLineVisible > 0 && linesToDisplay >= 1) {
632 // the >= 1 check prevents some wacky behavior when we have less than one line's worth of space, that caused bouncing between the top and bottom of the line
633 newDisplayRange.location -= linesToMoveUpToMakeFirstLineVisible;
634 }
635
636 // Use the minimum movement if it would be visually helpful; otherwise just center.
637 if (HFFPIntersectsRange(displayRange, newDisplayRange)) {
638 [self setDisplayedLineRange:newDisplayRange];
639 } else {
640 [self centerContentsRange:range];
641 }
642 }
643
644 - (void)centerContentsRange:(HFRange)range {
645 HFASSERT(HFRangeIsSubrangeOfRange(range, HFRangeMake(0, [self contentsLength])));
646 HFFPRange displayRange = [self displayedLineRange];
647 const long double numDisplayedLines = displayRange.length;
648 HFFPRange newDisplayRange;
649 unsigned long long startLine = range.location / bytesPerLine;
650 unsigned long long endLine = HFDivideULLRoundingUp(HFRoundUpToNextMultipleSaturate(HFMaxRange(range), bytesPerLine), bytesPerLine);
651 HFASSERT(endLine > startLine || endLine == ULLONG_MAX);
652 long double linesInRange = HFULToFP(endLine - startLine);
653
654 /* Handle the case of a line range bigger than we can display by choosing the top lines. */
655 if (numDisplayedLines <= linesInRange) {
656 newDisplayRange = (HFFPRange){startLine, numDisplayedLines};
657 }
658 else {
659 /* Construct a newDisplayRange that centers {startLine, endLine} */
660 long double center = startLine + (endLine - startLine) / 2.;
661 newDisplayRange = (HFFPRange){center - numDisplayedLines / 2., numDisplayedLines};
662 }
663
664 /* Move the newDisplayRange up or down as necessary */
665 newDisplayRange.location = fmaxl(newDisplayRange.location, (long double)0.);
666 newDisplayRange.location = fminl(newDisplayRange.location, HFULToFP([self totalLineCount]) - numDisplayedLines);
667 [self setDisplayedLineRange:newDisplayRange];
668 }
669
670 /* Clips the selection to a given length. If this would clip the entire selection, returns a zero length selection at the end. Indicates HFControllerSelectedRanges if the selection changes. */
671 - (void)_clipSelectedContentsRangesToLength:(unsigned long long)newLength {
672 NSMutableArray *newTempSelection = [selectedContentsRanges mutableCopy];
673 NSUInteger i, max = [newTempSelection count];
674 for (i=0; i < max; i++) {
675 HFRange range = [newTempSelection[i] HFRange];
676 if (HFMaxRange(range) > newLength) {
677 if (range.location > newLength) {
678 /* The range starts past our new max. Just remove this range entirely */
679 [newTempSelection removeObjectAtIndex:i];
680 i--;
681 max--;
682 }
683 else {
684 /* Need to clip this range */
685 range.length = newLength - range.location;
686 newTempSelection[i] = [HFRangeWrapper withRange:range];
687 }
688 }
689 }
690 [newTempSelection setArray:[HFRangeWrapper organizeAndMergeRanges:newTempSelection]];
691
692 /* If there are multiple empty ranges, remove all but the first */
693 BOOL foundEmptyRange = NO;
694 max = [newTempSelection count];
695 for (i=0; i < max; i++) {
696 HFRange range = [newTempSelection[i] HFRange];
697 HFASSERT(HFMaxRange(range) <= newLength);
698 if (range.length == 0) {
699 if (foundEmptyRange) {
700 [newTempSelection removeObjectAtIndex:i];
701 i--;
702 max--;
703 }
704 foundEmptyRange = YES;
705 }
706 }
707 if (max == 0) {
708 /* Removed all ranges - insert one at the end */
709 [newTempSelection addObject:[HFRangeWrapper withRange:HFRangeMake(newLength, 0)]];
710 }
711
712 /* If something changed, set the new selection and post the change bit */
713 if (! [selectedContentsRanges isEqualToArray:newTempSelection]) {
714 [selectedContentsRanges setArray:newTempSelection];
715 [self _addPropertyChangeBits:HFControllerSelectedRanges];
716 }
717
718 [newTempSelection release];
719 }
720
721 - (void)setByteArray:(HFByteArray *)val {
722 REQUIRE_NOT_NULL(val);
723 BEGIN_TRANSACTION();
724 [byteArray removeObserver:self forKeyPath:@"changesAreLocked"];
725 [val retain];
726 [byteArray release];
727 byteArray = val;
728 [cachedData release];
729 cachedData = nil;
730 [byteArray addObserver:self forKeyPath:@"changesAreLocked" options:0 context:KVOContextChangesAreLocked];
731 [self _updateDisplayedRange];
732 [self _addPropertyChangeBits: HFControllerContentValue | HFControllerContentLength];
733 [self _clipSelectedContentsRangesToLength:[byteArray length]];
734 END_TRANSACTION();
735 }
736
737 - (HFByteArray *)byteArray {
738 return byteArray;
739 }
740
741 - (void)_undoNotification:note {
742 USE(note);
743 }
744
745 - (void)_removeUndoManagerNotifications {
746 if (undoManager) {
747 NSNotificationCenter *noter = [NSNotificationCenter defaultCenter];
748 [noter removeObserver:self name:NSUndoManagerWillUndoChangeNotification object:undoManager];
749 }
750 }
751
752 - (void)_addUndoManagerNotifications {
753 if (undoManager) {
754 NSNotificationCenter *noter = [NSNotificationCenter defaultCenter];
755 [noter addObserver:self selector:@selector(_undoNotification:) name:NSUndoManagerWillUndoChangeNotification object:undoManager];
756 }
757 }
758
759 - (void)_removeAllUndoOperations {
760 /* Remove all the undo operations, because some undo operation is unsupported. Note that if we were smarter we would keep a stack of undo operations and only remove ones "up to" a certain point. */
761 [undoManager removeAllActionsWithTarget:self];
762 [undoOperations makeObjectsPerformSelector:@selector(invalidate)];
763 [undoOperations removeAllObjects];
764 }
765
766 - (void)setUndoManager:(NSUndoManager *)manager {
767 [self _removeUndoManagerNotifications];
768 [self _removeAllUndoOperations];
769 [manager retain];
770 [undoManager release];
771 undoManager = manager;
772 [self _addUndoManagerNotifications];
773 }
774
775 - (NSUndoManager *)undoManager {
776 return undoManager;
777 }
778
779 - (NSUInteger)bytesPerLine {
780 return bytesPerLine;
781 }
782
783 - (BOOL)editable {
784 return _hfflags.editable && ! [byteArray changesAreLocked] && _hfflags.editMode != HFReadOnlyMode;
785 }
786
787 - (void)setEditable:(BOOL)flag {
788 if (flag != _hfflags.editable) {
789 _hfflags.editable = flag;
790 [self _addPropertyChangeBits:HFControllerEditable];
791 }
792 }
793
794 - (void)_updateBytesPerLine {
795 NSUInteger newBytesPerLine = NSUIntegerMax;
796 FOREACH(HFRepresenter*, rep, representers) {
797 NSView *view = [rep view];
798 CGFloat width = [view frame].size.width;
799 NSUInteger repMaxBytesPerLine = [rep maximumBytesPerLineForViewWidth:width];
800 HFASSERT(repMaxBytesPerLine > 0);
801 newBytesPerLine = MIN(repMaxBytesPerLine, newBytesPerLine);
802 }
803 if (newBytesPerLine != bytesPerLine) {
804 HFASSERT(newBytesPerLine > 0);
805 bytesPerLine = newBytesPerLine;
806 BEGIN_TRANSACTION();
807 [self _addPropertyChangeBits:HFControllerBytesPerLine];
808 END_TRANSACTION();
809 }
810 }
811
812 - (void)representer:(HFRepresenter *)rep changedProperties:(HFControllerPropertyBits)properties {
813 USE(rep);
814 HFControllerPropertyBits remainingProperties = properties;
815 BEGIN_TRANSACTION();
816 if (remainingProperties & HFControllerBytesPerLine) {
817 [self _updateBytesPerLine];
818 remainingProperties &= ~HFControllerBytesPerLine;
819 }
820 if (remainingProperties & HFControllerDisplayedLineRange) {
821 [self _updateDisplayedRange];
822 remainingProperties &= ~HFControllerDisplayedLineRange;
823 }
824 if (remainingProperties & HFControllerByteRangeAttributes) {
825 [self _addPropertyChangeBits:HFControllerByteRangeAttributes];
826 remainingProperties &= ~HFControllerByteRangeAttributes;
827 }
828 if (remainingProperties & HFControllerViewSizeRatios) {
829 [self _addPropertyChangeBits:HFControllerViewSizeRatios];
830 remainingProperties &= ~HFControllerViewSizeRatios;
831 }
832 if (remainingProperties) {
833 NSLog(@"Unknown properties: %lx", (long)remainingProperties);
834 }
835 END_TRANSACTION();
836 }
837
838 - (HFByteArray *)byteArrayForSelectedContentsRanges {
839 HFByteArray *result = nil;
840 HFByteArray *bytes = [self byteArray];
841 VALIDATE_SELECTION();
842 FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
843 HFRange range = [wrapper HFRange];
844 HFByteArray *additionalBytes = [bytes subarrayWithRange:range];
845 if (! result) {
846 result = additionalBytes;
847 }
848 else {
849 [result insertByteArray:additionalBytes inRange:HFRangeMake([result length], 0)];
850 }
851 }
852 return result;
853 }
854
855 /* Flattens the selected range to a single range (the selected range becomes any character within or between the selected ranges). Modifies the selectedContentsRanges and returns the new single HFRange. Does not call notifyRepresentersOfChanges: */
856 - (HFRange)_flattenSelectionRange {
857 HFASSERT([selectedContentsRanges count] >= 1);
858
859 HFRange resultRange = [selectedContentsRanges[0] HFRange];
860 if ([selectedContentsRanges count] == 1) return resultRange; //already flat
861
862 FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
863 HFRange selectedRange = [wrapper HFRange];
864 if (selectedRange.location < resultRange.location) {
865 /* Extend our result range backwards */
866 resultRange.length += resultRange.location - selectedRange.location;
867 resultRange.location = selectedRange.location;
868 }
869 if (HFRangeExtendsPastRange(selectedRange, resultRange)) {
870 HFASSERT(selectedRange.location >= resultRange.location); //must be true by if statement above
871 resultRange.length = HFSum(selectedRange.location - resultRange.location, selectedRange.length);
872 }
873 }
874 [self _setSingleSelectedContentsRange:resultRange];
875 return resultRange;
876 }
877
878 - (unsigned long long)_minimumSelectionLocation {
879 HFASSERT([selectedContentsRanges count] >= 1);
880 unsigned long long minSelection = ULLONG_MAX;
881 FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
882 HFRange range = [wrapper HFRange];
883 minSelection = MIN(minSelection, range.location);
884 }
885 return minSelection;
886 }
887
888 - (unsigned long long)_maximumSelectionLocation {
889 HFASSERT([selectedContentsRanges count] >= 1);
890 unsigned long long maxSelection = 0;
891 FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
892 HFRange range = [wrapper HFRange];
893 maxSelection = MAX(maxSelection, HFMaxRange(range));
894 }
895 return maxSelection;
896 }
897
898 - (unsigned long long)minimumSelectionLocation {
899 return [self _minimumSelectionLocation];
900 }
901
902 - (unsigned long long)maximumSelectionLocation {
903 return [self _maximumSelectionLocation];
904 }
905
906 /* Put the selection at the left or right end of the current selection, with zero length. Modifies the selectedContentsRanges and returns the new single HFRange. Does not call notifyRepresentersOfChanges: */
907 - (HFRange)_telescopeSelectionRangeInDirection:(HFControllerMovementDirection)direction {
908 HFRange resultRange;
909 HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
910 resultRange.location = (direction == HFControllerDirectionLeft ? [self _minimumSelectionLocation] : [self _maximumSelectionLocation]);
911 resultRange.length = 0;
912 [self _setSingleSelectedContentsRange:resultRange];
913 return resultRange;
914 }
915
916 - (void)beginSelectionWithEvent:(NSEvent *)event forByteIndex:(unsigned long long)characterIndex {
917 USE(event);
918 HFASSERT(characterIndex <= [self contentsLength]);
919
920 /* Determine how to perform the selection - normally, with command key, or with shift key. Command + shift is the same as command. The shift key closes the selection - the selected range becomes the single range containing the first and last selected character. */
921 _hfflags.shiftExtendSelection = NO;
922 _hfflags.commandExtendSelection = NO;
923 NSUInteger flags = [event modifierFlags];
924 if (flags & NSCommandKeyMask) _hfflags.commandExtendSelection = YES;
925 else if (flags & NSShiftKeyMask) _hfflags.shiftExtendSelection = YES;
926
927 selectionAnchor = NO_SELECTION;
928 selectionAnchorRange = HFRangeMake(NO_SELECTION, 0);
929
930 _hfflags.selectionInProgress = YES;
931 if (_hfflags.commandExtendSelection) {
932 /* The selection anchor is used to track the "invert" range. All characters within this range have their selection inverted. This is tracked by the _shouldInvertSelectedRangesByAnchorRange method. */
933 selectionAnchor = characterIndex;
934 selectionAnchorRange = HFRangeMake(characterIndex, 0);
935 }
936 else if (_hfflags.shiftExtendSelection) {
937 /* The selection anchor is used to track the single (flattened) selected range. */
938 HFRange selectedRange = [self _flattenSelectionRange];
939 unsigned long long distanceFromRangeStart = HFAbsoluteDifference(selectedRange.location, characterIndex);
940 unsigned long long distanceFromRangeEnd = HFAbsoluteDifference(HFMaxRange(selectedRange), characterIndex);
941 if (selectedRange.length == 0) {
942 HFASSERT(distanceFromRangeStart == distanceFromRangeEnd);
943 selectionAnchor = selectedRange.location;
944 selectedRange.location = MIN(characterIndex, selectedRange.location);
945 selectedRange.length = distanceFromRangeStart;
946 }
947 else if (distanceFromRangeStart >= distanceFromRangeEnd) {
948 /* Push the "end forwards" */
949 selectedRange.length = distanceFromRangeStart;
950 selectionAnchor = selectedRange.location;
951 }
952 else {
953 /* Push the "start back" */
954 selectedRange.location = selectedRange.location + selectedRange.length - distanceFromRangeEnd;
955 selectedRange.length = distanceFromRangeEnd;
956 selectionAnchor = HFSum(selectedRange.length, selectedRange.location);
957 }
958 HFASSERT(HFRangeIsSubrangeOfRange(selectedRange, HFRangeMake(0, [self contentsLength])));
959 selectionAnchorRange = selectedRange;
960 [self _setSingleSelectedContentsRange:selectedRange];
961 }
962 else {
963 /* No modifier key selection. The selection anchor is not used. */
964 [self _setSingleSelectedContentsRange:HFRangeMake(characterIndex, 0)];
965 selectionAnchor = characterIndex;
966 }
967 }
968
969 - (void)continueSelectionWithEvent:(NSEvent *)event forByteIndex:(unsigned long long)byteIndex {
970 USE(event);
971 HFASSERT(_hfflags.selectionInProgress);
972 HFASSERT(byteIndex <= [self contentsLength]);
973 BEGIN_TRANSACTION();
974 if (_hfflags.commandExtendSelection) {
975 /* Clear any zero-length ranges, unless there's only one */
976 NSUInteger rangeCount = [selectedContentsRanges count];
977 NSUInteger rangeIndex = rangeCount;
978 while (rangeIndex-- > 0) {
979 if (rangeCount > 1 && [selectedContentsRanges[rangeIndex] HFRange].length == 0) {
980 [selectedContentsRanges removeObjectAtIndex:rangeIndex];
981 rangeCount--;
982 }
983 }
984 selectionAnchorRange.location = MIN(byteIndex, selectionAnchor);
985 selectionAnchorRange.length = MAX(byteIndex, selectionAnchor) - selectionAnchorRange.location;
986 [self _addPropertyChangeBits:HFControllerSelectedRanges];
987 }
988 else if (_hfflags.shiftExtendSelection) {
989 HFASSERT(selectionAnchorRange.location != NO_SELECTION);
990 HFASSERT(selectionAnchor != NO_SELECTION);
991 HFRange range;
992 if (! HFLocationInRange(byteIndex, selectionAnchorRange)) {
993 /* The character index is outside of the selection anchor range. The new range is just the selected anchor range combined with the character index. */
994 range.location = MIN(byteIndex, selectionAnchorRange.location);
995 unsigned long long rangeEnd = MAX(byteIndex, HFSum(selectionAnchorRange.location, selectionAnchorRange.length));
996 HFASSERT(rangeEnd >= range.location);
997 range.length = rangeEnd - range.location;
998 }
999 else {
1000 /* The character is within the selection anchor range. We use the selection anchor index to determine which "side" of the range is selected. */
1001 range.location = MIN(selectionAnchor, byteIndex);
1002 range.length = HFAbsoluteDifference(selectionAnchor, byteIndex);
1003 }
1004 [self _setSingleSelectedContentsRange:range];
1005 }
1006 else {
1007 /* No modifier key selection */
1008 HFRange range;
1009 range.location = MIN(byteIndex, selectionAnchor);
1010 range.length = MAX(byteIndex, selectionAnchor) - range.location;
1011 [self _setSingleSelectedContentsRange:range];
1012 }
1013 END_TRANSACTION();
1014 VALIDATE_SELECTION();
1015 }
1016
1017 - (void)endSelectionWithEvent:(NSEvent *)event forByteIndex:(unsigned long long)characterIndex {
1018 USE(event);
1019 HFASSERT(_hfflags.selectionInProgress);
1020 HFASSERT(characterIndex <= [self contentsLength]);
1021 if (_hfflags.commandExtendSelection) {
1022 selectionAnchorRange.location = MIN(characterIndex, selectionAnchor);
1023 selectionAnchorRange.length = MAX(characterIndex, selectionAnchor) - selectionAnchorRange.location;
1024
1025 /* "Commit" our selectionAnchorRange */
1026 NSArray *newSelection = [self _invertedSelectedContentsRanges];
1027 [selectedContentsRanges setArray:newSelection];
1028 }
1029 else if (_hfflags.shiftExtendSelection) {
1030 HFASSERT(selectionAnchorRange.location != NO_SELECTION);
1031 HFASSERT(selectionAnchor != NO_SELECTION);
1032 HFRange range;
1033 if (! HFLocationInRange(characterIndex, selectionAnchorRange)) {
1034 /* The character index is outside of the selection anchor range. The new range is just the selected anchor range combined with the character index. */
1035 range.location = MIN(characterIndex, selectionAnchorRange.location);
1036 unsigned long long rangeEnd = MAX(characterIndex, HFSum(selectionAnchorRange.location, selectionAnchorRange.length));
1037 HFASSERT(rangeEnd >= range.location);
1038 range.length = rangeEnd - range.location;
1039 }
1040 else {
1041 /* The character is within the selection anchor range. We use the selection anchor index to determine which "side" of the range is selected. */
1042 range.location = MIN(selectionAnchor, characterIndex);
1043 range.length = HFAbsoluteDifference(selectionAnchor, characterIndex);
1044 }
1045 [self _setSingleSelectedContentsRange:range];
1046 }
1047 else {
1048 /* No modifier key selection */
1049 HFRange range;
1050 range.location = MIN(characterIndex, selectionAnchor);
1051 range.length = MAX(characterIndex, selectionAnchor) - range.location;
1052 [self _setSingleSelectedContentsRange:range];
1053 }
1054
1055 _hfflags.selectionInProgress = NO;
1056 _hfflags.shiftExtendSelection = NO;
1057 _hfflags.commandExtendSelection = NO;
1058 selectionAnchor = NO_SELECTION;
1059 }
1060
1061 - (double)selectionPulseAmount {
1062 double result = 0;
1063 if (pulseSelectionStartTime > 0) {
1064 CFTimeInterval diff = pulseSelectionCurrentTime - pulseSelectionStartTime;
1065 if (diff > 0 && diff < kPulseDuration) {
1066 result = 1. - fabs(diff * 2 - kPulseDuration) / kPulseDuration;
1067 }
1068 }
1069 return result;
1070 }
1071
1072 - (void)firePulseTimer:(NSTimer *)timer {
1073 USE(timer);
1074 HFASSERT(pulseSelectionStartTime != 0);
1075 pulseSelectionCurrentTime = CFAbsoluteTimeGetCurrent();
1076 [self _addPropertyChangeBits:HFControllerSelectionPulseAmount];
1077 if (pulseSelectionCurrentTime - pulseSelectionStartTime > kPulseDuration) {
1078 [pulseSelectionTimer invalidate];
1079 [pulseSelectionTimer release];
1080 pulseSelectionTimer = nil;
1081 }
1082 }
1083
1084 - (void)pulseSelection {
1085 pulseSelectionStartTime = CFAbsoluteTimeGetCurrent();
1086 if (pulseSelectionTimer == nil) {
1087 pulseSelectionTimer = [[NSTimer scheduledTimerWithTimeInterval:(1. / 30.) target:self selector:@selector(firePulseTimer:) userInfo:nil repeats:YES] retain];
1088 }
1089 }
1090
1091 - (void)scrollByLines:(long double)lines {
1092 HFFPRange lineRange = [self displayedLineRange];
1093 HFASSERT(HFULToFP([self totalLineCount]) >= lineRange.length);
1094 long double maxScroll = HFULToFP([self totalLineCount]) - lineRange.length;
1095 if (lines < 0) {
1096 lineRange.location -= MIN(lineRange.location, -lines);
1097 }
1098 else {
1099 lineRange.location = MIN(maxScroll, lineRange.location + lines);
1100 }
1101 [self setDisplayedLineRange:lineRange];
1102 }
1103
1104 - (void)scrollWithScrollEvent:(NSEvent *)scrollEvent {
1105 HFASSERT(scrollEvent != NULL);
1106 HFASSERT([scrollEvent type] == NSScrollWheel);
1107 CGFloat preciseScroll = 0;
1108 BOOL hasPreciseScroll;
1109
1110 /* Prefer precise deltas */
1111 if ([scrollEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)]) {
1112 hasPreciseScroll = [scrollEvent hasPreciseScrollingDeltas];
1113 if (hasPreciseScroll) {
1114 /* In this case, we're going to scroll by a certain number of points */
1115 preciseScroll = [scrollEvent scrollingDeltaY];
1116 }
1117 } else if ([scrollEvent respondsToSelector:@selector(deviceDeltaY)]) {
1118 /* Legacy (SnowLeopard) support */
1119 hasPreciseScroll = ([scrollEvent subtype] == 1);
1120 if (hasPreciseScroll) {
1121 preciseScroll = [scrollEvent deviceDeltaY];
1122 }
1123 } else {
1124 hasPreciseScroll = NO;
1125 }
1126
1127 long double scrollY = 0;
1128 if (! hasPreciseScroll) {
1129 scrollY = -kScrollMultiplier * [scrollEvent deltaY];
1130 } else {
1131 scrollY = -preciseScroll / [self lineHeight];
1132 }
1133 [self scrollByLines:scrollY];
1134 }
1135
1136 - (void)setSelectedContentsRanges:(NSArray *)selectedRanges {
1137 REQUIRE_NOT_NULL(selectedRanges);
1138 [selectedContentsRanges setArray:selectedRanges];
1139 VALIDATE_SELECTION();
1140 selectionAnchor = NO_SELECTION;
1141 [self _addPropertyChangeBits:HFControllerSelectedRanges];
1142 }
1143
1144 - (IBAction)selectAll:sender {
1145 USE(sender);
1146 if (_hfflags.selectable) {
1147 [self _setSingleSelectedContentsRange:HFRangeMake(0, [self contentsLength])];
1148 }
1149 }
1150
1151 - (void)_addRangeToSelection:(HFRange)range {
1152 [selectedContentsRanges addObject:[HFRangeWrapper withRange:range]];
1153 [selectedContentsRanges setArray:[HFRangeWrapper organizeAndMergeRanges:selectedContentsRanges]];
1154 VALIDATE_SELECTION();
1155 }
1156
1157 - (void)_removeRangeFromSelection:(HFRange)inputRange withCursorLocationIfAllSelectionRemoved:(unsigned long long)cursorLocation {
1158 NSUInteger selectionCount = [selectedContentsRanges count];
1159 HFASSERT(selectionCount > 0 && selectionCount <= NSUIntegerMax / 2);
1160 NSUInteger rangeIndex = 0;
1161 NSArray *wrappers;
1162 NEW_ARRAY(HFRange, tempRanges, selectionCount * 2);
1163 FOREACH(HFRangeWrapper*, wrapper, selectedContentsRanges) {
1164 HFRange range = [wrapper HFRange];
1165 if (! HFIntersectsRange(range, inputRange)) {
1166 tempRanges[rangeIndex++] = range;
1167 }
1168 else {
1169 if (range.location < inputRange.location) {
1170 tempRanges[rangeIndex++] = HFRangeMake(range.location, inputRange.location - range.location);
1171 }
1172 if (HFMaxRange(range) > HFMaxRange(inputRange)) {
1173 tempRanges[rangeIndex++] = HFRangeMake(HFMaxRange(inputRange), HFMaxRange(range) - HFMaxRange(inputRange));
1174 }
1175 }
1176 }
1177 if (rangeIndex == 0 || (rangeIndex == 1 && tempRanges[0].length == 0)) {
1178 /* We removed all of our ranges. Telescope us. */
1179 HFASSERT(cursorLocation <= [self contentsLength]);
1180 [self _setSingleSelectedContentsRange:HFRangeMake(cursorLocation, 0)];
1181 }
1182 else {
1183 wrappers = [HFRangeWrapper withRanges:tempRanges count:rangeIndex];
1184 [selectedContentsRanges setArray:[HFRangeWrapper organizeAndMergeRanges:wrappers]];
1185 }
1186 FREE_ARRAY(tempRanges);
1187 VALIDATE_SELECTION();
1188 }
1189
1190 - (void)_moveDirectionDiscardingSelection:(HFControllerMovementDirection)direction byAmount:(unsigned long long)amountToMove {
1191 HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
1192 BEGIN_TRANSACTION();
1193 BOOL selectionWasEmpty = ([selectedContentsRanges count] == 1 && [selectedContentsRanges[0] HFRange].length == 0);
1194 BOOL directionIsForward = (direction == HFControllerDirectionRight);
1195 HFRange selectedRange = [self _telescopeSelectionRangeInDirection: (directionIsForward ? HFControllerDirectionRight : HFControllerDirectionLeft)];
1196 HFASSERT(selectedRange.length == 0);
1197 HFASSERT([self contentsLength] >= selectedRange.location);
1198 /* A movement of just 1 with a selection only clears the selection; it does not move the cursor */
1199 if (selectionWasEmpty || amountToMove > 1) {
1200 if (direction == HFControllerDirectionLeft) {
1201 selectedRange.location -= MIN(amountToMove, selectedRange.location);
1202 }
1203 else {
1204 selectedRange.location += MIN(amountToMove, [self contentsLength] - selectedRange.location);
1205 }
1206 }
1207 selectionAnchor = NO_SELECTION;
1208 [self _setSingleSelectedContentsRange:selectedRange];
1209 [self _ensureVisibilityOfLocation:selectedRange.location];
1210 END_TRANSACTION();
1211 }
1212
1213 /* In _extendSelectionInDirection:byAmount:, we only allow left/right movement. up/down is not allowed. */
1214 - (void)_extendSelectionInDirection:(HFControllerMovementDirection)direction byAmount:(unsigned long long)amountToMove {
1215 HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
1216 unsigned long long minSelection = [self _minimumSelectionLocation];
1217 unsigned long long maxSelection = [self _maximumSelectionLocation];
1218 BOOL selectionChanged = NO;
1219 unsigned long long locationToMakeVisible = NO_SELECTION;
1220 unsigned long long contentsLength = [self contentsLength];
1221 if (selectionAnchor == NO_SELECTION) {
1222 /* Pick the anchor opposite the choice of direction */
1223 if (direction == HFControllerDirectionLeft) selectionAnchor = maxSelection;
1224 else selectionAnchor = minSelection;
1225 }
1226 if (direction == HFControllerDirectionLeft) {
1227 if (minSelection >= selectionAnchor && maxSelection > minSelection) {
1228 unsigned long long amountToRemove = llmin(maxSelection - selectionAnchor, amountToMove);
1229 unsigned long long amountToAdd = llmin(amountToMove - amountToRemove, selectionAnchor);
1230 if (amountToRemove > 0) [self _removeRangeFromSelection:HFRangeMake(maxSelection - amountToRemove, amountToRemove) withCursorLocationIfAllSelectionRemoved:minSelection];
1231 if (amountToAdd > 0) [self _addRangeToSelection:HFRangeMake(selectionAnchor - amountToAdd, amountToAdd)];
1232 selectionChanged = YES;
1233 locationToMakeVisible = (amountToAdd > 0 ? selectionAnchor - amountToAdd : maxSelection - amountToRemove);
1234 }
1235 else {
1236 if (minSelection > 0) {
1237 NSUInteger amountToAdd = ll2l(llmin(minSelection, amountToMove));
1238 if (amountToAdd > 0) [self _addRangeToSelection:HFRangeMake(minSelection - amountToAdd, amountToAdd)];
1239 selectionChanged = YES;
1240 locationToMakeVisible = minSelection - amountToAdd;
1241 }
1242 }
1243 }
1244 else if (direction == HFControllerDirectionRight) {
1245 if (maxSelection <= selectionAnchor && maxSelection > minSelection) {
1246 HFASSERT(contentsLength >= maxSelection);
1247 unsigned long long amountToRemove = ll2l(llmin(maxSelection - minSelection, amountToMove));
1248 unsigned long long amountToAdd = amountToMove - amountToRemove;
1249 if (amountToRemove > 0) [self _removeRangeFromSelection:HFRangeMake(minSelection, amountToRemove) withCursorLocationIfAllSelectionRemoved:maxSelection];
1250 if (amountToAdd > 0) [self _addRangeToSelection:HFRangeMake(maxSelection, amountToAdd)];
1251 selectionChanged = YES;
1252 locationToMakeVisible = llmin(contentsLength, (amountToAdd > 0 ? maxSelection + amountToAdd : minSelection + amountToRemove));
1253 }
1254 else {
1255 if (maxSelection < contentsLength) {
1256 NSUInteger amountToAdd = ll2l(llmin(contentsLength - maxSelection, amountToMove));
1257 [self _addRangeToSelection:HFRangeMake(maxSelection, amountToAdd)];
1258 selectionChanged = YES;
1259 locationToMakeVisible = maxSelection + amountToAdd;
1260 }
1261 }
1262 }
1263 if (selectionChanged) {
1264 BEGIN_TRANSACTION();
1265 [self _addPropertyChangeBits:HFControllerSelectedRanges];
1266 if (locationToMakeVisible != NO_SELECTION) [self _ensureVisibilityOfLocation:locationToMakeVisible];
1267 END_TRANSACTION();
1268 }
1269 }
1270
1271 /* Returns the distance to the next "word" (at least 1, unless we are empty). Here a word is identified as a column. If there are no columns, a word is a line. This is used for word movement (e.g. option + right arrow) */
1272 - (unsigned long long)_distanceToWordBoundaryForDirection:(HFControllerMovementDirection)direction {
1273 unsigned long long result = 0, locationToConsider;
1274
1275 /* Figure out how big a word is. By default, it's the column width, unless we have no columns, in which case it's the bytes per line. */
1276 NSUInteger wordGranularity = [self bytesPerColumn];
1277 if (wordGranularity == 0) wordGranularity = MAX(1u, [self bytesPerLine]);
1278 if (selectionAnchor == NO_SELECTION) {
1279 /* Pick the anchor inline with the choice of direction */
1280 if (direction == HFControllerDirectionLeft) locationToConsider = [self _minimumSelectionLocation];
1281 else locationToConsider = [self _maximumSelectionLocation];
1282 } else {
1283 /* Just use the anchor */
1284 locationToConsider = selectionAnchor;
1285 }
1286 if (direction == HFControllerDirectionRight) {
1287 result = HFRoundUpToNextMultipleSaturate(locationToConsider, wordGranularity) - locationToConsider;
1288 } else {
1289 result = locationToConsider % wordGranularity;
1290 if (result == 0) result = wordGranularity;
1291 }
1292 return result;
1293
1294 }
1295
1296 /* Anchored selection is not allowed; neither is up/down movement */
1297 - (void)_shiftSelectionInDirection:(HFControllerMovementDirection)direction byAmount:(unsigned long long)amountToMove {
1298 HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
1299 HFASSERT(selectionAnchor == NO_SELECTION);
1300 NSUInteger i, max = [selectedContentsRanges count];
1301 const unsigned long long maxLength = [self contentsLength];
1302 NSMutableArray *newRanges = [NSMutableArray arrayWithCapacity:max];
1303 BOOL hasAddedNonemptyRange = NO;
1304 for (i=0; i < max; i++) {
1305 HFRange range = [selectedContentsRanges[i] HFRange];
1306 HFASSERT(range.location <= maxLength && HFMaxRange(range) <= maxLength);
1307 if (direction == HFControllerDirectionRight) {
1308 unsigned long long offset = MIN(maxLength - range.location, amountToMove);
1309 unsigned long long lengthToSubtract = MIN(range.length, amountToMove - offset);
1310 range.location += offset;
1311 range.length -= lengthToSubtract;
1312 }
1313 else { /* direction == HFControllerDirectionLeft */
1314 unsigned long long negOffset = MIN(amountToMove, range.location);
1315 unsigned long long lengthToSubtract = MIN(range.length, amountToMove - negOffset);
1316 range.location -= negOffset;
1317 range.length -= lengthToSubtract;
1318 }
1319 [newRanges addObject:[HFRangeWrapper withRange:range]];
1320 hasAddedNonemptyRange = hasAddedNonemptyRange || (range.length > 0);
1321 }
1322
1323 newRanges = [[[HFRangeWrapper organizeAndMergeRanges:newRanges] mutableCopy] autorelease];
1324
1325 BOOL hasFoundEmptyRange = NO;
1326 max = [newRanges count];
1327 for (i=0; i < max; i++) {
1328 HFRange range = [newRanges[i] HFRange];
1329 if (range.length == 0) {
1330 if (hasFoundEmptyRange || hasAddedNonemptyRange) {
1331 [newRanges removeObjectAtIndex:i];
1332 i--;
1333 max--;
1334 }
1335 hasFoundEmptyRange = YES;
1336 }
1337 }
1338 [selectedContentsRanges setArray:newRanges];
1339 VALIDATE_SELECTION();
1340 [self _addPropertyChangeBits:HFControllerSelectedRanges];
1341 }
1342
1343 __attribute__((unused))
1344 static BOOL rangesAreInAscendingOrder(NSEnumerator *rangeEnumerator) {
1345 unsigned long long index = 0;
1346 HFRangeWrapper *rangeWrapper;
1347 while ((rangeWrapper = [rangeEnumerator nextObject])) {
1348 HFRange range = [rangeWrapper HFRange];
1349 if (range.location < index) return NO;
1350 index = HFSum(range.location, range.length);
1351 }
1352 return YES;
1353 }
1354
1355 - (BOOL)_registerCondemnedRangesForUndo:(NSArray *)ranges selectingRangesAfterUndo:(BOOL)selectAfterUndo {
1356 HFASSERT(ranges != NULL);
1357 HFASSERT(ranges != selectedContentsRanges); //selectedContentsRanges is mutable - we really don't want to stash it away with undo
1358 BOOL result = NO;
1359 NSUndoManager *manager = [self undoManager];
1360 NSUInteger rangeCount = [ranges count];
1361 if (! manager || ! rangeCount) return NO;
1362
1363 HFASSERT(rangesAreInAscendingOrder([ranges objectEnumerator]));
1364
1365 NSMutableArray *rangesToRestore = [NSMutableArray arrayWithCapacity:rangeCount];
1366 NSMutableArray *correspondingByteArrays = [NSMutableArray arrayWithCapacity:rangeCount];
1367 HFByteArray *bytes = [self byteArray];
1368
1369 /* Enumerate the ranges in forward order so when we insert them, we insert later ranges before earlier ones, so we don't have to worry about shifting indexes */
1370 FOREACH(HFRangeWrapper *, rangeWrapper, ranges) {
1371 HFRange range = [rangeWrapper HFRange];
1372 if (range.length > 0) {
1373 [rangesToRestore addObject:[HFRangeWrapper withRange:HFRangeMake(range.location, 0)]];
1374 [correspondingByteArrays addObject:[bytes subarrayWithRange:range]];
1375 result = YES;
1376 }
1377 }
1378
1379 if (result) [self _registerUndoOperationForInsertingByteArrays:correspondingByteArrays inRanges:rangesToRestore withSelectionAction:(selectAfterUndo ? eSelectResult : eSelectAfterResult)];
1380 return result;
1381 }
1382
1383 - (void)_commandDeleteRanges:(NSArray *)rangesToDelete {
1384 HFASSERT(rangesToDelete != selectedContentsRanges); //selectedContentsRanges is mutable - we really don't want to stash it away with undo
1385 HFASSERT(rangesAreInAscendingOrder([rangesToDelete objectEnumerator]));
1386
1387 /* Delete all the selection - in reverse order */
1388 unsigned long long minSelection = ULLONG_MAX;
1389 BOOL somethingWasDeleted = NO;
1390 [self _registerCondemnedRangesForUndo:rangesToDelete selectingRangesAfterUndo:YES];
1391 NSUInteger rangeIndex = [rangesToDelete count];
1392 HFASSERT(rangeIndex > 0);
1393 while (rangeIndex--) {
1394 HFRange range = [rangesToDelete[rangeIndex] HFRange];
1395 minSelection = llmin(range.location, minSelection);
1396 if (range.length > 0) {
1397 [byteArray deleteBytesInRange:range];
1398 somethingWasDeleted = YES;
1399 }
1400 }
1401
1402 HFASSERT(minSelection != ULLONG_MAX);
1403 if (somethingWasDeleted) {
1404 BEGIN_TRANSACTION();
1405 [self _addPropertyChangeBits:HFControllerContentValue | HFControllerContentLength];
1406 [self _setSingleSelectedContentsRange:HFRangeMake(minSelection, 0)];
1407 [self _updateDisplayedRange];
1408 END_TRANSACTION();
1409 }
1410 else {
1411 NSBeep();
1412 }
1413 }
1414
1415 - (void)_commandInsertByteArrays:(NSArray *)byteArrays inRanges:(NSArray *)ranges withSelectionAction:(HFControllerSelectAction)selectionAction {
1416 HFASSERT(selectionAction < NUM_SELECTION_ACTIONS);
1417 REQUIRE_NOT_NULL(byteArrays);
1418 REQUIRE_NOT_NULL(ranges);
1419 HFASSERT([ranges count] == [byteArrays count]);
1420 NSUInteger index, max = [ranges count];
1421 HFByteArray *bytes = [self byteArray];
1422 HFASSERT(rangesAreInAscendingOrder([ranges objectEnumerator]));
1423
1424 NSMutableArray *byteArraysToInsertOnUndo = [NSMutableArray arrayWithCapacity:max];
1425 NSMutableArray *rangesToInsertOnUndo = [NSMutableArray arrayWithCapacity:max];
1426
1427 BEGIN_TRANSACTION();
1428 if (selectionAction == eSelectResult || selectionAction == eSelectAfterResult) {
1429 [selectedContentsRanges removeAllObjects];
1430 }
1431 unsigned long long endOfInsertedRanges = ULLONG_MAX;
1432 for (index = 0; index < max; index++) {
1433 HFRange range = [ranges[index] HFRange];
1434 HFByteArray *oldBytes = [bytes subarrayWithRange:range];
1435 [byteArraysToInsertOnUndo addObject:oldBytes];
1436 HFByteArray *newBytes = byteArrays[index];
1437 EXPECT_CLASS(newBytes, [HFByteArray class]);
1438 [bytes insertByteArray:newBytes inRange:range];
1439 HFRange insertedRange = HFRangeMake(range.location, [newBytes length]);
1440 HFRangeWrapper *insertedRangeWrapper = [HFRangeWrapper withRange:insertedRange];
1441 [rangesToInsertOnUndo addObject:insertedRangeWrapper];
1442 if (selectionAction == eSelectResult) {
1443 [selectedContentsRanges addObject:insertedRangeWrapper];
1444 }
1445 else {
1446 endOfInsertedRanges = HFMaxRange(insertedRange);
1447 }
1448 }
1449 if (selectionAction == eSelectAfterResult) {
1450 HFASSERT([ranges count] > 0);
1451 [selectedContentsRanges addObject:[HFRangeWrapper withRange:HFRangeMake(endOfInsertedRanges, 0)]];
1452 }
1453
1454 if (selectionAction == ePreserveSelection) {
1455 HFASSERT([selectedContentsRanges count] > 0);
1456 [self _clipSelectedContentsRangesToLength:[self contentsLength]];
1457 }
1458
1459 VALIDATE_SELECTION();
1460 HFASSERT([byteArraysToInsertOnUndo count] == [rangesToInsertOnUndo count]);
1461 [self _registerUndoOperationForInsertingByteArrays:byteArraysToInsertOnUndo inRanges:rangesToInsertOnUndo withSelectionAction:(selectionAction == ePreserveSelection ? ePreserveSelection : eSelectAfterResult)];
1462 [self _updateDisplayedRange];
1463 [self maximizeVisibilityOfContentsRange:[selectedContentsRanges[0] HFRange]];
1464 [self _addPropertyChangeBits:HFControllerContentValue | HFControllerContentLength | HFControllerSelectedRanges];
1465 END_TRANSACTION();
1466 }
1467
1468 /* The user has hit undo after typing a string. */
1469 - (void)_commandReplaceBytesAfterBytesFromBeginning:(unsigned long long)leftOffset upToBytesFromEnd:(unsigned long long)rightOffset withByteArray:(HFByteArray *)bytesToReinsert {
1470 HFASSERT(bytesToReinsert != NULL);
1471
1472 BEGIN_TRANSACTION();
1473 HFByteArray *bytes = [self byteArray];
1474 unsigned long long contentsLength = [self contentsLength];
1475 HFASSERT(leftOffset <= contentsLength);
1476 HFASSERT(rightOffset <= contentsLength);
1477 HFASSERT(contentsLength - rightOffset >= leftOffset);
1478 HFRange rangeToReplace = HFRangeMake(leftOffset, contentsLength - rightOffset - leftOffset);
1479 [self _registerCondemnedRangesForUndo:[HFRangeWrapper withRanges:&rangeToReplace count:1] selectingRangesAfterUndo:NO];
1480 [bytes insertByteArray:bytesToReinsert inRange:rangeToReplace];
1481 [self _updateDisplayedRange];
1482 [self _setSingleSelectedContentsRange:HFRangeMake(rangeToReplace.location, [bytesToReinsert length])];
1483 [self _addPropertyChangeBits:HFControllerContentValue | HFControllerContentLength | HFControllerSelectedRanges];
1484 END_TRANSACTION();
1485 }
1486
1487 /* We use NSNumbers instead of long longs here because Tiger/PPC NSInvocation had trouble with long longs */
1488 - (void)_commandValueObjectsReplaceBytesAfterBytesFromBeginning:(NSNumber *)leftOffset upToBytesFromEnd:(NSNumber *)rightOffset withByteArray:(HFByteArray *)bytesToReinsert {
1489 HFASSERT(leftOffset != NULL);
1490 HFASSERT(rightOffset != NULL);
1491 EXPECT_CLASS(leftOffset, NSNumber);
1492 EXPECT_CLASS(rightOffset, NSNumber);
1493 [self _commandReplaceBytesAfterBytesFromBeginning:[leftOffset unsignedLongLongValue] upToBytesFromEnd:[rightOffset unsignedLongLongValue] withByteArray:bytesToReinsert];
1494 }
1495
1496 - (void)moveInDirection:(HFControllerMovementDirection)direction byByteCount:(unsigned long long)amountToMove withSelectionTransformation:(HFControllerSelectionTransformation)transformation usingAnchor:(BOOL)useAnchor {
1497 if (! useAnchor) selectionAnchor = NO_SELECTION;
1498 switch (transformation) {
1499 case HFControllerDiscardSelection:
1500 [self _moveDirectionDiscardingSelection:direction byAmount:amountToMove];
1501 break;
1502
1503 case HFControllerShiftSelection:
1504 [self _shiftSelectionInDirection:direction byAmount:amountToMove];
1505 break;
1506
1507 case HFControllerExtendSelection:
1508 [self _extendSelectionInDirection:direction byAmount:amountToMove];
1509 break;
1510
1511 default:
1512 [NSException raise:NSInvalidArgumentException format:@"Invalid transformation %ld", (long)transformation];
1513 break;
1514 }
1515 if (! useAnchor) selectionAnchor = NO_SELECTION;
1516 }
1517
1518 - (void)moveInDirection:(HFControllerMovementDirection)direction withGranularity:(HFControllerMovementGranularity)granularity andModifySelection:(BOOL)extendSelection {
1519 HFASSERT(granularity == HFControllerMovementByte || granularity == HFControllerMovementColumn || granularity == HFControllerMovementLine || granularity == HFControllerMovementPage || granularity == HFControllerMovementDocument);
1520 HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
1521 unsigned long long bytesToMove = 0;
1522 switch (granularity) {
1523 case HFControllerMovementByte:
1524 bytesToMove = 1;
1525 break;
1526 case HFControllerMovementColumn:
1527 /* This is a tricky case because the amount we have to move depends on our position in the column. */
1528 bytesToMove = [self _distanceToWordBoundaryForDirection:direction];
1529 break;
1530 case HFControllerMovementLine:
1531 bytesToMove = [self bytesPerLine];
1532 break;
1533 case HFControllerMovementPage:
1534 bytesToMove = HFProductULL([self bytesPerLine], HFFPToUL(MIN(floorl([self displayedLineRange].length), 1.)));
1535 break;
1536 case HFControllerMovementDocument:
1537 bytesToMove = [self contentsLength];
1538 break;
1539 }
1540 HFControllerSelectionTransformation transformation = (extendSelection ? HFControllerExtendSelection : HFControllerDiscardSelection);
1541 [self moveInDirection:direction byByteCount:bytesToMove withSelectionTransformation:transformation usingAnchor:YES];
1542 }
1543
1544 - (void)moveToLineBoundaryInDirection:(HFControllerMovementDirection)direction andModifySelection:(BOOL)modifySelection {
1545 HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
1546 BEGIN_TRANSACTION();
1547 unsigned long long locationToMakeVisible;
1548 HFRange additionalSelection;
1549
1550 if (direction == HFControllerDirectionLeft) {
1551 /* If we are at the beginning of a line, this should be a no-op */
1552 unsigned long long minLocation = [self _minimumSelectionLocation];
1553 unsigned long long newMinLocation = (minLocation / bytesPerLine) * bytesPerLine;
1554 locationToMakeVisible = newMinLocation;
1555 additionalSelection = HFRangeMake(newMinLocation, minLocation - newMinLocation);
1556 }
1557 else {
1558 /* This always advances to the next line */
1559 unsigned long long maxLocation = [self _maximumSelectionLocation];
1560 unsigned long long proposedNewMaxLocation = HFRoundUpToNextMultipleSaturate(maxLocation, bytesPerLine);
1561 unsigned long long newMaxLocation = MIN([self contentsLength], proposedNewMaxLocation);
1562 HFASSERT(newMaxLocation >= maxLocation);
1563 locationToMakeVisible = newMaxLocation;
1564 additionalSelection = HFRangeMake(maxLocation, newMaxLocation - maxLocation);
1565 }
1566
1567 if (modifySelection) {
1568 if (additionalSelection.length > 0) {
1569 [self _addRangeToSelection:additionalSelection];
1570 [self _addPropertyChangeBits:HFControllerSelectedRanges];
1571 }
1572 }
1573 else {
1574 [self _setSingleSelectedContentsRange:HFRangeMake(locationToMakeVisible, 0)];
1575 }
1576 [self _ensureVisibilityOfLocation:locationToMakeVisible];
1577 END_TRANSACTION();
1578 }
1579
1580 - (void)deleteSelection {
1581 if ([self editMode] == HFOverwriteMode || ! [self editable]) {
1582 NSBeep();
1583 }
1584 else {
1585 [self _commandDeleteRanges:[HFRangeWrapper organizeAndMergeRanges:selectedContentsRanges]];
1586 }
1587 }
1588
1589 // Called after Replace All is finished.
1590 - (void)replaceByteArray:(HFByteArray *)newArray {
1591 REQUIRE_NOT_NULL(newArray);
1592 EXPECT_CLASS(newArray, HFByteArray);
1593 HFRange entireRange = HFRangeMake(0, [self contentsLength]);
1594 if ([self editMode] == HFOverwriteMode && [newArray length] != entireRange.length) {
1595 NSBeep();
1596 }
1597 else {
1598 [self _commandInsertByteArrays:@[newArray] inRanges:[HFRangeWrapper withRanges:&entireRange count:1] withSelectionAction:ePreserveSelection];
1599 }
1600 }
1601
1602 - (BOOL)insertData:(NSData *)data replacingPreviousBytes:(unsigned long long)previousBytes allowUndoCoalescing:(BOOL)allowUndoCoalescing {
1603 REQUIRE_NOT_NULL(data);
1604 BOOL result;
1605 #if ! NDEBUG
1606 const unsigned long long startLength = [byteArray length];
1607 unsigned long long expectedNewLength;
1608 if ([self editMode] == HFOverwriteMode) {
1609 expectedNewLength = startLength;
1610 }
1611 else {
1612 expectedNewLength = startLength + [data length] - previousBytes;
1613 FOREACH(HFRangeWrapper*, wrapper, [self selectedContentsRanges]) expectedNewLength -= [wrapper HFRange].length;
1614 }
1615 #endif
1616 HFByteSlice *slice = [[HFSharedMemoryByteSlice alloc] initWithUnsharedData:data];
1617 HFASSERT([slice length] == [data length]);
1618 HFByteArray *array = [[preferredByteArrayClass() alloc] init];
1619 [array insertByteSlice:slice inRange:HFRangeMake(0, 0)];
1620 HFASSERT([array length] == [data length]);
1621 result = [self insertByteArray:array replacingPreviousBytes:previousBytes allowUndoCoalescing:allowUndoCoalescing];
1622 [slice release];
1623 [array release];
1624 #if ! NDEBUG
1625 HFASSERT((result && [byteArray length] == expectedNewLength) || (! result && [byteArray length] == startLength));
1626 #endif
1627 return result;
1628 }
1629
1630 - (BOOL)_insertionModeCoreInsertByteArray:(HFByteArray *)bytesToInsert replacingPreviousBytes:(unsigned long long)previousBytes allowUndoCoalescing:(BOOL)allowUndoCoalescing outNewSingleSelectedRange:(HFRange *)outSelectedRange {
1631 HFASSERT([self editMode] == HFInsertMode);
1632 REQUIRE_NOT_NULL(bytesToInsert);
1633
1634 /* Guard against overflow. If [bytesToInsert length] + [self contentsLength] - previousBytes overflows, then we can't do it */
1635 HFASSERT([self contentsLength] >= previousBytes);
1636 if (! HFSumDoesNotOverflow([bytesToInsert length], [self contentsLength] - previousBytes)) {
1637 return NO; //don't do anything
1638 }
1639
1640
1641 unsigned long long amountDeleted = 0, amountAdded = [bytesToInsert length];
1642 HFByteArray *bytes = [self byteArray];
1643
1644 /* Delete all the selection - in reverse order - except the last (really first) one, which we will overwrite. */
1645 NSArray *allRangesToRemove = [HFRangeWrapper organizeAndMergeRanges:[self selectedContentsRanges]];
1646 HFRange rangeToReplace = [allRangesToRemove[0] HFRange];
1647 HFASSERT(rangeToReplace.location == [self _minimumSelectionLocation]);
1648 NSUInteger rangeIndex, rangeCount = [allRangesToRemove count];
1649 HFASSERT(rangeCount > 0);
1650 NSMutableArray *rangesToDelete = [NSMutableArray arrayWithCapacity:rangeCount - 1];
1651 for (rangeIndex = rangeCount - 1; rangeIndex > 0; rangeIndex--) {
1652 HFRangeWrapper *rangeWrapper = allRangesToRemove[rangeIndex];
1653 HFRange range = [rangeWrapper HFRange];
1654 if (range.length > 0) {
1655 amountDeleted = HFSum(amountDeleted, range.length);
1656 [rangesToDelete insertObject:rangeWrapper atIndex:0];
1657 }
1658 }
1659
1660 if ([rangesToDelete count] > 0) {
1661 HFASSERT(rangesAreInAscendingOrder([rangesToDelete objectEnumerator]));
1662 /* TODO: This is problematic because it overwrites the selection that gets set by _activateTypingUndoCoalescingForReplacingRange:, so we lose the first selection in a multiple selection scenario. */
1663 [self _registerCondemnedRangesForUndo:rangesToDelete selectingRangesAfterUndo:YES];
1664 NSEnumerator *enumer = [rangesToDelete reverseObjectEnumerator];
1665 HFRangeWrapper *rangeWrapper;
1666 while ((rangeWrapper = [enumer nextObject])) {
1667 [bytes deleteBytesInRange:[rangeWrapper HFRange]];
1668 }
1669 }
1670
1671 rangeToReplace.length = HFSum(rangeToReplace.length, previousBytes);
1672
1673 /* Insert data */
1674 #if ! NDEBUG
1675 unsigned long long expectedLength = [byteArray length] + [bytesToInsert length] - rangeToReplace.length;
1676 #endif
1677 [byteArray insertByteArray:bytesToInsert inRange:rangeToReplace];
1678 #if ! NDEBUG
1679 HFASSERT(expectedLength == [byteArray length]);
1680 #endif
1681
1682 /* return the new selected range */
1683 *outSelectedRange = HFRangeMake(HFSum(rangeToReplace.location, amountAdded), 0);
1684 return YES;
1685 }
1686
1687
1688 - (BOOL)_overwriteModeCoreInsertByteArray:(HFByteArray *)bytesToInsert replacingPreviousBytes:(unsigned long long)previousBytes allowUndoCoalescing:(BOOL)allowUndoCoalescing outRangeToRemoveFromSelection:(HFRange *)outRangeToRemove {
1689 REQUIRE_NOT_NULL(bytesToInsert);
1690 const unsigned long long byteArrayLength = [byteArray length];
1691 const unsigned long long bytesToInsertLength = [bytesToInsert length];
1692 HFRange firstSelectedRange = [selectedContentsRanges[0] HFRange];
1693 HFRange proposedRangeToOverwrite = HFRangeMake(firstSelectedRange.location, bytesToInsertLength);
1694 HFASSERT(proposedRangeToOverwrite.location >= previousBytes);
1695 proposedRangeToOverwrite.location -= previousBytes;
1696 if (! HFRangeIsSubrangeOfRange(proposedRangeToOverwrite, HFRangeMake(0, byteArrayLength))) {
1697 /* The user tried to overwrite past the end */
1698 NSBeep();
1699 return NO;
1700 }
1701
1702 [byteArray insertByteArray:bytesToInsert inRange:proposedRangeToOverwrite];
1703
1704 *outRangeToRemove = proposedRangeToOverwrite;
1705 return YES;
1706 }
1707
1708 - (BOOL)insertByteArray:(HFByteArray *)bytesToInsert replacingPreviousBytes:(unsigned long long)previousBytes allowUndoCoalescing:(BOOL)allowUndoCoalescing {
1709 #if ! NDEBUG
1710 if (previousBytes > 0) {
1711 NSArray *selectedRanges = [self selectedContentsRanges];
1712 HFASSERT([selectedRanges count] == 1);
1713 HFRange selectedRange = [selectedRanges[0] HFRange];
1714 HFASSERT(selectedRange.location >= previousBytes); //don't try to delete more trailing bytes than we actually have!
1715 }
1716 #endif
1717 REQUIRE_NOT_NULL(bytesToInsert);
1718
1719
1720 BEGIN_TRANSACTION();
1721 unsigned long long beforeLength = [byteArray length];
1722 BOOL inOverwriteMode = [self editMode] == HFOverwriteMode;
1723 HFRange modificationRange; //either range to remove from selection if in overwrite mode, or range to select if not
1724 BOOL success;
1725 if (inOverwriteMode) {
1726 success = [self _overwriteModeCoreInsertByteArray:bytesToInsert replacingPreviousBytes:previousBytes allowUndoCoalescing:allowUndoCoalescing outRangeToRemoveFromSelection:&modificationRange];
1727 }
1728 else {
1729 success = [self _insertionModeCoreInsertByteArray:bytesToInsert replacingPreviousBytes:previousBytes allowUndoCoalescing:allowUndoCoalescing outNewSingleSelectedRange:&modificationRange];
1730 }
1731
1732 if (success) {
1733 /* Update our selection */
1734 [self _addPropertyChangeBits:HFControllerContentValue];
1735 [self _updateDisplayedRange];
1736 [self _addPropertyChangeBits:HFControllerContentValue];
1737 if (inOverwriteMode) {
1738 [self _removeRangeFromSelection:modificationRange withCursorLocationIfAllSelectionRemoved:HFMaxRange(modificationRange)];
1739 [self maximizeVisibilityOfContentsRange:[selectedContentsRanges[0] HFRange]];
1740 }
1741 else {
1742 [self _setSingleSelectedContentsRange:modificationRange];
1743 [self maximizeVisibilityOfContentsRange:modificationRange];
1744 }
1745 if (beforeLength != [byteArray length]) [self _addPropertyChangeBits:HFControllerContentLength];
1746 }
1747 END_TRANSACTION();
1748 return success;
1749 }
1750
1751 - (void)deleteDirection:(HFControllerMovementDirection)direction {
1752 HFASSERT(direction == HFControllerDirectionLeft || direction == HFControllerDirectionRight);
1753 if ([self editMode] != HFInsertMode || ! [self editable]) {
1754 NSBeep();
1755 return;
1756 }
1757 unsigned long long minSelection = [self _minimumSelectionLocation];
1758 unsigned long long maxSelection = [self _maximumSelectionLocation];
1759 if (maxSelection != minSelection) {
1760 [self deleteSelection];
1761 }
1762 else {
1763 HFRange rangeToDelete = HFRangeMake(minSelection, 1);
1764 BOOL rangeIsValid;
1765 if (direction == HFControllerDirectionLeft) {
1766 rangeIsValid = (rangeToDelete.location > 0);
1767 rangeToDelete.location--;
1768 }
1769 else {
1770 rangeIsValid = (rangeToDelete.location < [self contentsLength]);
1771 }
1772 if (rangeIsValid) {
1773 BEGIN_TRANSACTION();
1774 [byteArray deleteBytesInRange:rangeToDelete];
1775 [self _setSingleSelectedContentsRange:HFRangeMake(rangeToDelete.location, 0)];
1776 [self _updateDisplayedRange];
1777 [self _addPropertyChangeBits:HFControllerSelectedRanges | HFControllerContentValue | HFControllerContentLength];
1778 END_TRANSACTION();
1779 }
1780 }
1781 }
1782
1783 - (HFEditMode)editMode {
1784 return _hfflags.editMode;
1785 }
1786
1787 - (void)setEditMode:(HFEditMode)val
1788 {
1789 if (val != _hfflags.editMode) {
1790 _hfflags.editMode = val;
1791 // don't allow undo coalescing when switching modes
1792 [self _addPropertyChangeBits:HFControllerEditable];
1793 }
1794 }
1795
1796 - (void)reloadData {
1797 BEGIN_TRANSACTION();
1798 [cachedData release];
1799 cachedData = nil;
1800 [self _updateDisplayedRange];
1801 [self _addPropertyChangeBits: HFControllerContentValue | HFControllerContentLength];
1802 END_TRANSACTION();
1803 }
1804
1805 #if BENCHMARK_BYTEARRAYS
1806
1807 + (void)_testByteArray {
1808 HFByteArray* first = [[[HFFullMemoryByteArray alloc] init] autorelease];
1809 HFBTreeByteArray* second = [[[HFBTreeByteArray alloc] init] autorelease];
1810 first = nil;
1811 // second = nil;
1812
1813 //srandom(time(NULL));
1814
1815 unsigned opCount = 4096 * 512;
1816 unsigned long long expectedLength = 0;
1817 unsigned i;
1818 for (i=1; i <= opCount; i++) {
1819 @autoreleasepool {
1820 NSUInteger op;
1821 const unsigned long long length = [first length];
1822 unsigned long long offset;
1823 unsigned long long number;
1824 switch ((op = (random()%2))) {
1825 case 0: { //insert
1826 offset = random() % (1 + length);
1827 HFByteSlice* slice = [[HFRandomDataByteSlice alloc] initWithRandomDataLength: 1 + random() % 1000];
1828 [first insertByteSlice:slice inRange:HFRangeMake(offset, 0)];
1829 [second insertByteSlice:slice inRange:HFRangeMake(offset, 0)];
1830 expectedLength += [slice length];
1831 [slice release];
1832 break;
1833 }
1834 case 1: { //delete
1835 if (length > 0) {
1836 offset = random() % length;
1837 number = 1 + random() % (length - offset);
1838 [first deleteBytesInRange:HFRangeMake(offset, number)];
1839 [second deleteBytesInRange:HFRangeMake(offset, number)];
1840 expectedLength -= number;
1841 }
1842 break;
1843 }
1844 }
1845 } // @autoreleasepool
1846 }
1847 }
1848
1849 + (void)_testAttributeArrays {
1850 HFByteRangeAttributeArray *naiveTree = [[HFNaiveByteRangeAttributeArray alloc] init];
1851 HFAnnotatedTreeByteRangeAttributeArray *smartTree = [[HFAnnotatedTreeByteRangeAttributeArray alloc] init];
1852 naiveTree = nil;
1853 // smartTree = nil;
1854
1855 NSString * const attributes[3] = {@"Alpha", @"Beta", @"Gamma"};
1856
1857 const NSUInteger supportedIndexEnd = NSNotFound;
1858 NSUInteger round;
1859 for (round = 0; round < 4096 * 256; round++) {
1860 NSString *attribute = attributes[random() % (sizeof attributes / sizeof *attributes)];
1861 BOOL insert = ([smartTree isEmpty] || [naiveTree isEmpty] || (random() % 2));
1862
1863 unsigned long long end = random();
1864 unsigned long long start = random();
1865 if (end < start) {
1866 unsigned long long temp = end;
1867 end = start;
1868 start = temp;
1869 }
1870 HFRange range = HFRangeMake(start, end - start);
1871
1872 if (insert) {
1873 [naiveTree addAttribute:attribute range:range];
1874 [smartTree addAttribute:attribute range:range];
1875 }
1876 else {
1877 [naiveTree removeAttribute:attribute range:range];
1878 [smartTree removeAttribute:attribute range:range];
1879 }
1880 }
1881
1882 [naiveTree release];
1883 [smartTree release];
1884 }
1885
1886
1887 + (void)initialize {
1888 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
1889 srandom(0);
1890 [self _testByteArray];
1891 CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
1892 printf("Byte array time: %f\n", end - start);
1893
1894 srandom(0);
1895 start = CFAbsoluteTimeGetCurrent();
1896 [self _testAttributeArrays];
1897 end = CFAbsoluteTimeGetCurrent();
1898 printf("Attribute array time: %f\n", end - start);
1899
1900 exit(0);
1901 }
1902
1903 #endif
1904
1905 @end
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.