diff options
Diffstat (limited to 'Source/SPAppController.m')
-rw-r--r-- | Source/SPAppController.m | 604 |
1 files changed, 399 insertions, 205 deletions
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:@" " 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]) { |