summaryrefslogtreecommitdiffstats
path: root/WebKit/mac/Misc/WebSearchableTextView.m
diff options
context:
space:
mode:
Diffstat (limited to 'WebKit/mac/Misc/WebSearchableTextView.m')
-rw-r--r--WebKit/mac/Misc/WebSearchableTextView.m254
1 files changed, 254 insertions, 0 deletions
diff --git a/WebKit/mac/Misc/WebSearchableTextView.m b/WebKit/mac/Misc/WebSearchableTextView.m
new file mode 100644
index 0000000..f3106e5
--- /dev/null
+++ b/WebKit/mac/Misc/WebSearchableTextView.m
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebSearchableTextView.h"
+#import "WebDocumentPrivate.h"
+#import "WebTypesInternal.h"
+
+@interface NSString (_Web_StringTextFinding)
+- (NSRange)findString:(NSString *)string selectedRange:(NSRange)selectedRange options:(unsigned)mask wrap:(BOOL)wrapFlag;
+@end
+
+@implementation WebSearchableTextView
+
+- (BOOL)searchFor: (NSString *)string direction: (BOOL)forward caseSensitive: (BOOL)caseFlag wrap: (BOOL)wrapFlag;
+{
+ if (![string length])
+ return NO;
+
+ BOOL lastFindWasSuccessful = NO;
+ NSString *textContents = [self string];
+ unsigned textLength;
+
+ if (textContents && (textLength = [textContents length])) {
+ NSRange range;
+ unsigned options = 0;
+
+ if (!forward)
+ options |= NSBackwardsSearch;
+
+ if (!caseFlag)
+ options |= NSCaseInsensitiveSearch;
+
+ range = [textContents findString:string selectedRange:[self selectedRange] options:options wrap:wrapFlag];
+ if (range.length) {
+ [self setSelectedRange:range];
+ [self scrollRangeToVisible:range];
+ lastFindWasSuccessful = YES;
+ }
+ }
+
+ return lastFindWasSuccessful;
+}
+
+- (void)copy:(id)sender
+{
+ if ([self isRichText]) {
+ [super copy:sender];
+ }else{
+ //Convert CRLF to LF to workaround: 3105538 - Carbon doesn't convert text with CRLF to LF
+ NSMutableString *string = [[[self string] substringWithRange:[self selectedRange]] mutableCopy];
+ [string replaceOccurrencesOfString:@"\r\n" withString:@"\n" options:0 range:NSMakeRange(0, [string length])];
+
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
+ [pasteboard setString:string forType:NSStringPboardType];
+ }
+}
+
+- (NSRect)selectionRect
+{
+ // Note that this method would work for any NSTextView; some day we might want to use it
+ // for an NSTextView that isn't a WebTextView.
+ NSRect result = NSZeroRect;
+
+ // iterate over multiple selected ranges
+ NSEnumerator *rangeEnumerator = [[self selectedRanges] objectEnumerator];
+ NSValue *rangeAsValue;
+ while ((rangeAsValue = [rangeEnumerator nextObject]) != nil) {
+ NSRange range = [rangeAsValue rangeValue];
+ NSUInteger rectCount;
+ NSRectArray rectArray = [[self layoutManager] rectArrayForCharacterRange:range
+ withinSelectedCharacterRange:range
+ inTextContainer:[self textContainer]
+ rectCount:&rectCount];
+ unsigned i;
+ // iterate over multiple rects in each selected range
+ for (i = 0; i < rectCount; ++i) {
+ NSRect rect = rectArray[i];
+ if (NSEqualRects(result, NSZeroRect)) {
+ result = rect;
+ } else {
+ result = NSUnionRect(result, rect);
+ }
+ }
+ }
+
+ return result;
+}
+
+- (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText
+{
+ // This is here to complete the <WebDocumentSelection> protocol, but it was introduced after this
+ // class was deprecated so there's no implementation.
+ return nil;
+}
+
+- (NSRect)selectionImageRect
+{
+ // This is here to complete the <WebDocumentSelection> protocol, but it was introduced after this
+ // class was deprecated so there's no implementation.
+ return NSZeroRect;
+}
+
+- (NSArray *)selectionTextRects
+{
+ // This is here to complete the <WebDocumentSelection> protocol, but it was introduced after this
+ // class was deprecated so there's no implementation.
+ return nil;
+}
+
+- (NSView *)selectionView
+{
+ return self;
+}
+
+- (NSArray *)pasteboardTypesForSelection
+{
+ return [self writablePasteboardTypes];
+}
+
+- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
+{
+ [self writeSelectionToPasteboard:pasteboard types:types];
+}
+
+- (BOOL)supportsTextEncoding
+{
+ return YES;
+}
+
+- (NSString *)string
+{
+ return [super string];
+}
+
+- (NSAttributedString *)attributedString
+{
+ return [self attributedSubstringFromRange:NSMakeRange(0, [[self string] length])];
+}
+
+- (NSString *)selectedString
+{
+ return [[self string] substringWithRange:[self selectedRange]];
+}
+
+- (NSAttributedString *)selectedAttributedString
+{
+ return [self attributedSubstringFromRange:[self selectedRange]];
+}
+
+- (void)selectAll
+{
+ [self setSelectedRange:NSMakeRange(0, [[self string] length])];
+}
+
+- (void)deselectAll
+{
+ [self setSelectedRange:NSMakeRange(0,0)];
+}
+
+@end
+
+@implementation NSString (_Web_StringTextFinding)
+
+- (NSRange)findString:(NSString *)string selectedRange:(NSRange)selectedRange options:(unsigned)options wrap:(BOOL)wrap
+{
+ BOOL forwards = (options & NSBackwardsSearch) == 0;
+ unsigned length = [self length];
+ NSRange searchRange, range;
+
+ // Our search algorithm, used in WebCore also, is to search in the selection first. If the found text is the
+ // entire selection, then we search again from just past the selection.
+
+ if (forwards) {
+ // FIXME: If selectedRange has length of 0, we ignore it, which is appropriate for non-editable text (since
+ // a zero-length selection in non-editable is invisible). We might want to change this someday to only ignore the
+ // selection if its location is NSNotFound when the text is editable (and similarly for the backwards case).
+ searchRange.location = selectedRange.length > 0 ? selectedRange.location : 0;
+ searchRange.length = length - searchRange.location;
+ range = [self rangeOfString:string options:options range:searchRange];
+
+ // If found range matches (non-empty) selection, search again from just past selection
+ if (range.location != NSNotFound && NSEqualRanges(range, selectedRange)) {
+ searchRange.location = NSMaxRange(selectedRange);
+ searchRange.length = length - searchRange.location;
+ range = [self rangeOfString:string options:options range:searchRange];
+ }
+
+ // If not found, search again from the beginning. Make search range large enough that
+ // we'll find a match even if it partially overlapped the existing selection (including the
+ // case where it exactly matches the existing selection).
+ if ((range.length == 0) && wrap) {
+ searchRange.location = 0;
+ searchRange.length = selectedRange.location + selectedRange.length + [string length];
+ if (searchRange.length > length) {
+ searchRange.length = length;
+ }
+ range = [self rangeOfString:string options:options range:searchRange];
+ }
+ } else {
+ searchRange.location = 0;
+ searchRange.length = selectedRange.length > 0 ? NSMaxRange(selectedRange) : length;
+ range = [self rangeOfString:string options:options range:searchRange];
+
+ // If found range matches (non-empty) selection, search again from just before selection
+ if (range.location != NSNotFound && NSEqualRanges(range, selectedRange)) {
+ searchRange.location = 0;
+ searchRange.length = selectedRange.location;
+ range = [self rangeOfString:string options:options range:searchRange];
+ }
+
+ // If not found, search again from the end. Make search range large enough that
+ // we'll find a match even if it partially overlapped the existing selection (including the
+ // case where it exactly matches the existing selection).
+ if ((range.length == 0) && wrap) {
+ unsigned stringLength = [string length];
+ if (selectedRange.location > stringLength) {
+ searchRange.location = selectedRange.location - stringLength;
+ } else {
+ searchRange.location = 0;
+ }
+ searchRange.length = length - searchRange.location;
+ range = [self rangeOfString:string options:options range:searchRange];
+ }
+}
+return range;
+}
+
+@end