Revision: 2690
          http://skim-app.svn.sourceforge.net/skim-app/?rev=2690&view=rev
Author:   hofman
Date:     2007-08-21 07:20:35 -0700 (Tue, 21 Aug 2007)

Log Message:
-----------
Implement type-select for thumbnail tableview and note outlineviews. 

Modified Paths:
--------------
    trunk/SKMainWindowController.m
    trunk/SKNoteOutlineView.h
    trunk/SKNoteOutlineView.m
    trunk/SKNotesDocument.m
    trunk/SKThumbnailTableView.h
    trunk/SKThumbnailTableView.m
    trunk/Skim.xcodeproj/project.pbxproj

Added Paths:
-----------
    trunk/SKTypeSelectHelper.h
    trunk/SKTypeSelectHelper.m

Modified: trunk/SKMainWindowController.m
===================================================================
--- trunk/SKMainWindowController.m      2007-08-20 18:21:29 UTC (rev 2689)
+++ trunk/SKMainWindowController.m      2007-08-21 14:20:35 UTC (rev 2690)
@@ -77,6 +77,7 @@
 #import "SKColorSwatch.h"
 #import "SKStatusBar.h"
 #import "SKTransitionController.h"
+#import "SKTypeSelectHelper.h"
 
 #define SEGMENTED_CONTROL_HEIGHT    25.0
 #define WINDOW_X_DELTA              0.0
@@ -371,6 +372,18 @@
             [self showSnapshotAtPageNumber:[[setup objectForKey:@"page"] 
unsignedIntValue] forRect:NSRectFromString([setup objectForKey:@"rect"]) 
factor:[[setup objectForKey:@"scaleFactor"] floatValue] autoFits:[[setup 
objectForKey:@"autoFits"] boolValue] display:[[setup objectForKey:@"hasWindow"] 
boolValue]];
     }
     
+    // typeSelectHelpers
+    SKTypeSelectHelper *typeSelectHelper = [[[SKTypeSelectHelper alloc] init] 
autorelease];
+    [typeSelectHelper setMatchesImmediately:NO];
+    [typeSelectHelper setMatchOption:SKFullStringMatch];
+    [typeSelectHelper setDataSource:self];
+    [thumbnailTableView setTypeSelectHelper:typeSelectHelper];
+    
+    typeSelectHelper = [[[SKTypeSelectHelper alloc] init] autorelease];
+    [typeSelectHelper setMatchOption:SKSubstringMatch];
+    [typeSelectHelper setDataSource:self];
+    [noteOutlineView setTypeSelectHelper:typeSelectHelper];
+    
     // This update toolbar item and other states
     [self handleChangedHistoryNotification:nil];
     [self handlePageChangedNotification:nil];
@@ -780,6 +793,7 @@
             [pageLabels addObject:label ? label : @""];
         }
         [self didChangeValueForKey:@"pageLabels"];
+        [[thumbnailTableView typeSelectHelper] rebuildTypeSelectSearchCache];
         
         NSEnumerator *setupEnum = [snapshotDicts objectEnumerator];
         NSDictionary *setup;
@@ -3516,6 +3530,35 @@
     return menu;
 }
 
