diff options
-rw-r--r-- | Source/SPCopyTable.h | 11 | ||||
-rw-r--r-- | Source/SPCopyTable.m | 39 | ||||
-rw-r--r-- | Source/SPCustomQuery.h | 1 | ||||
-rw-r--r-- | Source/SPCustomQuery.m | 255 | ||||
-rw-r--r-- | Source/SPTableContent.h | 1 | ||||
-rw-r--r-- | Source/SPTableContent.m | 324 |
6 files changed, 307 insertions, 324 deletions
diff --git a/Source/SPCopyTable.h b/Source/SPCopyTable.h index 498ba48c..6009e5d1 100644 --- a/Source/SPCopyTable.h +++ b/Source/SPCopyTable.h @@ -174,6 +174,17 @@ extern NSInteger SPEditCopyAsSQL; - (BOOL)isCellEditingMode; - (BOOL)isCellComplex; +/*! + @method shouldUseFieldEditorForRow:column: + @abstract Determine whether to trigger sheet editing or in-cell editing for a cell + @discussion Checks the column data type, and the cell contents if necessary, to check + the most appropriate editing type. + @param rowIndex The row in the table the cell is present in + @param colIndex The *original* column in the table the cell is present in (ie pre-reordering) + @result YES if sheet editing should be used, NO otherwise. +*/ +- (BOOL)shouldUseFieldEditorForRow:(NSUInteger)rowIndex column:(NSUInteger)colIndex; + - (IBAction)executeBundleItemForDataTable:(id)sender; - (void)selectTableRows:(NSArray*)rowIndices; diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m index c2cadf59..857b45c3 100644 --- a/Source/SPCopyTable.m +++ b/Source/SPCopyTable.m @@ -1155,6 +1155,45 @@ static const NSInteger kBlobAsImageFile = 4; } #pragma mark - +#pragma mark Field editing checks + +/** + * Determine whether to use the sheet for editing; do so if the multipleLineEditingButton is enabled, + * or if the column was a blob or a text, or if it contains linebreaks. + */ +- (BOOL)shouldUseFieldEditorForRow:(NSUInteger)rowIndex column:(NSUInteger)colIndex +{ + + // Retrieve the column definition + NSDictionary *columnDefinition = [[[self delegate] dataColumnDefinitions] objectAtIndex:colIndex]; + NSString *columnType = [columnDefinition objectForKey:@"typegrouping"]; + + // Return YES if the multiple line editing button is enabled - triggers sheet editing on all cells. +#ifndef SP_REFACTOR + if ([prefs boolForKey:SPEditInSheetEnabled]) return YES; +#endif + + // If the column is a BLOB or TEXT column, and not an enum, trigger sheet editing + BOOL isBlob = ([columnType isEqualToString:@"textdata"] || [columnType isEqualToString:@"blobdata"]); + if (isBlob && ![columnType isEqualToString:@"enum"]) return YES; + + // Otherwise, check the cell value for newlines. + id cellValue = [tableStorage cellDataAtRow:rowIndex column:colIndex]; + if ([cellValue isKindOfClass:[NSData class]]) { + cellValue = [[[NSString alloc] initWithData:cellValue encoding:[mySQLConnection stringEncoding]] autorelease]; + } + if (![cellValue isNSNull] + && [columnType isEqualToString:@"string"] + && [cellValue rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSLiteralSearch].location != NSNotFound) + { + return YES; + } + + // Otherwise, use standard editing + return NO; +} + +#pragma mark - #pragma mark Bundle Command Support - (IBAction)executeBundleItemForDataTable:(id)sender diff --git a/Source/SPCustomQuery.h b/Source/SPCustomQuery.h index 8d289f70..014fc599 100644 --- a/Source/SPCustomQuery.h +++ b/Source/SPCustomQuery.h @@ -278,6 +278,7 @@ - (void)commentOutCurrentQueryTakingSelection:(BOOL)takeSelection; - (NSString *)usedQuery; - (NSString *)argumentForRow:(NSUInteger)rowIndex ofTable:(NSString *)tableForColumn andDatabase:(NSString *)database includeBlobs:(BOOL)includeBlobs; +- (void)saveCellValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSUInteger)rowIndex; - (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSNumber *)columnIndex; - (NSUInteger)numberOfQueries; - (NSRange)currentQueryRange; diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m index 415552df..0d43d828 100644 --- a/Source/SPCustomQuery.m +++ b/Source/SPCustomQuery.m @@ -1890,6 +1890,104 @@ return [NSString stringWithFormat:@"WHERE (%@)", [argumentParts componentsJoinedByString:@" AND "]]; } +- (void)saveCellValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSUInteger)rowIndex +{ + NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[aTableColumn identifier] integerValue]]; + NSString *columnTypeGroup = [columnDefinition objectForKey:@"typegrouping"]; + + // Resolve the original table name for current column if AS was used + NSString *tableForColumn = [columnDefinition objectForKey:@"org_table"]; + + if(!tableForColumn || ![tableForColumn length]) { + [errorText setString:[NSString stringWithFormat:NSLocalizedString(@"Couldn't identify field origin unambiguously. The column '%@' contains data from more than one table.", @"Custom Query result editing error - could not identify a corresponding column"), [columnDefinition objectForKey:@"name"]]]; + NSBeep(); + return; + } + + // Resolve the original column name if AS was used + NSString *columnName = [columnDefinition objectForKey:@"org_name"]; + + // Check if the IDstring identifies the current field bijectively and get the WHERE clause + NSArray *editStatus = [self fieldEditStatusForRow:rowIndex andColumn:[aTableColumn identifier]]; + fieldIDQueryString = [editStatus objectAtIndex:1]; + NSInteger numberOfPossibleUpdateRows = [[editStatus objectAtIndex:0] integerValue]; + + if(numberOfPossibleUpdateRows == 1) { + + NSString *newObject = nil; + if ( [anObject isKindOfClass:[NSCalendarDate class]] ) { + newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; + } else if ( [anObject isKindOfClass:[NSNumber class]] ) { + newObject = [anObject stringValue]; + } else if ( [anObject isKindOfClass:[NSData class]] ) { + newObject = [NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:anObject]]; + } else { + if ( [[anObject description] isEqualToString:@"CURRENT_TIMESTAMP"] ) { + newObject = @"CURRENT_TIMESTAMP"; + } else if ([anObject isEqualToString:[prefs stringForKey:SPNullValue]] + || (([columnTypeGroup isEqualToString:@"float"] || [columnTypeGroup isEqualToString:@"integer"]) + && [[anObject description] isEqualToString:@""])) + { + newObject = @"NULL"; + } else if ([columnTypeGroup isEqualToString:@"geometry"]) { + newObject = [(NSString*)anObject getGeomFromTextString]; + } else if ([columnTypeGroup isEqualToString:@"bit"]) { + newObject = [NSString stringWithFormat:@"b'%@'", ((![[anObject description] length] || [[anObject description] isEqualToString:@"0"]) ? @"0" : [anObject description])]; + } else if ([columnTypeGroup isEqualToString:@"date"] + && [[anObject description] isEqualToString:@"NOW()"]) { + newObject = @"NOW()"; + } else { + newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; + } + } + + [mySQLConnection queryString: + [NSString stringWithFormat:@"UPDATE %@.%@ SET %@.%@.%@ = %@ %@ LIMIT 1", + [[columnDefinition objectForKey:@"db"] backtickQuotedString], [[columnDefinition objectForKey:@"org_table"] backtickQuotedString], + [[columnDefinition objectForKey:@"db"] backtickQuotedString], [[columnDefinition objectForKey:@"org_table"] backtickQuotedString], [columnName backtickQuotedString], newObject, fieldIDQueryString]]; + + // Check for errors while UPDATE + if ([mySQLConnection queryErrored]) { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"Couldn't write field.\nMySQL said: %@", @"message of panel when error while updating field to db"), [mySQLConnection getLastErrorMessage]]); + + return; + } + + // This shouldn't happen – for safety reasons + if ( ![mySQLConnection affectedRows] ) { +#ifndef SP_REFACTOR + if ( [prefs boolForKey:SPShowNoAffectedRowsError] ) { + SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + NSLocalizedString(@"The row was not written to the MySQL database. You probably haven't changed anything.\nReload the table to be sure that the row exists and use a primary key for your table.\n(This error can be turned off in the preferences.)", @"message of panel when no rows have been affected after writing to the db")); + } else { + NSBeep(); + } +#endif + return; + } + + // On success reload table data by executing the last query if reloading is enabled +#ifndef SP_REFACTOR + if ([prefs boolForKey:SPReloadAfterEditingRow]) { + reloadingExistingResult = YES; + [self storeCurrentResultViewForRestoration]; + [self performQueries:[NSArray arrayWithObject:lastExecutedQuery] withCallback:NULL]; + } else { +#endif + // otherwise, just update the data in the data storage + SPDataStorageReplaceObjectAtRowAndColumn(resultData, rowIndex, [[aTableColumn identifier] intValue], anObject); +#ifndef SP_REFACTOR + } +#endif + } else { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"Updating field content failed. Couldn't identify field origin unambiguously (%ld match%@). It's very likely that while editing this field of table `%@` was changed.", @"message of panel when error while updating field to db after enabling it"), + (numberOfPossibleUpdateRows<1)?0:numberOfPossibleUpdateRows, (numberOfPossibleUpdateRows>1)?@"es":@"", [columnDefinition objectForKey:@"org_table"]]); + + } +} + #pragma mark - #pragma mark TableView datasource methods @@ -1986,109 +2084,14 @@ { if (aTableView == customQueryView) { - NSDictionary *columnDefinition; - NSString *columnTypeGroup; - - // Retrieve the column defintion - for(id c in cqColumnDefinition) { - if([[c objectForKey:@"datacolumnindex"] isEqualToNumber:[aTableColumn identifier]]) { - columnDefinition = [NSDictionary dictionaryWithDictionary:c]; - columnTypeGroup = [columnDefinition objectForKey:@"typegrouping"]; - break; - } - } - - // Resolve the original table name for current column if AS was used - NSString *tableForColumn = [columnDefinition objectForKey:@"org_table"]; - - if(!tableForColumn || ![tableForColumn length]) { - [errorText setString:[NSString stringWithFormat:NSLocalizedString(@"Couldn't identify field origin unambiguously. The column '%@' contains data from more than one table.", @"Custom Query result editing error - could not identify a corresponding column"), [columnDefinition objectForKey:@"name"]]]; - NSBeep(); + // If the current cell should have been edited in a sheet, do nothing - field closing will have already + // updated the field. + if ([customQueryView shouldUseFieldEditorForRow:rowIndex column:[[aTableColumn identifier] integerValue]]) { return; } - // Resolve the original column name if AS was used - NSString *columnName = [columnDefinition objectForKey:@"org_name"]; - - // Check if the IDstring identifies the current field bijectively and get the WHERE clause - NSArray *editStatus = [self fieldEditStatusForRow:rowIndex andColumn:[aTableColumn identifier]]; - fieldIDQueryString = [editStatus objectAtIndex:1]; - NSInteger numberOfPossibleUpdateRows = [[editStatus objectAtIndex:0] integerValue]; - - if(numberOfPossibleUpdateRows == 1) { - - NSString *newObject = nil; - if ( [anObject isKindOfClass:[NSCalendarDate class]] ) { - newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; - } else if ( [anObject isKindOfClass:[NSNumber class]] ) { - newObject = [anObject stringValue]; - } else if ( [anObject isKindOfClass:[NSData class]] ) { - newObject = [NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:anObject]]; - } else { - if ( [[anObject description] isEqualToString:@"CURRENT_TIMESTAMP"] ) { - newObject = @"CURRENT_TIMESTAMP"; - } else if ([anObject isEqualToString:[prefs stringForKey:SPNullValue]] - || (([columnTypeGroup isEqualToString:@"float"] || [columnTypeGroup isEqualToString:@"integer"]) - && [[anObject description] isEqualToString:@""])) - { - newObject = @"NULL"; - } else if ([columnTypeGroup isEqualToString:@"geometry"]) { - newObject = [(NSString*)anObject getGeomFromTextString]; - } else if ([columnTypeGroup isEqualToString:@"bit"]) { - newObject = [NSString stringWithFormat:@"b'%@'", ((![[anObject description] length] || [[anObject description] isEqualToString:@"0"]) ? @"0" : [anObject description])]; - } else if ([columnTypeGroup isEqualToString:@"date"] - && [[anObject description] isEqualToString:@"NOW()"]) { - newObject = @"NOW()"; - } else { - newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; - } - } - - [mySQLConnection queryString: - [NSString stringWithFormat:@"UPDATE %@.%@ SET %@.%@.%@ = %@ %@ LIMIT 1", - [[columnDefinition objectForKey:@"db"] backtickQuotedString], [[columnDefinition objectForKey:@"org_table"] backtickQuotedString], - [[columnDefinition objectForKey:@"db"] backtickQuotedString], [[columnDefinition objectForKey:@"org_table"] backtickQuotedString], [columnName backtickQuotedString], newObject, fieldIDQueryString]]; - - // Check for errors while UPDATE - if ([mySQLConnection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, [tableDocumentInstance parentWindow], self, nil, nil, - [NSString stringWithFormat:NSLocalizedString(@"Couldn't write field.\nMySQL said: %@", @"message of panel when error while updating field to db"), [mySQLConnection getLastErrorMessage]]); - - return; - } - - // This shouldn't happen – for safety reasons - if ( ![mySQLConnection affectedRows] ) { -#ifndef SP_REFACTOR - if ( [prefs boolForKey:SPShowNoAffectedRowsError] ) { - SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, - NSLocalizedString(@"The row was not written to the MySQL database. You probably haven't changed anything.\nReload the table to be sure that the row exists and use a primary key for your table.\n(This error can be turned off in the preferences.)", @"message of panel when no rows have been affected after writing to the db")); - } else { - NSBeep(); - } -#endif - return; - } - - // On success reload table data by executing the last query if reloading is enabled -#ifndef SP_REFACTOR - if ([prefs boolForKey:SPReloadAfterEditingRow]) { - reloadingExistingResult = YES; - [self storeCurrentResultViewForRestoration]; - [self performQueries:[NSArray arrayWithObject:lastExecutedQuery] withCallback:NULL]; - } else { -#endif - // otherwise, just update the data in the data storage - SPDataStorageReplaceObjectAtRowAndColumn(resultData, rowIndex, [[aTableColumn identifier] intValue], anObject); -#ifndef SP_REFACTOR - } -#endif - } else { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, - [NSString stringWithFormat:NSLocalizedString(@"Updating field content failed. Couldn't identify field origin unambiguously (%ld match%@). It's very likely that while editing this field of table `%@` was changed.", @"message of panel when error while updating field to db after enabling it"), - (numberOfPossibleUpdateRows<1)?0:numberOfPossibleUpdateRows, (numberOfPossibleUpdateRows>1)?@"es":@"", [columnDefinition objectForKey:@"org_table"]]); - - } + // Otherwise trigger a save + [self saveCellValue:anObject forTableColumn:aTableColumn row:rowIndex]; } } @@ -2396,42 +2399,15 @@ // Check if the field can identified bijectively if ( aTableView == customQueryView ) { - - NSDictionary *columnDefinition; - - // Retrieve the column defintion - for(id c in cqColumnDefinition) { - if([[c objectForKey:@"datacolumnindex"] isEqualToNumber:[aTableColumn identifier]]) { - columnDefinition = [NSDictionary dictionaryWithDictionary:c]; - break; - } - } + NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[aTableColumn identifier] integerValue]]; // Check if current field is a blob BOOL isBlob = ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"textdata"] || [[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"blobdata"]); - // Determine whether to open the sheet for editing; do so if the multipleLineEditingButton is enabled, - // or if the column was a blob or a text, or if it contains linebreaks. - BOOL useFieldEditor = NO; -#ifndef SP_REFACTOR - if ([multipleLineEditingButton state] == NSOnState) useFieldEditor = YES; -#endif - if (!useFieldEditor && ![[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"enum"] && isBlob) useFieldEditor = YES; - if (!useFieldEditor) { - id cellValue = [resultData cellDataAtRow:rowIndex column:[[aTableColumn identifier] integerValue]]; - if ([cellValue isKindOfClass:[NSData class]]) { - cellValue = [[[NSString alloc] initWithData:cellValue encoding:[mySQLConnection stringEncoding]] autorelease]; - } - if (![cellValue isNSNull] - && [[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"string"] - && [cellValue rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSLiteralSearch].location != NSNotFound) - { - useFieldEditor = YES; - } - } - - if (useFieldEditor) { + // Open the editing sheet if required + if ([customQueryView shouldUseFieldEditorForRow:rowIndex column:[[aTableColumn identifier] integerValue]]) + { if (fieldEditor) [fieldEditor release], fieldEditor = nil; fieldEditor = [[SPFieldEditorController alloc] init]; @@ -3708,7 +3684,7 @@ BOOL isResultFieldEditable = ([contextInfo objectForKey:@"isFieldEditable"]) ? YES : NO; if(isResultFieldEditable) { - [self tableView:customQueryView setObjectValue:[[data copy] autorelease] forTableColumn:[customQueryView tableColumnWithIdentifier:[contextInfo objectForKey:@"column"]] row:row]; + [self saveCellValue:[[data copy] autorelease] forTableColumn:[customQueryView tableColumnWithIdentifier:[contextInfo objectForKey:@"column"]] row:row]; } } @@ -3884,23 +3860,10 @@ shouldBeginEditing = NO; } - NSString *fieldType = [columnDefinition objectForKey:@"typegrouping"]; isFieldEditable = shouldBeginEditing; - // Check if current field is a blob - BOOL isBlob = ([fieldType isEqualToString:@"textdata"] || [fieldType isEqualToString:@"blobdata"]); - - // Use the field editor sheet instead of inline editing if the target field is a text, blob, or binary - // type; if it contains linebreaks; or if the force-editing button is enabled. - BOOL useFieldEditor = NO; -#ifndef SP_REFACTOR - if ([multipleLineEditingButton state] == NSOnState) useFieldEditor = YES; -#endif - if (!useFieldEditor && fieldType && ![fieldType isEqualToString:@"enum"] && isBlob) useFieldEditor = YES; - if (!useFieldEditor && [[aFieldEditor string] rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound) useFieldEditor = YES; - // Open the field editor sheet if required - if (useFieldEditor) + if ([customQueryView shouldUseFieldEditorForRow:row column:column]) { [customQueryView setFieldEditorSelectedRange:[aFieldEditor selectedRange]]; diff --git a/Source/SPTableContent.h b/Source/SPTableContent.h index 2f571e70..4592de53 100644 --- a/Source/SPTableContent.h +++ b/Source/SPTableContent.h @@ -232,6 +232,7 @@ - (void)sortTableTaskWithColumn:(NSTableColumn *)tableColumn; - (void)showErrorSheetWith:(id)error; - (void)processFieldEditorResult:(id)data contextInfo:(NSDictionary*)contextInfo; +- (void)saveViewCellValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSUInteger)rowIndex; // Retrieving and setting table state - (NSString *) sortColumnName; diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 81bc44f6..58c6d10c 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -3256,11 +3256,15 @@ data = [[NSNull null] retain]; } if(isFieldEditable) { - if([tablesListInstance tableType] == SPTableTypeView) { + if ([tablesListInstance tableType] == SPTableTypeView) { + // since in a view we're editing a field rather than a row isEditingRow = NO; + // update the field and refresh the table - [self tableView:tableContentView setObjectValue:[[data copy] autorelease] forTableColumn:[tableContentView tableColumnWithIdentifier:[contextInfo objectForKey:@"column"]] row:row]; + [self saveViewCellValue:[[data copy] autorelease] forTableColumn:[tableContentView tableColumnWithIdentifier:[contextInfo objectForKey:@"column"]] row:row]; + + // Otherwise, in tables, save back to the row store } else { [tableValues replaceObjectInRow:row column:column withObject:[[data copy] autorelease]]; } @@ -3278,6 +3282,128 @@ [tableContentView editColumn:editedColumn row:row withEvent:nil select:YES]; } +- (void)saveViewCellValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSUInteger)rowIndex +{ + + // Field editing + NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[aTableColumn identifier] integerValue]]; + + // Resolve the original table name for current column if AS was used + NSString *tableForColumn = [columnDefinition objectForKey:@"org_table"]; + + if (!tableForColumn || ![tableForColumn length]) { + NSPoint pos = [NSEvent mouseLocation]; + pos.y -= 20; + [SPTooltip showWithObject:NSLocalizedString(@"Field is not editable. Field has no or multiple table or database origin(s).",@"field is not editable due to no table/database") + atLocation:pos + ofType:@"text"]; + NSBeep(); + return; + } + + // Resolve the original column name if AS was used + NSString *columnName = [columnDefinition objectForKey:@"org_name"]; + + [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Updating field data...", @"updating field task description")]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance]; + + [self storeCurrentDetailsForRestoration]; + + // Check if the IDstring identifies the current field bijectively and get the WHERE clause + NSArray *editStatus = [self fieldEditStatusForRow:rowIndex andColumn:[aTableColumn identifier]]; + NSString *fieldIDQueryStr = [editStatus objectAtIndex:1]; + NSInteger numberOfPossibleUpdateRows = [[editStatus objectAtIndex:0] integerValue]; + + if(numberOfPossibleUpdateRows == 1) { + + NSString *newObject = nil; + if ( [anObject isKindOfClass:[NSCalendarDate class]] ) { + newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; + } else if ( [anObject isKindOfClass:[NSNumber class]] ) { + newObject = [anObject stringValue]; + } else if ( [anObject isKindOfClass:[NSData class]] ) { + newObject = [NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:anObject]]; + } else { + if ( [[anObject description] isEqualToString:@"CURRENT_TIMESTAMP"] ) { + newObject = @"CURRENT_TIMESTAMP"; + } else if([anObject isEqualToString:[prefs stringForKey:SPNullValue]]) { + newObject = @"NULL"; + } else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"geometry"]) { + newObject = [(NSString*)anObject getGeomFromTextString]; + } else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"bit"]) { + newObject = [NSString stringWithFormat:@"b'%@'", ((![[anObject description] length] || [[anObject description] isEqualToString:@"0"]) ? @"0" : [anObject description])]; + } else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"date"] + && [[anObject description] isEqualToString:@"NOW()"]) { + newObject = @"NOW()"; + } else { + newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; + } + } + + [mySQLConnection queryString: + [NSString stringWithFormat:@"UPDATE %@.%@ SET %@.%@.%@ = %@ %@ LIMIT 1", + [[columnDefinition objectForKey:@"db"] backtickQuotedString], [tableForColumn backtickQuotedString], + [[columnDefinition objectForKey:@"db"] backtickQuotedString], [tableForColumn backtickQuotedString], [columnName backtickQuotedString], newObject, fieldIDQueryStr]]; + + + // Check for errors while UPDATE + if ([mySQLConnection queryErrored]) { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"Couldn't write field.\nMySQL said: %@", @"message of panel when error while updating field to db"), [mySQLConnection getLastErrorMessage]]); + + [tableDocumentInstance endTask]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; + return; + } + + + // This shouldn't happen – for safety reasons + if ( ![mySQLConnection affectedRows] ) { +#ifndef SP_REFACTOR + if ( [prefs boolForKey:SPShowNoAffectedRowsError] ) { + SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + NSLocalizedString(@"The row was not written to the MySQL database. You probably haven't changed anything.\nReload the table to be sure that the row exists and use a primary key for your table.\n(This error can be turned off in the preferences.)", @"message of panel when no rows have been affected after writing to the db")); + } else { + NSBeep(); + } +#endif + [tableDocumentInstance endTask]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; + return; + } + + } else { + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"Updating field content failed. Couldn't identify field origin unambiguously (%ld match%@). It's very likely that while editing this field the table `%@` was changed by an other user.", @"message of panel when error while updating field to db after enabling it"), + (long)numberOfPossibleUpdateRows, (numberOfPossibleUpdateRows>1)?@"es":@"", tableForColumn]); + + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; + [tableDocumentInstance endTask]; + return; + + } + + // Reload table after each editing due to complex declarations + if (isFirstChangeInView) { + + // Set up the table details for the new table, and trigger an interface update + // if the view was modified for the very first time + NSDictionary *tableDetails = [NSDictionary dictionaryWithObjectsAndKeys: + selectedTable, @"name", + [tableDataInstance columns], @"columns", + [tableDataInstance columnNames], @"columnNames", + [tableDataInstance getConstraints], @"constraints", + nil]; + [self performSelectorOnMainThread:@selector(setTableDetails:) withObject:tableDetails waitUntilDone:YES]; + isFirstChangeInView = NO; + } + + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; + [tableDocumentInstance endTask]; + + [self loadTableValues]; +} + #pragma mark - #pragma mark Filter Table @@ -4012,136 +4138,17 @@ else #endif if(aTableView == tableContentView) { - // If table data come from a view - if([tablesListInstance tableType] == SPTableTypeView) { - - // Field editing - // if (fieldIDQueryString == nil) return; - NSDictionary *columnDefinition; - - // Retrieve the column defintion - for(id c in cqColumnDefinition) { - if([[c objectForKey:@"datacolumnindex"] isEqualToNumber:[aTableColumn identifier]]) { - columnDefinition = [NSDictionary dictionaryWithDictionary:c]; - break; - } - } - - // Resolve the original table name for current column if AS was used - NSString *tableForColumn = [columnDefinition objectForKey:@"org_table"]; - - if(!tableForColumn || ![tableForColumn length]) { - NSPoint pos = [NSEvent mouseLocation]; - pos.y -= 20; - [SPTooltip showWithObject:NSLocalizedString(@"Field is not editable. Field has no or multiple table or database origin(s).",@"field is not editable due to no table/database") - atLocation:pos - ofType:@"text"]; - NSBeep(); - return; - } - - // Resolve the original column name if AS was used - NSString *columnName = [columnDefinition objectForKey:@"org_name"]; - - [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Updating field data...", @"updating field task description")]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance]; - - [self storeCurrentDetailsForRestoration]; - - // Check if the IDstring identifies the current field bijectively and get the WHERE clause - NSArray *editStatus = [self fieldEditStatusForRow:rowIndex andColumn:[aTableColumn identifier]]; - NSString *fieldIDQueryStr = [editStatus objectAtIndex:1]; - NSInteger numberOfPossibleUpdateRows = [[editStatus objectAtIndex:0] integerValue]; - - if(numberOfPossibleUpdateRows == 1) { - - NSString *newObject = nil; - if ( [anObject isKindOfClass:[NSCalendarDate class]] ) { - newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; - } else if ( [anObject isKindOfClass:[NSNumber class]] ) { - newObject = [anObject stringValue]; - } else if ( [anObject isKindOfClass:[NSData class]] ) { - newObject = [NSString stringWithFormat:@"X'%@'", [mySQLConnection prepareBinaryData:anObject]]; - } else { - if ( [[anObject description] isEqualToString:@"CURRENT_TIMESTAMP"] ) { - newObject = @"CURRENT_TIMESTAMP"; - } else if([anObject isEqualToString:[prefs stringForKey:SPNullValue]]) { - newObject = @"NULL"; - } else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"geometry"]) { - newObject = [(NSString*)anObject getGeomFromTextString]; - } else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"bit"]) { - newObject = [NSString stringWithFormat:@"b'%@'", ((![[anObject description] length] || [[anObject description] isEqualToString:@"0"]) ? @"0" : [anObject description])]; - } else if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"date"] - && [[anObject description] isEqualToString:@"NOW()"]) { - newObject = @"NOW()"; - } else { - newObject = [NSString stringWithFormat:@"'%@'", [mySQLConnection prepareString:[anObject description]]]; - } - } - - [mySQLConnection queryString: - [NSString stringWithFormat:@"UPDATE %@.%@ SET %@.%@.%@ = %@ %@ LIMIT 1", - [[columnDefinition objectForKey:@"db"] backtickQuotedString], [tableForColumn backtickQuotedString], - [[columnDefinition objectForKey:@"db"] backtickQuotedString], [tableForColumn backtickQuotedString], [columnName backtickQuotedString], newObject, fieldIDQueryStr]]; - - - // Check for errors while UPDATE - if ([mySQLConnection queryErrored]) { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), NSLocalizedString(@"Cancel", @"cancel button"), nil, [tableDocumentInstance parentWindow], self, nil, nil, - [NSString stringWithFormat:NSLocalizedString(@"Couldn't write field.\nMySQL said: %@", @"message of panel when error while updating field to db"), [mySQLConnection getLastErrorMessage]]); - - [tableDocumentInstance endTask]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; - return; - } - - - // This shouldn't happen – for safety reasons - if ( ![mySQLConnection affectedRows] ) { -#ifndef SP_REFACTOR - if ( [prefs boolForKey:SPShowNoAffectedRowsError] ) { - SPBeginAlertSheet(NSLocalizedString(@"Warning", @"warning"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, - NSLocalizedString(@"The row was not written to the MySQL database. You probably haven't changed anything.\nReload the table to be sure that the row exists and use a primary key for your table.\n(This error can be turned off in the preferences.)", @"message of panel when no rows have been affected after writing to the db")); - } else { - NSBeep(); - } -#endif - [tableDocumentInstance endTask]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; - return; - } - - } else { - SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [tableDocumentInstance parentWindow], self, nil, nil, - [NSString stringWithFormat:NSLocalizedString(@"Updating field content failed. Couldn't identify field origin unambiguously (%ld match%@). It's very likely that while editing this field the table `%@` was changed by an other user.", @"message of panel when error while updating field to db after enabling it"), - (long)numberOfPossibleUpdateRows, (numberOfPossibleUpdateRows>1)?@"es":@"", tableForColumn]); - - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; - [tableDocumentInstance endTask]; - return; - - } - - // Reload table after each editing due to complex declarations - if(isFirstChangeInView) { - // Set up the table details for the new table, and trigger an interface update - // if the view was modified for the very first time - NSDictionary *tableDetails = [NSDictionary dictionaryWithObjectsAndKeys: - selectedTable, @"name", - [tableDataInstance columns], @"columns", - [tableDataInstance columnNames], @"columnNames", - [tableDataInstance getConstraints], @"constraints", - nil]; - [self performSelectorOnMainThread:@selector(setTableDetails:) withObject:tableDetails waitUntilDone:YES]; - isFirstChangeInView = NO; - } - [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; - [tableDocumentInstance endTask]; - - [self loadTableValues]; + // If the current cell should have been edited in a sheet, do nothing - field closing will have already + // updated the field. + if ([tableContentView shouldUseFieldEditorForRow:rowIndex column:[[aTableColumn identifier] integerValue]]) { return; + } + // If table data comes from a view, save back to the view + if([tablesListInstance tableType] == SPTableTypeView) { + [self saveViewCellValue:anObject forTableColumn:aTableColumn row:rowIndex]; + return; } // Catch editing events in the row and if the row isn't currently being edited, @@ -4417,43 +4424,16 @@ [tableContentView reloadData]; } - BOOL isBlob = [tableDataInstance columnIsBlobOrText:[[aTableColumn headerCell] stringValue]]; - BOOL isFieldEditable = YES; - - // Retrieve the column defintion - NSDictionary *columnDefinition = nil; - for(id c in cqColumnDefinition) { - if([[c objectForKey:@"datacolumnindex"] isEqualToNumber:[aTableColumn identifier]]) { - columnDefinition = [NSDictionary dictionaryWithDictionary:c]; - break; - } - } - - // Determine whether to open the sheet for editing; do so if the multipleLineEditingButton is enabled, - // or if the column was a blob or a text, or if it contains linebreaks. - BOOL useFieldEditor = NO; -#ifndef SP_REFACTOR - if ([multipleLineEditingButton state] == NSOnState) useFieldEditor = YES; -#endif - if (!useFieldEditor && ![[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"enum"] && isBlob) useFieldEditor = YES; - if (!useFieldEditor) { - id cellValue = [tableValues cellDataAtRow:rowIndex column:[[aTableColumn identifier] integerValue]]; - if ([cellValue isKindOfClass:[NSData class]]) { - cellValue = [[[NSString alloc] initWithData:cellValue encoding:[mySQLConnection stringEncoding]] autorelease]; - } - if (![cellValue isNSNull] - && [[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"string"] - && [cellValue rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSLiteralSearch].location != NSNotFound) - { - useFieldEditor = YES; - } - } + // Open the editing sheet if required + if ([tableContentView shouldUseFieldEditorForRow:rowIndex column:[[aTableColumn identifier] integerValue]]) + { - // Open the sheet if required - if (useFieldEditor) { + // Retrieve the column definition + NSDictionary *columnDefinition = [cqColumnDefinition objectAtIndex:[[aTableColumn identifier] integerValue]]; + BOOL isBlob = [tableDataInstance columnIsBlobOrText:[[aTableColumn headerCell] stringValue]]; - // A table is per definitionem editable - isFieldEditable = YES; + // A table is per definition editable + BOOL isFieldEditable = YES; // Check for Views if field is editable if([tablesListInstance tableType] == SPTableTypeView) { @@ -4785,21 +4765,9 @@ } - // Use the field editor sheet instead of inline editing if the target field is a text, blob, or binary - // type; if it contains linebreaks; or if the force-editing button is enabled. - BOOL useFieldEditor = NO; - NSString *fieldType = [[tableDataInstance columnWithName:[[NSArrayObjectAtIndex([tableContentView tableColumns], column) headerCell] stringValue]] objectForKey:@"typegrouping"]; - -#ifndef SP_REFACTOR - if ([multipleLineEditingButton state] == NSOnState) useFieldEditor = YES; -#endif - - if (!useFieldEditor && fieldType && ![fieldType isEqualToString:@"enum"] && ([fieldType isEqualToString:@"textdata"] || [fieldType isEqualToString:@"blobdata"])) useFieldEditor = YES; - - if (!useFieldEditor && [[aFieldEditor string] rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location != NSNotFound) useFieldEditor = YES; - // Open the field editor sheet if required - if (useFieldEditor) { + if ([tableContentView shouldUseFieldEditorForRow:row column:column]) + { [tableContentView setFieldEditorSelectedRange:[aFieldEditor selectedRange]]; // Cancel editing |