diff options
Diffstat (limited to 'Source/SPFieldMapperController.m')
-rw-r--r-- | Source/SPFieldMapperController.m | 171 |
1 files changed, 122 insertions, 49 deletions
diff --git a/Source/SPFieldMapperController.m b/Source/SPFieldMapperController.m index 9bef9d03..6d5caf41 100644 --- a/Source/SPFieldMapperController.m +++ b/Source/SPFieldMapperController.m @@ -37,11 +37,10 @@ #import "SPCategoryAdditions.h" #import "RegexKitLite.h" #import "SPDatabaseData.h" +#import "SPFunctions.h" #import <SPMySQL/SPMySQL.h> -#define SP_NUMBER_OF_RECORDS_STRING NSLocalizedString(@"%ld of %@%lu records", @"Label showing the index of the selected CSV row") - // Constants static NSString *SPTableViewImportValueColumnID = @"import_value"; static NSString *SPTableViewTypeColumnID = @"type"; @@ -204,8 +203,8 @@ static NSUInteger SPSourceColumnTypeInteger = 1; [advancedUpdateView setHidden:YES]; [advancedInsertView setHidden:YES]; - [self changeHasHeaderCheckbox:self]; [self changeTableTarget:self]; + [self changeHasHeaderCheckbox:self]; [[self window] makeFirstResponder:fieldMapperTableView]; if([fieldMappingTableColumnNames count]) [fieldMapperTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; @@ -214,7 +213,8 @@ static NSUInteger SPSourceColumnTypeInteger = 1; [insertNULLValueButton setEnabled:([globalValuesTableView numberOfSelectedRows] == 1)]; [self updateFieldNameAlignment]; - + + [self validateImportButton]; } - (void)dealloc @@ -337,7 +337,18 @@ static NSUInteger SPSourceColumnTypeInteger = 1; - (BOOL)importFieldNamesHeader { - return ([importFieldNamesHeaderSwitch state] == NSOnState)?YES:NO; + if(importFieldNamesHeaderSwitch) { + return ([importFieldNamesHeaderSwitch state] == NSOnState); + } + else { + //this is a provisional field for the initial value of the checkbox until the window is actually loaded + return importFieldNamesHeader; + } +} + +- (BOOL)hasContentRows +{ + return (([fieldMappingImportArray count] - ([self importFieldNamesHeader]? 1 : 0)) > 0); } - (BOOL)insertRemainingRowsAfterUpdate @@ -607,9 +618,7 @@ static NSUInteger SPSourceColumnTypeInteger = 1; fieldMappingCurrentRow = 0; if (fieldMappingArray) SPClear(fieldMappingArray); [self setupFieldMappingArray]; - [rowDownButton setEnabled:NO]; - [rowUpButton setEnabled:([fieldMappingImportArray count] > 1)]; - [recordCountLabel setStringValue:[NSString stringWithFormat:SP_NUMBER_OF_RECORDS_STRING, (long)(fieldMappingCurrentRow+1), fieldMappingImportArrayIsPreview?@"first ":@"", (unsigned long)[fieldMappingImportArray count]]]; + [self updateRowNavigation]; [self updateFieldMappingButtonCell]; [self updateFieldMappingOperatorOptions]; @@ -714,7 +723,7 @@ static NSUInteger SPSourceColumnTypeInteger = 1; - (IBAction)changeFieldAlignment:(id)sender { - if(![fieldMappingImportArray count]) return; + if(![self hasContentRows]) return; NSUInteger i; NSInteger j; @@ -767,12 +776,8 @@ static NSUInteger SPSourceColumnTypeInteger = 1; [self updateFieldMappingButtonCell]; [fieldMapperTableView reloadData]; - - [recordCountLabel setStringValue:[NSString stringWithFormat:SP_NUMBER_OF_RECORDS_STRING, (long)(fieldMappingCurrentRow+1), fieldMappingImportArrayIsPreview?@"first ":@"", (unsigned long)[fieldMappingImportArray count]]]; - - // enable/disable buttons - [rowDownButton setEnabled:(fieldMappingCurrentRow != 0)]; - [rowUpButton setEnabled:(fieldMappingCurrentRow != (NSInteger)([fieldMappingImportArray count]-1))]; + + [self updateRowNavigation]; } - (IBAction)changeHasHeaderCheckbox:(id)sender @@ -780,15 +785,18 @@ static NSUInteger SPSourceColumnTypeInteger = 1; NSInteger i; NSArray *headerRow; - [matchingNameMenuItem setEnabled:([importFieldNamesHeaderSwitch state] == NSOnState)?YES:NO]; + [matchingNameMenuItem setEnabled:[self importFieldNamesHeader]]; // In New Table mode reset new field name according to importFieldNamesHeaderSwitch's state if (newTableMode) { [fieldMappingTableColumnNames removeAllObjects]; - if([importFieldNamesHeaderSwitch state] == NSOnState) { + if([self importFieldNamesHeader]) { headerRow = NSArrayObjectAtIndex(fieldMappingImportArray, 0); for (i = 0; i < numberOfImportColumns; i++) { - [fieldMappingTableColumnNames addObject:NSArrayObjectAtIndex(headerRow, i)]; + id headerCol = NSArrayObjectAtIndex(headerRow, i); + // we don't want a NSNull in the column headers to mess stuff up (issue #2375) + if([headerCol isNSNull]) headerCol = [prefs stringForKey:SPNullValue]; + [fieldMappingTableColumnNames addObject:headerCol]; } } else { for (i = 1; i <= numberOfImportColumns; i++) { @@ -797,6 +805,13 @@ static NSUInteger SPSourceColumnTypeInteger = 1; } [fieldMapperTableView reloadData]; } + + [self updateFieldMappingButtonCell]; + [fieldMapperTableView reloadData]; + + [self updateRowNavigation]; + + [self validateImportButton]; } - (IBAction)goBackToFileChooserFromPathControl:(id)sender @@ -850,7 +865,7 @@ static NSUInteger SPSourceColumnTypeInteger = 1; } // Step through the currently known data and get the types and values - NSUInteger i = (importFieldNamesHeader ? 1 : 0); + NSUInteger i = ([self importFieldNamesHeader] ? 1 : 0); NSArray *row; id col; for ( ; i < [fieldMappingImportArray count]; i++) { @@ -875,12 +890,14 @@ static NSUInteger SPSourceColumnTypeInteger = 1; [fieldMappingTableTypes removeAllObjects]; BOOL serverGreaterThanVersion4 = ([mySQLConnection serverMajorVersion] >= 5) ? YES : NO; - BOOL importFirstRowAsFieldNames = ([importFieldNamesHeaderSwitch state] == NSOnState); - NSString *headerName; + BOOL importFirstRowAsFieldNames = [self importFieldNamesHeader]; + NSArray *headerRow = NSArrayObjectAtIndex(fieldMappingImportArray, 0); for (columnCounter = 0; columnCounter < numberOfImportColumns; columnCounter++) { if (importFirstRowAsFieldNames) { - headerName = NSArrayObjectAtIndex(NSArrayObjectAtIndex(fieldMappingImportArray, 0), columnCounter); + id headerName = NSArrayObjectAtIndex(headerRow, columnCounter); + // we don't want a NSNull in the column headers to mess stuff up (issue #2375) + if([headerName isNSNull]) headerName = [prefs stringForKey:SPNullValue]; [fieldMappingTableColumnNames addObject:headerName]; } else { [fieldMappingTableColumnNames addObject:[NSString stringWithFormat:@"col_%ld", (long)(columnCounter + 1)]]; @@ -914,10 +931,8 @@ static NSUInteger SPSourceColumnTypeInteger = 1; fieldMappingCurrentRow = 0; if (fieldMappingArray) SPClear(fieldMappingArray); [self setupFieldMappingArray]; - [rowDownButton setEnabled:NO]; - [rowUpButton setEnabled:([fieldMappingImportArray count] > 1)]; - [recordCountLabel setStringValue:[NSString stringWithFormat:SP_NUMBER_OF_RECORDS_STRING, (long)(fieldMappingCurrentRow+1), fieldMappingImportArrayIsPreview?@"first ":@"", (unsigned long)[fieldMappingImportArray count]]]; - + [self updateRowNavigation]; + [self updateFieldMappingButtonCell]; [self updateFieldMappingOperatorOptions]; @@ -1043,9 +1058,7 @@ static NSUInteger SPSourceColumnTypeInteger = 1; NSArray *encodings = [databaseDataInstance getDatabaseCharacterSetEncodings]; NSString *utf8MenuItemTitle = nil; - if ([encodings count] > 0 - && ([mySQLConnection serverMajorVersion] > 4 - || ([mySQLConnection serverMajorVersion] == 4 && [mySQLConnection serverMinorVersion] >= 1))) + if ([encodings count] > 0 && ([mySQLConnection serverVersionIsGreaterThanOrEqualTo:4 minorVersion:1 releaseVersion:0])) { [[newTableInfoEncodingPopup menu] addItem:[NSMenuItem separatorItem]]; for (NSDictionary *encoding in encodings) { @@ -1102,8 +1115,8 @@ static NSUInteger SPSourceColumnTypeInteger = 1; // Add column placeholder NSInteger i = 0; - if([fieldMappingImportArray count] && [[fieldMappingImportArray objectAtIndex:0] count]) { - for(id item in [fieldMappingImportArray objectAtIndex:0]) { + if([self hasContentRows]) { + for(id item in [fieldMappingImportArray objectAtIndex:([self importFieldNamesHeader]? 1 : 0)]) { i++; if ([item isNSNull]) { [insertPullDownButton addItemWithTitle:[NSString stringWithFormat:@"%li. <%@>", (long)i, [prefs objectForKey:SPNullValue]]]; @@ -1385,7 +1398,7 @@ static NSUInteger SPSourceColumnTypeInteger = 1; - (void)matchHeaderNames { - if(![fieldMappingImportArray count]) return; + if(![self hasContentRows]) return; NSMutableArray *fileHeaderNames = [NSMutableArray array]; [fileHeaderNames setArray:NSArrayObjectAtIndex(fieldMappingImportArray, 0)]; @@ -1478,9 +1491,9 @@ static NSUInteger SPSourceColumnTypeInteger = 1; if (!fieldMappingArray) { fieldMappingArray = [[NSMutableArray alloc] init]; + NSArray *currentRowValues = NSArrayObjectAtIndex(fieldMappingImportArray, fieldMappingCurrentRow); for (i = 0; i < [fieldMappingTableColumnNames count]; i++) { - if (i < [NSArrayObjectAtIndex(fieldMappingImportArray, fieldMappingCurrentRow) count] - && ![NSArrayObjectAtIndex(NSArrayObjectAtIndex(fieldMappingImportArray, fieldMappingCurrentRow), i) isNSNull]) { + if (i < [currentRowValues count]) { value = i; } else { value = 0; @@ -1499,7 +1512,7 @@ static NSUInteger SPSourceColumnTypeInteger = 1; - (void)updateFieldMappingButtonCell { NSUInteger i; - if([fieldMappingImportArray count] == 0) return; + if(![self hasContentRows]) return; [fieldMappingButtonOptions setArray:[fieldMappingImportArray objectAtIndex:fieldMappingCurrentRow]]; for (i = 0; i < [fieldMappingButtonOptions count]; i++) { if ([[fieldMappingButtonOptions objectAtIndex:i] isNSNull]) @@ -1552,33 +1565,75 @@ static NSUInteger SPSourceColumnTypeInteger = 1; } #endif - // Set matching names only if csv file has an header - if(importFieldNamesHeader && alignment == 2) - [alignByPopup selectItemWithTag:2]; - else if(!importFieldNamesHeader && alignment == 2) - [alignByPopup selectItemWithTag:0]; - else + if(alignment == 2) { + // Set matching names only if csv file has a header + if([self importFieldNamesHeader]) + [alignByPopup selectItemWithTag:2]; + else + [alignByPopup selectItemWithTag:0]; + } + else { [alignByPopup selectItemWithTag:alignment]; + } [self changeFieldAlignment:nil]; } +- (void)updateRowNavigation +{ + int firstRowIsHeader = [self importFieldNamesHeader] ? 1 : 0; + + // if the first row becomes a header row it can no longer be a content row + if(!fieldMappingCurrentRow && firstRowIsHeader && [self hasContentRows]) { + fieldMappingCurrentRow++; + [self updateFieldMappingButtonCell]; + [fieldMapperTableView reloadData]; + } + + NSUInteger countRows = [fieldMappingImportArray count]; + [rowDownButton setEnabled:(fieldMappingCurrentRow > firstRowIsHeader)]; + [rowUpButton setEnabled:(SPIntS2U(fieldMappingCurrentRow) < (countRows - 1))]; + + long displayedCurrentRow = fieldMappingCurrentRow+1-firstRowIsHeader; + unsigned long displayedTotalRows = (countRows? (countRows - firstRowIsHeader) : 0); //avoid negative values on empty array + + NSString *fmt; + if(fieldMappingImportArrayIsPreview) + fmt = NSLocalizedString(@"%ld of first %lu record(s)", @"Label showing the index of the selected CSV row (csv partially loaded)"); + else + fmt = NSLocalizedString(@"%ld of %lu record(s)", @"Label showing the index of the selected CSV row"); + + [recordCountLabel setStringValue:[NSString stringWithFormat:fmt, displayedCurrentRow, displayedTotalRows]]; +} + - (void)validateImportButton { BOOL enableImportButton = YES; - + if (newTableMode) { if (![tablesListInstance isTableNameValid:[newTableNameTextField stringValue] forType:SPTableTypeTable ignoringSelectedTable:NO]) { [importButton setEnabled:NO]; return; } + + BOOL hasImportColumns = NO; for (NSUInteger i = 0; i < [fieldMappingTableColumnNames count]; i++) { - if (![[fieldMappingTableColumnNames objectAtIndex:i] length] && [doImportKey isEqualToNumber:[fieldMappingOperatorArray objectAtIndex:i]]) { + NSString *colName = [fieldMappingTableColumnNames objectAtIndex:i]; + BOOL shouldImport = [doImportKey isEqualToNumber:[fieldMappingOperatorArray objectAtIndex:i]]; + if (shouldImport && ![colName length]) { [importButton setEnabled:NO]; return; } + if(!hasImportColumns && shouldImport) hasImportColumns = YES; + } + + if(!hasImportColumns) { + // new table without any columns is not valid + [importButton setEnabled:NO]; + return; } + for (NSString* fieldType in fieldMappingTableTypes) { if(![fieldType length]) { [importButton setEnabled:NO]; @@ -1586,6 +1641,13 @@ static NSUInteger SPSourceColumnTypeInteger = 1; } } } + else { + // we don't want to create a new table and have no rows to import either => can't import nothing + if(![self hasContentRows]) { + [importButton setEnabled:NO]; + return; + } + } if ([[self selectedImportMethod] isEqualToString:@"UPDATE"]) { enableImportButton = NO; @@ -1606,15 +1668,21 @@ static NSUInteger SPSourceColumnTypeInteger = 1; */ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem { - NSInteger row = [fieldMapperTableView selectedRow]; // Hide/display Remove New Column menu item [[[fieldMapperTableView menu] itemAtIndex:3] setHidden:([toBeEditedRowIndexes containsIndex:row]) ? NO : YES]; if (newTableMode && [menuItem action] == @selector(setAllTypesTo:)) { - NSString *orgTitle = [[menuItem title] substringToIndex:[[menuItem title] rangeOfString:@":"].location]; - [menuItem setTitle:[NSString stringWithFormat:@"%@: %@", orgTitle, [fieldMappingTableTypes objectAtIndex:row]]]; + if(row > -1) { // row == -1 on empty selection + NSString *orgTitle = [[menuItem title] substringToIndex:[[menuItem title] rangeOfString:@":"].location]; + [menuItem setTitle:[NSString stringWithFormat:@"%@: %@", orgTitle, [fieldMappingTableTypes objectAtIndex:row]]]; + [menuItem setHidden:NO]; + } + else { + [menuItem setHidden:YES]; + return NO; + } } else if (!newTableMode && [menuItem action] == @selector(insertNULLValue:)) { return ([[globalValuesTableView selectedRowIndexes] count] == 1) ? YES : NO; @@ -1700,11 +1768,16 @@ static NSUInteger SPSourceColumnTypeInteger = 1; } else if([importFieldNamesHeaderSwitch state] == NSOffState) { - if([NSArrayObjectAtIndex(fieldMappingArray, rowIndex) unsignedIntegerValue]>=[NSArrayObjectAtIndex(fieldMappingImportArray, 0) count]) - return NSArrayObjectAtIndex(fieldMappingGlobalValues, [NSArrayObjectAtIndex(fieldMappingArray, rowIndex) integerValue]); + NSUInteger colIndex = [NSArrayObjectAtIndex(fieldMappingArray, rowIndex) unsignedIntegerValue]; + NSString *retval; + if(colIndex >= [NSArrayObjectAtIndex(fieldMappingImportArray, 0) count]) + retval = NSArrayObjectAtIndex(fieldMappingGlobalValues, colIndex); else - return NSArrayObjectAtIndex(NSArrayObjectAtIndex(fieldMappingImportArray, fieldMappingCurrentRow), [NSArrayObjectAtIndex(fieldMappingArray, rowIndex) integerValue]); + retval = NSArrayObjectAtIndex(NSArrayObjectAtIndex(fieldMappingImportArray, fieldMappingCurrentRow), colIndex); + + if([retval isNSNull]) retval = NSLocalizedString(@"Value will be imported as MySQL NULL", @"CSV Field Mapping : Table View : Tooltip for fields with NULL value"); + return retval; } } |