+#pragma mark SKTypeSelectHelper datasource protocol
+
+- (NSArray *)typeSelectHelperSelectionItems:(SKTypeSelectHelper 
*)typeSelectHelper {
+    if ([typeSelectHelper isEqual:[thumbnailTableView typeSelectHelper]]) {
+        return pageLabels;
+    } else if ([typeSelectHelper isEqual:[noteOutlineView typeSelectHelper]]) {
+        return [[noteArrayController arrangedObjects] valueForKey:@"contents"];
+    }
+    return nil;
+}
+
+- (unsigned int)typeSelectHelperCurrentlySelectedIndex:(SKTypeSelectHelper 
*)typeSelectHelper {
+    if ([typeSelectHelper isEqual:[thumbnailTableView typeSelectHelper]]) {
+        return [[thumbnailTableView selectedRowIndexes] lastIndex];
+    } else if ([typeSelectHelper isEqual:[noteOutlineView typeSelectHelper]]) {
+        return [[noteArrayController arrangedObjects] indexOfObject:[self 
selectedNote]];
+    }
+    return NSNotFound;
+}
+
+- (void)typeSelectHelper:(SKTypeSelectHelper *)typeSelectHelper 
selectItemAtIndex:(unsigned int)itemIndex {
+    if ([typeSelectHelper isEqual:[thumbnailTableView typeSelectHelper]]) {
+        [self setPageNumber:itemIndex + 1];
+    } else if ([typeSelectHelper isEqual:[noteOutlineView typeSelectHelper]]) {
+        int row = [noteOutlineView rowForItem:[[noteArrayController 
arrangedObjects] objectAtIndex:itemIndex]];
+        [noteOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] 
byExtendingSelection:NO];
+    }
+}
+
 #pragma mark Outline
 
 - (int)outlineRowForPageIndex:(unsigned int)pageIndex {

Modified: trunk/SKNoteOutlineView.h
===================================================================
--- trunk/SKNoteOutlineView.h   2007-08-20 18:21:29 UTC (rev 2689)
+++ trunk/SKNoteOutlineView.h   2007-08-21 14:20:35 UTC (rev 2690)
@@ -38,10 +38,12 @@
 
 #import <Cocoa/Cocoa.h>
 
[EMAIL PROTECTED] SKTypeSelectHelper;
 
 @interface SKNoteOutlineView : NSOutlineView {    
     IBOutlet NSWindow *noteTypeSheet;
     IBOutlet NSMatrix *noteTypeMatrix;
+    SKTypeSelectHelper *typeSelectHelper;
 }
 
 - (NSArray *)noteTypes;
@@ -53,6 +55,9 @@
 - (IBAction)selectNoteTypes:(id)sender;
 - (IBAction)dismissNoteTypeSheet:(id)sender;
 
+- (SKTypeSelectHelper *)typeSelectHelper;
+- (void)setTypeSelectHelper:(SKTypeSelectHelper *)newTypeSelectHelper;
+
 @end
 
 

Modified: trunk/SKNoteOutlineView.m
===================================================================
--- trunk/SKNoteOutlineView.m   2007-08-20 18:21:29 UTC (rev 2689)
+++ trunk/SKNoteOutlineView.m   2007-08-21 14:20:35 UTC (rev 2690)
@@ -39,11 +39,14 @@
 #import "SKNoteOutlineView.h"
 #import <Quartz/Quartz.h>
 #import "NSString_SKExtensions.h"
+#import "SKTypeSelectHelper.h"
 
 
 @implementation SKNoteOutlineView
 
 - (void)dealloc {
+    [typeSelectHelper setDataSource:nil];
+    [typeSelectHelper release];
     [noteTypeSheet release];
     [super dealloc];
 }
@@ -52,6 +55,22 @@
     [self noteTypeMenu]; // this sets the menu for the header view
 }
 
+- (SKTypeSelectHelper *)typeSelectHelper {
+    return typeSelectHelper;
+}
+
+- (void)setTypeSelectHelper:(SKTypeSelectHelper *)newTypeSelectHelper {
+    if (typeSelectHelper != newTypeSelectHelper) {
+        [typeSelectHelper release];
+        typeSelectHelper = [newTypeSelectHelper retain];
+    }
+}
+
+- (void)reloadData{
+    [super reloadData];
+    [typeSelectHelper rebuildTypeSelectSearchCache];
+}
+
 - (void)delete:(id)sender {
     if ([[self delegate] 
respondsToSelector:@selector(outlineViewDeleteSelectedRows:)]) {
                if ([[self selectedRowIndexes] count] == 0)
@@ -68,6 +87,8 @@
     
        if ((eventChar == NSDeleteCharacter || eventChar == 
NSDeleteFunctionKey) && modifiers == 0)
         [self delete:self];
+    else if (typeSelectHelper && modifiers == 0 && [[NSCharacterSet 
alphanumericCharacterSet] characterIsMember:eventChar])
+        [typeSelectHelper processKeyDownCharacter:eventChar];
        else
                [super keyDown:theEvent];
 }

Modified: trunk/SKNotesDocument.m
===================================================================
--- trunk/SKNotesDocument.m     2007-08-20 18:21:29 UTC (rev 2689)
+++ trunk/SKNotesDocument.m     2007-08-21 14:20:35 UTC (rev 2690)
@@ -45,6 +45,8 @@
 #import "SKApplicationController.h"
 #import "NSValue_SKExtensions.h"
 #import "NSString_SKExtensions.h"
+#import "SKTypeSelectHelper.h"
+#import "SKPDFAnnotationNote.h"
 
 @implementation SKNotesDocument
 
@@ -71,6 +73,11 @@
     NSSortDescriptor *contentsSortDescriptor = [[[NSSortDescriptor alloc] 
initWithKey:@"contents" ascending:YES 
selector:@selector(localizedCaseInsensitiveNumericCompare:)] autorelease];
     [arrayController setSortDescriptors:[NSArray 
arrayWithObjects:indexSortDescriptor, contentsSortDescriptor, nil]];
     [outlineView reloadData];
+    
+    SKTypeSelectHelper *typeSelectHelper = [[[SKTypeSelectHelper alloc] init] 
autorelease];
+    [typeSelectHelper setMatchOption:SKSubstringMatch];
+    [typeSelectHelper setDataSource:self];
+    [outlineView setTypeSelectHelper:typeSelectHelper];
 }
 
 - (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
@@ -319,4 +326,28 @@
     return [item valueForKey:@"type"] ? [item valueForKey:@"contents"] : 
[[item valueForKey:@"contents"] string];
 }
 
