diff options
author | Bibiko <bibiko@eva.mpg.de> | 2010-09-27 09:25:46 +0000 |
---|---|---|
committer | Bibiko <bibiko@eva.mpg.de> | 2010-09-27 09:25:46 +0000 |
commit | fa2b337d82047f8ec3474dc67b125f34f7a49bfc (patch) | |
tree | 88a6262f8deb53b336ac2f7da57970417cf0081a /Source/SPTableContent.m | |
parent | adf5d76a879d7e5474cf18843381f1dc553948a2 (diff) | |
download | sequelpro-fa2b337d82047f8ec3474dc67b125f34f7a49bfc.tar.gz sequelpro-fa2b337d82047f8ec3474dc67b125f34f7a49bfc.tar.bz2 sequelpro-fa2b337d82047f8ec3474dc67b125f34f7a49bfc.zip |
• initial sketch approach to allow to write complex WHERE clauses by using a table approach
- not yet activated
Diffstat (limited to 'Source/SPTableContent.m')
-rw-r--r-- | Source/SPTableContent.m | 571 |
1 files changed, 359 insertions, 212 deletions
diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m index 59a9e8c4..b7bcd0f9 100644 --- a/Source/SPTableContent.m +++ b/Source/SPTableContent.m @@ -68,8 +68,9 @@ tableValues = [[SPDataStorage alloc] init]; tableRowsCount = 0; previousTableRowsCount = 0; - dataColumns = [[NSMutableArray alloc] init]; - oldRow = [[NSMutableArray alloc] init]; + dataColumns = [[NSMutableArray alloc] init]; + oldRow = [[NSMutableArray alloc] init]; + filterTableData = [[NSMutableDictionary alloc] initWithCapacity:1]; selectedTable = nil; sortCol = nil; @@ -265,7 +266,7 @@ NSNumber *colWidth, *sortColumnNumberToRestore = nil; NSArray *columnNames; NSDictionary *columnDefinition; - NSTableColumn *theCol; + NSTableColumn *theCol, *filterCol; BOOL enableInteraction = ![[tableDocumentInstance selectedToolbarItemIdentifier] isEqualToString:SPMainToolbarTableContent] || ![tableDocumentInstance isWorking]; if (!tableDetails) { @@ -368,6 +369,10 @@ while ([[tableContentView tableColumns] count]) { [tableContentView removeTableColumn:NSArrayObjectAtIndex([tableContentView tableColumns], 0)]; } + while ([[filterTableView tableColumns] count]) { + [filterTableView removeTableColumn:NSArrayObjectAtIndex([filterTableView tableColumns], 0)]; + } + [filterTableData removeAllObjects]; // Retrieve the field names and types for this table from the data cache. This is used when requesting all data as part // of the fieldListForQuery method, and also to decide whether or not to preserve the current filter/sort settings. @@ -398,7 +403,7 @@ NSFont *tableFont = [NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPGlobalResultTableFont]]; [tableContentView setRowHeight:2.0f+NSSizeToCGSize([[NSString stringWithString:@"{ǞṶḹÜ∑zgyf"] sizeWithAttributes:[NSDictionary dictionaryWithObject:tableFont forKey:NSFontAttributeName]]).height]; - // Add the new columns to the table + // Add the new columns to the table and filterTable for ( i = 0 ; i < [dataColumns count] ; i++ ) { columnDefinition = NSArrayObjectAtIndex(dataColumns, i); @@ -407,6 +412,22 @@ [[theCol headerCell] setStringValue:[columnDefinition objectForKey:@"name"]]; [theCol setEditable:YES]; + // Set up column for filterTable + filterCol = [[NSTableColumn alloc] initWithIdentifier:[columnDefinition objectForKey:@"datacolumnindex"]]; + [[filterCol headerCell] setStringValue:[columnDefinition objectForKey:@"name"]]; + [filterCol setEditable:YES]; + SPTextAndLinkCell *filterDataCell = [[[SPTextAndLinkCell alloc] initTextCell:@""] autorelease]; + [filterDataCell setEditable:YES]; + [filterCol setDataCell:filterDataCell]; + [filterTableView addTableColumn:filterCol]; + [filterCol release]; + + [filterTableData setObject:[NSMutableDictionary dictionaryWithObjectsAndKeys: + [columnDefinition objectForKey:@"name"], @"name", + [columnDefinition objectForKey:@"typegrouping"], @"typegrouping", + [NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil], @"filter", + nil] forKey:[columnDefinition objectForKey:@"datacolumnindex"]]; + // Set up the data cell depending on the column type id dataCell; if ([[columnDefinition objectForKey:@"typegrouping"] isEqualToString:@"enum"]) { @@ -466,6 +487,9 @@ [theCol release]; } + [filterTableView setDelegate:self]; + [filterTableView setDataSource:self]; + // If the table has been reloaded and the previously selected sort column is still present, reselect it. if (sortColumnNumberToRestore) { theCol = [tableContentView tableColumnWithIdentifier:sortColumnNumberToRestore]; @@ -542,6 +566,7 @@ if (!previousTableRowsCount) { [self clearTableValues]; } + [filterTableView reloadData]; } @@ -2773,6 +2798,31 @@ } #pragma mark - +#pragma mark Filter Table + +- (IBAction)tableFilterExecute:(id)sender +{ + +} + +- (IBAction)tableFilterClear:(id)sender +{ + + for(NSNumber *col in [filterTableData allKeys]) + [[filterTableData objectForKey:col] setObject:[NSMutableArray arrayWithObjects:@"", @"", @"", @"", @"", @"", @"", @"", @"", @"", nil] forKey:@"filter"]; + + [filterTableView reloadData]; + [filterTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; + [filterTableWhereClause setString:@""]; + +} + +- (IBAction)showTableFilter:(id)sender +{ + [filterTableWindow makeKeyAndOrderFront:nil]; +} + +#pragma mark - #pragma mark Retrieving and setting table state /** @@ -3069,82 +3119,100 @@ - (NSString *)tableView:(NSTableView *)aTableView toolTipForCell:(SPTextAndLinkCell *)aCell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation { - if([[aCell stringValue] length] < 2 || [tableDocumentInstance isWorking]) return nil; + if(aTableView == filterTableView) { + return nil; + } + else if(aTableView == tableContentView) { - NSImage *image; + if([[aCell stringValue] length] < 2 || [tableDocumentInstance isWorking]) return nil; - NSPoint pos = [NSEvent mouseLocation]; - pos.y -= 20; + NSImage *image; - // Try to get the original data. If not possible return nil. - // @try clause is used due to the multifarious cases of - // possible exceptions (eg for reloading tables etc.) - id theValue; - @try{ - theValue = [tableValues cellDataAtRow:row column:[[aTableColumn identifier] integerValue]]; - } - @catch(id ae) { - return nil; - } + NSPoint pos = [NSEvent mouseLocation]; + pos.y -= 20; - // Get the original data for trying to display the blob data as an image - if ([theValue isKindOfClass:[NSData class]]) { - image = [[[NSImage alloc] initWithData:theValue] autorelease]; - if(image) { - [SPTooltip showWithObject:image atLocation:pos ofType:@"image"]; + // Try to get the original data. If not possible return nil. + // @try clause is used due to the multifarious cases of + // possible exceptions (eg for reloading tables etc.) + id theValue; + @try{ + theValue = [tableValues cellDataAtRow:row column:[[aTableColumn identifier] integerValue]]; + } + @catch(id ae) { return nil; } - } - // Show the cell string value as tooltip (including line breaks and tabs) - // by using the cell's font - [SPTooltip showWithObject:[aCell stringValue] - atLocation:pos - ofType:@"text" - displayOptions:[NSDictionary dictionaryWithObjectsAndKeys: - [[aCell font] familyName], @"fontname", - [NSString stringWithFormat:@"%f",[[aCell font] pointSize]], @"fontsize", - nil]]; + // Get the original data for trying to display the blob data as an image + if ([theValue isKindOfClass:[NSData class]]) { + image = [[[NSImage alloc] initWithData:theValue] autorelease]; + if(image) { + [SPTooltip showWithObject:image atLocation:pos ofType:@"image"]; + return nil; + } + } + + // Show the cell string value as tooltip (including line breaks and tabs) + // by using the cell's font + [SPTooltip showWithObject:[aCell stringValue] + atLocation:pos + ofType:@"text" + displayOptions:[NSDictionary dictionaryWithObjectsAndKeys: + [[aCell font] familyName], @"fontname", + [NSString stringWithFormat:@"%f",[[aCell font] pointSize]], @"fontsize", + nil]]; - return nil; + return nil; + } } -- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView +- (NSInteger)numberOfRowsInTableView:(SPCopyTable *)aTableView { - return tableRowsCount; + if(aTableView == filterTableView) { + return [[[filterTableData objectForKey:[NSNumber numberWithInteger:0]] objectForKey:@"filter"] count]; + } + else if(aTableView == tableContentView) { + return tableRowsCount; + } } - (id)tableView:(SPCopyTable *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { - NSUInteger columnIndex = [[aTableColumn identifier] integerValue]; - id theValue = nil; - - // While the table is being loaded, additional validation is required - data - // locks must be used to avoid crashes, and indexes higher than the available - // rows or columns may be requested. Return "..." to indicate loading in these - // cases. - if (isWorking) { - pthread_mutex_lock(&tableValuesLock); - if (rowIndex < tableRowsCount && columnIndex < [tableValues columnCount]) { - theValue = [[SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex) copy] autorelease]; - } - pthread_mutex_unlock(&tableValuesLock); - if (!theValue) return @"..."; - } else { - theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex); + if(aTableView == filterTableView) { + return NSArrayObjectAtIndex([[filterTableData objectForKey:[aTableColumn identifier]] objectForKey:@"filter"], rowIndex); } + else if(aTableView == tableContentView) { + + NSUInteger columnIndex = [[aTableColumn identifier] integerValue]; + id theValue = nil; + + // While the table is being loaded, additional validation is required - data + // locks must be used to avoid crashes, and indexes higher than the available + // rows or columns may be requested. Return "..." to indicate loading in these + // cases. + if (isWorking) { + pthread_mutex_lock(&tableValuesLock); + if (rowIndex < tableRowsCount && columnIndex < [tableValues columnCount]) { + theValue = [[SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex) copy] autorelease]; + } + pthread_mutex_unlock(&tableValuesLock); + + if (!theValue) return @"..."; + } else { + theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex); + } - if ([theValue isNSNull]) - return [prefs objectForKey:SPNullValue]; + if ([theValue isNSNull]) + return [prefs objectForKey:SPNullValue]; - if ([theValue isKindOfClass:[NSData class]]) - return [theValue shortStringRepresentationUsingEncoding:[mySQLConnection stringEncoding]]; + if ([theValue isKindOfClass:[NSData class]]) + return [theValue shortStringRepresentationUsingEncoding:[mySQLConnection stringEncoding]]; - if ([theValue isSPNotLoaded]) - return NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields"); + if ([theValue isSPNotLoaded]) + return NSLocalizedString(@"(not loaded)", @"value shown for hidden blob and text fields"); - return theValue; + return theValue; + } } /** @@ -3152,199 +3220,212 @@ */ - (void)tableView:(SPCopyTable *)aTableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)aTableColumn row:(NSInteger)rowIndex { - if (![cell respondsToSelector:@selector(setTextColor:)]) return; - NSUInteger columnIndex = [[aTableColumn identifier] integerValue]; - id theValue = nil; + if(aTableView == filterTableView) { + return; + } + else if(aTableView == tableContentView) { - // While the table is being loaded, additional validation is required - data - // locks must be used to avoid crashes, and indexes higher than the available - // rows or columns may be requested. Use gray to indicate loading in these cases. - if (isWorking) { - pthread_mutex_lock(&tableValuesLock); - if (rowIndex < tableRowsCount && columnIndex < [tableValues columnCount]) { + if (![cell respondsToSelector:@selector(setTextColor:)]) return; + + NSUInteger columnIndex = [[aTableColumn identifier] integerValue]; + id theValue = nil; + + // While the table is being loaded, additional validation is required - data + // locks must be used to avoid crashes, and indexes higher than the available + // rows or columns may be requested. Use gray to indicate loading in these cases. + if (isWorking) { + pthread_mutex_lock(&tableValuesLock); + if (rowIndex < tableRowsCount && columnIndex < [tableValues columnCount]) { + theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex); + } + pthread_mutex_unlock(&tableValuesLock); + + if (!theValue) { + [cell setTextColor:[NSColor lightGrayColor]]; + return; + } + } else { theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex); } - pthread_mutex_unlock(&tableValuesLock); - if (!theValue) { - [cell setTextColor:[NSColor lightGrayColor]]; + // 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] != -1 + && [aTableView editedRow] == rowIndex + && [[NSArrayObjectAtIndex([aTableView tableColumns], [aTableView editedColumn]) identifier] integerValue] == columnIndex) { + [cell setTextColor:[NSColor blackColor]]; return; } - } else { - theValue = SPDataStorageObjectAtRowAndColumn(tableValues, rowIndex, columnIndex); - } - - // 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] != -1 - && [aTableView editedRow] == rowIndex - && [[NSArrayObjectAtIndex([aTableView tableColumns], [aTableView editedColumn]) identifier] integerValue] == columnIndex) { - [cell setTextColor:[NSColor blackColor]]; - return; - } - // For null cells and not loaded cells, display the contents in gray. - if ([theValue isNSNull] || [theValue isSPNotLoaded]) { - [cell setTextColor:[NSColor lightGrayColor]]; + // For null cells and not loaded cells, display the contents in gray. + if ([theValue isNSNull] || [theValue isSPNotLoaded]) { + [cell setTextColor:[NSColor lightGrayColor]]; - // Otherwise, set the color to black - required as NSTableView reuses NSCells. - } else { - [cell setTextColor:[NSColor blackColor]]; + // Otherwise, set the color to black - required as NSTableView reuses NSCells. + } else { + [cell setTextColor:[NSColor blackColor]]; + } } } - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex { + if(aTableView == filterTableView) { + [[[filterTableData objectForKey:[aTableColumn identifier]] objectForKey:@"filter"] replaceObjectAtIndex:rowIndex withObject:(NSString*)anObject]; + [self updateFilterTableClause]; + return; + } + else if(aTableView == tableContentView) { + // If table data come from a view + if([tablesListInstance tableType] == SPTableTypeView) { - // If table data come from a view - if([tablesListInstance tableType] == SPTableTypeView) { - - // Field editing - // if (fieldIDQueryString == nil) return; - NSDictionary *columnDefinition; + // 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; + // 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"]; + // 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; - } + 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"]; + // 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]; + [tableDocumentInstance startTaskWithDescription:NSLocalizedString(@"Updating field data...", @"updating field task description")]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryWillBePerformed" object:tableDocumentInstance]; - [self storeCurrentDetailsForRestoration]; + [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]; + // 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) { + 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:@"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 { + 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:@"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]]; + [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]]); + // 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; - } + [tableDocumentInstance endTask]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; + return; + } - // This shouldn't happen – for safety reasons - if ( ![mySQLConnection affectedRows] ) { - 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(); + // This shouldn't happen – for safety reasons + if ( ![mySQLConnection affectedRows] ) { + 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(); + } + [tableDocumentInstance endTask]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"SMySQLQueryHasBeenPerformed" object:tableDocumentInstance]; + return; } - [tableDocumentInstance endTask]; + + } 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; + } - } 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]); + // 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: + tableForColumn, @"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]; - return; - } + [self loadTableValues]; + 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: - tableForColumn, @"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]; - - return; - - } - // Catch editing events in the row and if the row isn't currently being edited, - // start an edit. This allows edits including enum changes to save correctly. - if ( !isEditingRow ) { - [oldRow setArray:[tableValues rowContentsAtIndex:rowIndex]]; - isEditingRow = YES; - currentlyEditingRow = rowIndex; - } + // Catch editing events in the row and if the row isn't currently being edited, + // start an edit. This allows edits including enum changes to save correctly. + if ( !isEditingRow ) { + [oldRow setArray:[tableValues rowContentsAtIndex:rowIndex]]; + isEditingRow = YES; + currentlyEditingRow = rowIndex; + } - NSDictionary *column = NSArrayObjectAtIndex(dataColumns, [[aTableColumn identifier] integerValue]); + NSDictionary *column = NSArrayObjectAtIndex(dataColumns, [[aTableColumn identifier] integerValue]); - if (anObject) { + if (anObject) { - // Restore NULLs if necessary - if ([anObject isEqualToString:[prefs objectForKey:SPNullValue]] && [[column objectForKey:@"null"] boolValue]) - anObject = [NSNull null]; + // Restore NULLs if necessary + if ([anObject isEqualToString:[prefs objectForKey:SPNullValue]] && [[column objectForKey:@"null"] boolValue]) + anObject = [NSNull null]; - [tableValues replaceObjectInRow:rowIndex column:[[aTableColumn identifier] integerValue] withObject:anObject]; - } else { - [tableValues replaceObjectInRow:rowIndex column:[[aTableColumn identifier] integerValue] withObject:@""]; + [tableValues replaceObjectInRow:rowIndex column:[[aTableColumn identifier] integerValue] withObject:anObject]; + } else { + [tableValues replaceObjectInRow:rowIndex column:[[aTableColumn identifier] integerValue] withObject:@""]; + } } } @@ -3359,7 +3440,7 @@ - (void)tableView:(NSTableView*)tableView didClickTableColumn:(NSTableColumn *)tableColumn { - if ( [selectedTable isEqualToString:@""] || !selectedTable ) + if ( [selectedTable isEqualToString:@""] || !selectedTable || tableView != tableContentView ) return; // Prevent sorting while the table is still loading @@ -3373,6 +3454,7 @@ [self sortTableTaskWithColumn:tableColumn]; } } + - (void)sortTableTaskWithColumn:(NSTableColumn *)tableColumn { NSAutoreleasePool *sortPool = [[NSAutoreleasePool alloc] init]; @@ -3434,14 +3516,14 @@ - (void)tableViewSelectionDidChange:(NSNotification *)aNotification { - isFirstChangeInView = YES; + // Check our notification object is our table content view + if ([aNotification object] != tableContentView) return; - [addButton setEnabled:([tablesListInstance tableType] == SPTableTypeTable)]; + isFirstChangeInView = YES; - // Check our notification object is our table content view - if ([aNotification object] != tableContentView) return; + [addButton setEnabled:([tablesListInstance tableType] == SPTableTypeTable)]; // If we are editing a row, attempt to save that row - if saving failed, reselect the edit row. if (isEditingRow && [tableContentView selectedRow] != currentlyEditingRow && ![self saveRowOnDeselect]) return; @@ -3467,11 +3549,15 @@ [self updateCountText]; } -- (void)tableViewColumnDidResize:(NSNotification *)aNotification /** saves the new column size in the preferences */ +- (void)tableViewColumnDidResize:(NSNotification *)aNotification { + + // Check our notification object is our table content view + if ([aNotification object] != tableContentView) return; + // sometimes the column has no identifier. I can't figure out what is causing it, so we just skip over this item if (![[[aNotification userInfo] objectForKey:@"NSTableColumn"] identifier]) return; @@ -3666,7 +3752,14 @@ */ - (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex { - return tableRowsSelectable; + + if(aTableView == filterTableView) + return YES; + else if(aTableView == tableContentView) + return tableRowsSelectable; + else + return YES; + } /** @@ -3674,6 +3767,7 @@ */ - (CGFloat)tableView:(NSTableView *)tableView sizeToFitWidthOfColumn:(NSInteger)columnIndex { + NSTableColumn *theColumn = [[tableView tableColumns] objectAtIndex:columnIndex]; NSDictionary *columnDefinition = [dataColumns objectAtIndex:[[theColumn identifier] integerValue]]; @@ -3779,6 +3873,12 @@ #pragma mark - #pragma mark Other methods +- (void)controlTextDidChange:(NSNotification *)notification +{ + if ([notification object] == filterTableView) { + [self updateFilterTableClause]; + } +} /** * If user selected a table cell which is a blob field and tried to edit it * cancel the fieldEditor, display the field editor sheet instead for editing @@ -3787,6 +3887,8 @@ - (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor { + if(control != tableContentView) return YES; + NSUInteger row, column; BOOL shouldBeginEditing = YES; @@ -3859,18 +3961,19 @@ { // Check firstly if SPCopyTable can handle command - if([tableContentView control:control textView:textView doCommandBySelector:(SEL)command]) + if([control control:control textView:textView doCommandBySelector:(SEL)command]) return YES; // Trap the escape key if ( [[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(cancelOperation:)] ) { - NSUInteger row = [tableContentView editedRow]; + NSUInteger row = [control editedRow]; // Abort editing [control abortEditing]; - [self cancelRowEditing]; + if(control == tableContentView) + [self cancelRowEditing]; return TRUE; } @@ -3917,6 +4020,49 @@ } /** + * Update WHERE clause in Filter Table Window - TODO not yet finished - initial approach only by HansJB + */ +- (void)updateFilterTableClause +{ + NSMutableString *clause = [NSMutableString string]; + NSInteger numberOfRows = [self numberOfRowsInTableView:filterTableView]; + NSInteger numberOfCols = [[filterTableView tableColumns] count]; + NSInteger numberOfValues = 0; + NSRange opRange; + for(NSInteger i=0; i<numberOfRows; i++) { + numberOfValues = 0; + for(NSInteger index=0; index<numberOfCols; index++) { + NSString *filterCell; + NSDictionary *filterCellData = [NSDictionary dictionaryWithDictionary:[filterTableData objectForKey:[NSNumber numberWithInteger:index]]]; + if([filterCell = [[filterCellData objectForKey:@"filter"] objectAtIndex:i] length]) { + if(numberOfValues) + [clause appendString:@" AND "]; + opRange = [filterCell rangeOfString:@" "]; + if(opRange.length) { + NSString *op = [[filterCell substringToIndex:opRange.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + NSString *pattern = [[filterCell substringFromIndex:NSMaxRange(opRange)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if([op isEqualToString:@">"] || + [op isEqualToString:@"<"] || + [op isEqualToString:@"="]) { + [clause appendFormat:@"%@ %@ %@", [[filterCellData objectForKey:@"name"] backtickQuotedString], op, pattern]; + } + + } else { + [clause appendFormat:@"%@ LIKE '%%%@%%'", [[filterCellData objectForKey:@"name"] backtickQuotedString], filterCell]; + } + numberOfValues++; + } + } + if(numberOfValues) + [clause appendString:@"\nOR\n"]; + } + [filterTableWhereClause setString:[clause substringToIndex:([clause length]-4)]]; + [filterTableWhereClause insertText:@""]; + [filterTableWhereClause didChangeText]; + [filterTableWhereClause scrollRangeToVisible:NSMakeRange(0, 0)]; +} + +/** * Makes the content filter field have focus by making it the first responder. */ - (void)makeContentFilterHaveFocus @@ -3956,6 +4102,7 @@ pthread_mutex_destroy(&tableValuesLock); [dataColumns release]; [oldRow release]; + [filterTableData release]; if (selectedTable) [selectedTable release]; if (contentFilters) [contentFilters release]; if (numberOfDefaultFilters) [numberOfDefaultFilters release]; |