1996-05-30 20:03:15 +00:00
|
|
|
|
/*
|
|
|
|
|
NSText.m
|
|
|
|
|
|
|
|
|
|
The RTFD text class
|
|
|
|
|
|
|
|
|
|
Copyright (C) 1996 Free Software Foundation, Inc.
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
Author: Scott Christley <scottc@net - community.com>
|
1996-05-30 20:03:15 +00:00
|
|
|
|
Date: 1996
|
1998-08-01 15:41:49 +00:00
|
|
|
|
Author: Felipe A. Rodriguez <far@ix.netcom.com>
|
|
|
|
|
Date: July 1998
|
1999-11-02 07:58:11 +00:00
|
|
|
|
Author: Daniel B<EFBFBD>hringer <boehring@biomed.ruhr - uni - bochum.de>
|
1998-08-20 09:56:26 +00:00
|
|
|
|
Date: August 1998
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1996-05-30 20:03:15 +00:00
|
|
|
|
This file is part of the GNUstep GUI Library.
|
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
1996-10-18 17:14:13 +00:00
|
|
|
|
License along with this library; see the file COPYING.LIB.
|
|
|
|
|
If not, write to the Free Software Foundation,
|
1999-11-02 07:58:11 +00:00
|
|
|
|
59 Temple Place - Suite 330, Boston, MA 02111 - 1307, USA.
|
1999-06-22 23:37:24 +00:00
|
|
|
|
*/
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// doDo: - caret blinking
|
|
|
|
|
// - formatting routine: broader than 1.5x width cause display problems
|
|
|
|
|
// - optimization: 1.deletion of single char in paragraph [opti hook 1]
|
|
|
|
|
// - optimization: 2.newline in first line
|
|
|
|
|
// - optimization: 3.paragraph made one less line due to delition of single char [opti hook 1; diff from 1.]
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
#if !defined(ABS)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
#define ABS(A) ({ typeof(A) __a = (A); __a < 0 ? - __a : __a; })
|
|
|
|
|
#endif // the definition in gstep - base produces warnings FIX ME FAR
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1997-09-23 22:43:24 +00:00
|
|
|
|
#include <gnustep/gui/config.h>
|
1997-08-18 17:10:23 +00:00
|
|
|
|
#include <Foundation/NSString.h>
|
1998-08-20 09:56:26 +00:00
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
#include <AppKit/NSFileWrapper.h>
|
|
|
|
|
#include <AppKit/NSControl.h>
|
1997-02-18 00:29:25 +00:00
|
|
|
|
#include <AppKit/NSText.h>
|
|
|
|
|
#include <AppKit/NSApplication.h>
|
|
|
|
|
#include <AppKit/NSWindow.h>
|
|
|
|
|
#include <AppKit/NSFontPanel.h>
|
|
|
|
|
#include <AppKit/NSFont.h>
|
|
|
|
|
#include <AppKit/NSColor.h>
|
1998-08-20 09:56:26 +00:00
|
|
|
|
#include <AppKit/NSPasteboard.h>
|
1998-09-02 15:05:13 +00:00
|
|
|
|
#include <AppKit/NSSpellChecker.h>
|
1999-06-23 23:27:16 +00:00
|
|
|
|
#include <AppKit/NSClipView.h>
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
|
|
|
|
#include <AppKit/NSDragging.h>
|
1998-09-02 15:05:13 +00:00
|
|
|
|
#include <AppKit/NSStringDrawing.h>
|
1998-08-20 09:56:26 +00:00
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
#include <Foundation/NSNotification.h>
|
|
|
|
|
#include <Foundation/NSArchiver.h>
|
|
|
|
|
#include <Foundation/NSValue.h>
|
1998-08-20 09:56:26 +00:00
|
|
|
|
#include <Foundation/NSScanner.h>
|
1998-10-15 12:04:53 +00:00
|
|
|
|
#include <Foundation/NSData.h>
|
|
|
|
|
|
|
|
|
|
#define HUGE 1e99
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
enum {
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSBackspaceKey = 8,
|
|
|
|
|
NSCarriageReturnKey = 13,
|
|
|
|
|
NSDeleteKey = 0x7f,
|
|
|
|
|
NSBacktabKey = 25
|
1998-10-15 12:04:53 +00:00
|
|
|
|
};
|
1998-08-20 09:56:26 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
@interface _GNULineLayoutInfo: NSObject
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ NSRange lineRange;
|
|
|
|
|
NSRect lineRect;
|
|
|
|
|
float drawingOffset;
|
|
|
|
|
BOOL dontDisplay;
|
|
|
|
|
unsigned type;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
NSString *fingerprintString; // obsolete, unused
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef enum
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{ // do not use 0 in order to secure calls to nil (calls to nil return 0)!
|
1999-11-02 07:58:11 +00:00
|
|
|
|
LineLayoutInfoType_Text = 1,
|
|
|
|
|
LineLayoutInfoType_Paragraph = 2
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} _GNULineLayoutInfo_t;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
+ lineLayoutWithRange: (NSRange) aRange rect: (NSRect) aRect drawingOffset: (float) anOffset type: (unsigned) aType;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSRange) lineRange;
|
|
|
|
|
- (NSRect) lineRect;
|
|
|
|
|
- (float) drawingOffset;
|
|
|
|
|
- (BOOL) isDontDisplay;
|
|
|
|
|
- (unsigned) type;
|
|
|
|
|
|
|
|
|
|
- (void) setLineRange: (NSRange) aRange;
|
|
|
|
|
- (void) setLineRect: (NSRect) aRect;
|
|
|
|
|
- (void) setDrawingOffset: (float) anOffset;
|
|
|
|
|
- (void) setDontDisplay: (BOOL) flag;
|
|
|
|
|
- (void) setType: (unsigned) aType;
|
|
|
|
|
- (NSString*) fingerprintString;
|
|
|
|
|
- (void) setFingerprintString: (NSString*) aString;
|
|
|
|
|
- (BOOL) isLineTerminatingParagraph;
|
|
|
|
|
|
|
|
|
|
- (NSString*) description;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation _GNULineLayoutInfo
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
+ lineLayoutWithRange: (NSRange) aRange rect: (NSRect) aRect drawingOffset: (float) anOffset type: (unsigned) aType
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ id ret = [[[_GNULineLayoutInfo alloc] init] autorelease];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[ret setLineRange: aRange]; [ret setLineRect: aRect]; [ret setDrawingOffset: anOffset]; [ret setType: aType];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) isDontDisplay {return dontDisplay;}
|
|
|
|
|
- (unsigned) type {return type;}
|
|
|
|
|
- (NSRange) lineRange {return lineRange;}
|
|
|
|
|
- (NSRect) lineRect {return lineRect;}
|
|
|
|
|
- (float) drawingOffset {return drawingOffset;}
|
|
|
|
|
- (NSString*) fingerprintString {return fingerprintString;}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setLineRange: (NSRange) aRange {lineRange = aRange;}
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setLineRect: (NSRect) aRect
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{
|
|
|
|
|
//FIXME, line up textEditor with how text in text cell will be placed.
|
|
|
|
|
// aRect.origin.y += 2;
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
lineRect = aRect;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setDrawingOffset: (float) anOffset {drawingOffset = anOffset;}
|
|
|
|
|
- (void) setDontDisplay: (BOOL) flag {dontDisplay = flag;}
|
|
|
|
|
- (void) setType: (unsigned) aType {type = aType;}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setFingerprintString: (NSString*) aString
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ ASSIGN(fingerprintString,aString);
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSString*) description
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [[NSDictionary dictionaryWithObjectsAndKeys: NSStringFromRange(lineRange),@"LineRange",
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSStringFromRect(lineRect),@"LineRect",
|
|
|
|
|
fingerprintString,@"fingerprint",
|
|
|
|
|
nil] description];
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) isLineTerminatingParagraph {return type == LineLayoutInfoType_Paragraph && lineRect.origin.x> 0;} // sort of hackish
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) dealloc
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (fingerprintString) [fingerprintString release];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
static NSRange MakeRangeFromAbs(int a1,int a2) // not the same as NSMakeRange!
|
|
|
|
|
{
|
|
|
|
|
if (a1 < 0)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
a1 = 0;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
if (a2 < 0)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
a2 = 0;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
if (a1 < a2)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
return NSMakeRange(a1,a2 - a1);
|
1999-06-22 23:37:24 +00:00
|
|
|
|
else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
return NSMakeRange(a2,a1 - a2);
|
1999-06-22 23:37:24 +00:00
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
/*
|
|
|
|
|
static NSRange MakeRangeFromAbs(int a1,int a2)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ if (a1< a2) return NSMakeRange(a1,a2 - a1);
|
|
|
|
|
else return NSMakeRange(a2,a1 - a2);
|
1999-06-22 23:37:24 +00:00
|
|
|
|
}
|
|
|
|
|
*/
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// end: _GNULineLayoutInfo------------------------------------------------------------------------------------------
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
// NeXT's NSScanner's scanCharactersFromSet and friends seem to be a bit sluggish on whitespaces and newlines
|
1999-11-02 07:58:11 +00:00
|
|
|
|
//(have not tried GNUstep - base implementation though). so here is a more pedantic (and faster) implementation:
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
// this class should be considered private since it is not polished at all!
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
@interface _GNUTextScanner: NSObject
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ NSString *string;
|
|
|
|
|
NSCharacterSet *set,*iSet;
|
|
|
|
|
unsigned stringLength;
|
|
|
|
|
NSRange activeRange;
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
+ (_GNUTextScanner*) scannerWithString: (NSString*) aStr set: (NSCharacterSet*) aSet invertedSet: (NSCharacterSet*) anInvSet;
|
|
|
|
|
- (void) setString: (NSString*) aString set: (NSCharacterSet*) aSet invertedSet: (NSCharacterSet*) anInvSet;
|
|
|
|
|
- (NSRange) _scanCharactersInverted: (BOOL) inverted;
|
|
|
|
|
- (NSRange) scanSetCharacters;
|
|
|
|
|
- (NSRange) scanNonSetCharacters;
|
|
|
|
|
- (BOOL) isAtEnd;
|
|
|
|
|
- (unsigned) scanLocation;
|
|
|
|
|
- (void) setScanLocation: (unsigned) aLoc;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation _GNUTextScanner
|
1999-11-02 07:58:11 +00:00
|
|
|
|
+ (_GNUTextScanner*) scannerWithString: (NSString*) aStr set: (NSCharacterSet*) aSet invertedSet: (NSCharacterSet*) anInvSet
|
|
|
|
|
{ _GNUTextScanner *ret = [[self alloc] init];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[ret setString: aStr set: aSet invertedSet: anInvSet];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
return [ret autorelease];
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setString: (NSString*) aString set: (NSCharacterSet*) aSet invertedSet: (NSCharacterSet*) anInvSet
|
|
|
|
|
{ ASSIGN(string,aString); stringLength = [string length]; activeRange = NSMakeRange(0,stringLength);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
ASSIGN(set,aSet); ASSIGN(iSet,anInvSet);
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSRange) _scanCharactersInverted: (BOOL) inverted
|
|
|
|
|
{ NSRange range = NSMakeRange(activeRange.location,0);
|
|
|
|
|
NSCharacterSet *currentSet = inverted? iSet: set;
|
|
|
|
|
NSCharacterSet *currentISet = inverted? set: iSet;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (activeRange.location>= stringLength) return range;
|
|
|
|
|
if ([currentSet characterIsMember: [string characterAtIndex: activeRange.location]])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ range = [string rangeOfCharacterFromSet: currentSet options: 0 range: activeRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
if (range.length)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSRange iRange = range;
|
|
|
|
|
iRange = [string rangeOfCharacterFromSet: currentISet options: 0 range: MakeRangeFromAbs(NSMaxRange(range),stringLength)];
|
|
|
|
|
if (iRange.length) range = MakeRangeFromAbs(range.location,iRange.location);
|
|
|
|
|
else range = MakeRangeFromAbs(range.location,stringLength);
|
|
|
|
|
activeRange = MakeRangeFromAbs(NSMaxRange(range),stringLength);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
return range;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSRange) scanSetCharacters
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [self _scanCharactersInverted: NO];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSRange) scanNonSetCharacters
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [self _scanCharactersInverted: YES];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) isAtEnd
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{ return activeRange.location>= stringLength;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (unsigned) scanLocation {return activeRange.location;}
|
|
|
|
|
- (void) setScanLocation: (unsigned) aLoc { activeRange = MakeRangeFromAbs(aLoc,stringLength);}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) dealloc
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ [string release];
|
|
|
|
|
[set release];
|
|
|
|
|
[iSet release];
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
// end: _GNUTextScanner implementation--------------------------------------
|
|
|
|
|
|
1999-08-19 23:18:25 +00:00
|
|
|
|
/*
|
1998-10-15 12:04:53 +00:00
|
|
|
|
@interface NSAttributedString(DrawingAddition)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSSize) sizeRange: (NSRange) aRange;
|
|
|
|
|
- (void) drawRange: (NSRange) aRange atPoint: (NSPoint) aPoint;
|
|
|
|
|
- (void) drawRange: (NSRange) aRange inRect: (NSRect) aRect;
|
|
|
|
|
- (BOOL) areMultipleFontsInRange: (NSRange) aRange;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
@end
|
1999-08-19 23:18:25 +00:00
|
|
|
|
*/
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
|
|
|
|
@implementation NSAttributedString(DrawingAddition)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSSize) sizeRange: (NSRange) lineRange
|
|
|
|
|
{ NSRect retRect = NSZeroRect;
|
|
|
|
|
NSRange currRange = NSMakeRange(lineRange.location,0);
|
|
|
|
|
NSPoint currPoint = NSMakePoint(0,0);
|
|
|
|
|
NSString *string = [self string];
|
|
|
|
|
|
|
|
|
|
for (; NSMaxRange(currRange)< NSMaxRange(lineRange);) // draw all "runs"
|
|
|
|
|
{ NSDictionary *attributes = [self attributesAtIndex: NSMaxRange(currRange) longestEffectiveRange: &currRange inRange: lineRange];
|
|
|
|
|
NSString *substring = [string substringWithRange: currRange];
|
|
|
|
|
NSRect sizeRect = NSMakeRect(currPoint.x,0,0,0);
|
|
|
|
|
|
|
|
|
|
sizeRect.size = [substring sizeWithAttributes: attributes];
|
|
|
|
|
retRect = NSUnionRect(retRect,sizeRect);
|
|
|
|
|
currPoint.x += sizeRect.size.width;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
//<!> size attachments
|
1998-10-15 12:04:53 +00:00
|
|
|
|
} return retRect.size;
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) drawRange: (NSRange) lineRange atPoint: (NSPoint) aPoint
|
|
|
|
|
{ NSRange currRange = NSMakeRange(lineRange.location,0);
|
1998-10-15 12:04:53 +00:00
|
|
|
|
NSPoint currPoint;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSString *string = [self string];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (currPoint = aPoint; NSMaxRange(currRange)< NSMaxRange(lineRange);) // draw all "runs"
|
|
|
|
|
{ NSDictionary *attributes = [self attributesAtIndex: NSMaxRange(currRange) longestEffectiveRange: &currRange inRange: lineRange];
|
|
|
|
|
NSString *substring = [string substringWithRange: currRange];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[substring drawAtPoint: currPoint withAttributes: attributes];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
currPoint.x += [substring sizeWithAttributes: attributes].width;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
//<!> draw attachments
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void) drawRange: (NSRange)aRange inRect: (NSRect)aRect
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSString *substring = [[self string] substringWithRange: aRange];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[substring drawInRect: aRect
|
|
|
|
|
withAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
|
[NSFont systemFontOfSize: 12.0], NSFontAttributeName,
|
|
|
|
|
[NSColor blueColor], NSForegroundColorAttributeName,
|
|
|
|
|
nil]];
|
1999-08-19 23:18:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) areMultipleFontsInRange: (NSRange) aRange
|
|
|
|
|
{
|
|
|
|
|
NSRange longestRange;
|
|
|
|
|
|
|
|
|
|
[self attribute: NSFontAttributeName atIndex: aRange.location
|
|
|
|
|
longestEffectiveRange: &longestRange inRange: aRange];
|
|
|
|
|
if (NSEqualRanges(NSIntersectionRange(longestRange, aRange), aRange))
|
|
|
|
|
return NO;
|
|
|
|
|
else
|
|
|
|
|
return YES;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
@end
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
@interface _GNUSeekableArrayEnumerator: NSObject
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned currentIndex;
|
|
|
|
|
NSArray *array;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- initWithArray: (NSArray*) anArray;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
- nextObject;
|
|
|
|
|
- previousObject;
|
|
|
|
|
- currentObject;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation _GNUSeekableArrayEnumerator
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- initWithArray: (NSArray*) anArray
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{ [super init];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
array = [anArray retain];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
- nextObject
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (currentIndex >= [array count]) return nil;
|
|
|
|
|
return [array objectAtIndex: currentIndex++];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
- previousObject
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (!currentIndex) return nil;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
return [array objectAtIndex:--currentIndex];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
- currentObject
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [array objectAtIndex: currentIndex];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) dealloc
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{ [array release];
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
@interface NSArray(SeekableEnumerator)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (_GNUSeekableArrayEnumerator*) seekableEnumerator;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
@end
|
|
|
|
|
@implementation NSArray(SeekableEnumerator)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (_GNUSeekableArrayEnumerator*) seekableEnumerator
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [[[_GNUSeekableArrayEnumerator alloc] initWithArray: self] autorelease];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// begin: NSText------------------------------------------------------------
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1996-05-30 20:03:15 +00:00
|
|
|
|
@implementation NSText
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Class methods
|
|
|
|
|
//
|
|
|
|
|
+ (void)initialize
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (self == [NSText class])
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{
|
|
|
|
|
NSArray *r;
|
|
|
|
|
NSArray *s;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setVersion: 1]; // Initial version
|
1998-11-24 15:25:22 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
r = [NSArray arrayWithObjects: NSStringPboardType, nil];
|
|
|
|
|
s = [NSArray arrayWithObjects: NSStringPboardType, nil];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1998-11-24 15:25:22 +00:00
|
|
|
|
[[NSApplication sharedApplication] registerServicesMenuSendTypes: s
|
1999-06-22 23:37:24 +00:00
|
|
|
|
returnTypes: r];
|
1998-11-24 15:25:22 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
//<!>
|
1999-11-02 07:58:11 +00:00
|
|
|
|
//this is sort of a botch up: rtf should be spilled out here in order to be OPENSTEP - compatible
|
|
|
|
|
// the way to go is surely implementing - (NSData *)RTFDFromRange: (NSRange)range documentAttributes: (NSDictionary *)dict; and friends.
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// (NeXT OPENSTEP additions to NSAttributedString)
|
|
|
|
|
//
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// but on the other hand: since rtf is MS - technology, simply let us declare the NSArchiver generated data stream output being the GNU rich text format ; - )
|
|
|
|
|
+ (NSData*) dataForAttributedString: (NSAttributedString*) aString
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [NSArchiver archivedDataWithRootObject: aString];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
//this is sort of a botch up: a rtf parser should come in here in order to be OPENSTEP - compatible
|
|
|
|
|
// but on the other hand: since rtf is MS - technology, simply let us declare the NSArchiver generated data stream output being the GNU rich text format ; - )
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// return value is guaranteed to be a NSAttributedString even if data is only NSString
|
1999-11-02 07:58:11 +00:00
|
|
|
|
+ (NSAttributedString*) attributedStringForData: (NSData*) aData
|
|
|
|
|
{ id erg = [NSUnarchiver unarchiveObjectWithData: aData];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (![erg isKindOfClass: [NSAttributedString class]])
|
|
|
|
|
return [[[NSAttributedString alloc] initWithString: erg] autorelease];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
else return erg;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
+ (NSString*) newlineString {return @"\n";}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1996-05-30 20:03:15 +00:00
|
|
|
|
//
|
|
|
|
|
// Instance methods
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// Initialization
|
|
|
|
|
//
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (id) init
|
1998-08-30 16:06:47 +00:00
|
|
|
|
{
|
1999-08-31 09:19:39 +00:00
|
|
|
|
return [self initWithFrame: NSMakeRect(0,0,100,100)];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (id) initWithFrame: (NSRect)frameRect
|
|
|
|
|
{
|
|
|
|
|
[super initWithFrame: frameRect];
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
alignment = NSLeftTextAlignment;
|
|
|
|
|
is_editable = YES;
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setRichText: NO]; // sets up the contents object
|
1999-11-02 07:58:11 +00:00
|
|
|
|
is_selectable = YES;
|
|
|
|
|
imports_graphics = NO;
|
|
|
|
|
uses_font_panel = NO;
|
|
|
|
|
is_horizontally_resizable = NO;
|
|
|
|
|
is_vertically_resizable = YES;
|
|
|
|
|
is_ruler_visible = NO;
|
|
|
|
|
is_field_editor = NO;
|
|
|
|
|
draws_background = YES;
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setBackgroundColor: [NSColor textBackgroundColor]];
|
|
|
|
|
[self setTextColor: [NSColor textColor]];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
default_font = RETAIN([NSFont userFontOfSize: 12]);
|
1999-08-31 09:19:39 +00:00
|
|
|
|
|
|
|
|
|
[self setSelectionWordGranularitySet:
|
|
|
|
|
[NSCharacterSet characterSetWithCharactersInString: @" "]]; //[NSCharacterSet whitespaceCharacterSet]
|
|
|
|
|
[self setSelectionParagraphGranularitySet:
|
|
|
|
|
[NSCharacterSet characterSetWithCharactersInString: [[self class] newlineString]]];
|
|
|
|
|
|
|
|
|
|
[self setMinSize: frameRect.size];
|
|
|
|
|
[self setMaxSize: NSMakeSize(HUGE,HUGE)];
|
|
|
|
|
|
|
|
|
|
[self setString: @"Text"];
|
|
|
|
|
[self setSelectedRange: NSMakeRange(0,0)];
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSDictionary*) defaultTypingAttributes
|
1999-08-19 23:18:25 +00:00
|
|
|
|
{
|
1999-08-31 09:19:39 +00:00
|
|
|
|
return [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
|
default_font, NSFontAttributeName,
|
|
|
|
|
text_color, NSForegroundColorAttributeName,
|
|
|
|
|
nil];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-11-24 15:25:22 +00:00
|
|
|
|
/*
|
1999-06-22 23:37:24 +00:00
|
|
|
|
* Handle enabling/disabling of services menu items.
|
1998-11-24 15:25:22 +00:00
|
|
|
|
*/
|
|
|
|
|
- (id) validRequestorForSendType: (NSString*)sendType
|
1999-06-22 23:37:24 +00:00
|
|
|
|
returnType: (NSString*)returnType
|
1998-11-24 15:25:22 +00:00
|
|
|
|
{
|
|
|
|
|
if ((!sendType || [sendType isEqual: NSStringPboardType]) &&
|
|
|
|
|
(!returnType || [returnType isEqual: NSStringPboardType]))
|
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (([self selectedRange].length || !sendType)
|
|
|
|
|
&& ([self isEditable] || !returnType))
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{
|
|
|
|
|
return self;
|
|
|
|
|
}
|
1998-11-24 15:25:22 +00:00
|
|
|
|
}
|
|
|
|
|
return [super validRequestorForSendType: sendType
|
1999-06-22 23:37:24 +00:00
|
|
|
|
returnType: returnType];
|
|
|
|
|
|
1998-11-24 15:25:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) writeSelectionToPasteboard: (NSPasteboard*)pb
|
1999-06-22 23:37:24 +00:00
|
|
|
|
types: (NSArray*)sendTypes
|
1998-11-24 15:25:22 +00:00
|
|
|
|
{
|
1999-06-22 23:37:24 +00:00
|
|
|
|
NSArray *types;
|
|
|
|
|
NSRange range;
|
|
|
|
|
NSString *string;
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if ([sendTypes containsObject: NSStringPboardType] == NO)
|
1998-11-24 15:25:22 +00:00
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
types = [NSArray arrayWithObjects: NSStringPboardType, nil];
|
1998-11-24 15:25:22 +00:00
|
|
|
|
[pb declareTypes: types owner: nil];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
range = [self selectedRange];
|
|
|
|
|
string = [self string];
|
|
|
|
|
string = [string substringWithRange: range];
|
1998-11-24 15:25:22 +00:00
|
|
|
|
return [pb setString: string forType: NSStringPboardType];
|
|
|
|
|
}
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// <!>
|
|
|
|
|
// handle font pasteboard as well!
|
|
|
|
|
// handle ruler pasteboard as well!
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (BOOL) performPasteOperation: (NSPasteboard*)pboard
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{
|
|
|
|
|
// color accepting
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([pboard availableTypeFromArray:
|
|
|
|
|
[NSArray arrayWithObject: NSColorPboardType]])
|
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSColor *color = [NSColor colorFromPasteboard: pboard];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
|
|
|
|
|
if ([self isRichText])
|
|
|
|
|
{
|
|
|
|
|
[self setTextColor: color range: [self selectedRange]];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
[self setTextColor: color];
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self importsGraphics])
|
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSArray *types = [NSArray arrayWithObjects: NSFileContentsPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSTIFFPboardType, nil];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([[pboard availableTypeFromArray: types] isEqualToString: NSRTFDPboardType])
|
|
|
|
|
{ [self insertText: [[self class] attributedStringForData: [pboard dataForType: NSRTFDPboardType]]];
|
|
|
|
|
} else if ([[pboard availableTypeFromArray: types] isEqualToString: NSRTFPboardType])
|
|
|
|
|
{ [self insertText: [[self class] attributedStringForData: [pboard dataForType: NSRTFPboardType]]];
|
|
|
|
|
} else if ([[pboard availableTypeFromArray: types] isEqualToString: NSStringPboardType])
|
|
|
|
|
{ [self insertText: [pboard stringForType: NSStringPboardType]];
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
} else if ([self isRichText])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSArray *types = [NSArray arrayWithObjects: NSRTFPboardType, NSStringPboardType,nil];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([[pboard availableTypeFromArray: types] isEqualToString: NSRTFPboardType])
|
|
|
|
|
{ [self insertText: [[self class] attributedStringForData: [pboard dataForType: NSRTFPboardType]]];
|
|
|
|
|
} else if ([[pboard availableTypeFromArray: types] isEqualToString: NSStringPboardType])
|
|
|
|
|
{ [self insertText: [pboard stringForType: NSStringPboardType]];
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
} else // plain text
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSArray *types = [NSArray arrayWithObjects: NSStringPboardType, nil];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([[pboard availableTypeFromArray: types] isEqualToString: NSStringPboardType])
|
|
|
|
|
{ [self insertText: [pboard stringForType: NSStringPboardType]];
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
} return NO;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-07-18 03:53:42 +00:00
|
|
|
|
- (BOOL) readSelectionFromPasteboard: (NSPasteboard*)pb
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [self performPasteOperation: pb];
|
1999-07-18 03:53:42 +00:00
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// begin: dragging of colors and files---------------
|
|
|
|
|
- (unsigned int) draggingEntered: (id <NSDraggingInfo>)sender
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ return NSDragOperationGeneric;
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (unsigned int) draggingUpdated: (id <NSDraggingInfo>)sender
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ return NSDragOperationGeneric;
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) draggingExited: (id <NSDraggingInfo>)sender
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) prepareForDragOperation: (id <NSDraggingInfo>)sender
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ return YES;
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (BOOL) performDragOperation: (id <NSDraggingInfo>)sender
|
|
|
|
|
{
|
|
|
|
|
return [self performPasteOperation: [sender draggingPasteboard]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) concludeDragOperation: (id <NSDraggingInfo>)sender
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// end: drag accepting---------------------------------
|
1998-07-18 19:21:22 +00:00
|
|
|
|
|
|
|
|
|
- (void)dealloc
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{ [self unregisterDraggedTypes];
|
|
|
|
|
[background_color release];
|
1998-08-30 16:06:47 +00:00
|
|
|
|
[default_font release];
|
|
|
|
|
[text_color release];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1998-08-20 09:56:26 +00:00
|
|
|
|
[plainContent release];
|
|
|
|
|
[rtfContent release];
|
1998-07-18 19:21:22 +00:00
|
|
|
|
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSArray*) acceptableDragTypes
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSMutableArray *ret = [NSMutableArray arrayWithObjects: NSStringPboardType, NSColorPboardType, nil];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText])
|
|
|
|
|
[ret addObject: NSRTFPboardType];
|
|
|
|
|
if ([self importsGraphics])
|
|
|
|
|
[ret addObject: NSRTFDPboardType];
|
|
|
|
|
return ret;
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) updateDragTypeRegistration
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self registerForDraggedTypes: [self acceptableDragTypes]];
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSRange) selectionRangeForProposedRange: (NSRange)proposedCharRange granularity: (NSSelectionGranularity)granularity
|
|
|
|
|
{ NSCharacterSet *set = nil;
|
|
|
|
|
unsigned lastIndex = [self textLength] - 1,lpos = MIN(lastIndex,proposedCharRange.location),
|
|
|
|
|
rpos = NSMaxRange(proposedCharRange); // <!>better: rpos = MAX(0,(int)NSMaxRange(proposedCharRange) - 1);
|
|
|
|
|
NSString *string = [self string];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
BOOL rmemberstate,lmemberstate;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (![string length]) {
|
1999-06-22 23:37:24 +00:00
|
|
|
|
NSLog(@"We have no length, our range is 0,0\n");
|
|
|
|
|
return NSMakeRange(0,0);
|
|
|
|
|
}
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
switch(granularity)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ case NSSelectByCharacter: return NSIntersectionRange(proposedCharRange,NSMakeRange(0,[self textLength] + 1));
|
1999-08-31 09:19:39 +00:00
|
|
|
|
case NSSelectByWord:
|
1999-11-02 07:58:11 +00:00
|
|
|
|
set = selectionWordGranularitySet;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
break;
|
1999-08-31 09:19:39 +00:00
|
|
|
|
case NSSelectByParagraph:
|
1999-11-02 07:58:11 +00:00
|
|
|
|
set = selectionParagraphGranularitySet;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// now work on set...
|
1999-11-02 07:58:11 +00:00
|
|
|
|
lmemberstate = [set characterIsMember: [string characterAtIndex: lpos]];
|
|
|
|
|
rmemberstate = [set characterIsMember: [string characterAtIndex: MIN(rpos,lastIndex)]];
|
|
|
|
|
while (rpos<= lastIndex && [set characterIsMember: [string characterAtIndex: rpos]] == rmemberstate) rpos++;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
while(lpos && [set characterIsMember: [string characterAtIndex: lpos]] == lmemberstate) lpos--;
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([set characterIsMember: [string characterAtIndex: lpos]] != lmemberstate && lpos < proposedCharRange.location) lpos++;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
return MakeRangeFromAbs(lpos,rpos);
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
//
|
|
|
|
|
// Getting and Setting Contents
|
|
|
|
|
//
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
|
|
|
|
// low level (no selection handling, relayout or display)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) replaceRange: (NSRange)range withAttributedString: (NSAttributedString*)attrString
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self isRichText])
|
|
|
|
|
{ return [rtfContent replaceCharactersInRange: range withAttributedString: attrString];
|
|
|
|
|
} else return [plainContent replaceCharactersInRange: range withString: [attrString string]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) replaceRange: (NSRange)range withRTFD: (NSData *)rtfdData
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [self replaceRange: range withAttributedString: [[self class] attributedStringForData: rtfdData]];
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) replaceRange: (NSRange)range withRTF: (NSData*) rtfData
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self replaceRange: range withRTFD: rtfData];
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) replaceRange: (NSRange)range withString: (NSString*) aString
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self isRichText])
|
|
|
|
|
{ return [rtfContent replaceCharactersInRange: range withString: aString];
|
|
|
|
|
} else return [plainContent replaceCharactersInRange: range withString: aString];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setText: (NSString*) aString range: (NSRange) aRange
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self replaceRange: (NSRange)aRange withString: aString];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSData*) RTFDFromRange: (NSRange)range
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self isRichText])
|
|
|
|
|
{ return [[self class] dataForAttributedString: [rtfContent attributedSubstringFromRange: range]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} else return nil;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSData*) RTFFromRange: (NSRange)range
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [self RTFDFromRange: range];
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setString: (NSString *)string
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ [plainContent release];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
plainContent = [[NSMutableString stringWithString: string] retain];
|
|
|
|
|
[lineLayoutInformation autorelease]; lineLayoutInformation = nil; // force complete re - layout
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setRichText: NO];
|
|
|
|
|
[self setSelectedRangeNoDrawing: NSMakeRange(0,0)];
|
|
|
|
|
// [self rebuildLineLayoutInformationStartingAtLine: 0];
|
|
|
|
|
// [self setNeedsDisplay: YES];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setText: (NSString *)string {[self setString: string];}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSString*) string
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self isRichText]) return [rtfContent string];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
else return plainContent;
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSString*) text {return [self string];}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Managing Global Characteristics
|
|
|
|
|
//
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSTextAlignment)alignment { return alignment; }
|
|
|
|
|
- (BOOL) drawsBackground { return draws_background; }
|
|
|
|
|
- (BOOL) importsGraphics { return imports_graphics; }
|
|
|
|
|
- (BOOL) isEditable { return is_editable; }
|
|
|
|
|
- (BOOL) isRichText { return is_rich_text; }
|
|
|
|
|
- (BOOL) isSelectable { return is_selectable; }
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setAlignment: (NSTextAlignment)mode
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ alignment = mode;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setDrawsBackground: (BOOL)flag
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ draws_background = flag;
|
1998-08-01 15:41:49 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setEditable: (BOOL)flag
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ is_editable = flag;
|
|
|
|
|
if (flag) is_selectable = YES; // If we are editable then we are selectable
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setImportsGraphics: (BOOL)flag
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ imports_graphics = flag;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
[self updateDragTypeRegistration];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setRichText: (BOOL)flag
|
|
|
|
|
{ is_rich_text = flag;
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (flag)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ if (!rtfContent) rtfContent = [[NSMutableAttributedString alloc] initWithString: plainContent? (NSString*)plainContent: @"" attributes: [self defaultTypingAttributes]];
|
|
|
|
|
[lineLayoutInformation autorelease]; lineLayoutInformation = nil;
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self rebuildLineLayoutInformationStartingAtLine: 0];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
} else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ if (!plainContent) plainContent = [[NSMutableString alloc] initWithString: rtfContent? [rtfContent string]: @""];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self rebuildLineLayoutInformationStartingAtLine: 0];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self updateDragTypeRegistration];
|
|
|
|
|
|
|
|
|
|
[self sizeToFit];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setNeedsDisplay: YES];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setSelectable: (BOOL)flag
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ is_selectable = flag;
|
|
|
|
|
if (!flag) is_editable = NO; // If we are not selectable then we must not be editable
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* Managing Font and Color
|
|
|
|
|
*/
|
|
|
|
|
- (NSColor*) backgroundColor
|
|
|
|
|
{
|
|
|
|
|
return background_color;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSFont*) font
|
|
|
|
|
{
|
|
|
|
|
return default_font;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSColor*) textColor
|
|
|
|
|
{
|
|
|
|
|
return text_color;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) usesFontPanel
|
|
|
|
|
{
|
|
|
|
|
return uses_font_panel;
|
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This action method changes the font of the selection for a rich text object, or of all text for a plain text object. If the receiver doesn't use the Font Panel, however, this method does nothing.
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) changeFont: sender
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self usesFontPanel])
|
|
|
|
|
{ if ([self isRichText])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSRange selectedRange = [self selectedRange], searchRange = selectedRange,foundRange;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
int maxSelRange;
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (maxSelRange = NSMaxRange(selectedRange); searchRange.location< maxSelRange;
|
|
|
|
|
searchRange = NSMakeRange(NSMaxRange(foundRange),maxSelRange - NSMaxRange(foundRange)))
|
|
|
|
|
{ NSFont *font = [rtfContent attribute: NSFontAttributeName atIndex: searchRange.location longestEffectiveRange: &foundRange inRange: searchRange];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (font)
|
|
|
|
|
{ [self setFont: [sender convertFont: font] ofRange: foundRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self setFont: [sender convertFont: [[self defaultTypingAttributes] objectForKey: NSFontAttributeName]]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setSelectionWordGranularitySet: (NSCharacterSet*) aSet
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ ASSIGN(selectionWordGranularitySet, aSet);
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setSelectionParagraphGranularitySet: (NSCharacterSet*) aSet
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ ASSIGN(selectionParagraphGranularitySet, aSet);
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void) setBackgroundColor: (NSColor *)color
|
|
|
|
|
{
|
|
|
|
|
ASSIGN(background_color, color);
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setTypingAttributes: (NSDictionary*) dict
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (![dict isKindOfClass: [NSMutableDictionary class]])
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ [typingAttributes autorelease];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
typingAttributes = [[NSMutableDictionary alloc] initWithDictionary: dict]; // do not autorelease!
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} else ASSIGN(typingAttributes, (NSMutableDictionary*)dict);
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSMutableDictionary*) typingAttributes
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (typingAttributes) return typingAttributes;
|
|
|
|
|
else return [NSMutableDictionary dictionaryWithDictionary: [self defaultTypingAttributes]];
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void) setTextColor: (NSColor*)color range: (NSRange)range
|
|
|
|
|
{
|
|
|
|
|
if ([self isRichText])
|
|
|
|
|
{
|
|
|
|
|
if (color)
|
|
|
|
|
[rtfContent addAttribute: NSForegroundColorAttributeName
|
|
|
|
|
value: color
|
|
|
|
|
range: range];
|
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
|
|
|
|
|
- (void) setColor: (NSColor*)color ofRange: (NSRange)range
|
|
|
|
|
{
|
|
|
|
|
[self setTextColor: color range: range];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setFont: (NSFont*)obj
|
|
|
|
|
{
|
|
|
|
|
ASSIGN(default_font, obj);
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setFont: (NSFont *)font ofRange: (NSRange)range
|
|
|
|
|
{ if ([self isRichText])
|
|
|
|
|
{ if (font)
|
|
|
|
|
{ [rtfContent addAttribute: NSFontAttributeName value: font range: range];
|
|
|
|
|
[self rebuildFromCharacterIndex: range.location];
|
1999-09-09 02:56:20 +00:00
|
|
|
|
NSDebugLLog(@"NSText", @"did set font");
|
1999-06-22 23:37:24 +00:00
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} else {}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void) setTextColor: (NSColor *)color
|
|
|
|
|
{
|
|
|
|
|
ASSIGN(text_color,color);
|
|
|
|
|
if (![self isRichText])
|
|
|
|
|
[self setNeedsDisplay: YES];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setUsesFontPanel: (BOOL)flag
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ uses_font_panel = flag;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Managing the Selection
|
|
|
|
|
//
|
1998-08-04 08:33:31 +00:00
|
|
|
|
- (NSRange)selectedRange { return selected_range; }
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) shouldDrawInsertionPoint
|
|
|
|
|
{ return ([self selectedRange].length == 0) && [self isEditable];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void) drawInsertionPointInRect: (NSRect)rect
|
|
|
|
|
color: (NSColor *)color
|
|
|
|
|
turnedOn: (BOOL)flag
|
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
BOOL didLock = NO;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (![self window])
|
|
|
|
|
return;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self window] && [[self class] focusView] != self)
|
|
|
|
|
{
|
|
|
|
|
[self lockFocus];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
didLock = YES;
|
1999-08-31 09:19:39 +00:00
|
|
|
|
}
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (flag)
|
|
|
|
|
{
|
|
|
|
|
[color set];
|
|
|
|
|
NSRectFill(rect);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[[self backgroundColor] set];
|
|
|
|
|
NSRectFill(rect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (didLock)
|
|
|
|
|
{
|
|
|
|
|
[self unlockFocus];
|
|
|
|
|
[[self window] flushWindow];
|
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
|
|
|
|
|
- (void) drawInsertionPointAtIndex: (unsigned)index
|
|
|
|
|
color: (NSColor*)color
|
|
|
|
|
turnedOn: (BOOL)flag
|
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSRect startRect = [self rectForCharacterIndex: index];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
NSRect drawRect;
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
drawRect = NSMakeRect(startRect.origin.x, startRect.origin.y,
|
1999-08-31 09:19:39 +00:00
|
|
|
|
1, startRect.size.height);
|
|
|
|
|
[self drawInsertionPointInRect: drawRect
|
|
|
|
|
color: [NSColor blackColor]
|
|
|
|
|
turnedOn: flag];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) drawSelectionAsRangeNoCaret: (NSRange) aRange
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (aRange.length)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSRect startRect = [self rectForCharacterIndex: aRange.location],
|
|
|
|
|
endRect = [self rectForCharacterIndex: NSMaxRange(aRange)];
|
|
|
|
|
if (startRect.origin.y == endRect.origin.y) // single line selection
|
|
|
|
|
{ NSHighlightRect(NSMakeRect(startRect.origin.x,startRect.origin.y,endRect.origin.x - startRect.origin.x, startRect.size.height));
|
|
|
|
|
} else if (startRect.origin.y == endRect.origin.y - endRect.size.height) // two line selection
|
|
|
|
|
{ NSHighlightRect((NSMakeRect(startRect.origin.x,startRect.origin.y,[self frame].size.width - startRect.origin.x,startRect.size.height))); // first line
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSHighlightRect(NSMakeRect(0,endRect.origin.y,endRect.origin.x,endRect.size.height)); // second line
|
|
|
|
|
} else // 3 Rects: multiline selection
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSHighlightRect(((NSMakeRect(startRect.origin.x,startRect.origin.y,[self frame].size.width - startRect.origin.x,startRect.size.height)))); // first line
|
|
|
|
|
NSHighlightRect(NSMakeRect(0,NSMaxY(startRect),[self frame].size.width,endRect.origin.y - NSMaxY(startRect))); // intermediate lines
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSHighlightRect(NSMakeRect(0,endRect.origin.y,endRect.origin.x,endRect.size.height)); // last line
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
|
|
|
|
|
- (void) drawSelectionAsRange: (NSRange) aRange
|
|
|
|
|
{
|
|
|
|
|
if (aRange.length)
|
|
|
|
|
{
|
|
|
|
|
[self drawSelectionAsRangeNoCaret: aRange];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[self drawInsertionPointAtIndex: aRange.location
|
|
|
|
|
color: [NSColor blackColor]
|
|
|
|
|
turnedOn: YES];
|
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
// low level selection setting including delegation
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setSelectedRangeNoDrawing: (NSRange)range
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{
|
1999-06-22 23:37:24 +00:00
|
|
|
|
#if 0
|
|
|
|
|
//<!> ask delegate for selection validation
|
|
|
|
|
#endif
|
1999-11-02 07:58:11 +00:00
|
|
|
|
selected_range = range;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
#if 0
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName: NSTextViewDidChangeSelectionNotification object: self
|
|
|
|
|
userInfo: [NSDictionary dictionaryWithObjectsAndKeys: NSStringFromRange(selected_range),NSOldSelectedCharacterRange,
|
1998-09-02 15:05:13 +00:00
|
|
|
|
nil]];
|
|
|
|
|
#endif
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setSelectedRange: (NSRange)range
|
|
|
|
|
{ BOOL didLock = NO;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (![self window])
|
1999-06-22 23:37:24 +00:00
|
|
|
|
return;
|
1998-11-20 02:14:22 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self window] && [[self class] focusView] != self)
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ [self lockFocus];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
didLock = YES;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (selected_range.length == 0) // remove old cursor
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self drawInsertionPointAtIndex: selected_range.location color: nil turnedOn: NO];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} else
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self drawSelectionAsRange: selected_range];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setSelectedRangeNoDrawing: range];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self usesFontPanel]) // update fontPanel
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ BOOL isMultiple = NO;
|
|
|
|
|
NSFont *currentFont = nil;
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ if ([rtfContent areMultipleFontsInRange: selected_range]) isMultiple = YES;
|
|
|
|
|
else currentFont = [[rtfContent attribute: NSFontAttributeName atIndex: range.location longestEffectiveRange: NULL inRange: range]
|
1999-08-31 09:19:39 +00:00
|
|
|
|
objectForKey: NSFontAttributeName];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
} else currentFont = [[self defaultTypingAttributes] objectForKey: NSFontAttributeName];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[[NSFontPanel sharedFontPanel] setPanelFont: currentFont isMultiple: isMultiple];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
// display
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (range.length)
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{
|
|
|
|
|
// <!>disable caret timed entry
|
|
|
|
|
} else // no selection
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self isRichText])
|
|
|
|
|
{ [self setTypingAttributes: [NSMutableDictionary dictionaryWithDictionary: [rtfContent attributesAtIndex: range.location effectiveRange: NULL]]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
// <!>enable caret timed entry
|
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self drawSelectionAsRange: range];
|
|
|
|
|
[self scrollRangeToVisible: range];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (didLock)
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ [self unlockFocus];
|
|
|
|
|
[[self window] flushWindow];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1996-05-30 20:03:15 +00:00
|
|
|
|
//
|
|
|
|
|
// Sizing the Frame Rectangle
|
|
|
|
|
//
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setFrame: (NSRect)frameRect
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[super setFrame: frameRect];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) isHorizontallyResizable { return is_horizontally_resizable; }
|
|
|
|
|
- (BOOL) isVerticallyResizable { return is_vertically_resizable; }
|
|
|
|
|
- (NSSize) maxSize { return maxSize; }
|
|
|
|
|
- (NSSize) minSize { return minSize; }
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setHorizontallyResizable: (BOOL)flag
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ is_horizontally_resizable = flag;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setMaxSize: (NSSize)newMaxSize
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ maxSize = newMaxSize;
|
1998-08-01 15:41:49 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)setMinSize: (NSSize)newMinSize
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ minSize = newMinSize;
|
1998-08-01 15:41:49 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setVerticallyResizable: (BOOL)flag
|
|
|
|
|
{ is_vertically_resizable = flag;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (unsigned) textLength
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self isRichText]) return [rtfContent length];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
else return [plainContent length];
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSRect) boundingRectForLineRange: (NSRange)lineRange
|
|
|
|
|
{ NSArray *linesToDraw = [lineLayoutInformation subarrayWithRange: lineRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSEnumerator *lineEnum;
|
|
|
|
|
_GNULineLayoutInfo *currentInfo;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSRect retRect = NSMakeRect(0,0,0,0);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for ((lineEnum = [linesToDraw objectEnumerator]); (currentInfo = [lineEnum nextObject]);)
|
|
|
|
|
{ retRect = NSUnionRect(retRect,[currentInfo lineRect]);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} return retRect;
|
1998-08-04 08:33:31 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) disableDisplay {displayDisabled = YES;}
|
|
|
|
|
- (void) reenableDisplay {displayDisabled = NO;}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) sizeToFit
|
1999-06-22 00:02:42 +00:00
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSRect sizeToRect = [self frame];
|
1999-06-23 23:27:16 +00:00
|
|
|
|
|
|
|
|
|
if ([self isFieldEditor]) // if we are a field editor we don't have to handle the size.
|
|
|
|
|
return;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isHorizontallyResizable]) {
|
|
|
|
|
if ([lineLayoutInformation count]) {
|
1999-11-02 07:58:11 +00:00
|
|
|
|
sizeToRect = [self boundingRectForLineRange: NSMakeRange(0,[lineLayoutInformation count])];
|
1999-06-23 23:27:16 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
sizeToRect.size = minSize;
|
1999-06-23 23:27:16 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
else if ([self isVerticallyResizable]) {
|
|
|
|
|
if ([lineLayoutInformation count]) {
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSRect rect = NSUnionRect([[lineLayoutInformation objectAtIndex: 0]
|
1999-06-23 23:27:16 +00:00
|
|
|
|
lineRect],
|
|
|
|
|
[[lineLayoutInformation lastObject] lineRect]);
|
1999-11-02 07:58:11 +00:00
|
|
|
|
float newHeight = rect.size.height;
|
1999-06-23 23:27:16 +00:00
|
|
|
|
float newY;
|
|
|
|
|
NSRect tRect;
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if ([[lineLayoutInformation lastObject] type] == LineLayoutInfoType_Paragraph && NSMaxY(rect)<= newHeight)
|
|
|
|
|
newHeight += [[lineLayoutInformation lastObject] lineRect].size.height;
|
1999-06-23 23:27:16 +00:00
|
|
|
|
|
1999-07-18 03:53:42 +00:00
|
|
|
|
if ( [[self superview] isKindOfClass: [NSClipView class]] )
|
1999-11-02 07:58:11 +00:00
|
|
|
|
tRect = [(NSClipView*)[self superview] documentVisibleRect];
|
1999-07-18 03:53:42 +00:00
|
|
|
|
else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
tRect = [self bounds];
|
1999-06-23 23:27:16 +00:00
|
|
|
|
|
|
|
|
|
if (currentCursorY < tRect.size.height + tRect.origin.y -
|
|
|
|
|
[[lineLayoutInformation lastObject] lineRect].size.height)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
newY = sizeToRect.origin.y;
|
1999-06-23 23:27:16 +00:00
|
|
|
|
else if (currentCursorY > tRect.size.height + tRect.origin.y -
|
|
|
|
|
[[lineLayoutInformation lastObject] lineRect].size.height) {
|
1999-11-02 07:58:11 +00:00
|
|
|
|
newY = currentCursorY - tRect.size.height +
|
1999-06-23 23:27:16 +00:00
|
|
|
|
([[lineLayoutInformation lastObject] lineRect].size.height * 2);
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[(NSClipView *)[self superview] scrollToPoint: NSMakePoint(sizeToRect.origin.x,newY)];
|
1999-06-23 23:27:16 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSLog(@" ========= > Oops!\n");
|
1999-06-23 23:27:16 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
newHeight = MIN(maxSize.height,MAX(newHeight,minSize.height));
|
|
|
|
|
sizeToRect = NSMakeRect(sizeToRect.origin.x,sizeToRect.origin.y,
|
1999-06-23 23:27:16 +00:00
|
|
|
|
sizeToRect.size.width,newHeight);
|
|
|
|
|
}
|
|
|
|
|
else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
sizeToRect = NSMakeRect(0,0,minSize.width,minSize.height);
|
1999-06-23 23:27:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (!NSEqualSizes([self frame].size,sizeToRect.size)) {
|
|
|
|
|
[self setFrame: sizeToRect]; //[self setFrameSize: sizeToRect.size];
|
1999-06-23 23:27:16 +00:00
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-06-23 23:27:16 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) sizeToFit: sender {[self sizeToFit];}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1996-05-30 20:03:15 +00:00
|
|
|
|
//
|
|
|
|
|
// Responding to Editing Commands
|
|
|
|
|
//
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) alignCenter: sender
|
1998-08-04 08:33:31 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) alignLeft: sender
|
1998-08-04 08:33:31 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) alignRight: sender
|
1998-08-04 08:33:31 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) selectAll: sender
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self setSelectedRange: NSMakeRange(0,[self textLength])];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) subscript: sender
|
1998-08-04 08:33:31 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) superscript: sender
|
1998-08-04 08:33:31 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) underline: sender
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self isRichText])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ BOOL doUnderline = YES;
|
|
|
|
|
if ([[rtfContent attribute: NSUnderlineStyleAttributeName atIndex: [self selectedRange].location effectiveRange: NULL] intValue]) doUnderline = NO;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self selectedRange].length)
|
|
|
|
|
{ [rtfContent addAttribute: NSUnderlineStyleAttributeName value: [NSNumber numberWithInt: doUnderline] range: [self selectedRange]];
|
|
|
|
|
[self rebuildFromCharacterIndex: [self selectedRange].location];
|
|
|
|
|
} else [[self typingAttributes] setObject: [NSNumber numberWithInt: doUnderline] forKey: NSUnderlineStyleAttributeName]; // no redraw necess.
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1998-08-04 08:33:31 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) unscript: sender
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self isRichText])
|
|
|
|
|
{ if ([self selectedRange].length)
|
|
|
|
|
{ [rtfContent removeAttribute: NSUnderlineStyleAttributeName range: [self selectedRange]];
|
|
|
|
|
[self rebuildFromCharacterIndex: [self selectedRange].location];
|
|
|
|
|
} else [[self typingAttributes] removeObjectForKey: NSUnderlineStyleAttributeName]; // no redraw necess.
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
//
|
|
|
|
|
// Managing the Ruler
|
|
|
|
|
//
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) isRulerVisible { return NO; }
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) toggleRuler: sender
|
1998-08-04 08:33:31 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
//
|
|
|
|
|
// Scrolling
|
|
|
|
|
//
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) scrollRangeToVisible: (NSRange)range
|
1999-06-23 23:27:16 +00:00
|
|
|
|
{
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self scrollRectToVisible: NSUnionRect([self
|
|
|
|
|
rectForCharacterIndex: [self selectedRange].location],
|
|
|
|
|
[self rectForCharacterIndex: NSMaxRange([self selectedRange])])];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
//
|
|
|
|
|
// Reading and Writing RTFD Files
|
|
|
|
|
//
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) readRTFDFromFile: (NSString *)path
|
|
|
|
|
{ NSData *data = [NSData dataWithContentsOfFile: path];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
id peek;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (data && (peek = [[self class] attributedStringForData: data]))
|
|
|
|
|
{ is_rich_text = YES; // not [self setRichText: YES] for efficiancy reasons
|
1998-09-02 15:05:13 +00:00
|
|
|
|
[self updateDragTypeRegistration];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self replaceRange: NSMakeRange(0,[self textLength]) withAttributedString: peek];
|
|
|
|
|
[self rebuildLineLayoutInformationStartingAtLine: 0];
|
|
|
|
|
[self setNeedsDisplay: YES];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) writeRTFDToFile: (NSString *)path atomically: (BOOL)flag
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self isRichText])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents: [[self class] dataForAttributedString: rtfContent]] autorelease];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
return [wrapper writeToFile: path atomically: flag updateFilenames: YES];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} return NO;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// Managing the Field Editor
|
1996-05-30 20:03:15 +00:00
|
|
|
|
//
|
1999-11-09 23:07:31 +00:00
|
|
|
|
- (BOOL) isFieldEditor
|
|
|
|
|
{
|
|
|
|
|
return is_field_editor;
|
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) setFieldEditor: (BOOL)flag
|
1999-11-09 23:07:31 +00:00
|
|
|
|
{
|
|
|
|
|
is_field_editor = flag;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (int) lineLayoutIndexForCharacterIndex: (unsigned) anIndex
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ NSEnumerator *lineEnum;
|
|
|
|
|
_GNULineLayoutInfo *currentInfo;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([lineLayoutInformation count] && anIndex>= NSMaxRange([[lineLayoutInformation lastObject] lineRange]))
|
1999-11-02 07:58:11 +00:00
|
|
|
|
return [lineLayoutInformation count] - 1;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for ((lineEnum = [lineLayoutInformation objectEnumerator]); (currentInfo = [lineEnum nextObject]);)
|
|
|
|
|
{ NSRange lineRange = [currentInfo lineRange];
|
|
|
|
|
if (lineRange.location<= anIndex && anIndex<= NSMaxRange(lineRange) - ([currentInfo type] == LineLayoutInfoType_Paragraph? 1: 0))
|
1999-08-31 09:19:39 +00:00
|
|
|
|
return [lineLayoutInformation indexOfObject: currentInfo];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([lineLayoutInformation count]) NSLog(@"NSText's lineLayoutIndexForCharacterIndex: index out of bounds!");
|
1998-09-02 15:05:13 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
//<!> choose granularity according to keyboard modifier flags
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) moveCursorUp: sender
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{ unsigned cursorIndex;
|
|
|
|
|
NSPoint cursorPoint;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self selectedRange].length) {
|
1999-11-02 07:58:11 +00:00
|
|
|
|
currentCursorX = [self rectForCharacterIndex: [self selectedRange].location].origin.x;
|
|
|
|
|
currentCursorY = [self rectForCharacterIndex: [self selectedRange].location].origin.y;
|
1999-06-23 23:27:16 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
cursorIndex = [self selectedRange].location;
|
|
|
|
|
cursorPoint = [self rectForCharacterIndex: cursorIndex].origin;
|
|
|
|
|
cursorIndex = [self characterIndexForPoint: NSMakePoint(currentCursorX + 0.001,MAX(0,cursorPoint.y - 0.001))];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setSelectedRange: [self selectionRangeForProposedRange: NSMakeRange(cursorIndex,0) granularity: NSSelectByCharacter]];
|
1999-06-23 23:27:16 +00:00
|
|
|
|
// FIXME: Terrible hack.
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self insertText: @""];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) moveCursorDown: sender
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{ unsigned cursorIndex;
|
|
|
|
|
NSRect cursorRect;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self selectedRange].length) {
|
1999-11-02 07:58:11 +00:00
|
|
|
|
currentCursorX = [self rectForCharacterIndex: NSMaxRange([self selectedRange])].origin.x;
|
|
|
|
|
currentCursorY = [self rectForCharacterIndex: NSMaxRange([self selectedRange])].origin.y;
|
1999-06-23 23:27:16 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
cursorIndex = [self selectedRange].location;
|
|
|
|
|
cursorRect = [self rectForCharacterIndex: cursorIndex];
|
|
|
|
|
cursorIndex = [self characterIndexForPoint: NSMakePoint(currentCursorX + 0.001,NSMaxY(cursorRect) + 0.001)];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setSelectedRange: [self selectionRangeForProposedRange: NSMakeRange(cursorIndex,0) granularity: NSSelectByCharacter]];
|
1999-06-23 23:27:16 +00:00
|
|
|
|
// FIXME: Terrible hack.
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self insertText: @""];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) moveCursorLeft: sender
|
|
|
|
|
{ [self setSelectedRange: [self selectionRangeForProposedRange: NSMakeRange([self selectedRange].location - 1,0) granularity: NSSelectByCharacter]];
|
|
|
|
|
currentCursorX = [self rectForCharacterIndex: [self selectedRange].location].origin.x;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) moveCursorRight: sender
|
|
|
|
|
{ [self setSelectedRange: [self selectionRangeForProposedRange: NSMakeRange(MIN(NSMaxRange([self selectedRange]) + 1,[self textLength]),0)
|
1999-08-31 09:19:39 +00:00
|
|
|
|
granularity: NSSelectByCharacter]];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
currentCursorX = [self rectForCharacterIndex: [self selectedRange].location].origin.x;
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
|
|
|
|
//
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// Handling Events
|
1996-05-30 20:03:15 +00:00
|
|
|
|
//
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) mouseDown: (NSEvent *)theEvent
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSSelectionGranularity granularity = NSSelectByCharacter;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSRange chosenRange,prevChosenRange,proposedRange;
|
|
|
|
|
NSPoint point,startPoint;
|
|
|
|
|
NSEvent *currentEvent;
|
|
|
|
|
unsigned startIndex;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
BOOL didDragging = NO;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
if (!is_selectable) return; // If not selectable then don't recognize the mouse down
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[[self window] makeFirstResponder: self];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
switch([theEvent clickCount])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ case 1: granularity = NSSelectByCharacter;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
break;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
case 2: granularity = NSSelectByWord;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
break;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
case 3: granularity = NSSelectByParagraph;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
startPoint = [self convertPoint: [theEvent locationInWindow] fromView: nil];
|
|
|
|
|
startIndex = [self characterIndexForPoint: startPoint];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
proposedRange = NSMakeRange(startIndex,0);
|
|
|
|
|
chosenRange = prevChosenRange = [self selectionRangeForProposedRange: proposedRange granularity: granularity];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
[self lockFocus];
|
|
|
|
|
|
|
|
|
|
// clean up before doing the dragging
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if ([self selectedRange].length == 0) // remove old cursor
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self drawInsertionPointAtIndex: [self selectedRange].location color: nil turnedOn: NO];
|
|
|
|
|
} else [self drawSelectionAsRangeNoCaret: [self selectedRange]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
//<!> make this non - blocking (or make use of timed entries)
|
|
|
|
|
for (currentEvent = [[self window] nextEventMatchingMask: NSLeftMouseDraggedMask|NSLeftMouseUpMask];[currentEvent type] != NSLeftMouseUp;
|
|
|
|
|
(currentEvent = [[self window] nextEventMatchingMask: NSLeftMouseDraggedMask|NSLeftMouseUpMask]), prevChosenRange = chosenRange) // run modal loop
|
1999-08-22 04:04:38 +00:00
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
BOOL didScroll = [self autoscroll: currentEvent];
|
|
|
|
|
point = [self convertPoint: [currentEvent locationInWindow] fromView: nil];
|
|
|
|
|
proposedRange = MakeRangeFromAbs([self characterIndexForPoint: point],startIndex);
|
|
|
|
|
chosenRange = [self selectionRangeForProposedRange: proposedRange granularity: granularity];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
|
|
|
|
|
if (NSEqualRanges(prevChosenRange,chosenRange))
|
|
|
|
|
{ if (!didDragging)
|
|
|
|
|
{ [self drawSelectionAsRangeNoCaret: chosenRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
[[self window] flushWindow];
|
|
|
|
|
}
|
|
|
|
|
else continue;
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// this changes the selection without needing instance drawing (carefully thought out ; - )
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (!didScroll)
|
|
|
|
|
{ [self drawSelectionAsRangeNoCaret: MakeRangeFromAbs(MIN(chosenRange.location, prevChosenRange.location),
|
1998-09-02 15:05:13 +00:00
|
|
|
|
MAX(chosenRange.location, prevChosenRange.location))];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self drawSelectionAsRangeNoCaret: MakeRangeFromAbs(MIN(NSMaxRange(chosenRange),NSMaxRange(prevChosenRange)),
|
1998-09-02 15:05:13 +00:00
|
|
|
|
MAX(NSMaxRange(chosenRange),NSMaxRange(prevChosenRange)))];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
[[self window] flushWindow];
|
|
|
|
|
} else
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self drawRectNoSelection: [self visibleRect]];
|
|
|
|
|
[self drawSelectionAsRangeNoCaret: chosenRange];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
[[self window] flushWindow];
|
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
didDragging = YES;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSDebugLog(@"chosenRange. location = % d, length = %d\n",
|
1999-06-22 00:02:42 +00:00
|
|
|
|
(int)chosenRange.location, (int)chosenRange.length);
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setSelectedRangeNoDrawing: chosenRange];
|
|
|
|
|
if (!didDragging) [self drawSelectionAsRange: chosenRange];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
else if (chosenRange.length == 0) [self drawInsertionPointAtIndex: chosenRange.location color: [NSColor blackColor] turnedOn: YES];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
currentCursorX = [self rectForCharacterIndex: chosenRange.location].origin.x; // remember for column stable cursor up/down
|
|
|
|
|
currentCursorY = [self rectForCharacterIndex: chosenRange.location].origin.y; // remember for column stable cursor up/down
|
1998-09-02 15:05:13 +00:00
|
|
|
|
[self unlockFocus];
|
|
|
|
|
[[self window] flushWindow];
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) redisplayForLineRange: (NSRange) redrawLineRange
|
|
|
|
|
{ BOOL didLock = NO;
|
1999-02-17 12:22:46 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self window] && [[self class] focusView] != self)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ [self lockFocus]; didLock = YES;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([lineLayoutInformation count] && redrawLineRange.location < [lineLayoutInformation count] && redrawLineRange.length)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ _GNULineLayoutInfo *firstInfo = [lineLayoutInformation objectAtIndex: redrawLineRange.location];
|
|
|
|
|
NSRect displayRect,firstRect = [firstInfo lineRect];
|
|
|
|
|
if ([firstInfo type] == LineLayoutInfoType_Paragraph && firstRect.origin.x >0 && redrawLineRange.location)
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ redrawLineRange.location--;redrawLineRange.length++;
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
displayRect = NSUnionRect([[lineLayoutInformation objectAtIndex: redrawLineRange.location] lineRect],
|
|
|
|
|
[[lineLayoutInformation objectAtIndex: MAX(0,(int)NSMaxRange(redrawLineRange) - 1)] lineRect]);
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
displayRect.size.width = [self frame].size.width - displayRect.origin.x;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
[[self backgroundColor] set]; NSRectFill(displayRect);
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText])
|
|
|
|
|
{ [self drawRichLinesInLineRange: redrawLineRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} else
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self drawPlainLinesInLineRange: redrawLineRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self drawSelectionAsRange: [self selectedRange]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self drawsBackground]) // clean up the remaining area under text of us
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ float lowestY = 0;
|
|
|
|
|
NSRect myFrame = [self frame];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if ([lineLayoutInformation count]) lowestY = NSMaxY([[lineLayoutInformation lastObject] lineRect]);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (![lineLayoutInformation count] || (lowestY < NSMaxY(myFrame) && myFrame.size.height<= [self minSize].height))
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ [[self backgroundColor] set];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSRectFill(NSMakeRect(0,lowestY,myFrame.size.width,NSMaxY(myFrame) - lowestY));
|
|
|
|
|
if (![lineLayoutInformation count] || [[lineLayoutInformation lastObject] type] == LineLayoutInfoType_Paragraph)
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self drawSelectionAsRange: [self selectedRange]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
} if (didLock)
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ [self unlockFocus];
|
|
|
|
|
[[self window] flushWindow];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) rebuildFromCharacterIndex: (int) anIndex
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{ NSRange redrawLineRange;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
int start,count = [self rebuildLineLayoutInformationStartingAtLine: start = [self lineLayoutIndexForCharacterIndex: anIndex]];
|
|
|
|
|
redrawLineRange = NSMakeRange(MAX(0,start - 1),count + 1);
|
|
|
|
|
redrawLineRange = NSIntersectionRange(redrawLineRange,[self lineRangeForRect: [self visibleRect]]);
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self redisplayForLineRange: redrawLineRange];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
NSLog(NSStringFromRange(redrawLineRange));
|
|
|
|
|
}
|
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
// central text inserting method (takes care of optimized redraw/ cursor positioning)
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) insertText: insertObjc
|
|
|
|
|
{ NSRange selectedRange = [self selectedRange];
|
|
|
|
|
int lineIndex = [self lineLayoutIndexForCharacterIndex: selectedRange.location],origLineIndex = lineIndex,caretLineIndex = lineIndex;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSRange redrawLineRange;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSString *insertString = nil;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if ([insertObjc isKindOfClass: [NSString class]]) insertString = insertObjc;
|
|
|
|
|
else insertString = [insertObjc string];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
// in case e.g a space is inserted and a word actually shortened: redraw previous line to give it the chance to move up
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([lineLayoutInformation count] && [[lineLayoutInformation objectAtIndex: origLineIndex] type] != LineLayoutInfoType_Paragraph &&
|
1999-11-02 07:58:11 +00:00
|
|
|
|
origLineIndex && [[lineLayoutInformation objectAtIndex: origLineIndex - 1] type] != LineLayoutInfoType_Paragraph)
|
1998-10-15 12:04:53 +00:00
|
|
|
|
origLineIndex--;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
redrawLineRange = MakeRangeFromAbs(origLineIndex,[lineLayoutInformation count]);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText])
|
|
|
|
|
{ [self replaceRange: [self selectedRange]
|
|
|
|
|
withAttributedString: [insertObjc isKindOfClass: [NSAttributedString class]]? insertObjc:
|
|
|
|
|
[[[NSAttributedString alloc] initWithString: insertString attributes: [self typingAttributes]]
|
1999-06-22 23:37:24 +00:00
|
|
|
|
autorelease]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} else
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self replaceRange: [self selectedRange] withString: insertString];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
redrawLineRange.length = [self rebuildLineLayoutInformationStartingAtLine: redrawLineRange.location
|
|
|
|
|
delta: [insertString length] - selectedRange.length
|
1999-08-31 09:19:39 +00:00
|
|
|
|
actualLine: caretLineIndex];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
[self sizeToFit]; // ScrollView interaction
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
[self setSelectedRange: NSMakeRange([self selectedRange].location + [insertString length],0)]; // move cursor <!> [self selectionRangeForProposedRange: ]
|
|
|
|
|
currentCursorX = [self rectForCharacterIndex: [self selectedRange].location].origin.x; // remember x for row - stable cursor movements
|
|
|
|
|
currentCursorY = [self rectForCharacterIndex: [self selectedRange].location].origin.y; // remember x for row - stable cursor movements
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
redrawLineRange = NSIntersectionRange(redrawLineRange,[self lineRangeForRect: [self visibleRect]]);
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self redisplayForLineRange: redrawLineRange];
|
|
|
|
|
[self textDidChange: nil]; // broadcast notification
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
// central text deletion/backspace method (takes care of optimized redraw/ cursor positioning)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) deleteRange: (NSRange) aRange backspace: (BOOL) flag
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{ int redrawLineIndex,caretLineIndex,firstLineIndex,lastLineIndex,linePosition;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSRange redrawLineRange;
|
|
|
|
|
NSRange deleteRange;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (!aRange.length && !flag) return;
|
|
|
|
|
if (!aRange.location && ! aRange.length) return;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (aRange.length) { deleteRange = aRange;linePosition = deleteRange.location;}
|
|
|
|
|
else { deleteRange = NSMakeRange(MAX(0,aRange.location - 1),1);linePosition = NSMaxRange(deleteRange); }
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
firstLineIndex = caretLineIndex = [self lineLayoutIndexForCharacterIndex: linePosition];
|
|
|
|
|
lastLineIndex = [self lineLayoutIndexForCharacterIndex: NSMaxRange(deleteRange)];
|
|
|
|
|
redrawLineIndex = MAX(0,firstLineIndex - 1); // since first word may move upward
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (firstLineIndex && [[lineLayoutInformation objectAtIndex: firstLineIndex - 1] type] == LineLayoutInfoType_Paragraph)
|
|
|
|
|
{ _GNULineLayoutInfo *upperInfo = [lineLayoutInformation objectAtIndex: firstLineIndex],
|
|
|
|
|
*prevInfo = [lineLayoutInformation objectAtIndex: firstLineIndex - 1];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (linePosition> [upperInfo lineRange].location) redrawLineIndex++; // no danger of word moving up
|
1999-11-02 07:58:11 +00:00
|
|
|
|
else if ([prevInfo lineRect].origin.x > 0) redrawLineIndex--; // remove newline: skip paragraph - terminating infoObject
|
|
|
|
|
redrawLineIndex = MAX(0,redrawLineIndex);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
redrawLineIndex = MIN(redrawLineIndex,[lineLayoutInformation count] - 1);
|
|
|
|
|
redrawLineRange = MakeRangeFromAbs(redrawLineIndex,[lineLayoutInformation count]);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText]) [rtfContent deleteCharactersInRange: deleteRange];
|
|
|
|
|
else [plainContent deleteCharactersInRange: deleteRange];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
redrawLineRange.length = [self rebuildLineLayoutInformationStartingAtLine: redrawLineRange.location
|
|
|
|
|
delta: - deleteRange.length
|
1999-08-31 09:19:39 +00:00
|
|
|
|
actualLine: caretLineIndex];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
[self sizeToFit]; // ScrollView interaction
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self setSelectedRange: NSMakeRange(deleteRange.location,0)]; // move cursor <!> [self selectionRangeForProposedRange: ]
|
1999-11-02 07:58:11 +00:00
|
|
|
|
currentCursorX = [self rectForCharacterIndex: [self selectedRange].location].origin.x; // remember x for row - stable cursor movements
|
|
|
|
|
currentCursorY = [self rectForCharacterIndex: [self selectedRange].location].origin.y; // remember x for row - stable cursor movements
|
|
|
|
|
redrawLineRange = NSIntersectionRange(redrawLineRange,[self lineRangeForRect: [self visibleRect]]);
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self redisplayForLineRange: redrawLineRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self textDidChange: nil]; // broadcast notification
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) keyDown: (NSEvent *)theEvent
|
1999-11-09 23:07:31 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned short keyCode;
|
|
|
|
|
|
|
|
|
|
// If not editable then don't recognize the key down
|
|
|
|
|
if (!is_editable)
|
|
|
|
|
{
|
|
|
|
|
[super keyDown: theEvent];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyCode = [theEvent keyCode];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-09 23:07:31 +00:00
|
|
|
|
// Some keys are extremely special if we are field editor
|
|
|
|
|
if ([self isFieldEditor])
|
|
|
|
|
{
|
|
|
|
|
int notNumber = NSIllegalTextMovement;
|
|
|
|
|
|
|
|
|
|
switch (keyCode)
|
|
|
|
|
{
|
|
|
|
|
case NSUpArrowFunctionKey:
|
|
|
|
|
notNumber = NSUpTextMovement;
|
|
|
|
|
break;
|
|
|
|
|
case NSDownArrowFunctionKey:
|
|
|
|
|
notNumber = NSDownTextMovement;
|
|
|
|
|
break;
|
|
|
|
|
case NSLeftArrowFunctionKey:
|
|
|
|
|
//notNumber = NSLeftTextMovement;
|
|
|
|
|
break;
|
|
|
|
|
case NSRightArrowFunctionKey:
|
|
|
|
|
//notNumber = NSRightTextMovement;
|
|
|
|
|
break;
|
|
|
|
|
case NSCarriageReturnKey:
|
|
|
|
|
notNumber = NSReturnTextMovement;
|
|
|
|
|
break;
|
|
|
|
|
case 0x09:
|
|
|
|
|
if ([theEvent modifierFlags] & NSShiftKeyMask)
|
|
|
|
|
notNumber = NSBacktabTextMovement;
|
|
|
|
|
else
|
|
|
|
|
notNumber = NSTabTextMovement;
|
|
|
|
|
break;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-09 23:07:31 +00:00
|
|
|
|
if (notNumber != NSIllegalTextMovement)
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{
|
1999-11-09 23:07:31 +00:00
|
|
|
|
if ([self resignFirstResponder])
|
|
|
|
|
{
|
|
|
|
|
NSNumber *number;
|
|
|
|
|
NSDictionary *uiDictionary;
|
|
|
|
|
NSNotification *notification;
|
|
|
|
|
|
|
|
|
|
number = [NSNumber numberWithInt: notNumber];
|
|
|
|
|
uiDictionary = [NSDictionary dictionaryWithObjectsAndKeys: number,
|
|
|
|
|
@"NSTextMovement", NULL];
|
|
|
|
|
notification = [NSNotification notificationWithName:
|
|
|
|
|
NSTextDidEndEditingNotification
|
|
|
|
|
object: self
|
|
|
|
|
userInfo: uiDictionary];
|
|
|
|
|
[self textDidEndEditing: notification];
|
|
|
|
|
}
|
|
|
|
|
return;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-09 23:07:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Special Characters for generic NSText
|
|
|
|
|
switch(keyCode)
|
|
|
|
|
{
|
|
|
|
|
case NSUpArrowFunctionKey:
|
|
|
|
|
[self moveCursorUp: self];
|
|
|
|
|
return;
|
|
|
|
|
case NSDownArrowFunctionKey:
|
|
|
|
|
[self moveCursorDown: self];
|
|
|
|
|
return;
|
|
|
|
|
case NSLeftArrowFunctionKey:
|
|
|
|
|
[self moveCursorLeft: self];
|
|
|
|
|
return;
|
|
|
|
|
case NSRightArrowFunctionKey:
|
|
|
|
|
[self moveCursorRight: self];
|
|
|
|
|
return;
|
|
|
|
|
case NSBackspaceKey:
|
|
|
|
|
[self deleteRange: [self selectedRange] backspace: YES];
|
|
|
|
|
return;
|
|
|
|
|
#if 1
|
|
|
|
|
case 0x6d: // end - key: debugging: enforce complete re - layout
|
|
|
|
|
[lineLayoutInformation autorelease];
|
|
|
|
|
lineLayoutInformation = nil;
|
|
|
|
|
[self rebuildLineLayoutInformationStartingAtLine: 0];
|
|
|
|
|
[self setNeedsDisplay: YES];
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
#if 1
|
|
|
|
|
// TODO: Choose another key
|
|
|
|
|
case 0x45: // num - lock: debugging
|
|
|
|
|
NSLog ([lineLayoutInformation description]);
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
case NSCarriageReturnKey:
|
|
|
|
|
NSLog (@"\bCarriage return.\b\n");
|
|
|
|
|
[self insertText: [[self class] newlineString]];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the character(s) was not a special one, simply insert it.
|
|
|
|
|
[self insertText: [theEvent characters]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) acceptsFirstResponder
|
1999-11-09 23:07:31 +00:00
|
|
|
|
{
|
|
|
|
|
if ([self isSelectable])
|
|
|
|
|
return YES;
|
|
|
|
|
else
|
|
|
|
|
return NO;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) resignFirstResponder
|
1999-11-09 23:07:31 +00:00
|
|
|
|
{
|
|
|
|
|
if ([self isEditable])
|
|
|
|
|
if ([self textShouldEndEditing: self] == NO)
|
|
|
|
|
return NO;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-11-09 23:07:31 +00:00
|
|
|
|
// Add any clean-up stuff here
|
|
|
|
|
|
|
|
|
|
if ([self shouldDrawInsertionPoint])
|
|
|
|
|
{
|
|
|
|
|
[self drawInsertionPointAtIndex: [self selectedRange].location
|
|
|
|
|
color: nil turnedOn: NO];
|
|
|
|
|
|
|
|
|
|
//<!> stop timed entry
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self textDidEndEditing: nil];
|
|
|
|
|
return YES;
|
1999-02-10 15:09:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) becomeFirstResponder
|
1999-08-22 04:04:38 +00:00
|
|
|
|
{
|
1999-11-09 23:07:31 +00:00
|
|
|
|
if ([self isSelectable])
|
|
|
|
|
if ([self textShouldBeginEditing: self])
|
|
|
|
|
{
|
|
|
|
|
// Add any initialization stuff here.
|
|
|
|
|
|
|
|
|
|
//if ([self shouldDrawInsertionPoint])
|
|
|
|
|
// {
|
|
|
|
|
// [self lockFocus];
|
|
|
|
|
// [self drawInsertionPointAtIndex: [self selectedRange].location
|
|
|
|
|
// color: [NSColor blackColor] turnedOn: YES];
|
|
|
|
|
// [self unlockFocus];
|
|
|
|
|
// //<!> restart timed entry
|
|
|
|
|
// }
|
|
|
|
|
[self textDidBeginEditing: nil];
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NO;
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* Managing the Delegate
|
|
|
|
|
*/
|
|
|
|
|
- (id) delegate
|
|
|
|
|
{
|
|
|
|
|
return delegate;
|
|
|
|
|
}
|
1999-02-17 09:13:43 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void) setDelegate: (id)anObject
|
1999-02-17 09:13:43 +00:00
|
|
|
|
{
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
|
1999-02-17 09:13:43 +00:00
|
|
|
|
|
|
|
|
|
if (delegate)
|
|
|
|
|
[nc removeObserver: delegate name: nil object: self];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
ASSIGN(delegate, anObject);
|
1999-02-17 09:13:43 +00:00
|
|
|
|
|
|
|
|
|
#define SET_DELEGATE_NOTIFICATION(notif_name) \
|
|
|
|
|
if ([delegate respondsToSelector: @selector(text##notif_name:)]) \
|
|
|
|
|
[nc addObserver: delegate \
|
1999-06-22 23:37:24 +00:00
|
|
|
|
selector: @selector(text##notif_name:) \
|
|
|
|
|
name: NSText##notif_name##Notification \
|
|
|
|
|
object: self]
|
|
|
|
|
|
1999-02-17 09:13:43 +00:00
|
|
|
|
SET_DELEGATE_NOTIFICATION(DidBeginEditing);
|
|
|
|
|
SET_DELEGATE_NOTIFICATION(DidChange);
|
|
|
|
|
SET_DELEGATE_NOTIFICATION(DidEndEditing);
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
/*
|
|
|
|
|
* Implemented by the Delegate
|
|
|
|
|
*/
|
|
|
|
|
- (void) textDidBeginEditing: (NSNotification *)aNotification
|
1999-11-09 23:07:31 +00:00
|
|
|
|
{
|
|
|
|
|
if ([delegate respondsToSelector: @selector(textDidBeginEditing: )])
|
|
|
|
|
{
|
|
|
|
|
if (!aNotification)
|
|
|
|
|
aNotification = [NSNotification notificationWithName:
|
|
|
|
|
NSTextDidBeginEditingNotification
|
|
|
|
|
object: self];
|
|
|
|
|
[delegate textDidBeginEditing: aNotification];
|
|
|
|
|
}
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-11-09 23:07:31 +00:00
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
postNotificationName: NSTextDidBeginEditingNotification
|
|
|
|
|
object: (id)self];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)textDidChange: (NSNotification *)aNotification
|
1999-11-09 23:07:31 +00:00
|
|
|
|
{
|
|
|
|
|
if ([delegate respondsToSelector: @selector(textDidChange: )])
|
|
|
|
|
{
|
|
|
|
|
if (!aNotification)
|
|
|
|
|
aNotification = [NSNotification notificationWithName:
|
|
|
|
|
NSTextDidChangeNotification
|
|
|
|
|
object: (id)self];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-11-09 23:07:31 +00:00
|
|
|
|
[delegate textDidChange: aNotification];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
postNotificationName: NSTextDidChangeNotification
|
|
|
|
|
object: (id)self];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void)textDidEndEditing: (NSNotification *)aNotification
|
1999-11-09 23:07:31 +00:00
|
|
|
|
{
|
|
|
|
|
if ([delegate respondsToSelector: @selector(textDidEndEditing: )])
|
|
|
|
|
{
|
|
|
|
|
if (!aNotification)
|
|
|
|
|
aNotification = [NSNotification notificationWithName:
|
|
|
|
|
NSTextDidEndEditingNotification
|
|
|
|
|
object: (id)self];
|
|
|
|
|
[delegate textDidEndEditing: aNotification];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[[NSNotificationCenter defaultCenter]
|
|
|
|
|
postNotificationName: NSTextDidEndEditingNotification
|
|
|
|
|
object: (id)self];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) textShouldEndEditing: (NSText *)textObject
|
1999-11-09 23:07:31 +00:00
|
|
|
|
{
|
|
|
|
|
if ([delegate respondsToSelector: @selector(textShouldEndEditing: )])
|
|
|
|
|
return [delegate textShouldEndEditing: self];
|
|
|
|
|
else
|
|
|
|
|
return YES;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSRange) characterRangeForBoundingRect: (NSRect)boundsRect
|
|
|
|
|
{ NSRange lineRange = [self lineRangeForRect: boundsRect];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (lineRange.length) return MakeRangeFromAbs([[lineLayoutInformation objectAtIndex: lineRange.location] lineRange].location,
|
|
|
|
|
NSMaxRange([[lineLayoutInformation objectAtIndex: NSMaxRange(lineRange)] lineRange]));
|
1999-06-22 23:37:24 +00:00
|
|
|
|
else return NSMakeRange(0,0);
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (unsigned) characterIndexForPoint: (NSPoint)point
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ int i;
|
|
|
|
|
NSEnumerator *lineEnum;
|
|
|
|
|
_GNULineLayoutInfo *currentInfo;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSDictionary *attributes = [self defaultTypingAttributes];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (point.y >= NSMaxY([[lineLayoutInformation lastObject] lineRect])) return [self textLength];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
point.x = MAX(0,point.x); point.y = MAX(0,point.y);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (i = 0,(lineEnum = [lineLayoutInformation objectEnumerator]);(currentInfo = [lineEnum nextObject]);i++)
|
|
|
|
|
{ NSRect rect = [currentInfo lineRect];
|
|
|
|
|
if (NSMaxY(rect)>= point.y && rect.origin.y<point.y && rect.origin.x< point.x && point.x >= NSMaxX(rect) ) return NSMaxRange([currentInfo lineRange]);
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (NSPointInRect(point,rect)) // this loop holds some optimization potential (linear search)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ int retPos = 0;
|
|
|
|
|
NSRange range = [currentInfo lineRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (retPos = range.location; retPos<= NSMaxRange(range); retPos++) // this loop holds some optimization potential (linear search)
|
|
|
|
|
{ NSString *evalString = nil;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ if ([rtfContent sizeRange: NSMakeRange(range.location,retPos - range.location)].width >= point.x) return MAX(0,retPos - 1);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ evalString = [plainContent substringWithRange: NSMakeRange(range.location,retPos - range.location)];
|
|
|
|
|
if ([evalString sizeWithAttributes: attributes].width >= point.x) return MAX(0,retPos - 1);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
} return range.location;
|
|
|
|
|
}
|
|
|
|
|
} NSLog(@"NSText's characterIndexForPoint: index not found!");
|
|
|
|
|
return 0;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// rect to the end of line
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSRect) rectForCharacterIndex: (unsigned) index
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ int i;
|
|
|
|
|
NSEnumerator *lineEnum;
|
|
|
|
|
_GNULineLayoutInfo *currentInfo;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSDictionary *attributes = [self defaultTypingAttributes];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (![lineLayoutInformation count]) return NSMakeRect(0,0,[self frame].size.width,[[[self class] newlineString] sizeWithAttributes: attributes].height);
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (index >= NSMaxRange([[lineLayoutInformation lastObject] lineRange]))
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSRect rect = [[lineLayoutInformation lastObject] lineRect];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (NSMaxX(rect)>= [self frame].size.width)
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ return NSMakeRect(0, NSMaxY(rect),[self frame].size.width,rect.size.height);
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
return NSMakeRect(NSMaxX(rect), rect.origin.y,[self frame].size.width - NSMaxX(rect),rect.size.height);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (i = 0,(lineEnum = [lineLayoutInformation objectEnumerator]);(currentInfo = [lineEnum nextObject]);i++)
|
|
|
|
|
{ NSRange range = [currentInfo lineRange];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (NSLocationInRange(index,range))
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSRect rect = [currentInfo lineRect];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSSize stringSize = [rtfContent sizeRange: MakeRangeFromAbs(range.location,index)];
|
|
|
|
|
float x = rect.origin.x + stringSize.width;
|
|
|
|
|
return NSMakeRect(x,rect.origin.y,NSMaxX(rect) - x,rect.size.height);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSString *evalString = [plainContent substringWithRange: MakeRangeFromAbs(range.location,index)];
|
|
|
|
|
NSSize stringSize = [evalString sizeWithAttributes: attributes];
|
|
|
|
|
float x = rect.origin.x + stringSize.width;
|
|
|
|
|
return NSMakeRect(x,rect.origin.y,NSMaxX(rect) - x,rect.size.height);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} NSLog(@"NSText's rectForCharacterIndex: rect not found!");
|
|
|
|
|
return NSZeroRect;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (unsigned) lineLayoutIndexForPoint: (NSPoint)point
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{ int i;
|
|
|
|
|
NSEnumerator *lineEnum;
|
|
|
|
|
_GNULineLayoutInfo *currentInfo;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSDictionary *attributes = [self defaultTypingAttributes];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (point.y >= NSMaxY([[lineLayoutInformation lastObject] lineRect])) return [lineLayoutInformation count] - 1;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
point.x = MAX(0,point.x); point.y = MAX(0,point.y);
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (i = 0,(lineEnum = [lineLayoutInformation objectEnumerator]); (currentInfo = [lineEnum nextObject]);i++)
|
|
|
|
|
{ NSRect rect = [currentInfo lineRect];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (NSMaxY(rect)> point.y && rect.origin.y<= point.y && rect.origin.x< point.x && point.x >= NSMaxX(rect) )
|
|
|
|
|
return [lineLayoutInformation indexOfObject: currentInfo];
|
|
|
|
|
if (NSPointInRect(point,rect)) // this loop holds some optimization potential (linear search)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ int retPos = 0;
|
|
|
|
|
NSRange range = [currentInfo lineRange];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (retPos = range.location; retPos<= NSMaxRange(range); retPos++) // this loop holds some optimization potential (linear search)
|
|
|
|
|
{ NSString *evalString = nil;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ if ([rtfContent sizeRange: NSMakeRange(range.location,retPos - range.location)].width >= point.x)
|
1999-08-31 09:19:39 +00:00
|
|
|
|
return [lineLayoutInformation indexOfObject: currentInfo];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
} else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ evalString = [plainContent substringWithRange: NSMakeRange(range.location,retPos - range.location)];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([evalString sizeWithAttributes: attributes].width >= point.x) return [lineLayoutInformation indexOfObject: currentInfo];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
} return [lineLayoutInformation indexOfObject: currentInfo];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
} return 0;
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
// internal method <!> range is currently not passed as absolute
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) addNewlines: (NSRange) aRange intoLayoutArray: (NSMutableArray*) anArray attributes: (NSDictionary*) attributes atPoint: (NSPoint*) aPointP
|
1999-08-31 09:19:39 +00:00
|
|
|
|
width: (float) width characterIndex: (unsigned) startingLineCharIndex ghostEnumerator: (_GNUSeekableArrayEnumerator*) prevArrayEnum
|
|
|
|
|
didShift: (BOOL*) didShift verticalDisplacement: (float*) verticalDisplacement
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSSize advanceSize = [[[self class] newlineString] sizeWithAttributes: attributes];
|
|
|
|
|
int count = aRange.length,charIndex;
|
|
|
|
|
_GNULineLayoutInfo *thisInfo,*ghostInfo = nil;
|
|
|
|
|
BOOL isRich = [self isRichText];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
(*didShift) = NO;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (charIndex = aRange.location;--count>= 0;charIndex++)
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{ NSRect currentLineRect;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (0&& isRich)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ advanceSize = [rtfContent sizeRange: NSMakeRange(startingLineCharIndex,1)];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
currentLineRect = NSMakeRect(aPointP ->x,aPointP ->y,width - aPointP ->x,advanceSize.height);
|
|
|
|
|
[anArray addObject: thisInfo = [_GNULineLayoutInfo lineLayoutWithRange:
|
1999-08-31 09:19:39 +00:00
|
|
|
|
NSMakeRange(startingLineCharIndex,1) rect: currentLineRect drawingOffset: 0 type: LineLayoutInfoType_Paragraph]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
startingLineCharIndex++; aPointP ->x = 0; aPointP ->y += advanceSize.height;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (prevArrayEnum && !(ghostInfo = [prevArrayEnum nextObject])) prevArrayEnum = nil;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (ghostInfo && ([thisInfo type] != [ghostInfo type]))
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ _GNULineLayoutInfo *prevInfo = [prevArrayEnum previousObject];
|
|
|
|
|
prevArrayEnum = nil;
|
|
|
|
|
(*didShift) = YES;
|
|
|
|
|
(*verticalDisplacement) += aPointP ->y - [prevInfo lineRect].origin.y;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
// private helper function
|
1998-10-15 12:04:53 +00:00
|
|
|
|
static unsigned _relocLayoutArray(NSMutableArray *lineLayoutInformation,NSArray *ghostArray,int aLine,int relocOffset,int rebuildLineDrift,float yReloc)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ unsigned ret = [lineLayoutInformation count] - aLine; // lines actually updated (optimized drawing)
|
|
|
|
|
NSArray *relocArray = [ghostArray subarrayWithRange: MakeRangeFromAbs(MAX(0,ret + rebuildLineDrift),[ghostArray count])];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
NSEnumerator *relocEnum;
|
|
|
|
|
_GNULineLayoutInfo *currReloc;
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (![relocArray count]) return ret;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for ((relocEnum = [relocArray objectEnumerator]); (currReloc = [relocEnum nextObject]);)
|
|
|
|
|
{ NSRange range = [currReloc lineRange];
|
|
|
|
|
[currReloc setLineRange: NSMakeRange(range.location + relocOffset,range.length)];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (yReloc)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSRect rect = [currReloc lineRect];
|
|
|
|
|
[currReloc setLineRect: NSMakeRect(rect.origin.x,rect.origin.y + yReloc,rect.size.width,rect.size.height)];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[lineLayoutInformation addObjectsFromArray: relocArray];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
return ret;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// begin: central line formatting method---------------------------------------
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// returns count of lines actually updated
|
1999-08-31 09:19:39 +00:00
|
|
|
|
// <!> detachNewThreadSelector: selector toTarget: target withObject: argument;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (int) rebuildLineLayoutInformationStartingAtLine: (int) aLine delta: (int) insertionDelta actualLine: (int) insertionLineIndex
|
|
|
|
|
{ NSDictionary *attributes = [self defaultTypingAttributes];
|
|
|
|
|
NSPoint drawingPoint = NSZeroPoint;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
_GNUTextScanner *parscanner;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
float width = [self frame].size.width;
|
|
|
|
|
unsigned startingIndex = 0,currentLineIndex;
|
|
|
|
|
_GNULineLayoutInfo *lastValidLineInfo = nil;
|
|
|
|
|
NSArray *ghostArray = nil; // for optimization detection
|
|
|
|
|
_GNUSeekableArrayEnumerator *prevArrayEnum = nil;
|
|
|
|
|
NSCharacterSet *invSelectionWordGranularitySet = [selectionWordGranularitySet invertedSet];
|
|
|
|
|
NSCharacterSet *invSelectionParagraphGranularitySet = [selectionParagraphGranularitySet invertedSet];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
NSString *parsedString;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
BOOL isHorizontallyResizable = [self isHorizontallyResizable];
|
|
|
|
|
int lineDriftOffset = 0,rebuildLineDrift = 0;
|
|
|
|
|
BOOL frameshiftCorrection = NO,nlDidShift = NO,enforceOpti = NO;
|
|
|
|
|
float yDisplacement = 0;
|
|
|
|
|
BOOL isRich = [self isRichText];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (!lineLayoutInformation) lineLayoutInformation = [[NSMutableArray alloc] init];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ ghostArray = [lineLayoutInformation subarrayWithRange: NSMakeRange(aLine,[lineLayoutInformation count] - aLine)]; // remember old array for optimization purposes
|
|
|
|
|
prevArrayEnum = [ghostArray seekableEnumerator]; // every time an object is added to lineLayoutInformation a nextObject has to be performed on prevArrayEnum!
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (aLine)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ lastValidLineInfo = [lineLayoutInformation objectAtIndex: aLine - 1];
|
|
|
|
|
drawingPoint = [lastValidLineInfo lineRect].origin;
|
|
|
|
|
drawingPoint.y += [lastValidLineInfo lineRect].size.height;
|
|
|
|
|
startingIndex = NSMaxRange([lastValidLineInfo lineRange]);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if ([lastValidLineInfo type] == LineLayoutInfoType_Paragraph)
|
|
|
|
|
{ drawingPoint.x = 0;
|
|
|
|
|
} if ((((int)[lineLayoutInformation count]) - 1) >= aLine) // keep paragraph - terminating space on same line as paragraph
|
|
|
|
|
{ _GNULineLayoutInfo *anchorLine = [lineLayoutInformation objectAtIndex: aLine];
|
|
|
|
|
NSRect anchorRect = [anchorLine lineRect];
|
|
|
|
|
if (anchorRect.origin.x> drawingPoint.x && [lastValidLineInfo lineRect].origin.y == anchorRect.origin.y)
|
|
|
|
|
{ drawingPoint = anchorRect.origin;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
[lineLayoutInformation removeObjectsInRange: NSMakeRange(aLine,[lineLayoutInformation count] - aLine)];
|
|
|
|
|
currentLineIndex = aLine;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
// each paragraph
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (parscanner = [_GNUTextScanner scannerWithString: parsedString = [[self string] substringFromIndex: startingIndex]
|
1999-08-31 09:19:39 +00:00
|
|
|
|
set: selectionParagraphGranularitySet invertedSet: invSelectionParagraphGranularitySet];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
![parscanner isAtEnd];)
|
1999-06-22 23:37:24 +00:00
|
|
|
|
{ _GNUTextScanner *linescanner;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSString *paragraph;
|
|
|
|
|
NSRange paragraphRange,leadingNlRange,trailingNlRange;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
unsigned startingParagraphIndex = [parscanner scanLocation] + startingIndex,startingLineCharIndex = startingParagraphIndex;
|
|
|
|
|
BOOL isBuckled = NO,inBuckling = NO;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
leadingNlRange = [parscanner scanSetCharacters];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (leadingNlRange.length) // add the leading newlines of current paragraph if any (only the first time)
|
|
|
|
|
{ [self addNewlines: leadingNlRange intoLayoutArray: lineLayoutInformation attributes: attributes atPoint: &drawingPoint width: width
|
|
|
|
|
characterIndex: startingLineCharIndex ghostEnumerator: prevArrayEnum
|
|
|
|
|
didShift: &nlDidShift verticalDisplacement: &yDisplacement];
|
|
|
|
|
if (nlDidShift)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ if (insertionDelta == 1)
|
|
|
|
|
{ frameshiftCorrection = YES;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
rebuildLineDrift--;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
} else if (insertionDelta == - 1)
|
|
|
|
|
{ frameshiftCorrection = YES;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
rebuildLineDrift++;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
} else nlDidShift = NO;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
startingLineCharIndex += leadingNlRange.length; currentLineIndex += leadingNlRange.length;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
paragraphRange = [parscanner scanNonSetCharacters];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
trailingNlRange = [parscanner scanSetCharacters];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
// each line
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (linescanner = [_GNUTextScanner scannerWithString: paragraph = [parsedString substringWithRange: paragraphRange]
|
1999-08-31 09:19:39 +00:00
|
|
|
|
set: selectionWordGranularitySet invertedSet: invSelectionWordGranularitySet];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
![linescanner isAtEnd];)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSRect currentLineRect = NSMakeRect(0,drawingPoint.y,0,0);
|
|
|
|
|
unsigned localLineStartIndex = [linescanner scanLocation]; // starts with zero, do not confuse with startingLineCharIndex
|
|
|
|
|
NSSize advanceSize = NSZeroSize;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
// scan the individual words to the end of the line
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (;![linescanner isAtEnd]; drawingPoint.x += advanceSize.width)
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ NSRange currentStringRange,trailingSpacesRange,leadingSpacesRange;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
unsigned scannerPosition = [linescanner scanLocation];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
// snack next word
|
1999-11-02 07:58:11 +00:00
|
|
|
|
leadingSpacesRange = [linescanner scanSetCharacters]; // leading spaces: only first time
|
|
|
|
|
currentStringRange = [linescanner scanNonSetCharacters];
|
|
|
|
|
trailingSpacesRange = [linescanner scanSetCharacters];
|
|
|
|
|
if (leadingSpacesRange.length) currentStringRange = NSUnionRange (leadingSpacesRange,currentStringRange);
|
|
|
|
|
if (trailingSpacesRange.length) currentStringRange = NSUnionRange(trailingSpacesRange,currentStringRange);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
// evaluate size of current word and line so far
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (isRich) advanceSize = [rtfContent sizeRange: NSMakeRange(currentStringRange.location + paragraphRange.location + startingIndex,currentStringRange.length)];
|
|
|
|
|
else advanceSize = [[paragraph substringWithRange: currentStringRange] sizeWithAttributes: attributes];
|
|
|
|
|
currentLineRect = NSUnionRect(currentLineRect,NSMakeRect(drawingPoint.x,drawingPoint.y, advanceSize.width, advanceSize.height));
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-06-22 23:37:24 +00:00
|
|
|
|
// handle case where single word is broader than width (buckle word) <!> unfinished and untested for richText (absolute position see above)
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (!isHorizontallyResizable && advanceSize.width >= width)
|
|
|
|
|
{ if (isBuckled)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSSize currentSize = NSMakeSize(HUGE,0);
|
1998-10-15 12:04:53 +00:00
|
|
|
|
unsigned lastVisibleCharIndex;
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for (lastVisibleCharIndex = startingLineCharIndex + currentStringRange.length;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
currentSize.width>= width && lastVisibleCharIndex> startingLineCharIndex;
|
|
|
|
|
lastVisibleCharIndex--)
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (isRich)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ currentSize = [rtfContent sizeRange: MakeRangeFromAbs(startingLineCharIndex,lastVisibleCharIndex)];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
} else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSString *evalString = [plainContent substringWithRange: MakeRangeFromAbs(startingLineCharIndex,lastVisibleCharIndex)];
|
|
|
|
|
currentSize = [evalString sizeWithAttributes: attributes];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
isBuckled = NO; inBuckling = YES;
|
|
|
|
|
scannerPosition = localLineStartIndex + (lastVisibleCharIndex - startingLineCharIndex);
|
|
|
|
|
currentLineRect.size.width = advanceSize.width = width;
|
1999-06-22 23:37:24 +00:00
|
|
|
|
} else // undo layout of extralarge word (will be done the next line [see above])
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ isBuckled = YES;
|
|
|
|
|
currentLineRect.size.width -= advanceSize.width;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// end of line -> word wrap
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (!isHorizontallyResizable && (currentLineRect.size.width >= width || isBuckled)) // >= : wichtig f<EFBFBD>r abknicken (isBuckled)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ _GNULineLayoutInfo *ghostInfo = nil,*thisInfo;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[linescanner setScanLocation: scannerPosition]; // undo layout of last word
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
currentLineRect.origin.x = 0; currentLineRect.origin.y = drawingPoint.y;
|
|
|
|
|
drawingPoint.y += currentLineRect.size.height; drawingPoint.x = 0;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
[lineLayoutInformation addObject: thisInfo = [_GNULineLayoutInfo lineLayoutWithRange:
|
|
|
|
|
NSMakeRange(startingLineCharIndex,scannerPosition - localLineStartIndex)
|
1999-08-31 09:19:39 +00:00
|
|
|
|
rect: currentLineRect drawingOffset: 0 type: LineLayoutInfoType_Text]];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
currentLineIndex++;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
startingLineCharIndex = NSMaxRange([thisInfo lineRange]);
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (prevArrayEnum && !(ghostInfo = [prevArrayEnum nextObject])) prevArrayEnum = nil;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// optimization stuff (do relayout only as much lines as necessary and patch the rest)---------
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (ghostInfo)
|
|
|
|
|
{ if ([ghostInfo type] != [thisInfo type]) // frameshift correction
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ frameshiftCorrection = YES;
|
|
|
|
|
if (insertionDelta == - 1) // deletition of newline
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{ _GNULineLayoutInfo *nextObject;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (!(nextObject = [prevArrayEnum nextObject])) prevArrayEnum = nil;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
else
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (nlDidShift && frameshiftCorrection)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{// frameshiftCorrection = NO;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
#if 0
|
|
|
|
|
NSLog(@"opti hook 1 (preferred)");
|
|
|
|
|
#endif
|
|
|
|
|
} else
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ lineDriftOffset += ([thisInfo lineRange].length - [ghostInfo lineRange].length - [nextObject lineRange].length);
|
|
|
|
|
yDisplacement += [thisInfo lineRect].origin.y - [nextObject lineRect].origin.y;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
rebuildLineDrift++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
} else lineDriftOffset += ([thisInfo lineRange].length - [ghostInfo lineRange].length);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// is it possible to simply patch layout changes into layout array instead of doing a time consuming re - layout of the whole doc?
|
|
|
|
|
if ((currentLineIndex - 1 > insertionLineIndex && !inBuckling && !isBuckled) &&
|
|
|
|
|
(!(lineDriftOffset - insertionDelta) || (nlDidShift && !lineDriftOffset) || enforceOpti))
|
|
|
|
|
{ unsigned erg = _relocLayoutArray(lineLayoutInformation,ghostArray,aLine,insertionDelta, rebuildLineDrift,
|
1998-10-15 12:04:53 +00:00
|
|
|
|
yDisplacement);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (frameshiftCorrection) erg = [lineLayoutInformation count] - aLine; // y displacement: redisplay all remaining lines
|
|
|
|
|
else if (currentLineIndex - 1 == insertionLineIndex && ABS(insertionDelta) == 1)
|
|
|
|
|
{ erg = 2; // return 2: redisplay only this and previous line
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
1999-08-22 04:04:38 +00:00
|
|
|
|
#if 0
|
1999-08-31 09:19:39 +00:00
|
|
|
|
NSLog(@"opti for: %d",erg);
|
1998-10-15 12:04:53 +00:00
|
|
|
|
#endif
|
|
|
|
|
return erg;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// end: optimization stuff-------------------------------------------------------------------------
|
1998-09-02 15:05:13 +00:00
|
|
|
|
break;
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// newline - induced premature lineending: flush
|
1999-08-31 09:19:39 +00:00
|
|
|
|
} else if ([linescanner isAtEnd])
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ _GNULineLayoutInfo *thisInfo;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
scannerPosition = [linescanner scanLocation];
|
|
|
|
|
[lineLayoutInformation addObject: thisInfo = [_GNULineLayoutInfo lineLayoutWithRange:
|
|
|
|
|
NSMakeRange(startingLineCharIndex,scannerPosition - localLineStartIndex)
|
1999-08-31 09:19:39 +00:00
|
|
|
|
rect: currentLineRect drawingOffset: 0 type: LineLayoutInfoType_Text]];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
currentLineIndex++;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
startingLineCharIndex = NSMaxRange([thisInfo lineRange]);
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1998-10-15 12:04:53 +00:00
|
|
|
|
// check for optimization (lines after paragraph are unchanged and do not need redisplay/relayout)------
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (prevArrayEnum)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ _GNULineLayoutInfo *ghostInfo = nil;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
ghostInfo = [prevArrayEnum nextObject];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (ghostInfo)
|
|
|
|
|
{ if ([ghostInfo type] != [thisInfo type]) // frameshift correction for inserted newline
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ frameshiftCorrection = YES;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
if (insertionDelta == 1)
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{
|
|
|
|
|
[prevArrayEnum previousObject];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
lineDriftOffset += ([thisInfo lineRange].length - [ghostInfo lineRange].length) + insertionDelta;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
rebuildLineDrift--;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
yDisplacement += [thisInfo lineRect].origin.y - [ghostInfo lineRect].origin.y;
|
|
|
|
|
} else if (insertionDelta == - 1)
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (nlDidShift && frameshiftCorrection)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{// frameshiftCorrection = NO;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
#if 0
|
|
|
|
|
NSLog(@"opti hook 2");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
} else lineDriftOffset += ([thisInfo lineRange].length - [ghostInfo lineRange].length);
|
|
|
|
|
} else {prevArrayEnum = nil;} // new array obviously longer than the previous one
|
1998-10-15 12:04:53 +00:00
|
|
|
|
// end: optimization stuff-------------------------------------------------------------------------
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (trailingNlRange.length) // add the trailing newlines of current paragraph if any
|
|
|
|
|
{ [self addNewlines: trailingNlRange intoLayoutArray: lineLayoutInformation attributes: attributes atPoint: &drawingPoint width: width
|
|
|
|
|
characterIndex: startingLineCharIndex ghostEnumerator: prevArrayEnum
|
|
|
|
|
didShift: &nlDidShift verticalDisplacement: &yDisplacement];
|
|
|
|
|
if (nlDidShift)
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ if (insertionDelta == 1)
|
|
|
|
|
{ frameshiftCorrection = YES;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
rebuildLineDrift--;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
} else if (insertionDelta == - 1)
|
|
|
|
|
{ frameshiftCorrection = YES;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
rebuildLineDrift++;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
} else nlDidShift = NO;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
currentLineIndex += trailingNlRange.length;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
return [lineLayoutInformation count] - aLine; // lines actually updated (optimized drawing)
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
// end: central line formatting method------------------------------------
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (int) rebuildLineLayoutInformationStartingAtLine: (int) aLine
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ return [self rebuildLineLayoutInformationStartingAtLine: aLine delta: 0 actualLine: 0];
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// relies on lineLayoutInformation
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) drawPlainLinesInLineRange: (NSRange) aRange
|
|
|
|
|
{ if (NSMaxRange(aRange) > MAX(0,[lineLayoutInformation count] - 1)) // lay out lines before drawing them
|
|
|
|
|
{ [self rebuildLineLayoutInformationStartingAtLine: MAX(0,[lineLayoutInformation count] - 1)];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSArray *linesToDraw = [lineLayoutInformation subarrayWithRange: aRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSEnumerator *lineEnum;
|
|
|
|
|
_GNULineLayoutInfo *currentInfo;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
NSDictionary *attributes = [self defaultTypingAttributes];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for ((lineEnum = [linesToDraw objectEnumerator]);(currentInfo = [lineEnum nextObject]);)
|
|
|
|
|
{ if ([currentInfo isDontDisplay] || [currentInfo type] == LineLayoutInfoType_Paragraph) continue; // e.g. for nl
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[[plainContent substringWithRange: [currentInfo lineRange]] drawAtPoint: [currentInfo lineRect].origin withAttributes: attributes];
|
|
|
|
|
// <!> make this use drawInRect: withAttributes: in the future (for proper adoption of layout information [e.g. centering])
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) drawRichLinesInLineRange: (NSRange) aRange
|
|
|
|
|
{ if (NSMaxRange(aRange) > [lineLayoutInformation count] - 1) // lay out lines before drawing them
|
|
|
|
|
{ [self rebuildLineLayoutInformationStartingAtLine: [lineLayoutInformation count] - 1];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
{ NSArray *linesToDraw = [lineLayoutInformation subarrayWithRange: aRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
NSEnumerator *lineEnum;
|
|
|
|
|
_GNULineLayoutInfo *currentInfo;
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
for ((lineEnum = [linesToDraw objectEnumerator]);(currentInfo = [lineEnum nextObject]);)
|
|
|
|
|
{ if ([currentInfo isDontDisplay] || [currentInfo type] == LineLayoutInfoType_Paragraph) continue; // e.g. for nl
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[rtfContent drawRange: [currentInfo lineRange] atPoint: [currentInfo lineRect].origin];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
// <!> make this use drawRange: inRect: in the future (for proper adoption of layout information [e.g. centering])
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (NSRange) lineRangeForRect: (NSRect) rect
|
|
|
|
|
{ NSPoint upperLeftPoint = rect.origin, lowerRightPoint = NSMakePoint(NSMaxX(rect),NSMaxY(rect));
|
1999-06-22 23:37:24 +00:00
|
|
|
|
NSRange myTest;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
unsigned startLine,endLine;
|
1999-11-02 07:58:11 +00:00
|
|
|
|
startLine = [self lineLayoutIndexForPoint: upperLeftPoint],
|
|
|
|
|
endLine = [self lineLayoutIndexForPoint: lowerRightPoint];
|
|
|
|
|
//FIXME return MakeRangeFromAbs(startLine,endLine + 1);
|
1999-06-22 23:37:24 +00:00
|
|
|
|
if ([plainContent length] != 0) {
|
1999-11-02 07:58:11 +00:00
|
|
|
|
myTest = MakeRangeFromAbs(startLine,endLine + 1);
|
|
|
|
|
NSDebugLog(@"myTest: length = %d, location = %d\n",
|
1999-06-22 23:37:24 +00:00
|
|
|
|
(int)myTest.length,
|
|
|
|
|
(int)myTest.location);
|
|
|
|
|
return myTest;
|
|
|
|
|
} else {
|
1999-11-02 07:58:11 +00:00
|
|
|
|
myTest = MakeRangeFromAbs(startLine,endLine);
|
|
|
|
|
NSDebugLog(@"myTest: length = %d, location = %d\n",
|
1999-06-22 23:37:24 +00:00
|
|
|
|
(int)myTest.length,
|
|
|
|
|
(int)myTest.location);
|
|
|
|
|
return myTest;
|
|
|
|
|
}
|
1998-10-15 12:04:53 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) drawRectNoSelection: (NSRect)rect
|
1998-10-15 12:04:53 +00:00
|
|
|
|
{ NSRange redrawLineRange;
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (![lineLayoutInformation count]) // bootstrap layout information for [self lineLayoutIndexForCharacterIndex: anIndex] to work initially
|
|
|
|
|
{ [self rebuildLineLayoutInformationStartingAtLine: 0];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
redrawLineRange = [self lineRangeForRect: rect];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self drawsBackground]) // clear area under text
|
1998-09-02 15:05:13 +00:00
|
|
|
|
{ [[self backgroundColor] set]; NSRectFill(rect);
|
|
|
|
|
}
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText])
|
|
|
|
|
{ [self drawRichLinesInLineRange: redrawLineRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
} else
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self drawPlainLinesInLineRange: redrawLineRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) drawRect: (NSRect)rect
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if (displayDisabled) return;
|
1998-10-15 12:04:53 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[self drawRectNoSelection: rect];
|
|
|
|
|
[self drawSelectionAsRange: [self selectedRange]];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// text lays out from top to bottom
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (BOOL) isFlipped {return YES;}
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1996-05-30 20:03:15 +00:00
|
|
|
|
//
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// Copy and paste
|
1996-05-30 20:03:15 +00:00
|
|
|
|
//
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) copy: sender
|
|
|
|
|
{ NSMutableArray *types = [NSMutableArray arrayWithObjects: NSStringPboardType, nil];
|
|
|
|
|
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if ([self isRichText]) [types addObject: NSRTFPboardType];
|
|
|
|
|
if ([self importsGraphics]) [types addObject: NSRTFDPboardType];
|
|
|
|
|
[pboard declareTypes: types owner: self];
|
|
|
|
|
[pboard setString: [[self string] substringWithRange: [self selectedRange]] forType: NSStringPboardType];
|
|
|
|
|
if ([self isRichText]) [pboard setData: [self RTFFromRange: [self selectedRange]] forType: NSRTFPboardType];
|
|
|
|
|
if ([self importsGraphics]) [pboard setData: [self RTFDFromRange: [self selectedRange]] forType: NSRTFDPboardType];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// <!>
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) copyFont: sender
|
1996-05-30 20:03:15 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
// <!>
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) copyRuler: sender
|
1996-05-30 20:03:15 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) delete: sender
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self deleteRange: [self selectedRange] backspace: NO];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) cut: sender
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ if ([self selectedRange].length)
|
|
|
|
|
{ [self copy: self];
|
|
|
|
|
[self delete: self];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void) paste: sender
|
|
|
|
|
{
|
|
|
|
|
[self performPasteOperation: [NSPasteboard generalPasteboard]];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void) pasteFont: sender
|
|
|
|
|
{
|
|
|
|
|
[self performPasteOperation: [NSPasteboard pasteboardWithName: NSFontPboard]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void) pasteRuler: sender
|
|
|
|
|
{
|
|
|
|
|
[self performPasteOperation:
|
|
|
|
|
[NSPasteboard pasteboardWithName: NSRulerPboard]];
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1996-05-30 20:03:15 +00:00
|
|
|
|
//
|
|
|
|
|
// NSCoding protocol
|
|
|
|
|
//
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- (void)encodeWithCoder: aCoder
|
|
|
|
|
{ [super encodeWithCoder: aCoder];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
[aCoder encodeConditionalObject: delegate];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
|
|
|
|
[aCoder encodeObject: plainContent];
|
|
|
|
|
[aCoder encodeObject: rtfContent];
|
|
|
|
|
|
|
|
|
|
[aCoder encodeValueOfObjCType: "I" at: &alignment];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &is_editable];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &is_rich_text];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &is_selectable];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &imports_graphics];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &uses_font_panel];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &is_horizontally_resizable];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &is_vertically_resizable];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &is_ruler_visible];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &is_field_editor];
|
|
|
|
|
[aCoder encodeObject: background_color];
|
|
|
|
|
[aCoder encodeObject: text_color];
|
|
|
|
|
[aCoder encodeObject: default_font];
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(NSRange) at: &selected_range];
|
|
|
|
|
}
|
|
|
|
|
|
1999-08-31 09:19:39 +00:00
|
|
|
|
- initWithCoder: aDecoder
|
|
|
|
|
{ [super initWithCoder: aDecoder];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
delegate = [aDecoder decodeObject];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
plainContent = [aDecoder decodeObject];
|
|
|
|
|
rtfContent = [aDecoder decodeObject];
|
1999-06-22 23:37:24 +00:00
|
|
|
|
|
|
|
|
|
[aDecoder decodeValueOfObjCType: "I" at: &alignment];
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &is_editable];
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &is_rich_text];
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &is_selectable];
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &imports_graphics];
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &uses_font_panel];
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &is_horizontally_resizable];
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &is_vertically_resizable];
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &is_ruler_visible];
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &is_field_editor];
|
1999-11-02 07:58:11 +00:00
|
|
|
|
background_color = [aDecoder decodeObject];
|
|
|
|
|
text_color = RETAIN([aDecoder decodeObject]);
|
|
|
|
|
default_font = RETAIN([aDecoder decodeObject]);
|
1999-06-22 23:37:24 +00:00
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(NSRange) at: &selected_range];
|
|
|
|
|
|
|
|
|
|
return self;
|
1996-05-30 20:03:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-02 15:05:13 +00:00
|
|
|
|
//
|
|
|
|
|
// Spelling
|
|
|
|
|
//
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) checkSpelling: sender
|
|
|
|
|
{ NSRange errorRange = [[NSSpellChecker sharedSpellChecker] checkSpellingOfString: [self string] startingAt: NSMaxRange([self selectedRange])];
|
1999-08-31 09:19:39 +00:00
|
|
|
|
if (errorRange.length) [self setSelectedRange: errorRange];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
else NSBeep();
|
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) showGuessPanel: sender
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [[[NSSpellChecker sharedSpellChecker] spellingPanel] orderFront: self];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1997-01-29 16:07:56 +00:00
|
|
|
|
//
|
|
|
|
|
// NSChangeSpelling protocol
|
|
|
|
|
//
|
1998-09-02 15:05:13 +00:00
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) changeSpelling: sender
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [self insertText: [[(NSControl*)sender selectedCell] stringValue]];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (int) spellCheckerDocumentTag
|
|
|
|
|
{ if (!spellCheckerDocumentTag) spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
|
1998-09-02 15:05:13 +00:00
|
|
|
|
return spellCheckerDocumentTag;
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
1997-01-29 16:07:56 +00:00
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// NSIgnoreMisspelledWords protocol
|
|
|
|
|
//
|
1999-11-02 07:58:11 +00:00
|
|
|
|
- (void) ignoreSpelling: sender
|
1999-08-31 09:19:39 +00:00
|
|
|
|
{ [[NSSpellChecker sharedSpellChecker] ignoreWord: [[(NSControl*)sender selectedCell] stringValue] inSpellDocumentWithTag: [self spellCheckerDocumentTag]];
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
1997-01-29 16:07:56 +00:00
|
|
|
|
|
1996-05-30 20:03:15 +00:00
|
|
|
|
@end
|