The R Project SVN R-packages

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6128 bibiko 1
/*
2
 *  R.app : a Cocoa front end to: "R A Computer Language for Statistical Data Analysis"
3
 *  
4
 *  R.app Copyright notes:
5
 *                     Copyright (C) 2004-12  The R Foundation
6
 *                     written by Stefano M. Iacus and Simon Urbanek
7
 *
8
 *                  
9
 *  R Copyright notes:
10
 *                     Copyright (C) 1995-1996   Robert Gentleman and Ross Ihaka
11
 *                     Copyright (C) 1998-2001   The R Development Core Team
12
 *                     Copyright (C) 2002-2004   The R Foundation
13
 *
14
 *  This program is free software; you can redistribute it and/or modify
15
 *  it under the terms of the GNU General Public License as published by
16
 *  the Free Software Foundation; either version 2 of the License, or
17
 *  (at your option) any later version.
18
 *
19
 *  This program is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  A copy of the GNU General Public License is available via WWW at
25
 *  http://www.gnu.org/copyleft/gpl.html.  You can also obtain it by
26
 *  writing to the Free Software Foundation, Inc., 59 Temple Place,
27
 *  Suite 330, Boston, MA  02111-1307  USA.
28
 *
29
 *  RScriptEditorTextStorage.m
30
 *
31
 *  Created by Hans-J. Bibiko on 01/03/2012.
32
 *
33
 */
34
 
35
#import "RScriptEditorTextStorage.h"
36
#import "RScriptEditorTypesetter.h"
6238 bibiko 37
#import "RScriptEditorTextView.h"
6128 bibiko 38
#import "FoldingSignTextAttachmentCell.h"
6238 bibiko 39
#import "RGUI.h"
6128 bibiko 40
 
41
@implementation RScriptEditorTextStorage
42
 
43
static NSTextAttachment *sharedAttachment = nil;
6137 bibiko 44
static SEL _getSel;
45
static SEL _setSel;
6232 bibiko 46
static SEL _strSel;
47
static SEL _replSel;
48
static SEL _editSel;
6233 bibiko 49
static SEL _getlSel;
6128 bibiko 50
 
51
+ (void)initialize
52
{
53
 
54
	if ([self class] == [RScriptEditorTextStorage class]) {
55
		FoldingSignTextAttachmentCell *cell = [[FoldingSignTextAttachmentCell alloc] initImageCell:nil];
56
		sharedAttachment = [[NSTextAttachment alloc] init];
57
		[sharedAttachment setAttachmentCell:cell];
58
		[cell release];
6232 bibiko 59
		_getSel  = @selector(attributesAtIndex:effectiveRange:);
60
		_setSel  = @selector(setAttributes:range:);
61
		_strSel  = @selector(string);
62
		_replSel = @selector(replaceCharactersInRange:withString:);
63
		_editSel = @selector(edited:range:changeInLength:);
6233 bibiko 64
		_getlSel = @selector(attribute:atIndex:longestEffectiveRange:inRange:);
65
 
6128 bibiko 66
	}
67
}
68
 
69
+ (NSTextAttachment *)attachment
70
{
71
	return sharedAttachment;
72
}
73
 
6249 bibiko 74
- (id)initWithDelegate:(id)theDelegate
6128 bibiko 75
{
76
	self = [super init];
77
 
78
	if (self != nil) {
79
		_attributedString = [[NSTextStorage alloc] init];
6232 bibiko 80
		_getImp  = [_attributedString methodForSelector:_getSel];
81
		_setImp  = [_attributedString methodForSelector:_setSel];
82
		_strImp  = [_attributedString methodForSelector:_strSel];
83
		_replImp = [_attributedString methodForSelector:_replSel];
6233 bibiko 84
		_editImp = [self methodForSelector:_editSel];
85
		_getlImp = [_attributedString methodForSelector:_getlSel];
86
 
6249 bibiko 87
		selfDelegate = (RScriptEditorTextView*)theDelegate;
88
		[self setDelegate:theDelegate];
6233 bibiko 89
		foldedCounter = 0;
6238 bibiko 90
		currentMaxFoldedIndex = -1;
6233 bibiko 91
 
92
		for(NSInteger i = 0; i < R_MAX_FOLDED_ITEMS; i++) {
93
			foldedRanges[i][0] = -1;
94
			foldedRanges[i][1] = 0;
6238 bibiko 95
			foldedRanges[i][2] = 0;
6233 bibiko 96
		}
6128 bibiko 97
	}
98
 
99
	return self;
100
}
101
 
