Skip to content

Commit

Permalink
Change the way result highlighting is done in the "go to database" di…
Browse files Browse the repository at this point in the history
…alog

(Looks a lot less crappy now)
  • Loading branch information
dmoagx committed Sep 26, 2015
1 parent 7cd8cce commit 4aa8ff7
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 58 deletions.
15 changes: 9 additions & 6 deletions Interfaces/English.lproj/GotoDatabaseDialog.xib
Expand Up @@ -2,7 +2,7 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00"> <archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
<data> <data>
<int key="IBDocument.SystemTarget">1060</int> <int key="IBDocument.SystemTarget">1060</int>
<string key="IBDocument.SystemVersion">13F34</string> <string key="IBDocument.SystemVersion">13F1096</string>
<string key="IBDocument.InterfaceBuilderVersion">6751</string> <string key="IBDocument.InterfaceBuilderVersion">6751</string>
<string key="IBDocument.AppKitVersion">1265.21</string> <string key="IBDocument.AppKitVersion">1265.21</string>
<string key="IBDocument.HIToolboxVersion">698.00</string> <string key="IBDocument.HIToolboxVersion">698.00</string>
Expand Down Expand Up @@ -477,6 +477,14 @@
</object> </object>
<string key="id">O6w-uT-xK7</string> <string key="id">O6w-uT-xK7</string>
</object> </object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="1032614363"/>
<reference key="destination" ref="1042702399"/>
</object>
<string key="id">6Ur-iZ-LRv</string>
</object>
</array> </array>
<object class="IBMutableOrderedSet" key="objectRecords"> <object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects"> <array key="orderedObjects">
Expand Down Expand Up @@ -720,7 +728,6 @@
<string key="okClicked:">id</string> <string key="okClicked:">id</string>
<string key="searchChanged:">id</string> <string key="searchChanged:">id</string>
<string key="toggleWordSearch:">id</string> <string key="toggleWordSearch:">id</string>
<string key="toogleWordSearch:">id</string>
</dictionary> </dictionary>
<dictionary class="NSMutableDictionary" key="actionInfosByName"> <dictionary class="NSMutableDictionary" key="actionInfosByName">
<object class="IBActionInfo" key="cancelClicked:"> <object class="IBActionInfo" key="cancelClicked:">
Expand All @@ -739,10 +746,6 @@
<string key="name">toggleWordSearch:</string> <string key="name">toggleWordSearch:</string>
<string key="candidateClassName">id</string> <string key="candidateClassName">id</string>
</object> </object>
<object class="IBActionInfo" key="toogleWordSearch:">
<string key="name">toogleWordSearch:</string>
<string key="candidateClassName">id</string>
</object>
</dictionary> </dictionary>
<object class="IBClassDescriptionSource" key="sourceIdentifier"> <object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string> <string key="majorKey">IBProjectSource</string>
Expand Down
4 changes: 3 additions & 1 deletion Source/SPGotoDatabaseController.h
Expand Up @@ -36,7 +36,7 @@
* keyboard-based navigation between databases. The dialog also enables * keyboard-based navigation between databases. The dialog also enables
* jumping to a database by C&P-ing its full name. * jumping to a database by C&P-ing its full name.
*/ */
@interface SPGotoDatabaseController : NSWindowController <NSTableViewDataSource,NSControlTextEditingDelegate,NSUserInterfaceValidations> @interface SPGotoDatabaseController : NSWindowController <NSTableViewDataSource,NSTableViewDelegate,NSControlTextEditingDelegate,NSUserInterfaceValidations>
{ {
IBOutlet NSSearchField *searchField; IBOutlet NSSearchField *searchField;
IBOutlet NSButton *okButton; IBOutlet NSButton *okButton;
Expand All @@ -48,6 +48,8 @@


BOOL isFiltered; BOOL isFiltered;
BOOL allowCustomNames; BOOL allowCustomNames;

NSDictionary *highlightAttrs;
} }


