aboutsummaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-rw-r--r--Source/MGTemplateEngine.m14
-rw-r--r--Source/NoodleLineNumberView.m63
-rw-r--r--Source/SPAppController.m604
-rw-r--r--Source/SPBundleCommandTextView.m18
-rw-r--r--Source/SPBundleEditorController.h4
-rw-r--r--Source/SPBundleEditorController.m23
-rw-r--r--Source/SPBundleHTMLOutputController.h2
-rw-r--r--Source/SPBundleHTMLOutputController.m106
-rw-r--r--Source/SPConnectionController.h1
-rw-r--r--Source/SPConnectionController.m9
-rw-r--r--Source/SPConnectionHandler.m8
-rw-r--r--Source/SPConstants.h14
-rw-r--r--Source/SPConstants.m8
-rw-r--r--Source/SPCopyTable.m69
-rw-r--r--Source/SPCustomQuery.h5
-rw-r--r--Source/SPCustomQuery.m15
-rw-r--r--Source/SPDatabaseDocument.h4
-rw-r--r--Source/SPDatabaseDocument.m208
-rw-r--r--Source/SPEditorTokens.l34
-rw-r--r--Source/SPExportController.m2
-rw-r--r--Source/SPExtendedTableInfo.m2
-rw-r--r--Source/SPFieldEditorController.m5
-rw-r--r--Source/SPHistoryController.m2
-rw-r--r--Source/SPIndexesController.m71
-rw-r--r--Source/SPNarrowDownCompletion.h3
-rw-r--r--Source/SPNarrowDownCompletion.m40
-rw-r--r--Source/SPPreferencesUpgrade.m7
-rw-r--r--Source/SPQueryFavoriteManager.m1
-rw-r--r--Source/SPSSHTunnel.m4
-rw-r--r--Source/SPServerSupport.h6
-rw-r--r--Source/SPServerSupport.m7
-rw-r--r--Source/SPTableContent.h4
-rw-r--r--Source/SPTableContent.m68
-rw-r--r--Source/SPTableStructure.m22
-rw-r--r--Source/SPTableStructureDelegate.m3
-rw-r--r--Source/SPTablesList.h3
-rw-r--r--Source/SPTablesList.m66
-rw-r--r--Source/SPTextView.h1
-rw-r--r--Source/SPTextView.m209
-rw-r--r--Source/SPTextViewAdditions.m69
-rw-r--r--Source/SPTooltip.m14
-rw-r--r--Source/SPUserManager.h7
-rw-r--r--Source/SPUserManager.m99
-rw-r--r--Source/SPWindow.h32
-rw-r--r--Source/SPWindow.m112
-rw-r--r--Source/SequelProTunnelAssistant.m31
46 files changed, 1457 insertions, 642 deletions
diff --git a/Source/MGTemplateEngine.m b/Source/MGTemplateEngine.m
index 86068b66..89ea3a1d 100644
--- a/Source/MGTemplateEngine.m
+++ b/Source/MGTemplateEngine.m
@@ -177,10 +177,16 @@
- (void)reportError:(NSString *)errorStr code:(NSInteger)code continuing:(BOOL)continuing
{
if (delegate) {
- NSString *errStr = NSLocalizedString(errorStr, nil);
- if (!continuing) {
- errStr = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"Fatal Error", nil), errStr];
- }
+
+ NSString *errStr;
+ if(errorStr)
+ errStr = [NSString stringWithString:errorStr];
+ else
+ errStr = NSLocalizedString(@"MGTemplateEngine Error", @"mgtemplateengine error");
+
+ if (!continuing)
+ errStr = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"Fatal Error", @"fatal error"), errStr];
+
SEL selector = @selector(templateEngine:encounteredError:isContinuing:);
if ([(NSObject *)delegate respondsToSelector:selector]) {
NSError *error = [NSError errorWithDomain:TEMPLATE_ENGINE_ERROR_DOMAIN
diff --git a/Source/NoodleLineNumberView.m b/Source/NoodleLineNumberView.m
index 9e0d284a..3df2b423 100644
--- a/Source/NoodleLineNumberView.m
+++ b/Source/NoodleLineNumberView.m
@@ -191,75 +191,78 @@
NSLayoutManager *layoutManager;
NSTextContainer *container;
NSRange nullRange;
- NSMutableArray *lines;
+ NSArray *lines;
id view;
-
+
view = [self clientView];
visibleRect = [[[self scrollView] contentView] bounds];
-
+
lines = [self lineIndices];
location += NSMinY(visibleRect);
if ([view isKindOfClass:[NSTextView class]])
{
+
nullRange = NSMakeRange(NSNotFound, 0);
layoutManager = [view layoutManager];
container = [view textContainer];
count = [lines count];
-
- for (line = 0; line < count; line++)
+
+ // Find the characters that are currently visible
+ NSRange range = [layoutManager characterRangeForGlyphRange:[layoutManager glyphRangeForBoundingRect:visibleRect inTextContainer:container] actualGlyphRange:NULL];
+
+ // Fudge the range a tad in case there is an extra new line at end.
+ // It doesn't show up in the glyphs so would not be accounted for.
+ range.length++;
+
+ for (line = [self lineNumberForCharacterIndex:range.location inText:@""]; line < count; line++)
{
+
index = [NSArrayObjectAtIndex(lines, line) unsignedIntegerValue];
-
+
rects = [layoutManager rectArrayForCharacterRange:NSMakeRange(index, 0)
withinSelectedCharacterRange:nullRange
inTextContainer:container
rectCount:&rectCount];
-
+
for (i = 0; i < rectCount; i++)
- {
if ((location >= NSMinY(rects[i])) && (location < NSMaxY(rects[i])))
- {
return line + 1;
- }
- }
- }
+
+ }
}
return NSNotFound;
}
- (void)calculateLines
{
- id view;
-
- view = [self clientView];
+ id view = [self clientView];
if ([view isKindOfClass:[NSTextView class]])
{
- NSUInteger index, numberOfLines, stringLength, lineEnd, contentEnd;
+ NSUInteger index, stringLength, lineEnd, contentEnd;
NSString *text;
CGFloat oldThickness, newThickness;
text = [view string];
stringLength = [text length];
+
// Switch off line numbering if text larger than 6MB
// for performance reasons.
// TODO improve performance maybe via threading
if(stringLength>6000000)
return;
- if (lineIndices) [lineIndices release];
- lineIndices = [[NSMutableArray alloc] init];
+
+ if (lineIndices) [lineIndices release], lineIndices = nil;
+ lineIndices = [[NSMutableArray alloc] initWithCapacity:1];
index = 0;
- numberOfLines = 0;
do
{
[lineIndices addObject:[NSNumber numberWithUnsignedInteger:index]];
-
index = NSMaxRange([text lineRangeForRange:NSMakeRange(index, 0)]);
- numberOfLines++;
}
while (index < stringLength);
@@ -274,7 +277,7 @@
newThickness = [self requiredThickness];
if (fabs(oldThickness - newThickness) > 1)
{
- NSInvocation *invocation;
+ NSInvocation *invocation;
// Not a good idea to resize the view during calculations (which can happen during
// display). Do a delayed perform (using NSInvocation since arg is a float).
@@ -381,7 +384,7 @@
CGFloat ypos, yinset;
NSDictionary *textAttributes;
NSSize stringSize;
- NSMutableArray *lines;
+ NSArray *lines;
layoutManager = [view layoutManager];
container = [view textContainer];
@@ -407,7 +410,7 @@
CGFloat boundsRULERMargin2 = NSWidth(bounds) - RULER_MARGIN * 2.0;
CGFloat boundsWidthRULER = NSWidth(bounds) - RULER_MARGIN;
- CGFloat yinsetMinY = yinset - NSMinY(visibleRect);
+ CGFloat yinsetMinY = yinset - NSMinY(visibleRect);
for (line = [self lineNumberForCharacterIndex:range.location inText:text]; line < count; line++)
{
@@ -462,11 +465,11 @@
if (line != NSNotFound)
{
NSUInteger selectionStart, selectionEnd;
- NSMutableArray *lines = [self lineIndices];
+ NSArray *lines = [self lineIndices];
- selectionStart = [[lines objectAtIndex:(line - 1)] unsignedIntegerValue];
+ selectionStart = [NSArrayObjectAtIndex(lines, (line - 1)) unsignedIntegerValue];
if (line < [lines count]) {
- selectionEnd = [[lines objectAtIndex:line] unsignedIntegerValue];
+ selectionEnd = [NSArrayObjectAtIndex(lines, line) unsignedIntegerValue];
} else {
selectionEnd = [[view string] length];
}
@@ -489,7 +492,7 @@
if (line != NSNotFound)
{
NSUInteger selectionStart, selectionEnd;
- NSMutableArray *lines = [self lineIndices];
+ NSArray *lines = [self lineIndices];
if (line >= dragSelectionStartLine) {
startLine = dragSelectionStartLine;
endLine = line;
@@ -498,9 +501,9 @@
endLine = dragSelectionStartLine;
}
- selectionStart = [[lines objectAtIndex:(startLine - 1)] unsignedIntegerValue];
+ selectionStart = [NSArrayObjectAtIndex(lines, (startLine - 1)) unsignedIntegerValue];
if (endLine < [lines count]) {
- selectionEnd = [[lines objectAtIndex:endLine] unsignedIntegerValue];
+ selectionEnd = [NSArrayObjectAtIndex(lines, endLine) unsignedIntegerValue];
} else {
selectionEnd = [[view string] length];
}
diff --git a/Source/SPAppController.m b/Source/SPAppController.m
index d89f0cce..ba65df6f 100644
--- a/Source/SPAppController.m
+++ b/Source/SPAppController.m
@@ -42,6 +42,19 @@
#import <PSMTabBar/PSMTabBarControl.h>
#import <Sparkle/Sparkle.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 *);
+
@implementation SPAppController
@synthesize lastBundleBlobFilesDirectory;
@@ -496,7 +509,7 @@
NSFileManager *fm = [NSFileManager defaultManager];
- NSString *bundlePath = [[NSFileManager defaultManager] applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder error:nil];
+ NSString *bundlePath = [fm applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder error:nil];
if(!bundlePath) return;
@@ -539,6 +552,11 @@
if (cmdData) [cmdData release];
return;
}
+
+ // Reload Bundles if Sequel Pro didn't run
+ if(![installedBundleUUIDs count])
+ [self reloadBundles:self];
+
if([[installedBundleUUIDs allKeys] containsObject:[cmdData objectForKey:SPBundleFileUUIDKey]]) {
NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Installing Bundle", @"Open Files : Bundle : Already-Installed : 'Update Bundle' question dialog title")]
defaultButton:NSLocalizedString(@"Update", @"Open Files : Bundle : Already-Installed : Update button")
@@ -550,10 +568,11 @@
NSInteger answer = [alert runModal];
if(answer == NSAlertDefaultReturn) {
NSError *error = nil;
- NSString *moveToTrashCommand = [NSString stringWithFormat:@"osascript -e 'tell application \"Finder\" to move (POSIX file \"%@\") to the trash'", newPath];
+ NSString *removePath = [[[installedBundleUUIDs objectForKey:[cmdData objectForKey:SPBundleFileUUIDKey]] objectForKey:@"path"] substringToIndex:([[[installedBundleUUIDs objectForKey:[cmdData objectForKey:SPBundleFileUUIDKey]] objectForKey:@"path"] length]-[SPBundleFileName length]-1)];
+ NSString *moveToTrashCommand = [NSString stringWithFormat:@"osascript -e 'tell application \"Finder\" to move (POSIX file \"%@\") to the trash'", removePath];
[moveToTrashCommand runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&error];
if(error != nil) {
- NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while moving “%@” to Trash.", @"Open Files : Bundle : Already-Installed : Delete-Old-Error : Could not delete old bundle before installing new version."), [[installedBundleUUIDs objectForKey:[cmdData objectForKey:SPBundleFileUUIDKey]] objectForKey:@"path"]]
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while moving “%@” to Trash.", @"Open Files : Bundle : Already-Installed : Delete-Old-Error : Could not delete old bundle before installing new version."), removePath]
defaultButton:NSLocalizedString(@"OK", @"Open Files : Bundle : Already-Installed : Delete-Old-Error : OK button")
alternateButton:nil
otherButton:nil
@@ -621,7 +640,12 @@
{
NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
- [self handleEventWithURL:url];
+ if(url)
+ [self handleEventWithURL:url];
+ else {
+ NSBeep();
+ NSLog(@"Error in sequelpro URL scheme");
+ }
}
- (void)handleEventWithURL:(NSURL*)url
@@ -635,13 +659,14 @@
else
parameter = [NSArray array];
+ NSFileManager *fm = [NSFileManager defaultManager];
// Handle commands which don't need a connection window
if([command isEqualToString:@"chooseItemFromList"]) {
NSString *statusFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, (passedProcessID && [passedProcessID length]) ? passedProcessID : @""];
NSString *resultFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, (passedProcessID && [passedProcessID length]) ? passedProcessID : @""];
- [[NSFileManager defaultManager] removeItemAtPath:statusFileName error:nil];
- [[NSFileManager defaultManager] removeItemAtPath:resultFileName error:nil];
+ [fm removeItemAtPath:statusFileName error:nil];
+ [fm removeItemAtPath:resultFileName error:nil];
NSString *result = @"";
NSString *status = @"0";
if([parameter count]) {
@@ -659,6 +684,50 @@
return;
}
+ if([command isEqualToString:@"SyntaxHighlighting"]) {
+
+ BOOL isDir;
+
+ NSString *anUUID = (passedProcessID && [passedProcessID length]) ? passedProcessID : @"";
+ NSString *queryFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, anUUID];
+ NSString *resultFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, anUUID];
+ NSString *metaFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, anUUID];
+ NSString *statusFileName = [NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, anUUID];
+
+ 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([parameter count] > 0) {
+ if([[parameter lastObject] isEqualToString:@"html"])
+ result = [NSString stringWithString:[self doSQLSyntaxHighlightForString:query cssLike:NO]];
+ else if([[parameter 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;
+ }
NSString *activeProcessID = [[[[self frontDocumentWindow] delegate] selectedTableDocument] processID];
@@ -695,14 +764,33 @@
[cmdDict setObject:parameter forKey:@"parameter"];
[cmdDict setObject:(passedProcessID)?:@"" forKey:@"id"];
[processDocument handleSchemeCommand:cmdDict];
- return;
- }
- else {
+ } else {
SPBeginAlertSheet(NSLocalizedString(@"sequelpro URL Scheme Error", @"sequelpro url Scheme Error"), NSLocalizedString(@"OK", @"OK button"), nil, nil, [NSApp mainWindow], self, nil, nil,
[NSString stringWithFormat:@"%@ “%@”:\n%@", NSLocalizedString(@"Error for", @"error for message"), [command description], NSLocalizedString(@"sequelpro URL scheme command not supported.", @"sequelpro URL scheme command not supported.")]);
+
+ // If command failed notify the file handle hand shake mechanism
+ NSString *out = @"1";
+ NSString *anUUID = @"";
+ if(command && passedProcessID && [passedProcessID length])
+ anUUID = passedProcessID;
+ else
+ anUUID = command;
- return;
+ [out writeToFile:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, anUUID]
+ atomically:YES
+ encoding:NSUTF8StringEncoding
+ error:nil];
+
+ out = @"Error";
+ [out writeToFile:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, anUUID]
+ atomically:YES
+ encoding:NSUTF8StringEncoding
+ error:nil];
+
}
+
+ return;
+
}
if(passedProcessID && [passedProcessID length]) {
@@ -723,10 +811,10 @@
usleep(5000);
- [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, passedProcessID] error:nil];
- [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, passedProcessID] error:nil];
- [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, passedProcessID] error:nil];
- [[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, passedProcessID] error:nil];
+ [fm removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultStatusPathHeader, passedProcessID] error:nil];
+ [fm removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultPathHeader, passedProcessID] error:nil];
+ [fm removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryResultMetaPathHeader, passedProcessID] error:nil];
+ [fm removeItemAtPath:[NSString stringWithFormat:@"%@%@", SPURLSchemeQueryInputPathHeader, passedProcessID] error:nil];
@@ -745,6 +833,86 @@
}
+/**
+ * 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:@"&nbsp;&nbsp;&nbsp;&nbsp;" options:NSLiteralSearch range:NSMakeRange(0, [sqlHTML length])];
+
+ if(sqlHTML)
+ return sqlHTML;
+ else
+ return @"";
+
+}
+
- (IBAction)executeBundleItemForApp:(id)sender
{
@@ -760,6 +928,7 @@
}
if(!infoPath) {
+ NSLog(@"No path to Bundle command passed");
NSBeep();
return;
}
@@ -939,8 +1108,6 @@
}
}
- // if(doc && [doc shellVariables]) [env addEntriesFromDictionary:[doc shellVariables]];
- // if(doc) [doc release];
id firstResponder = [[NSApp keyWindow] firstResponder];
if([firstResponder respondsToSelector:@selector(executeBundleItemForInputField:)]) {
BOOL selfIsQueryEditor = ([[[firstResponder class] description] isEqualToString:@"SPTextView"]) ;
@@ -999,6 +1166,19 @@
{
[runningActivitiesArray addObject:commandDict];
[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPActivitiesUpdateNotification object:nil];
+
+ SPDatabaseDocument* frontMostDoc = [self frontDocument];
+ if(frontMostDoc) {
+ if([runningActivitiesArray count] || [[frontMostDoc runningActivities] count])
+ [frontMostDoc performSelector:@selector(setActivityPaneHidden:) withObject:[NSNumber numberWithInteger:0] afterDelay:1.0];
+ else {
+ [NSObject cancelPreviousPerformRequestsWithTarget:frontMostDoc
+ selector:@selector(setActivityPaneHidden:)
+ object:[NSNumber numberWithInteger:0]];
+ [frontMostDoc setActivityPaneHidden:[NSNumber numberWithInteger:1]];
+ }
+ }
+
}
- (void)removeRegisteredActivity:(NSInteger)pid
@@ -1009,7 +1189,20 @@
break;
}
}
+
[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPActivitiesUpdateNotification object:nil];
+
+ SPDatabaseDocument* frontMostDoc = [self frontDocument];
+ if(frontMostDoc) {
+ if([runningActivitiesArray count] || [[frontMostDoc runningActivities] count])
+ [frontMostDoc performSelector:@selector(setActivityPaneHidden:) withObject:[NSNumber numberWithInteger:0] afterDelay:1.0];
+ else {
+ [NSObject cancelPreviousPerformRequestsWithTarget:frontMostDoc
+ selector:@selector(setActivityPaneHidden:)
+ object:[NSNumber numberWithInteger:0]];
+ [frontMostDoc setActivityPaneHidden:[NSNumber numberWithInteger:1]];
+ }
+ }
}
- (NSArray*)runningActivities
@@ -1324,6 +1517,7 @@
- (IBAction)reloadBundles:(id)sender
{
+ // Force releasing of any HTML output windows
for(id c in bundleHTMLOutputController) {
if(![[c window] isVisible]) {
[c release];
@@ -1346,26 +1540,38 @@
// Clean menu
[menu compatibleRemoveAllItems];
+ // Set up the bundle search paths
+ // First process all in Application Support folder installed ones then Default ones
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSError *appPathError = nil;
NSArray *bundlePaths = [NSArray arrayWithObjects:
- ([[NSFileManager defaultManager] applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder createIfNotExists:NO error:nil])?:@"",
+ [fm applicationSupportDirectoryForSubDirectory:SPBundleSupportFolder createIfNotExists:YES error:&appPathError],
[NSString stringWithFormat:@"%@/Contents/SharedSupport/Default Bundles", [[NSBundle mainBundle] bundlePath]],
nil];
+ // If ~/Library/Application Path/Sequel Pro/Bundles couldn't be created bail
+ if(appPathError != nil) {
+ NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Bundles Installation Error", @"bundles installation error")
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:[NSString stringWithFormat:NSLocalizedString(@"Couldn't create Application Support Bundle folder!\nError: %@", @"Couldn't create Application Support Bundle folder!\nError: %@"), [appPathError localizedDescription]]];
+
+ [alert runModal];
+ return;
+ }
+
BOOL processDefaultBundles = NO;
- NSFileManager *fm = [NSFileManager defaultManager];
-
+
NSArray *deletedDefaultBundles;
- NSMutableArray *updatedDefaultBundles = [NSMutableArray array];
- if([[NSUserDefaults standardUserDefaults] objectForKey:SPBundleDeletedDefaultBundlesKey]) {
+
+ if([[NSUserDefaults standardUserDefaults] objectForKey:SPBundleDeletedDefaultBundlesKey])
deletedDefaultBundles = [[[NSUserDefaults standardUserDefaults] objectForKey:SPBundleDeletedDefaultBundlesKey] retain];
- } else {
+ else
deletedDefaultBundles = [[NSArray array] retain];
- }
- if([[NSUserDefaults standardUserDefaults] objectForKey:SPBundleUpdatedDefaultBundlesKey]) {
- [updatedDefaultBundles setArray:[[NSUserDefaults standardUserDefaults] objectForKey:SPBundleUpdatedDefaultBundlesKey]];
- }
NSMutableString *infoAboutUpdatedDefaultBundles = [NSMutableString string];
+ BOOL doBundleUpdate = ([[NSUserDefaults standardUserDefaults] objectForKey:@"doBundleUpdate"]) ? YES : NO;
for(NSString* bundlePath in bundlePaths) {
if([bundlePath length]) {
@@ -1386,125 +1592,115 @@
NSString *infoPath = [NSString stringWithFormat:@"%@/%@/%@", bundlePath, bundle, SPBundleFileName];
NSData *pData = [NSData dataWithContentsOfFile:infoPath options:NSUncachedRead error:&readError];
- cmdData = [[NSPropertyListSerialization propertyListFromData:pData
- mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain];
+ cmdData = [NSPropertyListSerialization propertyListFromData:pData
+ mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError];
if(!cmdData || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
-
NSLog(@"“%@” file couldn't be read.", infoPath);
NSBeep();
+ continue;
+ }
- } else {
- if((![cmdData objectForKey:SPBundleFileDisabledKey] || ![[cmdData objectForKey:SPBundleFileDisabledKey] intValue])
- && [cmdData objectForKey:SPBundleFileNameKey]
- && [[cmdData objectForKey:SPBundleFileNameKey] length]
- && [cmdData objectForKey:SPBundleFileScopeKey])
- {
-
- if([cmdData objectForKey:SPBundleFileUUIDKey] && [[cmdData objectForKey:SPBundleFileUUIDKey] length]) {
-
- if(processDefaultBundles) {
-
- // Skip deleted default Bundles
- BOOL bundleWasDeleted = NO;
- if([deletedDefaultBundles count]) {
- for(NSArray* item in deletedDefaultBundles) {
- if([[item objectAtIndex:0] isEqualToString:[cmdData objectForKey:SPBundleFileUUIDKey]]) {
- bundleWasDeleted = YES;
- break;
- }
+ if((![cmdData objectForKey:SPBundleFileDisabledKey] || ![[cmdData objectForKey:SPBundleFileDisabledKey] intValue])
+ && [cmdData objectForKey:SPBundleFileNameKey]
+ && [[cmdData objectForKey:SPBundleFileNameKey] length]
+ && [cmdData objectForKey:SPBundleFileScopeKey])
+ {
+
+ BOOL defaultBundleWasUpdated = NO;
+
+ if([cmdData objectForKey:SPBundleFileUUIDKey] && [[cmdData objectForKey:SPBundleFileUUIDKey] length]) {
+
+ if(processDefaultBundles) {
+
+ // Skip deleted default Bundles
+ BOOL bundleWasDeleted = NO;
+ if([deletedDefaultBundles count]) {
+ for(NSArray* item in deletedDefaultBundles) {
+ if([[item objectAtIndex:0] isEqualToString:[cmdData objectForKey:SPBundleFileUUIDKey]]) {
+ bundleWasDeleted = YES;
+ break;
}
}
- if(bundleWasDeleted) continue;
+ }
+ if(bundleWasDeleted) continue;
+
+ // If default Bundle is already installed check for possible update,
+ // if so duplicate the modified one by appending (user) and updated it
+ if(doBundleUpdate || [installedBundleUUIDs objectForKey:[cmdData objectForKey:SPBundleFileUUIDKey]] == nil) {
- // If default Bundle is already install check for possible update,
- // if so duplicate the modified one by appending (user) and updated it
if([installedBundleUUIDs objectForKey:[cmdData objectForKey:SPBundleFileUUIDKey]]) {
- if([updatedDefaultBundles containsObject:[cmdData objectForKey:SPBundleFileUUIDKey]]) {
-
- NSString *oldPath = [NSString stringWithFormat:@"%@/%@/%@", [bundlePaths objectAtIndex:0], bundle, SPBundleFileName];
- NSError *readError = nil;
- NSString *convError = nil;
- NSPropertyListFormat format;
- NSDictionary *cmdData = nil;
-
- NSData *pData = [NSData dataWithContentsOfFile:oldPath options:NSUncachedRead error:&readError];
- cmdData = [[NSPropertyListSerialization propertyListFromData:pData
- mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError] retain];
- if(!cmdData || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
- NSLog(@"“%@” file couldn't be read.", oldPath);
- NSBeep();
- if (cmdData) [cmdData release];
- continue;
- } else {
- NSString *oldBundle = [NSString stringWithFormat:@"%@/%@", [bundlePaths objectAtIndex:0], bundle];
- // Check for modifications
- if([cmdData objectForKey:SPBundleFileDefaultBundleWasModifiedKey]) {
-
- // Duplicate Bundle, change the UUID and rename the menu label
- NSString *duplicatedBundle = [NSString stringWithFormat:@"%@/%@_%ld.%@", [bundlePaths objectAtIndex:0], [bundle substringToIndex:([bundle length] - [SPUserBundleFileExtension length] - 1)], (NSUInteger)(random() % 35000), SPUserBundleFileExtension];
- if(![[NSFileManager defaultManager] copyItemAtPath:oldBundle toPath:duplicatedBundle error:nil]) {
- NSLog(@"Couldn't copy “%@” to update it", bundle);
- NSBeep();
- if (cmdData) [cmdData release];
- continue;
- }
- NSError *readError1 = nil;
- NSString *convError1 = nil;
- NSMutableDictionary *dupData = [NSMutableDictionary dictionary];
- NSString *duplicatedBundleCommand = [NSString stringWithFormat:@"%@/%@", duplicatedBundle, SPBundleFileName];
- NSData *dData = [NSData dataWithContentsOfFile:duplicatedBundleCommand options:NSUncachedRead error:&readError1];
- [dupData setDictionary:[NSPropertyListSerialization propertyListFromData:dData
- mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError1]];
- if(!dupData && ![dupData count] || readError1 != nil || [convError1 length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
- NSLog(@"“%@” file couldn't be read.", duplicatedBundleCommand);
- NSBeep();
- continue;
- }
- [dupData setObject:[NSString stringWithNewUUID] forKey:SPBundleFileUUIDKey];
- NSString *orgName = [dupData objectForKey:SPBundleFileNameKey];
- [dupData setObject:[NSString stringWithFormat:@"%@ (user)", orgName] forKey:SPBundleFileNameKey];
- [dupData removeObjectForKey:SPBundleFileIsDefaultBundleKey];
- [dupData writeToFile:duplicatedBundleCommand atomically:YES];
-
- NSError *error = nil;
- NSString *moveToTrashCommand = [NSString stringWithFormat:@"osascript -e 'tell application \"Finder\" to move (POSIX file \"%@\") to the trash'", oldBundle];
- [moveToTrashCommand runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&error];
-
- if(error != nil) {
- NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while moving “%@” to Trash.", @"error while moving “%@” to trash"), [[installedBundleUUIDs objectForKey:[cmdData objectForKey:SPBundleFileUUIDKey]] objectForKey:@"path"]]
- defaultButton:NSLocalizedString(@"OK", @"OK button")
- alternateButton:nil
- otherButton:nil
- informativeTextWithFormat:[error localizedDescription]];
-
- [alert setAlertStyle:NSCriticalAlertStyle];
- [alert runModal];
- if (cmdData) [cmdData release];
- continue;
- }
- [infoAboutUpdatedDefaultBundles appendFormat:@"• %@\n", orgName];
- } else {
-
- // If no modifications are done simply remove the old one
- if(![fm removeItemAtPath:oldBundle error:nil]) {
- NSLog(@"Couldn't remove “%@” to update it", bundle);
- NSBeep();
- if (cmdData) [cmdData release];
- continue;
- }
+ NSString *oldPath = [NSString stringWithFormat:@"%@/%@/%@", [bundlePaths objectAtIndex:0], bundle, SPBundleFileName];
+ NSError *readError = nil;
+ NSString *convError = nil;
+ NSPropertyListFormat format;
+ NSDictionary *cmdDataOld = nil;
+
+ NSData *pDataOld = [NSData dataWithContentsOfFile:oldPath options:NSUncachedRead error:&readError];
+ cmdDataOld = [NSPropertyListSerialization propertyListFromData:pDataOld
+ mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError];
+ if(!cmdDataOld || readError != nil || [convError length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
+ NSLog(@"“%@” file couldn't be read.", oldPath);
+ NSBeep();
+ continue;
+ } else {
+ NSString *oldBundle = [NSString stringWithFormat:@"%@/%@", [bundlePaths objectAtIndex:0], bundle];
+ // Check for modifications
+ if([cmdDataOld objectForKey:SPBundleFileDefaultBundleWasModifiedKey]) {
+
+ // Duplicate Bundle, change the UUID and rename the menu label
+ NSString *duplicatedBundle = [NSString stringWithFormat:@"%@/%@_%ld.%@", [bundlePaths objectAtIndex:0], [bundle substringToIndex:([bundle length] - [SPUserBundleFileExtension length] - 1)], (NSUInteger)(random() % 35000), SPUserBundleFileExtension];
+ if(![[NSFileManager defaultManager] copyItemAtPath:oldBundle toPath:duplicatedBundle error:nil]) {
+ NSLog(@"Couldn't copy “%@” to update it", bundle);
+ NSBeep();
+ continue;
}
+ NSError *readError1 = nil;
+ NSString *convError1 = nil;
+ NSMutableDictionary *dupData = [NSMutableDictionary dictionary];
+ NSString *duplicatedBundleCommand = [NSString stringWithFormat:@"%@/%@", duplicatedBundle, SPBundleFileName];
+ NSData *dData = [NSData dataWithContentsOfFile:duplicatedBundleCommand options:NSUncachedRead error:&readError1];
+ [dupData setDictionary:[NSPropertyListSerialization propertyListFromData:dData
+ mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&convError1]];
+ if(!dupData && ![dupData count] || readError1 != nil || [convError1 length] || !(format == NSPropertyListXMLFormat_v1_0 || format == NSPropertyListBinaryFormat_v1_0)) {
+ NSLog(@"“%@” file couldn't be read.", duplicatedBundleCommand);
+ NSBeep();
+ continue;
+ }
+ [dupData setObject:[NSString stringWithNewUUID] forKey:SPBundleFileUUIDKey];
+ NSString *orgName = [dupData objectForKey:SPBundleFileNameKey];
+ [dupData setObject:[NSString stringWithFormat:@"%@ (user)", orgName] forKey:SPBundleFileNameKey];
+ [dupData removeObjectForKey:SPBundleFileIsDefaultBundleKey];
+ [dupData writeToFile:duplicatedBundleCommand atomically:YES];
+
+ NSError *error = nil;
+ NSString *moveToTrashCommand = [NSString stringWithFormat:@"osascript -e 'tell application \"Finder\" to move (POSIX file \"%@\") to the trash'", oldBundle];
+ [moveToTrashCommand runBashCommandWithEnvironment:nil atCurrentDirectoryPath:nil error:&error];
+
+ if(error != nil) {
+ NSAlert *alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:NSLocalizedString(@"Error while moving “%@” to Trash.", @"error while moving “%@” to trash"), [[installedBundleUUIDs objectForKey:[cmdDataOld objectForKey:SPBundleFileUUIDKey]] objectForKey:@"path"]]
+ defaultButton:NSLocalizedString(@"OK", @"OK button")
+ alternateButton:nil
+ otherButton:nil
+ informativeTextWithFormat:[error localizedDescription]];
+
+ [alert setAlertStyle:NSCriticalAlertStyle];
+ [alert runModal];
+ continue;
+ }
+ [infoAboutUpdatedDefaultBundles appendFormat:@"• %@\n", orgName];
+ } else {
- // Remove item from update list which will be updated in the Prefs
- [updatedDefaultBundles removeObject:[cmdData objectForKey:SPBundleFileUUIDKey]];
+ // If no modifications are done simply remove the old one
+ if(![fm removeItemAtPath:oldBundle error:nil]) {
+ NSLog(@"Couldn't remove “%@” to update it", bundle);
+ NSBeep();
+ continue;
+ }
}
- if (cmdData) [cmdData release];
-
- } else {
- continue;
}
}
@@ -1523,96 +1719,93 @@
}
infoPath = [NSString stringWithString:newInfoPath];
- }
-
- [installedBundleUUIDs setObject:[NSDictionary dictionaryWithObjectsAndKeys:
- [NSString stringWithFormat:@"%@ (%@)", bundle, [cmdData objectForKey:SPBundleFileNameKey]], @"name",
- infoPath, @"path", nil] forKey:[cmdData objectForKey:SPBundleFileUUIDKey]];
-
- } else {
- NSLog(@"No UUID for %@", bundle);
- NSBeep();
- continue;
- }
-
- NSArray *scopes = [[cmdData objectForKey:SPBundleFileScopeKey] componentsSeparatedByString:@" "];
- for(NSString *scope in scopes) {
+ defaultBundleWasUpdated = YES;
- if(![bundleUsedScopes containsObject:scope]) {
- [bundleUsedScopes addObject:scope];
- [bundleItems setObject:[NSMutableArray array] forKey:scope];
- [bundleCategories setObject:[NSMutableArray array] forKey:scope];
- [bundleKeyEquivalents setObject:[NSMutableDictionary dictionary] forKey:scope];
}
- if([cmdData objectForKey:SPBundleFileCategoryKey] && [[cmdData objectForKey:SPBundleFileCategoryKey] length] && ![[bundleCategories objectForKey:scope] containsObject:[cmdData objectForKey:SPBundleFileCategoryKey]])
- [[bundleCategories objectForKey:scope] addObject:[cmdData objectForKey:SPBundleFileCategoryKey]];
- }
+ if(!defaultBundleWasUpdated) continue;
- NSMutableDictionary *aDict = [NSMutableDictionary dictionary];
- [aDict setObject:[cmdData objectForKey:SPBundleFileNameKey] forKey:SPBundleInternLabelKey];
- [aDict setObject:infoPath forKey:SPBundleInternPathToFileKey];
-
- // Register trigger
- if([cmdData objectForKey:SPBundleFileTriggerKey]) {
- if(![bundleTriggers objectForKey:[cmdData objectForKey:SPBundleFileTriggerKey]])
- [bundleTriggers setObject:[NSMutableArray array] forKey:[cmdData objectForKey:SPBundleFileTriggerKey]];
- [[bundleTriggers objectForKey:[cmdData objectForKey:SPBundleFileTriggerKey]] addObject:
- [NSString stringWithFormat:@"%@|%@|%@",
- infoPath,
- [cmdData objectForKey:SPBundleFileScopeKey],
- ([[cmdData objectForKey:SPBundleFileOutputActionKey] isEqualToString:SPBundleOutputActionShowAsHTML])?[cmdData objectForKey:SPBundleFileUUIDKey]:@""]];
}
- if([cmdData objectForKey:SPBundleFileKeyEquivalentKey] && [[cmdData objectForKey:SPBundleFileKeyEquivalentKey] length]) {
-
- NSString *theKey = [cmdData objectForKey:SPBundleFileKeyEquivalentKey];
- NSString *theChar = [theKey substringFromIndex:[theKey length]-1];
- NSString *theMods = [theKey substringToIndex:[theKey length]-1];
- NSUInteger mask = 0;
- if([theMods rangeOfString:@"^"].length)
- mask = mask | NSControlKeyMask;
- if([theMods rangeOfString:@"@"].length)
- mask = mask | NSCommandKeyMask;
- if([theMods rangeOfString:@"~"].length)
- mask = mask | NSAlternateKeyMask;
- if([theMods rangeOfString:@"$"].length)
- mask = mask | NSShiftKeyMask;
- for(NSString* scope in scopes) {
- if(![[bundleKeyEquivalents objectForKey:scope] objectForKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]])
- [[bundleKeyEquivalents objectForKey:scope] setObject:[NSMutableArray array] forKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]];
-
- [[[bundleKeyEquivalents objectForKey:scope] objectForKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]] addObject:
- [NSDictionary dictionaryWithObjectsAndKeys:
- infoPath, @"path",
- [cmdData objectForKey:SPBundleFileNameKey], @"title",
- ([cmdData objectForKey:SPBundleFileTooltipKey]) ?: @"", @"tooltip",
- nil]];
+ [installedBundleUUIDs setObject:[NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:@"%@ (%@)", bundle, [cmdData objectForKey:SPBundleFileNameKey]], @"name",
+ infoPath, @"path", nil] forKey:[cmdData objectForKey:SPBundleFileUUIDKey]];
- }
+ } else {
+ NSLog(@"No UUID for %@", bundle);
+ NSBeep();
+ continue;
+ }
- [aDict setObject:[NSArray arrayWithObjects:theChar, [NSNumber numberWithInteger:mask], nil] forKey:SPBundleInternKeyEquivalentKey];
- }
+ // Register Bundle
+ NSString *scope = [cmdData objectForKey:SPBundleFileScopeKey];
- if([cmdData objectForKey:SPBundleFileTooltipKey] && [[cmdData objectForKey:SPBundleFileTooltipKey] length])
- [aDict setObject:[cmdData objectForKey:SPBundleFileTooltipKey] forKey:SPBundleFileTooltipKey];
+ // Register scope/category menu structure
+ if(![bundleUsedScopes containsObject:scope]) {
+ [bundleUsedScopes addObject:scope];
+ [bundleItems setObject:[NSMutableArray array] forKey:scope];
+ [bundleCategories setObject:[NSMutableArray array] forKey:scope];
+ [bundleKeyEquivalents setObject:[NSMutableDictionary dictionary] forKey:scope];
+ }
+ if([cmdData objectForKey:SPBundleFileCategoryKey] && [[cmdData objectForKey:SPBundleFileCategoryKey] length] && ![[bundleCategories objectForKey:scope] containsObject:[cmdData objectForKey:SPBundleFileCategoryKey]])
+ [[bundleCategories objectForKey:scope] addObject:[cmdData objectForKey:SPBundleFileCategoryKey]];
+
+ NSMutableDictionary *aDict = [NSMutableDictionary dictionary];
+ [aDict setObject:[cmdData objectForKey:SPBundleFileNameKey] forKey:SPBundleInternLabelKey];
+ [aDict setObject:infoPath forKey:SPBundleInternPathToFileKey];
+
+ // Register trigger
+ if([cmdData objectForKey:SPBundleFileTriggerKey]) {
+ if(![bundleTriggers objectForKey:[cmdData objectForKey:SPBundleFileTriggerKey]])
+ [bundleTriggers setObject:[NSMutableArray array] forKey:[cmdData objectForKey:SPBundleFileTriggerKey]];
+ [[bundleTriggers objectForKey:[cmdData objectForKey:SPBundleFileTriggerKey]] addObject:
+ [NSString stringWithFormat:@"%@|%@|%@",
+ infoPath,
+ [cmdData objectForKey:SPBundleFileScopeKey],
+ ([[cmdData objectForKey:SPBundleFileOutputActionKey] isEqualToString:SPBundleOutputActionShowAsHTML])?[cmdData objectForKey:SPBundleFileUUIDKey]:@""]];
+ }
- if([cmdData objectForKey:SPBundleFileCategoryKey] && [[cmdData objectForKey:SPBundleFileCategoryKey] length])
- [aDict setObject:[cmdData objectForKey:SPBundleFileCategoryKey] forKey:SPBundleFileCategoryKey];
+ // Register key equivalent
+ if([cmdData objectForKey:SPBundleFileKeyEquivalentKey] && [[cmdData objectForKey:SPBundleFileKeyEquivalentKey] length]) {
+
+ NSString *theKey = [cmdData objectForKey:SPBundleFileKeyEquivalentKey];
+ NSString *theChar = [theKey substringFromIndex:[theKey length]-1];
+ NSString *theMods = [theKey substringToIndex:[theKey length]-1];
+ NSUInteger mask = 0;
+ if([theMods rangeOfString:@"^"].length) mask = mask | NSControlKeyMask;
+ if([theMods rangeOfString:@"@"].length) mask = mask | NSCommandKeyMask;
+ if([theMods rangeOfString:@"~"].length) mask = mask | NSAlternateKeyMask;
+ if([theMods rangeOfString:@"$"].length) mask = mask | NSShiftKeyMask;
+
+ if(![[bundleKeyEquivalents objectForKey:scope] objectForKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]])
+ [[bundleKeyEquivalents objectForKey:scope] setObject:[NSMutableArray array] forKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]];
+
+ if(!doBundleUpdate || (doBundleUpdate && (![[cmdData objectForKey:SPBundleFileIsDefaultBundleKey] boolValue] || processDefaultBundles)))
+ [[[bundleKeyEquivalents objectForKey:scope] objectForKey:[cmdData objectForKey:SPBundleFileKeyEquivalentKey]] addObject:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ infoPath, @"path",
+ [cmdData objectForKey:SPBundleFileNameKey], @"title",
+ ([cmdData objectForKey:SPBundleFileTooltipKey]) ?: @"", @"tooltip",
+ nil]];
+
+ [aDict setObject:[NSArray arrayWithObjects:theChar, [NSNumber numberWithInteger:mask], nil] forKey:SPBundleInternKeyEquivalentKey];
+ }
- if([cmdData objectForKey:SPBundleFileKeyEquivalentKey] && [[cmdData objectForKey:SPBundleFileKeyEquivalentKey] length])
- [aDict setObject:[cmdData objectForKey:SPBundleFileKeyEquivalentKey] forKey:@"key"];
+ if([cmdData objectForKey:SPBundleFileTooltipKey] && [[cmdData objectForKey:SPBundleFileTooltipKey] length])
+ [aDict setObject:[cmdData objectForKey:SPBundleFileTooltipKey] forKey:SPBundleFileTooltipKey];
- for(NSString* scope in scopes)
- [[bundleItems objectForKey:scope] addObject:aDict];
+ if([cmdData objectForKey:SPBundleFileCategoryKey] && [[cmdData objectForKey:SPBundleFileCategoryKey] length])
+ [aDict setObject:[cmdData objectForKey:SPBundleFileCategoryKey] forKey:SPBundleFileCategoryKey];
- }
+ if([cmdData objectForKey:SPBundleFileKeyEquivalentKey] && [[cmdData objectForKey:SPBundleFileKeyEquivalentKey] length])
+ [aDict setObject:[cmdData objectForKey:SPBundleFileKeyEquivalentKey] forKey:@"key"];
- if (cmdData) [cmdData release];
+ [[bundleItems objectForKey:scope] addObject:aDict];
}
}
+ // Sort items for menus
NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:SPBundleInternLabelKey ascending:YES] autorelease];
for(NSString* scope in [bundleItems allKeys]) {
[[bundleItems objectForKey:scope] sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
@@ -1624,9 +1817,10 @@
}
[deletedDefaultBundles release];
-
- // Synchronize updated Bundles
- [[NSUserDefaults standardUserDefaults] setObject:updatedDefaultBundles forKey:SPBundleUpdatedDefaultBundlesKey];
+ if(doBundleUpdate) {
+ [[NSUserDefaults standardUserDefaults] removeObjectForKey:@"doBundleUpdate"];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+ }
// Inform user about default Bundle updates which were modified by the user and re-run Reload Bundles
if([infoAboutUpdatedDefaultBundles length]) {
diff --git a/Source/SPBundleCommandTextView.m b/Source/SPBundleCommandTextView.m
index 6411ccfd..d9aae0f7 100644
--- a/Source/SPBundleCommandTextView.m
+++ b/Source/SPBundleCommandTextView.m
@@ -31,8 +31,8 @@
- (void)dealloc
{
- [prefs removeObserver:self forKeyPath:SPCustomQueryEditorTabStopWidth];
[[NSNotificationCenter defaultCenter] removeObserver:self];
+ [prefs removeObserver:self forKeyPath:SPCustomQueryEditorTabStopWidth];
[prefs release];
[lineNumberView release];
}
@@ -58,12 +58,13 @@
// Re-define tab stops for a better editing
[self setTabStops];
- // add NSViewBoundsDidChangeNotification to scrollView
- [[commandScrollView contentView] setPostsBoundsChangedNotifications:YES];
-
// disabled to get the current text range in textView safer
[[self layoutManager] setBackgroundLayoutEnabled:NO];
+ // add NSViewBoundsDidChangeNotification to scrollView
+ [commandScrollView setPostsBoundsChangedNotifications:YES];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChangeNotification:) name:NSViewBoundsDidChangeNotification object:[commandScrollView contentView]];
+
}
- (void)drawRect:(NSRect)rect
@@ -781,6 +782,15 @@
textWasChanged = YES;
}
+/**
+ * Scrollview delegate after the command textView's view port was changed.
+ * Manily used to render line numbering.
+ */
+- (void)boundsDidChangeNotification:(NSNotification *)notification
+{
+ [commandScrollView display];
+}
+
#pragma mark -
// Store the font in the prefs for selected delegates only
diff --git a/Source/SPBundleEditorController.h b/Source/SPBundleEditorController.h
index ac4c9e6b..1c3c0dab 100644
--- a/Source/SPBundleEditorController.h
+++ b/Source/SPBundleEditorController.h
@@ -27,7 +27,7 @@
#import "SPBundleCommandTextView.h"
#import "SPOutlineView.h"
-@class SRRecorderControl;
+@class SRRecorderControl, BWSplitView;
@interface SPBundleEditorController : NSWindowController {
@@ -61,9 +61,9 @@
IBOutlet NSMenuItem *revealInFinderMenuItem;
IBOutlet SRRecorderControl *keyEquivalentField;
IBOutlet NSButton *disabledCheckbox;
- IBOutlet NSView *bundleDataView;
IBOutlet NSScrollView *commandScrollView;
IBOutlet NSScrollView *descriptionScrollView;
+ IBOutlet BWSplitView *splitView;
IBOutlet id undeleteSheet;
IBOutlet NSTableView *undeleteTableView;
diff --git a/Source/SPBundleEditorController.m b/Source/SPBundleEditorController.m
index de779e67..279002f4 100644
--- a/Source/SPBundleEditorController.m
+++ b/Source/SPBundleEditorController.m
@@ -38,6 +38,8 @@
#define SP_BUNDLEEDITOR_SCOPE_GENERAL_STRING NSLocalizedString(@"General", @"Bundle Editor : Scope dropdown : 'general' item")
#define SP_BUNDLEEDITOR_OUTLINE_BUNDLE_TOOLTIP_STRING NSLocalizedString(@"“%@” Bundle",@"Bundle Editor : Outline View : Bundle item : tooltip")
+#define SP_BUNDLEEDITOR_SPLITVIEW_AUTOSAVE_STRING @"SPBundleEditorSplitView"
+
@interface SPBundleEditorController (PrivateAPI)
- (void)_updateBundleDataView;
@@ -116,6 +118,15 @@
- (void)awakeFromNib
{
+ // Set up the splitview width manually; autosave appears to save but not restore this value
+ // here, so restore in code if present.
+ NSString *splitViewKeyName = [NSString stringWithFormat:@"NSSplitView Subview Frames %@", SP_BUNDLEEDITOR_SPLITVIEW_AUTOSAVE_STRING];
+ if ([[NSUserDefaults standardUserDefaults] arrayForKey:splitViewKeyName]) {
+ NSString *detailString = [[[NSUserDefaults standardUserDefaults] arrayForKey:splitViewKeyName] objectAtIndex:0];
+ float dividerPosition = [[[detailString componentsSeparatedByString:@", "] objectAtIndex:2] floatValue];
+ [splitView setPosition:dividerPosition ofDividerAtIndex:0];
+ }
+
// Init all needed variables; popup menus (with the chance for localization); and set
// defaults
@@ -131,6 +142,7 @@
[[commandBundleTree objectForKey:kChildrenKey] addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:[NSMutableArray array], kChildrenKey, SP_BUNDLEEDITOR_SCOPE_INPUTFIELD_STRING, kBundleNameKey, nil]];
[[commandBundleTree objectForKey:kChildrenKey] addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:[NSMutableArray array], kChildrenKey, SP_BUNDLEEDITOR_SCOPE_DATATABLE_STRING, kBundleNameKey, nil]];
[[commandBundleTree objectForKey:kChildrenKey] addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:[NSMutableArray array], kChildrenKey, SP_BUNDLEEDITOR_SCOPE_GENERAL_STRING, kBundleNameKey, nil]];
+ [commandBundleTreeController setContent:commandBundleTree];
// Init all needed menus
inputGeneralScopePopUpMenu = [[NSMenu alloc] initWithTitle:@""];
@@ -353,6 +365,7 @@
SPBundleShellVariableSelectedTable,
SPBundleShellVariableSelectedTables,
SPBundleShellVariableSelectedText,
+ SPBundleShellVariableSelectedTextRange,
SPBundleShellVariableUsedQueryForTable,
nil
] retain];
@@ -1031,10 +1044,13 @@
if(deletionSuccessfully) {
[commandBundleTreeController removeObjectsAtArrangedObjectIndexPaths:selIndexPaths];
[commandBundleTreeController rearrangeObjects];
- } else {
- [self reloadBundles:self];
}
+ [self reloadBundles:self];
+
+ [commandBundleTreeController setSelectionIndexPath:[[selIndexPaths objectAtIndex:0] indexPathByRemovingLastIndex]];
+ [commandsOutlineView expandItem:[self _currentSelectedNode] expandChildren:NO];
+
// Set focus to table view to avoid an unstable state
[[self window] makeFirstResponder:commandsOutlineView];
@@ -1291,7 +1307,7 @@
[touchedBundleArray addObject:oldBundleName];
[self _updateBundleDataView];
-
+ [commandTextView setSelectedRange:NSMakeRange(0,0)];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldShowOutlineCellForItem:(id)item
@@ -1793,7 +1809,6 @@
[removeButton setEnabled:([[commandBundleTreeController selectedObjects] count] == 1 && ![[[commandBundleTreeController selectedObjects] objectAtIndex:0] objectForKey:kChildrenKey])];
[addButton setEnabled:([[commandBundleTreeController selectionIndexPath] length] > 1)];
- [commandBundleTreeController setContent:commandBundleTree];
NSUInteger *selPath[2];
selPath[0] = 0;
selPath[1] = 0;
diff --git a/Source/SPBundleHTMLOutputController.h b/Source/SPBundleHTMLOutputController.h
index ea0b96da..3665fc8e 100644
--- a/Source/SPBundleHTMLOutputController.h
+++ b/Source/SPBundleHTMLOutputController.h
@@ -33,6 +33,7 @@
NSString *initHTMLSourceString;
NSString *windowUUID;
NSString *docUUID;
+ BOOL suppressExceptionAlert;
WebPreferences *webPreferences;
}
@@ -41,6 +42,7 @@
@property(readwrite,retain) NSString *initHTMLSourceString;
@property(readwrite,retain) NSString *windowUUID;
@property(readwrite,retain) NSString *docUUID;
+@property(assign) BOOL suppressExceptionAlert;
- (IBAction)printDocument:(id)sender;
diff --git a/Source/SPBundleHTMLOutputController.m b/Source/SPBundleHTMLOutputController.m
index 7bcff455..3c47964a 100644
--- a/Source/SPBundleHTMLOutputController.m
+++ b/Source/SPBundleHTMLOutputController.m
@@ -33,6 +33,7 @@
@synthesize initHTMLSourceString;
@synthesize windowUUID;
@synthesize docUUID;
+@synthesize suppressExceptionAlert;
/**
* Initialisation
@@ -50,6 +51,7 @@
[webView setEditable:NO];
[webView setShouldCloseWithWindow:YES];
[webView setShouldUpdateWhileOffscreen:NO];
+ suppressExceptionAlert = NO;
}
@@ -364,8 +366,18 @@
return @"run";
if (aSelector == @selector(getShellEnvironmentForName:))
return @"getShellEnvironmentForName";
+ if (aSelector == @selector(insertText:))
+ return @"insertText";
+ if (aSelector == @selector(setText:))
+ return @"setText";
+ if (aSelector == @selector(setSelectedTextRange:))
+ return @"setSelectedTextRange";
if (aSelector == @selector(makeHTMLOutputWindowKeyWindow))
return @"makeHTMLOutputWindowKeyWindow";
+ if (aSelector == @selector(closeHTMLOutputWindow))
+ return @"closeHTMLOutputWindow";
+ if (aSelector == @selector(suppressExceptionAlert))
+ return @"suppressExceptionAlert";
return @"";
}
@@ -376,9 +388,24 @@
if (selector == @selector(getShellEnvironmentForName:)) {
return NO;
}
+ if (selector == @selector(insertText:)) {
+ return NO;
+ }
+ if (selector == @selector(setText:)) {
+ return NO;
+ }
+ if (selector == @selector(setSelectedTextRange:)) {
+ return NO;
+ }
if (selector == @selector(makeHTMLOutputWindowKeyWindow)) {
return NO;
}
+ if (selector == @selector(closeHTMLOutputWindow)) {
+ return NO;
+ }
+ if (selector == @selector(suppressExceptionAlert)) {
+ return NO;
+ }
return YES;
}
@@ -389,6 +416,15 @@
if (strcmp(property, "getShellEnvironmentForName") == 0) {
return NO;
}
+ if (strcmp(property, "insertText") == 0) {
+ return NO;
+ }
+ if (strcmp(property, "setText") == 0) {
+ return NO;
+ }
+ if (strcmp(property, "setSelectedTextRange") == 0) {
+ return NO;
+ }
if (strcmp(property, "makeHTMLOutputWindowKeyWindow") == 0) {
return NO;
}
@@ -411,8 +447,14 @@
- (void)webView:(WebView *)webView exceptionWasRaised:(WebScriptCallFrame *)frame sourceId:(NSInteger)sid line:(NSInteger)lineno forWebFrame:(WebFrame *)webFrame
{
+
NSString *mes = [NSString stringWithFormat:@"Exception:\nline = %ld\nfunction = %@\ncaller = %@\nexception = %@", lineno, [frame functionName], [frame caller], [frame userInfo], [frame exception]];
+ if([self suppressExceptionAlert]) {
+ NSLog(@"%@", mes);
+ return;
+ }
+
NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"JavaScript Exception", @"javascript exception")
defaultButton:NSLocalizedString(@"OK", @"OK button")
alternateButton:nil
@@ -441,6 +483,70 @@
}
/**
+ * JavaScript window.system.makeHTMLOutputWindowKeyWindow() function
+ * to close the HTML window
+ */
+- (void)closeHTMLOutputWindow
+{
+ [[self window] close];
+}
+
+/**
+ * JavaScript window.system.insertText(text) function
+ * to insert text into the first responder
+ */
+- (void)insertText:(NSString*)text
+{
+ id firstResponder = [[NSApp keyWindow] firstResponder];
+ if([firstResponder isKindOfClass:[NSTextView class]]) {
+ [firstResponder insertText:text];
+ return;
+ }
+ NSBeep();
+}
+
+/**
+ * JavaScript window.system.setText(text) function
+ * to set the content of the first responder to text
+ */
+- (void)setText:(NSString*)text
+{
+ id firstResponder = [[NSApp keyWindow] firstResponder];
+ if([firstResponder isKindOfClass:[NSTextView class]]) {
+ [firstResponder setSelectedRange:NSMakeRange(0, [[firstResponder string] length])];
+ [firstResponder insertText:text];
+ return;
+ }
+ NSBeep();
+}
+
+/**
+ * JavaScript window.system.setSelectedRange({location,length}) function
+ * to set the selection range of the first responder
+ */
+- (void)setSelectedTextRange:(NSString*)range
+{
+ id firstResponder = [[NSApp keyWindow] firstResponder];
+ if([firstResponder isKindOfClass:[NSTextView class]]) {
+ NSRange theRange = NSIntersectionRange(NSRangeFromString(range), NSMakeRange(0, [[firstResponder string] length]));
+ if(theRange.location != NSNotFound) {
+ [firstResponder setSelectedRange:theRange];
+ }
+ return;
+ }
+ NSBeep();
+}
+
+/**
+ * JavaScript window.system.suppressExceptionAlert() function
+ * to suppress an exception alert, instead write the message to NSLog
+ */
+- (void)suppressExceptionAlert
+{
+ [self setSuppressExceptionAlert:YES];
+}
+
+/**
* JavaScript window.system.run('a_command'|new Array('a_command', 'uuid')) function
* to return the result of the BASH command a_command
*/
diff --git a/Source/SPConnectionController.h b/Source/SPConnectionController.h
index 73a7ef77..590349bc 100644
--- a/Source/SPConnectionController.h
+++ b/Source/SPConnectionController.h
@@ -83,6 +83,7 @@
NSString *sshKeyLocation;
NSString *sshPort;
+ NSString *connectionKeychainID;
NSString *connectionKeychainItemName;
NSString *connectionKeychainItemAccount;
NSString *connectionSSHKeychainItemName;
diff --git a/Source/SPConnectionController.m b/Source/SPConnectionController.m
index c12f1ac4..9f542cec 100644
--- a/Source/SPConnectionController.m
+++ b/Source/SPConnectionController.m
@@ -285,15 +285,10 @@ static NSComparisonResult compareFavoritesUsingKey(id favorite1, id favorite2, v
[favoritesOutlineView setEnabled:NO];
[addToFavoritesButton setHidden:YES];
- [addToFavoritesButton display];
[helpButton setHidden:YES];
- [helpButton display];
[connectButton setEnabled:NO];
- [connectButton display];
[progressIndicator startAnimation:self];
- [progressIndicator display];
[progressIndicatorText setHidden:NO];
- [progressIndicatorText display];
// Start the current tab's progress indicator
[dbDocument setIsProcessing:YES];
@@ -424,7 +419,7 @@ static NSComparisonResult compareFavoritesUsingKey(id favorite1, id favorite2, v
[self setSslCertificateFileLocation:nil];
return;
}
- permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"cert", @"", nil];
+ permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"cert", @"crt", @"", nil];
[openPanel setAccessoryView:sslCertificateLocationHelp];
// SSL CA certificate file location:
@@ -433,7 +428,7 @@ static NSComparisonResult compareFavoritesUsingKey(id favorite1, id favorite2, v
[self setSslCACertFileLocation:nil];
return;
}
- permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"cert", @"", nil];
+ permittedFileTypes = [NSArray arrayWithObjects:@"pem", @"cert", @"crt", @"", nil];
[openPanel setAccessoryView:sslCACertLocationHelp];
}
diff --git a/Source/SPConnectionHandler.m b/Source/SPConnectionHandler.m
index 6d777ff5..adf21ffa 100644
--- a/Source/SPConnectionHandler.m
+++ b/Source/SPConnectionHandler.m
@@ -330,13 +330,7 @@ certificateAuthorityCertificatePath:[self sslCACertFileLocationEnabled] ? [self
for (NSInteger i = 0; i < [toolbarItems count]; i++) [[toolbarItems objectAtIndex:i] setEnabled:YES];
- // Set keychain id for saving SPF files
- if ([self valueForKeyPath:@"selectedFavorite.id"]) {
- [dbDocument setKeychainID:[[self valueForKeyPath:@"selectedFavorite.id"] stringValue]];
- }
- else {
- [dbDocument setKeychainID:@""];
- }
+ if (connectionKeychainID) [dbDocument setKeychainID:connectionKeychainID];
// Pass the connection to the table document, allowing it to set
// up the other classes and the rest of the interface.
diff --git a/Source/SPConstants.h b/Source/SPConstants.h
index c5a932ea..3a15f71c 100644
--- a/Source/SPConstants.h
+++ b/Source/SPConstants.h
@@ -180,6 +180,16 @@ typedef enum
SPEncodingEUCKRKorean = 180
} SPEncodingTypes;
+// Table index type menu tags
+typedef enum
+{
+ SPPrimaryKeyMenuTag = 0,
+ SPIndexMenuTag = 1,
+ SPUniqueMenuTag = 2,
+ SPFullTextMenuTag = 3,
+ SPSpatialMenuTag = 4
+} SPTableIndexTypeTags;
+
// File compression formats
typedef enum
{
@@ -312,6 +322,8 @@ extern NSString *SPCustomQueryAutoComplete;
extern NSString *SPCustomQueryAutoCompleteDelay;
extern NSString *SPCustomQueryFunctionCompletionInsertsArguments;
extern NSString *SPCustomQueryEditorThemeName;
+extern NSString *SPCustomQuerySoftIndent;
+extern NSString *SPCustomQuerySoftIndentWidth;
// AutoUpdate Prefpane
extern NSString *SPLastUsedVersion;
@@ -369,7 +381,6 @@ extern NSString *SPLastImportIntoNewTableEncoding;
extern NSString *SPLastImportIntoNewTableType;
extern NSString *SPGlobalValueHistory;
extern NSString *SPBundleDeletedDefaultBundlesKey;
-extern NSString *SPBundleUpdatedDefaultBundlesKey;
// URLs
extern NSString *SPDonationsURL;
@@ -510,6 +521,7 @@ extern NSString *SPBundleShellVariableSelectedText;
extern NSString *SPBundleShellVariableCurrentWord;
extern NSString *SPBundleShellVariableCurrentLine;
extern NSString *SPBundleShellVariableSelectedRowIndices;
+extern NSString *SPBundleShellVariableSelectedTextRange;
extern NSString *SPBundleShellVariableAllDatabases;
extern NSString *SPBundleShellVariableSelectedTables;
extern NSString *SPBundleShellVariableSelectedDatabase;
diff --git a/Source/SPConstants.m b/Source/SPConstants.m
index c7ac921d..df1d71dc 100644
--- a/Source/SPConstants.m
+++ b/Source/SPConstants.m
@@ -123,6 +123,8 @@ NSString *SPCustomQueryAutoComplete = @"CustomQueryAutoComplete";
NSString *SPCustomQueryAutoCompleteDelay = @"CustomQueryAutoCompleteDelay";
NSString *SPCustomQueryFunctionCompletionInsertsArguments = @"CustomQueryFunctionCompletionInsertsArguments";
NSString *SPCustomQueryEditorThemeName = @"CustomQueryEditorThemeName";
+NSString *SPCustomQuerySoftIndent = @"CustomQuerySoftIndent";
+NSString *SPCustomQuerySoftIndentWidth = @"CustomQuerySoftIndentWidth";
// AutoUpdate Prefpane
NSString *SPLastUsedVersion = @"LastUsedVersion";
@@ -180,11 +182,10 @@ NSString *SPLastImportIntoNewTableEncoding = @"LastImportIntoNewTableEncod
NSString *SPLastImportIntoNewTableType = @"LastImportIntoNewTableType";
NSString *SPGlobalValueHistory = @"GlobalValueHistory";
NSString *SPBundleDeletedDefaultBundlesKey = @"deletedDefaultBundles";
-NSString *SPBundleUpdatedDefaultBundlesKey = @"updatedDefaultBundles";
// URLs
NSString *SPDonationsURL = @"http://www.sequelpro.com/donate.html";
-NSString *SPMySQLSearchURL = @"http://search.mysql.com/search?q=%@&site=refman-%@&lr=lang_%@";
+NSString *SPMySQLSearchURL = @"http://search.mysql.com/search/query/search?q=%@&group=refman-%@";
NSString *SPDevURL = @"http://code.google.com/p/sequel-pro/";
// Toolbar constants
@@ -325,7 +326,7 @@ NSString *SPBundleShellVariableExitShowAsHTML = @"SP_BUNDLE_EXIT_S
NSString *SPBundleShellVariableExitShowAsHTMLTooltip = @"SP_BUNDLE_EXIT_SHOW_AS_HTML_TOOLTIP";
NSString *SPBundleShellVariableExitInsertAsSnippet = @"SP_BUNDLE_EXIT_INSERT_AS_SNIPPET";
NSString *SPBundleShellVariableExitInsertAsText = @"SP_BUNDLE_EXIT_INSERT_AS_TEXT";
-NSString *SPBundleShellVariableExitShowAsTextTooltip = @"SP_BUNDLE_EXIT_SHOW_AS_TEXT_TOOLTIP";
+NSString *SPBundleShellVariableExitShowAsTextTooltip = @"SP_BUNDLE_EXIT_SHOW_AS_TEXT_TOOLTIP";
NSString *SPBundleShellVariableExitNone = @"SP_BUNDLE_EXIT_NONE";
NSString *SPBundleShellVariableExitReplaceContent = @"SP_BUNDLE_EXIT_REPLACE_CONTENT";
NSString *SPBundleShellVariableExitReplaceSelection = @"SP_BUNDLE_EXIT_REPLACE_SELECTION";
@@ -345,6 +346,7 @@ NSString *SPBundleShellVariableSelectedRowIndices = @"SP_SELECTED_ROW_
NSString *SPBundleShellVariableSelectedTable = @"SP_SELECTED_TABLE";
NSString *SPBundleShellVariableSelectedTables = @"SP_SELECTED_TABLES";
NSString *SPBundleShellVariableSelectedText = @"SP_SELECTED_TEXT";
+NSString *SPBundleShellVariableSelectedTextRange = @"SP_SELECTED_TEXT_RANGE";
NSString *SPBundleShellVariableUsedQueryForTable = @"SP_USED_QUERY_FOR_TABLE";
const NSInteger SPBundleRedirectActionNone = 200;
diff --git a/Source/SPCopyTable.m b/Source/SPCopyTable.m
index 827672b5..3b84d867 100644
--- a/Source/SPCopyTable.m
+++ b/Source/SPCopyTable.m
@@ -698,6 +698,10 @@ NSInteger kBlobAsImageFile = 4;
NSUInteger columnWidth;
NSUInteger allColumnWidths = 0;
+ // Determine the available size
+ NSScrollView *parentScrollView = [[self superview] superview];
+ CGFloat visibleTableWidth = [parentScrollView bounds].size.width - [NSScroller scrollerWidth] - [columnDefinitions count] * 3.5;
+
for (NSDictionary *columnDefinition in columnDefinitions) {
if ([[NSThread currentThread] isCancelled]) return nil;
@@ -707,7 +711,7 @@ NSInteger kBlobAsImageFile = 4;
}
// Compare the column widths to the table width. If wider, narrow down wide columns as necessary
- if (allColumnWidths > [self bounds].size.width) {
+ if (allColumnWidths > visibleTableWidth) {
NSUInteger availableWidthToReduce = 0;
// Look for columns that are wider than the multi-column max
@@ -717,7 +721,7 @@ NSInteger kBlobAsImageFile = 4;
}
// Determine how much width can be reduced
- NSUInteger widthToReduce = allColumnWidths - [self bounds].size.width;
+ NSUInteger widthToReduce = allColumnWidths - visibleTableWidth;
if (availableWidthToReduce < widthToReduce) widthToReduce = availableWidthToReduce;
// Proportionally decrease the column sizes
@@ -930,6 +934,10 @@ NSInteger kBlobAsImageFile = 4;
{
NSInteger menuItemTag = [anItem tag];
+ if ([anItem action] == @selector(performFindPanelAction:)) {
+ return (menuItemTag == 1 && [[self delegate] isKindOfClass:[SPTableContent class]]);
+ }
+
// Don't validate anything other than the copy commands
if (menuItemTag != MENU_EDIT_COPY && menuItemTag != MENU_EDIT_COPY_WITH_COLUMN && menuItemTag != MENU_EDIT_COPY_AS_SQL) {
return YES;
@@ -1091,6 +1099,13 @@ NSInteger kBlobAsImageFile = 4;
[super keyDown:theEvent];
}
+- (void)performFindPanelAction:(id)sender
+{
+ if([sender tag] == 1 && [[self delegate] isKindOfClass:[SPTableContent class]]) {
+ [[self delegate] showFilterTable:self];
+ }
+}
+
#pragma mark -
#pragma mark Bundle Command Support
@@ -1221,33 +1236,47 @@ NSInteger kBlobAsImageFile = 4;
return;
}
+
+ // Create an array of table column mappings for fast iteration
+ NSArray *columns = [self tableColumns];
+ NSUInteger numColumns = [columns count];
+ NSUInteger *columnMappings = malloc(numColumns * sizeof(NSUInteger));
+ NSInteger c;
+ for ( c = 0; c < numColumns; c++ )
+ columnMappings[c] = [[NSArrayObjectAtIndex(columns, c) identifier] unsignedIntValue];
+
NSMutableString *tableMetaData = [NSMutableString string];
if([[self delegate] isKindOfClass:[SPCustomQuery class]]) {
[env setObject:@"query" forKey:SPBundleShellVariableDataTableSource];
NSArray *defs = [[self delegate] dataColumnDefinitions];
- for(NSDictionary* col in defs) {
- [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"type"]];
- [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"typegrouping"]];
- [tableMetaData appendFormat:@"%@\t", ([col objectForKey:@"char_length"]) ? : @""];
- [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"UNSIGNED_FLAG"]];
- [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"AUTO_INCREMENT_FLAG"]];
- [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"PRI_KEY_FLAG"]];
- [tableMetaData appendString:@"\n"];
- }
+ if(defs && [defs count] == numColumns)
+ for( c = 0; c < numColumns; c++ ) {
+ NSDictionary *col = NSArrayObjectAtIndex(defs, columnMappings[c]);
+ [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"type"]];
+ [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"typegrouping"]];
+ [tableMetaData appendFormat:@"%@\t", ([col objectForKey:@"char_length"]) ? : @""];
+ [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"UNSIGNED_FLAG"]];
+ [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"AUTO_INCREMENT_FLAG"]];
+ [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"PRI_KEY_FLAG"]];
+ [tableMetaData appendString:@"\n"];
+ }
}
else if([[self delegate] isKindOfClass:[SPTableContent class]]) {
[env setObject:@"content" forKey:SPBundleShellVariableDataTableSource];
NSArray *defs = [[self delegate] dataColumnDefinitions];
- for(NSDictionary* col in defs) {
- [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"type"]];
- [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"typegrouping"]];
- [tableMetaData appendFormat:@"%@\t", ([col objectForKey:@"length"]) ? : @""];
- [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"unsigned"]];
- [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"autoincrement"]];
- [tableMetaData appendFormat:@"%@\t", ([col objectForKey:@"isprimarykey"]) ? : @"0"];
- [tableMetaData appendFormat:@"%@\n", [col objectForKey:@"comment"]];
- }
+ if(defs && [defs count] == numColumns)
+ for( c = 0; c < numColumns; c++ ) {
+ NSDictionary *col = NSArrayObjectAtIndex(defs, columnMappings[c]);
+ [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"type"]];
+ [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"typegrouping"]];
+ [tableMetaData appendFormat:@"%@\t", ([col objectForKey:@"length"]) ? : @""];
+ [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"unsigned"]];
+ [tableMetaData appendFormat:@"%@\t", [col objectForKey:@"autoincrement"]];
+ [tableMetaData appendFormat:@"%@\t", ([col objectForKey:@"isprimarykey"]) ? : @"0"];
+ [tableMetaData appendFormat:@"%@\n", [col objectForKey:@"comment"]];
+ }
}
+ free(columnMappings);
inputFileError = nil;
[tableMetaData writeToFile:bundleInputTableMetaDataFilePath
diff --git a/Source/SPCustomQuery.h b/Source/SPCustomQuery.h
index f4c2440d..317db0c9 100644
--- a/Source/SPCustomQuery.h
+++ b/Source/SPCustomQuery.h
@@ -147,6 +147,7 @@
BOOL queryIsTableSorter;
BOOL isDesc;
BOOL isFieldEditable;
+ BOOL textViewWasChanged;
NSNumber *sortField;
NSIndexSet *selectionIndexToRestore;
@@ -170,6 +171,8 @@
NSString *kCellEditorErrorTooManyMatches;
}
+@property(assign) BOOL textViewWasChanged;
+
// IBAction methods
- (IBAction)runAllQueries:(id)sender;
- (void) runAllQueriesCallback;
@@ -245,7 +248,7 @@
- (void)commentOutCurrentQueryTakingSelection:(BOOL)takeSelection;
- (NSString *)usedQuery;
- (NSString *)argumentForRow:(NSUInteger)rowIndex ofTable:(NSString *)tableForColumn andDatabase:(NSString *)database includeBlobs:(BOOL)includeBlobs;
-- (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSInteger)columnIndex;
+- (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSNumber *)columnIndex;
- (NSUInteger)numberOfQueries;
- (NSRange)currentQueryRange;
- (NSString *)buildHistoryString;
diff --git a/Source/SPCustomQuery.m b/Source/SPCustomQuery.m
index e8420b7c..b2859010 100644
--- a/Source/SPCustomQuery.m
+++ b/Source/SPCustomQuery.m
@@ -45,6 +45,8 @@
@implementation SPCustomQuery
+@synthesize textViewWasChanged;
+
#pragma mark IBAction methods
/*
@@ -940,7 +942,8 @@
// Split the current text into ranges of queries
// only if the textView was really changed, otherwise use the cache
- if([[textView textStorage] editedMask] != 0) {
+ if([[textView textStorage] editedMask] != 0 || [self textViewWasChanged]) {
+ [self setTextViewWasChanged:NO];
customQueryParser = [[SPSQLParser alloc] initWithString:[textView string]];
[customQueryParser setDelimiterSupport:YES];
queries = [[NSArray alloc] initWithArray:[customQueryParser splitStringIntoRangesByCharacter:';']];
@@ -1634,7 +1637,7 @@
* -2 for other errors
* and the used WHERE clause to identify
*/
-- (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSInteger)columnIndex
+- (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSNumber*)columnIndex
{
NSDictionary *columnDefinition = nil;
@@ -2580,6 +2583,7 @@
BOOL isLookBehind = YES;
NSRange currentSelection = [textView selectedRange];
NSUInteger caretPosition = currentSelection.location;
+
NSRange qRange = [self queryRangeAtPosition:caretPosition lookBehind:&isLookBehind];
if(qRange.length)
@@ -3679,8 +3683,9 @@
column = [customQueryView editedColumn];
// Retrieve the column defintion
+ NSNumber *colIdentifier = [NSArrayObjectAtIndex([customQueryView tableColumns], column) identifier];
for(id c in cqColumnDefinition) {
- if([[c objectForKey:@"datacolumnindex"] isEqualToNumber:[NSNumber numberWithInteger:column]]) {
+ if([[c objectForKey:@"datacolumnindex"] isEqualToNumber:colIdentifier]) {
columnDefinition = [NSDictionary dictionaryWithDictionary:c];
break;
}
@@ -3688,8 +3693,8 @@
if(!columnDefinition) return NO;
- NSArray *editStatus = [self fieldEditStatusForRow:row andColumn:[NSArrayObjectAtIndex([customQueryView tableColumns], column) identifier]];
- NSInteger numberOfPossibleUpdateRows = [[editStatus objectAtIndex:0] integerValue];
+ NSArray *editStatus = [self fieldEditStatusForRow:row andColumn:colIdentifier];
+ NSInteger numberOfPossibleUpdateRows = [NSArrayObjectAtIndex(editStatus, 0) integerValue];
NSPoint pos = [[tableDocumentInstance parentWindow] convertBaseToScreen:[customQueryView convertPoint:[customQueryView frameOfCellAtColumn:column row:row].origin toView:nil]];
pos.y -= 20;
switch(numberOfPossibleUpdateRows) {
diff --git a/Source/SPDatabaseDocument.h b/Source/SPDatabaseDocument.h
index bcd92d59..3459b9b8 100644
--- a/Source/SPDatabaseDocument.h
+++ b/Source/SPDatabaseDocument.h
@@ -65,6 +65,9 @@
IBOutlet NSSearchField *listFilterField;
+ IBOutlet NSScrollView *tableInfoScrollView;
+ IBOutlet NSScrollView *activitiesScrollView;
+
IBOutlet NSView *parentView;
IBOutlet id titleAccessoryView;
@@ -363,6 +366,7 @@
- (void)handleSchemeCommand:(NSDictionary*)commandDict;
- (void)registerActivity:(NSDictionary*)commandDict;
- (void)removeRegisteredActivity:(NSInteger)pid;
+- (void)setActivityPaneHidden:(NSNumber*)hide;
- (NSArray*)runningActivities;
- (NSDictionary*)shellVariables;
diff --git a/Source/SPDatabaseDocument.m b/Source/SPDatabaseDocument.m
index 787817bc..e122205e 100644
--- a/Source/SPDatabaseDocument.m
+++ b/Source/SPDatabaseDocument.m
@@ -56,18 +56,6 @@
#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)
@@ -2514,8 +2502,10 @@ YY_BUFFER_STATE yy_scan_string (const char *);
[saveConnectionEncrypt setState:[[spfDocData objectForKey:@"encrypted"] boolValue]];
if([spfDocData objectForKey:@"include_session"])
[saveConnectionIncludeData setState:[[spfDocData objectForKey:@"include_session"] boolValue]];
- if([spfDocData objectForKey:@"save_editor_content"])
+ if([[spfDocData objectForKey:@"save_editor_content"] boolValue])
[saveConnectionIncludeQuery setState:[[spfDocData objectForKey:@"save_editor_content"] boolValue]];
+ else
+ [saveConnectionIncludeQuery setState:NSOnState];
[saveConnectionIncludeQuery setEnabled:([[[[customQueryInstance valueForKeyPath:@"textView"] textStorage] string] length])];
@@ -2568,8 +2558,10 @@ YY_BUFFER_STATE yy_scan_string (const char *);
[saveConnectionEncrypt setState:[[spfSessionData objectForKey:@"encrypted"] boolValue]];
if([spfSessionData objectForKey:@"include_session"])
[saveConnectionIncludeData setState:[[spfSessionData objectForKey:@"include_session"] boolValue]];
- if([spfSessionData objectForKey:@"save_editor_content"])
+ if([[spfSessionData objectForKey:@"save_editor_content"] boolValue])
[saveConnectionIncludeQuery setState:[[spfSessionData objectForKey:@"save_editor_content"] boolValue]];
+ else
+ [saveConnectionIncludeQuery setState:YES];
// Update accessory button states
[self validateSaveConnectionAccessory:nil];
@@ -4558,86 +4550,6 @@ YY_BUFFER_STATE yy_scan_string (const char *);
#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:@"&nbsp;&nbsp;&nbsp;&nbsp;" options:NSLiteralSearch range:NSMakeRange(0, [sqlHTML length])];
-
- if(sqlHTML)
- return sqlHTML;
- else
- return @"";
-
-}
-
-/**
* Called by handleSchemeCommand: to break a while loop
*/
- (void)setTimeout
@@ -4733,57 +4645,52 @@ YY_BUFFER_STATE yy_scan_string (const char *);
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";
+ // ==== the following commands need an authentication for safety reasons
- if([fm fileExistsAtPath:queryFileName isDirectory:&isDir] && !isDir) {
+ // 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(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]];
+ if([command isEqualToString:@"SetSelectedTextRange"]) {
+ if([params count] > 1) {
+ id firstResponder = [parentWindow firstResponder];
+ if([firstResponder isKindOfClass:[NSTextView class]]) {
+ NSRange theRange = NSIntersectionRange(NSRangeFromString([params objectAtIndex:1]), NSMakeRange(0, [[firstResponder string] length]));
+ if(theRange.location != NSNotFound) {
+ [firstResponder setSelectedRange:theRange];
}
+ return;
}
+ NSBeep();
}
+ return;
+ }
- [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) {
+ if([command isEqualToString:@"InsertText"]) {
+ if([params count] > 1) {
+ id firstResponder = [parentWindow firstResponder];
+ if([firstResponder isKindOfClass:[NSTextView class]]) {
+ [firstResponder insertText:[params objectAtIndex:1]];
+ return;
+ }
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"));
+ if([command isEqualToString:@"SetText"]) {
+ if([params count] > 1) {
+ id firstResponder = [parentWindow firstResponder];
+ if([firstResponder isKindOfClass:[NSTextView class]]) {
+ [firstResponder setSelectedRange:NSMakeRange(0, [[firstResponder string] length])];
+ [firstResponder insertText:[params objectAtIndex:1]];
+ return;
+ }
+ NSBeep();
+ }
return;
}
@@ -5141,17 +5048,50 @@ YY_BUFFER_STATE yy_scan_string (const char *);
{
[runningActivitiesArray addObject:commandDict];
[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPActivitiesUpdateNotification object:nil];
+
+ if([runningActivitiesArray count] || [[[NSApp delegate] runningActivities] count])
+ [self performSelector:@selector(setActivityPaneHidden:) withObject:[NSNumber numberWithInteger:0] afterDelay:1.0];
+ else {
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(setActivityPaneHidden:)
+ object:[NSNumber numberWithInteger:0]];
+ [self setActivityPaneHidden:[NSNumber numberWithInteger:1]];
+ }
+
}
- (void)removeRegisteredActivity:(NSInteger)pid
{
+
for(id cmd in runningActivitiesArray) {
if([[cmd objectForKey:@"pid"] integerValue] == pid) {
[runningActivitiesArray removeObject:cmd];
break;
}
}
+
+ if([runningActivitiesArray count] || [[[NSApp delegate] runningActivities] count])
+ [self performSelector:@selector(setActivityPaneHidden:) withObject:[NSNumber numberWithInteger:0] afterDelay:1.0];
+ else {
+ [NSObject cancelPreviousPerformRequestsWithTarget:self
+ selector:@selector(setActivityPaneHidden:)
+ object:[NSNumber numberWithInteger:0]];
+ [self setActivityPaneHidden:[NSNumber numberWithInteger:1]];
+ }
+
[[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:SPActivitiesUpdateNotification object:nil];
+
+}
+
+- (void)setActivityPaneHidden:(NSNumber*)hide
+{
+ if(![hide integerValue] == 1) {
+ [tableInfoScrollView setHidden:YES];
+ [activitiesScrollView setHidden:NO];
+ } else {
+ [activitiesScrollView setHidden:YES];
+ [tableInfoScrollView setHidden:NO];
+ }
}
- (NSArray*)runningActivities
diff --git a/Source/SPEditorTokens.l b/Source/SPEditorTokens.l
index a5726cce..7b19804a 100644
--- a/Source/SPEditorTokens.l
+++ b/Source/SPEditorTokens.l
@@ -56,9 +56,9 @@ ops "+"|"-"|"*"|"/"
word [a-z_\.0-9À-゚@]
variable @{1,2}[a-z_\.0-9À-゚$]+
nonword [^a-z_0-9À-゚#\n\t\r]
-keyworda (G(R(OUP{s}BY|ANT(S)?)|E(T_FORMAT|OMETRY(COLLECTION)?)|LOBAL)|B(Y(TE)?|TREE|I(GINT|N(LOG|ARY)|T)|O(TH|OL(EAN)?)|E(GIN|TWEEN|FORE)|LOB|ACKUP{s}TABLE)|H(IGH_PRIORITY|O(STS|UR(_(MI(NUTE|CROSECOND)|SECOND))?)|ELP|A(SH|NDLER|VING))|C(R(OSS|EATE)|H(ECK(SUM)?|A(R(SET|ACTER)?|NGE(D)?|IN))|IPHER|O(M(M(IT(TED)?|ENT)|P(RESSED|LETION|ACT))|N(S(TRAINT|ISTENT)|NECTION|CURRENT|T(RIBUTORS|INUE|AINS)|DITION|VERT)|DE|L(UMN(_FORMAT)?|LATE)|ALESCE{s}PARTITION)|U(R(RENT_(TIME(STAMP)?|DATE|USER)|SOR)|BE)|L(IENT|OSE)|A(S(CADE(D)?|E)|CHE{s}INDEX|LL))|I(GNORE|MPORT{s}TABLESPACE|S(SUER|OLATION)?|N(S(TALL({s}PLUGIN)?|E(RT(_METHOD)?|NSITIVE))|N(O(BASE|DB)|ER)|T(1|2|8|3|O({s}(DUMP|OUT)FILE)?|4|E(RVAL|GER))?|ITIAL_SIZE|OUT|DEX(ES)?|VOKER|FILE)?|TERATE|O_THREAD|DENTIFIED|F)|D(ROP|YNAMIC|I(RECTORY|S(CARD{s}TABLESPACE|TINCT(ROW)?|K|ABLE{s}KEYS)|V)|O(UBLE)?|U(MPFILE|PLICATE|AL)|E(S(C(RIBE)?|_KEY_FILE)|C(IMAL|LARE)?|TERMINISTIC|F(INER|AULT)|L(ETE|AY(_KEY_WRITE|ED))|ALLOCATE)|A(Y(_(MI(NUTE|CROSECOND)|SECOND|HOUR))?|T(E(TIME)?|A(BASE(S)?|FILE)?)))|JOIN|E(RRORS|X(TEN(T_SIZE|DED)|I(STS|T)|P(LAIN|ANSION)|ECUTE)|SCAPE(D{s}BY)?|N(GINE(S)?|CLOSED{s}BY|D(S)?|UM|ABLE{s}KEYS)|VE(RY|NT)|LSE(IF)?|ACH)|K(ILL({s}(CONNECTION|QUERY))?|EY(S|_BLOCK_SIZE)?)|F(R(OM|AC_SECOND)|I(RST|XED|LE)|O(R(CE|EIGN)?|UND)|U(NCTION|LL(TEXT)?)|ETCH|L(OAT(8|4)?|USH)|A(ST|LSE))|A(G(GREGATE|AINST)|S(C(II)?|ENSITIVE)?|N(Y|D|ALYZE)|C(CESSIBLE|TION)|T|DD|UT(HORS|O(_INCREMENT|EXTEND_SIZE))|VG(_ROW_LENGTH)?|FTER|L(GORITHM|TER|L)))
-keywordl (R(TREE|IGHT|O(UTINE|W(S|_FORMAT)?|LL(BACK|UP))|E(GEXP|MOVE{s}PARTITIONING|BUILD{s}PARTITION|S(T(RICT|ORE{s}TABLE)|UME|ET)|NAME|COVER|TURN(S)?|ORGANIZE{s}PARTITION|D(O(_BUFFER_SIZE|FILE)|UNDANT)|P(EAT(ABLE)?|L(ICATION|ACE)|AIR)|VOKE|QUIRE|FERENCES|L(OAD|EASE|AY_(THREAD|LOG_(POS|FILE)))|A(D(S|_(ONLY|WRITE))?|L))|LIKE|ANGE)|M(I(GRATE|N(_ROWS|UTE(_(MICROSECOND|SECOND))?)|CROSECOND|DDLEINT)|O(NTH|D(IF(Y|IES)|E)?)|U(TEX|LTI(PO(INT|LYGON)|LINESTRING))|E(RGE|MORY|DIUM(BLOB|TEXT|INT)?)|A(X(_(ROWS|SIZE|CONNECTIONS_PER_HOUR|U(SER_CONNECTIONS|PDATES_PER_HOUR)|QUERIES_PER_HOUR)|VALUE)|STER(_(S(SL(_(C(IPHER|ERT|A(PATH)?)|VERIFY_SERVER_CERT|KEY))?|ERVER_ID)|HOST|CONNECT_RETRY|USER|P(ORT|ASSWORD)|LOG_(POS|FILE)))?|TCH))|N(CHAR|O(NE|_W(RITE_TO_BINLOG|AIT)|T|DEGROUP)?|DB(CLUSTER)?|U(MERIC|LL)|E(XT|W)|VARCHAR|A(ME(S)?|T(IONAL|URAL)))|O(R(DER{s}BY)?|N({s}(DUPLICATE{s}KEY{s}UPDATE)?|E(_SHOT)?|LINE)|UT(ER|FILE)?|P(TI(MIZE|ON(S|ALLY)?)|EN)|FF(SET|LINE)|LD_PASSWORD)|P(R(I(MARY|VILEGES)|OCE(SS|DURE{s}(ANALYSE)?)|E(SERVE|CISION|PARE|V))|HASE|O(INT|LYGON)|URGE|A(R(SER|TI(TION(S|ING)?|AL))|SSWORD|CK_KEYS))|QU(ICK|ERY|ARTER)|L(I(MIT|ST|NE(S(TRING)?|AR)|KE)|O(G(S|FILE({s}GROUP))|NG(BLOB|TEXT)?|C(K(S)?|AL(TIME(STAMP)?)?)|OP|W_PRIORITY|AD{s}(DATA|INDEX{s}INTO{s}CACHE|XML))|E(SS|VEL|FT|A(DING|VE(S)?))|A(ST|NGUAGE)))
-keywords (X(OR|509|A)|S(MALLINT|SL|H(OW({s}(E(NGINE(S)?|RRORS)|M(ASTER|UTEX)|BINLOG|GRANTS|INNODB|P(RIVILEGES|ROFILE(S)?|ROCEDURE{s}CODE)|SLAVE{s}(HOSTS|STATUS)|TRIGGERS|VARIABLES|WARNINGS|(FULL{s})?PROCESSLIST|FIELDS|PLUGIN(S)?|STORAGE{s}ENGINES|TABLE{s}TYPES|CO(LUMNS|LLATION)|BINLOG{s}EVENTS))?|UTDOWN|ARE)|NAPSHOT|CHE(MA(S)?|DULE(R)?)|T(R(ING|AIGHT_JOIN)|O(RAGE|P)|A(RT(S|ING{s}BY)?|TUS))|I(GNED|MPLE)|O(ME|NAME|UNDS)|U(B(JECT|PARTITION(S)?)|SPEND|PER)|P(ECIFIC|ATIAL)|E(RIAL(IZABLE)?|SSION|NSITIVE|C(OND(_MICROSECOND)?|URITY)|T({s}(PASSWORD|NAMES|ONE_SHOT))?|PARATOR|LECT)|QL(STATE|_(MAX_JOIN_SIZE|B(IG_(RESULT|SELECTS|TABLES)|UFFER_RESULT)|S(MALL_RESULT|ELECT_LIMIT|LAVE_SKIP_COUNTER|AFE_UPDATES)|NO_CACHE|CA(CHE|LC_FOUND_ROWS)|T(SI_(M(INUTE|ONTH)|SECOND|HOUR|YEAR|DAY|QUARTER|FRAC_SECOND|WEEK)|HREAD)|QUOTE_SHOW_CREATE|WARNINGS|LO(G_(BIN|OFF|UPDATE)|W_PRIORITY_UPDATES)|AUTO_IS_NULL)|EXCEPTION|WARNING)?|LAVE|AVEPOINT)|YEAR(_MONTH)?|T(R(IGGER(S)?|U(NCATE|E)|A(NSACTION|ILING))|H(EN|AN)|YPE|I(ME(STAMP(DIFF|ADD)?)?|NY(BLOB|TEXT|INT))|O|E(RMINATED{s}BY|XT|MP(TABLE|ORARY))|ABLE(S(PACE)?)?)|ZEROFILL|U(S(ING|E(R(_RESOURCES)?|_FRM)?|AGE)|N(SIGNED|COMMITTED|TIL|I(NSTALL({s}PLUGIN)?|CODE|ON|QUE)|D(O(_BUFFER_SIZE|FILE)?|EFINED)|KNOWN|LOCK)|TC_(TIME(STAMP)?|DATE)|P(GRADE|DATE))|V(IEW|A(R(BINARY|YING|CHAR(ACTER)?|IABLES)|LUE(S)?))|W(RITE|H(ILE|E(RE|N))|ITH({s}PARSER)?|ORK|EEK|A(RNINGS|IT)))
+keyworda (G(R(OUP{s}BY|ANT(S)?)|E(NERAL|T_FORMAT|OMETRY(COLLECTION)?)|LOBAL)|B(Y(TE)?|TREE|I(GINT|N(LOG|ARY)|T)|O(TH|OL(EAN)?)|E(GIN|TWEEN|FORE)|LOB|ACKUP{s}TABLE)|H(IGH_PRIORITY|O(ST(S)?|UR(_(MI(NUTE|CROSECOND)|SECOND))?)|ELP|A(SH|NDLER|VING))|C(R(OSS|EATE)|H(ECK(SUM)?|A(R(SET|ACTER)?|NGE(D)?|IN))|IPHER|O(M(M(IT(TED)?|ENT)|P(RESSED|LETION|ACT))|N(S(TRAINT(_(SCHEMA|NAME|CATALOG))?|ISTENT)|NECTION|CURRENT|T(RIBUTORS|INUE|AINS)|DITION|VERT)|DE|L(UMN(S|_(NAME|FORMAT))?|LATE)|ALESCE{s}PARTITION)|U(R(RENT_(TIME(STAMP)?|DATE|USER)|SOR(_NAME)?)|BE)|L(IENT|OSE|ASS_ORIGIN)|A(S(CADE(D)?|E)|CHE{s}INDEX|TALOG_NAME|LL))|I(GNORE(_SERVER_IDS)?|MPORT{s}TABLESPACE|S(SUER|OLATION)?|N(S(TALL({s}PLUGIN)?|E(RT(_METHOD)?|NSITIVE))|N(O(BASE|DB)|ER)|T(1|2|8|3|O({s}(DUMP|OUT)FILE)?|4|E(RVAL|GER))?|ITIAL_SIZE|OUT|DEX(ES)?|VOKER|FILE)?|TERATE|O_THREAD|DENTIFIED|F)|D(ROP|YNAMIC|I(RECTORY|S(CARD{s}TABLESPACE|TINCT(ROW)?|K|ABLE{s}KEYS)|V)|O(UBLE)?|U(MPFILE|PLICATE|AL)|E(S(C(RIBE)?|_KEY_FILE)|C(IMAL|LARE)?|TERMINISTIC|F(INER|AULT)|L(ETE|AY(_KEY_WRITE|ED))|ALLOCATE)|A(Y(_(MI(NUTE|CROSECOND)|SECOND|HOUR))?|T(E(TIME)?|A(BASE(S)?|FILE)?)))|JOIN|E(RRORS|X(TEN(T_SIZE|DED)|I(STS|T)|P(LAIN|ANSION)|ECUTE)|SCAPE(D{s}BY)?|N(GINE(S)?|CLOSED{s}BY|D(S)?|UM|ABLE{s}KEYS)|VE(RY|NT)|LSE(IF)?|ACH)|K(ILL({s}(CONNECTION|QUERY))?|EY(S|_BLOCK_SIZE)?)|F(R(OM|AC_SECOND)|I(RST|XED|LE)|O(R(CE|EIGN)?|UND)|U(NCTION|LL(TEXT)?)|ETCH|L(OAT(8|4)?|USH)|A(ST|LSE))|A(G(GREGATE|AINST)|S(C(II)?|ENSITIVE)?|N(Y|D|ALYZE)|C(CESSIBLE|TION)|T|DD|UT(HORS|O(_INCREMENT|EXTEND_SIZE))|VG(_ROW_LENGTH)?|FTER|L(GORITHM|TER|L)))
+keywordl (R(TREE|IGHT|O(UTINE|W(S|_FORMAT)?|LL(BACK|UP))|E(GEXP|MOVE{s}PARTITIONING|BUILD{s}PARTITION|S(T(RICT|ORE{s}TABLE)|IGNAL|UME|ET)|NAME|COVER|TURN(S)?|ORGANIZE{s}PARTITION|D(O(_BUFFER_SIZE|FILE)|UNDANT)|P(EAT(ABLE)?|L(ICATION|ACE)|AIR)|VOKE|QUIRE|FERENCES|L(OAD|EASE|AY_(THREAD|LOG_(POS|FILE)))|A(D(S|_(ONLY|WRITE))?|L))|LIKE|ANGE)|M(YSQL_ERRNO|I(GRATE|N(_ROWS|UTE(_(MICROSECOND|SECOND))?)|CROSECOND|DDLEINT)|O(NTH|D(IF(Y|IES)|E)?)|U(TEX|LTI(PO(INT|LYGON)|LINESTRING))|E(RGE|MORY|SSAGE_TEXT|DIUM(BLOB|TEXT|INT)?)|A(X(_(ROWS|SIZE|CONNECTIONS_PER_HOUR|U(SER_CONNECTIONS|PDATES_PER_HOUR)|QUERIES_PER_HOUR)|VALUE)|STER(_(S(SL(_(C(IPHER|ERT|A(PATH)?)|VERIFY_SERVER_CERT|KEY))?|ERVER_ID)|H(OST|EARTBEAT_PERIOD)|CONNECT_RETRY|USER|P(ORT|ASSWORD)|LOG_(POS|FILE)))?|TCH))|N(CHAR|O(NE|_W(RITE_TO_BINLOG|AIT)|T|DEGROUP)?|DB(CLUSTER)?|U(MERIC|LL)|E(XT|W)|VARCHAR|A(ME(S)?|T(IONAL|URAL)))|O(R(DER{s}BY)?|N({s}(DUPLICATE{s}KEY{s}UPDATE)?|E(_SHOT)?|LINE)|UT(ER|FILE)?|P(TI(MIZE|ON(S|ALLY)?)|EN)|FF(SET|LINE)|WNER|LD_PASSWORD)|P(R(I(MARY|VILEGES)|OCE(SS|DURE{s}(ANALYSE)?)|E(SERVE|CISION|PARE|V))|HASE|O(RT|INT|LYGON)|URGE|A(R(SER|TI(TION(S|ING)?|AL))|SSWORD|CK_KEYS))|QU(ICK|ERY|ARTER)|L(I(MIT|ST|NE(S(TRING)?|AR)|KE)|O(G(S|FILE({s}GROUP))|NG(BLOB|TEXT)?|C(K(S)?|AL(TIME(STAMP)?)?)|OP|W_PRIORITY|AD{s}(DATA|INDEX{s}INTO{s}CACHE|XML))|E(SS|VEL|FT|A(DING|VE(S)?))|A(ST|NGUAGE)))
+keywords (X(OR|509|A)|S(MALLINT|SL|H(OW({s}(E(NGINE(S)?|RRORS)|M(ASTER|UTEX)|BINLOG|GRANTS|INNODB|P(RIVILEGES|ROFILE(S)?|ROCEDURE{s}CODE)|SLAVE{s}(HOSTS|STATUS)|TRIGGERS|VARIABLES|WARNINGS|(FULL{s})?PROCESSLIST|FIELDS|PLUGIN(S)?|STORAGE{s}ENGINES|TABLE{s}TYPES|CO(LUMNS|LLATION)|BINLOG{s}EVENTS))?|UTDOWN|ARE)|NAPSHOT|CHE(MA(S|_NAME)?|DULE(R)?)|T(R(ING|AIGHT_JOIN)|O(RAGE|P)|A(RT(S|ING{s}BY)?|TUS))|I(GN(ED|AL)|MPLE)|O(ME|NAME|CKET|UNDS)|U(B(CLASS_ORIGIN|JECT|PARTITION(S)?)|SPEND|PER)|P(ECIFIC|ATIAL)|E(R(IAL(IZABLE)?|VER)|SSION|NSITIVE|C(OND(_MICROSECOND)?|URITY)|T({s}(PASSWORD|NAMES|ONE_SHOT))?|PARATOR|LECT)|QL(STATE|_(MAX_JOIN_SIZE|B(IG_(RESULT|SELECTS|TABLES)|UFFER_RESULT)|S(MALL_RESULT|ELECT_LIMIT|LAVE_SKIP_COUNTER|AFE_UPDATES)|NO_CACHE|CA(CHE|LC_FOUND_ROWS)|T(SI_(M(INUTE|ONTH)|SECOND|HOUR|YEAR|DAY|QUARTER|FRAC_SECOND|WEEK)|HREAD)|QUOTE_SHOW_CREATE|WARNINGS|LO(G_(BIN|OFF|UPDATE)|W_PRIORITY_UPDATES)|AUTO_IS_NULL)|EXCEPTION|WARNING)?|L(OW|AVE)|AVEPOINT)|YEAR(_MONTH)?|T(R(IGGER(S)?|U(NCATE|E)|A(NSACTION|ILING))|H(EN|AN)|YPE|I(ME(STAMP(DIFF|ADD)?)?|NY(BLOB|TEXT|INT))|O|E(RMINATED{s}BY|XT|MP(TABLE|ORARY))|ABLE(S(PACE)?|_NAME)?)|ZEROFILL|U(S(ING|E(R(_RESOURCES)?|_FRM)?|AGE)|N(SIGNED|COMMITTED|TIL|I(NSTALL({s}PLUGIN)?|CODE|ON|QUE)|D(O(_BUFFER_SIZE|FILE)?|EFINED)|KNOWN|LOCK)|TC_(TIME(STAMP)?|DATE)|P(GRADE|DATE))|V(IEW|A(R(BINARY|YING|CHAR(ACTER)?|IABLES)|LUE(S)?))|W(R(ITE|APPER)|H(ILE|E(RE|N))|ITH({s}PARSER)?|ORK|EEK|A(RNINGS|IT)))
%x comment
@@ -87,6 +87,7 @@ keywords (X(OR|509|A)|S(MALLINT|SL|H(OW({s}(E(NGINE(S)?|RRORS)|M(ASTER|UTEX)|BIN
*/
#[^\n\r]*(\n|\r)? | /* # Comments */
+--[\n\r] { return SPT_COMMENT; } /* -- */
--[ \t][^\n\r]*(\n|\r)? { return SPT_COMMENT; } /* -- Comments */
{variable}/{ops} { BEGIN(varequation); return SPT_VARIABLE; }/* SQL variables before operator*/
@@ -216,6 +217,7 @@ CALL
CASCADE
CASCADED
CASE
+CATALOG_NAME
CHAIN
CHANGE
CHANGED
@@ -225,6 +227,7 @@ CHARSET
CHECK
CHECKSUM
CIPHER
+CLASS_ORIGIN
CLIENT
CLOSE
COALESCE{s}PARTITION
@@ -232,6 +235,8 @@ CODE
COLLATE
COLUMN
COLUMN_FORMAT
+COLUMN_NAME
+COLUMNS
COMMENT
COMMIT
COMMITTED
@@ -243,6 +248,9 @@ CONDITION
CONNECTION
CONSISTENT
CONSTRAINT
+CONSTRAINT_CATALOG
+CONSTRAINT_NAME
+CONSTRAINT_SCHEMA
CONTAINS
CONTINUE
CONTRIBUTORS
@@ -255,6 +263,7 @@ CURRENT_TIME
CURRENT_TIMESTAMP
CURRENT_USER
CURSOR
+CURSOR_NAME
DATA
DATABASE
DATABASES
@@ -334,6 +343,7 @@ FROM
FULL
FULLTEXT
FUNCTION
+GENERAL
GEOMETRY
GEOMETRYCOLLECTION
GET_FORMAT
@@ -346,6 +356,7 @@ HASH
HAVING
HELP
HIGH_PRIORITY
+HOST
HOSTS
HOUR
HOUR_MICROSECOND
@@ -354,6 +365,7 @@ HOUR_SECOND
IDENTIFIED
IF
IGNORE
+IGNORE_SERVER_IDS
IMPORT{s}TABLESPACE
IN
INDEX
@@ -417,6 +429,7 @@ LOOP
LOW_PRIORITY
MASTER
MASTER_CONNECT_RETRY
+MASTER_HEARTBEAT_PERIOD
MASTER_HOST
MASTER_LOG_FILE
MASTER_LOG_POS
@@ -445,6 +458,7 @@ MEDIUMINT
MEDIUMTEXT
MEMORY
MERGE
+MESSAGE_TEXT
MICROSECOND
MIDDLEINT
MIGRATE
@@ -461,6 +475,7 @@ MULTILINESTRING
MULTIPOINT
MULTIPOLYGON
MUTEX
+MYSQL_ERRNO
NAME
NAMES
NATIONAL
@@ -489,13 +504,14 @@ ON{s}(DUPLICATE{s}KEY{s}UPDATE)?
OPEN
OPTIMIZE
OPTION
-OPTIONS
OPTIONALLY
+OPTIONS
OR
ORDER{s}BY
OUT
OUTER
OUTFILE
+OWNER
PACK_KEYS
PARSER
PARTIAL
@@ -506,6 +522,7 @@ PASSWORD
PHASE
POINT
POLYGON
+PORT
PRECISION
PREPARE
PRESERVE
@@ -546,6 +563,7 @@ REPLACE
REPLICATION
REQUIRE
RESET
+RESIGNAL
RESTORE{s}TABLE
RESTRICT
RESUME
@@ -566,6 +584,7 @@ SCHEDULE
SCHEDULER
SCHEMA
SCHEMAS
+SCHEMA_NAME
SECOND
SECOND_MICROSECOND
SECURITY
@@ -574,16 +593,20 @@ SENSITIVE
SEPARATOR
SERIAL
SERIALIZABLE
+SERVER
SESSION
SET({s}(PASSWORD|NAMES|ONE_SHOT))?
SHARE
SHOW({s}(E(NGINE(S)?|RRORS)|M(ASTER|UTEX)|BINLOG|GRANTS|INNODB|P(RIVILEGES|ROFILE(S)?|ROCEDURE{s}CODE)|SLAVE{s}(HOSTS|STATUS)|TRIGGERS|VARIABLES|WARNINGS|(FULL{s})?PROCESSLIST|FIELDS|PLUGIN(S)?|STORAGE{s}ENGINES|TABLE{s}TYPES|CO(LUMNS|LLATION)|BINLOG{s}EVENTS))?
SHUTDOWN
+SIGNAL
SIGNED
SIMPLE
SLAVE
+SLOW
SMALLINT
SNAPSHOT
+SOCKET
SOME
SONAME
SOUNDS
@@ -631,6 +654,7 @@ STOP
STORAGE
STRAIGHT_JOIN
STRING
+SUBCLASS_ORIGIN
SUBJECT
SUBPARTITION
SUBPARTITIONS
@@ -639,6 +663,7 @@ SUSPEND
TABLE
TABLES
TABLESPACE
+TABLE_NAME
TEMPORARY
TEMPTABLE
TERMINATED{s}BY
@@ -700,6 +725,7 @@ WHERE
WHILE
WITH({s}PARSER)?
WORK
+WRAPPER
WRITE
X509
XA
diff --git a/Source/SPExportController.m b/Source/SPExportController.m
index 1d70f88f..9081b942 100644
--- a/Source/SPExportController.m
+++ b/Source/SPExportController.m
@@ -282,10 +282,12 @@ static const NSString *SPTableViewDropColumnID = @"drop";
// Close the advanced options view if it's open
[exportAdvancedOptionsView setHidden:YES];
[exportAdvancedOptionsViewButton setState:NSOffState];
+ showAdvancedView = NO;
// Close the customize filename view if it's open
[exportCustomFilenameView setHidden:YES];
[exportCustomFilenameViewButton setState:NSOffState];
+ showCustomFilenameView = NO;
// If open close the advanced options view and custom filename view
[self _resizeWindowForAdvancedOptionsViewByHeightDelta:0];
diff --git a/Source/SPExtendedTableInfo.m b/Source/SPExtendedTableInfo.m
index a765f6df..6f039659 100644
--- a/Source/SPExtendedTableInfo.m
+++ b/Source/SPExtendedTableInfo.m
@@ -98,7 +98,7 @@
}
// Alter table's storage type
- [connection queryString:[NSString stringWithFormat:@"ALTER TABLE %@ TYPE = %@", [selectedTable backtickQuotedString], newType]];
+ [connection queryString:[NSString stringWithFormat:@"ALTER TABLE %@ %@ = %@", [selectedTable backtickQuotedString], [[tableDocumentInstance serverSupport] engineTypeQueryName], newType]];
if ([connection getLastErrorID] == 0) {
// Reload the table's data
diff --git a/Source/SPFieldEditorController.m b/Source/SPFieldEditorController.m
index 2e761f80..5075ffaf 100644
--- a/Source/SPFieldEditorController.m
+++ b/Source/SPFieldEditorController.m
@@ -917,7 +917,10 @@
*/
- (id)previewPanel:(id)panel previewItemAtIndex:(NSInteger)index
{
- return [NSURL fileURLWithPath:tmpFileName];
+ if(tmpFileName)
+ return [NSURL fileURLWithPath:tmpFileName];
+
+ return nil;
}
/**
diff --git a/Source/SPHistoryController.m b/Source/SPHistoryController.m
index 3fb0e100..107c24cb 100644
--- a/Source/SPHistoryController.m
+++ b/Source/SPHistoryController.m
@@ -269,6 +269,7 @@
NSIndexSet *contentSelectedIndexSet = [tableContentInstance selectedRowIndexes];
NSRect contentViewport = [tableContentInstance viewport];
NSDictionary *contentFilter = [tableContentInstance filterSettings];
+ NSData *filterTableData = [tableContentInstance filterTableData];
if (!theDatabase) return;
// If a table is selected, save state information
@@ -283,6 +284,7 @@
if (contentSortCol) [contentState setObject:contentSortCol forKey:@"sortCol"];
if (contentSelectedIndexSet) [contentState setObject:contentSelectedIndexSet forKey:@"selection"];
if (contentFilter) [contentState setObject:contentFilter forKey:@"filter"];
+ if (filterTableData) [contentState setObject:filterTableData forKey:@"filterTable"];
// Update the table content states with this information - used when switching tables to restore last used view.
[tableContentStates setObject:contentState forKey:[NSString stringWithFormat:@"%@.%@", [theDatabase backtickQuotedString], [theTable backtickQuotedString]]];
diff --git a/Source/SPIndexesController.m b/Source/SPIndexesController.m
index 27b4471d..49ac8594 100644
--- a/Source/SPIndexesController.m
+++ b/Source/SPIndexesController.m
@@ -39,7 +39,7 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
- (void)_reloadIndexedColumnsTableData;
- (void)_addIndexUsingDetails:(NSDictionary *)indexDetails;
-- (void)_removeIndexUsingDeatails:(NSDictionary *)indexDetails;
+- (void)_removeIndexUsingDetails:(NSDictionary *)indexDetails;
- (void)_resizeWindowForAdvancedOptionsViewByHeightDelta:(NSInteger)delta;
@@ -122,18 +122,26 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
// Check whether a save of the current field row is required.
if (![tableStructure saveRowOnDeselect]) return;
- [indexTypePopUpButton insertItemWithTitle:@"PRIMARY KEY" atIndex:0];
+ // Reset visibility of the primary key item
+ [[[indexTypePopUpButton menu] itemWithTag:SPPrimaryKeyMenuTag] setHidden:NO];
// Set sheet defaults - key type PRIMARY, key name PRIMARY and disabled
- [indexTypePopUpButton selectItemAtIndex:0];
+ [indexTypePopUpButton selectItemWithTag:SPPrimaryKeyMenuTag];
[indexNameTextField setEnabled:NO];
[indexNameTextField setStringValue:@"PRIMARY"];
-
- // If the table is of type MyISAM and Spatial extension support is available, add the SPATIAL type
+
+ // Remove any existing SPATIAL menu item
+ if ([indexTypePopUpButton indexOfItemWithTag:SPSpatialMenuTag] != -1)
+ [indexTypePopUpButton removeItemAtIndex:[indexTypePopUpButton indexOfItemWithTag:SPSpatialMenuTag]];
+
+ // If the table is of type MyISAM and Spatial extension support is available, (re-)add the SPATIAL type
NSString *engine = [[tableData statusValues] objectForKey:@"Engine"];
if ([engine isEqualToString:@"MyISAM"] && [[dbDocument serverSupport] supportsSpatialExtensions]) {
- [indexTypePopUpButton addItemWithTitle:@"SPATIAL"];
+ NSMenuItem *spatialMenuItem = [[[NSMenuItem alloc] init] autorelease];
+ [spatialMenuItem setTitle:NSLocalizedString(@"SPATIAL", @"Spatial index menu item title")];
+ [spatialMenuItem setTag:SPSpatialMenuTag];
+ [[indexTypePopUpButton menu] addItem:spatialMenuItem];
}
// Check to see whether a primary key already exists for the table, and if so select INDEX instead
@@ -156,11 +164,11 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
if (isPrimaryKey || hasCompositePrimaryKey) {
- // Remove primary key option
- [indexTypePopUpButton removeItemAtIndex:0];
+ // Hide primary key option
+ [[[indexTypePopUpButton menu] itemWithTag:SPPrimaryKeyMenuTag] setHidden:YES];
// Select INDEX type
- [indexTypePopUpButton selectItemAtIndex:0];
+ [indexTypePopUpButton selectItemWithTag:SPIndexMenuTag];
[indexNameTextField setEnabled:YES];
[indexNameTextField setStringValue:@""];
@@ -188,8 +196,11 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
break;
}
}
+
+ // If no initial field has been selected yet - all fields are indexed - add the first field.
+ if (!initialField) initialField = [fields objectAtIndex:0];
- if (initialField) [indexedFieldNames release], initialField = nil;
+ if (indexedFieldNames) [indexedFieldNames release], indexedFieldNames = nil;
// Reset the indexed columns
[indexedFields removeAllObjects];
@@ -275,11 +286,12 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
*/
- (IBAction)chooseIndexType:(id)sender
{
- NSString *indexType = [indexTypePopUpButton titleOfSelectedItem];
+ NSInteger *indexType = [[indexTypePopUpButton selectedItem] tag];
- if ([indexType isEqualToString:@"PRIMARY KEY"] ) {
+ if (indexType == SPPrimaryKeyMenuTag) {
[indexNameTextField setEnabled:NO];
[indexNameTextField setStringValue:@"PRIMARY"];
+ [indexStorageTypePopUpButton setEnabled:NO];
}
else {
[indexNameTextField setEnabled:YES];
@@ -289,7 +301,7 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
}
// Specifiying an index storage type (i.e. HASH or BTREE) is not permitted with SPATIAL indexes
- [indexStorageTypePopUpButton setEnabled:(![indexType isEqualToString:@"SPATIAL"])];
+ [indexStorageTypePopUpButton setEnabled:(indexType != SPSpatialMenuTag)];
}
}
@@ -301,6 +313,7 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
// Close the advanced options view if it's open
[indexAdvancedOptionsView setHidden:YES];
[indexAdvancedOptionsViewButton setState:NSOffState];
+ showAdvancedView = NO;
// Hide the size column
[indexSizeTableColumn setHidden:YES];
@@ -532,14 +545,30 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
[indexDetails setObject:indexedFields forKey:SPNewIndexIndexedColumns];
[indexDetails setObject:[indexNameTextField stringValue] forKey:SPNewIndexIndexName];
- [indexDetails setObject:[indexTypePopUpButton titleOfSelectedItem] forKey:SPNewIndexIndexType];
+ switch ([[indexTypePopUpButton selectedItem] tag]) {
+ case SPPrimaryKeyMenuTag:
+ [indexDetails setObject:@"PRIMARY KEY" forKey:SPNewIndexIndexType];
+ break;
+ case SPIndexMenuTag:
+ [indexDetails setObject:@"INDEX" forKey:SPNewIndexIndexType];
+ break;
+ case SPUniqueMenuTag:
+ [indexDetails setObject:@"UNIQUE" forKey:SPNewIndexIndexType];
+ break;
+ case SPFullTextMenuTag:
+ [indexDetails setObject:@"FULLTEXT" forKey:SPNewIndexIndexType];
+ break;
+ case SPSpatialMenuTag:
+ [indexDetails setObject:@"SPATIAL" forKey:SPNewIndexIndexType];
+ break;
+ }
// If there is a key block size set it means the database version supports it
if ([[indexKeyBlockSizeTextField stringValue] length]) {
[indexDetails setObject:[NSNumber numberWithInteger:[indexKeyBlockSizeTextField integerValue]] forKey:SPNewIndexKeyBlockSize];
}
- if (([indexStorageTypePopUpButton indexOfSelectedItem] > 0) && (![[indexTypePopUpButton titleOfSelectedItem] isEqualToString:@"SPATIAL"])) {
+ if (([indexStorageTypePopUpButton indexOfSelectedItem] > 0) && ([[indexTypePopUpButton selectedItem] tag] != SPSpatialMenuTag)) {
[indexDetails setObject:[indexStorageTypePopUpButton titleOfSelectedItem] forKey:SPNewIndexStorageType];
}
@@ -572,12 +601,12 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
[indexDetails setObject:[NSNumber numberWithBool:[contextInfo hasSuffix:@"AndForeignKey"]] forKey:@"RemoveForeignKey"];
if ([NSThread isMainThread]) {
- [NSThread detachNewThreadSelector:@selector(_removeIndexUsingDeatails:) toTarget:self withObject:indexDetails];
+ [NSThread detachNewThreadSelector:@selector(_removeIndexUsingDetails:) toTarget:self withObject:indexDetails];
[dbDocument enableTaskCancellationWithTitle:NSLocalizedString(@"Cancel", @"cancel button") callbackObject:self callbackFunction:NULL];
}
else {
- [self _removeIndexUsingDeatails:indexDetails];
+ [self _removeIndexUsingDetails:indexDetails];
}
}
}
@@ -729,14 +758,14 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
[query appendString:indexName];
}
- // Add the columns
- [query appendFormat:@" (%@)", [tempIndexedColumns componentsJoinedByCommas]];
-
// If supplied specify the index's storage type
if (indexStorageType) {
[query appendString:@" USING "];
[query appendString:indexStorageType];
}
+
+ // Add the columns
+ [query appendFormat:@" (%@)", [tempIndexedColumns componentsJoinedByCommas]];
// If supplied specify the index's key block size
if (indexKeyBlockSize) {
@@ -776,7 +805,7 @@ static const NSString *SPNewIndexKeyBlockSize = @"IndexKeyBlockSize";
*
* @param indexDetails A dictionary containing the details of the index to be removed
*/
-- (void)_removeIndexUsingDeatails:(NSDictionary *)indexDetails
+- (void)_removeIndexUsingDetails:(NSDictionary *)indexDetails
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
diff --git a/Source/SPNarrowDownCompletion.h b/Source/SPNarrowDownCompletion.h
index 7b33dea0..192bbf38 100644
--- a/Source/SPNarrowDownCompletion.h
+++ b/Source/SPNarrowDownCompletion.h
@@ -55,6 +55,7 @@
NSRange theCharRange;
NSRange theParseRange;
NSString *theDbName;
+ NSString *theAliasName;
NSTimer *stateTimer;
NSArray *syncArrowImages;
@@ -78,7 +79,7 @@
dictMode:(BOOL)mode dbMode:(BOOL)theDbMode tabTriggerMode:(BOOL)tabTriggerMode fuzzySearch:(BOOL)fuzzySearch
backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName
selectedDb:(NSString*)selectedDb caretMovedLeft:(BOOL)caretMovedLeft autoComplete:(BOOL)autoComplete oneColumn:(BOOL)oneColumn
- isQueryingDBStructure:(BOOL)isQueryingDBStructure;
+ alias:(NSString*)anAlias isQueryingDBStructure:(BOOL)isQueryingDBStructure;
- (void)setCaretPos:(NSPoint)aPos;
- (void)insert_text:(NSString* )aString;
- (void)insertAutocompletePlaceholder;
diff --git a/Source/SPNarrowDownCompletion.m b/Source/SPNarrowDownCompletion.m
index f86c66c5..abc00d9a 100644
--- a/Source/SPNarrowDownCompletion.m
+++ b/Source/SPNarrowDownCompletion.m
@@ -215,7 +215,7 @@
dictMode:(BOOL)mode dbMode:(BOOL)theDbMode tabTriggerMode:(BOOL)tabTriggerMode fuzzySearch:(BOOL)fuzzySearch
backtickMode:(NSInteger)theBackTickMode withDbName:(NSString*)dbName withTableName:(NSString*)tableName
selectedDb:(NSString*)selectedDb caretMovedLeft:(BOOL)caretMovedLeft autoComplete:(BOOL)autoComplete oneColumn:(BOOL)oneColumn
- isQueryingDBStructure:(BOOL)isQueryingDBStructure
+ alias:(NSString*)anAlias isQueryingDBStructure:(BOOL)isQueryingDBStructure
{
if(self = [self init])
{
@@ -228,6 +228,7 @@
autoCompletionMode = autoComplete;
+ theAliasName = anAlias;
oneColumnMode = oneColumn;
isQueryingDatabaseStructure = isQueryingDBStructure;
@@ -953,8 +954,10 @@
NSString* toInsert = [curMatch substringFromIndex:[originalFilterString length]];
theCharRange.length += [toInsert length] - currentAutocompleteLength;
theParseRange.length += [toInsert length];
+
[theView breakUndoCoalescing];
[theView insertText:[toInsert lowercaseString]];
+
autocompletePlaceholderWasInserted = YES;
// Restore the text selection location, and clearly mark the autosuggested text
@@ -970,8 +973,14 @@
{
if (!autocompletePlaceholderWasInserted) return;
+ [theView breakUndoCoalescing];
+
if (useFastMethod) {
- [theView setSelectedRange:theCharRange];
+ if(backtickMode) {
+ NSRange r = NSMakeRange(theCharRange.location+1,theCharRange.length);
+ [theView setSelectedRange:r];
+ } else
+ [theView setSelectedRange:theCharRange];
[theView insertText:originalFilterString];
} else {
NSRange attributeResultRange = NSMakeRange(0, 0);
@@ -1015,12 +1024,30 @@
if(NSMaxRange(theCharRange) > [[theView string] length])
theCharRange = NSIntersectionRange(NSMakeRange(0,[[theView string] length]), theCharRange);
+ [theView breakUndoCoalescing];
+
NSRange r = [theView selectedRange];
if(r.length)
[theView setSelectedRange:r];
- else
- [theView setSelectedRange:theCharRange];
+ else {
+ if(backtickMode == 100) {
+ NSString *replaceString = [[theView string] substringWithRange:theCharRange];
+ BOOL nextCharIsBacktick = ([replaceString hasSuffix:@"`"]);
+ if(theCharRange.length == 1) nextCharIsBacktick = NO;
+ if(!nextCharIsBacktick) {
+ if([replaceString hasPrefix:@"`"])
+ [theView setSelectedRange:NSMakeRange(theCharRange.location, theCharRange.length+2)];
+ else
+ [theView setSelectedRange:theCharRange];
+ } else {
+ [theView setSelectedRange:theCharRange];
+ }
+ backtickMode = 0;
+ } else
+ [theView setSelectedRange:theCharRange];
+ }
+ [theView breakUndoCoalescing];
[theView insertText:aString];
// If completion string contains backticks move caret out of the backticks
@@ -1052,10 +1079,9 @@
NSString* candidateMatch = [selectedItem objectForKey:@"match"] ?: [selectedItem objectForKey:@"display"];
if([selectedItem objectForKey:@"isRef"]
&& ([[NSApp currentEvent] modifierFlags] & (NSShiftKeyMask))
- && [[selectedItem objectForKey:@"path"] length]) {
+ && [[selectedItem objectForKey:@"path"] length] && theAliasName == nil) {
NSString *path = [[[selectedItem objectForKey:@"path"] componentsSeparatedByString:SPUniqueSchemaDelimiter] componentsJoinedByPeriodAndBacktickQuotedAndIgnoreFirst];
-
// Check if path's db name is the current selected db name
NSRange r = [path rangeOfString:[currentDb backtickQuotedString] options:NSCaseInsensitiveSearch range:NSMakeRange(0, [[currentDb backtickQuotedString] length])];
theCharRange = theParseRange;
@@ -1068,7 +1094,7 @@
} else {
// Is completion string a schema name for current connection
if([selectedItem objectForKey:@"isRef"]) {
- backtickMode = 0; // suppress move the caret one step rightwards
+ backtickMode = 100; // suppress move the caret one step rightwards
[self insert_text:[candidateMatch backtickQuotedString]];
} else {
[self insert_text:candidateMatch];
diff --git a/Source/SPPreferencesUpgrade.m b/Source/SPPreferencesUpgrade.m
index 6f95057b..51bcd1b4 100644
--- a/Source/SPPreferencesUpgrade.m
+++ b/Source/SPPreferencesUpgrade.m
@@ -48,7 +48,7 @@ void SPApplyRevisionChanges(void)
// Get the current revision
if ([prefs objectForKey:@"lastUsedVersion"]) recordedVersionNumber = [[prefs objectForKey:@"lastUsedVersion"] integerValue];
if ([prefs objectForKey:SPLastUsedVersion]) recordedVersionNumber = [[prefs objectForKey:SPLastUsedVersion] integerValue];
-
+
// Skip processing if the current version matches or is less than recorded version
if (currentVersionNumber <= recordedVersionNumber) return;
@@ -57,7 +57,10 @@ void SPApplyRevisionChanges(void)
[prefs setObject:[NSNumber numberWithInteger:currentVersionNumber] forKey:SPLastUsedVersion];
return;
}
-
+
+ // Inform SPAppController to check installed default Bundles for available updates
+ [prefs setObject:[NSNumber numberWithBool:YES] forKey:@"doBundleUpdate"];
+
// For versions prior to r336 (0.9.4), where column widths have been saved, walk through them and remove
// any table widths set to 15 or less (fix for mangled columns caused by Issue #140)
if (recordedVersionNumber < 336 && [prefs objectForKey:SPTableColumnWidths] != nil) {
diff --git a/Source/SPQueryFavoriteManager.m b/Source/SPQueryFavoriteManager.m
index 609b083e..5e270a07 100644
--- a/Source/SPQueryFavoriteManager.m
+++ b/Source/SPQueryFavoriteManager.m
@@ -465,6 +465,7 @@
*/
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
{
+ if (rowIndex == -1) return YES;
return ([[favorites objectAtIndex:rowIndex] objectForKey:@"headerOfFileURL"]) ? NO : YES;
}
diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m
index 76508304..0d4b5993 100644
--- a/Source/SPSSHTunnel.m
+++ b/Source/SPSSHTunnel.m
@@ -328,8 +328,8 @@
[taskEnvironment setObject:tunnelConnectionVerifyHash forKey:@"SP_CONNECTION_VERIFY_HASH"];
if (passwordInKeychain) {
[taskEnvironment setObject:[[NSNumber numberWithInteger:SPSSHPasswordUsesKeychain] stringValue] forKey:@"SP_PASSWORD_METHOD"];
- [taskEnvironment setObject:keychainName forKey:@"SP_KEYCHAIN_ITEM_NAME"];
- [taskEnvironment setObject:keychainAccount forKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"];
+ [taskEnvironment setObject:[keychainName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:@"SP_KEYCHAIN_ITEM_NAME"];
+ [taskEnvironment setObject:[keychainAccount stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"];
} else if (password) {
[taskEnvironment setObject:[[NSNumber numberWithInteger:SPSSHPasswordAsksUI] stringValue] forKey:@"SP_PASSWORD_METHOD"];
} else {
diff --git a/Source/SPServerSupport.h b/Source/SPServerSupport.h
index fc16b426..bf18a8e8 100644
--- a/Source/SPServerSupport.h
+++ b/Source/SPServerSupport.h
@@ -65,6 +65,7 @@
BOOL supportsShowPrivileges;
// Storage engines
+ NSString *engineTypeQueryName;
BOOL supportsInformationSchemaEngines;
BOOL supportsPre41StorageEngines;
BOOL supportsBlackholeStorageEngine;
@@ -172,6 +173,11 @@
@property (readonly) BOOL supportsShowPrivileges;
/**
+ * @property engineTypeQueryName Returns the appropriate query part for specifying table engine - ENGINE or TYPE
+ */
+@property (readonly) NSString *engineTypeQueryName;
+
+/**
* @property supportsInformationSchemaEngines Indicates if the server supports the information_schema.engines table
*/
@property (readonly) BOOL supportsInformationSchemaEngines;
diff --git a/Source/SPServerSupport.m b/Source/SPServerSupport.m
index 0b3ba79d..6f8213d6 100644
--- a/Source/SPServerSupport.m
+++ b/Source/SPServerSupport.m
@@ -54,6 +54,7 @@
@synthesize supportsFullDropUser;
@synthesize supportsUserMaxVars;
@synthesize supportsShowPrivileges;
+@synthesize engineTypeQueryName;
@synthesize supportsInformationSchemaEngines;
@synthesize supportsPre41StorageEngines;
@synthesize supportsBlackholeStorageEngine;
@@ -150,7 +151,10 @@
// The SHOW PRIVILEGES statement wasn't added until MySQL 4.1.0
supportsShowPrivileges = [self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:0];
-
+
+ // MySQL 4.0.18+ and 4.1.2+ changed the TYPE option to ENGINE, but 4.x supports both
+ engineTypeQueryName = [self isEqualToOrGreaterThanMajorVersion:5 minor:0 release:0]?@"ENGINE":@"TYPE";
+
// Before MySQL 4.1 the MEMORY engine was known as HEAP and the ISAM engine was available
supportsPre41StorageEngines = (![self isEqualToOrGreaterThanMajorVersion:4 minor:1 release:0]);
@@ -245,6 +249,7 @@
supportsFullDropUser = NO;
supportsUserMaxVars = NO;
supportsShowPrivileges = NO;
+ engineTypeQueryName = @"ENGINE";
supportsInformationSchemaEngines = NO;
supportsPre41StorageEngines = NO;
supportsBlackholeStorageEngine = NO;
diff --git a/Source/SPTableContent.h b/Source/SPTableContent.h
index e3fd8a53..0b33146b 100644
--- a/Source/SPTableContent.h
+++ b/Source/SPTableContent.h
@@ -226,12 +226,14 @@
- (void) setFiltersToRestore:(NSDictionary *)filterSettings;
- (void) storeCurrentDetailsForRestoration;
- (void) clearDetailsToRestore;
+- (void) setFilterTableData:(NSData*)arcData;
+- (NSData*) filterTableData;
- (NSString *)escapeFilterArgument:(NSString *)argument againstClause:(NSString *)clause;
- (void)openContentFilterManager;
- (void)makeContentFilterHaveFocus;
-- (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSInteger)columnIndex;
+- (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSNumber *)columnIndex;
- (void)updateFilterTableClause:(id)currentValue;
- (NSString*)escapeFilterTableDefaultOperator:(NSString*)anOperator;
diff --git a/Source/SPTableContent.m b/Source/SPTableContent.m
index 8e2bde89..b136d90a 100644
--- a/Source/SPTableContent.m
+++ b/Source/SPTableContent.m
@@ -1604,6 +1604,12 @@
column = NSArrayObjectAtIndex(dataColumns, i);
if ([column objectForKey:@"default"] == nil || [column objectForKey:@"default"] == [NSNull null]) {
[newRow addObject:[NSNull null]];
+ } else if ([[column objectForKey:@"default"] isEqualToString:@""]
+ && ![[column objectForKey:@"null"] boolValue]
+ && ([[column objectForKey:@"typegrouping"] isEqualToString:@"float"]
+ || [[column objectForKey:@"typegrouping"] isEqualToString:@"integer"]))
+ {
+ [newRow addObject:@"0"];
} else {
[newRow addObject:[column objectForKey:@"default"]];
}
@@ -2394,9 +2400,11 @@
NSMutableArray *rowFieldsToSave = [[NSMutableArray alloc] initWithCapacity:[dataColumns count]];
NSMutableArray *rowValuesToSave = [[NSMutableArray alloc] initWithCapacity:[dataColumns count]];
NSInteger i;
+ NSDictionary *fieldDefinition;
id rowObject;
for (i = 0; i < [dataColumns count]; i++) {
rowObject = [tableValues cellDataAtRow:currentlyEditingRow column:i];
+ fieldDefinition = NSArrayObjectAtIndex(dataColumns, i);
// Skip "not loaded" cells entirely - these only occur when editing tables when the
// preference setting is enabled, and don't need to be saved back to the table.
@@ -2408,13 +2416,13 @@
// Prepare to derive the value to save
NSString *fieldValue;
- NSString *fieldTypeGroup = [NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"typegrouping"];
+ NSString *fieldTypeGroup = [fieldDefinition objectForKey:@"typegrouping"];
// Use NULL when the user has entered the nullValue string defined in the preferences,
// or when a numeric field is empty.
if ([rowObject isNSNull]
|| (([fieldTypeGroup isEqualToString:@"float"] || [fieldTypeGroup isEqualToString:@"integer"])
- && [[rowObject description] isEqualToString:@""]))
+ && [[rowObject description] isEqualToString:@""] && [[fieldDefinition objectForKey:@"null"] boolValue]))
{
fieldValue = @"NULL";
@@ -2449,7 +2457,7 @@
}
// Store the key and value in the ordered arrays for saving.
- [rowFieldsToSave addObject:[NSArrayObjectAtIndex(dataColumns, i) objectForKey:@"name"]];
+ [rowFieldsToSave addObject:[fieldDefinition objectForKey:@"name"]];
[rowValuesToSave addObject:fieldValue];
}
@@ -2490,10 +2498,19 @@
} else {
NSBeep();
}
- [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow];
+
+ // If creating a new row, remove the row; otherwise revert the row contents
+ if (isEditingNewRow) {
+ tableRowsCount--;
+ [tableValues removeRowAtIndex:currentlyEditingRow];
+ [self updateCountText];
+ isEditingNewRow = NO;
+ } else {
+ [tableValues replaceRowAtIndex:currentlyEditingRow withRowContents:oldRow];
+ }
isEditingRow = NO;
- isEditingNewRow = NO;
currentlyEditingRow = -1;
+ [tableContentView reloadData];
[[SPQueryController sharedQueryController] showErrorInConsole:NSLocalizedString(@"/* WARNING: No rows have been affected */\n", @"warning shown in the console when no rows have been affected after writing to the db") connection:[tableDocumentInstance name]];
return YES;
@@ -2784,7 +2801,7 @@
* -2 for other errors
* and the used WHERE clause to identify
*/
-- (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSInteger)columnIndex
+- (NSArray*)fieldEditStatusForRow:(NSInteger)rowIndex andColumn:(NSNumber *)columnIndex
{
NSDictionary *columnDefinition = nil;
@@ -2919,10 +2936,12 @@
NSInteger row = -1;
NSInteger column = -1;
+ NSInteger editedColumn = -1;
if(contextInfo) {
row = [[contextInfo objectForKey:@"row"] integerValue];
column = [[contextInfo objectForKey:@"column"] integerValue];
+ editedColumn = [[contextInfo objectForKey:@"editedColumn"] integerValue];
}
if (data && contextInfo) {
@@ -2958,8 +2977,8 @@
[[tableDocumentInstance parentWindow] makeFirstResponder:tableContentView];
- if(row > -1 && column > -1)
- [tableContentView editColumn:column row:row withEvent:nil select:YES];
+ if(row > -1 && editedColumn > -1)
+ [tableContentView editColumn:editedColumn row:row withEvent:nil select:YES];
}
#pragma mark -
@@ -3278,6 +3297,25 @@
[self setFiltersToRestore:nil];
}
+- (void) setFilterTableData:(NSData*)arcData
+{
+ if(!arcData) return;
+ NSDictionary *filterData = [NSUnarchiver unarchiveObjectWithData:arcData];
+ [filterTableData removeAllObjects];
+ [filterTableData addEntriesFromDictionary:filterData];
+ [filterTableWindow makeKeyAndOrderFront:nil];
+ // [filterTableView reloadData];
+}
+
+- (NSData*) filterTableData
+{
+ if(![filterTableWindow isVisible]) return nil;
+
+ [filterTableView deselectAll:nil];
+
+ return [NSArchiver archivedDataWithRootObject:filterTableData];
+}
+
#pragma mark -
#pragma mark Table drawing and editing
@@ -3494,8 +3532,9 @@
return c;
} else
return NSArrayObjectAtIndex([[filterTableData objectForKey:[NSNumber numberWithInteger:rowIndex]] objectForKey:@"filter"], [[aTableColumn identifier] integerValue]-1);
- else
+ else {
return NSArrayObjectAtIndex([[filterTableData objectForKey:[aTableColumn identifier]] objectForKey:@"filter"], rowIndex);
+ }
}
else if(aTableView == tableContentView) {
@@ -4050,6 +4089,12 @@
if ([cellValue isNSNull])
cellValue = [NSString stringWithString:[prefs objectForKey:SPNullValue]];
+ NSInteger editedColumn = 0;
+ for(NSTableColumn* col in [tableContentView tableColumns]) {
+ if([[col identifier] isEqualToNumber:[aTableColumn identifier]]) break;
+ editedColumn++;
+ }
+
[fieldEditor editWithObject:cellValue
fieldName:[[aTableColumn headerCell] stringValue]
usingEncoding:[mySQLConnection stringEncoding]
@@ -4060,6 +4105,7 @@
contextInfo:[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInteger:rowIndex], @"row",
[aTableColumn identifier], @"column",
+ [NSNumber numberWithInteger:editedColumn], @"editedColumn",
[NSNumber numberWithBool:isFieldEditable], @"isFieldEditable",
nil]];
@@ -4444,6 +4490,8 @@
NSString *re1 = @"^\\s*(<[=>]?|>=?|!?=|≠|≤|≥)\\s*(.*?)\\s*$";
NSString *re2 = @"^\\s*(.*)\\s+(.*?)\\s*$";
NSCharacterSet *whiteSpaceCharSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ NSInteger editedRow = [filterTableView editedRow];
+
if(currentValue == filterTableGearLookAllFields) {
numberOfRows = 1;
@@ -4477,7 +4525,7 @@
}
// Take value from currently edited table cell
} else if([currentValue isKindOfClass:[NSString class]]){
- if(index == [filterTableView editedColumn] && i == [filterTableView editedRow])
+ if(i == editedRow && index == [[NSArrayObjectAtIndex([filterTableView tableColumns], [filterTableView editedColumn]) identifier] integerValue])
filterCell = (NSString*)currentValue;
else
filterCell = NSArrayObjectAtIndex([filterCellData objectForKey:@"filter"], i);
diff --git a/Source/SPTableStructure.m b/Source/SPTableStructure.m
index f57a881f..1cd5ca4e 100644
--- a/Source/SPTableStructure.m
+++ b/Source/SPTableStructure.m
@@ -758,13 +758,7 @@
*/
- (void)setAutoIncrementTo:(NSString*)valueAsString
{
- NSString *selTable = nil;
-
- // if selectedTable is nil try to get the name from SPTablesList
- if (selectedTable == nil || ![selectedTable length])
- selTable = [tablesListInstance tableName];
- else
- selTable = [NSString stringWithString:selectedTable];
+ NSString *selTable = [tablesListInstance tableName];
if (selTable == nil || ![selTable length]) return;
@@ -1219,8 +1213,18 @@
alertSheetOpened = NO;
if(contextInfo && [contextInfo isEqualToString:@"autoincrementindex"]) {
- if(returnCode) {
- autoIncrementIndex = [chooseKeyButton titleOfSelectedItem];
+ if (returnCode) {
+ switch ([[chooseKeyButton selectedItem] tag]) {
+ case SPPrimaryKeyMenuTag:
+ autoIncrementIndex = @"PRIMARY KEY";
+ break;
+ case SPIndexMenuTag:
+ autoIncrementIndex = @"INDEX";
+ break;
+ case SPUniqueMenuTag:
+ autoIncrementIndex = @"UNIQUE";
+ break;
+ }
} else {
autoIncrementIndex = nil;
if([tableSourceView selectedRow] > -1 && [extraFieldSuggestions count])
diff --git a/Source/SPTableStructureDelegate.m b/Source/SPTableStructureDelegate.m
index d63064e2..17ad817d 100644
--- a/Source/SPTableStructureDelegate.m
+++ b/Source/SPTableStructureDelegate.m
@@ -116,9 +116,10 @@
isCurrentExtraAutoIncrement = [[[anObject stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString] isEqualToString:@"AUTO_INCREMENT"];
if(isCurrentExtraAutoIncrement) {
[currentRow setObject:[NSNumber numberWithInteger:0] forKey:@"null"];
+
// Asks the user to add an index to query if AUTO_INCREMENT is set and field isn't indexed
if ((![currentRow objectForKey:@"Key"] || [[currentRow objectForKey:@"Key"] isEqualToString:@""])) {
- [chooseKeyButton selectItemAtIndex:0];
+ [chooseKeyButton selectItemWithTag:SPPrimaryKeyMenuTag];
[NSApp beginSheet:keySheet
modalForWindow:[tableDocumentInstance parentWindow] modalDelegate:self
diff --git a/Source/SPTablesList.h b/Source/SPTablesList.h
index e53fb178..7729ed1d 100644
--- a/Source/SPTablesList.h
+++ b/Source/SPTablesList.h
@@ -103,6 +103,9 @@
BOOL tableListContainsViews;
BOOL alertSheetOpened;
+
+ NSFont *smallSystemFont;
+
}
// IBAction methods
diff --git a/Source/SPTablesList.m b/Source/SPTablesList.m
index da787a6d..a0a9bf92 100644
--- a/Source/SPTablesList.m
+++ b/Source/SPTablesList.m
@@ -1477,34 +1477,51 @@
/**
* Table view delegate method
*/
-- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
+- (void)tableView:(NSTableView *)aTableView willDisplayCell:(ImageAndTextCell*)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
- if (rowIndex > 0 && rowIndex < [filteredTableTypes count]
- && [[aTableColumn identifier] isEqualToString:@"tables"]) {
- if(![[filteredTables objectAtIndex:rowIndex] isKindOfClass:[NSString class]]) {
- [(ImageAndTextCell*)aCell setImage:nil];
- [(ImageAndTextCell*)aCell setIndentationLevel:0];
- }
- else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeView) {
- [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"table-view-small"]];
- } else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeTable) {
- [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"table-small"]];
- } else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeProc) {
- [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"proc-small"]];
- } else if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeFunc) {
- [(ImageAndTextCell*)aCell setImage:[NSImage imageNamed:@"func-small"]];
+ if (rowIndex > 0 && rowIndex < [filteredTableTypes count] && [[aTableColumn identifier] isEqualToString:@"tables"]) {
+
+ id item = NSArrayObjectAtIndex(filteredTables, rowIndex);
+
+ if(![item isKindOfClass:[NSString class]]) {
+ [aCell setImage:nil];
+ [aCell setIndentationLevel:0];
+ return;
}
- if ([[filteredTableTypes objectAtIndex:rowIndex] integerValue] == SPTableTypeNone) {
- [(ImageAndTextCell*)aCell setImage:nil];
- [(ImageAndTextCell*)aCell setIndentationLevel:0];
- } else {
- [(ImageAndTextCell*)aCell setIndentationLevel:1];
- [(ImageAndTextCell*)aCell setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
+ switch([NSArrayObjectAtIndex(filteredTableTypes, rowIndex) integerValue]) {
+ case SPTableTypeView:
+ [aCell setImage:[NSImage imageNamed:@"table-view-small"]];
+ [aCell setIndentationLevel:1];
+ [aCell setFont:smallSystemFont];
+ break;
+ case SPTableTypeTable:
+ [aCell setImage:[NSImage imageNamed:@"table-small"]];
+ [aCell setIndentationLevel:1];
+ [aCell setFont:smallSystemFont];
+ break;
+ case SPTableTypeProc:
+ [aCell setImage:[NSImage imageNamed:@"proc-small"]];
+ [aCell setIndentationLevel:1];
+ [aCell setFont:smallSystemFont];
+ break;
+ case SPTableTypeFunc:
+ [aCell setImage:[NSImage imageNamed:@"func-small"]];
+ [aCell setIndentationLevel:1];
+ [aCell setFont:smallSystemFont];
+ break;
+ case SPTableTypeNone:
+ [aCell setImage:nil];
+ [aCell setIndentationLevel:0];
+ break;
+ default:
+ [aCell setIndentationLevel:1];
+ [aCell setFont:smallSystemFont];
}
+
} else {
- [(ImageAndTextCell*)aCell setImage:nil];
- [(ImageAndTextCell*)aCell setIndentationLevel:0];
+ [aCell setImage:nil];
+ [aCell setIndentationLevel:0];
}
}
@@ -1775,6 +1792,7 @@
selectedTableType = SPTableTypeNone;
selectedTableName = nil;
[tables addObject:NSLocalizedString(@"TABLES",@"header for table list")];
+ smallSystemFont = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
}
return self;
@@ -2007,7 +2025,7 @@
// If there is a type selected other than the default we must specify it in CREATE TABLE statement
if ([tableTypeButton indexOfSelectedItem] > 0) {
- engineStatement = [NSString stringWithFormat:@"ENGINE = %@", [tableType backtickQuotedString]];
+ engineStatement = [NSString stringWithFormat:@"%@ = %@", [[tableDocumentInstance serverSupport] engineTypeQueryName], [tableType backtickQuotedString]];
}
NSString *createStatement = [NSString stringWithFormat:@"CREATE TABLE %@ (%@) %@ %@", [tableName backtickQuotedString], ([tableType isEqualToString:@"CSV"]) ? @"id INT NOT NULL" : @"id INT", charSetStatement, engineStatement];
diff --git a/Source/SPTextView.h b/Source/SPTextView.h
index 797fff34..b15030b4 100644
--- a/Source/SPTextView.h
+++ b/Source/SPTextView.h
@@ -107,6 +107,7 @@
- (BOOL) isNextCharMarkedBy:(id)attribute withValue:(id)aValue;
- (BOOL) areAdjacentCharsLinked;
- (BOOL) isCaretAdjacentToAlphanumCharWithInsertionOf:(unichar)aChar;
+- (BOOL) isCaretAtIndentPositionIgnoreLineStart:(BOOL)ignoreLineStart;
- (BOOL) wrapSelectionWithPrefix:(NSString *)prefix suffix:(NSString *)suffix;
- (BOOL) shiftSelectionRight;
- (BOOL) shiftSelectionLeft;
diff --git a/Source/SPTextView.m b/Source/SPTextView.m
index af4356a2..82bfdedf 100644
--- a/Source/SPTextView.m
+++ b/Source/SPTextView.m
@@ -144,7 +144,6 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[self setAutohelp:[prefs boolForKey:SPCustomQueryUpdateAutoHelp]];
[self setAutouppercaseKeywords:[prefs boolForKey:SPCustomQueryAutoUppercaseKeywords]];
[self setCompletionWasReinvokedAutomatically:NO];
-
// Re-define tab stops for a better editing
[self setTabStops];
@@ -153,9 +152,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[[self layoutManager] setBackgroundLayoutEnabled:NO];
// add NSViewBoundsDidChangeNotification to scrollView
- [[scrollView contentView] setPostsBoundsChangedNotifications:YES];
- NSNotificationCenter *aNotificationCenter = [NSNotificationCenter defaultCenter];
- [aNotificationCenter addObserver:self selector:@selector(boundsDidChangeNotification:) name:@"NSViewBoundsDidChangeNotification" object:[scrollView contentView]];
+ [scrollView setPostsBoundsChangedNotifications:YES];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChangeNotification:) name:NSViewBoundsDidChangeNotification object:[scrollView contentView]];
[self setQueryHiliteColor:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPCustomQueryEditorHighlightQueryColor]]];
[self setQueryEditorBackgroundColor:[NSUnarchiver unarchiveObjectWithData:[prefs dataForKey:SPCustomQueryEditorBackgroundColor]]];
@@ -775,6 +773,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
object:nil];
// Check for table name aliases
+ NSString *alias = nil;
if(dbBrowseMode && tableDocumentInstance && customQueryInstance) {
NSString *theDb = (dbName == nil) ? [NSString stringWithString:currentDb] : [NSString stringWithString:dbName];
NSString *connectionID = [tableDocumentInstance connectionID];
@@ -790,7 +789,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
for(NSString* m in matches) {
NSRange aliasRange = [m rangeOfRegex:re capture:1L];
if(aliasRange.length) {
- NSString *alias = [[m substringWithRange:aliasRange] stringByReplacingOccurrencesOfString:@"``" withString:@"`"];
+ alias = [[m substringWithRange:aliasRange] stringByReplacingOccurrencesOfString:@"``" withString:@"`"];
// If alias refers to db.table split and check it
if([alias rangeOfString:@"."].length) {
NSRange dbRange = [alias rangeOfRegex:@"^`?(.*?)`?\\." capture:1L];
@@ -819,6 +818,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
if (completionIsOpen) [completionPopup close], completionPopup = nil;
+
completionIsOpen = YES;
completionPopup = [[SPNarrowDownCompletion alloc] initWithItems:[self suggestionsForSQLCompletionWith:currentWord dictMode:isDictMode browseMode:dbBrowseMode withTableName:tableName withDbName:dbName]
alreadyTyped:filter
@@ -839,24 +839,19 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
caretMovedLeft:caretMovedLeft
autoComplete:autoCompleteMode
oneColumn:isDictMode
+ alias:alias
isQueryingDBStructure:[mySQLConnection isQueryingDatabaseStructure]];
completionParseRangeLocation = parseRange.location;
//Get the NSPoint of the first character of the current word
- NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(completionRange.location,0) actualCharacterRange:NULL];
+ NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(completionRange.location,1) actualCharacterRange:NULL];
NSRect boundingRect = [[self layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[self textContainer]];
- boundingRect = [self convertRect: boundingRect toView: NULL];
- NSPoint pos = [[self window] convertBaseToScreen: NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
-
- // TODO: check if needed
- // if(filter)
- // pos.x -= [filter sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width;
-
- // Adjust list location to be under the current word or insertion point
+ boundingRect = [self convertRect:boundingRect toView:nil];
+ NSPoint pos = [[self window] convertBaseToScreen:NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
pos.y -= [[self font] pointSize]*1.25;
-
[completionPopup setCaretPos:pos];
+
[completionPopup orderFront:self];
[completionPopup insertAutocompletePlaceholder];
@@ -924,6 +919,24 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
/**
+ * Checks if all the characters left from the caret are white spaces or caret is at the line begin.
+ */
+- (BOOL) isCaretAtIndentPositionIgnoreLineStart:(BOOL)ignoreLineStart
+{
+ NSString *textViewString = [[self textStorage] string];
+ NSUInteger caretPosition = [self selectedRange].location;
+ NSUInteger currentLineStartPosition = [textViewString lineRangeForRange:NSMakeRange(caretPosition, 0)].location;
+
+ // Check if caret is at the beginning of a line
+ // - used for deleteBackward: to allow to delete leading \n
+ if(!ignoreLineStart && caretPosition == currentLineStartPosition)
+ return NO;
+
+ NSString *lineHeadToCaret = [textViewString substringWithRange:NSMakeRange(currentLineStartPosition, caretPosition-currentLineStartPosition)];
+ return (![lineHeadToCaret length] || [lineHeadToCaret isMatchedByRegex:@"^\\s+$"]);
+}
+
+/**
* Checks if the caret is wrapped by auto-paired characters.
* e.g. [| := caret]: "|"
*/
@@ -1112,27 +1125,39 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
{
NSString *textViewString = [[self textStorage] string];
NSRange currentLineRange;
-
- if ([self selectedRange].location == NSNotFound || ![self isEditable]) return NO;
+ NSRange selectedRange = [self selectedRange];
+
+ if (selectedRange.location == NSNotFound || ![self isEditable]) return NO;
+
+ NSString *indentString = @"\t";
+ if ([prefs boolForKey:SPCustomQuerySoftIndent]) {
+ NSUInteger numberOfSpaces = [prefs integerForKey:SPCustomQuerySoftIndentWidth];
+ if(numberOfSpaces < 1) numberOfSpaces = 1;
+ if(numberOfSpaces > 32) numberOfSpaces = 32;
+ NSMutableString *spaces = [NSMutableString string];
+ for(NSInteger i = 0; i < numberOfSpaces; i++)
+ [spaces appendString:@" "];
+ indentString = [NSString stringWithString:spaces];
+ }
// Indent the currently selected line if the caret is within a single line
- if ([self selectedRange].length == 0) {
+ if (selectedRange.length == 0) {
// Extract the current line range based on the text caret
- currentLineRange = [textViewString lineRangeForRange:[self selectedRange]];
+ currentLineRange = [textViewString lineRangeForRange:selectedRange];
// Register the indent for undo
- [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 0) replacementString:@"\t"];
+ [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 0) replacementString:indentString];
// Insert the new tab
- [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 0) withString:@"\t"];
+ [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 0) withString:indentString];
return YES;
}
// Otherwise, something is selected
- NSRange firstLineRange = [textViewString lineRangeForRange:NSMakeRange([self selectedRange].location,0)];
- NSUInteger lastLineMaxRange = NSMaxRange([textViewString lineRangeForRange:NSMakeRange(NSMaxRange([self selectedRange])-1,0)]);
+ NSRange firstLineRange = [textViewString lineRangeForRange:NSMakeRange(selectedRange.location,0)];
+ NSUInteger lastLineMaxRange = NSMaxRange([textViewString lineRangeForRange:NSMakeRange(NSMaxRange(selectedRange)-1,0)]);
// Expand selection for first and last line to begin and end resp. but not the last line ending
NSRange blockRange = NSMakeRange(firstLineRange.location, lastLineMaxRange - firstLineRange.location);
@@ -1143,13 +1168,13 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
NSString *newString;
// check for line ending
if([textViewString characterAtIndex:NSMaxRange(firstLineRange)-1] == '\r')
- newString = [[NSString stringWithString:@"\t"] stringByAppendingString:
+ newString = [indentString stringByAppendingString:
[[textViewString substringWithRange:blockRange]
- stringByReplacingOccurrencesOfString:@"\r" withString:@"\r\t"]];
+ stringByReplacingOccurrencesOfString:@"\r" withString:[NSString stringWithFormat:@"\r%@", indentString]]];
else
- newString = [[NSString stringWithString:@"\t"] stringByAppendingString:
+ newString = [indentString stringByAppendingString:
[[textViewString substringWithRange:blockRange]
- stringByReplacingOccurrencesOfString:@"\n" withString:@"\n\t"]];
+ stringByReplacingOccurrencesOfString:@"\n" withString:[NSString stringWithFormat:@"\n%@", indentString]]];
// Register the indent for undo
[self shouldChangeTextInRange:blockRange replacementString:newString];
@@ -1190,11 +1215,30 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
|| ([textViewString characterAtIndex:currentLineRange.location] != '\t' && [textViewString characterAtIndex:currentLineRange.location] != ' '))
return NO;
+ NSRange replaceRange;
+
+ // Check for soft indention
+ NSUInteger indentStringLength = 1;
+ if ([prefs boolForKey:SPCustomQuerySoftIndent]) {
+ NSUInteger numberOfSpaces = [prefs integerForKey:SPCustomQuerySoftIndentWidth];
+ if(numberOfSpaces < 1) numberOfSpaces = 1;
+ if(numberOfSpaces > 32) numberOfSpaces = 32;
+ indentStringLength = numberOfSpaces;
+ replaceRange = NSIntersectionRange(NSMakeRange(currentLineRange.location, indentStringLength), NSMakeRange(0,[[self string] length]));
+ // Correct length for only white spaces
+ NSString *possibleIndentString = [[[self textStorage] string] substringWithRange:replaceRange];
+ NSUInteger numberOfLeadingWhiteSpaces = [possibleIndentString rangeOfRegex:@"^(\\s*)" capture:1L].length;
+ if(numberOfLeadingWhiteSpaces == NSNotFound) numberOfLeadingWhiteSpaces = 0;
+ replaceRange = NSMakeRange(currentLineRange.location, numberOfLeadingWhiteSpaces);
+ } else {
+ replaceRange = NSMakeRange(currentLineRange.location, indentStringLength);
+ }
+
// Register the undent for undo
- [self shouldChangeTextInRange:NSMakeRange(currentLineRange.location, 1) replacementString:@""];
+ [self shouldChangeTextInRange:replaceRange replacementString:@""];
// Remove the tab
- [self replaceCharactersInRange:NSMakeRange(currentLineRange.location, 1) withString:@""];
+ [self replaceCharactersInRange:replaceRange withString:@""];
return YES;
}
@@ -1208,25 +1252,36 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if([textViewString characterAtIndex:NSMaxRange(blockRange)-1] == '\n' || [textViewString characterAtIndex:NSMaxRange(blockRange)-1] == '\r')
blockRange.length--;
+ // Check for soft or hard indention
+ NSString *indentString = @"\t";
+ NSUInteger indentStringLength = 1;
+ if ([prefs boolForKey:SPCustomQuerySoftIndent]) {
+ indentStringLength = [prefs integerForKey:SPCustomQuerySoftIndentWidth];
+ if(indentStringLength < 1) indentStringLength = 1;
+ if(indentStringLength > 32) indentStringLength = 32;
+ NSMutableString *spaces = [NSMutableString string];
+ for(NSInteger i = 0; i < indentStringLength; i++)
+ [spaces appendString:@" "];
+ indentString = [NSString stringWithString:spaces];
+ }
+
// Check if blockRange starts with SPACE or TAB
// (this also catches the first line of the entire text buffer or
// if only one line is selected)
NSInteger leading = 0;
if([textViewString characterAtIndex:blockRange.location] == ' '
|| [textViewString characterAtIndex:blockRange.location] == '\t')
- leading++;
+ leading += indentStringLength;
// Replace \n[ \t] by \n of all lines in blockRange
NSString *newString;
// check for line ending
if([textViewString characterAtIndex:NSMaxRange(firstLineRange)-1] == '\r')
- newString = [[[textViewString substringWithRange:NSMakeRange(blockRange.location+leading, blockRange.length-leading)]
- stringByReplacingOccurrencesOfString:@"\r\t" withString:@"\r"]
- stringByReplacingOccurrencesOfString:@"\r " withString:@"\r"];
+ newString = [[textViewString substringWithRange:NSMakeRange(blockRange.location+leading, blockRange.length-leading)]
+ stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"\r%@", indentString] withString:@"\r"];
else
- newString = [[[textViewString substringWithRange:NSMakeRange(blockRange.location+leading, blockRange.length-leading)]
- stringByReplacingOccurrencesOfString:@"\n\t" withString:@"\n"]
- stringByReplacingOccurrencesOfString:@"\n " withString:@"\n"];
+ newString = [[textViewString substringWithRange:NSMakeRange(blockRange.location+leading, blockRange.length-leading)]
+ stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"\n%@", indentString] withString:@"\n"];
// Register the unindent for undo
[self shouldChangeTextInRange:blockRange replacementString:newString];
@@ -1407,10 +1462,11 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
caretMovedLeft:NO
autoComplete:NO
oneColumn:NO
+ alias:nil
isQueryingDBStructure:NO];
//Get the NSPoint of the first character of the current word
- NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(aRange.location,0) actualCharacterRange:NULL];
+ NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(aRange.location,1) actualCharacterRange:NULL];
NSRect boundingRect = [[self layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[self textContainer]];
boundingRect = [self convertRect: boundingRect toView: NULL];
NSPoint pos = [[self window] convertBaseToScreen: NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
@@ -1563,10 +1619,11 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
caretMovedLeft:NO
autoComplete:NO
oneColumn:YES
+ alias:nil
isQueryingDBStructure:NO];
//Get the NSPoint of the first character of the current word
- NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(r2.location,0) actualCharacterRange:NULL];
+ NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:NSMakeRange(r2.location,1) actualCharacterRange:NULL];
NSRect boundingRect = [[self layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[self textContainer]];
boundingRect = [self convertRect: boundingRect toView: NULL];
NSPoint pos = [[self window] convertBaseToScreen: NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
@@ -2033,6 +2090,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
// Check for {SHIFT}TAB to try to insert query favorite via TAB trigger if SPTextView belongs to SPCustomQuery
+ // and TAB as soft indention
if ([theEvent keyCode] == 48 && [self isEditable] && [[self delegate] isKindOfClass:[SPCustomQuery class]]){
NSRange targetRange = [self getRangeForCurrentWord];
NSString *tabTrigger = [[self string] substringWithRange:targetRange];
@@ -2079,13 +2137,19 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
// Check if tab trigger is defined; if so insert it, otherwise pass through event
- if(snippetControlCounter < 0 && [tableDocumentInstance fileURL]) {
+ if(snippetControlCounter < 0 && [tabTrigger length] && [tableDocumentInstance fileURL]) {
NSArray *snippets = [[SPQueryController sharedQueryController] queryFavoritesForFileURL:[tableDocumentInstance fileURL] andTabTrigger:tabTrigger includeGlobals:YES];
if([snippets count] > 0 && [(NSString*)[(NSDictionary*)[snippets objectAtIndex:0] objectForKey:@"query"] length]) {
[self insertAsSnippet:[(NSDictionary*)[snippets objectAtIndex:0] objectForKey:@"query"] atRange:targetRange];
return;
}
}
+
+ // Check for TAB as indention for current line, i.e. left of the caret there are only white spaces
+ // but only if Soft Indent is set
+ if([prefs boolForKey:SPCustomQuerySoftIndent] && [self isCaretAtIndentPositionIgnoreLineStart:YES]) {
+ if([self shiftSelectionRight]) return;
+ }
}
// Note: switch(insertedCharacter) {} does not work instead use charactersIgnoringModifiers
@@ -2277,7 +2341,14 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
- (void)moveWordRight:(id)sender
{
[super moveWordRight:sender];
- while([self selectedRange].location < [[[self textStorage] string] length] && [[[self textStorage] string] characterAtIndex:[self selectedRange].location] == '.')
+ NSCharacterSet *whiteSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ while([self selectedRange].location < [[[self textStorage] string] length]
+ && ([[[self textStorage] string] characterAtIndex:[self selectedRange].location] == '.'
+ || (
+ [[[self textStorage] string] characterAtIndex:[self selectedRange].location-1] == '.'
+ && ![whiteSet characterIsMember:[[[self textStorage] string] characterAtIndex:[self selectedRange].location]]
+ )
+ ))
[super moveWordRight:sender];
}
@@ -2298,22 +2369,41 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
- (void)moveWordRightAndModifySelection:(id)sender
{
[super moveWordRightAndModifySelection:sender];
- while(NSMaxRange([self selectedRange]) < [[[self textStorage] string] length] && [[[self textStorage] string] characterAtIndex:NSMaxRange([self selectedRange])] == '.')
+ NSCharacterSet *whiteSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ while(NSMaxRange([self selectedRange]) < [[[self textStorage] string] length]
+ && ([[[self textStorage] string] characterAtIndex:NSMaxRange([self selectedRange])] == '.'
+ || (
+ [[[self textStorage] string] characterAtIndex:NSMaxRange([self selectedRange])-1] == '.'
+ && ![whiteSet characterIsMember:[[[self textStorage] string] characterAtIndex:NSMaxRange([self selectedRange])]]
+ )
+ ))
[super moveWordRightAndModifySelection:sender];
}
- (void) deleteBackward:(id)sender
{
- // If the caret is currently inside a marked auto-pair, delete the characters on both sides
- // of the caret.
NSRange currentRange = [self selectedRange];
- if (currentRange.length == 0 && currentRange.location > 0 && [self areAdjacentCharsLinked])
- [self setSelectedRange:NSMakeRange(currentRange.location - 1,2)];
- // Avoid auto-uppercasing if resulting word would be a SQL keyword;
- // e.g. type inta| and deleteBackward:
- delBackwardsWasPressed = YES;
+ if (currentRange.length == 0) {
+
+ // If the caret is currently inside a marked auto-pair, delete the characters on both sides
+ // of the caret.
+ if (currentRange.location > 0 && [self areAdjacentCharsLinked]) {
+ [self setSelectedRange:NSMakeRange(currentRange.location - 1,2)];
+ // Avoid auto-uppercasing if resulting word would be a SQL keyword;
+ // e.g. type inta| and deleteBackward:
+ delBackwardsWasPressed = YES;
+ }
+
+ // Remove soft indent if active and left from caret are only white spaces
+ else if ([prefs boolForKey:SPCustomQuerySoftIndent] && [self isCaretAtIndentPositionIgnoreLineStart:NO])
+ {
+ [self shiftSelectionLeft];
+ return;
+ }
+
+ }
[super deleteBackward:sender];
@@ -2365,6 +2455,18 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
// Return to avoid the original implementation, preventing double linebreaks
return;
}
+
+ // Remove soft indent if active and left from caret are only white spaces
+ if (aSelector == @selector(deleteForward:)
+ && ![self selectedRange].length
+ && [prefs boolForKey:SPCustomQuerySoftIndent]
+ && [self isCaretAtIndentPositionIgnoreLineStart:YES]
+ && [self selectedRange].location < [[self string] length] && [[self string] characterAtIndex:[self selectedRange].location] == ' ')
+ {
+ [self shiftSelectionLeft];
+ return;
+ }
+
[super doCommandBySelector:aSelector];
}
@@ -3015,9 +3117,9 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
/**
* Scrollview delegate after the textView's view port was changed.
- * Manily used to update the syntax highlighting for a large text size.
+ * Manily used to update the syntax highlighting for a large text size and line numbering rendering.
*/
-- (void) boundsDidChangeNotification:(NSNotification *)notification
+- (void)boundsDidChangeNotification:(NSNotification *)notification
{
// Invoke syntax highlighting if text view port was changed for large text
if(startListeningToBoundChanges && [[self string] length] > SP_TEXT_SIZE_TRIGGER_FOR_PARTLY_PARSING)
@@ -3029,6 +3131,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
if(![[self textStorage] changeInLength])
[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.4];
}
+ // else
+ // [scrollView displayRect:[scrollView visibleRect]];
}
@@ -3057,7 +3161,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
}
// Start autocompletion if enabled
- if([[NSApp keyWindow] firstResponder] == self && [prefs boolForKey:SPCustomQueryAutoComplete] && !completionIsOpen && editedMask != 1 && [textStore editedRange].length)
+ if([[NSApp keyWindow] firstResponder] == self && [prefs boolForKey:SPCustomQueryAutoComplete] && !completionIsOpen && editedMask != 1 && [textStore changeInLength] == 1)
[self performSelector:@selector(doAutoCompletion) withObject:nil afterDelay:[[prefs valueForKey:SPCustomQueryAutoCompleteDelay] doubleValue]];
// Cancel calling doSyntaxHighlighting for large text
@@ -3069,6 +3173,8 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
// Do syntax highlighting/re-calculate snippet ranges only if the user really changed the text
if(editedMask != 1) {
+ [customQueryInstance setTextViewWasChanged:YES];
+
// Re-calculate snippet ranges if snippet session is active
if(snippetControlCounter > -1 && !snippetWasJustInserted && !isProcessingMirroredSnippets) {
// Remove any fully nested snippets relative to the current snippet which was edited
@@ -3132,6 +3238,7 @@ NSInteger alphabeticSort(id string1, id string2, void *reverse)
[self doSyntaxHighlighting];
} else {
+ [customQueryInstance setTextViewWasChanged:NO];
textBufferSizeIncreased = NO;
}
diff --git a/Source/SPTextViewAdditions.m b/Source/SPTextViewAdditions.m
index 2e1f8f9b..9cefa758 100644
--- a/Source/SPTextViewAdditions.m
+++ b/Source/SPTextViewAdditions.m
@@ -41,41 +41,34 @@
if (curRange.length)
return curRange;
- NSUInteger curLocation = curRange.location;
+ NSInteger curLocation = curRange.location;
+ NSInteger start = curLocation;
+ NSInteger end = curLocation;
+ NSUInteger strLen = [[self string] length];
+
+ NSMutableCharacterSet *wordCharSet = [NSMutableCharacterSet alphanumericCharacterSet];
+ [wordCharSet addCharactersInString:@"_."];
+ [wordCharSet removeCharactersInString:@"`"];
+
+ if(start) {
+ start--;
+ while([wordCharSet characterIsMember:[[self string] characterAtIndex:start]]) {
+ start--;
+ if(start < 0) break;
+ }
+ start++;
+ }
- [self moveWordLeft:self];
- [self moveWordRightAndModifySelection:self];
-
- NSUInteger newStartRange = [self selectedRange].location;
- NSUInteger newEndRange = newStartRange + [self selectedRange].length;
-
- // if current location does not intersect with found range
- // then caret is at the begin of a word -> change strategy
- if(curLocation < newStartRange || curLocation > newEndRange)
- {
- [self setSelectedRange:curRange];
- [self moveWordRight:self];
- [self moveWordLeftAndModifySelection:self];
- newStartRange = [self selectedRange].location;
- newEndRange = newStartRange + [self selectedRange].length;
+ while(end < strLen && [wordCharSet characterIsMember:[[self string] characterAtIndex:end]]) {
+ end++;
}
-
- // how many space in front of the selection
- NSInteger bias = [self selectedRange].length - [[[[self string] substringWithRange:[self selectedRange]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length];
- [self setSelectedRange:NSMakeRange([self selectedRange].location+bias, [self selectedRange].length-bias)];
- newStartRange += bias;
- newEndRange -= bias;
-
- // is caret inside the selection still?
- if(curLocation < newStartRange || curLocation > newEndRange
- || [[[self string] substringWithRange:[self selectedRange]] rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].location != NSNotFound)
- [self setSelectedRange:curRange];
-
- NSRange wordRange = [self selectedRange];
-
- [self setSelectedRange:curRange];
-
+
+ NSRange wordRange = NSMakeRange(start, end-start);
+ if(wordRange.length && [[self string] characterAtIndex:NSMaxRange(wordRange)-1] == '.')
+ wordRange.length--;
+
return(wordRange);
+
}
/*
@@ -534,7 +527,7 @@
[[NSFileManager defaultManager] removeItemAtPath:bundleInputFilePath error:nil];
- BOOL selfIsQueryEditor = ([[[self class] description] isEqualToString:@"SPTextView"] && [self respondsToSelector:@selector(currentQueryRange)]) ;
+ BOOL selfIsQueryEditor = ([[[self class] description] isEqualToString:@"SPTextView"] && [[self delegate] respondsToSelector:@selector(currentQueryRange)]);
if([cmdData objectForKey:SPBundleFileInputSourceKey])
inputAction = [[cmdData objectForKey:SPBundleFileInputSourceKey] lowercaseString];
@@ -562,7 +555,7 @@
replaceRange = currentLineRange;
else if([inputFallBackAction isEqualToString:SPBundleInputSourceCurrentQuery])
replaceRange = currentQueryRange;
- else if([inputAction isEqualToString:SPBundleInputSourceEntireContent])
+ else if([inputFallBackAction isEqualToString:SPBundleInputSourceEntireContent])
replaceRange = NSMakeRange(0,[[self string] length]);
} else {
replaceRange = currentSelectionRange;
@@ -622,8 +615,11 @@
if(currentLineRange.length)
[env setObject:[[self string] substringWithRange:currentLineRange] forKey:SPBundleShellVariableCurrentLine];
+ [env setObject:NSStringFromRange(replaceRange) forKey:SPBundleShellVariableSelectedTextRange];
+
NSError *inputFileError = nil;
NSString *input = [NSString stringWithString:[[self string] substringWithRange:replaceRange]];
+
[input writeToFile:bundleInputFilePath
atomically:YES
encoding:NSUTF8StringEncoding
@@ -739,8 +735,9 @@
}
else if([action isEqualToString:SPBundleOutputActionReplaceSelection]) {
- [self shouldChangeTextInRange:replaceRange replacementString:output];
- [self replaceCharactersInRange:replaceRange withString:output];
+ NSRange safeRange = NSIntersectionRange(replaceRange, NSMakeRange(0, [[self string] length]));
+ [self shouldChangeTextInRange:safeRange replacementString:output];
+ [self replaceCharactersInRange:safeRange withString:output];
}
} else {
diff --git a/Source/SPTooltip.m b/Source/SPTooltip.m
index e1cccbf9..b0344d52 100644
--- a/Source/SPTooltip.m
+++ b/Source/SPTooltip.m
@@ -257,24 +257,20 @@ static CGFloat slow_in_out (CGFloat t)
id fr = [[NSApp keyWindow] firstResponder];
//If first responder is a textview return the caret position
- if([fr respondsToSelector:@selector(getRangeForCurrentWord)] ) {
- NSRange range = NSMakeRange([fr selectedRange].location,0);
+ if(([fr isMemberOfClass:[NSTextView class]] && [fr alignment] == NSLeftTextAlignment) || [[[fr class] description] isEqualToString:@"SPTextView"]) {
+ NSRange range = NSMakeRange([fr selectedRange].location,1);
NSRange glyphRange = [[fr layoutManager] glyphRangeForCharacterRange:range actualCharacterRange:NULL];
NSRect boundingRect = [[fr layoutManager] boundingRectForGlyphRange:glyphRange inTextContainer:[fr textContainer]];
- boundingRect = [fr convertRect: boundingRect toView: NULL];
+ boundingRect = [fr convertRect: boundingRect toView:NULL];
pos = [[fr window] convertBaseToScreen: NSMakePoint(boundingRect.origin.x + boundingRect.size.width,boundingRect.origin.y + boundingRect.size.height)];
NSFont* font = [fr font];
- pos.y -= [font pointSize]*1.3;
+ if(font) pos.y -= [font pointSize]*1.3;
return pos;
- // Otherwise return the upper left corner of the current keyWindow
+ // Otherwise return mouse location
} else {
pos = [NSEvent mouseLocation];
pos.y -= 16;
return pos;
- // pos = [[NSApp keyWindow] frame].origin;
- // pos.x += 5;
- // pos.y += [[NSApp keyWindow] frame].size.height - 23;
- // return pos;
}
}
diff --git a/Source/SPUserManager.h b/Source/SPUserManager.h
index c4eff098..8bb4e4d2 100644
--- a/Source/SPUserManager.h
+++ b/Source/SPUserManager.h
@@ -59,6 +59,9 @@
IBOutlet NSTextField *userNameTextField;
+ IBOutlet NSWindow *errorsSheet;
+ IBOutlet NSTextView *errorsTextView;
+
IBOutlet BWAnchoredButtonBar *splitViewButtonBar;
NSMutableArray *schemas;
@@ -67,6 +70,9 @@
NSArray *treeSortDescriptors;
NSSortDescriptor *treeSortDescriptor;
+
+ BOOL isSaving;
+ NSMutableString *errorsString;
}
@property (nonatomic, retain) MCPConnection *mySqlConnection;
@@ -93,6 +99,7 @@
- (IBAction)doApply:(id)sender;
- (IBAction)checkAllPrivileges:(id)sender;
- (IBAction)uncheckAllPrivileges:(id)sender;
+- (IBAction)closeErrorsSheet:(id)sender;
// Schema Privieges
- (IBAction)addSchemaPriv:(id)sender;
diff --git a/Source/SPUserManager.m b/Source/SPUserManager.m
index b8d0e826..9118d8a9 100644
--- a/Source/SPUserManager.m
+++ b/Source/SPUserManager.m
@@ -91,6 +91,7 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
schemas = [[NSMutableArray alloc] init];
availablePrivs = [[NSMutableArray alloc] init];
grantedSchemaPrivs = [[NSMutableArray alloc] init];
+ isSaving = NO;
}
return self;
@@ -112,7 +113,11 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
// Set the button delegate
[splitViewButtonBar setSplitViewDelegate:self];
-
+
+ // Set schema table double-click actions
+ [grantedTableView setDoubleAction:@selector(doubleClickSchemaPriv:)];
+ [availableTableView setDoubleAction:@selector(doubleClickSchemaPriv:)];
+
[self _initializeUsers];
[self _initializeSchemaPrivs];
@@ -363,6 +368,10 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
{
// Assumes that the child has already been initialized with values from the
// global user table.
+
+ // Set an originalhost key on the child to allow the tracking of edits
+ [child setPrimitiveValue:[child valueForKey:@"host"] forKey:@"originalhost"];
+
// Select rows from the db table that contains schema privs for each user/host
NSString *queryString = [NSString stringWithFormat:@"SELECT * from mysql.db d WHERE d.user = %@ and d.host = %@",
[[[child parent] valueForKey:@"user"] tickQuotedString], [[child valueForKey:@"host"] tickQuotedString]];
@@ -597,21 +606,29 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
- (IBAction)doApply:(id)sender
{
NSError *error = nil;
+ errorsString = [[NSMutableString alloc] init];
//Change the first responder to end editing in any field
[[self window] makeFirstResponder:self];
+ isSaving = YES;
[[self managedObjectContext] save:&error];
-
- if (error != nil) {
- [[NSApplication sharedApplication] presentError:error];
- }
- else {
- // Close sheet
- [self.mySqlConnection queryString:@"FLUSH PRIVILEGES"];
- [NSApp endSheet:[self window] returnCode:0];
- [[self window] orderOut:self];
+ isSaving = NO;
+ if (error != nil) [errorsString appendString:[error localizedDescription]];
+
+ [self.mySqlConnection queryString:@"FLUSH PRIVILEGES"];
+
+ // Display any errors
+ if ([errorsString length]) {
+ [errorsTextView setString:errorsString];
+ [NSApp beginSheet:errorsSheet modalForWindow:[NSApp keyWindow] modalDelegate:nil didEndSelector:NULL contextInfo:nil];
+ [errorsString release];
+ return;
}
+
+ // Otherwise, close the sheet
+ [NSApp endSheet:[self window] returnCode:0];
+ [[self window] orderOut:self];
}
/**
@@ -779,6 +796,23 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
}
/**
+ * Move double-clicked rows across to the other table, using the
+ * appropriate methods.
+ */
+- (IBAction)doubleClickSchemaPriv:(id)sender
+{
+
+ // Ignore double-clicked header cells
+ if ([sender clickedRow] == -1) return;
+
+ if (sender == availableTableView) {
+ [self addSchemaPriv:sender];
+ } else {
+ [self removeSchemaPriv:sender];
+ }
+}
+
+/**
* Refreshes the current list of users.
*/
- (IBAction)refresh:(id)sender
@@ -934,12 +968,25 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
}
}
+/**
+ * Closes the supplied sheet, before closing the master window.
+ */
+- (IBAction)closeErrorsSheet:(id)sender
+{
+ [NSApp endSheet:[sender window] returnCode:[sender tag]];
+ [[sender window] orderOut:self];
+
+ // Close the window
+ [NSApp endSheet:[self window] returnCode:0];
+ [[self window] orderOut:self];
+}
+
#pragma mark -
#pragma mark Notifications
/**
* This notification is called when the managedObjectContext save happens.
- * This takes the inserted, updated, and deleted arrays and applys them to
+ * This takes the inserted, updated, and deleted arrays and applies them to
* the database.
*/
- (void)contextDidSave:(NSNotification *)notification
@@ -995,7 +1042,7 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
NSString *renameUserStatement = [NSString stringWithFormat:
@"RENAME USER %@@%@ TO %@@%@",
[[user valueForKey:@"originaluser"] tickQuotedString],
- [[child host] tickQuotedString],
+ [([child valueForKey:@"originalhost"]?[child valueForKey:@"originalhost"]:[child host]) tickQuotedString],
[[user valueForKey:@"user"] tickQuotedString],
[[child host] tickQuotedString]];
@@ -1020,6 +1067,19 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
}
}
else {
+
+ // If the hostname has changed, remane the detail before editing details.
+ if (![[user valueForKey:@"host"] isEqualToString:[user valueForKey:@"originalhost"]]) {
+ NSString *renameUserStatement = [NSString stringWithFormat:
+ @"RENAME USER %@@%@ TO %@@%@",
+ [[[user parent] valueForKey:@"originaluser"] tickQuotedString],
+ [[user valueForKey:@"originalhost"] tickQuotedString],
+ [[[user parent] valueForKey:@"user"] tickQuotedString],
+ [[user valueForKey:@"host"] tickQuotedString]];
+
+ [self.mySqlConnection queryString:renameUserStatement];
+ }
+
if ([serverSupport supportsUserMaxVars]) [self updateResourcesForUser:user];
[self grantPrivilegesToUser:user];
@@ -1366,11 +1426,14 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
- (BOOL)_checkAndDisplayMySqlError
{
if ([self.mySqlConnection queryErrored]) {
-
- SPBeginAlertSheet(NSLocalizedString(@"An error occurred", @"mysql error occurred message"),
- NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil,
- [NSString stringWithFormat:NSLocalizedString(@"An error occurred whilst trying to perform the operation.\n\nMySQL said: %@", @"mysql error occurred informative message"), [self.mySqlConnection getLastErrorMessage]]);
-
+ if (isSaving) {
+ [errorsString appendFormat:@"%@\n", [self.mySqlConnection getLastErrorMessage]];
+ } else {
+ SPBeginAlertSheet(NSLocalizedString(@"An error occurred", @"mysql error occurred message"),
+ NSLocalizedString(@"OK", @"OK button"), nil, nil, [self window], self, nil, nil,
+ [NSString stringWithFormat:NSLocalizedString(@"An error occurred whilst trying to perform the operation.\n\nMySQL said: %@", @"mysql error occurred informative message"), [self.mySqlConnection getLastErrorMessage]]);
+ }
+
return NO;
}
@@ -1470,7 +1533,7 @@ static const NSString *SPTableViewNameColumnID = @"NameColumn";
*/
- (CGFloat)splitView:(NSSplitView *)sender constrainMaxCoordinate:(CGFloat)proposedMax ofSubviewAt:(NSInteger)offset
{
- return (proposedMax - 555);
+ return (proposedMax - 620);
}
/**
diff --git a/Source/SPWindow.h b/Source/SPWindow.h
new file mode 100644
index 00000000..369ce769
--- /dev/null
+++ b/Source/SPWindow.h
@@ -0,0 +1,32 @@
+//
+// $Id$
+//
+// SPWindow.h
+// sequel-pro
+//
+// Created by Rowan Beentje on January 23, 2011
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface SPWindow : NSWindow {
+
+}
+
+@end
diff --git a/Source/SPWindow.m b/Source/SPWindow.m
new file mode 100644
index 00000000..58f38d0b
--- /dev/null
+++ b/Source/SPWindow.m
@@ -0,0 +1,112 @@
+//
+// $Id$
+//
+// SPWindow.m
+// sequel-pro
+//
+// Created by Rowan Beentje on January 23, 2011
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// More info at <http://code.google.com/p/sequel-pro/>
+
+#import "SPWindow.h"
+
+
+@implementation SPWindow
+
+#pragma mark -
+#pragma mark Keyboard shortcut additions
+
+/**
+ * While keyboard shortcuts are an easy way to apply code app-wide, alternate menu
+ * items only collapse if the unmodified key matches; this method allows keyboard
+ * shortcuts without menu equivalents for a window, or the use of different base shortcuts.
+ */
+- (void) sendEvent:(NSEvent *)theEvent
+{
+
+ if ([theEvent type] == NSKeyDown && [[theEvent characters] length]) {
+
+ unichar theCharacter = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
+
+ // ⌃⎋ sends a right-click to order front the context menu under the first responder's visible Rect
+ if ([theEvent keyCode] == 53 && (([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSAlternateKeyMask))) {
+
+ id firstResponder = [[NSApp keyWindow] firstResponder];
+
+ if(firstResponder && [firstResponder respondsToSelector:@selector(menuForEvent:)]) {
+
+ NSRect theRect = [firstResponder visibleRect];
+ NSPoint loc = theRect.origin;
+ loc.y += theRect.size.height+5;
+ loc = [firstResponder convertPoint:loc toView:nil];
+ NSEvent *anEvent = [NSEvent
+ mouseEventWithType:NSRightMouseDown
+ location:loc
+ modifierFlags:0
+ timestamp:1
+ windowNumber:[self windowNumber]
+ context:[NSGraphicsContext currentContext]
+ eventNumber:0
+ clickCount:1
+ pressure:0.0];
+
+ [NSMenu popUpContextMenu:[firstResponder menuForEvent:theEvent] withEvent:anEvent forView:firstResponder];
+
+ return;
+
+ }
+
+ }
+
+ switch (theCharacter) {
+
+ // Alternate keys for switching tabs - ⇧⌘[ and ⇧⌘]. These seem to be standards on some apps,
+ // including Apple applications under some circumstances
+ case '}':
+ if (([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask))
+ {
+ return [[self windowController] selectNextDocumentTab:self];
+ }
+ break;
+ case '{':
+ if (([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSShiftKeyMask))
+ {
+ return [[self windowController] selectPreviousDocumentTab:self];
+ }
+ break;
+
+ // Also support ⌥⌘← and ⌥⌘→, used in other applications, for maximum compatibility
+ case NSRightArrowFunctionKey:
+ if (([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSAlternateKeyMask | NSNumericPadKeyMask | NSFunctionKeyMask))
+ {
+ return [[self windowController] selectNextDocumentTab:self];
+ }
+ break;
+ case NSLeftArrowFunctionKey:
+ if (([theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask) == (NSCommandKeyMask | NSAlternateKeyMask | NSNumericPadKeyMask | NSFunctionKeyMask))
+ {
+ return [[self windowController] selectPreviousDocumentTab:self];
+ }
+ break;
+ }
+ }
+
+ [super sendEvent:theEvent];
+}
+
+
+@end
diff --git a/Source/SequelProTunnelAssistant.m b/Source/SequelProTunnelAssistant.m
index bbf21a3c..eb16ef5e 100644
--- a/Source/SequelProTunnelAssistant.m
+++ b/Source/SequelProTunnelAssistant.m
@@ -72,8 +72,8 @@ int main(int argc, const char *argv[])
// request the password
if ([[environment objectForKey:@"SP_PASSWORD_METHOD"] integerValue] == SPSSHPasswordUsesKeychain) {
SPKeychain *keychain;
- NSString *keychainName = [environment objectForKey:@"SP_KEYCHAIN_ITEM_NAME"];
- NSString *keychainAccount = [environment objectForKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"];
+ NSString *keychainName = [[environment objectForKey:@"SP_KEYCHAIN_ITEM_NAME"] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ NSString *keychainAccount = [[environment objectForKey:@"SP_KEYCHAIN_ITEM_ACCOUNT"] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if (!keychainName || !keychainAccount) {
NSLog(@"SSH Tunnel: keychain authentication specified but insufficient internal details supplied");
@@ -82,17 +82,16 @@ int main(int argc, const char *argv[])
}
keychain = [[SPKeychain alloc] init];
- if (![keychain passwordExistsForName:keychainName account:keychainAccount]) {
- NSLog(@"SSH Tunnel: specified keychain password not found");
+ if ([keychain passwordExistsForName:keychainName account:keychainAccount]) {
+ printf("%s\n", [[keychain getPasswordForName:keychainName account:keychainAccount] UTF8String]);
[keychain release];
[pool release];
- return 1;
+ return 0;
}
- printf("%s\n", [[keychain getPasswordForName:keychainName account:keychainAccount] UTF8String]);
- [keychain release];
- [pool release];
- return 0;
+ // If retrieving the password failed, log an error and fall back to requesting from the GUI
+ NSLog(@"SSH Tunnel: specified keychain password not found");
+ argument = [NSString stringWithFormat:NSLocalizedString(@"The SSH password could not be loaded from the keychain; please enter the SSH password for %@:", @"Prompt for SSH password when keychain fetch failed"), connectionName];
}
// If the password method is set to request the password from the tunnel instance, do so.
@@ -113,15 +112,15 @@ int main(int argc, const char *argv[])
}
password = [sequelProTunnel getPasswordWithVerificationHash:verificationHash];
- if (!password) {
- NSLog(@"SSH Tunnel: unable to successfully request password from Sequel Pro for internal authentication");
+ if (password) {
+ printf("%s\n", [password UTF8String]);
[pool release];
- return 1;
+ return 0;
}
-
- printf("%s\n", [password UTF8String]);
- [pool release];
- return 0;
+
+ // If retrieving the password failed, log an error and fall back to requesting from the GUI
+ NSLog(@"SSH Tunnel: unable to successfully request password from Sequel Pro for internal authentication");
+ argument = [NSString stringWithFormat:NSLocalizedString(@"The SSH password could not be loaded; please enter the SSH password for %@:", @"Prompt for SSH password when direct fetch failed"), connectionName];
}
}