diff options
-rw-r--r-- | Frameworks/MCPKit/MCPFoundationKit/MCPConnection.h | 2 | ||||
-rw-r--r-- | Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m | 34 | ||||
-rw-r--r-- | Source/CMTextView.m | 111 | ||||
-rw-r--r-- | Source/SPArrayAdditions.h | 1 | ||||
-rw-r--r-- | Source/SPArrayAdditions.m | 19 | ||||
-rw-r--r-- | Source/SPConstants.h | 1 | ||||
-rw-r--r-- | Source/SPConstants.m | 1 | ||||
-rw-r--r-- | Source/SPNarrowDownCompletion.m | 18 | ||||
-rw-r--r-- | Source/SPNavigatorController.m | 74 | ||||
-rw-r--r-- | Source/TableDocument.h | 2 | ||||
-rw-r--r-- | Source/TableDocument.m | 50 |
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]; } |