/** /**
Expand Down
151 changes: 100 additions & 51 deletions Source/SPGotoDatabaseController.m
Expand Up @@ -42,7 +42,7 @@ @interface SPGotoDatabaseController (Private)
* It will neither clear the filteredList first, nor change the isFiltered ivar! * It will neither clear the filteredList first, nor change the isFiltered ivar!
* Search is case insensitive. * Search is case insensitive.
*/ */
- (void)_buildHightlightedFilterList:(NSString *)filter didFindExactMatch:(BOOL *)exactMatch; - (void)_buildFilterList:(NSString *)filter didFindExactMatch:(BOOL *)exactMatch;


- (IBAction)okClicked:(id)sender; - (IBAction)okClicked:(id)sender;
- (IBAction)cancelClicked:(id)sender; - (IBAction)cancelClicked:(id)sender;
Expand All @@ -53,7 +53,29 @@ - (BOOL)qualifiesForWordSearch; //takes s from searchField
@end @end


static BOOL StringQualifiesForWordSearch(NSString *s); static BOOL StringQualifiesForWordSearch(NSString *s);
static NSUInteger CountSubMatches(NSAttributedString *s);
#pragma mark -

@interface SPGotoFilteredItem : NSObject {
NSString *string;
NSArray *matches;
BOOL isCustomItem;
}
@property(nonatomic,retain) NSString *string;
@property(nonatomic,retain) NSArray *matches;
@property(nonatomic,assign) BOOL isCustomItem;

+ (SPGotoFilteredItem *)item;
@end

@implementation SPGotoFilteredItem

@synthesize string;
@synthesize matches;
@synthesize isCustomItem;

+ (SPGotoFilteredItem *)item { return [[[SPGotoFilteredItem alloc] init] autorelease]; }
@end


#pragma mark - #pragma mark -


Expand All @@ -64,9 +86,14 @@ @implementation SPGotoDatabaseController
- (id)init - (id)init
{ {
if ((self = [super initWithWindowNibName:@"GotoDatabaseDialog"])) { if ((self = [super initWithWindowNibName:@"GotoDatabaseDialog"])) {
unfilteredList = [[NSMutableArray alloc] init]; unfilteredList = [[NSMutableArray alloc] init];
filteredList = [[NSMutableArray alloc] init]; filteredList = [[NSMutableArray alloc] init];
isFiltered = NO; isFiltered = NO;
highlightAttrs = [@{
NSBackgroundColorAttributeName: [NSColor colorWithCalibratedRed:249/255.0 green:247/255.0 blue:62/255.0 alpha:0.5],
NSUnderlineColorAttributeName: [NSColor colorWithCalibratedRed:246/255.0 green:189/255.0 blue:85/255.0 alpha:1.0],
NSUnderlineStyleAttributeName: [NSNumber numberWithInt:NSUnderlineStyleThick]
} retain];


[self setAllowCustomNames:YES]; [self setAllowCustomNames:YES];
} }
Expand Down Expand Up @@ -111,7 +138,7 @@ - (IBAction)searchChanged:(id)sender


BOOL exactMatch = NO; BOOL exactMatch = NO;


[self _buildHightlightedFilterList:newFilter didFindExactMatch:&exactMatch]; [self _buildFilterList:newFilter didFindExactMatch:&exactMatch];


//always add the search string to the end of the list (in case the user //always add the search string to the end of the list (in case the user
//wants to switch to a DB not in the list) unless there was an exact match //wants to switch to a DB not in the list) unless there was an exact match
Expand All @@ -121,19 +148,19 @@ - (IBAction)searchChanged:(id)sender
newFilter = [newFilter substringWithRange:NSMakeRange(1, [newFilter length]-2)]; newFilter = [newFilter substringWithRange:NSMakeRange(1, [newFilter length]-2)];


if([newFilter length]) { if([newFilter length]) {
NSMutableAttributedString *searchValue = [[NSMutableAttributedString alloc] initWithString:newFilter]; SPGotoFilteredItem *customItem = [SPGotoFilteredItem item];

[customItem setString:newFilter];
[searchValue applyFontTraits:NSItalicFontMask range:NSMakeRange(0, [newFilter length])]; [customItem setIsCustomItem:YES];

[filteredList addObject:[searchValue autorelease]]; [filteredList addObject:customItem];
} }
} }
} }