102
- (void)dealloc
103
{
104
	[[NSNotificationCenter defaultCenter] removeObserver:self];
105
	[_attributedString release];
6232 bibiko 106
	if(sharedAttachment) [sharedAttachment release];
6128 bibiko 107
	[super dealloc];
108
}
109
 
6233 bibiko 110
- (BOOL)hasFoldedItems
111
{
112
	return (foldedCounter == 0) ? NO : YES;
113
}
114
 
6238 bibiko 115
- (BOOL)inFoldedRangeForRange:(NSRange)range
116
{
117
	if(!foldedCounter) return NO;
118
 
119
	NSInteger i = 0;
120
	NSInteger maxRange = NSMaxRange(range);
121
	NSInteger rangeLoc = range.location;
122
	for(i = 0; i < currentMaxFoldedIndex+1; i++) {
123
		if(rangeLoc > foldedRanges[i][0] && maxRange <= foldedRanges[i][2]) {
124
			return YES;
125
		}
126
	}
127
	return NO;
128
 
129
}
130
 
6233 bibiko 131
- (NSInteger)foldedAtIndex:(NSInteger)index
132
{
133
 
134
	if(!foldedCounter) return -1;
135
 
136
	NSInteger i = 0;
6238 bibiko 137
	for(i = 0; i < currentMaxFoldedIndex+1; i++) {
138
		if(foldedRanges[i][2] > index && foldedRanges[i][0] <= index) {
6233 bibiko 139
			return i;
140
		}
141
	}
142
	return -1;
143
}
144
 
145
- (NSInteger)foldedForIndicatorAtIndex:(NSInteger)index
146
{
147
 
148
	if(!foldedCounter) return -1;
149
 
150
	if(index) {
151
		// Folded ranges are stored from { to } but indicator will drawn inside of { and }
6238 bibiko 152
		NSInteger adjIndex = index + 1;
153
		for(NSInteger i = 0; i < currentMaxFoldedIndex+1; i++) {
154
			if(foldedRanges[i][2] > adjIndex && foldedRanges[i][0] < index) {
6233 bibiko 155
				return i;
156
			}
157
		}
158
	}
159
	return -1;
160
}
161
 
162
- (NSInteger)registerFoldedRange:(NSRange)range
163
{
164
 
6238 bibiko 165
 
6248 bibiko 166
	[[selfDelegate undoManager] disableUndoRegistration];
6238 bibiko 167
 
6233 bibiko 168
	NSInteger index = -1;
169
	for(NSInteger i = 0; i < R_MAX_FOLDED_ITEMS; i++) {
170
		if(foldedRanges[i][0] == -1) {
171
			index = i;
172
			foldedRanges[i][0] = (NSInteger)range.location;
173
			foldedRanges[i][1] = (NSInteger)range.length;
6238 bibiko 174
			foldedRanges[i][2] = (NSInteger)NSMaxRange(range);
6233 bibiko 175
			foldedCounter++;
6238 bibiko 176
			if(i > currentMaxFoldedIndex) currentMaxFoldedIndex = i;
177
			SLog(@"RScriptEditorTextStorage:registerFoldedRange %@ at position %d : max index %d", NSStringFromRange(range), i, currentMaxFoldedIndex);
6233 bibiko 178
			break;
179
		}
180
	}
181
 
6248 bibiko 182
	[[selfDelegate undoManager] enableUndoRegistration];
6238 bibiko 183
 
6233 bibiko 184
	return(index);
185
 
186
}
187
 
