diff options
-rw-r--r-- | Frameworks/MCPKit/MCPFoundationKit/MCPNull.m | 2 | ||||
-rw-r--r-- | Source/CMCopyTable.m | 24 | ||||
-rw-r--r-- | Source/CustomQuery.m | 30 | ||||
-rw-r--r-- | Source/SPNotLoaded.h | 41 | ||||
-rw-r--r-- | Source/SPNotLoaded.m | 91 | ||||
-rw-r--r-- | Source/TableContent.m | 191 | ||||
-rw-r--r-- | sequel-pro.xcodeproj/project.pbxproj | 6 |
7 files changed, 245 insertions, 140 deletions
diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPNull.m b/Frameworks/MCPKit/MCPFoundationKit/MCPNull.m index 76d9cfb1..4232fb2a 100644 --- a/Frameworks/MCPKit/MCPFoundationKit/MCPNull.m +++ b/Frameworks/MCPKit/MCPFoundationKit/MCPNull.m @@ -36,7 +36,7 @@ - (BOOL) isNSNull { static id NSNullForComparison; - if (!NSNullForComparison) NSNullForComparison = [NSNull null];; + if (!NSNullForComparison) NSNullForComparison = [NSNull null]; return (self == NSNullForComparison); } diff --git a/Source/CMCopyTable.m b/Source/CMCopyTable.m index f42ff783..23bf7804 100644 --- a/Source/CMCopyTable.m +++ b/Source/CMCopyTable.m @@ -29,6 +29,7 @@ #import "SPStringAdditions.h" #import "TableContent.h" #import "CustomQuery.h" +#import "SPNotLoaded.h" int MENU_EDIT_COPY_WITH_COLUMN = 2001; int MENU_EDIT_COPY_AS_SQL = 2002; @@ -131,7 +132,12 @@ int MENU_EDIT_COPY_AS_SQL = 2002; if ( nil != rowData ) { - [result appendString:[NSString stringWithFormat:@"%@\t", [rowData description] ] ]; + if ([rowData isNSNull]) + [result appendString:[NSString stringWithFormat:@"%@\t", [prefs objectForKey:@"nullValue"]]]; + else if ([rowData isSPNotLoaded]) + [result appendString:[NSString stringWithFormat:@"%@\t", NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]]; + else + [result appendString:[NSString stringWithFormat:@"%@\t", [rowData description] ] ]; } else { @@ -175,7 +181,6 @@ int MENU_EDIT_COPY_AS_SQL = 2002; NSUInteger numColumns = [columns count]; NSIndexSet *selectedRows = [self selectedRowIndexes]; - NSString *spNULL = [prefs objectForKey:@"NullValue"]; NSMutableString *value = [NSMutableString stringWithCapacity:10]; NSArray *dbDataRow; NSMutableArray *columnMappings; @@ -227,8 +232,8 @@ int MENU_EDIT_COPY_AS_SQL = 2002; { rowData = [[tableData objectAtIndex:rowIndex] objectAtIndex:[[columnMappings objectAtIndex:c] intValue]]; - // Check for NULL value - TODO this is not safe!! - if([[rowData description] isEqualToString:spNULL]){ + // Check for NULL value + if([rowData isNSNull]) { [value appendString:@"NULL, "]; continue; } @@ -243,7 +248,7 @@ int MENU_EDIT_COPY_AS_SQL = 2002; [mySQLConnection prepareString:[rowData description]]]]; break; case 2: // blob - if (![[self delegate] isKindOfClass:[CustomQuery class]] && [prefs boolForKey:@"LoadBlobsAsNeeded"]) { + if (![[self delegate] isKindOfClass:[CustomQuery class]] && [rowData isSPNotLoaded]) { // Abort if there are no indices on this table or if there's no table name given. if (![[tableInstance argumentForRow:rowIndex] length] || selectedTable == nil) @@ -253,7 +258,7 @@ int MENU_EDIT_COPY_AS_SQL = 2002; dbDataRow = [[mySQLConnection queryString: [NSString stringWithFormat:@"SELECT * FROM %@ WHERE %@", [selectedTable backtickQuotedString], [tableInstance argumentForRow:rowIndex]]] fetchRowAsArray]; - if([[dbDataRow objectAtIndex:[[columnMappings objectAtIndex:c] intValue]] isKindOfClass:[NSNull class]]) + if([[dbDataRow objectAtIndex:[[columnMappings objectAtIndex:c] intValue]] isNSNull]) [value appendString:@"NULL, "]; else [value appendString:[NSString stringWithFormat:@"X'%@', ", @@ -365,7 +370,12 @@ int MENU_EDIT_COPY_AS_SQL = 2002; if ( nil != rowData ) { - [result appendString:[NSString stringWithFormat:@"%@\t", [rowData description] ] ]; + if ([rowData isNSNull]) + [result appendString:[NSString stringWithFormat:@"%@\t", [prefs objectForKey:@"nullValue"]]]; + else if ([rowData isSPNotLoaded]) + [result appendString:[NSString stringWithFormat:@"%@\t", NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]]; + else + [result appendString:[NSString stringWithFormat:@"%@\t", [rowData description] ] ]; } else { diff --git a/Source/CustomQuery.m b/Source/CustomQuery.m index f04f9fd1..0589ab6f 100644 --- a/Source/CustomQuery.m +++ b/Source/CustomQuery.m @@ -686,13 +686,8 @@ - (void)processResultIntoDataStorage:(MCPStreamingResult *)theResult { NSArray *tempRow; - NSMutableArray *newRow; - int i; long rowsProcessed = 0; - long columnsCount = 0; NSAutoreleasePool *dataLoadingPool; - id prefsNullValue = [[prefs objectForKey:@"NullValue"] retain]; - Class nullClass = [NSNull class]; // Remove all items from the table [fullResult removeAllObjects]; @@ -702,16 +697,8 @@ // Loop through the result rows as they become available while (tempRow = [theResult fetchNextRowAsArray]) { - if (columnsCount == 0) columnsCount = [tempRow count]; NSMutableArrayAddObject(fullResult, [NSMutableArray arrayWithArray:tempRow]); - newRow = NSArrayObjectAtIndex(fullResult, rowsProcessed); - - // Process the retrieved row - for ( i = 0; i < columnsCount; i++ ) { - if ( [NSArrayObjectAtIndex(tempRow, i) isMemberOfClass:nullClass] ) - [newRow replaceObjectAtIndex:i withObject:prefsNullValue]; - } // Update the count of rows processed rowsProcessed++; @@ -725,7 +712,6 @@ // Clean up the autorelease pool [dataLoadingPool drain]; - [prefsNullValue release]; } /* @@ -1182,7 +1168,7 @@ // If there is no primary key, all found fields belonging to the same table are used in the argument for(field in columnsForFieldTableName) { id aValue = [dataRow objectAtIndex:[[field objectForKey:@"datacolumnindex"] intValue]]; - if ([aValue isKindOfClass:[NSNull class]] || [[aValue description] isEqualToString:[prefs stringForKey:@"NullValue"]]) { + if ([aValue isKindOfClass:[NSNull class]] || [aValue isNSNull]) { [fieldIDQueryStr appendFormat:@"%@ IS NULL", [[field objectForKey:@"org_name"] backtickQuotedString]]; } else { [fieldIDQueryStr appendFormat:@"%@=", [[field objectForKey:@"org_name"] backtickQuotedString]]; @@ -1233,11 +1219,8 @@ // For NULL cell's display the user's NULL value placeholder in grey to easily distinguish it from other values if ([cell respondsToSelector:@selector(setTextColor:)]) { - // Note that this approach of changing the color of NULL placeholders is dependent on the cell's value matching that - // of the user's NULL value preference which was set in the result array when it was retrieved (see fetchResultAsArray). - // Also, as an added measure check that the table column actually allows NULLs to make sure we don't change a cell that - // happens to have a value matching the NULL placeholder, but the column doesn't allow NULLs. - [cell setTextColor:([[cell stringValue] isEqualToString:[prefs objectForKey:@"NullValue"]]) ? [NSColor lightGrayColor] : [NSColor blackColor]]; + id theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(fullResult, row), [[aTableColumn identifier] intValue]); + [cell setTextColor:[theValue isNSNull] ? [NSColor lightGrayColor] : [NSColor blackColor]]; } } @@ -1255,7 +1238,7 @@ if ( [theValue isKindOfClass:[NSData class]] ) return [theValue shortStringRepresentationUsingEncoding:[mySQLConnection encoding]]; - if ( [theValue isMemberOfClass:[NSNull class]] ) + if ( [theValue isNSNull] ) return [prefs objectForKey:@"NullValue"]; return theValue; @@ -1659,7 +1642,10 @@ && [columnDefinition valueForKey:@"char_length"]) [fieldEditor setTextMaxLength:[[columnDefinition valueForKey:@"char_length"] intValue]]; - id editData = [[fieldEditor editWithObject:[[fullResult objectAtIndex:rowIndex] objectAtIndex:[[aTableColumn identifier] intValue]] + id originalData = [[fullResult objectAtIndex:rowIndex] objectAtIndex:[[aTableColumn identifier] intValue]]; + if ([originalData isNSNull]) originalData = [prefs objectForKey:@"nullValue"]; + + id editData = [[fieldEditor editWithObject:originalData fieldName:[columnDefinition objectForKey:@"name"] usingEncoding:[mySQLConnection encoding] isObjectBlob:isBlob diff --git a/Source/SPNotLoaded.h b/Source/SPNotLoaded.h new file mode 100644 index 00000000..e2026525 --- /dev/null +++ b/Source/SPNotLoaded.h @@ -0,0 +1,41 @@ +// +// $Id$ +// +// SPNotLoaded.h +// sequel-pro +// +// Created by Rowan Beentje on 07/10/2009. +// Copyright 2009 Rowan Beentje. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import <Cocoa/Cocoa.h> + +@interface SPNotLoaded : NSObject { + +} + ++ (SPNotLoaded *) notLoaded; + +@end + +// Also provide a method for testing objects +@interface NSObject (SPNotLoadedTest) + +- (BOOL) isSPNotLoaded; + +@end
\ No newline at end of file diff --git a/Source/SPNotLoaded.m b/Source/SPNotLoaded.m new file mode 100644 index 00000000..f8afda1b --- /dev/null +++ b/Source/SPNotLoaded.m @@ -0,0 +1,91 @@ +// +// $Id$ +// +// SPNotLoaded.m +// sequel-pro +// +// Created by Rowan Beentje on 07/10/2009. +// Copyright 2009 Rowan Beentje. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPNotLoaded.h" + +static SPNotLoaded *notLoaded = nil; + +@implementation SPNotLoaded + +// Return the singleton object ++ (SPNotLoaded *) notLoaded +{ + @synchronized(self) { + if (notLoaded == nil) { + [[self alloc] init]; + } + } + return notLoaded; +} + ++ (id) allocWithZone:(NSZone *)zone +{ + @synchronized(self) { + if (notLoaded == nil) { + return [super allocWithZone:zone]; + } + } + return notLoaded; +} + +- (id) init +{ + Class notLoadedClass = [self class]; + @synchronized(notLoadedClass) { + if (notLoaded == nil) { + if (self = [super init]) { + notLoaded = self; + } + } + } + return notLoaded; +} + +- (id) copyWithZone:(NSZone *)zone { return self; } + +- (id) retain { return self; } + +- (unsigned) retainCount { return UINT_MAX; } + +- (void) release {} + +- (id) autorelease { return self; } + +@end + + +/** + * This Category is intended to allow easy testing of all objects for SPNotLoaded. + */ +@implementation NSObject (SPNotLoadedTest) + +- (BOOL) isSPNotLoaded +{ + static id SPNotLoadedForComparison; + if (!SPNotLoadedForComparison) SPNotLoadedForComparison = [SPNotLoaded notLoaded]; + return (self == SPNotLoadedForComparison); +} + +@end
\ No newline at end of file diff --git a/Source/TableContent.m b/Source/TableContent.m index 6537f574..1fa9d193 100644 --- a/Source/TableContent.m +++ b/Source/TableContent.m @@ -46,6 +46,7 @@ #import "SPTooltip.h" #import "RegexKitLite.h" #import "SPContentFilterManager.h" +#import "SPNotLoaded.h" @implementation TableContent @@ -160,7 +161,7 @@ // If no table has been supplied, reset the view to a blank table and disabled elements. // [tableDataInstance tableEncoding] == nil indicates that an error occured while retrieving table data - if ( [[[tableDataInstance statusValues] objectForKey:@"Rows"] isKindOfClass:[NSNull class]] || [aTable isEqualToString:@""] || !aTable || [tableDataInstance tableEncoding] == nil) + if ( [[[tableDataInstance statusValues] objectForKey:@"Rows"] isNSNull] || [aTable isEqualToString:@""] || !aTable || [tableDataInstance tableEncoding] == nil) { // Empty the stored data arrays [tableValues removeAllObjects]; @@ -851,7 +852,7 @@ for ( i = 0 ; i < [dataColumns count] ; i++ ) { column = NSArrayObjectAtIndex(dataColumns, i); if ([column objectForKey:@"default"] == nil || [[column objectForKey:@"default"] isEqualToString:@"NULL"]) { - [newRow addObject:[prefs stringForKey:@"NullValue"]]; + [newRow addObject:[NSNull null]]; } else { [newRow addObject:[column objectForKey:@"default"]]; } @@ -909,15 +910,9 @@ for ( i = 0 ; i < [queryResult numOfRows] ; i++ ) { row = [queryResult fetchRowAsDictionary]; if ( [[row objectForKey:@"Extra"] isEqualToString:@"auto_increment"] ) { - [tempRow replaceObjectAtIndex:i withObject:[prefs stringForKey:@"NullValue"]]; + [tempRow replaceObjectAtIndex:i withObject:[NSNull null]]; } else if ( [tableDataInstance columnIsBlobOrText:[row objectForKey:@"Field"]] && [prefs boolForKey:@"LoadBlobsAsNeeded"] && dbDataRow) { - NSString *valueString = nil; - //if what we read from DB is NULL (NSNull), we replace it with the string NULL - if([[dbDataRow objectAtIndex:i] isKindOfClass:[NSNull class]]) - valueString = [prefs objectForKey:@"NullValue"]; - else - valueString = [dbDataRow objectAtIndex:i]; - [tempRow replaceObjectAtIndex:i withObject:valueString]; + [tempRow replaceObjectAtIndex:i withObject:[dbDataRow objectAtIndex:i]]; } } @@ -1005,8 +1000,10 @@ enumerator = [tableColumns objectEnumerator]; while ( (tableColumn = [enumerator nextObject]) ) { id o = [NSArrayObjectAtIndex(tableValues, i) objectAtIndex:[[tableColumn identifier] intValue]]; - if([o isKindOfClass:[NSNull class]]) + if([o isNSNull]) [tempRow addObject:@"NULL"]; + else if ([o isSPNotLoaded]) + [tempRow addObject:NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]; else if([o isKindOfClass:[NSString class]]) [tempRow addObject:[o description]]; else { @@ -1117,7 +1114,7 @@ NSDictionary *filterSettings = [NSDictionary dictionaryWithObjectsAndKeys: [refDictionary objectForKey:@"column"], @"filterField", targetFilterValue, @"filterValue", - ([targetFilterValue isEqualToString:[prefs objectForKey:@"NullValue"]]?@"IS NULL":nil), @"filterComparison", + ([targetFilterValue isNSNull]?@"IS NULL":nil), @"filterComparison", nil]; [self setFiltersToRestore:filterSettings]; @@ -1317,9 +1314,7 @@ NSAutoreleasePool *dataLoadingPool; NSProgressIndicator *dataLoadingIndicator = [tableDocumentInstance valueForKey:@"queryProgressBar"]; - id prefsNullValue = [[prefs objectForKey:@"NullValue"] retain]; BOOL prefsLoadBlobsAsNeeded = [prefs boolForKey:@"LoadBlobsAsNeeded"]; - Class nullClass = [NSNull class]; // Build up an array of which columns are blobs for faster iteration for ( i = 0; i < columnsCount ; i++ ) { @@ -1337,25 +1332,22 @@ // Loop through the result rows as they become available while (tempRow = [theResult fetchNextRowAsArray]) { - NSMutableArrayAddObject(tableValues, [NSMutableArray arrayWithCapacity:columnsCount]); - newRow = NSArrayObjectAtIndex(tableValues, rowsProcessed); - - // Process the retrieved row - for ( i = 0; i < columnsCount; i++ ) { - if ( [NSArrayObjectAtIndex(tempRow, i) isMemberOfClass:nullClass] ) { - NSMutableArrayAddObject(newRow, prefsNullValue); - } else { - NSMutableArrayAddObject(newRow, NSArrayObjectAtIndex(tempRow, i)); - } - } // Add values for hidden blob and text fields if appropriate if ( prefsLoadBlobsAsNeeded ) { + NSMutableArrayAddObject(tableValues, [NSMutableArray arrayWithCapacity:columnsCount]); + newRow = NSArrayObjectAtIndex(tableValues, rowsProcessed); for ( i = 0 ; i < columnsCount ; i++ ) { if ( [NSArrayObjectAtIndex(columnBlobStatuses, i) boolValue] ) { - [newRow replaceObjectAtIndex:i withObject:NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]; + [newRow addObject:[SPNotLoaded notLoaded]]; + } else { + NSMutableArrayAddObject(newRow, NSArrayObjectAtIndex(tempRow, i)); } } + + // Otherwise just add the new row + } else { + NSMutableArrayAddObject(tableValues, [NSMutableArray arrayWithArray:tempRow]); } // Update the progress bar as necessary, minimising updates @@ -1382,7 +1374,6 @@ [dataLoadingIndicator setIndeterminate:YES]; [columnBlobStatuses release]; - [prefsNullValue release]; } @@ -1419,11 +1410,10 @@ for ( i = 0 ; i < [dataColumns count] ; i++ ) { rowObject = [NSArrayObjectAtIndex(tableValues, currentlyEditingRow) objectAtIndex:i]; - // Add (not loaded) placeholders directly for easy comparsion when added - if (prefsLoadBlobsAsNeeded && !isEditingNewRow - && [rowObject isEqualToString:NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]) + // Add not loaded placeholders directly for easy comparison when added + if (prefsLoadBlobsAsNeeded && !isEditingNewRow && [rowObject isSPNotLoaded]) { - [fieldValues addObject:[NSString stringWithString:rowObject]]; + [fieldValues addObject:[SPNotLoaded notLoaded]]; continue; // Catch CURRENT_TIMESTAMP automatic updates - if the row is new and the cell value matches @@ -1435,7 +1425,7 @@ [rowValue setString:@"CURRENT_TIMESTAMP"]; // Convert the object to a string (here we can add special treatment for date-, number- and data-fields) - } else if ( [[rowObject description] isEqualToString:[prefs stringForKey:@"NullValue"]] + } else if ( [rowObject isNSNull] || ([rowObject isMemberOfClass:[NSString class]] && [[rowObject description] isEqualToString:@""]) ) { //NULL when user entered the nullValue string defined in the prefs or when a number field isn't set @@ -1467,7 +1457,7 @@ [fieldValues addObject:[NSString stringWithString:rowValue]]; } - // Use INSERT syntax when creating new rows - no need to do (not loaded) checking, as all values have been entered + // Use INSERT syntax when creating new rows - no need to do not loaded checking, as all values have been entered if ( isEditingNewRow ) { queryString = [NSString stringWithFormat:@"INSERT INTO %@ (%@) VALUES (%@)", [selectedTable backtickQuotedString], [[tableDataInstance columnNames] componentsJoinedAndBacktickQuoted], [fieldValues componentsJoinedByString:@","]]; @@ -1479,7 +1469,7 @@ for ( i = 0 ; i < [dataColumns count] ; i++ ) { // If data column loading is deferred and the value is the not loaded string, skip this cell - if (prefsLoadBlobsAsNeeded && [[fieldValues objectAtIndex:i] isEqualToString:NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]) continue; + if (prefsLoadBlobsAsNeeded && [[fieldValues objectAtIndex:i] isSPNotLoaded]) continue; if (firstCellOutput) [queryString appendString:@", "]; else firstCellOutput = YES; @@ -1653,7 +1643,7 @@ [value setString:[tempValue description]]; } - if ( [value isEqualToString:[prefs stringForKey:@"NullValue"]] ) { + if ( [value isNSNull] ) { [argument appendString:[NSString stringWithFormat:@"%@ IS NULL", [NSArrayObjectAtIndex(keys, i) backtickQuotedString]]]; } else { @@ -2100,14 +2090,23 @@ if ([theValue isKindOfClass:[NSData class]]) return [theValue shortStringRepresentationUsingEncoding:[mySQLConnection encoding]]; + if ([theValue isNSNull]) + return [prefs objectForKey:@"NullValue"]; + + if ([theValue isSPNotLoaded]) + return NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields"); + return theValue; } /** - * This function changes the text color of text/blob fields which are not yet loaded to gray + * This function changes the text color of text/blob fields which are null or not yet loaded to gray */ - (void)tableView:(CMCopyTable *)aTableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)aTableColumn row:(int)row { + + if (![cell respondsToSelector:@selector(setTextColor:)]) return; + // If user wants to edit 'cell' set text color to black and return to avoid // writing in gray if value was NULL if ( [aTableView editedColumn] == [[aTableColumn identifier] intValue] && [aTableView editedRow] == row) { @@ -2115,46 +2114,15 @@ return; } - NSDictionary *column = NSArrayObjectAtIndex(dataColumns, [[aTableColumn identifier] intValue]); + id theValue = NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, row), [[aTableColumn identifier] intValue]); - // For NULL cell's display the user's NULL value placeholder in grey to easily distinguish it from other values - if ([cell respondsToSelector:@selector(setTextColor:)]) { - - // Note that this approach of changing the color of NULL placeholders is dependent on the cell's value matching that - // of the user's NULL value preference which was set in the result array when it was retrieved (see fetchResultAsArray). - // Also, as an added measure check that the table column actually allows NULLs to make sure we don't change a cell that - // happens to have a value matching the NULL placeholder, but the column doesn't allow NULLs. - [cell setTextColor:([[cell stringValue] isEqualToString:[prefs objectForKey:@"NullValue"]] && [[column objectForKey:@"null"] boolValue]) ? [NSColor lightGrayColor] : [NSColor blackColor]]; - } + // For null cells and not loaded cells, display the contents in gray. + if ([theValue isNSNull] || [theValue isSPNotLoaded]) { + [cell setTextColor:[NSColor lightGrayColor]]; - // Check if loading of text/blob fields is disabled - // If not, all text fields are loaded and we don't have to make them gray - if ([prefs boolForKey:@"LoadBlobsAsNeeded"]) - { - // Make sure that the cell actually responds to setTextColor: - // In the future, we might use different cells for the table view - // that don't support this selector - if ([cell respondsToSelector:@selector(setTextColor:)]) - { - // Test if the current column is a text or a blob field - NSString *columnTypeGrouping = [column objectForKey:@"typegrouping"]; - - if ([columnTypeGrouping isEqualToString:@"textdata"] || [columnTypeGrouping isEqualToString:@"blobdata"]) { - - // now check if the field has been loaded already or not - if ([[cell stringValue] isEqualToString:NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields")]) - { - // change the text color of the cell to gray - [cell setTextColor:[NSColor lightGrayColor]]; - } - else { - // Change the text color back to black - // This is necessary because NSTableView reuses - // the NSCell to draw further rows in the column - [cell setTextColor:([[cell stringValue] isEqualToString:[prefs objectForKey:@"NullValue"]] && [[column objectForKey:@"null"] boolValue]) ? [NSColor lightGrayColor] : [NSColor blackColor]]; - } - } - } + // Otherwise, set the color to black - required as NSTableView reuses NSCells. + } else { + [cell setTextColor:[NSColor blackColor]]; } } @@ -2167,12 +2135,19 @@ isEditingRow = YES; currentlyEditingRow = rowIndex; } + + NSDictionary *column = NSArrayObjectAtIndex(dataColumns, [[aTableColumn identifier] intValue]); - if (anObject) + if (anObject) { + + // Restore NULLs if necessary + if ([anObject isEqualToString:[prefs objectForKey:@"NullValue"]] && [[column objectForKey:@"null"] boolValue]) + anObject = [NSNull null]; + [NSArrayObjectAtIndex(tableValues, rowIndex) replaceObjectAtIndex:[[aTableColumn identifier] intValue] withObject:anObject]; - else + } else { [NSArrayObjectAtIndex(tableValues, rowIndex) replaceObjectAtIndex:[[aTableColumn identifier] intValue] withObject:@""]; - + } } #pragma mark - @@ -2291,39 +2266,26 @@ */ - (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { - NSUInteger i; - // If the preference value for not showing blobs is set, check whether the row contains any blobs. - if ([prefs boolForKey:@"LoadBlobsAsNeeded"]) { + // If the selected cell hasn't been loaded, load it. + if ([NSArrayObjectAtIndex(NSArrayObjectAtIndex(tableValues, rowIndex), [[aTableColumn identifier] intValue]) isSPNotLoaded]) { - // If the table does contain blob or text fields, load the values ready for editing. - if ([self tableContainsBlobOrTextColumns]) { - NSString *wherePart = [NSString stringWithString:[self argumentForRow:[tableContentView selectedRow]]]; - - if ([wherePart length] == 0) return NO; - - // Only get the data for the selected column, not all of them - NSString *query = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@", [[[aTableColumn headerCell] stringValue] backtickQuotedString], [selectedTable backtickQuotedString], wherePart]; - - MCPResult *tempResult = [mySQLConnection queryString:query]; - - if (![tempResult numOfRows]) { - NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, - NSLocalizedString(@"Couldn't load the row. Reload the table to be sure that the row exists and use a primary key for your table.", @"message of panel when loading of row failed")); - return NO; - } - - NSArray *tempRow = [tempResult fetchRowAsArray]; - NSMutableArray *modifiedRow = [NSMutableArray array]; - - for (i = 0; i < [tempRow count]; i++) - { - [modifiedRow addObject:([[tempRow objectAtIndex:i] isMemberOfClass:[NSNull class]]) ? [prefs stringForKey:@"NullValue"] : [tempRow objectAtIndex:i]]; - } - - [[tableValues objectAtIndex:rowIndex] replaceObjectAtIndex:[[tableContentView tableColumns] indexOfObject:aTableColumn] withObject:[modifiedRow objectAtIndex:0]]; - [tableContentView reloadData]; + NSString *wherePart = [NSString stringWithString:[self argumentForRow:[tableContentView selectedRow]]]; + if ([wherePart length] == 0) return NO; + + // Only get the data for the selected column, not all of them + NSString *query = [NSString stringWithFormat:@"SELECT %@ FROM %@ WHERE %@", [[[aTableColumn headerCell] stringValue] backtickQuotedString], [selectedTable backtickQuotedString], wherePart]; + + MCPResult *tempResult = [mySQLConnection queryString:query]; + if (![tempResult numOfRows]) { + NSBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, tableWindow, self, nil, nil, nil, + NSLocalizedString(@"Couldn't load the row. Reload the table to be sure that the row exists and use a primary key for your table.", @"message of panel when loading of row failed")); + return NO; } + + NSArray *tempRow = [tempResult fetchRowAsArray]; + [[tableValues objectAtIndex:rowIndex] replaceObjectAtIndex:[[tableContentView tableColumns] indexOfObject:aTableColumn] withObject:[tempRow objectAtIndex:0]]; + [tableContentView reloadData]; } BOOL isBlob = [tableDataInstance columnIsBlobOrText:[[aTableColumn headerCell] stringValue]]; @@ -2335,7 +2297,10 @@ [fieldEditor setTextMaxLength:[[[aTableColumn dataCellForRow:rowIndex] formatter] textLimit]]; - id editData = [[fieldEditor editWithObject:[[tableValues objectAtIndex:rowIndex] objectAtIndex:[[aTableColumn identifier] intValue]] + id cellValue = [[tableValues objectAtIndex:rowIndex] objectAtIndex:[[aTableColumn identifier] intValue]]; + if ([cellValue isNSNull]) cellValue = [NSString stringWithString:[prefs objectForKey:@"nullValue"]]; + + id editData = [[fieldEditor editWithObject:cellValue fieldName:[[aTableColumn headerCell] stringValue] usingEncoding:[mySQLConnection encoding] isObjectBlob:isBlob @@ -2348,8 +2313,14 @@ isEditingRow = YES; currentlyEditingRow = rowIndex; } - - [[tableValues objectAtIndex:rowIndex] replaceObjectAtIndex:[[aTableColumn identifier] intValue] withObject:[editData copy]]; + + if ([editData isEqualToString:[prefs objectForKey:@"NullValue"]] + && [[NSArrayObjectAtIndex(dataColumns, [[aTableColumn identifier] intValue]) objectForKey:@"null"] boolValue]) + { + [editData release]; + editData = [[NSNull null] retain]; + } + [[tableValues objectAtIndex:rowIndex] replaceObjectAtIndex:[[aTableColumn identifier] intValue] withObject:[[editData copy] autorelease]]; } [fieldEditor release]; diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj index d678b0bb..adf4032a 100644 --- a/sequel-pro.xcodeproj/project.pbxproj +++ b/sequel-pro.xcodeproj/project.pbxproj @@ -141,6 +141,7 @@ 5822C9B51000DB2400DCC3D6 /* SPConnectionController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5822C9B41000DB2400DCC3D6 /* SPConnectionController.m */; }; 5822CAE110011C8000DCC3D6 /* ConnectionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5822CADF10011C8000DCC3D6 /* ConnectionView.xib */; }; 5822D3091061833C00CE2157 /* SPCSVParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 5822D3081061833C00CE2157 /* SPCSVParser.m */; }; + 582A01E9107C0C170027D42B /* SPNotLoaded.m in Sources */ = {isa = PBXBuildFile; fileRef = 582A01E8107C0C170027D42B /* SPNotLoaded.m */; }; 583B77D4103870C800B21F7E /* MCPStreamingResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 583B779810386B0200B21F7E /* MCPStreamingResult.m */; }; 5841423F0F97E11000A34B47 /* NoodleLineNumberView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5841423E0F97E11000A34B47 /* NoodleLineNumberView.m */; }; 584192A1101E57BB0089807F /* NSMutableArray-MultipleSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 584192A0101E57BB0089807F /* NSMutableArray-MultipleSort.m */; }; @@ -523,6 +524,8 @@ 5822CAE010011C8000DCC3D6 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/ConnectionView.xib; sourceTree = "<group>"; }; 5822D3071061833C00CE2157 /* SPCSVParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPCSVParser.h; sourceTree = "<group>"; }; 5822D3081061833C00CE2157 /* SPCSVParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPCSVParser.m; sourceTree = "<group>"; }; + 582A01E7107C0C170027D42B /* SPNotLoaded.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPNotLoaded.h; sourceTree = "<group>"; }; + 582A01E8107C0C170027D42B /* SPNotLoaded.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPNotLoaded.m; sourceTree = "<group>"; }; 583B779710386B0200B21F7E /* MCPStreamingResult.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; fileEncoding = 4; path = MCPStreamingResult.h; sourceTree = "<group>"; }; 583B779810386B0200B21F7E /* MCPStreamingResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MCPStreamingResult.m; sourceTree = "<group>"; }; 5841423D0F97E11000A34B47 /* NoodleLineNumberView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NoodleLineNumberView.h; sourceTree = "<group>"; }; @@ -1349,6 +1352,8 @@ B57747D80F7A8990003B34F9 /* SPWindowAdditions.m */, BC2C16D20FEBEDF10003993B /* SPDataAdditions.h */, BC2C16D30FEBEDF10003993B /* SPDataAdditions.m */, + 582A01E7107C0C170027D42B /* SPNotLoaded.h */, + 582A01E8107C0C170027D42B /* SPNotLoaded.m */, ); name = "Category Additions"; sourceTree = "<group>"; @@ -1773,6 +1778,7 @@ 5822D3091061833C00CE2157 /* SPCSVParser.m in Sources */, BC675A141072039C00C5ACD4 /* SPContentFilterManager.m in Sources */, 17292443107AC41000B21980 /* SPXMLExporter.m in Sources */, + 582A01E9107C0C170027D42B /* SPNotLoaded.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |