aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPCopyTable.m
diff options
context:
space:
mode:
Diffstat (limited to 'Source/SPCopyTable.m')
-rw-r--r--Source/SPCopyTable.m247
1 files changed, 203 insertions, 44 deletions
diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m
index d37a0b73..4dcf6d9c 100644
--- a/Source/SPCopyTable.m
+++ b/Source/SPCopyTable.m
@@ -44,16 +44,16 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
- (void)copy:(id)sender
{
NSString *tmp = nil;
-
+
if([sender tag] == MENU_EDIT_COPY_AS_SQL) {
tmp = [self selectedRowsAsSqlInserts];
if ( nil != tmp )
{
NSPasteboard *pb = [NSPasteboard generalPasteboard];
-
+
[pb declareTypes:[NSArray arrayWithObjects: NSStringPboardType, nil]
owner:nil];
-
+
[pb setString:tmp forType:NSStringPboardType];
}
} else {
@@ -61,17 +61,183 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
if ( nil != tmp )
{
NSPasteboard *pb = [NSPasteboard generalPasteboard];
-
- [pb declareTypes:[NSArray arrayWithObjects: NSTabularTextPboardType,
+
+ [pb declareTypes:[NSArray arrayWithObjects: NSTabularTextPboardType,
NSStringPboardType, nil]
owner:nil];
-
+
[pb setString:tmp forType:NSStringPboardType];
[pb setString:tmp forType:NSTabularTextPboardType];
}
}
}
+/*
+ * Trap the enter, escape, tab and arrow keys, overriding default behaviour and continuing/ending editing,
+ * only within the current row.
+ */
+- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
+{
+
+ NSUInteger row, column, i;
+
+ row = [self editedRow];
+ column = [self editedColumn];
+ BOOL isCellEditing = NO;
+
+ // cell editing for views in SPTableContent or in SPCustomQuery
+ if([[self delegate] isKindOfClass:[SPCustomQuery class]] || ([[self delegate] isKindOfClass:[SPTableContent class]] && [[self delegate] valueForKeyPath:@"tablesListInstance"] && [[[self delegate] valueForKeyPath:@"tablesListInstance"] tableType] == SPTableTypeView))
+ isCellEditing = YES;
+
+ // Trap tab key
+ // -- for handling of blob fields look at [self control:textShouldBeginEditing:]
+ if ( [textView methodForSelector:command] == [textView methodForSelector:@selector(insertTab:)] )
+ {
+ [[control window] makeFirstResponder:control];
+
+ if(isCellEditing) {
+ // Look for the next editable field
+ if ( column != ( [self numberOfColumns] - 1 ) ) {
+ i = 1;
+ while ([[self delegate] fieldEditStatusForRow:row andColumn:[NSArrayObjectAtIndex([self tableColumns], column+i) identifier]] != 1) {
+ i++;
+
+ // If there are no columns after the latest blob or text column, save the current line.
+ if ( (column+i) >= [self numberOfColumns] )
+ return YES;
+
+ }
+
+ [self editColumn:column+i row:row withEvent:nil select:YES];
+
+ }
+
+ } else {
+
+ // Save the current line if it's the last field in the table
+ if ( column == ( [self numberOfColumns] - 1 ) ) {
+ if([[self delegate] respondsToSelector:@selector(addRowToDB)])
+ [[self delegate] addRowToDB];
+ } else {
+ // Select the next field for editing
+ [self editColumn:column+1 row:row withEvent:nil select:YES];
+ }
+
+ }
+
+ return YES;
+ }
+
+ // Trap shift-tab key
+ else if ( [textView methodForSelector:command] == [textView methodForSelector:@selector(insertBacktab:)] )
+ {
+ [[control window] makeFirstResponder:control];
+
+ if(isCellEditing) {
+ // Look for the next editable field backwards
+ if ( column > 0 ) {
+ i = 1;
+ while ([[self delegate] fieldEditStatusForRow:row andColumn:[NSArrayObjectAtIndex([self tableColumns], column-i) identifier]] != 1) {
+ i++;
+
+ // If there are no columns before the latestone, return.
+ if ( column == i ) {
+ [[self onMainThread] makeFirstResponder];
+ return TRUE;
+ }
+ }
+
+ [self editColumn:column-i row:row withEvent:nil select:YES];
+
+ }
+ } else {
+
+ // Save the current line if it's the last field in the table
+ if ( column < 1 ) {
+ if([[self delegate] respondsToSelector:@selector(addRowToDB)])
+ [[self delegate] addRowToDB];
+ [[self onMainThread] makeFirstResponder];
+ } else {
+ // Select the previous field for editing
+ [self editColumn:column-1 row:row withEvent:nil select:YES];
+ }
+
+ }
+ return YES;
+ }
+
+ // Trap enter key
+ else if ( [textView methodForSelector:command] == [textView methodForSelector:@selector(insertNewline:)] )
+ {
+ // If enum field is edited RETURN selects the new value instead of saving the entire row
+ if([[self delegate] isKindOfClass:[SPTableContent class]] && [[self delegate] valueForKeyPath:@"tableDataInstance"]) {
+ NSString *fieldType = [[[[self delegate] valueForKeyPath:@"tableDataInstance"] columnWithName:[[NSArrayObjectAtIndex([self tableColumns], column) headerCell] stringValue]] objectForKey:@"typegrouping"];
+ if([fieldType isEqualToString:@"enum"])
+ return YES;
+ }
+ [[control window] makeFirstResponder:control];
+ if([[self delegate] isKindOfClass:[SPTableContent class]] && !isCellEditing && [[self delegate] respondsToSelector:@selector(addRowToDB)])
+ [[self delegate] addRowToDB];
+ return YES;
+
+ }
+
+ // Trap down arrow key
+ else if ( [textView methodForSelector:command] == [textView methodForSelector:@selector(moveDown:)] )
+ {
+
+ // If enum field is edited ARROW key navigates through the popup list
+ if([[self delegate] isKindOfClass:[SPTableContent class]] && [[self delegate] valueForKeyPath:@"tableDataInstance"]) {
+ NSString *fieldType = [[[[self delegate] valueForKeyPath:@"tableDataInstance"] columnWithName:[[NSArrayObjectAtIndex([self tableColumns], column) headerCell] stringValue]] objectForKey:@"typegrouping"];
+ if([fieldType isEqualToString:@"enum"])
+ return NO;
+ }
+
+ NSUInteger newRow = row+1;
+ if (newRow>=[[self delegate] numberOfRowsInTableView:self]) return YES; //check if we're already at the end of the list
+
+ [[control window] makeFirstResponder:control];
+ if([[self delegate] isKindOfClass:[SPTableContent class]] && !isCellEditing && [[self delegate] respondsToSelector:@selector(addRowToDB)])
+ [[self delegate] addRowToDB];
+
+ if (newRow>=[[self delegate] numberOfRowsInTableView:self]) return YES; //check again. addRowToDB could reload the table and change the number of rows
+ if (column>=[tableStorage columnCount]) return YES; //the column count could change too
+
+ [self selectRowIndexes:[NSIndexSet indexSetWithIndex:newRow] byExtendingSelection:NO];
+ [self editColumn:column row:newRow withEvent:nil select:YES];
+ return YES;
+ }
+
+ // Trap up arrow key
+ else if ( [textView methodForSelector:command] == [textView methodForSelector:@selector(moveUp:)] )
+ {
+
+ // If enum field is edited ARROW key navigates through the popup list
+ if([[self delegate] isKindOfClass:[SPTableContent class]] && [[self delegate] valueForKeyPath:@"tableDataInstance"]) {
+ NSString *fieldType = [[[[self delegate] valueForKeyPath:@"tableDataInstance"] columnWithName:[[NSArrayObjectAtIndex([self tableColumns], column) headerCell] stringValue]] objectForKey:@"typegrouping"];
+ if([fieldType isEqualToString:@"enum"])
+ return NO;
+ }
+
+ if (row==0) return TRUE; //already at the beginning of the list
+ NSUInteger newRow = row-1;
+
+ [[control window] makeFirstResponder:control];
+ if([[self delegate] isKindOfClass:[SPTableContent class]] && !isCellEditing && [[self delegate] respondsToSelector:@selector(addRowToDB)])
+ [[self delegate] addRowToDB];
+
+ if (newRow>=[[self delegate] numberOfRowsInTableView:self]) return YES; // addRowToDB could reload the table and change the number of rows
+ if (column>=[tableStorage columnCount]) return YES; //the column count could change too
+
+ [self selectRowIndexes:[NSIndexSet indexSetWithIndex:newRow] byExtendingSelection:NO];
+ [self editColumn:column row:newRow withEvent:nil select:YES];
+ return YES;
+ }
+
+ return NO;
+}
+
+
//allow for drag-n-drop out of the application as a copy
- (NSUInteger)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
@@ -82,7 +248,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
* Only have the copy menu item enabled when row(s) are selected in
* supported tables.
*/
-- (BOOL)validateMenuItem:(NSMenuItem*)anItem
+- (BOOL)validateMenuItem:(NSMenuItem*)anItem
{
NSInteger menuItemTag = [anItem tag];
@@ -114,11 +280,11 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
- (NSString *)selectedRowsAsTabStringWithHeaders:(BOOL)withHeaders
{
if ([self numberOfSelectedRows] == 0) return nil;
-
+
NSIndexSet *selectedRows = [self selectedRowIndexes];
NSArray *columns = [self tableColumns];
- NSUInteger numColumns = [columns count];
+ NSUInteger numColumns = [columns count];
NSMutableString *result = [NSMutableString stringWithCapacity:2000];
// Add the table headers if requested to do so
@@ -144,7 +310,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
NSString * nullString = [prefs objectForKey:SPNullValue];
NSStringEncoding connectionEncoding = [mySQLConnection encoding];
while ( rowIndex != NSNotFound )
- {
+ {
for ( c = 0; c < numColumns; c++) {
cellData = SPDataStorageObjectAtRowAndColumn(tableStorage, rowIndex, columnMappings[c]);
@@ -189,7 +355,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
return result;
}
-/*
+/*
* Return selected rows as SQL INSERT INTO `foo` VALUES (baz) string.
* If no selected table name is given `<table>` will be used instead.
*/
@@ -205,7 +371,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
NSMutableString *value = [NSMutableString stringWithCapacity:10];
id cellData = nil;
-
+
NSUInteger rowCounter = 0;
NSUInteger penultimateRowIndex = [selectedRows count];
NSUInteger c;
@@ -226,11 +392,11 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
columnMappings[c] = [[[columns objectAtIndex:c] identifier] unsignedIntValue];
NSString *t = [[columnDefinitions objectAtIndex:columnMappings[c]] objectForKey:@"typegrouping"];
-
+
// Numeric data
if ([t isEqualToString:@"bit"] || [t isEqualToString:@"integer"] || [t isEqualToString:@"float"])
columnTypes[c] = 0;
-
+
// Blob data or long text data
else if ([t isEqualToString:@"blobdata"] || [t isEqualToString:@"textdata"])
columnTypes[c] = 2;
@@ -241,7 +407,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
}
// Begin the SQL string
- [result appendFormat:@"INSERT INTO %@ (%@)\nVALUES\n",
+ [result appendFormat:@"INSERT INTO %@ (%@)\nVALUES\n",
[(selectedTable == nil)?@"<table>":selectedTable backtickQuotedString], [tbHeader componentsJoinedAndBacktickQuoted]];
NSUInteger rowIndex = [selectedRows firstIndex];
@@ -282,7 +448,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
continue;
} else if (cellData) {
-
+
// Check column type and insert the data accordingly
switch(columnTypes[c]) {
@@ -345,7 +511,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
rowIndex = [selectedRows indexGreaterThanIndex:rowIndex];
}
-
+
// Remove the trailing ",\n" from the query string
if ( [result length] > 3 )
[result deleteCharactersInRange:NSMakeRange([result length]-2, 2)];
@@ -354,7 +520,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
free(columnMappings);
free(columnTypes);
-
+
return result;
}
@@ -366,11 +532,11 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
NSArray *columns = [self tableColumns];
NSUInteger numColumns = [columns count];
NSIndexSet *selectedRows = [self selectedRowIndexes];
-
+
NSMutableString *result = [NSMutableString stringWithCapacity:2000];
NSUInteger c;
id cellData = nil;
-
+
// Create an array of table column mappings for fast iteration
NSUInteger *columnMappings = malloc(numColumns * sizeof(NSUInteger));
for ( c = 0; c < numColumns; c++) {
@@ -383,10 +549,10 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
Class nsDataClass = [NSData class];
NSStringEncoding connectionEncoding = [mySQLConnection encoding];
while ( rowIndex != NSNotFound )
- {
+ {
for ( c = 0; c < numColumns; c++) {
cellData = SPDataStorageObjectAtRowAndColumn(tableStorage, rowIndex, columnMappings[c]);
-
+
// Copy the shown representation of the cell - custom NULL display strings, (not loaded),
// and the string representation of any blobs or binary texts.
if (cellData) {
@@ -411,7 +577,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
if ([result length]) {
[result deleteCharactersInRange:NSMakeRange([result length]-1, 1)];
}
-
+
[result appendString:@"\n"];
// Retrieve the next selected row index
@@ -437,7 +603,7 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
mySQLConnection = aMySqlConnection;
tableInstance = anInstance;
tableStorage = theTableStorage;
-
+
if (columnDefinitions) [columnDefinitions release];
columnDefinitions = [[NSArray alloc] initWithArray:columnDefs];
}
@@ -582,32 +748,25 @@ NSInteger MENU_EDIT_COPY_AS_SQL = 2003;
- (void)keyDown:(NSEvent *)theEvent
{
+
// RETURN or ENTER invoke editing mode for selected row
// by calling tableView:shouldEditTableColumn: to validate
- if([[[[self delegate] class] description] isEqualToString:@"SPTableContent"]) {
+ if([self numberOfSelectedRows] == 1 && ([theEvent keyCode] == 36 || [theEvent keyCode] == 76)) {
+
+ NSUInteger i = 0;
- id tableContentView = [[self delegate] valueForKeyPath:@"tableContentView"];
- if([tableContentView numberOfSelectedRows] == 1 && ([theEvent keyCode] == 36 || [theEvent keyCode] == 76)) {
- if([[self delegate] tableView:tableContentView shouldEditTableColumn:[[tableContentView tableColumns] objectAtIndex:0] row:[tableContentView selectedRow]]) {
+ for(id item in [self tableColumns]) {
+ // Run in fieldEditorMode?
+ if(![[self delegate] tableView:self shouldEditTableColumn:item row:[self selectedRow]])
+ ;
+ else {
+ // if not in fieldEditorMode select the first item
[self editColumn:0 row:[self selectedRow] withEvent:nil select:YES];
- return;
- }
- }
- }
- if([[[[self delegate] class] description] isEqualToString:@"SPCustomQuery"]) {
- id tableContentView = [[self delegate] valueForKeyPath:@"customQueryView"];
- if([tableContentView numberOfSelectedRows] == 1 && ([theEvent keyCode] == 36 || [theEvent keyCode] == 76)) {
-
- // TODO: this works until the user presses OK in the Field Editor Sheet!!
- // in the future we should store the new row data temporarily and then
- // after editing the last column update the db field by field (ask HansJB)
- NSInteger colNum = [[tableContentView tableColumns] count];
- NSInteger i;
- for(i=0; i<colNum; i++) {
- [[self delegate] tableView:tableContentView shouldEditTableColumn:[[tableContentView tableColumns] objectAtIndex:i] row:[tableContentView selectedRow]];
+ break;
}
- return;
}
+
+ return;
}
[super keyDown:theEvent];