[databaseListView reloadData]; [databaseListView reloadData];


// Ensure we have a selection // Ensure we have a selection
if ([databaseListView selectedRow] < 0) { if ([databaseListView selectedRow] < 0 && [self numberOfRowsInTableView:databaseListView]) {
[databaseListView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; [databaseListView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
} }


Expand Down Expand Up @@ -170,16 +197,12 @@ - (NSString *)selectedDatabase
id attrValue; id attrValue;


if (isFiltered) { if (isFiltered) {
attrValue = [filteredList objectOrNilAtIndex:row]; attrValue = [(SPGotoFilteredItem *)[filteredList objectOrNilAtIndex:row] string];
} }
else { else {
attrValue = [unfilteredList objectOrNilAtIndex:row]; attrValue = [unfilteredList objectOrNilAtIndex:row];
} }


if ([attrValue isKindOfClass:[NSAttributedString class]]) {
return [attrValue string];
}

return attrValue; return attrValue;
} }


Expand Down Expand Up @@ -209,14 +232,8 @@ - (BOOL)runModal
#pragma mark - #pragma mark -
#pragma mark Private #pragma mark Private


- (void)_buildHightlightedFilterList:(NSString *)filter didFindExactMatch:(BOOL *)exactMatch - (void)_buildFilterList:(NSString *)filter didFindExactMatch:(BOOL *)exactMatch
{ {
NSDictionary *attrs = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSColor colorWithCalibratedRed:249/255.0 green:247/255.0 blue:62/255.0 alpha:0.5],NSBackgroundColorAttributeName,
[NSColor colorWithCalibratedRed:180/255.0 green:164/255.0 blue:31/255.0 alpha:1.0],NSUnderlineColorAttributeName,
[NSNumber numberWithInt:NSUnderlineStyleSingle],NSUnderlineStyleAttributeName,
nil];

NSStringCompareOptions opts = NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch|NSWidthInsensitiveSearch; NSStringCompareOptions opts = NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch|NSWidthInsensitiveSearch;


BOOL useWordSearch = StringQualifiesForWordSearch(filter); BOOL useWordSearch = StringQualifiesForWordSearch(filter);
Expand All @@ -239,9 +256,11 @@ - (void)_buildHightlightedFilterList:(NSString *)filter didFindExactMatch:(BOOL
} }
} }


NSMutableAttributedString *attrMatch = [[NSMutableAttributedString alloc] initWithString:db]; SPGotoFilteredItem *item = [SPGotoFilteredItem item];
[attrMatch setAttributes:attrs range:matchRange]; [item setString:db];
[filteredList addObject:[attrMatch autorelease]]; [item setMatches:@[[NSValue valueWithRange:matchRange]]];

[filteredList addObject:item];
} }
} }
// default to a per-character search // default to a per-character search
Expand All @@ -263,38 +282,34 @@ - (void)_buildHightlightedFilterList:(NSString *)filter didFindExactMatch:(BOOL
} }
} }


NSMutableAttributedString *attrMatch = [[NSMutableAttributedString alloc] initWithString:db]; SPGotoFilteredItem *item = [SPGotoFilteredItem item];
[item setString:db];
[item setMatches:matches];


for (NSValue *matchValue in matches) { [filteredList addObject:item];
[attrMatch setAttributes:attrs range:[matchValue rangeValue]];
}

[filteredList addObject:[attrMatch autorelease]];
} }
} }