+#pragma mark SKTypeSelectHelper datasource protocol
+
+- (NSArray *)typeSelectHelperSelectionItems:(SKTypeSelectHelper 
*)typeSelectHelper {
+    return [[arrayController arrangedObjects] valueForKey:@"contents"];
+}
+
+- (unsigned int)typeSelectHelperCurrentlySelectedIndex:(SKTypeSelectHelper 
*)typeSelectHelper {
+    NSArray *arrangedNotes = [arrayController arrangedObjects];
+    int row = [outlineView selectedRow];
+    id item = nil;
+    if (row == -1)
+        return NSNotFound;
+    item = [outlineView itemAtRow:row];
+    if ([item valueForKey:@"type"])
+        return [arrangedNotes indexOfObject:item];
+    else 
+        return [[arrangedNotes valueForKey:@"child"] indexOfObject:item];
+}
+
+- (void)typeSelectHelper:(SKTypeSelectHelper *)typeSelectHelper 
selectItemAtIndex:(unsigned int)itemIndex {
+    int row = [outlineView rowForItem:[[arrayController arrangedObjects] 
objectAtIndex:itemIndex]];
+    [outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] 
byExtendingSelection:NO];
+}
+
 @end

Modified: trunk/SKThumbnailTableView.h
===================================================================
--- trunk/SKThumbnailTableView.h        2007-08-20 18:21:29 UTC (rev 2689)
+++ trunk/SKThumbnailTableView.h        2007-08-21 14:20:35 UTC (rev 2690)
@@ -39,12 +39,20 @@
 #import <Cocoa/Cocoa.h>
 
 
[EMAIL PROTECTED] SKTypeSelectHelper;
+
 @interface SKThumbnailTableView : NSTableView
 {
     BOOL isScrolling;
+    SKTypeSelectHelper *typeSelectHelper;
 }
+
 - (BOOL)isScrolling;
 - (BOOL)canCopy;
+
+- (SKTypeSelectHelper *)typeSelectHelper;
+- (void)setTypeSelectHelper:(SKTypeSelectHelper *)newTypeSelectHelper;
+
 @end
 
 

Modified: trunk/SKThumbnailTableView.m
===================================================================
--- trunk/SKThumbnailTableView.m        2007-08-20 18:21:29 UTC (rev 2689)
+++ trunk/SKThumbnailTableView.m        2007-08-21 14:20:35 UTC (rev 2690)
@@ -38,6 +38,7 @@
 
 #import "SKThumbnailTableView.h"
 #import "OBUtilities.h"
+#import "SKTypeSelectHelper.h"
 
 static NSString *SKScrollerWillScrollNotification = 
@"SKScrollerWillScrollNotification";
 static NSString *SKScrollerDidScrollNotification = 
@"SKScrollerDidScrollNotification";
@@ -66,6 +67,8 @@
 
 - (void)dealloc {
     [[NSNotificationCenter defaultCenter] removeObserver:self];
+    [typeSelectHelper setDataSource:nil];
+    [typeSelectHelper release];
     [super dealloc];
 }
 
