aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h2
-rw-r--r--Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m34
-rw-r--r--Source/CMTextView.m111
-rw-r--r--Source/SPArrayAdditions.h1
-rw-r--r--Source/SPArrayAdditions.m19
-rw-r--r--Source/SPConstants.h1
-rw-r--r--Source/SPConstants.m1
-rw-r--r--Source/SPNarrowDownCompletion.m18
-rw-r--r--Source/SPNavigatorController.m74
-rw-r--r--Source/TableDocument.h2
-rw-r--r--Source/TableDocument.m50
11 files changed, 230 insertions, 83 deletions
diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h
index 8e5a60f9..45491884 100644
--- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h
+++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h
@@ -67,6 +67,8 @@ static inline NSData* NSStringDataUsingLossyEncoding(NSString* self, NSInteger e
- (NSString *)onReconnectShouldUseEncoding:(id)connection;
- (void)noConnectionAvailable:(id)connection;
- (MCPConnectionCheck)connectionLost:(id)connection;
+- (void)updateNavigator:(id)sender;
+- (NSString *)connectionID;
@end
diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
index 3223964c..6dac15b3 100644
--- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
+++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
@@ -1849,7 +1849,7 @@ void performThreadedKeepAlive(void *ptr)
}
/**
- * Updates the dict containing the structure of all available databases (mainly for completion)
+ * Updates the dict containing the structure of all available databases (mainly for completion/navigator)
* executed on a new connection.
*/
- (void)queryDbStructure
@@ -1888,7 +1888,7 @@ void performThreadedKeepAlive(void *ptr)
} else {
thePass = [self cStringFromString:connectionPassword];
}
-
+
// Connect
connectionSetupStatus = mysql_real_connect(structConnection, theHost, theLogin, thePass, NULL, connectionPort, theSocket, mConnectionFlags);
thePass = NULL;
@@ -1931,10 +1931,23 @@ void performThreadedKeepAlive(void *ptr)
NSMutableArray *allDbNames = [NSMutableArray array];
NSMutableArray *allTableNames = [NSMutableArray array];
+ NSString *connectionID;
+ if([delegate respondsToSelector:@selector(connectionID)])
+ connectionID = [NSString stringWithString:[[self delegate] connectionID]];
+ else
+ connectionID = @"_";
+
+ NSString *SPUniqueSchemaDelimiter = @"￸";
+
+ [structure setObject:[NSMutableDictionary dictionary] forKey:connectionID];
+
while(row = mysql_fetch_row(theResult)) {
NSString *db = [self stringWithUTF8CString:row[0]];
+ NSString *db_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, db];
NSString *table = [self stringWithUTF8CString:row[1]];
+ NSString *table_id = [NSString stringWithFormat:@"%@%@%@", db_id, SPUniqueSchemaDelimiter, table];
NSString *field = [self stringWithUTF8CString:row[2]];
+ NSString *field_id = [NSString stringWithFormat:@"%@%@%@", table_id, SPUniqueSchemaDelimiter, field];
NSString *type = [self stringWithUTF8CString:row[3]];
NSString *charset = (row[4]) ? [self stringWithUTF8CString:row[4]] : @"";
NSString *structtype = [self stringWithUTF8CString:row[5]];
@@ -1947,19 +1960,20 @@ void performThreadedKeepAlive(void *ptr)
[allDbNames addObject:[db lowercaseString]];
[allTableNames addObject:[table lowercaseString]];
- if(![structure valueForKey:db]) {
- [structure setObject:[NSMutableDictionary dictionary] forKey:db];
+ if(![[structure valueForKey:connectionID] valueForKey:db_id]) {
+ [[structure valueForKey:connectionID] setObject:[NSMutableDictionary dictionary] forKey:db_id];
}
- if(![[structure valueForKey:db] valueForKey:table]) {
- [[structure valueForKey:db] setObject:[NSMutableDictionary dictionary] forKey:table];
+ if(![[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id]) {
+ [[[structure valueForKey:connectionID] valueForKey:db_id] setObject:[NSMutableDictionary dictionary] forKey:table_id];
}
- [[[structure valueForKey:db] valueForKey:table] setObject:[NSArray arrayWithObjects:type, charset, key, extra, priv, nil] forKey:field];
- [[[structure valueForKey:db] valueForKey:table] setObject:structtype forKey:@" struct_type "];
+ [[[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id] setObject:[NSArray arrayWithObjects:type, charset, key, extra, priv, nil] forKey:field_id];
+ [[[[structure valueForKey:connectionID] valueForKey:db_id] valueForKey:table_id] setObject:structtype forKey:@" struct_type "];
}
+
mysql_free_result(theResult);
mysql_close(structConnection);
@@ -1987,6 +2001,10 @@ void performThreadedKeepAlive(void *ptr)
}
uniqueDbIdentifier = [[NSDictionary dictionaryWithDictionary:uniqueIdentifier] retain];
+
+ if(delegate && [delegate respondsToSelector:@selector(updateNavigator:)])
+ [[self delegate] updateNavigator:self];
+
isQueryingDbStructure = NO;
[queryPool release];
return;
diff --git a/Source/CMTextView.m b/Source/CMTextView.m
index 1f44687b..27de550f 100644
--- a/Source/CMTextView.m
+++ b/Source/CMTextView.m
@@ -301,7 +301,15 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if(!isDictMode && [mySQLConnection isConnected])
{
// Add structural db/table/field data to completions list or fallback to gathering TablesList data
- NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[mySQLConnection getDbStructure]];
+
+ NSString* connectionID;
+ if([[[self window] delegate] respondsToSelector:@selector(connectionID)])
+ connectionID = [[[self window] delegate] connectionID];
+ else
+ connectionID = @"_";
+NSLog(@"cid %@", connectionID);
+ NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[mySQLConnection getDbStructure] objectForKey:connectionID]];
+
if(dbs != nil && [dbs count]) {
NSMutableArray *allDbs = [NSMutableArray array];
[allDbs addObjectsFromArray:[dbs allKeys]];
@@ -309,8 +317,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
// Add database names having no tables since they don't appear in the information_schema
if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allDatabaseNames"] != nil)
for(id db in [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"allDatabaseNames"])
- if(![allDbs containsObject:db])
- [allDbs addObject:db];
+ if(![allDbs containsObject:[NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, db]])
+ [allDbs addObject:[NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, db]];
NSSortDescriptor *desc = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES selector:@selector(localizedCompare:)];
NSMutableArray *sortedDbs = [NSMutableArray array];
@@ -320,25 +328,35 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSString *currentTable = nil;
if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"selectedDatabase"] != nil)
- currentDb = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"];
+ currentDb = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]];
if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"tableName"] != nil)
currentTable = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"tableName"];
// Put current selected db at the top
if(aTableName == nil && aDbName == nil && [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]) {
- currentDb = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"];
+ currentDb = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"]];
[sortedDbs removeObject:currentDb];
[sortedDbs insertObject:currentDb atIndex:0];
}
+ NSString* aTableName_id;
+ NSString* aDbName_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, aDbName];
+ if(aDbName && aTableName)
+ aTableName_id = [NSString stringWithFormat:@"%@%@%@", aDbName_id, SPUniqueSchemaDelimiter, aTableName];
+ else
+ aTableName_id = [NSString stringWithFormat:@"%@%@%@", currentDb, SPUniqueSchemaDelimiter, aTableName];
+
+
// Put information_schema and/or mysql db at the end if not selected
- if(currentDb && ![currentDb isEqualToString:@"mysql"] && [sortedDbs containsObject:@"mysql"]) {
- [sortedDbs removeObject:@"mysql"];
- [sortedDbs addObject:@"mysql"];
+ NSString* mysql_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, @"mysql"];
+ NSString* inf_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, @"information_schema"];
+ if(currentDb && ![currentDb isEqualToString:mysql_id] && [sortedDbs containsObject:mysql_id]) {
+ [sortedDbs removeObject:mysql_id];
+ [sortedDbs addObject:mysql_id];
}
- if(currentDb && ![currentDb isEqualToString:@"information_schema"] && [sortedDbs containsObject:@"information_schema"]) {
- [sortedDbs removeObject:@"information_schema"];
- [sortedDbs addObject:@"information_schema"];
+ if(currentDb && ![currentDb isEqualToString:inf_id] && [sortedDbs containsObject:inf_id]) {
+ [sortedDbs removeObject:inf_id];
+ [sortedDbs addObject:inf_id];
}
BOOL aTableNameExists = NO;
@@ -348,40 +366,40 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSInteger uniqueSchemaKind = [mySQLConnection getUniqueDbIdentifierFor:[aTableName lowercaseString]];
// If no db name but table name check if table name is a valid name in the current selected db
- if(aTableName && [aTableName length] && [dbs objectForKey:currentDb] && [[dbs objectForKey:currentDb] objectForKey:aTableName] && uniqueSchemaKind == 2) {
+ if(aTableName && [aTableName length] && [dbs objectForKey:currentDb] && [[dbs objectForKey:currentDb] objectForKey:[NSString stringWithFormat:@"%@%@%@", currentDb, SPUniqueSchemaDelimiter, aTableName]] && uniqueSchemaKind == 2) {
aTableNameExists = YES;
- aDbName = [NSString stringWithString:currentDb];
+ aDbName_id = [NSString stringWithString:currentDb];
}
// If no db name but table name check if table name is a valid db name
if(!aTableNameExists && aTableName && [aTableName length] && uniqueSchemaKind == 1) {
- aDbName = [NSString stringWithString:aTableName];
+ aDbName_id = [NSString stringWithFormat:@"%@%@%@", connectionID, SPUniqueSchemaDelimiter, aTableName];
aTableNameExists = NO;
}
} else if (aDbName && [aDbName length]) {
- if(aTableName && [aTableName length] && [dbs objectForKey:aDbName] && [[dbs objectForKey:aDbName] objectForKey:aTableName]) {
+ if(aTableName && [aTableName length] && [dbs objectForKey:aDbName_id] && [[dbs objectForKey:aDbName_id] objectForKey:[NSString stringWithFormat:@"%@%@%@", aDbName_id, SPUniqueSchemaDelimiter, aTableName]]) {
aTableNameExists = YES;
}
}
// If aDbName exist show only those table
- if(aDbName && [aDbName length] && [allDbs containsObject:aDbName]) {
+ if([allDbs containsObject:aDbName_id]) {
[sortedDbs removeAllObjects];
- [sortedDbs addObject:aDbName];
+ [sortedDbs addObject:aDbName_id];
}
for(id db in sortedDbs) {
NSArray *allTables = [[dbs objectForKey:db] allKeys];
NSMutableArray *sortedTables = [NSMutableArray array];
if(aTableNameExists) {
- [sortedTables addObject:aTableName];
+ [sortedTables addObject:aTableName_id];
} else {
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:db, @"display", @"database-small", @"image", db, @"isRef", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:[[db componentsSeparatedByString:SPUniqueSchemaDelimiter] lastObject], @"display", @"database-small", @"image", @"", @"isRef", nil]];
[sortedTables addObjectsFromArray:[allTables sortedArrayUsingDescriptors:[NSArray arrayWithObject:desc]]];
- if([sortedTables count] > 1 && [sortedTables containsObject:currentTable]) {
- [sortedTables removeObject:currentTable];
- [sortedTables insertObject:currentTable atIndex:0];
+ if([sortedTables count] > 1 && [sortedTables containsObject:[NSString stringWithFormat:@"%@%@%@", db, SPUniqueSchemaDelimiter, currentTable]]) {
+ [sortedTables removeObject:[NSString stringWithFormat:@"%@%@%@", db, SPUniqueSchemaDelimiter, currentTable]];
+ [sortedTables insertObject:[NSString stringWithFormat:@"%@%@%@", db, SPUniqueSchemaDelimiter, currentTable] atIndex:0];
}
}
for(id table in sortedTables) {
@@ -392,17 +410,17 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if(!aTableNameExists)
switch(structtype) {
case 0:
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-small-square", @"image", db, @"path", [NSString stringWithFormat:@"%@.%@",db,table], @"isRef", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:[[table componentsSeparatedByString:SPUniqueSchemaDelimiter] lastObject], @"display", @"table-small-square", @"image", db, @"path", @"", @"isRef", nil]];
break;
case 1:
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-view-small-square", @"image", db, @"path", [NSString stringWithFormat:@"%@.%@",db,table], @"isRef", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:[[table componentsSeparatedByString:SPUniqueSchemaDelimiter] lastObject], @"display", @"table-view-small-square", @"image", db, @"path", @"", @"isRef", nil]];
break;
case 2:
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"proc-small", @"image", db, @"path", [NSString stringWithFormat:@"%@.%@",db,table], @"isRef", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:[[table componentsSeparatedByString:SPUniqueSchemaDelimiter] lastObject], @"display", @"proc-small", @"image", db, @"path", @"", @"isRef", nil]];
breakFlag = YES;
break;
case 3:
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"func-small", @"image", db, @"path", [NSString stringWithFormat:@"%@.%@",db,table], @"isRef", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:[[table componentsSeparatedByString:SPUniqueSchemaDelimiter] lastObject], @"display", @"func-small", @"image", db, @"path", @"", @"isRef", nil]];
breakFlag = YES;
break;
}
@@ -418,20 +436,20 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSString *t = [typ stringByReplacingOccurrencesOfRegex:@"\\(.*?\\)" withString:@"(…)"];
NSString *lst = [typ stringByMatching:@"\\(([^\\)]*?)\\)" capture:1L];
[possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- field, @"display",
+ [[field componentsSeparatedByString:SPUniqueSchemaDelimiter] lastObject], @"display",
@"field-small-square", @"image",
- [NSString stringWithFormat:@"%@⇠%@",table,db], @"path",
+ table, @"path",
t, @"type",
lst, @"list",
- [NSString stringWithFormat:@"%@.%@.%@",db,table,field], @"isRef",
+ @"", @"isRef",
nil]];
} else {
[possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:
- field, @"display",
+ [[field componentsSeparatedByString:SPUniqueSchemaDelimiter] lastObject], @"display",
@"field-small-square", @"image",
- [NSString stringWithFormat:@"%@⇠%@",table,db], @"path",
+ table, @"path",
typ, @"type",
- [NSString stringWithFormat:@"%@.%@.%@",db,table,field], @"isRef",
+ @"", @"isRef",
nil]];
}
}
@@ -706,7 +724,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
completionParseRangeLocation = parseRange.location;
//Get the NSPoint of the first character of the current word
- NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(completionRange.location,1) actualCharacterRange:NULL];
+ NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(completionRange.location,0) actualCharacterRange:NULL];
NSRect boundingRect = [[self layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[self textContainer]];
boundingRect = [self convertRect: boundingRect toView: NULL];
NSPoint pos = [[self window] convertBaseToScreen: NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
@@ -1115,6 +1133,13 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
object:nil];
NSMutableArray *possibleCompletions = [[[NSMutableArray alloc] initWithCapacity:0] autorelease];
+
+ NSString *connectionID;
+ if([[[self window] delegate] respondsToSelector:@selector(connectionID)])
+ connectionID = [[[self window] delegate] connectionID];
+ else
+ connectionID = @"_";
+
NSArray *arr = nil;
if([kind isEqualToString:@"$SP_ASLIST_ALL_TABLES"]) {
NSString *currentDb = nil;
@@ -1122,7 +1147,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"selectedDatabase"] != nil)
currentDb = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"selectedDatabase"];
- NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[mySQLConnection getDbStructure]];
+ NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[mySQLConnection getDbStructure] objectForKey:connectionID]];
if(currentDb != nil && dbs != nil && [dbs count] && [dbs objectForKey:currentDb]) {
NSArray *allTables = [[dbs objectForKey:currentDb] allKeys];
@@ -1134,10 +1159,10 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSInteger structtype = [[theTable objectForKey:@" struct_type "] intValue];
switch(structtype) {
case 0:
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-small-square", @"image", currentDb, @"path", [NSString stringWithFormat:@"%@.%@",currentDb,table], @"isRef", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-small-square", @"image", currentDb, @"path", @"", @"isRef", nil]];
break;
case 1:
- [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-view-small-square", @"image", currentDb, @"path", [NSString stringWithFormat:@"%@.%@",currentDb,table], @"isRef", nil]];
+ [possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:table, @"display", @"table-view-small-square", @"image", currentDb, @"path", @"", @"isRef", nil]];
break;
}
}
@@ -1174,7 +1199,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if ([[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKey:@"tableName"] != nil)
currentTable = [[[[self window] delegate] valueForKeyPath:@"tablesListInstance"] valueForKeyPath:@"tableName"];
- NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[mySQLConnection getDbStructure]];
+ NSDictionary *dbs = [NSDictionary dictionaryWithDictionary:[[mySQLConnection getDbStructure] objectForKey:connectionID]];
if(currentDb != nil && currentTable != nil && dbs != nil && [dbs count] && [dbs objectForKey:currentDb] && [[dbs objectForKey:currentDb] objectForKey:currentTable]) {
NSDictionary * theTable = [[dbs objectForKey:currentDb] objectForKey:currentTable];
NSArray *allFields = [theTable allKeys];
@@ -1193,18 +1218,18 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:
field, @"display",
@"field-small-square", @"image",
- [NSString stringWithFormat:@"%@⇠%@",currentTable,currentDb], @"path",
+ [NSString stringWithFormat:@"%@@%%@",currentTable,currentDb], @"path", SPUniqueSchemaDelimiter,
t, @"type",
lst, @"list",
- [NSString stringWithFormat:@"%@.%@.%@",currentDb,currentTable,field], @"isRef",
+ @"", @"isRef",
nil]];
} else {
[possibleCompletions addObject:[NSDictionary dictionaryWithObjectsAndKeys:
field, @"display",
@"field-small-square", @"image",
- [NSString stringWithFormat:@"%@⇠%@",currentTable,currentDb], @"path",
+ [NSString stringWithFormat:@"%@%@%@",currentTable,currentDb], @"path", SPUniqueSchemaDelimiter,
typ, @"type",
- [NSString stringWithFormat:@"%@.%@.%@",currentDb,currentTable,field], @"isRef",
+ @"", @"isRef",
nil]];
}
}
@@ -1245,7 +1270,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
oneColumn:NO];
//Get the NSPoint of the first character of the current word
- NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(aRange.location,1) actualCharacterRange:NULL];
+ NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(aRange.location,0) actualCharacterRange:NULL];
NSRect boundingRect = [[self layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[self textContainer]];
boundingRect = [self convertRect: boundingRect toView: NULL];
NSPoint pos = [[self window] convertBaseToScreen: NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
@@ -1408,7 +1433,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
autoComplete:NO
oneColumn:YES];
//Get the NSPoint of the first character of the current word
- NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(r2.location,1) actualCharacterRange:NULL];
+ NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(r2.location,0) actualCharacterRange:NULL];
NSRect boundingRect = [[self layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[self textContainer]];
boundingRect = [self convertRect: boundingRect toView: NULL];
NSPoint pos = [[self window] convertBaseToScreen: NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
diff --git a/Source/SPArrayAdditions.h b/Source/SPArrayAdditions.h
index e5e2e6a3..2c3ce7c4 100644
--- a/Source/SPArrayAdditions.h
+++ b/Source/SPArrayAdditions.h
@@ -39,6 +39,7 @@ static inline void NSMutableArrayReplaceObject(NSArray* self, CFIndex idx, id an
- (NSString *)componentsJoinedAndBacktickQuoted;
- (NSString *)componentsJoinedByCommas;
- (NSString *)componentsJoinedByPeriodAndBacktickQuoted;
+- (NSString *)componentsJoinedByPeriodAndBacktickQuotedAndIgnoreFirst;
- (NSArray *)subarrayWithIndexes:(NSIndexSet *)indexes;
@end
diff --git a/Source/SPArrayAdditions.m b/Source/SPArrayAdditions.m
index de7b8a3e..6dd9aa30 100644
--- a/Source/SPArrayAdditions.m
+++ b/Source/SPArrayAdditions.m
@@ -76,6 +76,25 @@
return result;
}
+- (NSString *)componentsJoinedByPeriodAndBacktickQuotedAndIgnoreFirst
+{
+ NSMutableString *result = [NSMutableString string];
+ [result setString:@""];
+ BOOL notFirst = NO;
+ for (NSString *component in self)
+ {
+ if ([result length])
+ [result appendString: @"."];
+
+ if (notFirst)
+ [result appendString:[component backtickQuotedString]];
+
+ notFirst = YES;
+ }
+ return result;
+}
+
+
- (NSArray *)subarrayWithIndexes:(NSIndexSet *)indexes
{
NSMutableArray *subArray = [NSMutableArray arrayWithCapacity:[indexes count]];
diff --git a/Source/SPConstants.h b/Source/SPConstants.h
index 0843a79c..ddc31be3 100644
--- a/Source/SPConstants.h
+++ b/Source/SPConstants.h
@@ -183,6 +183,7 @@ extern NSString *SPQueryHistoryReplacesContent;
extern NSString *SPQuickLookTypes;
extern NSString *SPTableChangedNotification;
extern NSString *SPBlobTextEditorSpellCheckingEnabled;
+extern NSString *SPUniqueSchemaDelimiter;
// URLs
extern NSString *SPHomePageURL;
diff --git a/Source/SPConstants.m b/Source/SPConstants.m
index 1b34b1c7..82a92bc7 100644
--- a/Source/SPConstants.m
+++ b/Source/SPConstants.m
@@ -151,6 +151,7 @@ NSString *SPQueryHistoryReplacesContent = @"QueryHistoryReplacesContent
NSString *SPQuickLookTypes = @"QuickLookTypes";
NSString *SPTableChangedNotification = @"SPTableSelectionChanged";
NSString *SPBlobTextEditorSpellCheckingEnabled = @"BlobTextEditorSpellCheckingEnabled";
+NSString *SPUniqueSchemaDelimiter = @"￸"; // U+FFF8
// URLs
NSString *SPHomePageURL = @"http://www.sequelpro.com/";
diff --git a/Source/SPNarrowDownCompletion.m b/Source/SPNarrowDownCompletion.m
index cfdd7605..de9d39aa 100644
--- a/Source/SPNarrowDownCompletion.m
+++ b/Source/SPNarrowDownCompletion.m
@@ -346,8 +346,11 @@
if([[filtered objectAtIndex:rowIndex] objectForKey:@"path"]) {
NSMutableString *tt = [NSMutableString string];
[tt setString:NSLocalizedString(@"Schema path:", @"schema path header for completion tooltip")];
- for(id p in [[[[[filtered objectAtIndex:rowIndex] objectForKey:@"path"] componentsSeparatedByString:@"⇠"] reverseObjectEnumerator] allObjects])
- [tt appendFormat:@"\n• %@",p];
+ BOOL flag = NO;
+ for(id p in [[[filtered objectAtIndex:rowIndex] objectForKey:@"path"] componentsSeparatedByString:SPUniqueSchemaDelimiter]) {
+ if(flag) [tt appendFormat:@"\n• %@",p];
+ flag=YES;
+ }
return tt;
}
return @"";
@@ -426,8 +429,9 @@
[b setAltersStateOfSelectedItem:NO];
[b setControlSize:NSMiniControlSize];
NSMenu *m = [[NSMenu alloc] init];
- for(id p in [[[filtered objectAtIndex:rowIndex] objectForKey:@"path"] componentsSeparatedByString:@"⇠"])
+ for(id p in [[[[[filtered objectAtIndex:rowIndex] objectForKey:@"path"] componentsSeparatedByString:SPUniqueSchemaDelimiter] reverseObjectEnumerator] allObjects])
[m addItemWithTitle:p action:NULL keyEquivalent:@""];
+ [m removeItemAtIndex:[m numberOfItems]-1];
[b setMenu:m];
[m release];
[b setPreferredEdge:NSMinXEdge];
@@ -486,7 +490,9 @@
for(i=0; i<[[self filterString] length]; i++) {
c = [[self filterString] characterAtIndex:i];
if(c != '`') {
- if(c == '.' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}')
+ if(c == '.')
+ [fuzzyRegexp appendString:[NSString stringWithFormat:@".*?",SPUniqueSchemaDelimiter]];
+ else if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}')
[fuzzyRegexp appendString:[NSString stringWithFormat:@".*?\\%c",c]];
else
[fuzzyRegexp appendString:[NSString stringWithFormat:@".*?%c",c]];
@@ -494,7 +500,7 @@
}
for(id s in suggestions)
- if([[s objectForKey:@"display"] isMatchedByRegex:fuzzyRegexp] || [[s objectForKey:@"isRef"] isMatchedByRegex:fuzzyRegexp])
+ if([[s objectForKey:@"display"] isMatchedByRegex:fuzzyRegexp] || [[s objectForKey:@"path"] isMatchedByRegex:fuzzyRegexp])
[newFiltered addObject:s];
@@ -799,7 +805,7 @@
&& ([[NSApp currentEvent] modifierFlags] & (NSShiftKeyMask))
&& [[selectedItem objectForKey:@"path"] length]) {
NSString *path = [NSString stringWithFormat:@"%@.%@",
- [[[[[selectedItem objectForKey:@"path"] componentsSeparatedByString:@"⇠"] reverseObjectEnumerator] allObjects] componentsJoinedByPeriodAndBacktickQuoted],
+ [[[selectedItem objectForKey:@"path"] componentsSeparatedByString:SPUniqueSchemaDelimiter] componentsJoinedByPeriodAndBacktickQuotedAndIgnoreFirst],
[candidateMatch backtickQuotedString]];
// Check if path's db name is the current selected db name
diff --git a/Source/SPNavigatorController.m b/Source/SPNavigatorController.m
index e2edc2c2..1445f433 100644
--- a/Source/SPNavigatorController.m
+++ b/Source/SPNavigatorController.m
@@ -23,6 +23,9 @@
// More info at <http://code.google.com/p/sequel-pro/>
#import "SPNavigatorController.h"
+#import "RegexKitLite.h"
+#import "SPConstants.h"
+
static SPNavigatorController *sharedNavigatorController = nil;
@@ -88,7 +91,7 @@ static SPNavigatorController *sharedNavigatorController = nil;
prefs = [NSUserDefaults standardUserDefaults];
[self setWindowFrameAutosaveName:@"SPNavigator"];
-
+
}
- (NSString *)windowFrameAutosaveName
@@ -96,30 +99,41 @@ static SPNavigatorController *sharedNavigatorController = nil;
return @"SPNavigator";
}
+#pragma mark -
+#pragma mark IBActions
- (IBAction)updateEntries:(id)sender;
{
+
if(schemaData) [schemaData release]; schemaData = nil;
schemaData = [[NSMutableDictionary alloc] init];
if ([[[NSDocumentController sharedDocumentController] documents] count]) {
for(id doc in [[NSDocumentController sharedDocumentController] documents]) {
- NSString *connectionName;
- if([(NSString*)[doc port] length])
- connectionName = [NSString stringWithFormat:@"%@:%@", [doc host], [doc port]];
- else
- connectionName = [doc host];
+
+ if(![[doc valueForKeyPath:@"mySQLConnection"] isConnected]) continue;
+
+ NSString *connectionName = [doc connectionID];
+
+ if(!connectionName || [connectionName isEqualToString:@"_"]) continue;
+
if(![schemaData objectForKey:connectionName]) {
+
id data = [[doc valueForKeyPath:@"mySQLConnection"] getDbStructure];
- if(data) {
- [schemaData setObject:data forKey:connectionName];
+
+ if(data && [data objectForKey:connectionName]) {
+ [schemaData setObject:[data objectForKey:connectionName] forKey:connectionName];
} else {
- if([[doc valueForKeyPath:@"mySQLConnection"] serverMajorVersion] > 4)
+
+ if([[doc valueForKeyPath:@"mySQLConnection"] serverMajorVersion] > 4) {
[schemaData setObject:[NSDictionary dictionary] forKey:[NSString stringWithFormat:@"%@ – no data loaded yet", connectionName]];
- else
+ } else {
[schemaData setObject:[NSDictionary dictionary] forKey:[NSString stringWithFormat:@"%@ – no data for this server version", connectionName]];
+ }
+
}
}
}
+
[outlineSchema1 reloadData];
[outlineSchema2 reloadData];
}
@@ -130,9 +144,8 @@ static SPNavigatorController *sharedNavigatorController = nil;
}
-// ================================================================
-// NSOutlineView data source methods
-// ================================================================
+#pragma mark -
+#pragma mark outline delegates
- (id)outlineView:(id)outlineView child:(NSInteger)index ofItem:(id)item
{
@@ -152,6 +165,7 @@ static SPNavigatorController *sharedNavigatorController = nil;
- (BOOL)outlineView:(id)outlineView isItemExpandable:(id)item
{
if([item isKindOfClass:[NSDictionary class]] && [item count]) {
+ // Suppress expanding for PROCEDUREs and FUNCTIONs
if([item objectForKey:@" struct_type "] && [[item objectForKey:@" struct_type "] intValue] > 1) {
return NO;
}
@@ -179,6 +193,13 @@ static SPNavigatorController *sharedNavigatorController = nil;
id parentObject = [outlineView parentForItem:item] ? [outlineView parentForItem:item] : schemaData;
if ([[tableColumn identifier] isEqualToString:@"field"]) {
+
+ // top level is connection
+ if([outlineView levelForItem:item] == 0) {
+ [[tableColumn dataCell] setImage:[NSImage imageNamed:@"network-small"]];
+ return [[[[parentObject allKeysForObject:item] objectAtIndex:0] componentsSeparatedByString:@"&SSH&"] objectAtIndex:0];
+ }
+
if ([parentObject isKindOfClass:[NSDictionary class]]) {
if([outlineView parentForItem:item]) {
if([item isKindOfClass:[NSDictionary class]]) {
@@ -202,27 +223,30 @@ static SPNavigatorController *sharedNavigatorController = nil;
[[tableColumn dataCell] setImage:[NSImage imageNamed:@"database-small"]];
}
} else {
- // It's a field
- if(![[[parentObject allKeysForObject:item] objectAtIndex:0] hasPrefix:@" "])
+ // It's a field and use the key " struct_type " to increase the distance between node and first child
+ if(![[[parentObject allKeysForObject:item] objectAtIndex:0] hasPrefix:@" "]) {
[[tableColumn dataCell] setImage:[NSImage imageNamed:@"field-small-square"]];
- else
+ } else {
[[tableColumn dataCell] setImage:[NSImage imageNamed:@"dummy-small"]];
+ }
}
- } else {
- [[tableColumn dataCell] setImage:[NSImage imageNamed:@"network-small"]];
}
- if([[parentObject allKeysForObject:item] count])
- if(![[[parentObject allKeysForObject:item] objectAtIndex:0] hasPrefix:@" "])
- return [[parentObject allKeysForObject:item] objectAtIndex:0];
-
- return nil;
+ if([[parentObject allKeysForObject:item] count] && ![[[parentObject allKeysForObject:item] objectAtIndex:0] hasPrefix:@" "]) {
+ return [[[[parentObject allKeysForObject:item] objectAtIndex:0] componentsSeparatedByString:SPUniqueSchemaDelimiter] lastObject];
+ }
}
return nil;
}
else if ([[tableColumn identifier] isEqualToString:@"type"]) {
+
+ if([outlineView levelForItem:item] == 0 && [[[parentObject allKeysForObject:item] objectAtIndex:0] rangeOfString:@"&SSH&"].length) {
+ return [NSString stringWithFormat:@"ssh: %@", [[[[parentObject allKeysForObject:item] objectAtIndex:0] componentsSeparatedByString:@"&SSH&"] lastObject]];
+ }
+
if ([item isKindOfClass:[NSArray class]] && ![[[parentObject allKeysForObject:item] objectAtIndex:0] hasPrefix:@" "])
{
- NSTokenFieldCell *b = [[[NSTokenFieldCell alloc] initTextCell:[item componentsJoinedByString:@", "]] autorelease];
+ NSString *typ = [NSString stringWithFormat:@"%@,%@,%@", [[item objectAtIndex:0] stringByReplacingOccurrencesOfRegex:@"\\(.*?,.*?\\)" withString:@"(…)"], [item objectAtIndex:1], [item objectAtIndex:2]];
+ NSTokenFieldCell *b = [[[NSTokenFieldCell alloc] initTextCell:typ] autorelease];
[b setEditable:NO];
[b setAlignment:NSRightTextAlignment];
[b setFont:[NSFont systemFontOfSize:11]];
@@ -252,7 +276,7 @@ static SPNavigatorController *sharedNavigatorController = nil;
if([[[parentObject allKeysForObject:item] objectAtIndex:0] hasPrefix:@" "])
return 5.0;
- return 17.0;
+ return 18.0;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
diff --git a/Source/TableDocument.h b/Source/TableDocument.h
index f125277d..c1dd92a6 100644
--- a/Source/TableDocument.h
+++ b/Source/TableDocument.h
@@ -251,6 +251,7 @@
- (void)showConsole:(id)sender;
- (IBAction)showNavigator:(id)sender;
- (IBAction)toggleNavigator:(id)sender;
+- (void)updateNavigator:(id)sender;
// Accessor methods
- (NSString *)host;
@@ -262,6 +263,7 @@
- (NSString *)user;
- (NSString *)displaySPName;
- (NSString *)keyChainID;
+- (NSString *)connectionID;
// Notification center methods
- (void)willPerformQuery:(NSNotification *)notification;
diff --git a/Source/TableDocument.m b/Source/TableDocument.m
index d741c55d..1616e659 100644
--- a/Source/TableDocument.m
+++ b/Source/TableDocument.m
@@ -1181,6 +1181,16 @@
}
}
+/*
+ * Called from MCPConnection or self to inform the navigator that an instance invoked queryDbStructure
+ * or a window was closed
+ */
+- (void)updateNavigator:(id)sender
+{
+ if([[[SPNavigatorController sharedNavigatorController] window] isVisible])
+ [[SPNavigatorController sharedNavigatorController] updateEntries:self];
+}
+
#pragma mark -
#pragma mark Task progress and notification methods
@@ -2393,6 +2403,44 @@
}
/**
+ * Returns a string to identify the connection uniquely (mainly used to set up db structure with unique keys)
+ */
+- (NSString *)connectionID
+{
+
+ if(!_isConnected) return @"_";
+
+ NSString *port;
+ if([[self port] length])
+ port = [NSString stringWithFormat:@":%@", [self port]];
+ else
+ port = @"";
+
+ switch([connectionController type]) {
+ case SPSocketConnection:
+ return [NSString stringWithFormat:@"%@@localhost%@", ([connectionController user] && [[connectionController user] length])?[connectionController user]:@"anonymous", port];
+ break;
+ case SPTCPIPConnection:
+ return [NSString stringWithFormat:@"%@@%@%@",
+ ([connectionController user] && [[connectionController user] length])?[connectionController user]:@"anonymous",
+ [connectionController host]?[connectionController host]:@"",
+ port];
+ break;
+ case SPSSHTunnelConnection:
+ return [NSString stringWithFormat:@"%@@%@%@&SSH&%@@%@:%@",
+ ([connectionController user] && [[connectionController user] length])?[connectionController user]:@"anonymous",
+ [connectionController host]?[connectionController host]:@"",
+ port,
+ ([connectionController sshUser] && [[connectionController sshUser] length])?[connectionController sshUser]:@"anonymous",
+ [connectionController sshHost]?[connectionController sshHost]:@"",
+ ([[connectionController sshPort] length])?[connectionController sshPort]:@"22"];
+ }
+
+ return @"_";
+
+}
+
+/**
* Returns the currently selected database
*/
- (NSString *)database
@@ -3608,7 +3656,7 @@
if (_isConnected) [self closeConnection];
else [connectionController cancelConnection];
if ([[[SPQueryController sharedQueryController] window] isVisible]) [self toggleConsole:self];
- if ([[[SPNavigatorController sharedNavigatorController] window] isVisible]) [self toggleNavigator:self];
+ if ([[[SPNavigatorController sharedNavigatorController] window] isVisible]) [self updateNavigator:self];
[createTableSyntaxWindow orderOut:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}