diff options
-rw-r--r-- | Source/SPAppController.m | 30 | ||||
-rw-r--r-- | Source/SPBundleEditorController.m | 2 | ||||
-rw-r--r-- | Source/SPBundleHTMLOutputController.m | 8 | ||||
-rw-r--r-- | Source/SPConnectionControllerDelegate.m | 10 | ||||
-rw-r--r-- | Source/SPCopyTable.m | 2 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.h | 3 | ||||
-rw-r--r-- | Source/SPDatabaseDocument.m | 364 | ||||
-rw-r--r-- | Source/SPTablesList.m | 3 |
8 files changed, 363 insertions, 59 deletions
diff --git a/Source/SPAppController.m b/Source/SPAppController.m index 2117a3ac..879ea116 100644 --- a/Source/SPAppController.m +++ b/Source/SPAppController.m @@ -659,36 +659,16 @@ } } - BOOL userTerminated = NO; - - // while(1) { - // NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask - // untilDate:[NSDate distantPast] - // inMode:NSDefaultRunLoopMode - // dequeue:YES]; - // - // if ([event type] == NSKeyDown) { - // unichar key = [[event characters] length] == 1 ? [[event characters] characterAtIndex:0] : 0; - // if (([event modifierFlags] & NSCommandKeyMask) && key == '.') { - // userTerminated = YES; - // break; - // } - // } - // [NSApp sendEvent:event]; - // if(![processDocument isWorking]) break; - // usleep(1000); - // } - // - // if(userTerminated) { - // NSBeep(); - // return; - // } + // if no processDoc found and no passedProcessID was passed execute + // command at front most doc + if(!processDocument && !passedProcessID) + processDocument = [[[self frontDocumentWindow] delegate] selectedTableDocument]; if(processDocument && command) { if([command isEqualToString:@"passToDoc"]) { NSMutableDictionary *cmdDict = [NSMutableDictionary dictionary]; [cmdDict setObject:parameter forKey:@"parameter"]; - [cmdDict setObject:passedProcessID forKey:@"id"]; + [cmdDict setObject:(passedProcessID)?:@"" forKey:@"id"]; [processDocument handleSchemeCommand:cmdDict]; return; } diff --git a/Source/SPBundleEditorController.m b/Source/SPBundleEditorController.m index dffcb20c..32f5b581 100644 --- a/Source/SPBundleEditorController.m +++ b/Source/SPBundleEditorController.m @@ -1257,7 +1257,7 @@ */ - (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command { - if ( [[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(_cancelKey:)] || + if ( [[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(cancelOperation:)] || [textView methodForSelector:command] == [textView methodForSelector:@selector(complete:)] ) { //abort editing diff --git a/Source/SPBundleHTMLOutputController.m b/Source/SPBundleHTMLOutputController.m index 7cad079b..7bcff455 100644 --- a/Source/SPBundleHTMLOutputController.m +++ b/Source/SPBundleHTMLOutputController.m @@ -267,7 +267,13 @@ if([[[request URL] scheme] isEqualToString:@"sequelpro"] && navigationType == WebNavigationTypeLinkClicked) { [[NSApp delegate] handleEventWithURL:[request URL]]; [listener ignore]; - } else { + } + // file://a_file_path opens the reveal the file in Finder + else if([[[request URL] scheme] isEqualToString:@"file"] && navigationType == WebNavigationTypeLinkClicked) { + [[NSWorkspace sharedWorkspace] selectFile:[[[request mainDocumentURL] absoluteString] substringFromIndex:6] inFileViewerRootedAtPath:nil]; + [listener ignore]; + } + else { switch(navigationType) { case WebNavigationTypeLinkClicked: diff --git a/Source/SPConnectionControllerDelegate.m b/Source/SPConnectionControllerDelegate.m index 6ad83963..9e63a547 100644 --- a/Source/SPConnectionControllerDelegate.m +++ b/Source/SPConnectionControllerDelegate.m @@ -28,6 +28,8 @@ #import "SPFavoriteNode.h" #import "SPGroupNode.h" +#define CELL(cell) (SPTableTextFieldCell *)cell + @implementation SPConnectionController (SPConnectionControllerDelegate) #pragma mark - @@ -159,15 +161,15 @@ { SPTreeNode *node = (SPTreeNode *)item; - [(SPTableTextFieldCell *)cell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; + [CELL(cell) setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - [(SPTableTextFieldCell *)cell setTextColor:([favoritesOutlineView isEnabled]) ? [NSColor blackColor] : [NSColor grayColor]]; + [CELL(cell) setTextColor:([favoritesOutlineView isEnabled]) ? [NSColor blackColor] : [NSColor grayColor]]; if (![[node parentNode] parentNode]) { - [(SPTableTextFieldCell *)cell setImage:nil]; + [CELL(cell) setImage:nil]; } else { - [(SPTableTextFieldCell *)cell setImage:(![node isGroup]) ? [NSImage imageNamed:@"database-small"] : folderImage]; + [CELL(cell) setImage:(![node isGroup]) ? [NSImage imageNamed:@"database-small"] : folderImage]; } } diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m index 6b9383ab..fad9332e 100644 --- a/Source/SPCopyTable.m +++ b/Source/SPCopyTable.m @@ -228,7 +228,7 @@ NSInteger kBlobAsImageFile = 4; } } else - [result appendFormat:@"%@\t", [cellData description]]; + [result appendFormat:@"%@\t", [[[cellData description] stringByReplacingOccurrencesOfString:@"\n" withString:@"↵"] stringByReplacingOccurrencesOfString:@"\t" withString:@"⇥"]]; } else { [result appendString:@"\t"]; } diff --git a/Source/SPDatabaseDocument.h b/Source/SPDatabaseDocument.h index e7fac8bf..bcd92d59 100644 --- a/Source/SPDatabaseDocument.h +++ b/Source/SPDatabaseDocument.h @@ -163,6 +163,8 @@ NSInteger _queryMode; BOOL _isSavedInBundle; + BOOL _workingTimeout; + NSWindow *taskProgressWindow; BOOL taskDisplayIsIndeterminate; CGFloat taskProgressValue; @@ -357,6 +359,7 @@ - (NSWindow *)parentWindow; // Scripting +- (NSString*)doSQLSyntaxHighlightForString:(NSString*)sqlText cssLike:(BOOL)cssLike; - (void)handleSchemeCommand:(NSDictionary*)commandDict; - (void)registerActivity:(NSDictionary*)commandDict; - (void)removeRegisteredActivity:(NSInteger)pid; diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m index 86588f88..096974ba 100644 --- a/Source/SPDatabaseDocument.m +++ b/Source/SPDatabaseDocument.m @@ -56,6 +56,19 @@ #import "SPDatabaseRename.h" #import "SPServerSupport.h" #import "SPTooltip.h" +#import "SPEditorTokens.h" + +#pragma mark lex init + +/* +* Include all the extern variables and prototypes required for flex (used for syntax highlighting) +*/ +extern NSUInteger yylex(); +extern NSUInteger yyuoffset, yyuleng; +typedef struct yy_buffer_state *YY_BUFFER_STATE; +void yy_switch_to_buffer(YY_BUFFER_STATE); +YY_BUFFER_STATE yy_scan_string (const char *); + @interface SPDatabaseDocument (PrivateAPI) @@ -4543,20 +4556,126 @@ #pragma mark - #pragma mark Scheme scripting methods +/** + * Return an HTML formatted string representing the passed SQL string syntax highlighted + */ +- (NSString*)doSQLSyntaxHighlightForString:(NSString*)sqlText cssLike:(BOOL)cssLike +{ + + NSMutableString *sqlHTML = [[[NSMutableString alloc] initWithCapacity:[sqlText length]] autorelease]; + + NSRange textRange = NSMakeRange(0, [sqlText length]); + NSString *tokenColor; + NSString *cssId; + size_t token; + NSRange tokenRange; + + // initialise flex + yyuoffset = 0; yyuleng = 0; + yy_switch_to_buffer(yy_scan_string([sqlText UTF8String])); + BOOL skipFontTag; + + while (token=yylex()){ + skipFontTag = NO; + switch (token) { + case SPT_SINGLE_QUOTED_TEXT: + case SPT_DOUBLE_QUOTED_TEXT: + tokenColor = @"#A7221C"; + cssId = @"sp_sql_quoted"; + break; + case SPT_BACKTICK_QUOTED_TEXT: + tokenColor = @"#001892"; + cssId = @"sp_sql_backtick"; + break; + case SPT_RESERVED_WORD: + tokenColor = @"#0041F6"; + cssId = @"sp_sql_keyword"; + break; + case SPT_NUMERIC: + tokenColor = @"#67350F"; + cssId = @"sp_sql_numeric"; + break; + case SPT_COMMENT: + tokenColor = @"#265C10"; + cssId = @"sp_sql_comment"; + break; + case SPT_VARIABLE: + tokenColor = @"#6C6C6C"; + cssId = @"sp_sql_variable"; + break; + case SPT_WHITESPACE: + skipFontTag = YES; + cssId = @""; + break; + default: + skipFontTag = YES; + cssId = @""; + } + + tokenRange = NSMakeRange(yyuoffset, yyuleng); + + if(skipFontTag) + [sqlHTML appendString:[[sqlText substringWithRange:tokenRange] HTMLEscapeString]]; + else { + if(cssLike) + [sqlHTML appendFormat:@"<span class=\"%@\">%@</span>", cssId, [[sqlText substringWithRange:tokenRange] HTMLEscapeString]]; + else + [sqlHTML appendFormat:@"<font color=%@>%@</font>", tokenColor, [[sqlText substringWithRange:tokenRange] HTMLEscapeString]]; + } + + } + + // Wrap lines, and replace tabs with spaces + [sqlHTML replaceOccurrencesOfString:@"\n" withString:@"<br>" options:NSLiteralSearch range:NSMakeRange(0, [sqlHTML length])]; + [sqlHTML replaceOccurrencesOfString:@"\t" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, [sqlHTML length])]; + + if(sqlHTML) + return sqlHTML; + else + return @""; + +} + +/** + * Called by handleSchemeCommand: to break a while loop + */ +- (void)setTimeout +{ + _workingTimeout = YES; +} + +/** + * Process passed URL scheme command and wait (timeouted) for the document if it's busy or not yet connected + */ - (void)handleSchemeCommand:(NSDictionary*)commandDict { + if(!commandDict) return; + NSArray *params = [commandDict objectForKey:@"parameter"]; - if(![params count]) return; + if(![params count]) { + NSLog(@"No URL scheme command passed"); + NSBeep(); + return; + } NSString *command = [params objectAtIndex:0]; NSString *docProcessID = [self processID]; if(!docProcessID) docProcessID = @""; - // Authenticate command - if(![docProcessID isEqualToString:[commandDict objectForKey:@"id"]]) { - [SPTooltip showWithObject:NSLocalizedString(@"URL scheme command couldn't authenticated", @"URL scheme command couldn't authenticated") atLocation:[NSApp mouseLocation]]; - return; + // Wait for self + _workingTimeout = NO; + // the following while loop waits maximal 5secs + [self performSelector:@selector(setTimeout) withObject:nil afterDelay:5.0]; + while (_isWorkingLevel || !_isConnected) { + if(_workingTimeout) break; + // Do not block self + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if(event) [NSApp sendEvent:event]; + } if([command isEqualToString:@"SelectDocumentView"]) { @@ -4599,6 +4718,74 @@ return; } + if([command isEqualToString:@"SelectDatabase"]) { + if([params count] > 1) { + NSString *dbName = [params objectAtIndex:1]; + NSString *tableName = nil; + if([dbName length]) { + if([params count] == 3) { + tableName = [params objectAtIndex:2]; + } + [self selectDatabase:dbName item:tableName]; + } + } + return; + } + + if([command isEqualToString:@"SyntaxHighlighting"]) { + + NSFileManager *fm = [NSFileManager defaultManager]; + BOOL isDir; + + NSString *queryFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, docProcessID]; + NSString *resultFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, docProcessID]; + NSString *metaFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, docProcessID]; + NSString *statusFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, docProcessID]; + + NSError *inError = nil; + NSString *query = [NSString stringWithContentsOfFile:queryFileName encoding:NSUTF8StringEncoding error:inError]; + NSString *result = @""; + NSString *status = @"0"; + + if([fm fileExistsAtPath:queryFileName isDirectory:&isDir] && !isDir) { + + if(inError == nil && query && [query length]) { + if([params count] > 1) { + if([[params lastObject] isEqualToString:@"html"]) + result = [NSString stringWithString:[self doSQLSyntaxHighlightForString:query cssLike:NO]]; + else if([[params lastObject] isEqualToString:@"htmlcss"]) + result = [NSString stringWithString:[self doSQLSyntaxHighlightForString:query cssLike:YES]]; + } + } + } + + [fm removeItemAtPath:queryFileName error:nil]; + [fm removeItemAtPath:resultFileName error:nil]; + [fm removeItemAtPath:metaFileName error:nil]; + [fm removeItemAtPath:statusFileName error:nil]; + + if(![result writeToFile:resultFileName atomically:YES encoding:NSUTF8StringEncoding error:nil]) + status = @"1"; + + // write status file as notification that query was finished + BOOL succeed = [status writeToFile:statusFileName atomically:YES encoding:NSUTF8StringEncoding error:nil]; + if(!succeed) { + NSBeep(); + SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self parentWindow], self, nil, nil, + NSLocalizedString(@"Status file for sequelpro url scheme command couldn't be written!", @"status file for sequelpro url scheme command couldn't be written error message")); + } + return; + } + + // ==== the following commands need an authentication for safety reasons + + // Authenticate command + if(![docProcessID isEqualToString:[commandDict objectForKey:@"id"]]) { + SPBeginAlertSheet(NSLocalizedString(@"Remote Error", @"remote error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self parentWindow], self, nil, nil, + NSLocalizedString(@"URL scheme command couldn't authenticated", @"URL scheme command couldn't authenticated")); + return; + } + if([command isEqualToString:@"SelectTableRows"]) { if([params count] > 1 && [[[NSApp mainWindow] firstResponder] respondsToSelector:@selector(selectTableRows:)]) { [[[NSApp mainWindow] firstResponder] selectTableRows:[params subarrayWithRange:NSMakeRange(1, [params count]-1)]]; @@ -4616,21 +4803,6 @@ return; } - if([command isEqualToString:@"SelectDatabase"]) { - if (_isWorkingLevel) return; - if([params count] > 1) { - NSString *dbName = [params objectAtIndex:1]; - NSString *tableName = nil; - if([dbName length]) { - if([params count] == 3) { - tableName = [params objectAtIndex:2]; - } - [self selectDatabase:dbName item:tableName]; - } - } - return; - } - if([command isEqualToString:@"ReloadContentTableWithWHEREClause"]) { NSString *queryFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, docProcessID]; NSFileManager *fm = [NSFileManager defaultManager]; @@ -4646,11 +4818,148 @@ return; } - if([command isEqualToString:@"ExecuteQuery"]) { - // Bail if document is busy - if (_isWorkingLevel) - [SPTooltip showWithObject:NSLocalizedString(@"Connection window is busy. URL scheme command bailed", @"Connection window is busy. URL scheme command bailed") atLocation:[NSApp mouseLocation]]; + if([command isEqualToString:@"CreateSyntaxForTables"]) { + + if([params count] > 1) { + + NSString *queryFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, docProcessID]; + NSString *resultFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, docProcessID]; + NSString *metaFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, docProcessID]; + NSString *statusFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, docProcessID]; + NSFileManager *fm = [NSFileManager defaultManager]; + NSString *status = @"0"; + BOOL isDir; + BOOL userTerminated = NO; + BOOL doSyntaxHighlighting = NO; + BOOL doSyntaxHighlightingViaCSS = NO; + + if([[params lastObject] hasPrefix:@"html"]) { + doSyntaxHighlighting = YES; + if([[params lastObject] hasSuffix:@"css"]) { + doSyntaxHighlightingViaCSS = YES; + } + } + + if(doSyntaxHighlighting && [params count] < 3) return; + + BOOL changeEncoding = ![[mySQLConnection encoding] isEqualToString:@"utf8"]; + + NSArray *items = [params subarrayWithRange:NSMakeRange(1, [params count]-( (doSyntaxHighlighting) ? 2 : 1) )]; + NSArray *availableItems = [tablesListInstance tables]; + NSArray *availableItemTypes = [tablesListInstance tableTypes]; + NSMutableString *result = [NSMutableString string]; + + for(NSString* item in items) { + + NSEvent* event = [NSApp currentEvent]; + if ([event type] == NSKeyDown) { + unichar key = [[event characters] length] == 1 ? [[event characters] characterAtIndex:0] : 0; + if (([event modifierFlags] & NSCommandKeyMask) && key == '.') { + userTerminated = YES; + break; + } + } + + NSInteger itemType = SPTableTypeNone; + NSString *itemTypeStr = @"TABLE"; + NSInteger i; + NSInteger queryCol = 1; + + // Loop through the unfiltered tables/views to find the desired item + for (i = 0; i < [availableItems count]; i++) { + itemType = [[availableItemTypes objectAtIndex:i] integerValue]; + if (itemType == SPTableTypeNone) continue; + if ([[availableItems objectAtIndex:i] isEqualToString:item]) { + break; + } + } + // If no match found, continue + if (itemType == SPTableTypeNone) continue; + + switch(itemType) { + case SPTableTypeTable: + case SPTableTypeView: + itemTypeStr = @"TABLE"; + break; + case SPTableTypeProc: + itemTypeStr = @"PROCEDURE"; + queryCol = 2; + break; + case SPTableTypeFunc: + itemTypeStr = @"FUNCTION"; + queryCol = 2; + break; + } + + // Ensure that queries are made in UTF8 + if (changeEncoding) { + [mySQLConnection storeEncodingForRestoration]; + [mySQLConnection setEncoding:@"utf8"]; + } + + // Get create syntax + MCPResult *queryResult = [mySQLConnection queryString:[NSString stringWithFormat:@"SHOW CREATE %@ %@", + itemTypeStr, + [item backtickQuotedString] + ]]; + [queryResult setReturnDataAsStrings:YES]; + + if (changeEncoding) [mySQLConnection restoreStoredEncoding]; + + if ( ![queryResult numOfRows] ) { + //error while getting table structure + SPBeginAlertSheet(NSLocalizedString(@"Error", @"error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"Couldn't get create syntax.\nMySQL said: %@", @"message of panel when table information cannot be retrieved"), [mySQLConnection getLastErrorMessage]]); + + status = @"1"; + + } else { + NSString *syntaxString = [[queryResult fetchRowAsArray] objectAtIndex:queryCol]; + + // A NULL value indicates that the user does not have permission to view the syntax + if ([syntaxString isNSNull]) { + [[NSAlert alertWithMessageText:NSLocalizedString(@"Permission Denied", @"Permission Denied") + defaultButton:NSLocalizedString(@"OK", @"OK button") + alternateButton:nil otherButton:nil + informativeTextWithFormat:NSLocalizedString(@"The creation syntax could not be retrieved due to a permissions error.\n\nPlease check your user permissions with an administrator.", @"Create syntax permission denied detail")] + beginSheetModalForWindow:[NSApp mainWindow] + modalDelegate:self didEndSelector:NULL contextInfo:NULL]; + + return; + } + if(doSyntaxHighlighting) { + [result appendFormat:@"%@<br>", [self doSQLSyntaxHighlightForString:[syntaxString createViewSyntaxPrettifier] cssLike:doSyntaxHighlightingViaCSS]]; + } else { + [result appendFormat:@"%@\n", [syntaxString createViewSyntaxPrettifier]]; + } + } + } + + [fm removeItemAtPath:queryFileName error:nil]; + [fm removeItemAtPath:resultFileName error:nil]; + [fm removeItemAtPath:metaFileName error:nil]; + [fm removeItemAtPath:statusFileName error:nil]; + + if(userTerminated) + status = @"1"; + + if(![result writeToFile:resultFileName atomically:YES encoding:NSUTF8StringEncoding error:nil]) + status = @"1"; + + // write status file as notification that query was finished + BOOL succeed = [status writeToFile:statusFileName atomically:YES encoding:NSUTF8StringEncoding error:nil]; + if(!succeed) { + NSBeep(); + SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self parentWindow], self, nil, nil, + NSLocalizedString(@"Status file for sequelpro url scheme command couldn't be written!", @"status file for sequelpro url scheme command couldn't be written error message")); + } + + } + return; + } + + if([command isEqualToString:@"ExecuteQuery"]) { NSString *outputFormat = @"tab"; if([params count] == 2) @@ -4815,13 +5124,16 @@ BOOL succeed = [status writeToFile:statusFileName atomically:YES encoding:NSUTF8StringEncoding error:nil]; if(!succeed) { NSBeep(); - SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil, + SPBeginAlertSheet(NSLocalizedString(@"BASH Error", @"bash error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self parentWindow], self, nil, nil, NSLocalizedString(@"Status file for sequelpro url scheme command couldn't be written!", @"status file for sequelpro url scheme command couldn't be written error message")); } return; } - NSLog(@"received: %@", commandDict); + SPBeginAlertSheet(NSLocalizedString(@"Remote Error", @"remote error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [self parentWindow], self, nil, nil, + [NSString stringWithFormat:NSLocalizedString(@"URL scheme command “%@” unsupported", @"URL scheme command “%@” unsupported"), command]); + + } - (void)registerActivity:(NSDictionary*)commandDict diff --git a/Source/SPTablesList.m b/Source/SPTablesList.m index 54e0f400..da787a6d 100644 --- a/Source/SPTablesList.m +++ b/Source/SPTablesList.m @@ -1358,11 +1358,12 @@ [[control window] makeFirstResponder:control]; return TRUE; - } else if ( [[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(_cancelKey:)] || + } else if ( [[control window] methodForSelector:command] == [[control window] methodForSelector:@selector(cancelOperation:)] || [textView methodForSelector:command] == [textView methodForSelector:@selector(complete:)] ) { //abort editing [control abortEditing]; + [[NSApp mainWindow] makeFirstResponder:tablesListView]; return TRUE; } else{ |