2000-02-19 00:40:47 +00:00
|
|
|
|
/*
|
|
|
|
|
NSTextView.m
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
Copyright (C) 1996, 1998, 2000 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
|
|
Much of the code here is derived from code which was originally in
|
|
|
|
|
NSText.m.
|
|
|
|
|
|
|
|
|
|
Author: Scott Christley <scottc@net-community.com>
|
|
|
|
|
Date: 1996
|
|
|
|
|
|
|
|
|
|
Author: Felipe A. Rodriguez <far@ix.netcom.com>
|
|
|
|
|
Date: July 1998
|
|
|
|
|
|
|
|
|
|
Author: Daniel B<EFBFBD>hringer <boehring@biomed.ruhr-uni-bochum.de>
|
|
|
|
|
Date: August 1998
|
2000-02-19 00:40:47 +00:00
|
|
|
|
|
2000-09-30 23:11:50 +00:00
|
|
|
|
Author: Fred Kiefer <FredKiefer@gmx.de>
|
2000-12-20 17:00:32 +00:00
|
|
|
|
Date: March 2000, September 2000
|
|
|
|
|
|
|
|
|
|
Author: Nicola Pero <n.pero@mi.flashnet.it>
|
|
|
|
|
Date: December 2000
|
2000-09-30 23:11:50 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +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
|
|
|
|
|
License along with this library; see the file COPYING.LIB.
|
|
|
|
|
If not, write to the Free Software Foundation,
|
2000-12-20 17:00:32 +00:00
|
|
|
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
2000-02-19 00:40:47 +00:00
|
|
|
|
|
1999-07-24 22:24:14 +00:00
|
|
|
|
#include <gnustep/gui/config.h>
|
|
|
|
|
#include <Foundation/NSCoder.h>
|
|
|
|
|
#include <Foundation/NSArray.h>
|
|
|
|
|
#include <Foundation/NSException.h>
|
|
|
|
|
#include <Foundation/NSProcessInfo.h>
|
|
|
|
|
#include <Foundation/NSString.h>
|
|
|
|
|
#include <Foundation/NSNotification.h>
|
|
|
|
|
#include <AppKit/NSApplication.h>
|
|
|
|
|
#include <AppKit/NSWindow.h>
|
|
|
|
|
#include <AppKit/NSEvent.h>
|
|
|
|
|
#include <AppKit/NSFont.h>
|
|
|
|
|
#include <AppKit/NSColor.h>
|
1998-08-19 09:00:26 +00:00
|
|
|
|
#include <AppKit/NSTextView.h>
|
2000-12-20 17:00:32 +00:00
|
|
|
|
#include <AppKit/NSParagraphStyle.h>
|
1999-02-22 00:41:22 +00:00
|
|
|
|
#include <AppKit/NSRulerView.h>
|
1998-08-19 09:00:26 +00:00
|
|
|
|
#include <AppKit/NSPasteboard.h>
|
1998-08-20 09:56:26 +00:00
|
|
|
|
#include <AppKit/NSSpellChecker.h>
|
|
|
|
|
#include <AppKit/NSControl.h>
|
1998-09-01 13:23:23 +00:00
|
|
|
|
#include <AppKit/NSLayoutManager.h>
|
|
|
|
|
#include <AppKit/NSTextStorage.h>
|
2000-05-23 22:59:36 +00:00
|
|
|
|
#include <AppKit/NSColorPanel.h>
|
1998-08-19 09:00:26 +00:00
|
|
|
|
|
2000-12-19 15:34:21 +00:00
|
|
|
|
#define HUGE 1e7
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* not the same as NSMakeRange! */
|
2000-12-19 22:35:22 +00:00
|
|
|
|
static inline
|
|
|
|
|
NSRange MakeRangeFromAbs (unsigned a1, unsigned a2)
|
|
|
|
|
{
|
|
|
|
|
if (a1 < a2)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return NSMakeRange (a1, a2 - a1);
|
2000-12-19 22:35:22 +00:00
|
|
|
|
else
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return NSMakeRange (a2, a1 - a2);
|
2000-12-19 22:35:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-19 18:45:19 +00:00
|
|
|
|
#define SET_DELEGATE_NOTIFICATION(notif_name) \
|
2000-12-20 12:50:15 +00:00
|
|
|
|
if ([_delegate respondsToSelector: @selector(text##notif_name: )]) \
|
2000-12-19 18:45:19 +00:00
|
|
|
|
[nc addObserver: _delegate \
|
2000-12-20 12:50:15 +00:00
|
|
|
|
selector: @selector(text##notif_name: ) \
|
|
|
|
|
name: NSText##notif_name##Notification \
|
2000-12-19 18:45:19 +00:00
|
|
|
|
object: _notifObject]
|
2000-12-19 21:20:39 +00:00
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
/*
|
|
|
|
|
* Synchronizing flags. Used to manage synchronizing shared
|
|
|
|
|
* attributes between textviews coupled with the same layout manager.
|
|
|
|
|
* These synchronizing flags are only accessed when
|
|
|
|
|
* _tvf.multiple_textviews == YES and this can only happen if we have
|
|
|
|
|
* a non-nil NSLayoutManager - so we don't check. */
|
|
|
|
|
|
2000-12-19 18:45:19 +00:00
|
|
|
|
/* YES when in the process of synchronizing text view attributes.
|
2000-12-21 17:32:01 +00:00
|
|
|
|
Used to avoid recursive synchronizations. */
|
|
|
|
|
#define IS_SYNCHRONIZING_FLAGS _layoutManager->_isSynchronizingFlags
|
|
|
|
|
/* YES when in the process of synchronizing delegates.
|
|
|
|
|
Used to avoid recursive synchronizations. */
|
|
|
|
|
#define IS_SYNCHRONIZING_DELEGATES _layoutManager->_isSynchronizingDelegates
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Began editing flag. There are quite some different ways in which
|
|
|
|
|
* editing can be started. Each time editing is started, we need to check
|
|
|
|
|
* with the delegate if it is OK to start editing - but we need to check
|
|
|
|
|
* only once. So, we use a flag. */
|
|
|
|
|
|
|
|
|
|
static BOOL noLayoutManagerException ()
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"Can't edit a NSTextView without a layout manager!"];
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* YES when editing has already began. If NO, then we need to ask to
|
|
|
|
|
the delegate for permission to begin editing before allowing any
|
|
|
|
|
change to be made. We explicitly check for a layout manager, and
|
|
|
|
|
raise an exception if not found. */
|
|
|
|
|
#define BEGAN_EDITING \
|
|
|
|
|
(_layoutManager ? _layoutManager->_beganEditing : noLayoutManagerException ())
|
|
|
|
|
#define SET_BEGAN_EDITING(X) \
|
|
|
|
|
if (_layoutManager != nil) _layoutManager->_beganEditing = X
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
|
|
|
|
/* The shared notification center */
|
|
|
|
|
static NSNotificationCenter *nc;
|
2000-12-19 15:34:21 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
@interface NSTextView (GNUstepPrivate)
|
|
|
|
|
/*
|
|
|
|
|
* these NSLayoutManager- like methods are here only informally
|
|
|
|
|
*/
|
|
|
|
|
- (unsigned) characterIndexForPoint: (NSPoint)point;
|
|
|
|
|
- (NSRect) rectForCharacterIndex: (unsigned)index;
|
2000-12-19 22:35:22 +00:00
|
|
|
|
- (NSRect) rectForCharacterRange: (NSRange)aRange;
|
2000-12-22 16:57:21 +00:00
|
|
|
|
- (NSRect) rectForInsertionPointAtIndex: (unsigned)index;
|
2000-12-19 15:34:21 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* various GNU extensions
|
|
|
|
|
*/
|
|
|
|
|
+ (NSDictionary*) defaultTypingAttributes;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// GNU utility methods
|
|
|
|
|
//
|
|
|
|
|
- (void) setAttributes: (NSDictionary *) attributes range: (NSRange) aRange;
|
|
|
|
|
- (void) _illegalMovement: (int) notNumber;
|
|
|
|
|
- (void) deleteRange: (NSRange)aRange backspace: (BOOL)flag;
|
2000-12-18 22:07:28 +00:00
|
|
|
|
@end
|
|
|
|
|
|
1998-08-20 09:56:26 +00:00
|
|
|
|
@implementation NSTextView
|
1998-08-19 09:00:26 +00:00
|
|
|
|
|
1999-07-24 22:24:14 +00:00
|
|
|
|
/* Class methods */
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
+ (void) initialize
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-02-19 00:40:47 +00:00
|
|
|
|
if ([self class] == [NSTextView class])
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-02-19 00:40:47 +00:00
|
|
|
|
[self setVersion: 1];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
nc = [NSNotificationCenter defaultCenter];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self registerForServices];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
+ (void) registerForServices
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-05-23 22:59:36 +00:00
|
|
|
|
NSArray *types;
|
2000-02-19 00:40:47 +00:00
|
|
|
|
|
2000-12-18 18:08:21 +00:00
|
|
|
|
types = [NSArray arrayWithObjects: NSStringPboardType,
|
|
|
|
|
NSRTFPboardType, NSRTFDPboardType, nil];
|
2000-02-19 00:40:47 +00:00
|
|
|
|
|
2000-05-23 22:59:36 +00:00
|
|
|
|
[[NSApplication sharedApplication] registerServicesMenuSendTypes: types
|
|
|
|
|
returnTypes: types];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-07-24 22:24:14 +00:00
|
|
|
|
/* Initializing Methods */
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (NSTextContainer*) buildUpTextNetwork: (NSSize)aSize;
|
|
|
|
|
{
|
|
|
|
|
NSTextContainer *textContainer;
|
|
|
|
|
NSLayoutManager *layoutManager;
|
|
|
|
|
NSTextStorage *textStorage;
|
|
|
|
|
|
|
|
|
|
textStorage = [[NSTextStorage alloc] init];
|
|
|
|
|
|
|
|
|
|
layoutManager = [[NSLayoutManager alloc] init];
|
|
|
|
|
/*
|
|
|
|
|
[textStorage addLayoutManager: layoutManager];
|
|
|
|
|
RELEASE (layoutManager);
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
textContainer = [[NSTextContainer alloc] initWithContainerSize: aSize];
|
|
|
|
|
[layoutManager addTextContainer: textContainer];
|
|
|
|
|
RELEASE (textContainer);
|
|
|
|
|
|
|
|
|
|
/* FIXME: The following two lines should go *before* */
|
|
|
|
|
[textStorage addLayoutManager: layoutManager];
|
|
|
|
|
RELEASE (layoutManager);
|
|
|
|
|
|
|
|
|
|
/* The situation at this point is as follows:
|
|
|
|
|
|
|
|
|
|
textView (us) --RETAINs--> textStorage
|
|
|
|
|
textStorage --RETAINs--> layoutManager
|
|
|
|
|
layoutManager --RETAINs--> textContainer */
|
|
|
|
|
|
|
|
|
|
/* We keep a flag to remember that we are directly responsible for
|
|
|
|
|
managing the text objects. */
|
|
|
|
|
_tvf.owns_text_network = YES;
|
|
|
|
|
|
|
|
|
|
return textContainer;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-19 15:34:21 +00:00
|
|
|
|
/* Designated initializer */
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (id) initWithFrame: (NSRect)frameRect
|
|
|
|
|
textContainer: (NSTextContainer*)aTextContainer
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-19 15:34:21 +00:00
|
|
|
|
[super initWithFrame: frameRect];
|
|
|
|
|
|
2001-01-07 01:05:44 +00:00
|
|
|
|
[self setMinSize: NSMakeSize (0, 0)];
|
2000-12-19 15:34:21 +00:00
|
|
|
|
[self setMaxSize: NSMakeSize (HUGE,HUGE)];
|
|
|
|
|
|
|
|
|
|
_tf.is_field_editor = NO;
|
|
|
|
|
_tf.is_editable = YES;
|
|
|
|
|
_tf.is_selectable = YES;
|
|
|
|
|
_tf.is_rich_text = NO;
|
|
|
|
|
_tf.imports_graphics = NO;
|
|
|
|
|
_tf.draws_background = YES;
|
|
|
|
|
_tf.is_horizontally_resizable = NO;
|
|
|
|
|
_tf.is_vertically_resizable = NO;
|
|
|
|
|
_tf.uses_font_panel = YES;
|
|
|
|
|
_tf.uses_ruler = YES;
|
|
|
|
|
_tf.is_ruler_visible = NO;
|
2000-12-22 19:08:29 +00:00
|
|
|
|
_original_selected_range.location = NSNotFound;
|
2000-12-19 15:34:21 +00:00
|
|
|
|
ASSIGN (_caret_color, [NSColor blackColor]);
|
|
|
|
|
[self setTypingAttributes: [isa defaultTypingAttributes]];
|
|
|
|
|
|
|
|
|
|
[self setBackgroundColor: [NSColor textBackgroundColor]];
|
|
|
|
|
|
|
|
|
|
//[self setSelectedRange: NSMakeRange (0, 0)];
|
|
|
|
|
|
|
|
|
|
[aTextContainer setTextView: self];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
[self setEditable: YES];
|
2000-05-23 22:59:36 +00:00
|
|
|
|
[self setUsesFontPanel: YES];
|
|
|
|
|
[self setUsesRuler: YES];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
|
|
|
|
return self;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (id) initWithFrame: (NSRect)frameRect
|
1999-06-30 03:10:38 +00:00
|
|
|
|
{
|
2000-12-18 22:07:28 +00:00
|
|
|
|
NSTextContainer *aTextContainer;
|
|
|
|
|
|
|
|
|
|
aTextContainer = [self buildUpTextNetwork: frameRect.size];
|
|
|
|
|
|
|
|
|
|
self = [self initWithFrame: frameRect textContainer: aTextContainer];
|
|
|
|
|
|
|
|
|
|
/* At this point the situation is as follows:
|
|
|
|
|
|
|
|
|
|
textView (us) --RETAINs--> textStorage
|
|
|
|
|
textStorage --RETAINs--> layoutManager
|
|
|
|
|
layoutManager --RETAINs--> textContainer
|
|
|
|
|
textContainter --RETAINs --> textView (us) */
|
|
|
|
|
|
|
|
|
|
/* The text system should be destroyed when the textView (us) is
|
|
|
|
|
released. To get this result, we send a RELEASE message to us
|
|
|
|
|
breaking the RETAIN cycle. */
|
|
|
|
|
RELEASE (self);
|
|
|
|
|
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-19 15:34:21 +00:00
|
|
|
|
- (void) encodeWithCoder: (NSCoder *)aCoder
|
|
|
|
|
{
|
|
|
|
|
BOOL flag;
|
|
|
|
|
|
|
|
|
|
[super encodeWithCoder: aCoder];
|
|
|
|
|
|
|
|
|
|
flag = _tvf.smart_insert_delete;
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
|
|
|
flag = _tvf.allows_undo;
|
|
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder *)aDecoder
|
2000-12-18 22:07:28 +00:00
|
|
|
|
{
|
|
|
|
|
NSTextContainer *aTextContainer;
|
2000-12-19 15:34:21 +00:00
|
|
|
|
BOOL flag;
|
2000-12-18 22:07:28 +00:00
|
|
|
|
|
2000-12-19 15:34:21 +00:00
|
|
|
|
self = [super initWithCoder: aDecoder];
|
|
|
|
|
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
|
|
|
_tvf.smart_insert_delete = flag;
|
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
|
|
|
_tvf.allows_undo = flag;
|
2000-12-18 22:07:28 +00:00
|
|
|
|
|
|
|
|
|
/* build up the rest of the text system, which doesn't get stored
|
|
|
|
|
<doesn't even implement the Coding protocol>. */
|
|
|
|
|
aTextContainer = [self buildUpTextNetwork: _frame.size];
|
|
|
|
|
[aTextContainer setTextView: (NSTextView*)self];
|
|
|
|
|
/* See initWithFrame: for comments on this RELEASE */
|
|
|
|
|
RELEASE (self);
|
|
|
|
|
|
|
|
|
|
return self;
|
2000-09-30 23:11:50 +00:00
|
|
|
|
}
|
1999-07-15 17:32:38 +00:00
|
|
|
|
|
2000-09-30 23:11:50 +00:00
|
|
|
|
- (void)dealloc
|
|
|
|
|
{
|
2000-12-19 15:34:21 +00:00
|
|
|
|
if (_tvf.owns_text_network == YES)
|
2000-12-18 22:07:28 +00:00
|
|
|
|
{
|
|
|
|
|
/* Prevent recursive dealloc */
|
2000-12-19 15:34:21 +00:00
|
|
|
|
if (_tvf.is_in_dealloc == YES)
|
2000-12-18 22:07:28 +00:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2000-12-19 15:34:21 +00:00
|
|
|
|
_tvf.is_in_dealloc = YES;
|
2000-12-18 22:07:28 +00:00
|
|
|
|
/* This releases all the text objects (us included) in fall */
|
|
|
|
|
RELEASE (_textStorage);
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-16 20:19:18 +00:00
|
|
|
|
RELEASE (_selectedTextAttributes);
|
|
|
|
|
RELEASE (_markedTextAttributes);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
RELEASE (_caret_color);
|
|
|
|
|
RELEASE (_typingAttributes);
|
1999-08-19 23:18:25 +00:00
|
|
|
|
|
2000-09-30 23:11:50 +00:00
|
|
|
|
[super dealloc];
|
1999-06-30 03:10:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-18 22:07:28 +00:00
|
|
|
|
/*
|
|
|
|
|
* Implementation of methods declared in superclass but depending
|
|
|
|
|
* on the internals of the NSTextView
|
|
|
|
|
*/
|
|
|
|
|
- (void) replaceCharactersInRange: (NSRange)aRange
|
2000-12-22 16:57:21 +00:00
|
|
|
|
withString: (NSString *)aString
|
2000-12-18 22:07:28 +00:00
|
|
|
|
{
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ([self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: aString] == NO)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage replaceCharactersInRange: aRange withString: aString];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (NSData*) RTFDFromRange: (NSRange)aRange
|
|
|
|
|
{
|
|
|
|
|
return [_textStorage RTFDFromRange: aRange documentAttributes: nil];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSData*) RTFFromRange: (NSRange)aRange
|
|
|
|
|
{
|
|
|
|
|
return [_textStorage RTFFromRange: aRange documentAttributes: nil];
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-19 18:45:19 +00:00
|
|
|
|
- (NSString *) string
|
|
|
|
|
{
|
|
|
|
|
return [_textStorage string];
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* [NSText] Managing the Ruler
|
|
|
|
|
*/
|
|
|
|
|
- (void) toggleRuler: (id)sender
|
|
|
|
|
{
|
|
|
|
|
[self setRulerVisible: !_tf.is_ruler_visible];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* [NSText] Managing the Selected Range
|
|
|
|
|
*/
|
2000-12-19 22:35:22 +00:00
|
|
|
|
- (NSRange) selectedRange
|
|
|
|
|
{
|
|
|
|
|
return _selected_range;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setSelectedRange: (NSRange)range
|
|
|
|
|
{
|
2000-12-22 17:52:23 +00:00
|
|
|
|
[self setSelectedRange: range affinity: [self selectionAffinity]
|
|
|
|
|
stillSelecting: NO];
|
2000-12-19 22:35:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2000-12-20 17:00:32 +00:00
|
|
|
|
* [NSText] Copy and Paste
|
2000-12-19 22:35:22 +00:00
|
|
|
|
*/
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) copy: (id)sender
|
|
|
|
|
{
|
|
|
|
|
NSMutableArray *types = [NSMutableArray array];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (_tf.imports_graphics)
|
|
|
|
|
[types addObject: NSRTFDPboardType];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (_tf.is_rich_text)
|
|
|
|
|
[types addObject: NSRTFPboardType];
|
2000-12-20 12:50:15 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[types addObject: NSStringPboardType];
|
2000-12-20 12:50:15 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self writeSelectionToPasteboard: [NSPasteboard generalPasteboard]
|
|
|
|
|
types: types];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* Copy the current font to the font pasteboard */
|
|
|
|
|
- (void) copyFont: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSFontPboard];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self writeSelectionToPasteboard: pb type: NSFontPboardType];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* Copy the current ruler settings to the ruler pasteboard */
|
|
|
|
|
- (void) copyRuler: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSRulerPboard];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self writeSelectionToPasteboard: pb type: NSRulerPboardType];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) delete: (id)sender
|
2000-12-19 15:34:21 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self deleteRange: _selected_range backspace: NO];
|
2000-12-19 15:34:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) paste: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self readSelectionFromPasteboard: [NSPasteboard generalPasteboard]];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) pasteFont: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSFontPboard];
|
|
|
|
|
|
|
|
|
|
[self readSelectionFromPasteboard: pb type: NSFontPboardType];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) pasteRuler: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSPasteboard *pb = [NSPasteboard pasteboardWithName: NSRulerPboard];
|
|
|
|
|
|
|
|
|
|
[self readSelectionFromPasteboard: pb type: NSRulerPboardType];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* [NSText] Managing Fonts
|
|
|
|
|
*/
|
|
|
|
|
- (NSFont*) font
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if ([_textStorage length] > 0)
|
|
|
|
|
{
|
2001-01-03 10:39:02 +00:00
|
|
|
|
NSFont *font;
|
|
|
|
|
|
|
|
|
|
font = [_textStorage attribute: NSFontAttributeName
|
|
|
|
|
atIndex: 0
|
|
|
|
|
effectiveRange: NULL];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (font != nil)
|
|
|
|
|
{
|
|
|
|
|
return font;
|
|
|
|
|
}
|
|
|
|
|
}
|
2000-06-16 17:12:25 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return [_typingAttributes objectForKey: NSFontAttributeName];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) changeFont: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSRange foundRange;
|
|
|
|
|
int maxSelRange;
|
|
|
|
|
NSRange aRange= [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
NSRange searchRange = aRange;
|
|
|
|
|
NSFont *font;
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
2000-12-23 13:07:36 +00:00
|
|
|
|
if (![self shouldChangeTextInRange: aRange replacementString: nil])
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return;
|
2000-12-23 13:07:36 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[_textStorage beginEditing];
|
2000-12-23 13:07:36 +00:00
|
|
|
|
for (maxSelRange = NSMaxRange (aRange);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
searchRange.location < maxSelRange;
|
|
|
|
|
searchRange = NSMakeRange (NSMaxRange (foundRange),
|
2000-12-23 13:07:36 +00:00
|
|
|
|
maxSelRange - NSMaxRange (foundRange)))
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
font = [_textStorage attribute: NSFontAttributeName
|
|
|
|
|
atIndex: searchRange.location
|
|
|
|
|
longestEffectiveRange: &foundRange
|
|
|
|
|
inRange: searchRange];
|
|
|
|
|
if (font != nil)
|
2000-12-23 13:07:36 +00:00
|
|
|
|
{
|
|
|
|
|
[self setFont: [sender convertFont: font] ofRange: foundRange];
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
2000-12-23 13:07:36 +00:00
|
|
|
|
/* Set typing attributes */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
font = [_typingAttributes objectForKey: NSFontAttributeName];
|
|
|
|
|
if (font != nil)
|
|
|
|
|
{
|
|
|
|
|
[_typingAttributes setObject: [sender convertFont: font]
|
|
|
|
|
forKey: NSFontAttributeName];
|
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setFont: (NSFont*)font
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (font == nil)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self setFont: font ofRange: NSMakeRange (0, [self textLength])];
|
2000-12-23 13:07:36 +00:00
|
|
|
|
[_typingAttributes setObject: font forKey: NSFontAttributeName];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setFont: (NSFont*)font ofRange: (NSRange)aRange
|
2000-06-29 14:25:07 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (font != nil)
|
|
|
|
|
{
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
[_textStorage beginEditing];
|
2000-12-23 13:07:36 +00:00
|
|
|
|
[_textStorage addAttribute: NSFontAttributeName value: font
|
2000-12-20 17:00:32 +00:00
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
|
|
|
|
}
|
2000-06-29 14:25:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* [NSText] Managing Alignment
|
|
|
|
|
*/
|
|
|
|
|
- (NSTextAlignment) alignment
|
2000-06-29 14:25:07 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* FIXME */
|
|
|
|
|
NSRange aRange = [self rangeForUserParagraphAttributeChange];
|
|
|
|
|
NSParagraphStyle *aStyle;
|
|
|
|
|
|
|
|
|
|
if (aRange.location != NSNotFound)
|
|
|
|
|
{
|
|
|
|
|
aStyle = [_textStorage attribute: NSParagraphStyleAttributeName
|
|
|
|
|
atIndex: aRange.location
|
|
|
|
|
effectiveRange: NULL];
|
|
|
|
|
if (aStyle != nil)
|
|
|
|
|
return [aStyle alignment];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get alignment from typing attributes
|
2000-12-23 13:07:36 +00:00
|
|
|
|
return [[_typingAttributes objectForKey: NSParagraphStyleAttributeName]
|
|
|
|
|
alignment];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setAlignment: (NSTextAlignment)mode
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self setAlignment: mode range: NSMakeRange (0, [self textLength])];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) alignCenter: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self setAlignment: NSCenterTextAlignment
|
|
|
|
|
range: [self rangeForUserParagraphAttributeChange]];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) alignLeft: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self setAlignment: NSLeftTextAlignment
|
|
|
|
|
range: [self rangeForUserParagraphAttributeChange]];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) alignRight: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self setAlignment: NSRightTextAlignment
|
|
|
|
|
range: [self rangeForUserParagraphAttributeChange]];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* [NSText] Managing Text Color
|
|
|
|
|
*/
|
|
|
|
|
- (NSColor*) textColor
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2001-01-03 10:39:02 +00:00
|
|
|
|
if ([_textStorage length] > 0)
|
|
|
|
|
{
|
|
|
|
|
NSColor *color;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2001-01-03 10:39:02 +00:00
|
|
|
|
color = [_textStorage attribute: NSForegroundColorAttributeName
|
|
|
|
|
atIndex: 0
|
|
|
|
|
effectiveRange: NULL];
|
|
|
|
|
if (color != nil)
|
|
|
|
|
{
|
|
|
|
|
return color;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return [_typingAttributes objectForKey: NSForegroundColorAttributeName];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setTextColor: (NSColor*)color
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* FIXME: This should work also for non rich text objects */
|
|
|
|
|
NSRange fullRange = NSMakeRange (0, [self textLength]);
|
|
|
|
|
|
|
|
|
|
[self setTextColor: color range: fullRange];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setTextColor: (NSColor*)color range: (NSRange)aRange
|
2000-06-16 17:12:25 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* FIXME: This should only work for rich text object */
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
if (color != nil)
|
|
|
|
|
{
|
|
|
|
|
[_textStorage addAttribute: NSForegroundColorAttributeName
|
|
|
|
|
value: color
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_typingAttributes setObject: color
|
|
|
|
|
forKey: NSForegroundColorAttributeName];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[_textStorage removeAttribute: NSForegroundColorAttributeName
|
|
|
|
|
range: aRange];
|
|
|
|
|
}
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
2000-06-16 17:12:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* [NSText] Text Attributes
|
2000-12-19 18:45:19 +00:00
|
|
|
|
*/
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) subscript: (id)sender
|
|
|
|
|
{
|
|
|
|
|
NSNumber *value = [_typingAttributes
|
|
|
|
|
objectForKey: NSSuperscriptAttributeName];
|
|
|
|
|
int sValue;
|
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (aRange.length)
|
|
|
|
|
{
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage subscriptRange: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set the typing attributes
|
|
|
|
|
if (value != nil)
|
|
|
|
|
sValue = [value intValue] - 1;
|
|
|
|
|
else
|
|
|
|
|
sValue = -1;
|
|
|
|
|
[_typingAttributes setObject: [NSNumber numberWithInt: sValue]
|
|
|
|
|
forKey: NSSuperscriptAttributeName];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) superscript: (id)sender
|
2000-12-19 18:45:19 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSNumber *value = [_typingAttributes
|
|
|
|
|
objectForKey: NSSuperscriptAttributeName];
|
|
|
|
|
int sValue;
|
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (aRange.length)
|
2000-12-19 18:45:19 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage superscriptRange: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// Set the typing attributes
|
|
|
|
|
if (value != nil)
|
|
|
|
|
sValue = [value intValue] + 1;
|
|
|
|
|
else
|
|
|
|
|
sValue = 1;
|
|
|
|
|
[_typingAttributes setObject: [NSNumber numberWithInt: sValue]
|
|
|
|
|
forKey: NSSuperscriptAttributeName];
|
|
|
|
|
}
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) unscript: (id)sender
|
|
|
|
|
{
|
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (aRange.length)
|
2000-12-19 18:45:19 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage unscriptRange: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// Set the typing attributes
|
|
|
|
|
[_typingAttributes removeObjectForKey: NSSuperscriptAttributeName];
|
|
|
|
|
}
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) underline: (id)sender
|
|
|
|
|
{
|
|
|
|
|
BOOL doUnderline = YES;
|
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ([[_textStorage attribute: NSUnderlineStyleAttributeName
|
|
|
|
|
atIndex: aRange.location
|
|
|
|
|
effectiveRange: NULL] intValue])
|
|
|
|
|
doUnderline = NO;
|
|
|
|
|
|
|
|
|
|
if (aRange.length)
|
|
|
|
|
{
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage addAttribute: NSUnderlineStyleAttributeName
|
|
|
|
|
value: [NSNumber numberWithInt: doUnderline]
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[_typingAttributes
|
|
|
|
|
setObject: [NSNumber numberWithInt: doUnderline]
|
|
|
|
|
forKey: NSUnderlineStyleAttributeName];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-19 21:20:39 +00:00
|
|
|
|
/*
|
2000-12-20 17:00:32 +00:00
|
|
|
|
* [NSText] Reading and Writing RTFD files
|
|
|
|
|
*/
|
|
|
|
|
- (BOOL) readRTFDFromFile: (NSString*)path
|
|
|
|
|
{
|
2000-12-23 13:07:36 +00:00
|
|
|
|
NSAttributedString *peek;
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-23 13:07:36 +00:00
|
|
|
|
peek = [[NSAttributedString alloc] initWithPath: path
|
|
|
|
|
documentAttributes: NULL];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (peek != nil)
|
|
|
|
|
{
|
|
|
|
|
if (!_tf.is_rich_text)
|
|
|
|
|
{
|
|
|
|
|
[self setRichText: YES];
|
|
|
|
|
}
|
|
|
|
|
[self replaceRange: NSMakeRange (0, [self textLength])
|
|
|
|
|
withAttributedString: peek];
|
|
|
|
|
RELEASE(peek);
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) writeRTFDToFile: (NSString*)path atomically: (BOOL)flag
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSFileWrapper *wrapper;
|
|
|
|
|
NSRange range = NSMakeRange (0, [self textLength]);
|
|
|
|
|
|
|
|
|
|
wrapper = [_textStorage RTFDFileWrapperFromRange: range
|
|
|
|
|
documentAttributes: nil];
|
|
|
|
|
return [wrapper writeToFile: path atomically: flag updateFilenames: YES];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* [NSText] Managing size
|
|
|
|
|
*/
|
|
|
|
|
- (void) setHorizontallyResizable: (BOOL)flag
|
2000-12-19 18:45:19 +00:00
|
|
|
|
{
|
2001-01-07 01:05:44 +00:00
|
|
|
|
/* Safety call */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[_textContainer setWidthTracksTextView: !flag];
|
|
|
|
|
|
|
|
|
|
[super setHorizontallyResizable: flag];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setVerticallyResizable: (BOOL)flag
|
2000-12-19 18:45:19 +00:00
|
|
|
|
{
|
2001-01-07 01:05:44 +00:00
|
|
|
|
/* Safety call */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[_textContainer setHeightTracksTextView: !flag];
|
|
|
|
|
|
|
|
|
|
[super setVerticallyResizable: flag];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) sizeToFit
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2001-01-07 01:05:44 +00:00
|
|
|
|
if (_tf.is_horizontally_resizable || _tf.is_vertically_resizable)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2001-01-07 01:05:44 +00:00
|
|
|
|
NSSize size;
|
|
|
|
|
|
|
|
|
|
size = [_layoutManager usedRectForTextContainer: _textContainer].size;
|
|
|
|
|
size.width += 2 * _textContainerInset.width;
|
|
|
|
|
size.height += 2 * _textContainerInset.height;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2001-01-07 01:05:44 +00:00
|
|
|
|
[self setConstrainedFrameSize: size];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* [NSText] Spelling
|
|
|
|
|
*/
|
|
|
|
|
- (void) checkSpelling: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSSpellChecker *sp = [NSSpellChecker sharedSpellChecker];
|
2000-12-19 18:45:19 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSRange errorRange;
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
errorRange = [sp checkSpellingOfString: [_textStorage string]
|
2000-12-20 17:00:32 +00:00
|
|
|
|
startingAt: NSMaxRange (_selected_range)];
|
|
|
|
|
|
|
|
|
|
if (errorRange.length)
|
|
|
|
|
{
|
|
|
|
|
[self setSelectedRange: errorRange];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSBeep();
|
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) changeSpelling: (id)sender
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* FIXME: Replace, not insert */
|
|
|
|
|
[self insertText: [[(NSControl*)sender selectedCell] stringValue]];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) ignoreSpelling: (id)sender
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSSpellChecker *sp = [NSSpellChecker sharedSpellChecker];
|
|
|
|
|
|
|
|
|
|
[sp ignoreWord: [[(NSControl*)sender selectedCell] stringValue]
|
|
|
|
|
inSpellDocumentWithTag: [self spellCheckerDocumentTag]];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* [NSText] Scrolling
|
|
|
|
|
*/
|
|
|
|
|
- (void) scrollRangeToVisible: (NSRange)aRange
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// Don't try scrolling an ancestor clipview if we are field editor.
|
|
|
|
|
// This makes things so much simpler and stabler for now.
|
|
|
|
|
if (_tf.is_field_editor == NO)
|
|
|
|
|
{
|
2000-12-20 22:56:40 +00:00
|
|
|
|
[self scrollRectToVisible: [self rectForCharacterRange: aRange]];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* [NSResponder] Handle enabling/disabling of services menu items.
|
|
|
|
|
*/
|
|
|
|
|
- (id) validRequestorForSendType: (NSString*)sendType
|
|
|
|
|
returnType: (NSString*)returnType
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
BOOL sendOK = NO;
|
|
|
|
|
BOOL returnOK = NO;
|
|
|
|
|
|
|
|
|
|
if (sendType == nil)
|
|
|
|
|
{
|
|
|
|
|
sendOK = YES;
|
|
|
|
|
}
|
|
|
|
|
else if (_selected_range.length && [sendType isEqual: NSStringPboardType])
|
|
|
|
|
{
|
|
|
|
|
sendOK = YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (returnType == nil)
|
|
|
|
|
{
|
|
|
|
|
returnOK = YES;
|
|
|
|
|
}
|
|
|
|
|
else if (_tf.is_editable && [returnType isEqual: NSStringPboardType])
|
|
|
|
|
{
|
|
|
|
|
returnOK = YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sendOK && returnOK)
|
|
|
|
|
{
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [super validRequestorForSendType: sendType returnType: returnType];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* NSTextView's specific methods
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
- (void) _updateMultipleTextViews
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
id oldNotifObject = _notifObject;
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if ([[_layoutManager textContainers] count] > 1)
|
2000-05-20 16:57:04 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
_tvf.multiple_textviews = YES;
|
|
|
|
|
_notifObject = [_layoutManager firstTextView];
|
2000-05-20 16:57:04 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
_tvf.multiple_textviews = NO;
|
|
|
|
|
_notifObject = self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((_delegate != nil) && (oldNotifObject != _notifObject))
|
|
|
|
|
{
|
|
|
|
|
[nc removeObserver: _delegate name: nil object: oldNotifObject];
|
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
if ([_delegate respondsToSelector:
|
|
|
|
|
@selector(shouldChangeTextInRange:replacementString:)])
|
|
|
|
|
{
|
|
|
|
|
_tvf.delegate_responds_to_should_change = YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_tvf.delegate_responds_to_should_change = NO;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* SET_DELEGATE_NOTIFICATION defined at the beginning of file */
|
|
|
|
|
|
|
|
|
|
/* NSText notifications */
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (DidBeginEditing);
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (DidChange);
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (DidEndEditing);
|
|
|
|
|
/* NSTextView notifications */
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (ViewDidChangeSelection);
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (ViewWillChangeNotifyingTextView);
|
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* This should only be called by [NSTextContainer -setTextView:] */
|
|
|
|
|
- (void) setTextContainer: (NSTextContainer*)aTextContainer
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
_textContainer = aTextContainer;
|
|
|
|
|
_layoutManager = [aTextContainer layoutManager];
|
|
|
|
|
_textStorage = [_layoutManager textStorage];
|
|
|
|
|
|
|
|
|
|
[self _updateMultipleTextViews];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) replaceTextContainer: (NSTextContainer*)aTextContainer
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2001-01-07 01:05:44 +00:00
|
|
|
|
/* FIXME/TODO: Tell the layout manager the text container is changed
|
|
|
|
|
keeping all the rest intact */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
/* Do not retain: text container is owning us. */
|
|
|
|
|
_textContainer = aTextContainer;
|
|
|
|
|
|
|
|
|
|
[self _updateMultipleTextViews];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (NSTextContainer *) textContainer
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return _textContainer;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setTextContainerInset: (NSSize)inset
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
_textContainerInset = inset;
|
|
|
|
|
[self invalidateTextContainerOrigin];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (NSSize) textContainerInset
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return _textContainerInset;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (NSPoint) textContainerOrigin
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return _textContainerOrigin;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) invalidateTextContainerOrigin
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2001-01-07 01:05:44 +00:00
|
|
|
|
NSRect usedRect;
|
|
|
|
|
NSSize textContainerSize;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2001-01-07 01:05:44 +00:00
|
|
|
|
usedRect = [_layoutManager usedRectForTextContainer: _textContainer];
|
|
|
|
|
textContainerSize = [_textContainer containerSize];
|
|
|
|
|
|
|
|
|
|
/* The `text container origin' is - I think - the origin of the used
|
|
|
|
|
rect, but relative to our own coordinate system (used rect as
|
|
|
|
|
returned by [NSLayoutManager -usedRectForTextContainer:] is
|
|
|
|
|
instead relative to the text container coordinate system). This
|
|
|
|
|
information is used when we ask the layout manager to draw - the
|
|
|
|
|
base point is precisely this `text container origin', which is
|
|
|
|
|
the origin of the used rect in our own coordinate system. */
|
|
|
|
|
|
|
|
|
|
/* First get the pure text container origin */
|
|
|
|
|
_textContainerOrigin.x = NSMinX (_bounds);
|
|
|
|
|
_textContainerOrigin.x += _textContainerInset.width;
|
|
|
|
|
/* Then move to the used rect origin */
|
|
|
|
|
_textContainerOrigin.x += usedRect.origin.x;
|
|
|
|
|
|
|
|
|
|
/* First get the pure text container origin */
|
|
|
|
|
_textContainerOrigin.y = NSMaxY (_bounds);
|
|
|
|
|
_textContainerOrigin.y -= _textContainerInset.height;
|
|
|
|
|
_textContainerOrigin.y -= textContainerSize.height;
|
|
|
|
|
/* Then move to the used rect origin */
|
|
|
|
|
_textContainerOrigin.y += usedRect.origin.y;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (NSLayoutManager*) layoutManager
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return _layoutManager;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (NSTextStorage*) textStorage
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return _textStorage;
|
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setAllowsUndo: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
_tvf.allows_undo = flag;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (BOOL) allowsUndo
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return _tvf.allows_undo;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setNeedsDisplayInRect: (NSRect)aRect
|
|
|
|
|
avoidAdditionalLayout: (BOOL)flag
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-22 16:57:21 +00:00
|
|
|
|
/* FIXME: This is here until the layout manager is working */
|
|
|
|
|
/* This is very important */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[super setNeedsDisplayInRect: aRect];
|
1998-08-19 09:00:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* We override NSView's setNeedsDisplayInRect: */
|
|
|
|
|
|
|
|
|
|
- (void) setNeedsDisplayInRect: (NSRect)aRect
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[self setNeedsDisplayInRect: aRect avoidAdditionalLayout: NO];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (BOOL) shouldDrawInsertionPoint
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 22:56:40 +00:00
|
|
|
|
return (_selected_range.length == 0) && _tf.is_editable;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1998-08-20 09:56:26 +00:00
|
|
|
|
|
2000-12-22 16:57:21 +00:00
|
|
|
|
/*
|
|
|
|
|
* It only makes real sense to call this method with `flag == YES'.
|
|
|
|
|
* If you want to delete the insertion point, what you want is rather
|
|
|
|
|
* to redraw what was under the insertion point - which can't be done
|
|
|
|
|
* here - you need to set the rect as needing redisplay (without
|
|
|
|
|
* additional layout) instead. NB: You need to flush the window after
|
|
|
|
|
* calling this method if you want the insertion point to appear on
|
|
|
|
|
* the screen immediately. This could only be needed to implement
|
|
|
|
|
* blinking insertion point - but even there, it could probably be
|
|
|
|
|
* done without. */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) drawInsertionPointInRect: (NSRect)rect
|
|
|
|
|
color: (NSColor*)color
|
|
|
|
|
turnedOn: (BOOL)flag
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-22 16:57:21 +00:00
|
|
|
|
if (_window == nil)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (flag)
|
|
|
|
|
{
|
|
|
|
|
if (color == nil)
|
|
|
|
|
color = _caret_color;
|
|
|
|
|
|
|
|
|
|
[color set];
|
2000-12-22 16:57:21 +00:00
|
|
|
|
NSRectFill (rect);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2000-12-20 22:56:40 +00:00
|
|
|
|
[_background_color set];
|
2000-12-22 16:57:21 +00:00
|
|
|
|
NSRectFill (rect);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
1998-08-19 09:00:26 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setConstrainedFrameSize: (NSSize)desiredSize
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2001-01-07 01:05:44 +00:00
|
|
|
|
NSSize newSize;
|
|
|
|
|
|
|
|
|
|
if (_tf.is_horizontally_resizable)
|
|
|
|
|
{
|
|
|
|
|
newSize.width = desiredSize.width;
|
|
|
|
|
newSize.width = MAX (newSize.width, _minSize.width);
|
|
|
|
|
newSize.width = MIN (newSize.width, _maxSize.width);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newSize.width = _frame.size.width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_tf.is_vertically_resizable)
|
|
|
|
|
{
|
|
|
|
|
newSize.height = desiredSize.height;
|
|
|
|
|
newSize.height = MAX (newSize.height, _minSize.height);
|
|
|
|
|
newSize.height = MIN (newSize.height, _maxSize.height);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newSize.height = _frame.size.height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (NSEqualSizes (_frame.size, newSize) == NO)
|
|
|
|
|
{
|
|
|
|
|
[self setFrameSize: newSize];
|
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) cleanUpAfterDragOperation
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// release drag information
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (unsigned int) dragOperationForDraggingInfo: (id <NSDraggingInfo>)dragInfo
|
|
|
|
|
type: (NSString *)type
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
//FIXME
|
|
|
|
|
return NSDragOperationNone;
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
* Code to share settings between multiple textviews
|
|
|
|
|
*
|
|
|
|
|
*/
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/*
|
|
|
|
|
_syncTextViewsCalling:withFlag: calls a set method on all text
|
|
|
|
|
views sharing the same layout manager as this one. It sets the
|
2000-12-21 17:32:01 +00:00
|
|
|
|
IS_SYNCHRONIZING_FLAGS flag to YES to prevent recursive calls;
|
|
|
|
|
calls the specified action on all the textviews (this one included)
|
|
|
|
|
with the specified flag; sets back the IS_SYNCHRONIZING_FLAGS flag
|
|
|
|
|
to NO; then returns.
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
We need to explicitly call the methods - we can't copy the flags
|
|
|
|
|
directly from one textview to another, to allow subclasses to
|
|
|
|
|
override eg setEditable: to take some particular action when
|
|
|
|
|
editing is turned on or off. */
|
|
|
|
|
- (void) _syncTextViewsByCalling: (SEL)action withFlag: (BOOL)flag
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSArray *array;
|
|
|
|
|
int i, count;
|
|
|
|
|
void (*msg)(id, SEL, BOOL);
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
if (IS_SYNCHRONIZING_FLAGS == YES)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"_syncTextViewsCalling:withFlag: "
|
|
|
|
|
@"called recursively"];
|
|
|
|
|
}
|
2000-05-20 16:57:04 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
array = [_layoutManager textContainers];
|
|
|
|
|
count = [array count];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
msg = (void (*)(id, SEL, BOOL))[self methodForSelector: action];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (!msg)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
|
format: @"invalid selector in "
|
|
|
|
|
@"_syncTextViewsCalling:withFlag:"];
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
IS_SYNCHRONIZING_FLAGS = YES;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
NSTextView *tv;
|
|
|
|
|
|
|
|
|
|
tv = [(NSTextContainer *)[array objectAtIndex: i] textView];
|
|
|
|
|
(*msg) (tv, action, flag);
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
IS_SYNCHRONIZING_FLAGS = NO;
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
#define NSTEXTVIEW_SYNC(X) \
|
2000-12-21 17:32:01 +00:00
|
|
|
|
if (_tvf.multiple_textviews && (IS_SYNCHRONIZING_FLAGS == NO)) \
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{ [self _syncTextViewsByCalling: @selector(##X##) withFlag: flag]; \
|
|
|
|
|
return; }
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* NB: You might override these methods in subclasses, as in the
|
|
|
|
|
* following example:
|
|
|
|
|
* - (void) setEditable: (BOOL)flag
|
|
|
|
|
* {
|
|
|
|
|
* [super setEditable: flag];
|
|
|
|
|
* XXX your custom code here XXX
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* If you override them in this way, they are automatically
|
|
|
|
|
* synchronized between multiple textviews - ie, when it is called on
|
|
|
|
|
* one, it will be automatically called on all related textviews.
|
|
|
|
|
* */
|
|
|
|
|
|
|
|
|
|
- (void) setEditable: (BOOL)flag
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSTEXTVIEW_SYNC (setEditable:);
|
|
|
|
|
[super setEditable: flag];
|
|
|
|
|
/* FIXME/TODO: Update/show the insertion point */
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setFieldEditor: (BOOL)flag
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSTEXTVIEW_SYNC (setFieldEditor:);
|
2001-01-07 01:05:44 +00:00
|
|
|
|
[self setHorizontallyResizable: NO];
|
|
|
|
|
[self setVerticallyResizable: NO];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[super setFieldEditor: flag];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setSelectable: (BOOL)flag
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSTEXTVIEW_SYNC (setSelectable:);
|
|
|
|
|
[super setSelectable: flag];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setRichText: (BOOL)flag
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSTEXTVIEW_SYNC (setRichText:);
|
2000-05-20 16:57:04 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[super setRichText: flag];
|
|
|
|
|
[self updateDragTypeRegistration];
|
|
|
|
|
/* FIXME/TODO: Also convert text to plain text or to rich text */
|
|
|
|
|
}
|
2000-05-20 16:57:04 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setImportsGraphics: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
NSTEXTVIEW_SYNC (setImportsGraphics:);
|
2000-05-20 16:57:04 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[super setImportsGraphics: flag];
|
|
|
|
|
[self updateDragTypeRegistration];
|
|
|
|
|
}
|
2000-05-20 16:57:04 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setUsesRuler: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
NSTEXTVIEW_SYNC (setUsesRuler:);
|
|
|
|
|
_tf.uses_ruler = flag;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (BOOL) usesRuler
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return _tf.uses_ruler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setUsesFontPanel: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
NSTEXTVIEW_SYNC (setUsesFontPanel:);
|
|
|
|
|
[super setUsesFontPanel: flag];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setRulerVisible: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
NSScrollView *sv;
|
|
|
|
|
|
|
|
|
|
NSTEXTVIEW_SYNC (setRulerVisible:);
|
|
|
|
|
|
|
|
|
|
sv = [self enclosingScrollView];
|
|
|
|
|
_tf.is_ruler_visible = flag;
|
|
|
|
|
if (sv != nil)
|
|
|
|
|
{
|
|
|
|
|
[sv setRulersVisible: _tf.is_ruler_visible];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef NSTEXTVIEW_SYNC
|
|
|
|
|
|
2000-12-22 17:52:23 +00:00
|
|
|
|
- (void) setSelectedRange: (NSRange)range
|
2000-12-20 17:00:32 +00:00
|
|
|
|
affinity: (NSSelectionAffinity)affinity
|
|
|
|
|
stillSelecting: (BOOL)flag
|
|
|
|
|
{
|
2000-12-22 17:52:23 +00:00
|
|
|
|
/* TODO: Use affinity to determine the insertion point */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-22 19:08:29 +00:00
|
|
|
|
/* The `official' (the last one the delegate approved of) selected
|
|
|
|
|
range before this one. */
|
|
|
|
|
NSRange oldRange;
|
|
|
|
|
/* If the user was interactively changing the selection, the last
|
|
|
|
|
displayed selection could have been a temporary selection,
|
|
|
|
|
different from the last official one: */
|
|
|
|
|
NSRange oldDisplayedRange = _selected_range;
|
|
|
|
|
|
2000-12-22 17:52:23 +00:00
|
|
|
|
if (flag == YES)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2000-12-22 19:08:29 +00:00
|
|
|
|
/* Store the original range before the interactive selection
|
|
|
|
|
process begin. That's because we will need to ask the delegate
|
|
|
|
|
if it's all right for him to do the change, and then notify
|
|
|
|
|
him we did. In both cases, we need to post the original selection
|
|
|
|
|
together with the new one. */
|
|
|
|
|
if (_original_selected_range.location == NSNotFound)
|
|
|
|
|
{
|
|
|
|
|
_original_selected_range = _selected_range;
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
2000-12-22 19:08:29 +00:00
|
|
|
|
else
|
2000-12-22 17:52:23 +00:00
|
|
|
|
{
|
2000-12-22 19:08:29 +00:00
|
|
|
|
/* Retrieve the original range */
|
|
|
|
|
if (_original_selected_range.location != NSNotFound)
|
|
|
|
|
{
|
|
|
|
|
oldRange = _original_selected_range;
|
|
|
|
|
_original_selected_range.location = NSNotFound;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
oldRange = _selected_range;
|
|
|
|
|
}
|
2000-12-22 17:52:23 +00:00
|
|
|
|
|
2000-12-22 19:08:29 +00:00
|
|
|
|
/* Ask delegate to modify the range */
|
|
|
|
|
if (_tvf.delegate_responds_to_will_change_sel)
|
|
|
|
|
{
|
|
|
|
|
range = [_delegate textView: _notifObject
|
|
|
|
|
willChangeSelectionFromCharacterRange: oldRange
|
|
|
|
|
toCharacterRange: range];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set the new selected range */
|
|
|
|
|
_selected_range = range;
|
|
|
|
|
|
|
|
|
|
if (flag == NO)
|
|
|
|
|
{
|
2000-12-22 17:52:23 +00:00
|
|
|
|
[self updateFontPanel];
|
|
|
|
|
|
2000-12-22 19:08:29 +00:00
|
|
|
|
/* Insertion Point */
|
2000-12-22 17:52:23 +00:00
|
|
|
|
if (range.length)
|
|
|
|
|
{
|
|
|
|
|
/* <!>disable caret timed entry */
|
|
|
|
|
}
|
|
|
|
|
else /* no selection, only insertion point */
|
|
|
|
|
{
|
|
|
|
|
if (_tf.is_rich_text)
|
|
|
|
|
{
|
|
|
|
|
NSDictionary *dict;
|
|
|
|
|
|
2000-12-23 13:07:36 +00:00
|
|
|
|
if (range.location > 0)
|
|
|
|
|
{
|
|
|
|
|
/* If the insertion point is after a bold word, for
|
|
|
|
|
example, we need to use bold for further
|
|
|
|
|
insertions - this is why we take the attributes
|
|
|
|
|
from range.location - 1. */
|
|
|
|
|
dict = [_textStorage attributesAtIndex: (range.location - 1)
|
|
|
|
|
effectiveRange: NULL];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Unless we are at the beginning of text - we use the
|
|
|
|
|
first valid attributes then */
|
|
|
|
|
dict = [_textStorage attributesAtIndex: range.location
|
|
|
|
|
effectiveRange: NULL];
|
|
|
|
|
}
|
2000-12-22 17:52:23 +00:00
|
|
|
|
[self setTypingAttributes: dict];
|
|
|
|
|
}
|
|
|
|
|
/* <!>enable caret timed entry */
|
|
|
|
|
}
|
2000-12-22 19:08:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_window != nil)
|
|
|
|
|
{
|
|
|
|
|
NSRange overlap;
|
|
|
|
|
|
|
|
|
|
if (flag == NO)
|
|
|
|
|
{
|
|
|
|
|
/* Make the selected range visible */
|
|
|
|
|
[self scrollRangeToVisible: range];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Try to optimize for overlapping ranges */
|
2000-12-22 17:52:23 +00:00
|
|
|
|
overlap = NSIntersectionRange (oldRange, range);
|
|
|
|
|
if (overlap.length)
|
|
|
|
|
{
|
2000-12-22 19:08:29 +00:00
|
|
|
|
if (range.location != oldDisplayedRange.location)
|
2000-12-22 17:52:23 +00:00
|
|
|
|
{
|
|
|
|
|
NSRange r;
|
2000-12-22 19:08:29 +00:00
|
|
|
|
r = MakeRangeFromAbs (MIN (range.location,
|
|
|
|
|
oldDisplayedRange.location),
|
|
|
|
|
MAX (range.location,
|
|
|
|
|
oldDisplayedRange.location));
|
|
|
|
|
[self setNeedsDisplayInRect: [self rectForCharacterRange: r]
|
|
|
|
|
avoidAdditionalLayout: YES];
|
2000-12-22 17:52:23 +00:00
|
|
|
|
}
|
2000-12-22 19:08:29 +00:00
|
|
|
|
if (NSMaxRange (range) != NSMaxRange (oldDisplayedRange))
|
2000-12-22 17:52:23 +00:00
|
|
|
|
{
|
2000-12-22 19:08:29 +00:00
|
|
|
|
NSRange r;
|
|
|
|
|
|
|
|
|
|
r = MakeRangeFromAbs (MIN (NSMaxRange (range),
|
|
|
|
|
NSMaxRange (oldDisplayedRange)),
|
|
|
|
|
MAX (NSMaxRange (range),
|
|
|
|
|
NSMaxRange (oldDisplayedRange)));
|
|
|
|
|
[self setNeedsDisplayInRect: [self rectForCharacterRange: r]
|
|
|
|
|
avoidAdditionalLayout: YES];
|
2000-12-22 17:52:23 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2000-12-22 19:08:29 +00:00
|
|
|
|
[self setNeedsDisplayInRect: [self rectForCharacterRange: range]
|
|
|
|
|
avoidAdditionalLayout: YES];
|
|
|
|
|
[self setNeedsDisplayInRect: [self rectForCharacterRange:
|
|
|
|
|
oldDisplayedRange]
|
|
|
|
|
avoidAdditionalLayout: YES];
|
2000-12-22 17:52:23 +00:00
|
|
|
|
}
|
2000-12-22 19:08:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self setSelectionGranularity: NSSelectByCharacter];
|
|
|
|
|
|
|
|
|
|
/* TODO: Remove the marking from marked text if the new selection is
|
|
|
|
|
greater than the marked region. */
|
|
|
|
|
|
|
|
|
|
if (flag == NO)
|
|
|
|
|
{
|
|
|
|
|
NSDictionary *userInfo;
|
|
|
|
|
|
|
|
|
|
userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
|
[NSValue valueWithBytes: &oldRange
|
|
|
|
|
objCType: @encode(NSRange)],
|
|
|
|
|
NSOldSelectedCharacterRange, nil];
|
|
|
|
|
|
|
|
|
|
[nc postNotificationName: NSTextViewDidChangeSelectionNotification
|
|
|
|
|
object: _notifObject userInfo: userInfo];
|
2000-12-22 17:52:23 +00:00
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-22 16:57:21 +00:00
|
|
|
|
/* Override in subclasses to change the default selection affinity */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (NSSelectionAffinity) selectionAffinity
|
|
|
|
|
{
|
2000-12-22 16:57:21 +00:00
|
|
|
|
/* FIXME: which one should be the default ? */
|
|
|
|
|
return NSSelectionAffinityDownstream;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setSelectionGranularity: (NSSelectionGranularity)granularity
|
|
|
|
|
{
|
|
|
|
|
_selectionGranularity = granularity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSSelectionGranularity) selectionGranularity
|
|
|
|
|
{
|
|
|
|
|
return _selectionGranularity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setInsertionPointColor: (NSColor*)aColor
|
|
|
|
|
{
|
|
|
|
|
ASSIGN(_caret_color, aColor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSColor*) insertionPointColor
|
|
|
|
|
{
|
|
|
|
|
return _caret_color;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) updateInsertionPointStateAndRestartTimer: (BOOL)flag
|
|
|
|
|
{
|
|
|
|
|
// _caretLocation =
|
|
|
|
|
|
|
|
|
|
// restart blinking timer.
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-22 16:57:21 +00:00
|
|
|
|
- (void) setSelectedTextAttributes: (NSDictionary *)attributes
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2000-12-22 16:57:21 +00:00
|
|
|
|
ASSIGN (_selectedTextAttributes, attributes);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-22 16:57:21 +00:00
|
|
|
|
- (NSDictionary *) selectedTextAttributes
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
return _selectedTextAttributes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSRange) markedRange
|
|
|
|
|
{
|
|
|
|
|
// calculate
|
|
|
|
|
|
|
|
|
|
return NSMakeRange(NSNotFound, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setMarkedTextAttributes: (NSDictionary*)attributes
|
|
|
|
|
{
|
|
|
|
|
ASSIGN(_markedTextAttributes, attributes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSDictionary*) markedTextAttributes
|
|
|
|
|
{
|
|
|
|
|
return _markedTextAttributes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) alignJustified: (id)sender
|
|
|
|
|
{
|
|
|
|
|
[self setAlignment: NSJustifiedTextAlignment
|
|
|
|
|
range: [self rangeForUserParagraphAttributeChange]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) changeColor: (id)sender
|
|
|
|
|
{
|
|
|
|
|
NSColor *aColor = (NSColor*)[sender color];
|
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// sets the color for the selected range.
|
|
|
|
|
[self setTextColor: aColor range: aRange];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setAlignment: (NSTextAlignment)alignment range: (NSRange)aRange
|
|
|
|
|
{
|
|
|
|
|
NSParagraphStyle *style;
|
|
|
|
|
NSMutableParagraphStyle *mstyle;
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
if (![self shouldChangeTextInRange: aRange replacementString: nil])
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage setAlignment: alignment
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
|
|
|
|
|
|
|
|
|
// Set the typing attributes
|
|
|
|
|
style = [_typingAttributes objectForKey: NSParagraphStyleAttributeName];
|
|
|
|
|
if (style == nil)
|
|
|
|
|
style = [NSParagraphStyle defaultParagraphStyle];
|
|
|
|
|
|
|
|
|
|
mstyle = [style mutableCopy];
|
|
|
|
|
|
|
|
|
|
[mstyle setAlignment: alignment];
|
|
|
|
|
// FIXME: Should use setTypingAttributes
|
|
|
|
|
[_typingAttributes setObject: mstyle forKey: NSParagraphStyleAttributeName];
|
|
|
|
|
RELEASE (mstyle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) insertText: (NSString*) insertString
|
|
|
|
|
{
|
|
|
|
|
NSRange insertRange = [self rangeForUserTextChange];
|
|
|
|
|
|
|
|
|
|
if (insertRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (_tf.is_rich_text)
|
|
|
|
|
{
|
|
|
|
|
[self replaceRange: insertRange
|
|
|
|
|
withAttributedString: AUTORELEASE([[NSAttributedString alloc]
|
|
|
|
|
initWithString: insertString
|
2000-12-23 13:07:36 +00:00
|
|
|
|
attributes: _typingAttributes])];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[self replaceCharactersInRange: insertRange
|
|
|
|
|
withString: insertString];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// move cursor <!> [self selectionRangeForProposedRange: ]
|
|
|
|
|
[self setSelectedRange:
|
|
|
|
|
NSMakeRange (insertRange.location + [insertString length], 0)];
|
|
|
|
|
|
|
|
|
|
// remember x for row - stable cursor movements
|
|
|
|
|
_currentCursor = [self rectForCharacterIndex:
|
|
|
|
|
_selected_range.location].origin;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-23 13:07:36 +00:00
|
|
|
|
- (void) setTypingAttributes: (NSDictionary*)dict
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
if (dict == nil)
|
2000-12-23 13:07:36 +00:00
|
|
|
|
{
|
|
|
|
|
dict = [isa defaultTypingAttributes];
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-23 13:07:36 +00:00
|
|
|
|
if ([dict isKindOfClass: [NSMutableDictionary class]] == NO)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2000-12-23 13:07:36 +00:00
|
|
|
|
RELEASE (_typingAttributes);
|
|
|
|
|
_typingAttributes = [[NSMutableDictionary alloc]
|
|
|
|
|
initWithDictionary: dict];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2000-12-23 13:07:36 +00:00
|
|
|
|
{
|
|
|
|
|
ASSIGN (_typingAttributes, (NSMutableDictionary*)dict);
|
|
|
|
|
}
|
2001-01-03 10:39:02 +00:00
|
|
|
|
[self updateFontPanel];
|
|
|
|
|
[self updateRuler];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSDictionary*) typingAttributes
|
|
|
|
|
{
|
|
|
|
|
return [NSDictionary dictionaryWithDictionary: _typingAttributes];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) useStandardKerning: (id)sender
|
|
|
|
|
{
|
|
|
|
|
// rekern for selected range if rich text, else rekern entire document.
|
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage removeAttribute: NSKernAttributeName
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) lowerBaseline: (id)sender
|
|
|
|
|
{
|
|
|
|
|
id value;
|
|
|
|
|
float sValue;
|
|
|
|
|
NSRange effRange;
|
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
// We take the value form the first character and use it for the whole range
|
|
|
|
|
value = [_textStorage attribute: NSBaselineOffsetAttributeName
|
|
|
|
|
atIndex: aRange.location
|
|
|
|
|
effectiveRange: &effRange];
|
|
|
|
|
|
|
|
|
|
if (value != nil)
|
|
|
|
|
sValue = [value floatValue] + 1.0;
|
|
|
|
|
else
|
|
|
|
|
sValue = 1.0;
|
|
|
|
|
|
|
|
|
|
[_textStorage addAttribute: NSBaselineOffsetAttributeName
|
|
|
|
|
value: [NSNumber numberWithFloat: sValue]
|
|
|
|
|
range: aRange];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) raiseBaseline: (id)sender
|
|
|
|
|
{
|
|
|
|
|
id value;
|
|
|
|
|
float sValue;
|
|
|
|
|
NSRange effRange;
|
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
2000-05-20 16:57:04 +00:00
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
// We take the value form the first character and use it for the whole range
|
|
|
|
|
value = [_textStorage attribute: NSBaselineOffsetAttributeName
|
|
|
|
|
atIndex: aRange.location
|
|
|
|
|
effectiveRange: &effRange];
|
|
|
|
|
|
|
|
|
|
if (value != nil)
|
|
|
|
|
sValue = [value floatValue] - 1.0;
|
1999-07-24 22:24:14 +00:00
|
|
|
|
else
|
2000-05-20 16:57:04 +00:00
|
|
|
|
sValue = -1.0;
|
|
|
|
|
|
|
|
|
|
[_textStorage addAttribute: NSBaselineOffsetAttributeName
|
|
|
|
|
value: [NSNumber numberWithFloat: sValue]
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) turnOffKerning: (id)sender
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-05-20 16:57:04 +00:00
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage addAttribute: NSKernAttributeName
|
|
|
|
|
value: [NSNumber numberWithFloat: 0.0]
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) loosenKerning: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-05-20 16:57:04 +00:00
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
// FIXME: Should use the current kerning and work relative to point size
|
|
|
|
|
[_textStorage addAttribute: NSKernAttributeName
|
|
|
|
|
value: [NSNumber numberWithFloat: 1.0]
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) tightenKerning: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-05-20 16:57:04 +00:00
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
// FIXME: Should use the current kerning and work relative to point size
|
|
|
|
|
[_textStorage addAttribute: NSKernAttributeName
|
|
|
|
|
value: [NSNumber numberWithFloat: -1.0]
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) useStandardLigatures: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-05-20 16:57:04 +00:00
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage addAttribute: NSLigatureAttributeName
|
|
|
|
|
value: [NSNumber numberWithInt: 1]
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) turnOffLigatures: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-05-20 16:57:04 +00:00
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage addAttribute: NSLigatureAttributeName
|
|
|
|
|
value: [NSNumber numberWithInt: 0]
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) useAllLigatures: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-05-20 16:57:04 +00:00
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
|
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage addAttribute: NSLigatureAttributeName
|
|
|
|
|
value: [NSNumber numberWithInt: 2]
|
|
|
|
|
range: aRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) clickedOnLink: (id)link
|
|
|
|
|
atIndex: (unsigned int)charIndex
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
|
|
|
|
|
2000-12-23 13:07:36 +00:00
|
|
|
|
/* Notifies the delegate that the user clicked in a link at the
|
|
|
|
|
specified charIndex. The delegate may take any appropriate actions
|
|
|
|
|
to handle the click in its textView: clickedOnLink: atIndex:
|
|
|
|
|
method. */
|
2000-06-16 17:12:25 +00:00
|
|
|
|
if (_delegate != nil &&
|
2000-12-18 18:08:21 +00:00
|
|
|
|
[_delegate respondsToSelector:
|
|
|
|
|
@selector(textView:clickedOnLink:atIndex:)])
|
2000-06-16 17:12:25 +00:00
|
|
|
|
[_delegate textView: self clickedOnLink: link atIndex: charIndex];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
The text is inserted at the insertion point if there is one, otherwise
|
|
|
|
|
replacing the selection.
|
|
|
|
|
*/
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) pasteAsPlainText: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-05-23 22:59:36 +00:00
|
|
|
|
[self readSelectionFromPasteboard: [NSPasteboard generalPasteboard]
|
2001-01-03 10:39:02 +00:00
|
|
|
|
type: NSStringPboardType];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) pasteAsRichText: (id)sender
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-05-23 22:59:36 +00:00
|
|
|
|
[self readSelectionFromPasteboard: [NSPasteboard generalPasteboard]
|
2001-01-03 10:39:02 +00:00
|
|
|
|
type: NSRTFPboardType];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) updateFontPanel
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-23 13:07:36 +00:00
|
|
|
|
/* Update fontPanel only if told so */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (_tf.uses_font_panel)
|
|
|
|
|
{
|
|
|
|
|
NSRange longestRange;
|
2000-12-20 22:56:40 +00:00
|
|
|
|
NSFontManager *fm = [NSFontManager sharedFontManager];
|
|
|
|
|
NSFont *currentFont;
|
|
|
|
|
|
2000-12-23 13:07:36 +00:00
|
|
|
|
if (_selected_range.length > 0) /* Multiple chars selection */
|
|
|
|
|
{
|
|
|
|
|
currentFont = [_textStorage attribute: NSFontAttributeName
|
|
|
|
|
atIndex: _selected_range.location
|
|
|
|
|
longestEffectiveRange: &longestRange
|
|
|
|
|
inRange: _selected_range];
|
|
|
|
|
[fm setSelectedFont: currentFont
|
|
|
|
|
isMultiple: !NSEqualRanges (longestRange, _selected_range)];
|
|
|
|
|
}
|
|
|
|
|
else /* Just Insertion Point. */
|
|
|
|
|
{
|
2001-01-03 10:39:02 +00:00
|
|
|
|
currentFont = [_typingAttributes objectForKey: NSFontAttributeName];
|
2000-12-23 13:07:36 +00:00
|
|
|
|
[fm setSelectedFont: currentFont isMultiple: NO];
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) updateRuler
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
|
|
|
|
// ruler!
|
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (NSArray*) acceptableDragTypes
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-05-23 22:59:36 +00:00
|
|
|
|
return [self readablePasteboardTypes];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-02-19 00:40:47 +00:00
|
|
|
|
- (void) updateDragTypeRegistration
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-05-23 22:59:36 +00:00
|
|
|
|
// FIXME: Should change registration for all our text views
|
|
|
|
|
if (_tf.is_editable && _tf.is_rich_text)
|
|
|
|
|
[self registerForDraggedTypes: [self acceptableDragTypes]];
|
|
|
|
|
else
|
|
|
|
|
[self unregisterDraggedTypes];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (BOOL) shouldChangeTextInRange: (NSRange)affectedCharRange
|
|
|
|
|
replacementString: (NSString*)replacementString
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-21 17:32:01 +00:00
|
|
|
|
if (BEGAN_EDITING == NO)
|
|
|
|
|
{
|
|
|
|
|
if (([_delegate respondsToSelector: @selector(textShouldBeginEditing:)])
|
|
|
|
|
&& ([_delegate textShouldBeginEditing: _notifObject] == NO))
|
|
|
|
|
return NO;
|
|
|
|
|
|
|
|
|
|
SET_BEGAN_EDITING (YES);
|
|
|
|
|
|
|
|
|
|
[nc postNotificationName: NSTextDidBeginEditingNotification
|
|
|
|
|
object: _notifObject];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_tvf.delegate_responds_to_should_change)
|
|
|
|
|
{
|
|
|
|
|
return [_delegate shouldChangeTextInRange: affectedCharRange
|
|
|
|
|
replacementString: replacementString];
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
return YES;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) didChangeText
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[nc postNotificationName: NSTextDidChangeNotification
|
|
|
|
|
object: _notifObject];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) setSmartInsertDeleteEnabled: (BOOL)flag
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
_tvf.smart_insert_delete = flag;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (BOOL) smartInsertDeleteEnabled
|
|
|
|
|
{
|
|
|
|
|
return _tvf.smart_insert_delete;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSRange) smartDeleteRangeForProposedRange: (NSRange)proposedCharRange
|
|
|
|
|
{
|
|
|
|
|
// FIXME.
|
|
|
|
|
return proposedCharRange;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)smartInsertAfterStringForString: (NSString *)aString
|
|
|
|
|
replacingRange: (NSRange)charRange
|
|
|
|
|
{
|
|
|
|
|
// FIXME.
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString *)smartInsertBeforeStringForString: (NSString *)aString
|
|
|
|
|
replacingRange: (NSRange)charRange
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// FIXME.
|
|
|
|
|
return nil;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) smartInsertForString: (NSString*)aString
|
|
|
|
|
replacingRange: (NSRange)charRange
|
|
|
|
|
beforeString: (NSString**)beforeString
|
|
|
|
|
afterString: (NSString**)afterString
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* Determines whether whitespace needs to be added around aString to
|
|
|
|
|
preserve proper spacing and punctuation when it's inserted into the
|
|
|
|
|
receiver's text over charRange. Returns by reference in beforeString and
|
|
|
|
|
afterString any whitespace that should be added, unless either or both is
|
|
|
|
|
nil. Both are returned as nil if aString is nil or if smart insertion and
|
|
|
|
|
deletion is disabled.
|
|
|
|
|
|
|
|
|
|
As part of its implementation, this method calls
|
|
|
|
|
smartInsertAfterStringForString: replacingRange: and
|
|
|
|
|
smartInsertBeforeStringForString: replacingRange: .To change this method's
|
|
|
|
|
behavior, override those two methods instead of this one.
|
|
|
|
|
|
|
|
|
|
NSTextView uses this method as necessary. You can also use it in
|
|
|
|
|
implementing your own methods that insert text. To do so, invoke this
|
|
|
|
|
method with the proper arguments, then insert beforeString, aString, and
|
|
|
|
|
afterString in order over charRange. */
|
|
|
|
|
if (beforeString)
|
|
|
|
|
*beforeString = [self smartInsertBeforeStringForString: aString
|
|
|
|
|
replacingRange: charRange];
|
|
|
|
|
|
|
|
|
|
if (afterString)
|
|
|
|
|
*afterString = [self smartInsertAfterStringForString: aString
|
|
|
|
|
replacingRange: charRange];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Handling Events
|
|
|
|
|
*/
|
|
|
|
|
- (void) mouseDown: (NSEvent*)theEvent
|
|
|
|
|
{
|
2000-12-22 17:52:23 +00:00
|
|
|
|
NSSelectionAffinity affinity = [self selectionAffinity];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSSelectionGranularity granularity = NSSelectByCharacter;
|
|
|
|
|
NSRange chosenRange, proposedRange;
|
|
|
|
|
NSPoint point, startPoint;
|
|
|
|
|
NSEvent *currentEvent;
|
|
|
|
|
unsigned startIndex;
|
2000-12-22 17:52:23 +00:00
|
|
|
|
unsigned mask;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
/* If non selectable then ignore the mouse down. */
|
|
|
|
|
if (_tf.is_selectable == NO)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
/* Otherwise, NSWindow has already made us first responder (if
|
|
|
|
|
possible) */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-22 17:52:23 +00:00
|
|
|
|
startPoint = [self convertPoint: [theEvent locationInWindow] fromView: nil];
|
|
|
|
|
|
|
|
|
|
if ([theEvent modifierFlags] & NSShiftKeyMask)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2000-12-22 17:52:23 +00:00
|
|
|
|
/* Shift-click is for extending an existing selection using
|
|
|
|
|
the existing granularity */
|
|
|
|
|
granularity = _selectionGranularity;
|
|
|
|
|
/* Compute the new selection */
|
|
|
|
|
startIndex = [self characterIndexForPoint: startPoint];
|
|
|
|
|
proposedRange = NSMakeRange (startIndex, 0);
|
2000-12-27 06:28:29 +00:00
|
|
|
|
proposedRange = NSUnionRange (_selected_range, proposedRange);
|
2000-12-22 17:52:23 +00:00
|
|
|
|
proposedRange = [self selectionRangeForProposedRange: proposedRange
|
|
|
|
|
granularity: granularity];
|
|
|
|
|
/* Merge it with the old one */
|
|
|
|
|
proposedRange = NSUnionRange (_selected_range, proposedRange);
|
|
|
|
|
/* Now decide what happens if the user shift-drags. The range
|
|
|
|
|
will be based in startIndex, so we need to adjust it. */
|
|
|
|
|
if (startIndex <= _selected_range.location)
|
|
|
|
|
{
|
|
|
|
|
startIndex = NSMaxRange (proposedRange);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
startIndex = proposedRange.location;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else /* No shift */
|
|
|
|
|
{
|
|
|
|
|
switch ([theEvent clickCount])
|
|
|
|
|
{
|
|
|
|
|
case 1: granularity = NSSelectByCharacter;
|
|
|
|
|
break;
|
|
|
|
|
case 2: granularity = NSSelectByWord;
|
|
|
|
|
break;
|
|
|
|
|
case 3: granularity = NSSelectByParagraph;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
startIndex = [self characterIndexForPoint: startPoint];
|
|
|
|
|
proposedRange = NSMakeRange (startIndex, 0);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chosenRange = [self selectionRangeForProposedRange: proposedRange
|
|
|
|
|
granularity: granularity];
|
2000-12-22 19:08:29 +00:00
|
|
|
|
[self setSelectedRange: chosenRange affinity: affinity
|
|
|
|
|
stillSelecting: YES];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-22 17:52:23 +00:00
|
|
|
|
/* Do an immediate redisplay for visual feedback */
|
|
|
|
|
[_window flushWindow]; /* FIXME: This doesn't work while it should ! */
|
|
|
|
|
|
|
|
|
|
/* Enter modal loop tracking the mouse */
|
|
|
|
|
|
|
|
|
|
mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask;
|
|
|
|
|
|
|
|
|
|
for (currentEvent = [_window nextEventMatchingMask: mask];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[currentEvent type] != NSLeftMouseUp;
|
2000-12-22 17:52:23 +00:00
|
|
|
|
currentEvent = [_window nextEventMatchingMask: mask])
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
BOOL didScroll = [self autoscroll: currentEvent];
|
2000-12-22 17:52:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
point = [self convertPoint: [currentEvent locationInWindow]
|
|
|
|
|
fromView: nil];
|
|
|
|
|
proposedRange = MakeRangeFromAbs ([self characterIndexForPoint: point],
|
|
|
|
|
startIndex);
|
|
|
|
|
chosenRange = [self selectionRangeForProposedRange: proposedRange
|
|
|
|
|
granularity: granularity];
|
2000-12-22 19:08:29 +00:00
|
|
|
|
[self setSelectedRange: chosenRange affinity: affinity
|
|
|
|
|
stillSelecting: YES];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
if (didScroll)
|
2000-12-22 17:52:23 +00:00
|
|
|
|
{
|
|
|
|
|
/* FIXME: Only redisplay where needed, and avoid relayout */
|
|
|
|
|
[self setNeedsDisplay: YES];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do an immediate redisplay for visual feedback */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[_window flushWindow];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NSDebugLog(@"chosenRange. location = %d, length = %d\n",
|
|
|
|
|
(int)chosenRange.location, (int)chosenRange.length);
|
2000-12-22 17:52:23 +00:00
|
|
|
|
|
2000-12-22 19:08:29 +00:00
|
|
|
|
[self setSelectedRange: chosenRange affinity: affinity
|
|
|
|
|
stillSelecting: NO];
|
|
|
|
|
|
|
|
|
|
/* Ahm - this shouldn't really be needed but... */
|
|
|
|
|
[_window flushWindow];
|
|
|
|
|
|
2000-12-22 17:52:23 +00:00
|
|
|
|
/* Remember granularity till a new selection destroys the memory */
|
|
|
|
|
[self setSelectionGranularity: granularity];
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// remember for column stable cursor up/down
|
|
|
|
|
_currentCursor = [self rectForCharacterIndex: chosenRange.location].origin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) keyDown: (NSEvent*)theEvent
|
|
|
|
|
{
|
|
|
|
|
// If not editable, don't recognize the key down
|
2000-12-21 17:32:01 +00:00
|
|
|
|
if (_tf.is_editable == NO)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
[super keyDown: theEvent];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[self interpretKeyEvents: [NSArray arrayWithObject: theEvent]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) insertNewline: (id)sender
|
|
|
|
|
{
|
|
|
|
|
if (_tf.is_field_editor)
|
|
|
|
|
{
|
|
|
|
|
[self _illegalMovement: NSReturnTextMovement];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self insertText: @"\n"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) insertTab: (id)sender
|
|
|
|
|
{
|
|
|
|
|
if (_tf.is_field_editor)
|
|
|
|
|
{
|
|
|
|
|
[self _illegalMovement: NSTabTextMovement];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[self insertText: @"\t"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) insertBacktab: (id)sender
|
|
|
|
|
{
|
|
|
|
|
if (_tf.is_field_editor)
|
|
|
|
|
{
|
|
|
|
|
[self _illegalMovement: NSBacktabTextMovement];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//[self insertText: @"\t"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) deleteForward: (id)sender
|
|
|
|
|
{
|
2000-12-20 22:56:40 +00:00
|
|
|
|
unsigned location = _selected_range.location;
|
|
|
|
|
|
|
|
|
|
if (location != [self textLength])
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* Not at the end of text -- delete following character */
|
2000-12-20 22:56:40 +00:00
|
|
|
|
NSRange delRange = NSMakeRange (location, 1);
|
|
|
|
|
|
|
|
|
|
delRange = [self selectionRangeForProposedRange: delRange
|
|
|
|
|
granularity: NSSelectByCharacter];
|
|
|
|
|
[self deleteRange: delRange backspace: NO];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* end of text: behave the same way as NSBackspaceKey */
|
|
|
|
|
[self deleteBackward: sender];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) deleteBackward: (id)sender
|
|
|
|
|
{
|
|
|
|
|
[self deleteRange: _selected_range backspace: YES];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//<!> choose granularity according to keyboard modifier flags
|
|
|
|
|
- (void) moveUp: (id)sender
|
|
|
|
|
{
|
|
|
|
|
unsigned cursorIndex;
|
|
|
|
|
NSPoint cursorPoint;
|
|
|
|
|
NSRange newRange;
|
|
|
|
|
|
|
|
|
|
if (_tf.is_field_editor)
|
|
|
|
|
{
|
|
|
|
|
[self _illegalMovement: NSUpTextMovement];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do nothing if we are at beginning of text */
|
|
|
|
|
if (_selected_range.location == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (_selected_range.length)
|
|
|
|
|
{
|
|
|
|
|
_currentCursor = [self rectForCharacterIndex:
|
|
|
|
|
_selected_range.location].origin;
|
|
|
|
|
}
|
|
|
|
|
cursorIndex = _selected_range.location;
|
|
|
|
|
cursorPoint = [self rectForCharacterIndex: cursorIndex].origin;
|
|
|
|
|
cursorIndex = [self characterIndexForPoint:
|
|
|
|
|
NSMakePoint (_currentCursor.x + 0.001,
|
|
|
|
|
MAX (0, cursorPoint.y - 0.001))];
|
|
|
|
|
|
|
|
|
|
newRange.location = cursorIndex;
|
|
|
|
|
newRange.length = 0;
|
|
|
|
|
[self setSelectedRange: newRange];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) moveDown: (id)sender
|
|
|
|
|
{
|
|
|
|
|
unsigned cursorIndex;
|
|
|
|
|
NSRect cursorRect;
|
|
|
|
|
NSRange newRange;
|
|
|
|
|
|
|
|
|
|
if (_tf.is_field_editor)
|
|
|
|
|
{
|
|
|
|
|
[self _illegalMovement: NSDownTextMovement];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do nothing if we are at end of text */
|
|
|
|
|
if (_selected_range.location == [self textLength])
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (_selected_range.length != 0)
|
|
|
|
|
{
|
|
|
|
|
_currentCursor = [self rectForCharacterIndex:
|
|
|
|
|
NSMaxRange (_selected_range)].origin;
|
|
|
|
|
}
|
|
|
|
|
cursorIndex = _selected_range.location;
|
|
|
|
|
cursorRect = [self rectForCharacterIndex: cursorIndex];
|
|
|
|
|
cursorIndex = [self characterIndexForPoint:
|
|
|
|
|
NSMakePoint(_currentCursor.x + 0.001,
|
|
|
|
|
NSMaxY (cursorRect) + 0.001)];
|
|
|
|
|
|
|
|
|
|
newRange.location = cursorIndex;
|
|
|
|
|
newRange.length = 0;
|
|
|
|
|
[self setSelectedRange: newRange];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) moveLeft: (id)sender
|
|
|
|
|
{
|
|
|
|
|
NSRange newSelectedRange;
|
|
|
|
|
|
|
|
|
|
/* Do nothing if we are at beginning of text with no selection */
|
|
|
|
|
if (_selected_range.location == 0 && _selected_range.length == 0)
|
|
|
|
|
{
|
|
|
|
|
if (_tf.is_field_editor)
|
|
|
|
|
{
|
|
|
|
|
[self _illegalMovement: NSLeftTextMovement];
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_selected_range.location == 0)
|
|
|
|
|
newSelectedRange.location = 0;
|
|
|
|
|
else
|
|
|
|
|
newSelectedRange.location = _selected_range.location - 1;
|
|
|
|
|
newSelectedRange.length = 0;
|
|
|
|
|
|
|
|
|
|
[self setSelectedRange: newSelectedRange];
|
|
|
|
|
|
|
|
|
|
_currentCursor.x = [self rectForCharacterIndex:
|
|
|
|
|
_selected_range.location].origin.x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) moveRight: (id)sender
|
|
|
|
|
{
|
|
|
|
|
NSRange newSelectedRange;
|
|
|
|
|
unsigned int length = [self textLength];
|
|
|
|
|
|
|
|
|
|
/* Do nothing if we are at end of text */
|
|
|
|
|
if (_selected_range.location == length)
|
|
|
|
|
{
|
|
|
|
|
if (_tf.is_field_editor)
|
|
|
|
|
{
|
|
|
|
|
[self _illegalMovement: NSRightTextMovement];
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newSelectedRange.location = MIN(NSMaxRange(_selected_range) + 1, length);
|
|
|
|
|
newSelectedRange.length = 0;
|
|
|
|
|
|
|
|
|
|
[self setSelectedRange: newSelectedRange];
|
|
|
|
|
|
|
|
|
|
_currentCursor.x = [self rectForCharacterIndex:
|
|
|
|
|
_selected_range.location].origin.x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- (BOOL) acceptsFirstResponder
|
|
|
|
|
{
|
2000-12-20 22:56:40 +00:00
|
|
|
|
if (_tf.is_selectable)
|
2000-12-21 17:32:01 +00:00
|
|
|
|
{
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
else
|
2000-12-21 17:32:01 +00:00
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) resignFirstResponder
|
|
|
|
|
{
|
2000-12-21 17:32:01 +00:00
|
|
|
|
if (_tvf.multiple_textviews == YES)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2000-12-21 17:32:01 +00:00
|
|
|
|
id futureFirstResponder;
|
|
|
|
|
NSArray *textContainers;
|
|
|
|
|
int i, count;
|
|
|
|
|
|
|
|
|
|
futureFirstResponder = [_window _futureFirstResponder];
|
|
|
|
|
textContainers = [_layoutManager textContainers];
|
|
|
|
|
count = [textContainers count];
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
NSTextContainer *container;
|
|
|
|
|
NSTextView *view;
|
|
|
|
|
|
|
|
|
|
container = (NSTextContainer *)[textContainers objectAtIndex: i];
|
|
|
|
|
view = [container textView];
|
|
|
|
|
|
|
|
|
|
if (view == futureFirstResponder)
|
|
|
|
|
{
|
|
|
|
|
/* NB: We do not reset the BEGAN_EDITING flag so that no
|
|
|
|
|
spurious notification is generated. */
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
2000-12-21 17:32:01 +00:00
|
|
|
|
|
|
|
|
|
/* NB: Possible change: ask always - not only if editable - but we
|
|
|
|
|
need to change NSTextField etc to allow this. */
|
2000-12-20 22:56:40 +00:00
|
|
|
|
if ((_tf.is_editable)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
&& ([_delegate respondsToSelector: @selector(textShouldEndEditing:)])
|
|
|
|
|
&& ([_delegate textShouldEndEditing: self] == NO))
|
2000-12-21 17:32:01 +00:00
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add any clean-up stuff here */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
if ([self shouldDrawInsertionPoint])
|
|
|
|
|
{
|
2000-12-22 16:57:21 +00:00
|
|
|
|
NSRect rect;
|
|
|
|
|
|
|
|
|
|
rect = [self rectForInsertionPointAtIndex: _selected_range.location];
|
|
|
|
|
[self setNeedsDisplayInRect: rect avoidAdditionalLayout: YES];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
//<!> stop timed entry
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
SET_BEGAN_EDITING (NO);
|
|
|
|
|
|
|
|
|
|
/* NB: According to the doc (and to the tradition), we post this
|
|
|
|
|
notification even if no real editing was actually done (only
|
|
|
|
|
selection of text) [Note: in this case, no editing was started,
|
|
|
|
|
so the notification does not come after a
|
|
|
|
|
NSTextDidBeginEditingNotification!]. The notification only means
|
|
|
|
|
that we are resigning first responder status. This makes sense
|
|
|
|
|
because many objects inside the gui need this notification anyway
|
|
|
|
|
- typically, it is needed to remove a field editor (editable or
|
|
|
|
|
not) when the user presses TAB to move to the next view. Anyway
|
|
|
|
|
yes, the notification name is misleading. */
|
|
|
|
|
[nc postNotificationName: NSTextDidEndEditingNotification
|
|
|
|
|
object: _notifObject];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) becomeFirstResponder
|
|
|
|
|
{
|
2000-12-20 22:56:40 +00:00
|
|
|
|
if (_tf.is_selectable == NO)
|
2000-12-21 17:32:01 +00:00
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
/* NB: Notifications (NSTextBeginEditingNotification etc) are managed
|
|
|
|
|
on the first time the user tries to edit us. */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
/* Draw selection, update insertion point */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
//if ([self shouldDrawInsertionPoint])
|
|
|
|
|
// {
|
|
|
|
|
// [self lockFocus];
|
|
|
|
|
// [self drawInsertionPointAtIndex: _selected_range.location
|
|
|
|
|
// color: _caret_color turnedOn: YES];
|
|
|
|
|
// [self unlockFocus];
|
|
|
|
|
// //<!> restart timed entry
|
|
|
|
|
// }
|
2000-12-21 17:32:01 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) drawRect: (NSRect)rect
|
|
|
|
|
{
|
2000-12-22 16:57:21 +00:00
|
|
|
|
/* TODO: Only do relayout if needed */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSRange drawnRange = [_layoutManager glyphRangeForBoundingRect: rect
|
2000-12-20 22:56:40 +00:00
|
|
|
|
inTextContainer: _textContainer];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (_tf.draws_background)
|
|
|
|
|
{
|
|
|
|
|
[_layoutManager drawBackgroundForGlyphRange: drawnRange
|
2000-12-20 22:56:40 +00:00
|
|
|
|
atPoint: _textContainerOrigin];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[_layoutManager drawGlyphsForGlyphRange: drawnRange
|
2000-12-20 22:56:40 +00:00
|
|
|
|
atPoint: _textContainerOrigin];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
if ([self shouldDrawInsertionPoint])
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2000-12-20 22:56:40 +00:00
|
|
|
|
unsigned location = _selected_range.location;
|
|
|
|
|
|
|
|
|
|
if (NSLocationInRange (location, drawnRange)
|
|
|
|
|
|| location == NSMaxRange (drawnRange))
|
|
|
|
|
{
|
2000-12-22 16:57:21 +00:00
|
|
|
|
NSRect rect;
|
|
|
|
|
|
|
|
|
|
rect = [self rectForInsertionPointAtIndex: location];
|
|
|
|
|
[self drawInsertionPointInRect: rect color: _caret_color
|
2000-12-20 22:56:40 +00:00
|
|
|
|
turnedOn: YES];
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Ruler Views
|
|
|
|
|
*/
|
|
|
|
|
- (void) rulerView: (NSRulerView*)aRulerView
|
|
|
|
|
didMoveMarker: (NSRulerMarker*)aMarker
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
NSTextView checks for permission to make the change in its
|
|
|
|
|
rulerView: shouldMoveMarker: method, which invokes
|
|
|
|
|
shouldChangeTextInRange: replacementString: to send out the proper request
|
|
|
|
|
and notifications, and only invokes this
|
|
|
|
|
method if permission is granted.
|
|
|
|
|
|
|
|
|
|
[self didChangeText];
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) rulerView: (NSRulerView*)aRulerView
|
|
|
|
|
didRemoveMarker: (NSRulerMarker*)aMarker
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
NSTextView checks for permission to move or remove a tab stop in its
|
|
|
|
|
rulerView: shouldMoveMarker: method, which invokes
|
|
|
|
|
shouldChangeTextInRange: replacementString: to send out the proper request
|
|
|
|
|
and notifications, and only invokes this method if permission is granted.
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)rulerView:(NSRulerView *)ruler
|
|
|
|
|
didAddMarker:(NSRulerMarker *)marker
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) rulerView: (NSRulerView*)aRulerView
|
|
|
|
|
handleMouseDown: (NSEvent*)theEvent
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
This NSRulerView client method adds a left tab marker to the ruler, but a
|
|
|
|
|
subclass can override this method to provide other behavior, such as
|
|
|
|
|
creating guidelines. This method is invoked once with theEvent when the
|
|
|
|
|
user first clicks in the aRulerView's ruler area, as described in the
|
|
|
|
|
NSRulerView class specification.
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) rulerView: (NSRulerView*)aRulerView
|
|
|
|
|
shouldAddMarker: (NSRulerMarker*)aMarker
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* This NSRulerView client method controls whether a new tab stop can be
|
|
|
|
|
added. The receiver checks for permission to make the change by invoking
|
|
|
|
|
shouldChangeTextInRange: replacementString: and returning the return value
|
|
|
|
|
of that message. If the change is allowed, the receiver is then sent a
|
|
|
|
|
rulerView: didAddMarker: message. */
|
|
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) rulerView: (NSRulerView*)aRulerView
|
|
|
|
|
shouldMoveMarker: (NSRulerMarker*)aMarker
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* This NSRulerView client method controls whether an existing tab stop
|
|
|
|
|
can be moved. The receiver checks for permission to make the change by
|
|
|
|
|
invoking shouldChangeTextInRange: replacementString: and returning the
|
|
|
|
|
return value of that message. If the change is allowed, the receiver is
|
|
|
|
|
then sent a rulerView: didAddMarker: message. */
|
|
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) rulerView: (NSRulerView*)aRulerView
|
|
|
|
|
shouldRemoveMarker: (NSRulerMarker*)aMarker
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* This NSRulerView client method controls whether an existing tab stop
|
|
|
|
|
can be removed. Returns YES if aMarker represents an NSTextTab, NO
|
|
|
|
|
otherwise. Because this method can be invoked repeatedly as the user drags
|
|
|
|
|
a ruler marker, it returns that value immediately. If the change is allows
|
|
|
|
|
and the user actually removes the marker, the receiver is also sent a
|
|
|
|
|
rulerView: didRemoveMarker: message. */
|
|
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (float) rulerView: (NSRulerView*)aRulerView
|
|
|
|
|
willAddMarker: (NSRulerMarker*)aMarker
|
|
|
|
|
atLocation: (float)location
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* This NSRulerView client method ensures that the proposed location of
|
|
|
|
|
aMarker lies within the appropriate bounds for the receiver's text
|
|
|
|
|
container, returning the modified location. */
|
|
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (float) rulerView: (NSRulerView*)aRulerView
|
|
|
|
|
willMoveMarker: (NSRulerMarker*)aMarker
|
|
|
|
|
toLocation: (float)location
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* This NSRulerView client method ensures that the proposed location of
|
|
|
|
|
aMarker lies within the appropriate bounds for the receiver's text
|
|
|
|
|
container, returning the modified location. */
|
|
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) setDelegate: (id)anObject
|
|
|
|
|
{
|
2000-12-22 19:08:29 +00:00
|
|
|
|
SEL selector;
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* Code to allow sharing the delegate */
|
2000-12-21 17:32:01 +00:00
|
|
|
|
if (_tvf.multiple_textviews && (IS_SYNCHRONIZING_DELEGATES == NO))
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
/* Invoke setDelegate: on all the textviews which share this
|
|
|
|
|
delegate. */
|
|
|
|
|
NSArray *array;
|
|
|
|
|
int i, count;
|
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
IS_SYNCHRONIZING_DELEGATES = YES;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
array = [_layoutManager textContainers];
|
|
|
|
|
count = [array count];
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
NSTextView *view;
|
|
|
|
|
|
|
|
|
|
view = [(NSTextContainer *)[array objectAtIndex: i] textView];
|
|
|
|
|
[view setDelegate: anObject];
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-21 17:32:01 +00:00
|
|
|
|
IS_SYNCHRONIZING_DELEGATES = NO;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now the real code to set the delegate */
|
|
|
|
|
|
|
|
|
|
if (_delegate != nil)
|
|
|
|
|
{
|
|
|
|
|
[nc removeObserver: _delegate name: nil object: _notifObject];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[super setDelegate: anObject];
|
|
|
|
|
|
2000-12-22 19:08:29 +00:00
|
|
|
|
selector = @selector(shouldChangeTextInRange:replacementString:);
|
|
|
|
|
if ([_delegate respondsToSelector: selector])
|
2000-12-21 17:32:01 +00:00
|
|
|
|
{
|
|
|
|
|
_tvf.delegate_responds_to_should_change = YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_tvf.delegate_responds_to_should_change = NO;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-22 19:08:29 +00:00
|
|
|
|
selector = @selector(textView:willChangeSelectionFromCharacterRange:toCharacterRange:);
|
|
|
|
|
if ([_delegate respondsToSelector: selector])
|
|
|
|
|
{
|
|
|
|
|
_tvf.delegate_responds_to_will_change_sel = YES;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_tvf.delegate_responds_to_will_change_sel = NO;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
/* SET_DELEGATE_NOTIFICATION defined at the beginning of file */
|
|
|
|
|
|
|
|
|
|
/* NSText notifications */
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (DidBeginEditing);
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (DidChange);
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (DidEndEditing);
|
|
|
|
|
|
|
|
|
|
/* NSTextView notifications */
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (ViewDidChangeSelection);
|
|
|
|
|
SET_DELEGATE_NOTIFICATION (ViewWillChangeNotifyingTextView);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (int) spellCheckerDocumentTag
|
|
|
|
|
{
|
|
|
|
|
if (!_spellCheckerDocumentTag)
|
|
|
|
|
_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
|
|
|
|
|
|
|
|
|
|
return _spellCheckerDocumentTag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSRange) selectionRangeForProposedRange: (NSRange)proposedCharRange
|
2000-12-23 13:07:36 +00:00
|
|
|
|
granularity: (NSSelectionGranularity)granul
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned index;
|
|
|
|
|
NSRange aRange;
|
|
|
|
|
NSRange newRange;
|
|
|
|
|
NSString *string = [self string];
|
|
|
|
|
unsigned length = [string length];
|
|
|
|
|
|
|
|
|
|
if (proposedCharRange.location >= length)
|
|
|
|
|
{
|
|
|
|
|
proposedCharRange.location = length;
|
|
|
|
|
proposedCharRange.length = 0;
|
|
|
|
|
return proposedCharRange;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-23 13:07:36 +00:00
|
|
|
|
if (NSMaxRange (proposedCharRange) > length)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
proposedCharRange.length = length - proposedCharRange.location;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (length == 0)
|
|
|
|
|
{
|
|
|
|
|
return proposedCharRange;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-23 13:07:36 +00:00
|
|
|
|
switch (granul)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
case NSSelectByWord:
|
2000-12-27 06:28:29 +00:00
|
|
|
|
index = proposedCharRange.location;
|
|
|
|
|
if (index >= length)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2000-12-27 06:28:29 +00:00
|
|
|
|
index = length - 1;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
2000-12-27 06:28:29 +00:00
|
|
|
|
newRange = [_textStorage doubleClickAtIndex: index];
|
|
|
|
|
if (proposedCharRange.length > 1)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2000-12-27 06:28:29 +00:00
|
|
|
|
index = NSMaxRange(proposedCharRange) - 1;
|
|
|
|
|
if (index >= length)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
2000-12-27 06:28:29 +00:00
|
|
|
|
index = length - 1;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
2000-12-27 06:28:29 +00:00
|
|
|
|
aRange = [_textStorage doubleClickAtIndex: index];
|
|
|
|
|
newRange = NSUnionRange(newRange, aRange);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
return newRange;
|
|
|
|
|
|
|
|
|
|
case NSSelectByParagraph:
|
|
|
|
|
return [string lineRangeForRange: proposedCharRange];
|
|
|
|
|
|
|
|
|
|
case NSSelectByCharacter:
|
|
|
|
|
default:
|
|
|
|
|
if (proposedCharRange.length == 0)
|
|
|
|
|
return proposedCharRange;
|
|
|
|
|
|
|
|
|
|
/* Expand the beginning character */
|
|
|
|
|
index = proposedCharRange.location;
|
|
|
|
|
newRange = [string rangeOfComposedCharacterSequenceAtIndex: index];
|
|
|
|
|
/* If the proposedCharRange is empty we only ajust the beginning */
|
|
|
|
|
if (proposedCharRange.length == 0)
|
|
|
|
|
{
|
|
|
|
|
return newRange;
|
|
|
|
|
}
|
|
|
|
|
/* Expand the finishing character */
|
|
|
|
|
index = NSMaxRange (proposedCharRange) - 1;
|
|
|
|
|
aRange = [string rangeOfComposedCharacterSequenceAtIndex: index];
|
|
|
|
|
newRange.length = NSMaxRange(aRange) - newRange.location;
|
|
|
|
|
return newRange;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSRange) rangeForUserCharacterAttributeChange
|
|
|
|
|
{
|
|
|
|
|
if (!_tf.is_editable || !_tf.uses_font_panel)
|
2000-12-20 22:56:40 +00:00
|
|
|
|
{
|
|
|
|
|
return NSMakeRange (NSNotFound, 0);
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
if (_tf.is_rich_text)
|
2000-12-20 22:56:40 +00:00
|
|
|
|
{
|
|
|
|
|
// This expects the selection to be already corrected to characters
|
|
|
|
|
return _selected_range;
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
else
|
2000-12-20 22:56:40 +00:00
|
|
|
|
{
|
|
|
|
|
return NSMakeRange (0, [_textStorage length]);
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSRange) rangeForUserParagraphAttributeChange
|
|
|
|
|
{
|
|
|
|
|
if (!_tf.is_editable || !_tf.uses_ruler)
|
2000-12-20 22:56:40 +00:00
|
|
|
|
{
|
|
|
|
|
return NSMakeRange (NSNotFound, 0);
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
if (_tf.is_rich_text)
|
2000-12-20 22:56:40 +00:00
|
|
|
|
{
|
|
|
|
|
return [self selectionRangeForProposedRange: _selected_range
|
|
|
|
|
granularity: NSSelectByParagraph];
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
else
|
2000-12-20 22:56:40 +00:00
|
|
|
|
{
|
|
|
|
|
return NSMakeRange (0, [_textStorage length]);
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSRange) rangeForUserTextChange
|
|
|
|
|
{
|
|
|
|
|
if (!_tf.is_editable)
|
2000-12-20 22:56:40 +00:00
|
|
|
|
{
|
|
|
|
|
return NSMakeRange (NSNotFound, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// This expects the selection to be already corrected to characters
|
|
|
|
|
return _selected_range;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSString*) preferredPasteboardTypeFromArray: (NSArray*)availableTypes
|
|
|
|
|
restrictedToTypesFromArray: (NSArray*)allowedTypes
|
|
|
|
|
{
|
|
|
|
|
NSEnumerator *enumerator;
|
|
|
|
|
NSString *type;
|
|
|
|
|
|
|
|
|
|
if (availableTypes == nil)
|
|
|
|
|
return nil;
|
|
|
|
|
|
|
|
|
|
if (allowedTypes == nil)
|
|
|
|
|
return [availableTypes objectAtIndex: 0];
|
|
|
|
|
|
|
|
|
|
enumerator = [allowedTypes objectEnumerator];
|
|
|
|
|
while ((type = [enumerator nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
if ([availableTypes containsObject: type])
|
|
|
|
|
{
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) readSelectionFromPasteboard: (NSPasteboard*)pboard
|
|
|
|
|
{
|
|
|
|
|
/*
|
2000-12-20 22:56:40 +00:00
|
|
|
|
Reads the text view's preferred type of data from the pasteboard
|
|
|
|
|
specified by the pboard parameter. This method invokes the
|
|
|
|
|
preferredPasteboardTypeFromArray: restrictedToTypesFromArray: method
|
|
|
|
|
to determine the text view's preferred type of data and then reads
|
|
|
|
|
the data using the readSelectionFromPasteboard: type:
|
|
|
|
|
method. Returns YES if the data was successfully read. */
|
|
|
|
|
NSString *type;
|
|
|
|
|
|
|
|
|
|
type = [self preferredPasteboardTypeFromArray: [pboard types]
|
|
|
|
|
restrictedToTypesFromArray: [self readablePasteboardTypes]];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
if (type == nil)
|
|
|
|
|
return NO;
|
|
|
|
|
|
|
|
|
|
return [self readSelectionFromPasteboard: pboard type: type];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) readSelectionFromPasteboard: (NSPasteboard*)pboard
|
|
|
|
|
type: (NSString*)type
|
|
|
|
|
{
|
|
|
|
|
/*
|
2000-12-20 22:56:40 +00:00
|
|
|
|
Reads data of the given type from pboard. The new data is placed at
|
|
|
|
|
the current insertion point, replacing the current selection if one
|
|
|
|
|
exists. Returns YES if the data was successfully read.
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
You should override this method to read pasteboard types other than
|
|
|
|
|
the default types. Use the rangeForUserTextChange method to obtain
|
|
|
|
|
the range of characters (if any) to be replaced by the new data. */
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
if ([type isEqualToString: NSStringPboardType])
|
|
|
|
|
{
|
|
|
|
|
[self insertText: [pboard stringForType: NSStringPboardType]];
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
if (_tf.is_rich_text)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
{
|
|
|
|
|
if ([type isEqualToString: NSRTFPboardType])
|
|
|
|
|
{
|
|
|
|
|
[self replaceCharactersInRange: [self rangeForUserTextChange]
|
|
|
|
|
withRTF: [pboard dataForType: NSRTFPboardType]];
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_tf.imports_graphics)
|
|
|
|
|
{
|
|
|
|
|
if ([type isEqualToString: NSRTFDPboardType])
|
|
|
|
|
{
|
|
|
|
|
[self replaceCharactersInRange: [self rangeForUserTextChange]
|
|
|
|
|
withRTFD: [pboard dataForType: NSRTFDPboardType]];
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
// FIXME: Should also support: NSTIFFPboardType
|
|
|
|
|
if ([type isEqualToString: NSFileContentsPboardType])
|
|
|
|
|
{
|
|
|
|
|
NSTextAttachment *attachment = [[NSTextAttachment alloc]
|
|
|
|
|
initWithFileWrapper:
|
|
|
|
|
[pboard readFileWrapper]];
|
|
|
|
|
|
|
|
|
|
[self replaceRange: [self rangeForUserTextChange]
|
|
|
|
|
withAttributedString:
|
|
|
|
|
[NSAttributedString attributedStringWithAttachment: attachment]];
|
|
|
|
|
RELEASE(attachment);
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// color accepting
|
|
|
|
|
if ([type isEqualToString: NSColorPboardType])
|
|
|
|
|
{
|
|
|
|
|
NSColor *color = [NSColor colorFromPasteboard: pboard];
|
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
2001-01-03 10:39:02 +00:00
|
|
|
|
NSMutableDictionary *d = [[self typingAttributes] mutableCopy];
|
|
|
|
|
|
|
|
|
|
[d setObject: color forKey: NSForegroundColorAttributeName];
|
|
|
|
|
[self setTypingAttributes: d];
|
|
|
|
|
RELEASE(d);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
if (aRange.location != NSNotFound)
|
2001-01-03 10:39:02 +00:00
|
|
|
|
{
|
|
|
|
|
[self setTextColor: color range: aRange];
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// font pasting
|
|
|
|
|
if ([type isEqualToString: NSFontPboardType])
|
|
|
|
|
{
|
|
|
|
|
NSData *data = [pboard dataForType: NSFontPboardType];
|
|
|
|
|
NSDictionary *dict = [NSUnarchiver unarchiveObjectWithData: data];
|
|
|
|
|
|
|
|
|
|
if (dict != nil)
|
|
|
|
|
{
|
2001-01-03 10:39:02 +00:00
|
|
|
|
NSRange aRange = [self rangeForUserCharacterAttributeChange];
|
|
|
|
|
NSMutableDictionary *d;
|
|
|
|
|
|
|
|
|
|
if (aRange.location != NSNotFound)
|
|
|
|
|
{
|
|
|
|
|
[self setAttributes: dict range: aRange];
|
|
|
|
|
}
|
|
|
|
|
d = [[self typingAttributes] mutableCopy];
|
|
|
|
|
[d addEntriesFromDictionary: dict];
|
|
|
|
|
[self setTypingAttributes: d];
|
|
|
|
|
RELEASE(d);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ruler pasting
|
|
|
|
|
if ([type isEqualToString: NSRulerPboardType])
|
|
|
|
|
{
|
|
|
|
|
NSData *data = [pboard dataForType: NSRulerPboardType];
|
|
|
|
|
NSDictionary *dict = [NSUnarchiver unarchiveObjectWithData: data];
|
|
|
|
|
|
|
|
|
|
if (dict != nil)
|
|
|
|
|
{
|
2001-01-03 10:39:02 +00:00
|
|
|
|
NSRange aRange = [self rangeForUserParagraphAttributeChange];
|
|
|
|
|
NSMutableDictionary *d;
|
|
|
|
|
|
|
|
|
|
if (aRange.location != NSNotFound)
|
|
|
|
|
{
|
|
|
|
|
[self setAttributes: dict range: aRange];
|
|
|
|
|
}
|
|
|
|
|
d = [[self typingAttributes] mutableCopy];
|
|
|
|
|
[d addEntriesFromDictionary: dict];
|
|
|
|
|
[self setTypingAttributes: d];
|
|
|
|
|
RELEASE(d);
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSArray*) readablePasteboardTypes
|
|
|
|
|
{
|
|
|
|
|
// get default types, what are they?
|
|
|
|
|
NSMutableArray *ret = [NSMutableArray arrayWithObjects: NSRulerPboardType,
|
|
|
|
|
NSColorPboardType, NSFontPboardType, nil];
|
|
|
|
|
|
|
|
|
|
if (_tf.imports_graphics)
|
|
|
|
|
{
|
|
|
|
|
[ret addObject: NSRTFDPboardType];
|
|
|
|
|
//[ret addObject: NSTIFFPboardType];
|
|
|
|
|
[ret addObject: NSFileContentsPboardType];
|
|
|
|
|
}
|
|
|
|
|
if (_tf.is_rich_text)
|
|
|
|
|
[ret addObject: NSRTFPboardType];
|
|
|
|
|
|
|
|
|
|
[ret addObject: NSStringPboardType];
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (NSArray*) writablePasteboardTypes
|
|
|
|
|
{
|
|
|
|
|
// the selected text can be written to the pasteboard with which types.
|
|
|
|
|
return [self readablePasteboardTypes];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) writeSelectionToPasteboard: (NSPasteboard*)pboard
|
|
|
|
|
type: (NSString*)type
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
Writes the current selection to pboard using the given type. Returns YES
|
|
|
|
|
if the data was successfully written. You can override this method to add
|
|
|
|
|
support for writing new types of data to the pasteboard. You should invoke
|
|
|
|
|
super's implementation of the method to handle any types of data your
|
|
|
|
|
overridden version does not.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
return [self writeSelectionToPasteboard: pboard
|
|
|
|
|
types: [NSArray arrayWithObject: type]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (BOOL) writeSelectionToPasteboard: (NSPasteboard*)pboard
|
|
|
|
|
types: (NSArray*)types
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* Writes the current selection to pboard under each type in the types
|
|
|
|
|
array. Returns YES if the data for any single type was written
|
|
|
|
|
successfully.
|
|
|
|
|
|
|
|
|
|
You should not need to override this method. You might need to invoke this
|
|
|
|
|
method if you are implementing a new type of pasteboard to handle services
|
|
|
|
|
other than copy/paste or dragging. */
|
|
|
|
|
BOOL ret = NO;
|
|
|
|
|
NSEnumerator *enumerator;
|
|
|
|
|
NSString *type;
|
|
|
|
|
|
|
|
|
|
if (types == nil)
|
|
|
|
|
return NO;
|
2001-01-03 10:39:02 +00:00
|
|
|
|
if (_selected_range.location == NSNotFound)
|
|
|
|
|
return NO;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
|
|
|
|
[pboard declareTypes: types owner: self];
|
|
|
|
|
|
|
|
|
|
enumerator = [types objectEnumerator];
|
|
|
|
|
while ((type = [enumerator nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
if ([type isEqualToString: NSStringPboardType])
|
|
|
|
|
{
|
|
|
|
|
ret = ret || [pboard setString: [[self string] substringWithRange: _selected_range]
|
|
|
|
|
forType: NSStringPboardType];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([type isEqualToString: NSRTFPboardType])
|
|
|
|
|
{
|
|
|
|
|
ret = ret || [pboard setData: [self RTFFromRange: _selected_range]
|
|
|
|
|
forType: NSRTFPboardType];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([type isEqualToString: NSRTFDPboardType])
|
|
|
|
|
{
|
|
|
|
|
ret = ret || [pboard setData: [self RTFDFromRange: _selected_range]
|
|
|
|
|
forType: NSRTFDPboardType];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ([type isEqualToString: NSColorPboardType])
|
|
|
|
|
{
|
2001-01-03 10:39:02 +00:00
|
|
|
|
NSColor *color;
|
2000-05-23 22:59:36 +00:00
|
|
|
|
|
2001-01-03 10:39:02 +00:00
|
|
|
|
color = [_textStorage attribute: NSForegroundColorAttributeName
|
|
|
|
|
atIndex: _selected_range.location
|
|
|
|
|
effectiveRange: 0];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (color != nil)
|
|
|
|
|
{
|
|
|
|
|
[color writeToPasteboard: pboard];
|
|
|
|
|
ret = YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
2000-05-23 22:59:36 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if ([type isEqualToString: NSFontPboardType])
|
|
|
|
|
{
|
2001-01-03 10:39:02 +00:00
|
|
|
|
NSDictionary *dict;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
dict = [_textStorage fontAttributesInRange: _selected_range];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (dict != nil)
|
|
|
|
|
{
|
|
|
|
|
[pboard setData: [NSArchiver archivedDataWithRootObject: dict]
|
|
|
|
|
forType: NSFontPboardType];
|
|
|
|
|
ret = YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
2000-05-23 22:59:36 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if ([type isEqualToString: NSRulerPboardType])
|
|
|
|
|
{
|
2001-01-03 10:39:02 +00:00
|
|
|
|
NSDictionary *dict;
|
2000-12-20 17:00:32 +00:00
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
dict = [_textStorage rulerAttributesInRange: _selected_range];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (dict != nil)
|
|
|
|
|
{
|
|
|
|
|
[pboard setData: [NSArchiver archivedDataWithRootObject: dict]
|
|
|
|
|
forType: NSRulerPboardType];
|
|
|
|
|
ret = YES;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2000-05-23 22:59:36 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return ret;
|
2000-05-23 22:59:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
@end
|
2000-05-23 22:59:36 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
@implementation NSTextView (GNUstepExtensions)
|
|
|
|
|
|
|
|
|
|
- (void) replaceRange: (NSRange)aRange
|
|
|
|
|
withAttributedString: (NSAttributedString*)attrString
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: [attrString string]])
|
|
|
|
|
return;
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
if (_tf.is_rich_text)
|
|
|
|
|
[_textStorage replaceCharactersInRange: aRange
|
|
|
|
|
withAttributedString: attrString];
|
|
|
|
|
else
|
|
|
|
|
[_textStorage replaceCharactersInRange: aRange
|
|
|
|
|
withString: [attrString string]];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (unsigned) textLength
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return [_textStorage length];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
}
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
@end
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
@implementation NSTextView (GNUstepPrivate)
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
+ (NSDictionary*) defaultTypingAttributes
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
|
[NSParagraphStyle defaultParagraphStyle], NSParagraphStyleAttributeName,
|
|
|
|
|
[NSFont userFontOfSize: 0], NSFontAttributeName,
|
|
|
|
|
[NSColor textColor], NSForegroundColorAttributeName,
|
|
|
|
|
nil];
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-01-07 01:05:44 +00:00
|
|
|
|
- (void) setAttributes: (NSDictionary*)attributes range: (NSRange)aRange
|
2000-05-23 22:59:36 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSString *type;
|
|
|
|
|
id val;
|
|
|
|
|
NSEnumerator *enumerator = [attributes keyEnumerator];
|
2000-05-23 22:59:36 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
|
|
|
|
if (![self shouldChangeTextInRange: aRange
|
|
|
|
|
replacementString: nil])
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
while ((type = [enumerator nextObject]) != nil)
|
|
|
|
|
{
|
|
|
|
|
val = [attributes objectForKey: type];
|
2000-12-20 22:56:40 +00:00
|
|
|
|
[_textStorage addAttribute: type value: val range: aRange];
|
2000-12-20 17:00:32 +00:00
|
|
|
|
}
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
2000-05-23 22:59:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) _illegalMovement: (int)textMovement
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// This is similar to [self resignFirstResponder],
|
|
|
|
|
// with the difference that in the notification we need
|
|
|
|
|
// to put the NSTextMovement, which resignFirstResponder
|
|
|
|
|
// does not. Also, if we are ending editing, we are going
|
|
|
|
|
// to be removed, so it's useless to update any drawing.
|
|
|
|
|
NSNumber *number;
|
|
|
|
|
NSDictionary *uiDictionary;
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
if ((_tf.is_editable)
|
2000-12-20 17:00:32 +00:00
|
|
|
|
&& ([_delegate respondsToSelector:
|
|
|
|
|
@selector(textShouldEndEditing:)])
|
|
|
|
|
&& ([_delegate textShouldEndEditing: self] == NO))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Add any clean-up stuff here
|
|
|
|
|
|
|
|
|
|
number = [NSNumber numberWithInt: textMovement];
|
|
|
|
|
uiDictionary = [NSDictionary dictionaryWithObject: number
|
|
|
|
|
forKey: @"NSTextMovement"];
|
|
|
|
|
[nc postNotificationName: NSTextDidEndEditingNotification
|
|
|
|
|
object: self userInfo: uiDictionary];
|
|
|
|
|
return;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// begin: dragging of colors and files---------------
|
|
|
|
|
- (unsigned int) draggingEntered: (id <NSDraggingInfo>)sender
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return NSDragOperationGeneric;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (unsigned int) draggingUpdated: (id <NSDraggingInfo>)sender
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return NSDragOperationGeneric;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) draggingExited: (id <NSDraggingInfo>)sender
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (BOOL) prepareForDragOperation: (id <NSDraggingInfo>)sender
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return YES;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (BOOL) performDragOperation: (id <NSDraggingInfo>)sender
|
2000-05-13 14:28:02 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return [self readSelectionFromPasteboard: [sender draggingPasteboard]];
|
2000-05-13 14:28:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (void) concludeDragOperation: (id <NSDraggingInfo>)sender
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// end: drag accepting---------------------------------
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// central text deletion/backspace method
|
|
|
|
|
// (takes care of optimized redraw/ cursor positioning)
|
|
|
|
|
- (void) deleteRange: (NSRange) aRange backspace: (BOOL) flag
|
1999-07-24 22:24:14 +00:00
|
|
|
|
{
|
2000-12-20 17:00:32 +00:00
|
|
|
|
NSRange deleteRange;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (aRange.location == NSNotFound)
|
|
|
|
|
return;
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (!aRange.length && !(flag && aRange.location))
|
|
|
|
|
return;
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
if (aRange.length)
|
|
|
|
|
{
|
|
|
|
|
deleteRange = aRange;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
deleteRange = NSMakeRange (MAX (0, aRange.location - 1), 1);
|
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
if (![self shouldChangeTextInRange: deleteRange replacementString: @""])
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return;
|
|
|
|
|
[_textStorage beginEditing];
|
|
|
|
|
[_textStorage deleteCharactersInRange: deleteRange];
|
|
|
|
|
[_textStorage endEditing];
|
|
|
|
|
[self didChangeText];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
// move cursor <!> [self selectionRangeForProposedRange: ]
|
|
|
|
|
[self setSelectedRange: NSMakeRange (deleteRange.location, 0)];
|
|
|
|
|
|
|
|
|
|
// remember x for row - stable cursor movements
|
|
|
|
|
_currentCursor = [self rectForCharacterIndex:
|
|
|
|
|
_selected_range.location].origin;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-01-07 01:05:44 +00:00
|
|
|
|
- (unsigned) characterIndexForPoint: (NSPoint)point
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-27 07:39:25 +00:00
|
|
|
|
unsigned index;
|
|
|
|
|
float fraction;
|
2000-12-20 22:56:40 +00:00
|
|
|
|
|
2000-12-27 07:39:25 +00:00
|
|
|
|
index = [_layoutManager glyphIndexForPoint: point
|
|
|
|
|
inTextContainer: _textContainer
|
|
|
|
|
fractionOfDistanceThroughGlyph: &fraction];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-27 07:39:25 +00:00
|
|
|
|
index = [_layoutManager characterIndexForGlyphAtIndex: index];
|
|
|
|
|
if (fraction > 0.5)
|
|
|
|
|
{
|
|
|
|
|
index++;
|
|
|
|
|
}
|
|
|
|
|
return index;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
- (NSRect) rectForCharacterIndex: (unsigned)index
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 22:56:40 +00:00
|
|
|
|
NSRange charRange;
|
|
|
|
|
NSRange glyphRange;
|
|
|
|
|
unsigned glyphIndex;
|
|
|
|
|
NSRect rect;
|
|
|
|
|
NSPoint loc;
|
|
|
|
|
|
|
|
|
|
charRange = NSMakeRange (index, 1);
|
|
|
|
|
glyphRange = [_layoutManager glyphRangeForCharacterRange: charRange
|
|
|
|
|
actualCharacterRange: NULL];
|
|
|
|
|
glyphIndex = glyphRange.location;
|
|
|
|
|
|
|
|
|
|
rect = [_layoutManager lineFragmentRectForGlyphAtIndex: glyphIndex
|
|
|
|
|
effectiveRange: NULL];
|
|
|
|
|
loc = [_layoutManager locationForGlyphAtIndex: glyphIndex];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
rect.origin.x += loc.x;
|
|
|
|
|
rect.size.width -= loc.x;
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return rect;
|
1998-09-01 13:23:23 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
- (NSRect) rectForCharacterRange: (NSRange) aRange
|
1998-09-01 13:23:23 +00:00
|
|
|
|
{
|
2000-12-20 22:56:40 +00:00
|
|
|
|
NSRange glyphRange;
|
|
|
|
|
|
|
|
|
|
glyphRange = [_layoutManager glyphRangeForCharacterRange: aRange
|
|
|
|
|
actualCharacterRange: NULL];
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
2000-12-20 17:00:32 +00:00
|
|
|
|
return [_layoutManager boundingRectForGlyphRange: glyphRange
|
2000-12-20 22:56:40 +00:00
|
|
|
|
inTextContainer: _textContainer];
|
1998-08-20 09:56:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-12-22 16:57:21 +00:00
|
|
|
|
- (NSRect) rectForInsertionPointAtIndex: (unsigned)index
|
1999-02-17 09:13:43 +00:00
|
|
|
|
{
|
2000-12-22 16:57:21 +00:00
|
|
|
|
NSRect rect = [self rectForCharacterIndex: index];
|
|
|
|
|
rect.size.width = 1;
|
|
|
|
|
|
|
|
|
|
if (rect.size.height == 0)
|
|
|
|
|
rect.size.height = 12;
|
|
|
|
|
|
|
|
|
|
return rect;
|
1999-02-17 09:13:43 +00:00
|
|
|
|
}
|
1999-07-24 22:24:14 +00:00
|
|
|
|
|
1997-12-04 01:58:57 +00:00
|
|
|
|
@end
|
2000-05-13 14:28:02 +00:00
|
|
|
|
|
|
|
|
|
@implementation NSTextView(NSTextInput)
|
|
|
|
|
// This are all the NSTextInput methods that are not implemented on NSTextView
|
|
|
|
|
// or one of its super classes.
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
- (void) setMarkedText:(NSString *)aString selectedRange:(NSRange)selRange
|
2000-05-13 14:28:02 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
- (BOOL) hasMarkedText
|
2000-05-13 14:28:02 +00:00
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
- (void) unmarkText
|
2000-05-13 14:28:02 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
- (NSArray*) validAttributesForMarkedText
|
2000-05-13 14:28:02 +00:00
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
- (long) conversationIdentifier
|
2000-05-13 14:28:02 +00:00
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-20 22:56:40 +00:00
|
|
|
|
- (NSRect) firstRectForCharacterRange: (NSRange)theRange
|
2000-05-13 14:28:02 +00:00
|
|
|
|
{
|
|
|
|
|
return NSZeroRect;
|
|
|
|
|
}
|
|
|
|
|
@end
|
2000-12-18 18:08:21 +00:00
|
|
|
|
|