Revision: 4816
http://skim-app.svn.sourceforge.net/skim-app/?rev=4816&view=rev
Author: hofman
Date: 2008-10-18 17:13:51 +0000 (Sat, 18 Oct 2008)
Log Message:
-----------
get character ranges for selection recursively, to add support for recursive
ranges like "word 3 of paragraph 2".
Modified Paths:
--------------
trunk/PDFSelection_SKExtensions.m
Modified: trunk/PDFSelection_SKExtensions.m
===================================================================
--- trunk/PDFSelection_SKExtensions.m 2008-10-18 12:05:30 UTC (rev 4815)
+++ trunk/PDFSelection_SKExtensions.m 2008-10-18 17:13:51 UTC (rev 4816)
@@ -151,6 +151,129 @@
return range;
}
+static inline NSArray *characterRangesForSpecifier(NSScriptObjectSpecifier
*spec, NSScriptObjectSpecifier **containerSpec) {
+ if ([spec isKindOfClass:[NSScriptObjectSpecifier class]] == NO)
+ return nil;
+
+ // we should get ranges of characters, words, or parapgraphs of the
richText of a page
+ NSString *key = [spec key];
+ if ([key isEqualToString:@"characters"] == NO && [key
isEqualToString:@"words"] == NO && [key isEqualToString:@"paragraphs"] == NO &&
[key isEqualToString:@"attributeRuns"] == NO)
+ return nil;
+
+ // get the richText specifier
+ NSScriptObjectSpecifier *textSpec = [spec containerSpecifier];
+ NSTextStorage *textStorage = [textSpec objectsByEvaluatingSpecifier];
+ if ([textStorage isKindOfClass:[NSArray class]])
+ textStorage = [(NSArray *)textStorage count] ? [(NSArray *)textStorage
objectAtIndex:0] : nil;
+ if ([textStorage isKindOfClass:[NSTextStorage class]] == NO)
+ return nil;
+
+ unsigned int containerOffset = 0;
+ if ([key isEqualToString:@"characters"] || [key isEqualToString:@"words"]
|| [key isEqualToString:@"paragraphs"] || [key
isEqualToString:@"attributeRuns"] == NO) {
+ NSArray *textRanges = characterRangesForSpecifier(textSpec,
containerSpec);
+ if ([textRanges count] == 0)
+ return nil;
+ containerOffset = [[textRanges objectAtIndex:0] rangeValue].location;
+ } else {
+ *containerSpec = textSpec;
+ }
+
+ // now get the ranges, which can be any kind of specifier
+ int startIndex, endIndex, i, count, *indices;
+ NSRange *ranges = NULL;
+ int numRanges = 0;
+
+ if ([spec isKindOfClass:[NSPropertySpecifier class]]) {
+ // this should be the full range of characters, words, or paragraphs
+ numRanges = 1;
+ ranges = NSZoneRealloc(NSDefaultMallocZone(), ranges, sizeof(NSRange));
+ ranges[0] = NSMakeRange(0, [[textStorage valueForKey:key] count]);
+ } else if ([spec isKindOfClass:[NSRangeSpecifier class]]) {
+ // somehow getting the indices as for the general case sometimes leads
to an exception for NSRangeSpecifier, so we get the indices of the
start/endSpecifiers
+ NSScriptObjectSpecifier *startSpec = [(NSRangeSpecifier *)spec
startSpecifier];
+ NSScriptObjectSpecifier *endSpec = [(NSRangeSpecifier *)spec
endSpecifier];
+
+ if (startSpec == nil && endSpec == nil)
+ return nil;
+
+ if (startSpec) {
+ indices = [startSpec
indicesOfObjectsByEvaluatingWithContainer:textStorage count:&count];
+ if (count <= 0)
+ return nil;
+ startIndex = indices[0];
+ } else {
+ startIndex = 0;
+ }
+
+ if (endSpec) {
+ indices = [endSpec
indicesOfObjectsByEvaluatingWithContainer:textStorage count:&count];
+ if (count <= 0)
+ return nil;
+ endIndex = indices[count - 1];
+ } else {
+ endIndex = [[textStorage valueForKey:key] count];
+ }
+
+ numRanges = 1;
+ ranges = NSZoneRealloc(NSDefaultMallocZone(), ranges, sizeof(NSRange));
+ ranges[0] = NSMakeRange(MIN(startIndex, endIndex), MAX(startIndex,
endIndex) + 1 - MIN(startIndex, endIndex));
+ } else {
+ // this handles other objectSpecifiers (index, middel, random,
relative, whose). It can contain several ranges, e.g. for aan NSWhoseSpecifier
+ indices = [spec indicesOfObjectsByEvaluatingWithContainer:textStorage
count:&count];
+ if (count <= 0)
+ return nil;
+
+ for (i = 0; i < count; i++) {
+ unsigned int idx = indices[i];
+ if (numRanges == 0 || idx > NSMaxRange(ranges[numRanges - 1])) {
+ numRanges++;
+ ranges = NSZoneRealloc(NSDefaultMallocZone(), ranges,
numRanges * sizeof(NSRange));
+ ranges[numRanges - 1] = NSMakeRange(idx, 1);
+ } else {
+ ++(ranges[numRanges - 1].length);
+ }
+ }
+ }
+
+ if ([key isEqualToString:@"characters"] == NO) {
+ // translate from subtext ranges to character ranges
+ for (i = 0; i < numRanges; i++) {
+ startIndex = ranges[i].location;
+ endIndex = NSMaxRange(ranges[i]) - 1;
+ NSString *string = [textStorage string];
+ NSArray *substrings = [[textStorage valueForKey:key]
valueForKey:@"string"];
+ NSRange range = rangeOfSubstringOfStringAtIndex(string,
substrings, startIndex);
+ if (range.location == NSNotFound) {
+ ranges[i] = NSMakeRange(NSNotFound, 0);
+ continue;
+ }
+ startIndex = range.location;
+ if (ranges[i].length > 1) {
+ range = rangeOfSubstringOfStringAtIndex(string, substrings,
endIndex);
+ if (range.location == NSNotFound) {
+ ranges[i] = NSMakeRange(NSNotFound, 0);
+ continue;
+ }
+ }
+ endIndex = NSMaxRange(range) - 1;
+ ranges[i] = NSMakeRange(startIndex, endIndex + 1 - startIndex);
+ }
+ }
+
+ NSMutableArray *rangeValues = [NSMutableArray array];
+
+ for (i = 0; i < numRanges; i++) {
+ NSRange range = ranges[i];
+ if (range.length) {
+ range.location += containerOffset;
+ [rangeValues addObject:[NSValue valueWithRange:range]];
+ }
+ }
+ if (ranges) NSZoneFree(NSDefaultMallocZone(), ranges);
+
+ return rangeValues;
+}
+
+ (id)selectionWithSpecifier:(id)specifier {
return [self selectionWithSpecifier:specifier onPage:nil];
}
@@ -180,112 +303,23 @@
if ([spec isKindOfClass:[NSScriptObjectSpecifier class]] == NO)
continue;
- // we should get ranges of characters, words, or parapgraphs of the
richText of a page
- NSString *key = [spec key];
- if ([key isEqualToString:@"characters"] == NO && [key
isEqualToString:@"words"] == NO && [key isEqualToString:@"paragraphs"] == NO &&
[key isEqualToString:@"attributeRuns"] == NO)
- continue;
+ NSScriptObjectSpecifier *containerSpec = nil;
+ NSArray *ranges = characterRangesForSpecifier(spec, &containerSpec);
+ unsigned int i, numRanges = [ranges count];
- // get the richText specifier
- NSScriptObjectSpecifier *textSpec = [spec containerSpecifier];
- if ([[textSpec key] isEqualToString:@"richText"] == NO)
+ if ([containerSpec isKindOfClass:[NSScriptObjectSpecifier class]] ==
NO || [[containerSpec key] isEqualToString:@"richText"] == NO)
continue;
- // get the page
- NSScriptObjectSpecifier *pageSpec = [textSpec containerSpecifier];
+ NSScriptObjectSpecifier *pageSpec = [containerSpec containerSpecifier];
PDFPage *page = [pageSpec objectsByEvaluatingSpecifier];
if ([page isKindOfClass:[NSArray class]])
page = [(NSArray *)page count] ? [(NSArray *)page objectAtIndex:0]
: nil;
if ([page isKindOfClass:[PDFPage class]] == NO || (aPage && [aPage
isEqual:page] == NO))
continue;
- // we could also evaluate textSpec, but we already have the page
- NSTextStorage *textStorage = [page richText];
-
- // now get the ranges, which can be any kind of specifier
- int startIndex, endIndex, i, count, *indices;
- NSRange *ranges = NULL;
- int numRanges = 0;
-
- if ([spec isKindOfClass:[NSPropertySpecifier class]]) {
- // this should be the full range of characters, words, or
paragraphs
- numRanges = 1;
- ranges = NSZoneRealloc([self zone], ranges, sizeof(NSRange));
- ranges[0] = NSMakeRange(0, [[textStorage valueForKey:key] count]);
- } else if ([spec isKindOfClass:[NSRangeSpecifier class]]) {
- // somehow getting the indices as for the general case sometimes
leads to an exception for NSRangeSpecifier, so we get the indices of the
start/endSpecifiers
- NSScriptObjectSpecifier *startSpec = [(NSRangeSpecifier *)spec
startSpecifier];
- NSScriptObjectSpecifier *endSpec = [(NSRangeSpecifier *)spec
endSpecifier];
-
- if (startSpec == nil && endSpec == nil)
- continue;
-
- if (startSpec) {
- indices = [startSpec
indicesOfObjectsByEvaluatingWithContainer:textStorage count:&count];
- if (count <= 0)
- continue;
- startIndex = indices[0];
- } else {
- startIndex = 0;
- }
-
- if (endSpec) {
- indices = [endSpec
indicesOfObjectsByEvaluatingWithContainer:textStorage count:&count];
- if (count <= 0)
- continue;
- endIndex = indices[count - 1];
- } else {
- endIndex = [[textStorage valueForKey:key] count];
- }
-
- numRanges = 1;
- ranges = NSZoneRealloc([self zone], ranges, sizeof(NSRange));
- ranges[0] = NSMakeRange(MIN(startIndex, endIndex), MAX(startIndex,
endIndex) + 1 - MIN(startIndex, endIndex));
- } else {
- // this handles other objectSpecifiers (index, middel, random,
relative, whose). It can contain several ranges, e.g. for aan NSWhoseSpecifier
- indices = [spec
indicesOfObjectsByEvaluatingWithContainer:textStorage count:&count];
- if (count <= 0)
- continue;
-
- for (i = 0; i < count; i++) {
- unsigned int idx = indices[i];
- if (numRanges == 0 || idx > NSMaxRange(ranges[numRanges - 1]))
{
- numRanges++;
- ranges = NSZoneRealloc([self zone], ranges, numRanges *
sizeof(NSRange));
- ranges[numRanges - 1] = NSMakeRange(idx, 1);
- } else {
- ++(ranges[numRanges - 1].length);
- }
- }
- }
-
- if ([key isEqualToString:@"characters"] == NO) {
- // translate from subtext ranges to character ranges
- for (i = 0; i < numRanges; i++) {
- startIndex = ranges[i].location;
- endIndex = NSMaxRange(ranges[i]) - 1;
- NSString *string = [textStorage string];
- NSArray *substrings = [[textStorage valueForKey:key]
valueForKey:@"string"];
- NSRange range = rangeOfSubstringOfStringAtIndex(string,
substrings, startIndex);
- if (range.location == NSNotFound) {
- ranges[i] = NSMakeRange(NSNotFound, 0);
- continue;
- }
- startIndex = range.location;
- if (ranges[i].length > 1) {
- range = rangeOfSubstringOfStringAtIndex(string,
substrings, endIndex);
- if (range.location == NSNotFound) {
- ranges[i] = NSMakeRange(NSNotFound, 0);
- continue;
- }
- }
- endIndex = NSMaxRange(range) - 1;
- ranges[i] = NSMakeRange(startIndex, endIndex + 1 - startIndex);
- }
- }
-
for (i = 0; i < numRanges; i++) {
PDFSelection *sel;
- NSRange range = ranges[i];
+ NSRange range = [[ranges objectAtIndex:i] rangeValue];
if (range.length && (sel = [page selectionForRange:range])) {
if (selection == nil)
selection = sel;
@@ -293,7 +327,6 @@
[selection addSelection:sel];
}
}
- if (ranges) NSZoneFree([self zone], ranges);
}
return selection;
}
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Skim-app-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/skim-app-commit