6238 bibiko 188
- (BOOL)removeFoldedRangeWithIndex:(NSInteger)index
6233 bibiko 189
{
190
 
6238 bibiko 191
	BOOL exists = NO;
192
	NSRange range;
193
 
6243 bibiko 194
	SLog(@"RScriptEditorTextStorage:removeFoldedRangeWithIndex %d", index);
6238 bibiko 195
 
196
	if(index > -1 && index < R_MAX_FOLDED_ITEMS) {
197
		if(foldedRanges[index][0] > -1 && foldedRanges[index][1] > 0) {
198
			range = NSMakeRange(foldedRanges[index][0], foldedRanges[index][1]);
199
			exists = YES;
200
		}
201
	}
6233 bibiko 202
 
6238 bibiko 203
	if(!exists) {
6233 bibiko 204
		[self removeAllFoldedRanges];
6238 bibiko 205
		NSLog(@"Removing folded text chunk failed. For safety reasons all folded chunks were be unfolded.");
6233 bibiko 206
		return NO;
207
	}
208
 
6248 bibiko 209
	[[selfDelegate undoManager] disableUndoRegistration];
6233 bibiko 210
 
6238 bibiko 211
	range = NSIntersectionRange(NSMakeRange(0, [[_attributedString string] length]), range);
6233 bibiko 212
	if(range.length) {
213
		[self removeAttribute:NSCursorAttributeName range:range];
214
		[self removeAttribute:NSToolTipAttributeName range:range];
215
	}
216
 
6238 bibiko 217
	foldedRanges[index][0] = -1;
218
	foldedRanges[index][1] = 0;
219
	foldedRanges[index][2] = 0;
6233 bibiko 220
	foldedCounter--;
221
	if(foldedCounter < 0) foldedCounter = 0;
222
 
6238 bibiko 223
	// check folded chunks inside range
224
	NSInteger rloc = range.location;
225
	NSInteger maxrlen = NSMaxRange(range);
226
	NSRange r;
227
	for(NSInteger j = currentMaxFoldedIndex; j >= 0; j--) {
228
		if(foldedRanges[j][0] > rloc && foldedRanges[j][2] < maxrlen) {
229
			r = NSMakeRange(foldedRanges[j][0], foldedRanges[j][1]);
230
			foldedRanges[j][0] = -1;
231
			foldedRanges[j][1] = 0;
232
			foldedRanges[j][2] = 0;
6248 bibiko 233
			[selfDelegate refoldLinesInRange:r];
6238 bibiko 234
		}
235
	}
236
 
6248 bibiko 237
	[[selfDelegate undoManager] enableUndoRegistration];
6238 bibiko 238
 
239
	// update currentMaxFoldedIndex
240
	NSInteger maxCount = -1;
241
	for(NSInteger i = 0; i < R_MAX_FOLDED_ITEMS; i++) {
242
		if(foldedRanges[i][0] > -1) {
243
			if(i > maxCount) maxCount = i;
244
		}
245
	}
246
	currentMaxFoldedIndex = maxCount;
247
	SLog(@"RScriptEditorTextStorage:removeFoldedRangeWithIndex: done. Max index: %d", currentMaxFoldedIndex);
6233 bibiko 248
 
249
	return YES;
250
}
251
 
252
- (void)removeAllFoldedRanges
253
{
6238 bibiko 254
 
6248 bibiko 255
	[[selfDelegate undoManager] disableUndoRegistration];
6238 bibiko 256
 
6233 bibiko 257
	for(NSInteger i = 0; i < R_MAX_FOLDED_ITEMS; i++) {
258
		foldedRanges[i][0] = -1;
259
		foldedRanges[i][1] = 0;
6238 bibiko 260
		foldedRanges[i][2] = 0;
6233 bibiko 261
	}
262
	NSRange range = NSMakeRange(0, [_attributedString length]);
263
	[self removeAttribute:NSCursorAttributeName range:range];
264
	[self removeAttribute:NSToolTipAttributeName range:range];
6238 bibiko 265
 
266
	foldedCounter = 0;
267
	currentMaxFoldedIndex = -1;
268
 
6248 bibiko 269
	[[selfDelegate undoManager] enableUndoRegistration];
6238 bibiko 270
 
6233 bibiko 271
}
272
 