@@ -122,6 +125,34 @@
     [super highlightSelectionInClipRect:clipRect]; 
 }
 
+- (SKTypeSelectHelper *)typeSelectHelper {
+    return typeSelectHelper;
+}
+
+- (void)setTypeSelectHelper:(SKTypeSelectHelper *)newTypeSelectHelper {
+    if (typeSelectHelper != newTypeSelectHelper) {
+        [typeSelectHelper release];
+        typeSelectHelper = [newTypeSelectHelper retain];
+    }
+}
+
+- (void)reloadData{
+    [super reloadData];
+    [typeSelectHelper rebuildTypeSelectSearchCache];
+}
+
+- (void)keyDown:(NSEvent *)theEvent {
+    NSString *characters = [theEvent charactersIgnoringModifiers];
+    unichar eventChar = [characters length] > 0 ? [characters 
characterAtIndex:0] : 0;
+       unsigned modifierFlags = [theEvent modifierFlags] & 
NSDeviceIndependentModifierFlagsMask;
+    
+    if (typeSelectHelper && modifierFlags == 0 && [[NSCharacterSet 
alphanumericCharacterSet] characterIsMember:eventChar]) {
+        [typeSelectHelper processKeyDownCharacter:eventChar];
+    } else {
+        [super keyDown:theEvent];
+    }
+}
+
 - (void)mouseDown:(NSEvent *)theEvent {
     if (([theEvent modifierFlags] & NSCommandKeyMask) && [[self delegate] 
respondsToSelector:@selector(tableView:commandSelectRow:)]) {
         int row = [self rowAtPoint:[self convertPoint:[theEvent 
locationInWindow] fromView:nil]];

Added: trunk/SKTypeSelectHelper.h
===================================================================
--- trunk/SKTypeSelectHelper.h                          (rev 0)
+++ trunk/SKTypeSelectHelper.h  2007-08-21 14:20:35 UTC (rev 2690)
@@ -0,0 +1,88 @@
+//
+//  SKTypeSelectHelper.h
+//  Skim
+//
+//  Created by Christiaan Hofman on 8/21/07.
+/*
+ This software is Copyright (c) 2007
+ Christiaan Hofman. All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 
+ - 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.
+ 
+ - Neither the name of Christiaan Hofman nor the names of any
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ OWNER OR 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 <Cocoa/Cocoa.h>
+
+
+enum {
+    SKPrefixMatch,
+    SKSubstringMatch,
+    SKFullStringMatch
+};
+
[EMAIL PROTECTED] SKTypeSelectHelper : NSObject {
+    id dataSource;
+    BOOL cycleResults;
+    int matchOption;
+    BOOL matchesImmediately;
+    
+    NSArray *searchCache;
+    NSMutableString *searchString;
+    NSTimer *timer;
+}
+
+- (id)dataSource;
+- (void)setDataSource:(id)anObject;
+
+- (BOOL)cyclesSimilarResults;
+- (void)setCyclesSimilarResults:(BOOL)newValue;
+
+- (BOOL)matchesImmediately;
+- (void)setMatchesImmediately:(BOOL)newValue;
+
+- (int)matchOption;
+- (void)setMatchOption:(int)newValue;
+
+- (void)rebuildTypeSelectSearchCache;
+    
+- (BOOL)isProcessing;
+
+- (void)processKeyDownCharacter:(unichar)character;
+
[EMAIL PROTECTED]
+
+
[EMAIL PROTECTED] NSObject (SKTypeSelectDataSource)
+
+- (NSArray *)typeSelectHelperSelectionItems:(SKTypeSelectHelper 
*)typeSelectHelper; // required
+- (unsigned int)typeSelectHelperCurrentlySelectedIndex:(SKTypeSelectHelper 
*)typeSelectHelper; // required
+- (void)typeSelectHelper:(SKTypeSelectHelper *)typeSelectHelper 
selectItemAtIndex:(unsigned int)itemIndex; // required
+
+- (void)typeSelectHelper:(SKTypeSelectHelper *)typeSelectHelper 
updateSearchString:(NSString *)searchString; // optional
+
[EMAIL PROTECTED]

Added: trunk/SKTypeSelectHelper.m
===================================================================
--- trunk/SKTypeSelectHelper.m                          (rev 0)
+++ trunk/SKTypeSelectHelper.m  2007-08-21 14:20:35 UTC (rev 2690)
@@ -0,0 +1,239 @@
+//
+//  SKTypeSelectHelper.m
+//  Skim
+//
+//  Created by Christiaan Hofman on 8/21/07.
+/*
+ This software is Copyright (c) 2007
+ Christiaan Hofman. All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 
+ - 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.
+ 
+ - Neither the name of Christiaan Hofman nor the names of any
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ OWNER OR 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 "SKTypeSelectHelper.h"
+
+
[EMAIL PROTECTED] SKTypeSelectHelper (SKPrivate)
+- (void)typeSelectSearchTimeout;
+- (unsigned int)indexOfMatchedItemAfterIndex:(unsigned int)selectedIndex;
[EMAIL PROTECTED]
+
[EMAIL PROTECTED] SKTypeSelectHelper
+
+// Init and dealloc
+
+- (id)init {
+    if (self = [super init]){
+        cycleResults = YES;
+        matchesImmediately = NO;
+        matchOption = SKPrefixMatch;
+    }
+    return self;
+}
+
+- (void)dealloc {
+    [timer invalidate];
+    [timer release];
+    [searchString release];
+    [searchCache release];
+    [super dealloc];
+}
+
+// API
+- (id)dataSource {
+    return dataSource;
+}
+
+- (void)setDataSource:(id)newDataSource {
+    if (dataSource == newDataSource)
+        return;
+    
+    dataSource = newDataSource;
+    [self rebuildTypeSelectSearchCache];
+}
+
+- (BOOL)cyclesSimilarResults {
+    return cycleResults;
+}
+
+- (void)setCyclesSimilarResults:(BOOL)newValue {
+    cycleResults = newValue;
+}
+
+- (BOOL)matchesImmediately {
+    return matchesImmediately;
+}
+
+- (void)setMatchesImmediately:(BOOL)newValue {
+    matchesImmediately = newValue;
+}
+
+- (int)matchOption {
+    return matchOption;
+}
+
+- (void)setMatchOption:(int)newValue {
+    matchOption = newValue;
+}
+
+- (void)rebuildTypeSelectSearchCache {    
+    if (searchCache)
+        [searchCache release];
+    
+    searchCache = [[dataSource typeSelectHelperSelectionItems:self] retain];
+}
+
+- (void)processKeyDownCharacter:(unichar)character {
+    NSString *selectedItem = nil;
+    unsigned int selectedIndex, foundIndex;
+    
+    // Create the search string the first time around
+    if (searchString == nil)
+        searchString = [[NSMutableString alloc] init];
+    
+    // Append the new character to the search string
+    [searchString appendFormat:@"%C", character];
+    
+    if ([dataSource 
respondsToSelector:@selector(typeSelectHelper:updateSearchString:)])
+        [dataSource typeSelectHelper:self updateSearchString:searchString];
+    
+    // Reset the timer if it hasn't expired yet
+    NSDate *date = [NSDate dateWithTimeIntervalSinceNow:0.7];
+    [timer invalidate];
+    [timer release];
+    timer = nil;
+    timer = [[NSTimer alloc] initWithFireDate:date interval:0 target:self 
selector:@selector(typeSelectSearchTimeout:) userInfo:NULL repeats:NO];
+    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
+    
+    if (matchesImmediately) {
+        if (cycleResults) {
+            selectedIndex = [dataSource 
typeSelectHelperCurrentlySelectedIndex:self];
+            if (selectedIndex < [searchCache count])
+               selectedItem = [searchCache objectAtIndex:selectedIndex];
+            else
+                selectedIndex = NSNotFound;
+            
+            // Avoid flashing a selection all over the place while you're 
still typing the thing you have selected
+            if (matchOption == SKFullStringMatch) {
+                if ([selectedItem caseInsensitiveCompare:searchString] == 
NSOrderedSame)
+                    return;
+            } else {
+                unsigned int searchStringLength = [searchString length];
+                unsigned int selectedItemLength = [selectedItem length];
+                NSRange range = NSMakeRange(0, matchOption == SKPrefixMatch ? 
searchStringLength : selectedItemLength);
+                if (searchStringLength > 1 && selectedItemLength >= 
searchStringLength && [selectedItem rangeOfString:searchString 
options:NSCaseInsensitiveSearch range:range].location != NSNotFound)
+                    return;
+            }
+            
+        } else {
+            selectedIndex = NSNotFound;
+        }
+        
+        foundIndex = [self indexOfMatchedItemAfterIndex:selectedIndex];
+        
+        if (foundIndex != NSNotFound)
+            [dataSource typeSelectHelper:self selectItemAtIndex:foundIndex];
+    }
+}
+
+- (BOOL)isProcessing {
+    return timer != nil;
+}
+
+- (void)typeSelectSearchTimeout:(NSTimer *)aTimer {
+    if (matchesImmediately == NO && [searchString length]) {
+        unsigned int selectedIndex, foundIndex;
+        
+        if (cycleResults) {
+            selectedIndex = NSNotFound;
+        } else {
+            selectedIndex = [dataSource 
typeSelectHelperCurrentlySelectedIndex:self];
+            if (selectedIndex >= [searchCache count])
+                selectedIndex = NSNotFound;
+        }
+        foundIndex = [self indexOfMatchedItemAfterIndex:selectedIndex];
+        if (foundIndex != NSNotFound)
+            [dataSource typeSelectHelper:self selectItemAtIndex:foundIndex];
+    }
+    
+    if ([dataSource 
respondsToSelector:@selector(typeSelectHelper:updateSearchString:)])
+        [dataSource typeSelectHelper:self updateSearchString:nil];
+    [timer invalidate];
+    [timer release];
+    timer = nil;
+    [searchString release];
+    searchString = nil;
+}
+
+- (unsigned int)indexOfMatchedItemAfterIndex:(unsigned int)selectedIndex {
+    if (searchCache == nil)
+        [self rebuildTypeSelectSearchCache];
+    
+    unsigned int labelCount = [searchCache count];
+    
+    if (labelCount == NO)
+        return NSNotFound;
+    
+    if (selectedIndex == NSNotFound)
+        selectedIndex = labelCount - 1;
+
+    unsigned int labelIndex = selectedIndex;
+    BOOL looped = NO;
+    unsigned int searchStringLength = [searchString length];
+    int options = NSCaseInsensitiveSearch;
+    
+    if (matchOption == SKPrefixMatch)
+        options |= NSAnchoredSearch;
+    
+    while (looped == NO) {
+        NSString *label;
+        
+        if (++labelIndex == labelCount)
+            labelIndex = 0;
+        if (labelIndex == selectedIndex)
+            looped = YES;
+        
+        label = [searchCache objectAtIndex:labelIndex];
+        
+        if (matchOption == SKFullStringMatch) {
+            if ([label caseInsensitiveCompare:searchString] == NSOrderedSame)
+                return labelIndex;
+        } else {
+            int location = [label length] < searchStringLength ? NSNotFound : 
[label rangeOfString:searchString options:options].location;
+            if (location != NSNotFound) {
+                if (location == 0 || [[NSCharacterSet letterCharacterSet] 
characterIsMember:[label characterAtIndex:location - 1]] == NO)
+                    return labelIndex;
+            }
+        }
+    }
+    
+    return NSNotFound;
+}
+
[EMAIL PROTECTED]

Modified: trunk/Skim.xcodeproj/project.pbxproj
===================================================================
--- trunk/Skim.xcodeproj/project.pbxproj        2007-08-20 18:21:29 UTC (rev 
2689)
+++ trunk/Skim.xcodeproj/project.pbxproj        2007-08-21 14:20:35 UTC (rev 
2690)
@@ -128,6 +128,7 @@
                CE54898F0B35D50E00F8AFB6 /* SKInfoWindowController.m in Sources 
*/ = {isa = PBXBuildFile; fileRef = CE54898E0B35D50E00F8AFB6 /* 
SKInfoWindowController.m */; };
                CE5BC5B10C79A27B00EBDCF7 /* NSTableView_SKExtensions.h in 
CopyFiles */ = {isa = PBXBuildFile; fileRef = CE5BC5AF0C79A27B00EBDCF7 /* 
NSTableView_SKExtensions.h */; };
                CE5BC5B20C79A27B00EBDCF7 /* NSTableView_SKExtensions.m in 
Sources */ = {isa = PBXBuildFile; fileRef = CE5BC5B00C79A27B00EBDCF7 /* 
NSTableView_SKExtensions.m */; };
+               CE5BD6740C7ADF1500EBDCF7 /* SKTypeSelectHelper.m in Sources */ 
= {isa = PBXBuildFile; fileRef = CE5BD6720C7ADF1500EBDCF7 /* 
SKTypeSelectHelper.m */; };
                CE5F42AF0BF89FE00069D89C /* AppleRemote.m in Sources */ = {isa 
= PBXBuildFile; fileRef = CE5F42AD0BF89FE00069D89C /* AppleRemote.m */; };
                CE5F43730BF8A3410069D89C /* IOKit.framework in Frameworks */ = 
{isa = PBXBuildFile; fileRef = CE5F42D30BF8A3400069D89C /* IOKit.framework */; 
};
                CE67BB260BC44AC9007B6929 /* ZoomValues.strings in Resources */ 
= {isa = PBXBuildFile; fileRef = CE67BB240BC44AC9007B6929 /* ZoomValues.strings 
*/; };
@@ -485,6 +486,8 @@
                CE54AA8E0BBC037400008750 /* ReleaseNotes.rtf */ = {isa = 
PBXFileReference; lastKnownFileType = text.rtf; path = ReleaseNotes.rtf; 
sourceTree = "<group>"; };
                CE5BC5AF0C79A27B00EBDCF7 /* NSTableView_SKExtensions.h */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; 
path = NSTableView_SKExtensions.h; sourceTree = "<group>"; };
                CE5BC5B00C79A27B00EBDCF7 /* NSTableView_SKExtensions.m */ = 
{isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = 
sourcecode.c.objc; path = NSTableView_SKExtensions.m; sourceTree = "<group>"; };
+               CE5BD6710C7ADF1500EBDCF7 /* SKTypeSelectHelper.h */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 
SKTypeSelectHelper.h; sourceTree = "<group>"; };
+               CE5BD6720C7ADF1500EBDCF7 /* SKTypeSelectHelper.m */ = {isa = 
PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path 
= SKTypeSelectHelper.m; sourceTree = "<group>"; };
                CE5F42AC0BF89FE00069D89C /* AppleRemote.h */ = {isa = 
PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = 
AppleRemote.h; path = vendorsrc/martinkahr/AppleRemoteSource_R80/AppleRemote.h; 
sourceTree = "<group>"; };
                CE5F42AD0BF89FE00069D89C /* AppleRemote.m */ = {isa = 
PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; 
name = AppleRemote.m; path = 
vendorsrc/martinkahr/AppleRemoteSource_R80/AppleRemote.m; sourceTree = 
"<group>"; };
                CE5F42D30BF8A3400069D89C /* IOKit.framework */ = {isa = 
PBXFileReference; lastKnownFileType = wrapper.framework; name = 
IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree 
= "<absolute>"; };
@@ -979,6 +982,8 @@
                                CE1E2B270BDAB6180011D9DD /* SKPDFSynchronizer.m 
*/,
                                CE48BAD50C089EA300A166C6 /* SKTemplateParser.h 
*/,
                                CE48BAD60C089EA300A166C6 /* SKTemplateParser.m 
*/,
+                               CE5BD6710C7ADF1500EBDCF7 /* 
SKTypeSelectHelper.h */,
+                               CE5BD6720C7ADF1500EBDCF7 /* 
SKTypeSelectHelper.m */,
                        );
                        name = Miscellaneous;
                        sourceTree = "<group>";
@@ -1468,6 +1473,7 @@
                                CEAA67260C70A882006BD633 /* 
NSURL_SKExtensions.m in Sources */,
                                CEF839B30C77742A00A3AD51 /* 
Files_SKExtensions.m in Sources */,
                                CE5BC5B20C79A27B00EBDCF7 /* 
NSTableView_SKExtensions.m in Sources */,
+                               CE5BD6740C7ADF1500EBDCF7 /* 
SKTypeSelectHelper.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };


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: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
Skim-app-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/skim-app-commit

Reply via email to