2
* Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14
* its contributors may be used to endorse or promote products derived
15
* from this software without specific prior written permission.
17
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
#import "WebSearchableTextView.h"
30
#import "WebDocumentPrivate.h"
31
#import "WebTypesInternal.h"
33
@interface NSString (_Web_StringTextFinding)
34
- (NSRange)findString:(NSString *)string selectedRange:(NSRange)selectedRange options:(unsigned)mask wrap:(BOOL)wrapFlag;
37
@implementation WebSearchableTextView
39
- (BOOL)searchFor: (NSString *)string direction: (BOOL)forward caseSensitive: (BOOL)caseFlag wrap: (BOOL)wrapFlag;
44
BOOL lastFindWasSuccessful = NO;
45
NSString *textContents = [self string];
48
if (textContents && (textLength = [textContents length])) {
53
options |= NSBackwardsSearch;
56
options |= NSCaseInsensitiveSearch;
58
range = [textContents findString:string selectedRange:[self selectedRange] options:options wrap:wrapFlag];
60
[self setSelectedRange:range];
61
[self scrollRangeToVisible:range];
62
lastFindWasSuccessful = YES;
66
return lastFindWasSuccessful;
69
- (void)copy:(id)sender
71
if ([self isRichText]) {
74
//Convert CRLF to LF to workaround: 3105538 - Carbon doesn't convert text with CRLF to LF
75
NSMutableString *string = [[[self string] substringWithRange:[self selectedRange]] mutableCopy];
76
[string replaceOccurrencesOfString:@"\r\n" withString:@"\n" options:0 range:NSMakeRange(0, [string length])];
78
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
79
[pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
80
[pasteboard setString:string forType:NSStringPboardType];
84
- (NSRect)selectionRect
86
// Note that this method would work for any NSTextView; some day we might want to use it
87
// for an NSTextView that isn't a WebTextView.
88
NSRect result = NSZeroRect;
90
// iterate over multiple selected ranges
91
NSEnumerator *rangeEnumerator = [[self selectedRanges] objectEnumerator];
92
NSValue *rangeAsValue;
93
while ((rangeAsValue = [rangeEnumerator nextObject]) != nil) {
94
NSRange range = [rangeAsValue rangeValue];
96
NSRectArray rectArray = [[self layoutManager] rectArrayForCharacterRange:range
97
withinSelectedCharacterRange:range
98
inTextContainer:[self textContainer]
99
rectCount:&rectCount];
101
// iterate over multiple rects in each selected range
102
for (i = 0; i < rectCount; ++i) {
103
NSRect rect = rectArray[i];
104
if (NSEqualRects(result, NSZeroRect)) {
107
result = NSUnionRect(result, rect);
115
- (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText
117
// This is here to complete the <WebDocumentSelection> protocol, but it was introduced after this
118
// class was deprecated so there's no implementation.
122
- (NSImage *)selectionImageForcingWhiteText:(BOOL)forceWhiteText
124
// This is here to complete the <WebDocumentSelection> protocol, but it was introduced after this
125
// class was deprecated so there's no implementation.
129
- (NSRect)selectionImageRect
131
// This is here to complete the <WebDocumentSelection> protocol, but it was introduced after this
132
// class was deprecated so there's no implementation.
136
- (NSArray *)selectionTextRects
138
// This is here to complete the <WebDocumentSelection> protocol, but it was introduced after this
139
// class was deprecated so there's no implementation.
143
- (NSView *)selectionView
148
- (NSArray *)pasteboardTypesForSelection
150
return [self writablePasteboardTypes];
153
- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
155
[self writeSelectionToPasteboard:pasteboard types:types];
158
- (BOOL)supportsTextEncoding
165
return [super string];
168
- (NSAttributedString *)attributedString
170
return [self attributedSubstringFromRange:NSMakeRange(0, [[self string] length])];
173
- (NSString *)selectedString
175
return [[self string] substringWithRange:[self selectedRange]];
178
- (NSAttributedString *)selectedAttributedString
180
return [self attributedSubstringFromRange:[self selectedRange]];
185
[self setSelectedRange:NSMakeRange(0, [[self string] length])];
190
[self setSelectedRange:NSMakeRange(0,0)];
195
@implementation NSString (_Web_StringTextFinding)
197
- (NSRange)findString:(NSString *)string selectedRange:(NSRange)selectedRange options:(unsigned)options wrap:(BOOL)wrap
199
BOOL forwards = (options & NSBackwardsSearch) == 0;
200
unsigned length = [self length];
201
NSRange searchRange, range;
203
// Our search algorithm, used in WebCore also, is to search in the selection first. If the found text is the
204
// entire selection, then we search again from just past the selection.
207
// FIXME: If selectedRange has length of 0, we ignore it, which is appropriate for non-editable text (since
208
// a zero-length selection in non-editable is invisible). We might want to change this someday to only ignore the
209
// selection if its location is NSNotFound when the text is editable (and similarly for the backwards case).
210
searchRange.location = selectedRange.length > 0 ? selectedRange.location : 0;
211
searchRange.length = length - searchRange.location;
212
range = [self rangeOfString:string options:options range:searchRange];
214
// If found range matches (non-empty) selection, search again from just past selection
215
if (range.location != NSNotFound && NSEqualRanges(range, selectedRange)) {
216
searchRange.location = NSMaxRange(selectedRange);
217
searchRange.length = length - searchRange.location;
218
range = [self rangeOfString:string options:options range:searchRange];
221
// If not found, search again from the beginning. Make search range large enough that
222
// we'll find a match even if it partially overlapped the existing selection (including the
223
// case where it exactly matches the existing selection).
224
if ((range.length == 0) && wrap) {
225
searchRange.location = 0;
226
searchRange.length = selectedRange.location + selectedRange.length + [string length];
227
if (searchRange.length > length) {
228
searchRange.length = length;
230
range = [self rangeOfString:string options:options range:searchRange];
233
searchRange.location = 0;
234
searchRange.length = selectedRange.length > 0 ? NSMaxRange(selectedRange) : length;
235
range = [self rangeOfString:string options:options range:searchRange];
237
// If found range matches (non-empty) selection, search again from just before selection
238
if (range.location != NSNotFound && NSEqualRanges(range, selectedRange)) {
239
searchRange.location = 0;
240
searchRange.length = selectedRange.location;
241
range = [self rangeOfString:string options:options range:searchRange];
244
// If not found, search again from the end. Make search range large enough that
245
// we'll find a match even if it partially overlapped the existing selection (including the
246
// case where it exactly matches the existing selection).
247
if ((range.length == 0) && wrap) {
248
unsigned stringLength = [string length];
249
if (selectedRange.location > stringLength) {
250
searchRange.location = selectedRange.location - stringLength;
252
searchRange.location = 0;
254
searchRange.length = length - searchRange.location;
255
range = [self rangeOfString:string options:options range:searchRange];