273
- (BOOL)existsFoldedRange:(NSRange)range
274
{
275
 
276
	if(!foldedCounter) return NO;
277
 
278
	BOOL success = NO;
6238 bibiko 279
	for(NSInteger i = 0; i < currentMaxFoldedIndex+1; i++) {
6233 bibiko 280
		if(foldedRanges[i][0] == range.location && foldedRanges[i][1] == range.length) {
281
			success = YES;
282
			break;
283
		}
284
	}
285
 
286
	return success;
287
 
288
}
289
 
290
- (NSRange)foldedRangeAtIndex:(NSInteger)index
291
{
292
 
293
	if(!foldedCounter || index < 0 || index > R_MAX_FOLDED_ITEMS) return NSMakeRange(NSNotFound, 0);
294
 
295
	NSInteger loc = foldedRanges[index][0];
6238 bibiko 296
 
6233 bibiko 297
	if(loc == -1) return NSMakeRange(NSNotFound, 0);
6238 bibiko 298
 
6246 bibiko 299
	return NSMakeRange(loc, foldedRanges[index][1]);
6238 bibiko 300
 
6233 bibiko 301
}
6238 bibiko 302
#pragma mark -
303
#pragma mark Primitives
6233 bibiko 304
 
6128 bibiko 305
- (NSString *)string
306
{ 
6232 bibiko 307
	return (*_strImp)(_attributedString, _strSel);
6128 bibiko 308
}
309
 