//sort the filtered list //sort the filtered list
[filteredList sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { [filteredList sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
// word search produces only 1 match, skip. // word search produces only 1 match, skip.
if(!useWordSearch) { if(!useWordSearch) {
// All the items are NSAttributedStrings.
// First we want to sort by number of match groups. // First we want to sort by number of match groups.
// Less match groups -> better result: // Less match groups -> better result:
// Search string: abc // Search string: abc
// Matches: schema_abc, tablecloth // Matches: schema_abc, tablecloth
// => First only has 1 match group, is more likely to be the desired result // => First only has 1 match group, is more likely to be the desired result
NSUInteger mgc1 = CountSubMatches(obj1); NSUInteger mgc1 = [[(SPGotoFilteredItem *)obj1 matches] count];
NSUInteger mgc2 = CountSubMatches(obj2); NSUInteger mgc2 = [[(SPGotoFilteredItem *)obj2 matches] count];
if(mgc1 < mgc2) if(mgc1 < mgc2)
return NSOrderedAscending; return NSOrderedAscending;
if(mgc2 > mgc1) if(mgc2 > mgc1)
return NSOrderedDescending; return NSOrderedDescending;
} }
// For strings with the same number of match groups we just sort alphabetically // For strings with the same number of match groups we just sort alphabetically
return [[(NSAttributedString *)obj1 string] compare:[(NSAttributedString *)obj2 string]]; return [[(SPGotoFilteredItem *)obj1 string] compare:[(SPGotoFilteredItem *)obj2 string]];
}]; }];


[attrs release];
} }


- (BOOL)qualifiesForWordSearch - (BOOL)qualifiesForWordSearch
Expand All @@ -321,10 +336,56 @@ - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColu
return [unfilteredList objectAtIndex:rowIndex]; return [unfilteredList objectAtIndex:rowIndex];
} }
else { else {
return [filteredList objectAtIndex:rowIndex]; return [(SPGotoFilteredItem *)[filteredList objectAtIndex:rowIndex] string];
} }
} }


#pragma mark -
#pragma mark NSTableViewDelegate

- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
//nothing to do here, unless the list is filtered
if(!isFiltered) return;

// The styling of source list table views is basically done by Apple by replacing
// the cell's string with an attributedstring. But if the data source were to
// already return an attributedstring, most of the other attributes Apple sets
// would not get applied. So we have to add our attributes after Apple has already
// modified the string returned by the data source.

id cellValue = [cell objectValue];
//turn the cell value into something we can work with
NSMutableAttributedString *attrString;
if([cellValue isKindOfClass:[NSMutableAttributedString class]]) {
attrString = cellValue;
}
else if([cellValue isKindOfClass:[NSAttributedString class]]) {
attrString = [[[NSMutableAttributedString alloc] initWithAttributedString:cellValue] autorelease];
}
else if([cellValue isKindOfClass:[NSString class]]) {
attrString = [[[NSMutableAttributedString alloc] initWithString:cellValue] autorelease];
}
else {
SPLog(@"Unknown object for cellValue (type=%@)",[cellValue className]);
return;
}

SPGotoFilteredItem *item = [filteredList objectAtIndex:row];

if([item isCustomItem]) {
[[attrString mutableString] appendString:@""];
[attrString addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange([attrString length]-1, 1)];
}
else {
for (NSValue *matchValue in [item matches]) {
[attrString addAttributes:highlightAttrs range:[matchValue rangeValue]];
}
}

[cell setObjectValue:attrString];
}

#pragma mark - #pragma mark -
#pragma mark NSControlTextEditingDelegate #pragma mark NSControlTextEditingDelegate


Expand Down Expand Up @@ -373,6 +434,7 @@ - (void)dealloc
{ {
SPClear(unfilteredList); SPClear(unfilteredList);
SPClear(filteredList); SPClear(filteredList);
SPClear(highlightAttrs);


[super dealloc]; [super dealloc];
} }
Expand All @@ -385,16 +447,3 @@ BOOL StringQualifiesForWordSearch(NSString *s)
{ {
return (s && ([s length] > 1) && (([s hasPrefix:@"\""] && [s hasSuffix:@"\""]) || ([s hasPrefix:@"'"] && [s hasSuffix:@"'"]))); return (s && ([s length] > 1) && (([s hasPrefix:@"\""] && [s hasSuffix:@"\""]) || ([s hasPrefix:@"'"] && [s hasSuffix:@"'"])));
} }

NSUInteger CountSubMatches(NSAttributedString *s)
{
__block NSUInteger matches = 0;
[s enumerateAttribute:NSBackgroundColorAttributeName
inRange:NSMakeRange(0, [s length])
options:0
usingBlock:^(id value, NSRange range, BOOL *stop) {
if(value)
matches++;
}];
return matches;
}

0 comments on commit 4aa8ff7

Please sign in to comment.