310
- (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range
311
{
312
 
6137 bibiko 313
	NSDictionary *attributes = (*_getImp)(_attributedString, _getSel, location, range);
6128 bibiko 314
 
6246 bibiko 315
	if(!foldedCounter || location > [_attributedString length]) return attributes;
6128 bibiko 316
 
6137 bibiko 317
	NSRange effectiveRange;
6128 bibiko 318
 
6233 bibiko 319
	// Check if location is inside folded range for drawing indicator
320
	NSInteger index = -1;
321
	if(location) {
322
		// Notes folded ranges are stored from { to } but indicator will drawn inside of { and }
6238 bibiko 323
		NSInteger adjLocation = location + 2;
324
		for(NSInteger i = 0; i < currentMaxFoldedIndex+1; i++) {
325
			if(foldedRanges[i][2] > adjLocation && foldedRanges[i][0] < location) {
6233 bibiko 326
				index = i;
327
				break;
328
			}
329
		}
330
	}
6128 bibiko 331
 
6233 bibiko 332
	if (index > -1) {
333
		effectiveRange = NSMakeRange(foldedRanges[index][0]+1, foldedRanges[index][1]-2);
6238 bibiko 334
		// We adds NSAttachmentAttributeName if location is at beginning of folded range
6137 bibiko 335
		if (location == effectiveRange.location) { // beginning of a folded range
6233 bibiko 336
 
6137 bibiko 337
			NSMutableDictionary *dict = [attributes mutableCopyWithZone:NULL];
338
			[dict setObject:sharedAttachment forKey:NSAttachmentAttributeName];
339
			attributes = [dict autorelease];
340
			effectiveRange.length = 1;
6233 bibiko 341
 
6137 bibiko 342
		} else {
343
			++(effectiveRange.location); --(effectiveRange.length);
6128 bibiko 344
		}
6246 bibiko 345
		effectiveRange = NSIntersectionRange(effectiveRange, NSMakeRange(0, [_attributedString length]));
6137 bibiko 346
		if (range) *range = effectiveRange;
6128 bibiko 347
	}
348
 
349
	return attributes;
6137 bibiko 350
 
6128 bibiko 351
}
352
 
6233 bibiko 353
- (void)edited:(NSUInteger)mask range:(NSRange)oldRange changeInLength:(NSInteger)lengthChange
354
{
355
 
356
	if(foldedCounter && mask == NSTextStorageEditedCharacters) {
6238 bibiko 357
		// update foldedRanges array due to changes
358
		NSInteger index = oldRange.location-1;
359
		NSInteger maxOldRange = NSMaxRange(oldRange) + 1;
360
		for(NSInteger i = 0; i < currentMaxFoldedIndex+1; i++) {
6233 bibiko 361
			if(index < foldedRanges[i][0]) {
6238 bibiko 362
				// if change covers the entire folded range -> delete it
363
				if(foldedRanges[i][2] < maxOldRange && foldedRanges[i][0] > index) {
364
					[self removeFoldedRangeWithIndex:i];
6233 bibiko 365
					continue;
366
				}
6238 bibiko 367
				// otherwise correct folded start range and maxrange
6233 bibiko 368
				foldedRanges[i][0] += lengthChange;
6238 bibiko 369
				foldedRanges[i][2] += lengthChange;
370
				// for safety delete it if new location is negative
371
				if(foldedRanges[i][0] < 0) {
372
					[self removeFoldedRangeWithIndex:i];
6233 bibiko 373
				}
374
			}
375
		}
376
	}
377
 
378
	[super edited:mask range:oldRange changeInLength:lengthChange];
379
}
380
 
6128 bibiko 381
// NSMutableAttributedString primitives
382
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
383
{
6232 bibiko 384
	(*_replImp)(_attributedString, _replSel, range, str);
385
	(*_editImp)(self, _editSel, NSTextStorageEditedCharacters, range, [str length] - range.length);
6128 bibiko 386
}
387
 
388
- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range
389
{
6137 bibiko 390
	(*_setImp)(_attributedString, _setSel, attrs, range);
6232 bibiko 391
	(*_editImp)(self, _editSel, NSTextStorageEditedAttributes, range, 0);
6128 bibiko 392
}
393
 
394
// Attribute Fixing Overrides
395
/*
396
- (void)fixAttributesInRange:(NSRange)range
397
{
398
	[super fixAttributesInRange:range];
399
 
400
	if(NSMaxRange(range) == 0) return;
401
 
402
	//	we want to avoid extending to the last paragraph separator
403
	NSDictionary *attributeDict;
404
	NSRange effectiveRange = { 0, 0 };
405
	NSUInteger idx = range.location;
406
	while (NSMaxRange(effectiveRange) < NSMaxRange(range)) {
407
		attributeDict = [_attributedString attributesAtIndex:idx
408
								   longestEffectiveRange:&effectiveRange
409
												 inRange:range];
410
		id value = [attributeDict objectForKey:foldingAttributeName];
411
		if (value && effectiveRange.length) {
412
			NSUInteger paragraphStart, paragraphEnd, contentsEnd;
413
			[[self string] getParagraphStart:&paragraphStart end:&paragraphEnd contentsEnd:&contentsEnd forRange:range];
414
			if ((NSMaxRange(range) == paragraphEnd) && (contentsEnd < paragraphEnd)) {
415
				[self removeAttribute:foldingAttributeName range:NSMakeRange(contentsEnd, paragraphEnd - contentsEnd)];
416
			}
417
		}
418
		idx = NSMaxRange(effectiveRange);
419
	}
420
 
421
	// 10.6 implementation
422
	// [self enumerateAttribute:lineFoldingAttributeName inRange:range options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
423
	// 	if (value && (range.length > 1)) {
424
	// 		NSUInteger paragraphStart, paragraphEnd, contentsEnd;
425
	// 		[[self string] getParagraphStart:&paragraphStart end:&paragraphEnd contentsEnd:&contentsEnd forRange:range];
426
	// 		if ((NSMaxRange(range) == paragraphEnd) && (contentsEnd < paragraphEnd)) {
427
	// 			[self removeAttribute:lineFoldingAttributeName range:NSMakeRange(contentsEnd, paragraphEnd - contentsEnd)];
428
	// 		}
429
	// 	}
430
	// }];
431
}
432
*/
433
 
434
@end