├── .gitmodules ├── XcodeEditor.framework ├── Versions │ ├── Current │ └── A │ │ ├── XcodeEditor │ │ ├── Resources │ │ ├── en.lproj │ │ │ └── InfoPlist.strings │ │ └── Info.plist │ │ └── Headers │ │ ├── XcodeGroupMember.h │ │ ├── XCKeyBuilder.h │ │ ├── XCFrameworkDefinition.h │ │ ├── XCXibDefinition.h │ │ ├── XcodeMemberType.h │ │ ├── XcodeSourceFileType.h │ │ ├── XCSourceFileDefinition.h │ │ ├── XCFileOperationQueue.h │ │ ├── XCAbstractDefinition.h │ │ ├── XCClassDefinition.h │ │ ├── XCTarget.h │ │ ├── XCSourceFile.h │ │ ├── XCProject.h │ │ └── XCGroup.h ├── Headers ├── Resources └── XcodeEditor ├── SCStringsUtility ├── Resources │ ├── icon.icns │ ├── icon-export.png │ ├── icon-open.png │ └── icon-save.png ├── SCRootViewController.h ├── main.m ├── Others │ ├── NSData+SCAdditions.h │ ├── NSString+SCAdditions.h │ ├── NSData+SCAdditions.m │ ├── OrderedDictionary.h │ ├── NSString+SCAdditions.m │ └── OrderedDictionary.m ├── Views │ ├── SCOpenAccessoryView.h │ ├── SCExportAccessoryView.h │ ├── SCOpenAccessoryView.m │ ├── SCExportAccessoryView.m │ ├── SCOpenAccessoryView.xib │ └── SCExportAccessoryView.xib ├── Readers&Writers │ ├── SCCSVWriter.h │ ├── SCStringsWriter.h │ ├── SCReader.h │ ├── SCCSVWriter.m │ ├── SCStringsWriter.m │ └── SCReader.m ├── SCStringsUtility-Prefix.pch ├── SCAppDelegate.h ├── SCAppDelegate.m ├── SCStringsUtility-Info.plist ├── en.lproj │ └── Credits.rtf ├── SCStringsController.h ├── SCRootViewController.m ├── SCRootViewController.xib └── SCStringsController.m ├── SCStringsUtility.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── project.pbxproj ├── .gitignore ├── README.md └── LICENSE /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /XcodeEditor.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /XcodeEditor.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /XcodeEditor.framework/XcodeEditor: -------------------------------------------------------------------------------- 1 | Versions/Current/XcodeEditor -------------------------------------------------------------------------------- /SCStringsUtility/Resources/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefanceriu/SCStringsUtility/HEAD/SCStringsUtility/Resources/icon.icns -------------------------------------------------------------------------------- /SCStringsUtility/Resources/icon-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefanceriu/SCStringsUtility/HEAD/SCStringsUtility/Resources/icon-export.png -------------------------------------------------------------------------------- /SCStringsUtility/Resources/icon-open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefanceriu/SCStringsUtility/HEAD/SCStringsUtility/Resources/icon-open.png -------------------------------------------------------------------------------- /SCStringsUtility/Resources/icon-save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefanceriu/SCStringsUtility/HEAD/SCStringsUtility/Resources/icon-save.png -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/XcodeEditor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefanceriu/SCStringsUtility/HEAD/XcodeEditor.framework/Versions/A/XcodeEditor -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Resources/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefanceriu/SCStringsUtility/HEAD/XcodeEditor.framework/Versions/A/Resources/en.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /SCStringsUtility.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # xcode noise 2 | build 3 | *.pbxuser 4 | *.mode2v3 5 | *.mode1v3 6 | *.perspective 7 | *.perspectivev3 8 | 9 | # old skool 10 | .svn 11 | 12 | # osx noise 13 | .DS_Store 14 | profile 15 | 16 | *.swp 17 | *~.nib 18 | /xcode/SCStringsUtility.xcodeproj/xcuserdata 19 | *.xcuserdatad 20 | -------------------------------------------------------------------------------- /SCStringsUtility/SCRootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCRootViewController.h 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/12/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | @interface SCRootViewController : NSViewController 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /SCStringsUtility/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/12/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | -------------------------------------------------------------------------------- /SCStringsUtility/Others/NSData+SCAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+SCAdditions.h 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/26/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSData (SCAdditions) 12 | - (NSRange) rangeOfData_dd:(NSData *)dataToFind; 13 | @end -------------------------------------------------------------------------------- /SCStringsUtility/Views/SCOpenAccessoryView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCOpenAccessoryView.h 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 2/4/13. 6 | // Copyright (c) 2013 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SCOpenAccessoryView : NSView 12 | 13 | - (NSString*)genstringsRoutine; 14 | - (BOOL)shouldAddPositionalParameters; 15 | - (NSString*)stringsFileName; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SCStringsUtility/Readers&Writers/SCCSVWriter.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCCSVWriter.h 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/14/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SCCSVWriter : NSObject 12 | 13 | - (id)initWithHeaders:(NSArray*)heads filePath:(NSString*)path separator:(NSString*)sep; 14 | - (void)appendRow:(NSArray*)components; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /SCStringsUtility/SCStringsUtility-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SCStringsUtility' target in the 'SCStringsUtility' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | 7 | #import 8 | 9 | #define SCLog(id, ...) NSLog((@"%s [Line %d] " id), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 10 | 11 | typedef enum { 12 | SCFileTypeInvalid, 13 | SCFileTypeXcodeProject, 14 | SCFileTypeCSV, 15 | SCFileTypeXML 16 | }SCFileType; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /SCStringsUtility/Readers&Writers/SCStringsWriter.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCStringsWriter.h 3 | // CSVWriter 4 | // 5 | // Created by Stefan Ceriu on 10/11/12. 6 | // 7 | // 8 | 9 | @interface SCStringsWriter : NSObject 10 | 11 | - (id)initWithHeaders:(NSArray*)headers; 12 | - (void)writeTranslations:(NSDictionary*)translations toPath:(NSString*)path failure:(void(^)(NSError *error))failure; 13 | 14 | - (id)initWithTranslationFiles:(NSDictionary *)files; 15 | - (void)writeTranslations:(NSDictionary*)translations failure:(void(^)(NSError *error))failure;; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SCStringsUtility/SCAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCAppDelegate.h 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/12/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import "SCRootViewController.h" 10 | 11 | @interface SCAppDelegate : NSObject 12 | 13 | @property (nonatomic, assign) IBOutlet NSWindow *window; 14 | @property (nonatomic, strong) IBOutlet NSMenu *menu; 15 | @property (nonatomic, strong) IBOutlet SCRootViewController *rootViewController; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /SCStringsUtility/Others/NSString+SCAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SCAdditions.h 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/14/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (SCAdditions) 12 | 13 | - (NSUInteger)numberOfOccurrencesOfString:(NSString *)cursor; 14 | 15 | - (NSString*)stringByTrimmingLeadingWhiteSpaces; 16 | - (NSString*)stringByTrimmingTralingWhiteSpaces; 17 | 18 | + (BOOL)detectFileEncoding:(NSStringEncoding *)encoding path:(NSString *)path error:(NSError **)error; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /SCStringsUtility/Readers&Writers/SCReader.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSVReader.h 3 | // CSVWriter 4 | // 5 | // Created by Stefan Ceriu on 10/11/12. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface SCReader : NSObject 12 | 13 | @property (nonatomic, assign, readonly) NSUInteger currentLine; 14 | @property (nonatomic, assign, readonly) NSStringEncoding encoding; 15 | 16 | - (id)initWithPath:(NSString*)path; 17 | 18 | - (NSString *) readLine; 19 | - (NSString *) readTrimmedLine; 20 | - (void) enumerateLinesUsingBlock:(void(^)(NSString*, BOOL*))block; 21 | 22 | - (NSString*)translationForKey:(NSString*)key; 23 | - (BOOL) getNextComment:(NSString**)comment key:(NSString**)key translation:(NSString**)translation; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /SCStringsUtility/SCAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCAppDelegate.m 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/12/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import "SCAppDelegate.h" 10 | #import "NSString+SCAdditions.h" 11 | 12 | @implementation SCAppDelegate 13 | 14 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 15 | { 16 | [self.window setContentView:self.rootViewController.view]; 17 | } 18 | 19 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender 20 | { 21 | return YES; 22 | } 23 | 24 | - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window 25 | { 26 | return self.rootViewController.undoManager; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /SCStringsUtility/Views/SCExportAccessoryView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCExportAccessoryView.h 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 2/4/13. 6 | // Copyright (c) 2013 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol SCExportAccessoryViewDelegate; 12 | 13 | @interface SCExportAccessoryView : NSView 14 | 15 | @property (nonatomic, assign) id delegate; 16 | 17 | - (BOOL)shouldIncludeComments; 18 | - (BOOL)shouldUseKeyForMissingTranslations; 19 | 20 | - (void)setSelectedExportType:(SCFileType)type; 21 | - (SCFileType)selectedExportType; 22 | 23 | @end 24 | 25 | @protocol SCExportAccessoryViewDelegate 26 | 27 | @optional 28 | - (void)exportAccessoryView:(SCExportAccessoryView*)view didSelectFormatType:(SCFileType)type; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XcodeGroupMember.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XcodeMemberType.h" 15 | 16 | @protocol XcodeGroupMember 17 | 18 | - (NSString*) key; 19 | 20 | - (NSString*) displayName; 21 | 22 | - (NSString*) pathRelativeToProjectRoot; 23 | 24 | /** 25 | * Group members can either be other groups (PBXGroup) or source files (PBXFileReference). 26 | */ 27 | - (XcodeMemberType) groupMemberType; 28 | @end -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCKeyBuilder.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | #import 15 | 16 | #define HASH_VALUE_STORAGE_SIZE 48 17 | 18 | typedef struct { 19 | char value[CC_MD5_DIGEST_LENGTH]; 20 | } HashValueMD5Hash; 21 | 22 | 23 | @interface XCKeyBuilder : NSObject { 24 | unsigned char _value[HASH_VALUE_STORAGE_SIZE]; 25 | } 26 | 27 | + (XCKeyBuilder*) forItemNamed:(NSString*)name; 28 | 29 | - (id) initHashValueMD5HashWithBytes:(const void*)bytes length:(NSUInteger)length; 30 | 31 | - (NSString*) build; 32 | 33 | @end 34 | 35 | -------------------------------------------------------------------------------- /SCStringsUtility/Views/SCOpenAccessoryView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCOpenAccessoryView.m 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 2/4/13. 6 | // Copyright (c) 2013 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import "SCOpenAccessoryView.h" 10 | 11 | @interface SCOpenAccessoryView () 12 | @property (nonatomic, weak) IBOutlet NSTextField *genstringsRoutineTextField; 13 | @property (nonatomic, weak) IBOutlet NSTextField *stringsFileNameTextField; 14 | @property (nonatomic, weak) IBOutlet NSButton *shouldAddPositionalParametersButton; 15 | @end 16 | 17 | @implementation SCOpenAccessoryView 18 | 19 | - (NSString *)genstringsRoutine 20 | { 21 | NSString *routine = self.genstringsRoutineTextField.stringValue; 22 | return routine.length > 0 ? routine : nil; 23 | } 24 | 25 | - (BOOL)shouldAddPositionalParameters 26 | { 27 | return self.shouldAddPositionalParametersButton.state; 28 | } 29 | 30 | - (NSString*)stringsFileName 31 | { 32 | NSString *fileName = self.stringsFileNameTextField.stringValue; 33 | return fileName.length > 0 ? fileName : nil; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCFrameworkDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XCAbstractDefinition.h" 16 | 17 | 18 | @interface XCFrameworkDefinition : XCAbstractDefinition { 19 | NSString* _filePath; 20 | BOOL _copyToDestination; 21 | } 22 | 23 | @property(nonatomic, strong, readonly) NSString* filePath; 24 | @property(nonatomic, readonly) BOOL copyToDestination; 25 | 26 | + (XCFrameworkDefinition*) frameworkDefinitionWithFilePath:(NSString*)filePath 27 | copyToDestination:(BOOL)copyToDestination; 28 | 29 | - (id) initWithFilePath:(NSString*)filePath copyToDestination:(BOOL)copyToDestination; 30 | 31 | - (NSString*) name; 32 | 33 | 34 | @end -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCXibDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XCAbstractDefinition.h" 16 | 17 | 18 | @interface XCXibDefinition : XCAbstractDefinition { 19 | NSString* _name; 20 | NSString* _content; 21 | } 22 | @property(nonatomic, strong, readonly) NSString* name; 23 | @property(nonatomic, strong) NSString* content; 24 | 25 | + (XCXibDefinition*) xibDefinitionWithName:(NSString*)name; 26 | 27 | + (XCXibDefinition*) xibDefinitionWithName:(NSString*)name content:(NSString*)content; 28 | 29 | - (id) initWithName:(NSString*)name; 30 | 31 | - (id) initWithName:(NSString*)name content:(NSString*)content; 32 | 33 | - (NSString*) xibFileName; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XcodeMemberType.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | typedef enum { 15 | PBXNilType, 16 | PBXBuildFileType, 17 | PBXContainerItemProxyType, 18 | PBXCopyFilesBuildPhaseType, 19 | PBXFileReferenceType, 20 | PBXFrameworksBuildPhaseType, 21 | PBXGroupType, 22 | PBXNativeTargetType, 23 | PBXProjectType, 24 | PBXReferenceProxyType, 25 | PBXResourcesBuildPhaseType, 26 | PBXSourcesBuildPhaseType, 27 | PBXTargetDependencyType, 28 | PBXVariantGroupType, 29 | XCBuildConfigurationType, 30 | XCConfigurationListType 31 | } XcodeMemberType; 32 | 33 | @interface NSString (XcodeMemberTypeExtensions) 34 | 35 | + (NSString*) stringFromMemberType:(XcodeMemberType)nodeType; 36 | 37 | - (XcodeMemberType) asMemberType; 38 | 39 | @end 40 | 41 | 42 | -------------------------------------------------------------------------------- /SCStringsUtility/Others/NSData+SCAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+SCAdditions.m 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/26/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import "NSData+SCAdditions.h" 10 | 11 | @implementation NSData (SCAdditions) 12 | 13 | //http://stackoverflow.com/a/3711079 14 | - (NSRange) rangeOfData_dd:(NSData *)dataToFind { 15 | 16 | const void * bytes = [self bytes]; 17 | NSUInteger length = [self length]; 18 | 19 | const void * searchBytes = [dataToFind bytes]; 20 | NSUInteger searchLength = [dataToFind length]; 21 | NSUInteger searchIndex = 0; 22 | 23 | NSRange foundRange = {NSNotFound, searchLength}; 24 | for (NSUInteger index = 0; index < length; index++) { 25 | if (((char *)bytes)[index] == ((char *)searchBytes)[searchIndex]) { 26 | //the current character matches 27 | if (foundRange.location == NSNotFound) { 28 | foundRange.location = index; 29 | } 30 | searchIndex++; 31 | if (searchIndex >= searchLength) { return foundRange; } 32 | } else { 33 | searchIndex = 0; 34 | foundRange.location = NSNotFound; 35 | } 36 | } 37 | return foundRange; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XcodeSourceFileType.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | typedef enum { 15 | FileTypeNil, // Unknown filetype 16 | Framework, // .framework 17 | PropertyList, // .plist 18 | SourceCodeHeader, // .h 19 | SourceCodeObjC, // .m 20 | SourceCodeObjCPlusPlus, // .mm 21 | SourceCodeCPlusPlus, // .cpp 22 | XibFile, // .xib 23 | ImageResourcePNG, // .png 24 | Bundle, // .bundle .octet 25 | Archive, // .a files 26 | HTML, // HTML file 27 | TEXT, // Some text file 28 | XcodeProject // .xcodeproj 29 | } XcodeSourceFileType; 30 | 31 | @interface NSString (XCodeFileType) 32 | 33 | + (NSString*) stringFromSourceFileType:(XcodeSourceFileType)type; 34 | 35 | - (XcodeSourceFileType) asSourceFileType; 36 | 37 | @end -------------------------------------------------------------------------------- /SCStringsUtility/SCStringsUtility-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | icon 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSApplicationCategoryType 26 | public.app-category.developer-tools 27 | LSMinimumSystemVersion 28 | ${MACOSX_DEPLOYMENT_TARGET} 29 | NSHumanReadableCopyright 30 | Copyright © 2012 Stefan Ceriu (stefan.ceriu@yahoo.com). All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /SCStringsUtility/Views/SCExportAccessoryView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCExportAccessoryView.m 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 2/4/13. 6 | // Copyright (c) 2013 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import "SCExportAccessoryView.h" 10 | 11 | @interface SCExportAccessoryView () 12 | @property (nonatomic, weak) IBOutlet NSButton *includeCommentsButton; 13 | @property (nonatomic, weak) IBOutlet NSButton *useKeyForMissingTranslationsButton; 14 | @property (nonatomic, weak) IBOutlet NSComboBox *formatComboBox; 15 | @end 16 | 17 | @implementation SCExportAccessoryView 18 | 19 | - (BOOL)shouldIncludeComments 20 | { 21 | return [self.includeCommentsButton state]; 22 | } 23 | 24 | - (BOOL)shouldUseKeyForMissingTranslations 25 | { 26 | return [self.useKeyForMissingTranslationsButton state]; 27 | } 28 | 29 | - (void)setSelectedExportType:(SCFileType)type 30 | { 31 | [self.formatComboBox selectItemAtIndex:type-1]; 32 | } 33 | 34 | - (SCFileType)selectedExportType 35 | { 36 | return (SCFileType)self.formatComboBox.indexOfSelectedItem + 1; 37 | } 38 | 39 | - (void)comboBoxSelectionDidChange:(NSNotification *)notification 40 | { 41 | if([self.delegate respondsToSelector:@selector(exportAccessoryView:didSelectFormatType:)]) 42 | [self.delegate exportAccessoryView:self didSelectFormatType:[self selectedExportType]]; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 11G56 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | XcodeEditor 11 | CFBundleIdentifier 12 | expanz.XcodeEditor 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | XcodeEditor 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | DTCompiler 26 | com.apple.compilers.llvm.clang.1_0 27 | DTPlatformBuild 28 | 4H127 29 | DTPlatformVersion 30 | GM 31 | DTSDKBuild 32 | 11G56 33 | DTSDKName 34 | 35 | DTXcode 36 | 0460 37 | DTXcodeBuild 38 | 4H127 39 | NSHumanReadableCopyright 40 | Copyright © 2012 Jasper Blues. All rights reserved. 41 | 42 | 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SCStringsUtility 2 | 3 | SCStringsUtility is an OS X Application built for easing the way we deal with localizable strings. It started off as a simple to/from CSV utility but ended up being a little bit more than that. 4 | 5 | ![SCStringsUtility](https://drive.google.com/u/0/uc?id=10mzG3ZZqpuCRacH1jmb9_H9iNN8QLS3Y&export=download) 6 | 7 | ## Features 8 | * Import from Xcode project using genstrings and its .string files (options for a different genstrings routine and positional parameters) 9 | 10 | * Export to Localizable.strings/CSV/XML (with comments / key for missing translations) 11 | 12 | * Import from CSV/XML 13 | 14 | * Save back on top of the original files 15 | 16 | * Search (filter all or just the keys) 17 | 18 | * Editing & undo/redo support 19 | 20 | * Console for displaying genstrings warnings 21 | 22 | ## How to use it 23 | - download the zipped app from the "releases" tab 24 | 25 | - fire up the application and point it to an .xcodeproj 26 | 27 | ##Known issues 28 | - The application doesn't work when opening Xcode projects that have the .strings files inside virual groups. XcodeEditor doesn't provide correct paths for that case 29 | 30 | ##ToDo 31 | - Individual cell selection 32 | 33 | - Better error handling 34 | 35 | ## License 36 | SCStringsUtility is released under the GNU GENERAL PUBLIC LICENSE (see the LICENSE file) 37 | 38 | ## Contact 39 | 40 | Feel free to contact me at stefan.ceriu@gmail.com or https://twitter.com/stefanceriu 41 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCSourceFileDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XCAbstractDefinition.h" 16 | #import "XcodeSourceFileType.h" 17 | 18 | @interface XCSourceFileDefinition : XCAbstractDefinition { 19 | 20 | NSString* _sourceFileName; 21 | XcodeSourceFileType _type; 22 | NSData* _data; 23 | 24 | } 25 | 26 | @property(nonatomic, strong, readonly) NSString* sourceFileName; 27 | @property(nonatomic, strong, readonly) NSData* data; 28 | @property(nonatomic, readonly) XcodeSourceFileType type; 29 | 30 | + (XCSourceFileDefinition*) sourceDefinitionWithName:(NSString*)name text:(NSString*)text 31 | type:(XcodeSourceFileType)type; 32 | 33 | + (XCSourceFileDefinition*) sourceDefinitionWithName:(NSString*)name data:(NSData*)data 34 | type:(XcodeSourceFileType)type; 35 | 36 | - (id) initWithName:(NSString*)name text:(NSString*)text type:(XcodeSourceFileType)type; 37 | 38 | - (id) initWithName:(NSString*)name data:(NSData*)data type:(XcodeSourceFileType)type; 39 | 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /SCStringsUtility/Others/OrderedDictionary.h: -------------------------------------------------------------------------------- 1 | // 2 | // OrderedDictionary.h 3 | // OrderedDictionary 4 | // 5 | // Created by Matt Gallagher on 19/12/08. 6 | // Copyright 2008 Matt Gallagher. All rights reserved. 7 | // 8 | // This software is provided 'as-is', without any express or implied 9 | // warranty. In no event will the authors be held liable for any damages 10 | // arising from the use of this software. Permission is granted to anyone to 11 | // use this software for any purpose, including commercial applications, and to 12 | // alter it and redistribute it freely, subject to the following restrictions: 13 | // 14 | // 1. The origin of this software must not be misrepresented; you must not 15 | // claim that you wrote the original software. If you use this software 16 | // in a product, an acknowledgment in the product documentation would be 17 | // appreciated but is not required. 18 | // 2. Altered source versions must be plainly marked as such, and must not be 19 | // misrepresented as being the original software. 20 | // 3. This notice may not be removed or altered from any source 21 | // distribution. 22 | // 23 | 24 | #import 25 | 26 | @interface OrderedDictionary : NSMutableDictionary 27 | { 28 | NSMutableDictionary *dictionary; 29 | NSMutableArray *array; 30 | } 31 | 32 | - (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex; 33 | - (id)keyAtIndex:(NSUInteger)anIndex; 34 | - (NSEnumerator *)reverseKeyEnumerator; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCFileOperationQueue.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | 15 | 16 | @interface XCFileOperationQueue : NSObject { 17 | 18 | @private 19 | NSString* _baseDirectory; 20 | NSMutableDictionary* _filesToWrite; 21 | NSMutableDictionary* _frameworksToCopy; 22 | NSMutableArray* _filesToDelete; 23 | NSMutableArray* _directoriesToCreate; 24 | } 25 | 26 | 27 | - (id) initWithBaseDirectory:(NSString*)baseDirectory; 28 | 29 | - (BOOL) fileWithName:(NSString*)name existsInProjectDirectory:(NSString*)directory; 30 | 31 | - (void) queueTextFile:(NSString*)fileName inDirectory:(NSString*)directory withContents:(NSString*)contents; 32 | 33 | - (void) queueDataFile:(NSString*)fileName inDirectory:(NSString*)directory withContents:(NSData*)contents; 34 | 35 | - (void) queueFrameworkWithFilePath:(NSString*)filePath inDirectory:(NSString*)directory; 36 | 37 | - (void) queueDeletion:(NSString*)filePath; 38 | 39 | - (void) queueDirectory:(NSString*)withName inDirectory:(NSString*)parentDirectory; 40 | 41 | - (void) commitFileOperations; 42 | 43 | @end 44 | 45 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCAbstractDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | 15 | typedef enum 16 | { 17 | 18 | /** 19 | * Creates the reference in the project and writes the contents to disk. If a file already exists at the specified 20 | * location, its contents will be updated. 21 | */ 22 | FileOperationStyleOverwrite, 23 | 24 | /** 25 | * Creates the reference in the project. If a file already exists at the specified location, the contents will not 26 | * be updated. 27 | */ 28 | FileOperationStyleAcceptExisting, 29 | 30 | /** 31 | * Creates the reference in the project, but does not write to disk. The filesystem is expected to be updated 32 | * through some other means. 33 | */ 34 | FileOperationStyleReferenceOnly 35 | } XcodeFileOperationStyle; 36 | 37 | /** 38 | * Holds properties to all types of resource that can be added to an Xcode project. 39 | */ 40 | @interface XCAbstractDefinition : NSObject 41 | { 42 | XcodeFileOperationStyle _fileOperationStyle; 43 | } 44 | 45 | @property(nonatomic) XcodeFileOperationStyle fileOperationStyle; 46 | 47 | 48 | @end -------------------------------------------------------------------------------- /SCStringsUtility/Readers&Writers/SCCSVWriter.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCCSVWriter.m 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/14/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import "SCCSVWriter.h" 10 | 11 | @interface SCCSVWriter () 12 | @property (nonatomic, retain) NSFileHandle *fileHandle; 13 | @property (nonatomic, retain) NSString *separator; 14 | @property (nonatomic, retain) NSArray *headers; 15 | @end 16 | 17 | @implementation SCCSVWriter 18 | @synthesize fileHandle; 19 | @synthesize separator; 20 | @synthesize headers; 21 | 22 | - (id)initWithHeaders:(NSArray*)heads filePath:(NSString*)path separator:(NSString*)sep 23 | { 24 | if(self = [super init]) 25 | { 26 | [[NSFileManager defaultManager] createFileAtPath:path contents:[NSData data] attributes:nil]; 27 | self.fileHandle = [NSFileHandle fileHandleForWritingAtPath:path]; 28 | if(fileHandle == nil) {SCLog(@"Unable to open file for writing at path %@", path); return nil;} 29 | 30 | self.separator = sep; 31 | self.headers = heads; 32 | 33 | [self appendRow:headers]; 34 | } 35 | 36 | return self; 37 | } 38 | 39 | - (void)appendRow:(NSArray*)components 40 | { 41 | if(components.count != self.headers.count) 42 | { 43 | SCLog(@"Number of components doesn't match the number of headers"); 44 | return; 45 | } 46 | 47 | [self.fileHandle writeData:[[components componentsJoinedByString:self.separator] dataUsingEncoding:NSUTF8StringEncoding]]; 48 | [self.fileHandle writeData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 49 | } 50 | 51 | - (void)dealloc 52 | { 53 | [self.fileHandle closeFile]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCClassDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | #import "XCAbstractDefinition.h" 15 | 16 | typedef enum { 17 | ObjectiveC, 18 | ObjectiveCPlusPlus, 19 | CPlusPlus, 20 | } ClassDefinitionLanguage; 21 | 22 | @interface XCClassDefinition : XCAbstractDefinition { 23 | 24 | NSString* _className; 25 | NSString* _header; 26 | NSString* _source; 27 | 28 | @private 29 | ClassDefinitionLanguage _language; 30 | } 31 | 32 | @property(strong, nonatomic, readonly) NSString* className; 33 | @property(nonatomic, strong) NSString* header; 34 | @property(nonatomic, strong) NSString* source; 35 | 36 | + (XCClassDefinition*) classDefinitionWithName:(NSString*)fileName; 37 | 38 | + (XCClassDefinition*) classDefinitionWithName:(NSString*)className language:(ClassDefinitionLanguage)language; 39 | 40 | /** 41 | * Initializes a new objective-c class definition. 42 | */ 43 | - (id) initWithName:(NSString*)fileName; 44 | 45 | /** 46 | * Initializes a new class definition with the specified language. 47 | */ 48 | - (id) initWithName:(NSString*)className language:(ClassDefinitionLanguage)language; 49 | 50 | - (BOOL) isObjectiveC; 51 | 52 | - (BOOL) isObjectiveCPlusPlus; 53 | 54 | - (BOOL) isCPlusPlus; 55 | 56 | - (NSString*) headerFileName; 57 | 58 | - (NSString*) sourceFileName; 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /SCStringsUtility/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf510 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | \vieww9600\viewh8400\viewkind0 5 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 6 | 7 | \f0\fs24 \cf0 \ 8 | Thanks to:\ 9 | \ 10 | Jasper Blues for the xcode-editor project\ 11 | {\field{\*\fldinst{HYPERLINK "https://github.com/jasperblues/xcode-editor"}}{\fldrslt https://github.com/jasperblues/xcode-editor}}\ 12 | \ 13 | \pard\tx560\pardeftab560\pardirnatural 14 | \cf0 \CocoaLigature0 Matt Gallagher for his OrderedDictionary\ 15 | {\field{\*\fldinst{HYPERLINK "http://www.cocoawithlove.com/2008/12/ordereddictionary-subclassing-cocoa.html"}}{\fldrslt http://www.cocoawithlove.com/2008/12/ordereddictionary-subclassing-cocoa.html}}\ 16 | \pard\pardeftab720\sl240 17 | {\field{\*\fldinst{HYPERLINK "http://stackoverflow.com/users/115730/dave-delong"}}{\fldrslt \cf0 \CocoaLigature1 \ 18 | }}\pard\pardeftab720\sl340 19 | \cf0 \CocoaLigature1 Dave DeLong for his answers on StackOverflow\ 20 | {\field{\*\fldinst{HYPERLINK "http://stackoverflow.com/questions/2166809/number-of-occurrences-of-a-substring-in-an-nsstring/2166853#2166853"}}{\fldrslt http://stackoverflow.com/questions/2166809/number-of-occurrences-of-a-substring-in-an-nsstring/2166853#2166853}}\ 21 | \ 22 | {\field{\*\fldinst{HYPERLINK "http://stackoverflow.com/questions/3707427/how-to-read-data-from-nsfilehandle-line-by-line/3711079#3711079"}}{\fldrslt http://stackoverflow.com/questions/3707427/how-to-read-data-from-nsfilehandle-line-by-line/3711079#3711079}}\ 23 | \ 24 | Icons taken from:\ 25 | {\field{\*\fldinst{HYPERLINK "http://www.iconarchive.com/show/isimple-system-icons-by-fasticon.html"}}{\fldrslt http://www.iconarchive.com/show/isimple-system-icons-by-fasticon.html}}\ 26 | \ 27 | {\field{\*\fldinst{HYPERLINK "http://www.iconarchive.com/show/pretty-office-7-icons-by-custom-icon-design.html"}}{\fldrslt http://www.iconarchive.com/show/pretty-office-7-icons-by-custom-icon-design.html}}} -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCTarget.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | 15 | @class XCProject; 16 | @class XCSourceFile; 17 | @class XCBuildConfiguration; 18 | 19 | /** 20 | * Represents a target in an xcode project. 21 | */ 22 | @interface XCTarget : NSObject 23 | { 24 | 25 | XCProject* _project; 26 | NSString* _key; 27 | NSString* _name; 28 | NSString* _productName; 29 | NSString* _productReference; 30 | NSString* _defaultConfigurationName; 31 | 32 | @private 33 | NSMutableArray* _members; 34 | NSMutableArray* _resources; 35 | NSMutableDictionary* _configurations; 36 | } 37 | 38 | @property(nonatomic, strong, readonly) NSString* key; 39 | @property(nonatomic, strong, readonly) NSString* name; 40 | @property(nonatomic, strong, readonly) NSString* productName; 41 | @property(nonatomic, strong, readonly) NSString* productReference; 42 | 43 | + (XCTarget*)targetWithProject:(XCProject*)project key:(NSString*)key name:(NSString*)name productName:(NSString*)productName 44 | productReference:(NSString*)productReference; 45 | 46 | - (id)initWithProject:(XCProject*)project key:(NSString*)key name:(NSString*)name productName:(NSString*)productName 47 | productReference:(NSString*)productReference; 48 | 49 | - (NSArray*)resources; 50 | 51 | - (NSArray*)members; 52 | 53 | - (NSDictionary*)configurations; 54 | 55 | - (XCBuildConfiguration*)configurationWithName:(NSString*)name; 56 | 57 | - (XCBuildConfiguration*)defaultConfiguration; 58 | 59 | - (void)addMember:(XCSourceFile*)member; 60 | 61 | - (void)removeMemberWithKey:(NSString*)key; 62 | 63 | - (void)removeMembersWithKeys:(NSArray*)keys; 64 | 65 | - (void)addDependency:(NSString*)key; 66 | 67 | @end 68 | 69 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCSourceFile.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XcodeGroupMember.h" 16 | #import "XcodeSourceFileType.h" 17 | 18 | @class XCProject; 19 | 20 | /** 21 | * Represents a file resource in an xcode project. 22 | */ 23 | @interface XCSourceFile : NSObject { 24 | 25 | @private 26 | XCProject* _project; 27 | 28 | NSNumber* _isBuildFile; 29 | NSString* _buildFileKey; 30 | NSString* _name; 31 | NSString* _sourceTree; 32 | NSString* _key; 33 | NSString* _path; 34 | XcodeSourceFileType _type; 35 | } 36 | 37 | @property(nonatomic, readonly) XcodeSourceFileType type; 38 | @property(nonatomic, strong, readonly) NSString* key; 39 | @property(nonatomic, strong, readonly) NSString* name; 40 | @property(nonatomic, strong, readonly) NSString* sourceTree; 41 | @property(nonatomic, strong, readonly) NSString* path; 42 | 43 | + (XCSourceFile*) sourceFileWithProject:(XCProject*)project 44 | key:(NSString*)key 45 | type:(XcodeSourceFileType)type 46 | name:(NSString*)name 47 | sourceTree:(NSString*)tree 48 | path:(NSString*)path; 49 | 50 | - (id) initWithProject:(XCProject*)project 51 | key:(NSString*)key 52 | type:(XcodeSourceFileType)type 53 | name:(NSString*)name 54 | sourceTree:(NSString*)tree 55 | path:(NSString*)path; 56 | 57 | /** 58 | * If yes, indicates the file is able to be included for compilation in an `XCTarget`. 59 | */ 60 | - (BOOL) isBuildFile; 61 | 62 | - (BOOL) canBecomeBuildFile; 63 | 64 | - (XcodeMemberType) buildPhase; 65 | 66 | - (NSString*) buildFileKey; 67 | 68 | /** 69 | * Adds this file to the project as an `xcode_BuildFile`, ready to be included in targets. 70 | */ 71 | - (void) becomeBuildFile; 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /SCStringsUtility/SCStringsController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCStringsDataSource.h 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 2/2/13. 6 | // Copyright (c) 2013 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | @protocol SCStringsControllerDelegate; 10 | 11 | @interface SCStringsController : NSObject 12 | 13 | @property (nonatomic, assign, readonly) SCFileType sourceType; 14 | 15 | @property (nonatomic, assign) id delegate; 16 | 17 | @property (nonatomic, strong) NSTableView *tableView; 18 | @property (nonatomic, strong) NSOutlineView *outlineView; 19 | 20 | @property (nonatomic, strong) NSUndoManager *undoManager; 21 | 22 | - (void)reset; 23 | 24 | - (void)save:(void (^)(void))success failure:(void (^)(NSError *error))failure; 25 | 26 | - (void)importProjectAtPath:(NSString*)path 27 | positionalParameters:(BOOL)includePositionalParameters 28 | genstringsRoutine:(NSString*)genstringsRoutine 29 | stringsFileName:(NSString*)stringsFileName 30 | success:(void (^)(void))success 31 | failure:(void(^)(NSError *error))failure; 32 | 33 | - (void)importCSVFileAtPath:(NSString*)path 34 | success:(void (^)(void))success 35 | failure:(void(^)(NSError *error))failure; 36 | 37 | - (void)importXMLFileAtPath:(NSString*)path 38 | success:(void (^)(void))success 39 | failure:(void(^)(NSError *error))failure; 40 | 41 | - (void)generateCSVAtPath:(NSString *)path 42 | includeComments:(BOOL)includeComments 43 | useKeyForEmptyTranslations:(BOOL)useKeyForEmptyTranslations 44 | success:(void (^)(void))success 45 | failure:(void (^)(NSError *error))failure; 46 | 47 | - (void)generateStringFilesAtPath:(NSString *)path 48 | success:(void (^)(void))success 49 | failure:(void (^)(NSError *error))failure; 50 | 51 | - (void)generateXMLFileAtPath:(NSString*)path 52 | includeComments:(BOOL)includeComments 53 | useKeyForEmptyTranslations:(BOOL)useKeyForEmptyTranslations 54 | success:(void (^)(void))success 55 | failure:(void (^)(NSError *error))failure; 56 | 57 | - (void)filterEntriesWithSearchString:(NSString*)searchString onlyKeys:(BOOL)searchOnlyKeys; 58 | 59 | @end 60 | 61 | @protocol SCStringsControllerDelegate 62 | 63 | @optional 64 | - (void)stringsController:(SCStringsController*)stringsController didGetGenstringsOutput:(NSString*)output; 65 | 66 | @end -------------------------------------------------------------------------------- /SCStringsUtility/Others/NSString+SCAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SCAdditions.m 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/14/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import "NSString+SCAdditions.h" 10 | 11 | @implementation NSString (SCAdditions) 12 | 13 | //http://stackoverflow.com/a/2166853 14 | - (NSUInteger)numberOfOccurrencesOfString:(NSString *)needle 15 | { 16 | const char * rawNeedle = [needle UTF8String]; 17 | NSUInteger needleLength = strlen(rawNeedle); 18 | 19 | const char * rawHaystack = [self UTF8String]; 20 | NSUInteger haystackLength = strlen(rawHaystack); 21 | 22 | NSUInteger needleCount = 0; 23 | NSUInteger needleIndex = 0; 24 | for (NSUInteger index = 0; index < haystackLength; ++index) { 25 | const char thisCharacter = rawHaystack[index]; 26 | if (thisCharacter != rawNeedle[needleIndex]) { 27 | needleIndex = 0; 28 | } 29 | 30 | if (thisCharacter == rawNeedle[needleIndex]) { 31 | needleIndex++; 32 | if (needleIndex >= needleLength) { 33 | needleCount++; 34 | needleIndex = 0; 35 | } 36 | } 37 | } 38 | 39 | return needleCount; 40 | } 41 | 42 | - (NSString*)stringByTrimmingLeadingWhiteSpaces 43 | { 44 | NSRange range = [self rangeOfString:@"^\\s*" options:NSRegularExpressionSearch]; 45 | return [self stringByReplacingCharactersInRange:range withString:@""]; 46 | } 47 | 48 | - (NSString*)stringByTrimmingTralingWhiteSpaces 49 | { 50 | NSRange range = [self rangeOfString:@"\\s*$" options:NSRegularExpressionSearch]; 51 | return [self stringByReplacingCharactersInRange:range withString:@""]; 52 | } 53 | 54 | + (BOOL)detectFileEncoding:(NSStringEncoding *)encoding path:(NSString *)path error:(NSError **)error 55 | { 56 | NSString *string = [NSString stringWithContentsOfFile:path usedEncoding:encoding error:error]; 57 | if (string != nil) { 58 | if (*encoding == NSUTF16StringEncoding) { 59 | // fix for little endian 60 | NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path]; 61 | [fileHandle seekToFileOffset:0]; 62 | NSData *data = [fileHandle readDataOfLength:2]; 63 | const char *head = [data bytes]; 64 | if (*head == '\xFF' && *(head + 1) == '\xFE') { 65 | *encoding = NSUTF16LittleEndianStringEncoding; 66 | } else if (*head == '\xFE' && *(head + 1) == '\xFF') { 67 | *encoding = NSUTF16BigEndianStringEncoding; 68 | } 69 | 70 | [fileHandle closeFile]; 71 | } 72 | 73 | return YES; 74 | } else { 75 | SCLog(@"Possible problem in detecting encoding %@", *error); 76 | 77 | return NO; 78 | } 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /SCStringsUtility/Others/OrderedDictionary.m: -------------------------------------------------------------------------------- 1 | // 2 | // OrderedDictionary.m 3 | // OrderedDictionary 4 | // 5 | // Created by Matt Gallagher on 19/12/08. 6 | // Copyright 2008 Matt Gallagher. All rights reserved. 7 | // 8 | // This software is provided 'as-is', without any express or implied 9 | // warranty. In no event will the authors be held liable for any damages 10 | // arising from the use of this software. Permission is granted to anyone to 11 | // use this software for any purpose, including commercial applications, and to 12 | // alter it and redistribute it freely, subject to the following restrictions: 13 | // 14 | // 1. The origin of this software must not be misrepresented; you must not 15 | // claim that you wrote the original software. If you use this software 16 | // in a product, an acknowledgment in the product documentation would be 17 | // appreciated but is not required. 18 | // 2. Altered source versions must be plainly marked as such, and must not be 19 | // misrepresented as being the original software. 20 | // 3. This notice may not be removed or altered from any source 21 | // distribution. 22 | // 23 | 24 | #import "OrderedDictionary.h" 25 | 26 | 27 | @implementation OrderedDictionary (SCAdditions) 28 | 29 | -(id) mutableCopyWithZone: (NSZone *) zone 30 | { 31 | OrderedDictionary *newDictionary = [[OrderedDictionary allocWithZone: zone] init]; 32 | 33 | for(id key in self.keyEnumerator) { 34 | [newDictionary setObject:[self objectForKey:key] forKey:key]; 35 | } 36 | 37 | return newDictionary; 38 | } 39 | 40 | @end 41 | 42 | NSString *DescriptionForObject(NSObject *object, id locale, NSUInteger indent) 43 | { 44 | NSString *objectString; 45 | if ([object isKindOfClass:[NSString class]]) 46 | { 47 | objectString = (NSString *)[[object retain] autorelease]; 48 | } 49 | else if ([object respondsToSelector:@selector(descriptionWithLocale:indent:)]) 50 | { 51 | objectString = [(NSDictionary *)object descriptionWithLocale:locale indent:indent]; 52 | } 53 | else if ([object respondsToSelector:@selector(descriptionWithLocale:)]) 54 | { 55 | objectString = [(NSSet *)object descriptionWithLocale:locale]; 56 | } 57 | else 58 | { 59 | objectString = [object description]; 60 | } 61 | return objectString; 62 | } 63 | 64 | @implementation OrderedDictionary 65 | 66 | - (id)init 67 | { 68 | return [self initWithCapacity:0]; 69 | } 70 | 71 | - (id)initWithCapacity:(NSUInteger)capacity 72 | { 73 | self = [super init]; 74 | if (self != nil) 75 | { 76 | dictionary = [[NSMutableDictionary alloc] initWithCapacity:capacity]; 77 | array = [[NSMutableArray alloc] initWithCapacity:capacity]; 78 | } 79 | return self; 80 | } 81 | 82 | - (void)dealloc 83 | { 84 | [dictionary release]; 85 | [array release]; 86 | [super dealloc]; 87 | } 88 | 89 | - (id)copy 90 | { 91 | return [self mutableCopy]; 92 | } 93 | 94 | - (void)setObject:(id)anObject forKey:(id)aKey 95 | { 96 | if (![dictionary objectForKey:aKey]) 97 | { 98 | [array addObject:aKey]; 99 | array = [[array sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] mutableCopy]; 100 | } 101 | [dictionary setObject:anObject forKey:aKey]; 102 | } 103 | 104 | - (void)removeObjectForKey:(id)aKey 105 | { 106 | [dictionary removeObjectForKey:aKey]; 107 | [array removeObject:aKey]; 108 | } 109 | 110 | - (NSUInteger)count 111 | { 112 | return [dictionary count]; 113 | } 114 | 115 | - (id)objectForKey:(id)aKey 116 | { 117 | return [dictionary objectForKey:aKey]; 118 | } 119 | 120 | - (NSEnumerator *)keyEnumerator 121 | { 122 | return [array objectEnumerator]; 123 | } 124 | 125 | - (NSEnumerator *)reverseKeyEnumerator 126 | { 127 | return [array reverseObjectEnumerator]; 128 | } 129 | 130 | - (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex 131 | { 132 | if ([dictionary objectForKey:aKey]) 133 | { 134 | [self removeObjectForKey:aKey]; 135 | } 136 | [array insertObject:aKey atIndex:anIndex]; 137 | [dictionary setObject:anObject forKey:aKey]; 138 | } 139 | 140 | - (id)keyAtIndex:(NSUInteger)anIndex 141 | { 142 | return [array objectAtIndex:anIndex]; 143 | } 144 | 145 | - (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level 146 | { 147 | NSMutableString *indentString = [NSMutableString string]; 148 | NSUInteger i, count = level; 149 | for (i = 0; i < count; i++) 150 | { 151 | [indentString appendFormat:@" "]; 152 | } 153 | 154 | NSMutableString *description = [NSMutableString string]; 155 | [description appendFormat:@"%@{\n", indentString]; 156 | for (NSObject *key in self) 157 | { 158 | [description appendFormat:@"%@ %@ = %@;\n", 159 | indentString, 160 | DescriptionForObject(key, locale, level), 161 | DescriptionForObject([self objectForKey:key], locale, level)]; 162 | } 163 | [description appendFormat:@"%@}\n", indentString]; 164 | return description; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /SCStringsUtility/Readers&Writers/SCStringsWriter.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCStringsWriter.m 3 | // CSVWriter 4 | // 5 | // Created by Stefan Ceriu on 10/11/12. 6 | // 7 | // 8 | 9 | #import "SCStringsWriter.h" 10 | #import "SCReader.h" 11 | #import "NSString+SCAdditions.h" 12 | 13 | static NSString *kKeyFileHandle = @"kKeyFileHandle"; 14 | static NSString *kKeyEncoding = @"kKeyEncoding"; 15 | 16 | @interface SCStringsWriter () 17 | @property (nonatomic, strong) NSMutableDictionary *fileHandlers; 18 | @property (nonatomic, strong) NSArray *headers; 19 | @end 20 | 21 | @implementation SCStringsWriter 22 | 23 | - (id)initWithHeaders:(NSArray *)heads 24 | { 25 | if(self = [super init]) 26 | { 27 | self.headers = heads; 28 | } 29 | 30 | return self; 31 | } 32 | 33 | - (id)initWithTranslationFiles:(NSDictionary *)files 34 | { 35 | if(self = [super init]) 36 | { 37 | self.fileHandlers = [NSMutableDictionary dictionary]; 38 | 39 | NSMutableArray *heads = [NSMutableArray array]; 40 | for(NSString *key in files) 41 | { 42 | for(NSDictionary *x in [files objectForKey:key]) 43 | { 44 | NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:[x objectForKey:@"Path"]]; 45 | if(fileHandle == nil) {SCLog(@"Unable to open file for writing at path %@", [x objectForKey:@"Path"]); return nil;} 46 | 47 | NSStringEncoding encoding; 48 | if (![NSString detectFileEncoding:&encoding path:[x objectForKey:@"Path"] error:nil]) { 49 | // fallback 50 | encoding = NSUTF8StringEncoding; 51 | } 52 | 53 | [self.fileHandlers setObject:@{kKeyFileHandle : fileHandle, kKeyEncoding : @(encoding)} forKey:[x objectForKey:@"Language"]]; 54 | [heads addObject:[x objectForKey:@"Language"]]; 55 | } 56 | } 57 | self.headers = heads; 58 | } 59 | 60 | return self; 61 | } 62 | 63 | - (void)writeTranslations:(NSDictionary*)translations failure:(void(^)(NSError *error))failure 64 | { 65 | for(NSString *key in translations) 66 | { 67 | NSDictionary *translationDict = [translations objectForKey:key]; 68 | 69 | if([[translationDict objectForKey:@"Comment"] length]) 70 | { 71 | NSString *comment = [NSString stringWithFormat:@"%@\n", [[translationDict objectForKey:@"Comment"] stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"]]; 72 | for(NSString *fileHandleKey in self.fileHandlers) 73 | { 74 | NSFileHandle *fileHandle = [[self.fileHandlers objectForKey:fileHandleKey] objectForKey:kKeyFileHandle]; 75 | NSStringEncoding encoding = [[[self.fileHandlers objectForKey:fileHandleKey] objectForKey:kKeyEncoding] unsignedIntValue]; 76 | 77 | [fileHandle writeData:[comment dataUsingEncoding:encoding]]; 78 | } 79 | } 80 | 81 | for(NSString *header in self.headers) 82 | { 83 | NSString *translation = [translationDict objectForKey:header]; 84 | if(!translation.length) translation = key; 85 | 86 | NSFileHandle *fileHandle = [[self.fileHandlers objectForKey:header] objectForKey:kKeyFileHandle]; 87 | NSStringEncoding encoding = [[[self.fileHandlers objectForKey:header] objectForKey:kKeyEncoding] unsignedIntValue]; 88 | 89 | NSString *line = [NSString stringWithFormat:@"\"%@\" = \"%@\";\n\n", key, translation]; 90 | [fileHandle writeData:[line dataUsingEncoding:encoding]]; 91 | } 92 | } 93 | 94 | for(NSString *fileHandleKey in self.fileHandlers) 95 | { 96 | NSFileHandle *fileHandle = [[self.fileHandlers objectForKey:fileHandleKey] objectForKey:kKeyFileHandle]; 97 | [fileHandle closeFile]; 98 | } 99 | } 100 | 101 | - (void)writeTranslations:(NSDictionary*)translations toPath:(NSString*)path failure:(void(^)(NSError *error))failure 102 | { 103 | self.fileHandlers = [NSMutableDictionary dictionary]; 104 | 105 | NSError *error; 106 | for(NSString *languageIdentifier in self.headers) 107 | { 108 | NSString *lprojPath = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.lproj", languageIdentifier]]; 109 | [[NSFileManager defaultManager] createDirectoryAtPath:lprojPath withIntermediateDirectories:YES attributes:nil error:&error]; 110 | if(error) { 111 | if(failure) failure(error); 112 | return; 113 | } 114 | 115 | NSString *stringsPath = [lprojPath stringByAppendingPathComponent:@"Localizable.strings"]; 116 | [[NSFileManager defaultManager] createFileAtPath:stringsPath contents:[NSData data] attributes:nil]; 117 | 118 | NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:stringsPath]; 119 | if(fileHandle == nil) SCLog(@"Unable to open file for writing at path %@", stringsPath); 120 | 121 | [self.fileHandlers setObject:@{kKeyFileHandle : fileHandle, kKeyEncoding : @(NSUTF8StringEncoding)} forKey:languageIdentifier]; 122 | } 123 | 124 | [self writeTranslations:translations failure:failure]; 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /SCStringsUtility/Views/SCOpenAccessoryView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCProject.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | 15 | #import 16 | #import "XcodeMemberType.h" 17 | #import "XcodeSourceFileType.h" 18 | 19 | @class XCClassDefinition; 20 | @class XCGroup; 21 | @class XCFileOperationQueue; 22 | @class XCSourceFile; 23 | @class XCTarget; 24 | @class XCSubProjectDefinition; 25 | @class XCBuildConfiguration; 26 | 27 | 28 | __attribute__((__visibility__("default"))) @interface XCProject : NSObject 29 | { 30 | 31 | @private 32 | XCFileOperationQueue* _fileOperationQueue; 33 | 34 | NSString* _filePath; 35 | NSMutableDictionary* _dataStore; 36 | NSMutableArray* _targets; 37 | 38 | NSMutableDictionary* _groups; 39 | NSMutableDictionary* _configurations; 40 | 41 | NSString* _defaultConfigurationName; 42 | NSString* _rootObjectKey; 43 | } 44 | 45 | @property(nonatomic, strong, readonly) XCFileOperationQueue* fileOperationQueue; 46 | 47 | /* ============================================================ Initializers ============================================================ */ 48 | 49 | + (XCProject*)projectWithFilePath:(NSString*)filePath; 50 | 51 | /** 52 | * Creates a new project editor instance with the specified Project.xcodeproj file. 53 | */ 54 | - (id)initWithFilePath:(NSString*)filePath; 55 | 56 | /* ====================================================================================================================================== */ 57 | 58 | #pragma mark Files 59 | /** 60 | * Returns all file resources in the project, as an array of `XCSourceFile` objects. 61 | */ 62 | - (NSArray*)files; 63 | 64 | /** 65 | * Returns the project file with the specified key, or nil. 66 | */ 67 | - (XCSourceFile*)fileWithKey:(NSString*)key; 68 | 69 | /** 70 | * Returns the project file with the specified name, or nil. If more than one project file matches the specified name, 71 | * which one is returned is undefined. 72 | */ 73 | - (XCSourceFile*)fileWithName:(NSString*)name; 74 | 75 | /** 76 | * Returns all header files in the project, as an array of `XCSourceFile` objects. 77 | */ 78 | - (NSArray*)headerFiles; 79 | 80 | /** 81 | * Returns all implementation obj-c implementation files in the project, as an array of `XCSourceFile` objects. 82 | */ 83 | - (NSArray*)objectiveCFiles; 84 | 85 | /** 86 | * Returns all implementation obj-c++ implementation files in the project, as an array of `XCSourceFile` objects. 87 | */ 88 | - (NSArray*)objectiveCPlusPlusFiles; 89 | 90 | /** 91 | * Returns all the xib files in the project, as an array of `XCSourceFile` objects. 92 | */ 93 | - (NSArray*)xibFiles; 94 | 95 | - (NSArray*)imagePNGFiles; 96 | 97 | - (NSString*)filePath; 98 | 99 | 100 | /* ====================================================================================================================================== */ 101 | #pragma mark Groups 102 | /** 103 | * Lists the groups in an xcode project, returning an array of `XCGroup` objects. 104 | */ 105 | - (NSArray*)groups; 106 | 107 | /** 108 | * Returns the root (top-level) group. 109 | */ 110 | - (XCGroup*)rootGroup; 111 | 112 | /** 113 | * Returns the root (top-level) groups, if there are multiple. An array of rootGroup if there is only one. 114 | */ 115 | - (NSArray*)rootGroups; 116 | 117 | /** 118 | * Returns the group with the given key, or nil. 119 | */ 120 | - (XCGroup*)groupWithKey:(NSString*)key; 121 | 122 | /** 123 | * Returns the group with the specified display name path - the directory relative to the root group. Eg Source/Main 124 | */ 125 | - (XCGroup*)groupWithPathFromRoot:(NSString*)path; 126 | 127 | /** 128 | * Returns the parent group for the group or file with the given key; 129 | */ 130 | - (XCGroup*)groupForGroupMemberWithKey:(NSString*)key; 131 | 132 | /** 133 | * Returns the parent group for the group or file with the source file 134 | */ 135 | - (XCGroup*)groupWithSourceFile:(XCSourceFile*)sourceFile; 136 | 137 | /* ====================================================================================================================================== */ 138 | #pragma mark Targets 139 | /** 140 | * Lists the targets in an xcode project, returning an array of `XCTarget` objects. 141 | */ 142 | - (NSArray*)targets; 143 | 144 | /** 145 | * Returns the target with the specified name, or nil. 146 | */ 147 | - (XCTarget*)targetWithName:(NSString*)name; 148 | 149 | #pragma mark Configurations 150 | 151 | /** 152 | * Returns the target with the specified name, or nil. 153 | */ 154 | - (NSDictionary*)configurations; 155 | 156 | - (NSDictionary*)configurationWithName:(NSString*)name; 157 | 158 | - (XCBuildConfiguration*)defaultConfiguration; 159 | 160 | /* ====================================================================================================================================== */ 161 | #pragma mark Saving 162 | /** 163 | * Saves a project after editing. 164 | */ 165 | - (void)save; 166 | 167 | 168 | /* ====================================================================================================================================== */ 169 | /** 170 | * Raw project data. 171 | */ 172 | - (NSMutableDictionary*)objects; 173 | 174 | @end 175 | -------------------------------------------------------------------------------- /SCStringsUtility/Readers&Writers/SCReader.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSVReader.m 3 | // CSVWriter 4 | // 5 | // Created by Stefan Ceriu on 10/11/12. 6 | // 7 | // 8 | 9 | #import "SCReader.h" 10 | #import "NSString+SCAdditions.h" 11 | #import "NSData+SCAdditions.h" 12 | 13 | @interface SCReader () 14 | { 15 | NSFileHandle * fileHandle; 16 | unsigned long long currentOffset; 17 | unsigned long long totalFileLength; 18 | 19 | NSString * lineDelimiter; 20 | BOOL commentStarted; 21 | } 22 | 23 | @property (nonatomic) NSUInteger chunkSize; 24 | @property (nonatomic, strong) NSString *filePath; 25 | 26 | @end 27 | 28 | @implementation SCReader 29 | @synthesize filePath; 30 | @synthesize currentLine; 31 | @synthesize chunkSize; 32 | @synthesize encoding; 33 | 34 | - (id)initWithPath:(NSString*)path 35 | { 36 | if(self = [super init]) 37 | { 38 | self.filePath = path; 39 | 40 | fileHandle = [NSFileHandle fileHandleForReadingAtPath:self.filePath]; 41 | if (fileHandle == nil) return nil; 42 | 43 | NSError *error; 44 | if (![NSString detectFileEncoding:&encoding path:filePath error:&error]) { 45 | // fallback 46 | encoding = NSUTF8StringEncoding; 47 | } 48 | 49 | lineDelimiter = @"\n"; 50 | filePath = path; 51 | currentOffset = 0ULL; 52 | chunkSize = 10; 53 | [fileHandle seekToEndOfFile]; 54 | totalFileLength = [fileHandle offsetInFile]; 55 | } 56 | 57 | return self; 58 | } 59 | 60 | - (NSString *)readLine 61 | { 62 | if (currentOffset >= totalFileLength) { currentOffset = 0ULL; currentLine = 0; return nil; } 63 | 64 | NSData * newLineData = [lineDelimiter dataUsingEncoding:self.encoding]; 65 | [fileHandle seekToFileOffset:currentOffset]; 66 | NSMutableData * currentData = [[NSMutableData alloc] init]; 67 | BOOL shouldReadMore = YES; 68 | 69 | @autoreleasepool { 70 | 71 | while (shouldReadMore) { 72 | if (currentOffset >= totalFileLength) { break; } 73 | NSData * chunk = [fileHandle readDataOfLength:chunkSize]; 74 | NSRange newLineRange = [chunk rangeOfData_dd:newLineData]; 75 | if (newLineRange.location != NSNotFound) { 76 | 77 | //include the length so we can include the delimiter in the string 78 | chunk = [chunk subdataWithRange:NSMakeRange(0, newLineRange.location+[newLineData length])]; 79 | shouldReadMore = NO; 80 | } 81 | [currentData appendData:chunk]; 82 | currentOffset += [chunk length]; 83 | } 84 | } 85 | 86 | currentLine ++; 87 | 88 | NSString * line = [[NSString alloc] initWithData:currentData encoding:self.encoding]; 89 | return line; 90 | } 91 | 92 | - (NSString *) readTrimmedLine 93 | { 94 | return [[self readLine] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 95 | } 96 | 97 | #if NS_BLOCKS_AVAILABLE 98 | - (void)enumerateLinesUsingBlock:(void(^)(NSString*, BOOL*))block 99 | { 100 | NSString * line = nil; 101 | BOOL stop = NO; 102 | while (stop == NO && (line = [self readLine])) { 103 | block(line, &stop); 104 | } 105 | } 106 | #endif 107 | 108 | - (NSString*)translationForKey:(NSString*)someKey 109 | { 110 | NSString *comment = nil; 111 | NSString *key = nil; 112 | NSString *translation = nil; 113 | 114 | while([self getNextComment:&comment key:&key translation:&translation]) 115 | { 116 | if([key isEqualToString:someKey]) return translation; 117 | } 118 | return nil; 119 | } 120 | 121 | - (BOOL) getNextComment:(NSString**)comment key:(NSString**)key translation:(NSString**)translation 122 | { 123 | *comment = @""; 124 | *key = nil; 125 | *translation = nil; 126 | 127 | NSString *tempString = @""; 128 | while ((tempString = [self readTrimmedLine])) 129 | { 130 | if([tempString rangeOfString:@"/*"].location != NSNotFound) 131 | { 132 | *comment = [*comment stringByAppendingString:tempString]; 133 | commentStarted = YES; 134 | continue; 135 | } 136 | else if([tempString rangeOfString:@"*/"].location != NSNotFound && commentStarted) 137 | { 138 | tempString = [@"\\n" stringByAppendingString:tempString]; 139 | *comment = [*comment stringByAppendingString:tempString]; 140 | commentStarted = NO; 141 | continue; 142 | } 143 | else if([tempString rangeOfString:@"="].location != NSNotFound) 144 | { 145 | NSArray *components = [tempString componentsSeparatedByString:@"="]; 146 | *key = [components[0] stringByTrimmingTralingWhiteSpaces]; 147 | *key = [*key substringWithRange:NSMakeRange(1, [*key length]-2)]; 148 | 149 | *translation = [components[1] stringByTrimmingLeadingWhiteSpaces]; 150 | *translation = [*translation substringWithRange:NSMakeRange(1, [*translation length] - 3)]; 151 | break; 152 | } 153 | else if([tempString length] && commentStarted) 154 | { 155 | tempString = [@"\\n" stringByAppendingString:tempString]; 156 | *comment = [*comment stringByAppendingString:tempString]; 157 | } 158 | } 159 | 160 | if(!*key) return NO; 161 | 162 | *key = [*key stringByReplacingOccurrencesOfString:@"\\\"" withString:@"'"]; 163 | *translation = [*translation stringByReplacingOccurrencesOfString:@"\\\"" withString:@"'"]; 164 | 165 | return YES; 166 | } 167 | 168 | - (void) dealloc 169 | { 170 | [fileHandle closeFile]; 171 | fileHandle = nil; 172 | filePath = nil; 173 | lineDelimiter = nil; 174 | currentOffset = 0ULL; 175 | } 176 | 177 | @end 178 | -------------------------------------------------------------------------------- /XcodeEditor.framework/Versions/A/Headers/XCGroup.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | #import "XcodeGroupMember.h" 15 | 16 | @class XCProject; 17 | @class XCClassDefinition; 18 | @class XCSourceFile; 19 | @class XCXibDefinition; 20 | @class XCFileOperationQueue; 21 | @class XCFrameworkDefinition; 22 | @class XCSourceFileDefinition; 23 | @class XCSubProjectDefinition; 24 | 25 | 26 | /** 27 | * Represents a group container in an Xcode project. A group can contain members of type `XCSourceFile` or other 28 | * groups. 29 | */ 30 | @interface XCGroup : NSObject { 31 | 32 | NSString* _pathRelativeToParent; 33 | NSString* _key; 34 | NSString* _alias; 35 | 36 | 37 | @private 38 | NSString* _pathRelativeToProjectRoot; 39 | NSMutableArray* _children; 40 | NSMutableArray* _members; 41 | 42 | XCFileOperationQueue* _fileOperationQueue; // weak 43 | XCProject* _project; 44 | 45 | } 46 | 47 | /* =================================================== Properties =================================================== */ 48 | 49 | /** 50 | * The alias of the group, which can be used to give the group a name other than the last path component. 51 | * 52 | * See: [XcodeGroupMember displayName] 53 | */ 54 | @property(nonatomic, strong, readonly) NSString* alias; 55 | 56 | /** 57 | * The path of the group relative to the group's parent. 58 | * 59 | * See: [XcodeGroupMember displayName] 60 | */ 61 | @property(nonatomic, strong, readonly) NSString* pathRelativeToParent; 62 | 63 | /** 64 | * The group's unique key. 65 | */ 66 | @property(nonatomic, strong, readonly) NSString* key; 67 | 68 | /** 69 | * An array containing the groups members as `XcodeGroupMember` types. 70 | */ 71 | @property(nonatomic, strong, readonly) NSMutableArray* children; 72 | 73 | 74 | /* ================================================================================================================== */ 75 | #pragma mark Initializers 76 | 77 | + (XCGroup*) groupWithProject:(XCProject*)project 78 | key:(NSString*)key 79 | alias:(NSString*)alias 80 | path:(NSString*)path 81 | children:(NSArray*)children; 82 | 83 | - (id) initWithProject:(XCProject*)project key:(NSString*)key alias:(NSString*)alias path:(NSString*)path 84 | children:(NSArray*)children; 85 | 86 | /* ================================================================================================================== */ 87 | #pragma mark Parent group 88 | 89 | - (void) removeFromParentGroup; 90 | 91 | - (void)removeFromParentDeletingChildren:(BOOL)deleteChildren; 92 | 93 | - (XCGroup*) parentGroup; 94 | 95 | - (BOOL) isRootGroup; 96 | 97 | /* ================================================================================================================== */ 98 | #pragma mark Adding children 99 | /** 100 | * Adds a class to the group, as specified by the ClassDefinition. If the group already contains a class by the same 101 | * name, the contents will be updated. 102 | */ 103 | - (void) addClass:(XCClassDefinition*)classDefinition; 104 | 105 | /** 106 | * Adds a class to the group, making it a member of the specified [targets](XCTarget). 107 | */ 108 | - (void) addClass:(XCClassDefinition*)classDefinition toTargets:(NSArray*)targets; 109 | 110 | /** 111 | * Adds a framework to the group. If the group already contains the framework, the contents will be updated if the 112 | * framework definition's copyToDestination flag is yes, otherwise it will be ignored. 113 | */ 114 | - (void) addFramework:(XCFrameworkDefinition*)frameworkDefinition; 115 | 116 | /** 117 | * Adds a group with a path relative to this group. 118 | */ 119 | - (XCGroup*) addGroupWithPath:(NSString*)path; 120 | 121 | /** 122 | * Adds a framework to the group, making it a member of the specified targets. 123 | */ 124 | - (void) addFramework:(XCFrameworkDefinition*)framework toTargets:(NSArray*)targets; 125 | 126 | /** 127 | * Adds a source file of arbitrary type - image resource, header, etc. 128 | */ 129 | - (void) addSourceFile:(XCSourceFileDefinition*)sourceFileDefinition; 130 | 131 | /** 132 | * Adds a xib file to the group. If the group already contains a class by the same name, the contents will be updated. 133 | */ 134 | - (void) addXib:(XCXibDefinition*)xibDefinition; 135 | 136 | /** 137 | * Adds a xib to the group, making it a member of the specified [targets](XCTarget). 138 | */ 139 | - (void) addXib:(XCXibDefinition*)xibDefinition toTargets:(NSArray*)targets; 140 | 141 | /** 142 | * Adds a sub-project to the group. If the group already contains a sub-project by the same name, the contents will be 143 | * updated. 144 | * Returns boolean success/fail; if method fails, caller should assume that project file is corrupt (or file format has 145 | * changed). 146 | */ 147 | - (void) addSubProject:(XCSubProjectDefinition*)projectDefinition; 148 | 149 | /** 150 | * Adds a sub-project to the group, making it a member of the specified [targets](XCTarget). 151 | */ 152 | - (void) addSubProject:(XCSubProjectDefinition*)projectDefinition toTargets:(NSArray*)targets; 153 | 154 | - (void) removeSubProject:(XCSubProjectDefinition*)projectDefinition; 155 | 156 | - (void) removeSubProject:(XCSubProjectDefinition*)projectDefinition fromTargets:(NSArray*)targets; 157 | 158 | /* ================================================================================================================== */ 159 | #pragma mark Locating children 160 | /** 161 | * Instances of `XCSourceFile` and `XCGroup` returned as the type `XcodeGroupMember`. 162 | */ 163 | - (NSArray*) members; 164 | 165 | /** 166 | * Instances of `XCSourceFile` from this group and any child groups. 167 | */ 168 | - (NSArray*) recursiveMembers; 169 | 170 | 171 | - (NSArray*) buildFileKeys; 172 | 173 | /** 174 | * Returns the child with the specified key, or nil. 175 | */ 176 | - (id) memberWithKey:(NSString*)key; 177 | 178 | /** 179 | * Returns the child with the specified name, or nil. 180 | */ 181 | - (id) memberWithDisplayName:(NSString*)name; 182 | 183 | 184 | @end 185 | -------------------------------------------------------------------------------- /SCStringsUtility/SCRootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCRootViewController.m 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 10/12/12. 6 | // Copyright (c) 2012 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import "SCRootViewController.h" 10 | #import "SCStringsController.h" 11 | 12 | #import "SCOpenAccessoryView.h" 13 | #import "SCExportAccessoryView.h" 14 | 15 | static NSString *kFileTypeXcodeProject = @"xcodeproj"; 16 | static NSString *kFileTypeCSV = @"csv"; 17 | static NSString *kFileTypeXML = @"xml"; 18 | 19 | @interface SCRootViewController () 20 | 21 | @property (nonatomic, weak) IBOutlet NSSplitView *splitView; 22 | 23 | @property (nonatomic, weak) IBOutlet NSOutlineView *outlineView; 24 | @property (nonatomic, weak) IBOutlet NSTableView *tableView; 25 | @property (nonatomic, weak) IBOutlet NSProgressIndicator *progressIndicator; 26 | 27 | @property (nonatomic, weak) IBOutlet NSButton *saveButton; 28 | @property (nonatomic, weak) IBOutlet NSButton *exportButton; 29 | 30 | @property (nonatomic, weak) IBOutlet NSSearchField *searchField; 31 | @property (nonatomic, weak) IBOutlet NSButton *searchKeysOnlyCheckboxButton; 32 | 33 | @property (nonatomic, assign) IBOutlet NSTextView *textView; 34 | 35 | @property (nonatomic, weak) IBOutlet SCOpenAccessoryView *openPanelAccessoryView; 36 | @property (nonatomic, weak) IBOutlet SCExportAccessoryView *exportPanelAccessoryView; 37 | 38 | @property (nonatomic, strong) __block NSSavePanel *exportPanel; 39 | 40 | @property (nonatomic, strong) SCStringsController *stringsController; 41 | 42 | @property (nonatomic, assign) SCFileType selectedFileType; 43 | 44 | @end 45 | 46 | @implementation SCRootViewController 47 | 48 | - (NSUndoManager *)undoManager 49 | { 50 | return self.stringsController.undoManager; 51 | } 52 | 53 | - (void)loadView 54 | { 55 | [super loadView]; 56 | 57 | self.stringsController = [[SCStringsController alloc] init]; 58 | [self.stringsController setDelegate:self]; 59 | 60 | [self.outlineView setDataSource:self.stringsController]; 61 | [self.outlineView setDelegate:self.stringsController]; 62 | 63 | [self.tableView setDataSource:self.stringsController]; 64 | [self.tableView setDelegate:self.stringsController]; 65 | 66 | [self.stringsController setOutlineView:self.outlineView]; 67 | [self.stringsController setTableView:self.tableView]; 68 | 69 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidEndLiveResizeNotification object:nil]; 70 | } 71 | 72 | - (void)reset 73 | { 74 | [self.stringsController reset]; 75 | self.textView.string = @""; 76 | } 77 | 78 | - (void)reloadData 79 | { 80 | dispatch_async(dispatch_get_main_queue(), ^{ 81 | [self.outlineView reloadData]; 82 | [self.outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; 83 | [self.outlineView expandItem:nil expandChildren:YES]; 84 | 85 | [self.progressIndicator stopAnimation:self]; 86 | [self.progressIndicator.animator setAlphaValue:0.0f]; 87 | [self.saveButton.animator setAlphaValue:1.0f]; 88 | [self.exportButton.animator setAlphaValue:1.0f]; 89 | }); 90 | } 91 | 92 | #pragma mark - Actions 93 | 94 | - (IBAction)onExportClick:(id)sender 95 | { 96 | if(!self.exportPanel) 97 | { 98 | switch (self.stringsController.sourceType) 99 | { 100 | case SCFileTypeXcodeProject: 101 | { 102 | [self.exportPanelAccessoryView setSelectedExportType:SCFileTypeCSV]; 103 | break; 104 | } 105 | case SCFileTypeCSV: 106 | { 107 | [self.exportPanelAccessoryView setSelectedExportType:SCFileTypeXcodeProject]; 108 | break; 109 | } 110 | case SCFileTypeXML: 111 | { 112 | [self.exportPanelAccessoryView setSelectedExportType:SCFileTypeXcodeProject]; 113 | break; 114 | } 115 | default: 116 | { 117 | [self.exportPanelAccessoryView setSelectedExportType:SCFileTypeCSV]; 118 | break; 119 | } 120 | } 121 | } 122 | else { 123 | 124 | [self.exportPanel setCanCreateDirectories:YES]; 125 | [self.exportPanel setAccessoryView:self.exportPanelAccessoryView]; 126 | 127 | [self.exportPanel beginWithCompletionHandler:^(NSInteger result) { 128 | 129 | if(result != NSOKButton) return; 130 | 131 | switch ([self.exportPanelAccessoryView selectedExportType]) 132 | { 133 | case SCFileTypeXcodeProject: 134 | { 135 | [self.stringsController generateStringFilesAtPath:[[self.exportPanel URL] path] 136 | success:nil 137 | failure:nil]; 138 | 139 | break; 140 | } 141 | case SCFileTypeCSV: 142 | { 143 | BOOL includeComments = [self.exportPanelAccessoryView shouldIncludeComments]; 144 | BOOL useKeyForMissingTranslations = [self.exportPanelAccessoryView shouldUseKeyForMissingTranslations]; 145 | 146 | [self.stringsController generateCSVAtPath:[[self.exportPanel URL] path] 147 | includeComments:includeComments 148 | useKeyForEmptyTranslations:useKeyForMissingTranslations 149 | success:nil 150 | failure:nil]; 151 | 152 | break; 153 | } 154 | case SCFileTypeXML: 155 | { 156 | BOOL includeComments = [self.exportPanelAccessoryView shouldIncludeComments]; 157 | BOOL useKeyForMissingTranslations = [self.exportPanelAccessoryView shouldUseKeyForMissingTranslations]; 158 | 159 | [self.stringsController generateXMLFileAtPath:[[self.exportPanel URL] path] 160 | includeComments:includeComments 161 | useKeyForEmptyTranslations:useKeyForMissingTranslations 162 | success:nil 163 | failure:nil]; 164 | break; 165 | } 166 | default: 167 | { 168 | break; 169 | } 170 | } 171 | }]; 172 | } 173 | } 174 | 175 | - (void)exportAccessoryView:(SCExportAccessoryView*)view didSelectFormatType:(SCFileType)type 176 | { 177 | switch (type) { 178 | case SCFileTypeXcodeProject: 179 | { 180 | [self.exportPanel close]; 181 | 182 | self.exportPanel = [NSOpenPanel openPanel]; 183 | [(NSOpenPanel*)self.exportPanel setCanChooseFiles:NO]; 184 | [(NSOpenPanel*)self.exportPanel setCanChooseDirectories:YES]; 185 | 186 | break; 187 | } 188 | default: 189 | { 190 | BOOL shouldReload = !self.exportPanel || [self.exportPanel isKindOfClass:[NSOpenPanel class]]; 191 | 192 | if(shouldReload) 193 | { 194 | [self.exportPanel close]; 195 | 196 | self.exportPanel = [NSSavePanel savePanel]; 197 | [self.exportPanel setCanCreateDirectories:YES]; 198 | } 199 | 200 | [self.exportPanel setAllowedFileTypes:(type == SCFileTypeXML ? @[@"xml"] : @[@"csv"])]; 201 | 202 | break; 203 | } 204 | } 205 | 206 | if(!self.exportPanel.isVisible) 207 | [self onExportClick:nil]; 208 | } 209 | 210 | - (IBAction)onSaveClick:(id)sender 211 | { 212 | [self.stringsController save:nil failure:^(NSError *error) { 213 | SCLog(@"Could not save data %@", error); 214 | }]; 215 | } 216 | 217 | - (IBAction)onOpenClick:(id)sender 218 | { 219 | NSOpenPanel *openPanel = [NSOpenPanel openPanel]; 220 | [openPanel setDelegate:(id )self]; 221 | [openPanel setCanChooseDirectories:NO]; 222 | [openPanel setCanChooseFiles:YES]; 223 | [openPanel setCanCreateDirectories:NO]; 224 | [openPanel setAllowsMultipleSelection:NO]; 225 | 226 | [openPanel setAllowedFileTypes:@[@"xcodeproj", @"csv", @"xml"]]; 227 | 228 | NSInteger result = [openPanel runModal]; 229 | if(result != NSOKButton) return; 230 | 231 | [self.progressIndicator.animator setAlphaValue:1.0f]; 232 | [self.progressIndicator startAnimation:self]; 233 | 234 | [self reset]; 235 | 236 | switch (self.selectedFileType) { 237 | case SCFileTypeXcodeProject: 238 | { 239 | if(!openPanel.accessoryView) { 240 | SCLog(@"Unkown error"); 241 | return; 242 | } 243 | 244 | BOOL includePositionalParameters = [self.openPanelAccessoryView shouldAddPositionalParameters]; 245 | NSString *routine = [self.openPanelAccessoryView genstringsRoutine]; 246 | NSString *stringsFile = [self.openPanelAccessoryView stringsFileName]; 247 | 248 | [self.stringsController importProjectAtPath:[[openPanel URL] path] 249 | positionalParameters:includePositionalParameters 250 | genstringsRoutine:routine 251 | stringsFileName:stringsFile 252 | success:^{ [self reloadData];} 253 | failure:^(NSError *error) { SCLog(@"Could not import Xcode project %@", error);}]; 254 | break; 255 | } 256 | case SCFileTypeCSV: 257 | { 258 | [self.stringsController importCSVFileAtPath:[[openPanel URL] path] 259 | success:^{ [self reloadData];} 260 | failure:^(NSError *error) { SCLog(@"Could not import CSV file %@",error);}]; 261 | break; 262 | } 263 | case SCFileTypeXML: 264 | { 265 | [self.stringsController importXMLFileAtPath:[[openPanel URL] path] 266 | success:^{ [self reloadData]; } 267 | failure:^(NSError *error) { SCLog(@"Could not import XML file %@",error);}]; 268 | } 269 | default: 270 | { 271 | break; 272 | } 273 | } 274 | 275 | self.selectedFileType = SCFileTypeInvalid; 276 | } 277 | 278 | - (void)panelSelectionDidChange:(NSOpenPanel*)sender 279 | { 280 | static NSDictionary *extensionToType; 281 | static dispatch_once_t onceToken; 282 | dispatch_once(&onceToken, ^{ 283 | extensionToType = (@{kFileTypeXcodeProject : @(SCFileTypeXcodeProject), 284 | kFileTypeCSV : @(SCFileTypeCSV), 285 | kFileTypeXML : @(SCFileTypeXML) 286 | }); 287 | }); 288 | 289 | NSString *extension = [[sender.URL path] pathExtension]; 290 | 291 | if(extension.length == 0) return; 292 | 293 | SCFileType selectedType = [extensionToType[extension] intValue]; 294 | switch (selectedType) { 295 | case SCFileTypeXcodeProject: 296 | { 297 | [sender setAccessoryView:self.openPanelAccessoryView]; 298 | break; 299 | } 300 | case SCFileTypeCSV: 301 | { 302 | [sender setAccessoryView:nil]; 303 | break; 304 | } 305 | case SCFileTypeXML: 306 | { 307 | [sender setAccessoryView:nil]; 308 | break; 309 | } 310 | default: 311 | { 312 | break; 313 | } 314 | } 315 | 316 | self.selectedFileType = selectedType; 317 | } 318 | 319 | typedef enum { 320 | SearchViewModeClosed, 321 | SearchViewModeOpened 322 | } SearchViewMode; 323 | 324 | - (IBAction)triggerSearchField:(id)sender 325 | { 326 | CGRect frame = self.splitView.frame; 327 | switch (self.searchField.tag) 328 | { 329 | case SearchViewModeClosed: 330 | { 331 | [self.searchField setTag:SearchViewModeOpened]; 332 | [self.searchField.animator setAlphaValue:1.0f]; 333 | [self.searchField becomeFirstResponder]; 334 | 335 | frame.size.height -= self.searchField.frame.size.height + 5.0f; 336 | break; 337 | } 338 | case SearchViewModeOpened: 339 | { 340 | [self.searchField setTag:SearchViewModeClosed]; 341 | [self.searchField.animator setAlphaValue:0.0f]; 342 | [[[NSApplication sharedApplication] keyWindow] makeFirstResponder:nil]; 343 | 344 | frame.size.height += self.searchField.frame.size.height + 5.0f; 345 | break; 346 | } 347 | } 348 | 349 | [self.splitView.animator setFrame:frame]; 350 | } 351 | 352 | #pragma mark - Filtering 353 | 354 | - (void)filterEntries 355 | { 356 | BOOL searchOnlyKeys = self.searchKeysOnlyCheckboxButton.state; 357 | NSString *searchString = [self.searchField.stringValue lowercaseString]; 358 | 359 | [self.stringsController filterEntriesWithSearchString:searchString onlyKeys:searchOnlyKeys]; 360 | [self.tableView reloadData]; 361 | } 362 | 363 | - (void)controlTextDidChange:(NSNotification *)notification { 364 | if(![notification.object isEqual:self.searchField]) { 365 | return; 366 | } 367 | 368 | [self filterEntries]; 369 | } 370 | 371 | - (IBAction)onSearchKeysOnlyCheckboxButtonValueChanged:(id)sender { 372 | [self filterEntries]; 373 | } 374 | 375 | #pragma mark - NSSplitViewDelegate 376 | 377 | - (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)oldSize 378 | { 379 | CGFloat dividerThickness = [splitView dividerThickness]; 380 | NSRect leftRect = [[[splitView subviews] objectAtIndex:0] frame]; 381 | NSRect rightRect = [[[splitView subviews] objectAtIndex:1] frame]; 382 | NSRect newFrame = [splitView frame]; 383 | 384 | leftRect.size.height = newFrame.size.height; 385 | leftRect.origin = NSMakePoint(0, 0); 386 | rightRect.size.width = newFrame.size.width - leftRect.size.width - dividerThickness; 387 | rightRect.size.height = newFrame.size.height ; 388 | rightRect.origin.x = leftRect.size.width + dividerThickness; 389 | 390 | [[[splitView subviews] objectAtIndex:0] setFrame:leftRect]; 391 | [[[splitView subviews] objectAtIndex:1] setFrame:rightRect]; 392 | } 393 | 394 | #pragma mark - SCStringsControllerDelegate 395 | 396 | - (void)stringsController:(SCStringsController *)stringsController didGetGenstringsOutput:(NSString *)output 397 | { 398 | dispatch_async(dispatch_get_main_queue(), ^{ 399 | self.textView.string = [self.textView.string stringByAppendingString:output]; 400 | [self.textView scrollToEndOfDocument:nil]; 401 | }); 402 | } 403 | 404 | #pragma mark - Properties 405 | 406 | - (SCOpenAccessoryView *)openPanelAccessoryView 407 | { 408 | if(!_openPanelAccessoryView) 409 | { 410 | Class class = [SCOpenAccessoryView class]; 411 | 412 | NSArray *items; 413 | 414 | NSNib *nib = [[NSNib alloc] initWithNibNamed:NSStringFromClass(class) bundle:nil]; 415 | [nib instantiateNibWithOwner:self topLevelObjects:&items]; 416 | 417 | [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 418 | if([obj isKindOfClass:class]) 419 | { 420 | _openPanelAccessoryView = obj; 421 | stop = YES; 422 | } 423 | 424 | }]; 425 | 426 | } 427 | 428 | return _openPanelAccessoryView; 429 | } 430 | 431 | - (SCExportAccessoryView *)exportPanelAccessoryView 432 | { 433 | if(!_exportPanelAccessoryView) 434 | { 435 | Class class = [SCExportAccessoryView class]; 436 | 437 | NSArray *items; 438 | 439 | NSNib *nib = [[NSNib alloc] initWithNibNamed:NSStringFromClass(class) bundle:nil]; 440 | [nib instantiateNibWithOwner:nil topLevelObjects:&items]; 441 | 442 | [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 443 | if([obj isKindOfClass:class]) 444 | { 445 | _exportPanelAccessoryView = obj; 446 | [_exportPanelAccessoryView setDelegate:(id)self]; 447 | stop = YES; 448 | } 449 | }]; 450 | } 451 | 452 | return _exportPanelAccessoryView; 453 | } 454 | 455 | #pragma mark - Others 456 | 457 | - (void)windowDidResize:(NSNotification *)notification 458 | { 459 | [self.tableView reloadData]; 460 | } 461 | 462 | typedef enum 463 | { 464 | SCMenuItemOpenProject = 1, 465 | SCMenuItemImport, 466 | SCMenuItemSave, 467 | SCMenuItemExport, 468 | SCMenuItemFind 469 | } SCMenuItem; 470 | 471 | - (BOOL)validateMenuItem:(NSMenuItem *)menuItem 472 | { 473 | switch (menuItem.tag) 474 | { 475 | case SCMenuItemOpenProject: 476 | { 477 | return YES; 478 | } 479 | case SCMenuItemImport: 480 | { 481 | return YES; 482 | } 483 | case SCMenuItemSave: 484 | { 485 | return (self.stringsController.sourceType != SCFileTypeInvalid); 486 | } 487 | case SCMenuItemExport: 488 | { 489 | return (self.stringsController.sourceType != SCFileTypeInvalid); 490 | } 491 | case SCMenuItemFind: 492 | { 493 | return YES; 494 | } 495 | } 496 | 497 | return YES; 498 | } 499 | 500 | @end -------------------------------------------------------------------------------- /SCStringsUtility/Views/SCExportAccessoryView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1070 5 | 11G56 6 | 3084 7 | 1138.51 8 | 569.00 9 | 10 | com.apple.InterfaceBuilder.CocoaPlugin 11 | 3084 12 | 13 | 14 | NSButton 15 | NSButtonCell 16 | NSComboBox 17 | NSComboBoxCell 18 | NSCustomObject 19 | NSCustomView 20 | 21 | 22 | com.apple.InterfaceBuilder.CocoaPlugin 23 | 24 | 25 | PluginDependencyRecalculationVersion 26 | 27 | 28 | 29 | 30 | NSObject 31 | 32 | 33 | FirstResponder 34 | 35 | 36 | NSApplication 37 | 38 | 39 | 40 | 268 41 | 42 | 43 | 44 | 268 45 | {{20, 77}, {214, 26}} 46 | 47 | 48 | 49 | _NS:9 50 | YES 51 | 52 | 74448961 53 | 306189312 54 | 55 | 56 | LucidaGrande 57 | 13 58 | 1044 59 | 60 | _NS:9 61 | 62 | YES 63 | 64 | 6 65 | System 66 | textBackgroundColor 67 | 68 | 3 69 | MQA 70 | 71 | 72 | 73 | 6 74 | System 75 | controlTextColor 76 | 77 | 3 78 | MAA 79 | 80 | 81 | 3 82 | YES 83 | 84 | .strings 85 | .csv 86 | .xml 87 | 88 | 89 | 90 | 91 | 274 92 | {13, 63} 93 | 94 | 95 | _NS:24 96 | YES 97 | 98 | 99 | 10 100 | 10 101 | 1000 102 | 103 | 75497472 104 | 0 105 | 106 | 107 | LucidaGrande 108 | 12 109 | 16 110 | 111 | 112 | 3 113 | MC4zMzMzMzI5ODU2AA 114 | 115 | 116 | 117 | 118 | 338690112 119 | 1024 120 | 121 | 122 | YES 123 | 124 | 6 125 | System 126 | controlBackgroundColor 127 | 128 | 3 129 | MC42NjY2NjY2NjY3AA 130 | 131 | 132 | 133 | 134 | 3 135 | YES 136 | 137 | 138 | 139 | 3 140 | 2 141 | 142 | 143 | 6 144 | System 145 | gridColor 146 | 147 | 3 148 | MC41AA 149 | 150 | 151 | 19 152 | tableViewAction: 153 | -765427712 154 | 155 | 156 | 157 | 1 158 | 15 159 | 0 160 | YES 161 | 0 162 | 1 163 | 164 | 165 | 166 | 167 | 168 | 301 169 | {{18, 18}, {215, 18}} 170 | 171 | 172 | _NS:9 173 | YES 174 | 175 | 67108864 176 | 268435456 177 | Use key for empty translations 178 | 179 | _NS:9 180 | 181 | 1211912448 182 | 2 183 | 184 | NSImage 185 | NSSwitch 186 | 187 | 188 | NSSwitch 189 | 190 | 191 | 192 | 200 193 | 25 194 | 195 | 196 | 197 | 198 | 301 199 | {{18, 38}, {138, 18}} 200 | 201 | 202 | 203 | _NS:9 204 | YES 205 | 206 | -2080374784 207 | 268435456 208 | Include comments 209 | 210 | _NS:9 211 | 212 | 1211912448 213 | 2 214 | 215 | 216 | 217 | 218 | 200 219 | 25 220 | 221 | 222 | 223 | {251, 121} 224 | 225 | 226 | 227 | _NS:9 228 | SCExportAccessoryView 229 | 230 | 231 | 232 | 233 | 234 | 235 | formatComboBox 236 | 237 | 238 | 239 | 34 240 | 241 | 242 | 243 | includeCommentsButton 244 | 245 | 246 | 247 | 35 248 | 249 | 250 | 251 | useKeyForMissingTranslationsButton 252 | 253 | 254 | 255 | 36 256 | 257 | 258 | 259 | delegate 260 | 261 | 262 | 263 | 37 264 | 265 | 266 | 267 | 268 | 269 | 0 270 | 271 | 272 | 273 | 274 | 275 | -2 276 | 277 | 278 | File's Owner 279 | 280 | 281 | -1 282 | 283 | 284 | First Responder 285 | 286 | 287 | -3 288 | 289 | 290 | Application 291 | 292 | 293 | 20 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 21 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 22 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 23 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 24 328 | 329 | 330 | 331 | 332 | 25 333 | 334 | 335 | 336 | 337 | 26 338 | 339 | 340 | 341 | 342 | 343 | 344 | com.apple.InterfaceBuilder.CocoaPlugin 345 | com.apple.InterfaceBuilder.CocoaPlugin 346 | com.apple.InterfaceBuilder.CocoaPlugin 347 | com.apple.InterfaceBuilder.CocoaPlugin 348 | com.apple.InterfaceBuilder.CocoaPlugin 349 | com.apple.InterfaceBuilder.CocoaPlugin 350 | com.apple.InterfaceBuilder.CocoaPlugin 351 | com.apple.InterfaceBuilder.CocoaPlugin 352 | com.apple.InterfaceBuilder.CocoaPlugin 353 | 354 | .strings 355 | .csv 356 | .xml 357 | 358 | com.apple.InterfaceBuilder.CocoaPlugin 359 | 360 | 361 | 362 | 363 | 364 | 37 365 | 366 | 367 | 368 | 369 | SCExportAccessoryView 370 | NSView 371 | 372 | NSComboBox 373 | NSButton 374 | NSButton 375 | 376 | 377 | 378 | formatComboBox 379 | NSComboBox 380 | 381 | 382 | includeCommentsButton 383 | NSButton 384 | 385 | 386 | useKeyForMissingTranslationsButton 387 | NSButton 388 | 389 | 390 | 391 | IBProjectSource 392 | ./Classes/SCExportAccessoryView.h 393 | 394 | 395 | 396 | 397 | 0 398 | IBCocoaFramework 399 | YES 400 | 3 401 | 402 | NSSwitch 403 | {15, 15} 404 | 405 | 406 | 407 | -------------------------------------------------------------------------------- /SCStringsUtility/SCRootViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 58 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 100 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 184 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 211 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /SCStringsUtility.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1831CBE11628086A004B6DD4 /* SCRootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1831CBDF1628086A004B6DD4 /* SCRootViewController.m */; }; 11 | 1831CBE21628086A004B6DD4 /* SCRootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1831CBE01628086A004B6DD4 /* SCRootViewController.xib */; }; 12 | 1831CBE4162809E0004B6DD4 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1831CBE3162809E0004B6DD4 /* QuartzCore.framework */; }; 13 | 1843F4501650EB9900CEDBEF /* icon-export.png in Resources */ = {isa = PBXBuildFile; fileRef = 1843F44C1650EB9900CEDBEF /* icon-export.png */; }; 14 | 1843F4521650EB9900CEDBEF /* icon-open.png in Resources */ = {isa = PBXBuildFile; fileRef = 1843F44E1650EB9900CEDBEF /* icon-open.png */; }; 15 | 1843F4531650EB9900CEDBEF /* icon-save.png in Resources */ = {isa = PBXBuildFile; fileRef = 1843F44F1650EB9900CEDBEF /* icon-save.png */; }; 16 | 1852D07816D01F8D00574822 /* icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 1852D07716D01F8D00574822 /* icon.icns */; }; 17 | 186758CD1654C83F00BEE1E1 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 186758CC1654C83F00BEE1E1 /* MainMenu.xib */; }; 18 | 1878321816BECEC800A8C760 /* XcodeEditor.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 18F0D44F16BECE6800CC0264 /* XcodeEditor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 19 | 18D185A516C050E00082E573 /* NSData+SCAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D1859216C050E00082E573 /* NSData+SCAdditions.m */; }; 20 | 18D185A616C050E00082E573 /* NSString+SCAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D1859416C050E00082E573 /* NSString+SCAdditions.m */; }; 21 | 18D185A716C050E00082E573 /* OrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D1859616C050E00082E573 /* OrderedDictionary.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 22 | 18D185A816C050E00082E573 /* SCCSVWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D1859916C050E00082E573 /* SCCSVWriter.m */; }; 23 | 18D185A916C050E00082E573 /* SCReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D1859B16C050E00082E573 /* SCReader.m */; }; 24 | 18D185AA16C050E00082E573 /* SCStringsWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D1859D16C050E00082E573 /* SCStringsWriter.m */; }; 25 | 18D185AB16C050E00082E573 /* SCExportAccessoryView.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D185A016C050E00082E573 /* SCExportAccessoryView.m */; }; 26 | 18D185AC16C050E00082E573 /* SCExportAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18D185A116C050E00082E573 /* SCExportAccessoryView.xib */; }; 27 | 18D185AD16C050E00082E573 /* SCOpenAccessoryView.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D185A316C050E00082E573 /* SCOpenAccessoryView.m */; }; 28 | 18D185AE16C050E00082E573 /* SCOpenAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18D185A416C050E00082E573 /* SCOpenAccessoryView.xib */; }; 29 | 18D36750162772B500B6BACE /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18D3674F162772B500B6BACE /* Cocoa.framework */; }; 30 | 18D3675C162772B500B6BACE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3675B162772B500B6BACE /* main.m */; }; 31 | 18D36760162772B600B6BACE /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 18D3675E162772B600B6BACE /* Credits.rtf */; }; 32 | 18D36763162772B600B6BACE /* SCAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D36762162772B600B6BACE /* SCAppDelegate.m */; }; 33 | 18D9AFAB16BD4E98008601C0 /* SCStringsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D9AFAA16BD4E98008601C0 /* SCStringsController.m */; }; 34 | 18F0D45316BECE8300CC0264 /* XcodeEditor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18F0D44F16BECE6800CC0264 /* XcodeEditor.framework */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXCopyFilesBuildPhase section */ 38 | 1878321716BECEC400A8C760 /* CopyFiles */ = { 39 | isa = PBXCopyFilesBuildPhase; 40 | buildActionMask = 2147483647; 41 | dstPath = ""; 42 | dstSubfolderSpec = 10; 43 | files = ( 44 | 1878321816BECEC800A8C760 /* XcodeEditor.framework in CopyFiles */, 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXCopyFilesBuildPhase section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | 1831CBDE1628086A004B6DD4 /* SCRootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCRootViewController.h; sourceTree = ""; }; 52 | 1831CBDF1628086A004B6DD4 /* SCRootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCRootViewController.m; sourceTree = ""; }; 53 | 1831CBE01628086A004B6DD4 /* SCRootViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCRootViewController.xib; sourceTree = ""; }; 54 | 1831CBE3162809E0004B6DD4 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 55 | 1843F44C1650EB9900CEDBEF /* icon-export.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-export.png"; sourceTree = ""; }; 56 | 1843F44E1650EB9900CEDBEF /* icon-open.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-open.png"; sourceTree = ""; }; 57 | 1843F44F1650EB9900CEDBEF /* icon-save.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "icon-save.png"; sourceTree = ""; }; 58 | 1852D07716D01F8D00574822 /* icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon.icns; sourceTree = ""; }; 59 | 186758CC1654C83F00BEE1E1 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; 60 | 18D1859116C050E00082E573 /* NSData+SCAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+SCAdditions.h"; sourceTree = ""; }; 61 | 18D1859216C050E00082E573 /* NSData+SCAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+SCAdditions.m"; sourceTree = ""; }; 62 | 18D1859316C050E00082E573 /* NSString+SCAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+SCAdditions.h"; sourceTree = ""; }; 63 | 18D1859416C050E00082E573 /* NSString+SCAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+SCAdditions.m"; sourceTree = ""; }; 64 | 18D1859516C050E00082E573 /* OrderedDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OrderedDictionary.h; sourceTree = ""; }; 65 | 18D1859616C050E00082E573 /* OrderedDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OrderedDictionary.m; sourceTree = ""; }; 66 | 18D1859816C050E00082E573 /* SCCSVWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCCSVWriter.h; sourceTree = ""; }; 67 | 18D1859916C050E00082E573 /* SCCSVWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCCSVWriter.m; sourceTree = ""; }; 68 | 18D1859A16C050E00082E573 /* SCReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCReader.h; sourceTree = ""; }; 69 | 18D1859B16C050E00082E573 /* SCReader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCReader.m; sourceTree = ""; }; 70 | 18D1859C16C050E00082E573 /* SCStringsWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCStringsWriter.h; sourceTree = ""; }; 71 | 18D1859D16C050E00082E573 /* SCStringsWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCStringsWriter.m; sourceTree = ""; }; 72 | 18D1859F16C050E00082E573 /* SCExportAccessoryView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCExportAccessoryView.h; sourceTree = ""; }; 73 | 18D185A016C050E00082E573 /* SCExportAccessoryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCExportAccessoryView.m; sourceTree = ""; }; 74 | 18D185A116C050E00082E573 /* SCExportAccessoryView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCExportAccessoryView.xib; sourceTree = ""; }; 75 | 18D185A216C050E00082E573 /* SCOpenAccessoryView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCOpenAccessoryView.h; sourceTree = ""; }; 76 | 18D185A316C050E00082E573 /* SCOpenAccessoryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCOpenAccessoryView.m; sourceTree = ""; }; 77 | 18D185A416C050E00082E573 /* SCOpenAccessoryView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCOpenAccessoryView.xib; sourceTree = ""; }; 78 | 18D3674B162772B500B6BACE /* SCStringsUtility.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SCStringsUtility.app; sourceTree = BUILT_PRODUCTS_DIR; }; 79 | 18D3674F162772B500B6BACE /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 80 | 18D36752162772B500B6BACE /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 81 | 18D36753162772B500B6BACE /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 82 | 18D36754162772B500B6BACE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 83 | 18D36757162772B500B6BACE /* SCStringsUtility-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SCStringsUtility-Info.plist"; sourceTree = ""; }; 84 | 18D3675B162772B500B6BACE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 85 | 18D3675D162772B600B6BACE /* SCStringsUtility-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SCStringsUtility-Prefix.pch"; sourceTree = ""; }; 86 | 18D3675F162772B600B6BACE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 87 | 18D36761162772B600B6BACE /* SCAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SCAppDelegate.h; sourceTree = ""; }; 88 | 18D36762162772B600B6BACE /* SCAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SCAppDelegate.m; sourceTree = ""; }; 89 | 18D9AFA916BD4E98008601C0 /* SCStringsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCStringsController.h; sourceTree = ""; }; 90 | 18D9AFAA16BD4E98008601C0 /* SCStringsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCStringsController.m; sourceTree = ""; }; 91 | 18F0D44F16BECE6800CC0264 /* XcodeEditor.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = XcodeEditor.framework; sourceTree = ""; }; 92 | /* End PBXFileReference section */ 93 | 94 | /* Begin PBXFrameworksBuildPhase section */ 95 | 18D36748162772B500B6BACE /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | 1831CBE4162809E0004B6DD4 /* QuartzCore.framework in Frameworks */, 100 | 18D36750162772B500B6BACE /* Cocoa.framework in Frameworks */, 101 | 18F0D45316BECE8300CC0264 /* XcodeEditor.framework in Frameworks */, 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | /* End PBXFrameworksBuildPhase section */ 106 | 107 | /* Begin PBXGroup section */ 108 | 1843F44B1650EB9900CEDBEF /* Resources */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 1852D07716D01F8D00574822 /* icon.icns */, 112 | 1843F44C1650EB9900CEDBEF /* icon-export.png */, 113 | 1843F44E1650EB9900CEDBEF /* icon-open.png */, 114 | 1843F44F1650EB9900CEDBEF /* icon-save.png */, 115 | ); 116 | path = Resources; 117 | sourceTree = ""; 118 | }; 119 | 18D1859016C050E00082E573 /* Others */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 18D1859116C050E00082E573 /* NSData+SCAdditions.h */, 123 | 18D1859216C050E00082E573 /* NSData+SCAdditions.m */, 124 | 18D1859316C050E00082E573 /* NSString+SCAdditions.h */, 125 | 18D1859416C050E00082E573 /* NSString+SCAdditions.m */, 126 | 18D1859516C050E00082E573 /* OrderedDictionary.h */, 127 | 18D1859616C050E00082E573 /* OrderedDictionary.m */, 128 | ); 129 | path = Others; 130 | sourceTree = ""; 131 | }; 132 | 18D1859716C050E00082E573 /* Readers&Writers */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 18D1859816C050E00082E573 /* SCCSVWriter.h */, 136 | 18D1859916C050E00082E573 /* SCCSVWriter.m */, 137 | 18D1859A16C050E00082E573 /* SCReader.h */, 138 | 18D1859B16C050E00082E573 /* SCReader.m */, 139 | 18D1859C16C050E00082E573 /* SCStringsWriter.h */, 140 | 18D1859D16C050E00082E573 /* SCStringsWriter.m */, 141 | ); 142 | path = "Readers&Writers"; 143 | sourceTree = ""; 144 | }; 145 | 18D1859E16C050E00082E573 /* Views */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 18D1859F16C050E00082E573 /* SCExportAccessoryView.h */, 149 | 18D185A016C050E00082E573 /* SCExportAccessoryView.m */, 150 | 18D185A116C050E00082E573 /* SCExportAccessoryView.xib */, 151 | 18D185A216C050E00082E573 /* SCOpenAccessoryView.h */, 152 | 18D185A316C050E00082E573 /* SCOpenAccessoryView.m */, 153 | 18D185A416C050E00082E573 /* SCOpenAccessoryView.xib */, 154 | ); 155 | path = Views; 156 | sourceTree = ""; 157 | }; 158 | 18D36740162772B500B6BACE = { 159 | isa = PBXGroup; 160 | children = ( 161 | 18D36755162772B500B6BACE /* SCStringsUtility */, 162 | 18D3674E162772B500B6BACE /* Frameworks */, 163 | 18D3674C162772B500B6BACE /* Products */, 164 | ); 165 | sourceTree = ""; 166 | }; 167 | 18D3674C162772B500B6BACE /* Products */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 18D3674B162772B500B6BACE /* SCStringsUtility.app */, 171 | ); 172 | name = Products; 173 | sourceTree = ""; 174 | }; 175 | 18D3674E162772B500B6BACE /* Frameworks */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | 18F0D44F16BECE6800CC0264 /* XcodeEditor.framework */, 179 | 18D36752162772B500B6BACE /* AppKit.framework */, 180 | 18D3674F162772B500B6BACE /* Cocoa.framework */, 181 | 18D36753162772B500B6BACE /* CoreData.framework */, 182 | 18D36754162772B500B6BACE /* Foundation.framework */, 183 | 1831CBE3162809E0004B6DD4 /* QuartzCore.framework */, 184 | ); 185 | name = Frameworks; 186 | sourceTree = ""; 187 | }; 188 | 18D36755162772B500B6BACE /* SCStringsUtility */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 186758CC1654C83F00BEE1E1 /* MainMenu.xib */, 192 | 18D36761162772B600B6BACE /* SCAppDelegate.h */, 193 | 18D36762162772B600B6BACE /* SCAppDelegate.m */, 194 | 1831CBDE1628086A004B6DD4 /* SCRootViewController.h */, 195 | 1831CBDF1628086A004B6DD4 /* SCRootViewController.m */, 196 | 1831CBE01628086A004B6DD4 /* SCRootViewController.xib */, 197 | 18D9AFA916BD4E98008601C0 /* SCStringsController.h */, 198 | 18D9AFAA16BD4E98008601C0 /* SCStringsController.m */, 199 | 18D1859E16C050E00082E573 /* Views */, 200 | 18D1859716C050E00082E573 /* Readers&Writers */, 201 | 18D1859016C050E00082E573 /* Others */, 202 | 18D36756162772B500B6BACE /* Supporting Files */, 203 | ); 204 | path = SCStringsUtility; 205 | sourceTree = ""; 206 | }; 207 | 18D36756162772B500B6BACE /* Supporting Files */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | 1843F44B1650EB9900CEDBEF /* Resources */, 211 | 18D36757162772B500B6BACE /* SCStringsUtility-Info.plist */, 212 | 18D3675B162772B500B6BACE /* main.m */, 213 | 18D3675D162772B600B6BACE /* SCStringsUtility-Prefix.pch */, 214 | 18D3675E162772B600B6BACE /* Credits.rtf */, 215 | ); 216 | name = "Supporting Files"; 217 | sourceTree = ""; 218 | }; 219 | /* End PBXGroup section */ 220 | 221 | /* Begin PBXNativeTarget section */ 222 | 18D3674A162772B500B6BACE /* SCStringsUtility */ = { 223 | isa = PBXNativeTarget; 224 | buildConfigurationList = 18D36769162772B600B6BACE /* Build configuration list for PBXNativeTarget "SCStringsUtility" */; 225 | buildPhases = ( 226 | 18D36747162772B500B6BACE /* Sources */, 227 | 18D36748162772B500B6BACE /* Frameworks */, 228 | 18D36749162772B500B6BACE /* Resources */, 229 | 1878321716BECEC400A8C760 /* CopyFiles */, 230 | ); 231 | buildRules = ( 232 | ); 233 | dependencies = ( 234 | ); 235 | name = SCStringsUtility; 236 | productName = SCConverter; 237 | productReference = 18D3674B162772B500B6BACE /* SCStringsUtility.app */; 238 | productType = "com.apple.product-type.application"; 239 | }; 240 | /* End PBXNativeTarget section */ 241 | 242 | /* Begin PBXProject section */ 243 | 18D36742162772B500B6BACE /* Project object */ = { 244 | isa = PBXProject; 245 | attributes = { 246 | CLASSPREFIX = SC; 247 | LastUpgradeCheck = 0720; 248 | ORGANIZATIONNAME = "Stefan Ceriu"; 249 | }; 250 | buildConfigurationList = 18D36745162772B500B6BACE /* Build configuration list for PBXProject "SCStringsUtility" */; 251 | compatibilityVersion = "Xcode 3.2"; 252 | developmentRegion = English; 253 | hasScannedForEncodings = 0; 254 | knownRegions = ( 255 | en, 256 | ); 257 | mainGroup = 18D36740162772B500B6BACE; 258 | productRefGroup = 18D3674C162772B500B6BACE /* Products */; 259 | projectDirPath = ""; 260 | projectRoot = ""; 261 | targets = ( 262 | 18D3674A162772B500B6BACE /* SCStringsUtility */, 263 | ); 264 | }; 265 | /* End PBXProject section */ 266 | 267 | /* Begin PBXResourcesBuildPhase section */ 268 | 18D36749162772B500B6BACE /* Resources */ = { 269 | isa = PBXResourcesBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | 18D36760162772B600B6BACE /* Credits.rtf in Resources */, 273 | 1831CBE21628086A004B6DD4 /* SCRootViewController.xib in Resources */, 274 | 1843F4501650EB9900CEDBEF /* icon-export.png in Resources */, 275 | 1843F4521650EB9900CEDBEF /* icon-open.png in Resources */, 276 | 1843F4531650EB9900CEDBEF /* icon-save.png in Resources */, 277 | 186758CD1654C83F00BEE1E1 /* MainMenu.xib in Resources */, 278 | 18D185AC16C050E00082E573 /* SCExportAccessoryView.xib in Resources */, 279 | 18D185AE16C050E00082E573 /* SCOpenAccessoryView.xib in Resources */, 280 | 1852D07816D01F8D00574822 /* icon.icns in Resources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | /* End PBXResourcesBuildPhase section */ 285 | 286 | /* Begin PBXSourcesBuildPhase section */ 287 | 18D36747162772B500B6BACE /* Sources */ = { 288 | isa = PBXSourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | 18D3675C162772B500B6BACE /* main.m in Sources */, 292 | 18D36763162772B600B6BACE /* SCAppDelegate.m in Sources */, 293 | 1831CBE11628086A004B6DD4 /* SCRootViewController.m in Sources */, 294 | 18D9AFAB16BD4E98008601C0 /* SCStringsController.m in Sources */, 295 | 18D185A516C050E00082E573 /* NSData+SCAdditions.m in Sources */, 296 | 18D185A616C050E00082E573 /* NSString+SCAdditions.m in Sources */, 297 | 18D185A716C050E00082E573 /* OrderedDictionary.m in Sources */, 298 | 18D185A816C050E00082E573 /* SCCSVWriter.m in Sources */, 299 | 18D185A916C050E00082E573 /* SCReader.m in Sources */, 300 | 18D185AA16C050E00082E573 /* SCStringsWriter.m in Sources */, 301 | 18D185AB16C050E00082E573 /* SCExportAccessoryView.m in Sources */, 302 | 18D185AD16C050E00082E573 /* SCOpenAccessoryView.m in Sources */, 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | }; 306 | /* End PBXSourcesBuildPhase section */ 307 | 308 | /* Begin PBXVariantGroup section */ 309 | 18D3675E162772B600B6BACE /* Credits.rtf */ = { 310 | isa = PBXVariantGroup; 311 | children = ( 312 | 18D3675F162772B600B6BACE /* en */, 313 | ); 314 | name = Credits.rtf; 315 | sourceTree = ""; 316 | }; 317 | /* End PBXVariantGroup section */ 318 | 319 | /* Begin XCBuildConfiguration section */ 320 | 18D36767162772B600B6BACE /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ALWAYS_SEARCH_USER_PATHS = NO; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_ENABLE_OBJC_ARC = YES; 326 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 327 | COPY_PHASE_STRIP = NO; 328 | ENABLE_TESTABILITY = YES; 329 | GCC_C_LANGUAGE_STANDARD = gnu99; 330 | GCC_DYNAMIC_NO_PIC = NO; 331 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 332 | GCC_OPTIMIZATION_LEVEL = 0; 333 | GCC_PREPROCESSOR_DEFINITIONS = ( 334 | "DEBUG=1", 335 | "$(inherited)", 336 | ); 337 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 338 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 339 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 340 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 341 | GCC_WARN_UNUSED_VARIABLE = YES; 342 | MACOSX_DEPLOYMENT_TARGET = 10.7; 343 | ONLY_ACTIVE_ARCH = YES; 344 | SDKROOT = macosx; 345 | }; 346 | name = Debug; 347 | }; 348 | 18D36768162772B600B6BACE /* Release */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | ALWAYS_SEARCH_USER_PATHS = NO; 352 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 353 | CLANG_ENABLE_OBJC_ARC = YES; 354 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 355 | COPY_PHASE_STRIP = YES; 356 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 357 | GCC_C_LANGUAGE_STANDARD = gnu99; 358 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 359 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 360 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 361 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | MACOSX_DEPLOYMENT_TARGET = 10.7; 364 | SDKROOT = macosx; 365 | }; 366 | name = Release; 367 | }; 368 | 18D3676A162772B600B6BACE /* Debug */ = { 369 | isa = XCBuildConfiguration; 370 | buildSettings = { 371 | COMBINE_HIDPI_IMAGES = YES; 372 | FRAMEWORK_SEARCH_PATHS = ( 373 | "$(inherited)", 374 | "\"$(SRCROOT)\"/**", 375 | "\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks\"", 376 | "\"$(SRCROOT)\"", 377 | ); 378 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 379 | GCC_PREFIX_HEADER = "SCStringsUtility/SCStringsUtility-Prefix.pch"; 380 | INFOPLIST_FILE = "SCStringsUtility/SCStringsUtility-Info.plist"; 381 | PRODUCT_BUNDLE_IDENTIFIER = "com.stefanceriu.${PRODUCT_NAME:rfc1034identifier}"; 382 | PRODUCT_NAME = SCStringsUtility; 383 | WRAPPER_EXTENSION = app; 384 | }; 385 | name = Debug; 386 | }; 387 | 18D3676B162772B600B6BACE /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | COMBINE_HIDPI_IMAGES = YES; 391 | FRAMEWORK_SEARCH_PATHS = ( 392 | "$(inherited)", 393 | "\"$(SRCROOT)\"/**", 394 | "\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks\"", 395 | "\"$(SRCROOT)\"", 396 | ); 397 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 398 | GCC_PREFIX_HEADER = "SCStringsUtility/SCStringsUtility-Prefix.pch"; 399 | INFOPLIST_FILE = "SCStringsUtility/SCStringsUtility-Info.plist"; 400 | PRODUCT_BUNDLE_IDENTIFIER = "com.stefanceriu.${PRODUCT_NAME:rfc1034identifier}"; 401 | PRODUCT_NAME = SCStringsUtility; 402 | WRAPPER_EXTENSION = app; 403 | }; 404 | name = Release; 405 | }; 406 | /* End XCBuildConfiguration section */ 407 | 408 | /* Begin XCConfigurationList section */ 409 | 18D36745162772B500B6BACE /* Build configuration list for PBXProject "SCStringsUtility" */ = { 410 | isa = XCConfigurationList; 411 | buildConfigurations = ( 412 | 18D36767162772B600B6BACE /* Debug */, 413 | 18D36768162772B600B6BACE /* Release */, 414 | ); 415 | defaultConfigurationIsVisible = 0; 416 | defaultConfigurationName = Release; 417 | }; 418 | 18D36769162772B600B6BACE /* Build configuration list for PBXNativeTarget "SCStringsUtility" */ = { 419 | isa = XCConfigurationList; 420 | buildConfigurations = ( 421 | 18D3676A162772B600B6BACE /* Debug */, 422 | 18D3676B162772B600B6BACE /* Release */, 423 | ); 424 | defaultConfigurationIsVisible = 0; 425 | defaultConfigurationName = Release; 426 | }; 427 | /* End XCConfigurationList section */ 428 | }; 429 | rootObject = 18D36742162772B500B6BACE /* Project object */; 430 | } 431 | -------------------------------------------------------------------------------- /SCStringsUtility/SCStringsController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCStringsDataSource.m 3 | // SCStringsUtility 4 | // 5 | // Created by Stefan Ceriu on 2/2/13. 6 | // Copyright (c) 2013 Stefan Ceriu. All rights reserved. 7 | // 8 | 9 | #import "SCStringsController.h" 10 | #import "SCReader.h" 11 | #import "NSString+SCAdditions.h" 12 | #import "SCCSVWriter.h" 13 | #import "SCStringsWriter.h" 14 | #import "OrderedDictionary.h" 15 | 16 | #import 17 | #import 18 | #import 19 | 20 | static NSString *kKey = @"Key"; 21 | static NSString *kKeyPath = @"Path"; 22 | 23 | static NSString *kKeyComment = @"Comment"; 24 | static NSString *kKeyImported = @"Imported"; 25 | static NSString *kKeyLanguage = @"Language"; 26 | static NSString *kKeyLocalizable = @"Localizable"; 27 | static NSString *kKeyStringsFile = @"Localizable.strings"; 28 | 29 | @interface SCStringsController () 30 | 31 | @property (nonatomic, strong) XCProject *project; 32 | @property (nonatomic, strong) NSString *sourceFilePath; 33 | 34 | @property (nonatomic, assign) SCFileType sourceType; 35 | 36 | @property (nonatomic, strong) NSMutableDictionary *translationFiles; 37 | @property (nonatomic, strong) OrderedDictionary *filteredTranslationsDictionary; 38 | @property (nonatomic, strong) OrderedDictionary *translationsDictionary; 39 | 40 | @end 41 | 42 | @implementation SCStringsController 43 | 44 | - (id)init 45 | { 46 | if(self = [super init]) 47 | { 48 | self.undoManager = [[NSUndoManager alloc] init]; 49 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(outlineViewSelectionDidChange:) name:NSOutlineViewSelectionDidChangeNotification object:nil]; 50 | } 51 | 52 | return self; 53 | } 54 | 55 | - (void)dealloc 56 | { 57 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 58 | } 59 | 60 | - (void)reset 61 | { 62 | self.translationsDictionary = [OrderedDictionary dictionary]; 63 | self.translationFiles = [NSMutableDictionary dictionary]; 64 | self.sourceType = SCFileTypeInvalid; 65 | 66 | [self.outlineView reloadData]; 67 | 68 | NSTableColumn* column = [[self.tableView tableColumns] lastObject]; 69 | while (column) 70 | { 71 | [self.tableView removeTableColumn:column]; 72 | column = [[self.tableView tableColumns] lastObject]; 73 | } 74 | } 75 | 76 | - (void)save:(void (^)(void))success failure:(void (^)(NSError *error))failure 77 | { 78 | switch (self.sourceType) { 79 | case SCFileTypeInvalid: 80 | { 81 | return; 82 | } 83 | case SCFileTypeXcodeProject: 84 | { 85 | [self generateStringFilesAtPath:nil success:success failure:failure]; 86 | break; 87 | } 88 | case SCFileTypeCSV: 89 | { 90 | [self generateCSVAtPath:nil includeComments:YES useKeyForEmptyTranslations:NO success:success failure:failure]; 91 | break; 92 | } 93 | case SCFileTypeXML: 94 | { 95 | [self generateXMLFileAtPath:nil includeComments:YES useKeyForEmptyTranslations:NO success:success failure:failure]; 96 | break; 97 | } 98 | } 99 | } 100 | 101 | #pragma mark - Importers 102 | 103 | - (void)importCSVFileAtPath:(NSString *)path success:(void (^)(void))success failure:(void (^)(NSError *))failure 104 | { 105 | self.sourceFilePath = path; 106 | self.project = nil; 107 | self.sourceType = SCFileTypeCSV; 108 | 109 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 110 | 111 | SCReader * reader = [[SCReader alloc] initWithPath:self.sourceFilePath]; 112 | 113 | NSArray *headers = [[reader readTrimmedLine] componentsSeparatedByString:@","]; 114 | headers = [headers subarrayWithRange:NSMakeRange(1, headers.count-1)]; 115 | 116 | self.translationFiles = [NSMutableDictionary dictionaryWithObject:[NSMutableArray array] forKey:kKeyImported]; 117 | for(NSString *header in headers) 118 | { 119 | if([header isEqualToString:kKeyComment]) continue; 120 | 121 | [[self.translationFiles objectForKey:kKeyImported] addObject:[NSDictionary dictionaryWithObject:header forKey:kKeyLanguage]]; 122 | } 123 | 124 | self.translationsDictionary = [OrderedDictionary dictionary]; 125 | 126 | [reader enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) { 127 | line = [line stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]]; 128 | 129 | NSArray *components = [line componentsSeparatedByString:@","]; 130 | if([line rangeOfString:@"\""].location != NSNotFound) //line contains a comma 131 | { 132 | NSMutableArray *mutableComponents = [components mutableCopy]; 133 | 134 | BOOL commaPresent = YES; 135 | 136 | //Try merging together components from the same string 137 | while(commaPresent) 138 | { 139 | commaPresent = NO; 140 | 141 | for(int i=0;i 0) 440 | { 441 | [self.translationsDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 442 | 443 | __block BOOL souldKeepLine = NO; 444 | 445 | if([[key lowercaseString] rangeOfString:searchString].location != NSNotFound) 446 | { 447 | souldKeepLine = YES; 448 | } 449 | 450 | if(!searchOnlyKeys) 451 | { 452 | [obj enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 453 | 454 | if([[obj lowercaseString] rangeOfString:searchString].location != NSNotFound) 455 | { 456 | souldKeepLine = YES; 457 | } 458 | }]; 459 | } 460 | 461 | if(!souldKeepLine) 462 | [self.filteredTranslationsDictionary removeObjectForKey:key]; 463 | }]; 464 | } 465 | } 466 | 467 | #pragma mark - NSOutlineView DataSource&Delegate 468 | 469 | - (void)outlineViewSelectionDidChange:(NSNotification*)sender 470 | { 471 | if(![sender.object isEqual:self.outlineView]) return; 472 | 473 | if(self.outlineView.selectedRow == -1) return; 474 | 475 | id selectedItem = [self.outlineView itemAtRow:self.outlineView.selectedRow]; 476 | 477 | NSTableColumn* column = [[self.tableView tableColumns] lastObject]; 478 | while (column) 479 | { 480 | [self.tableView removeTableColumn:column]; 481 | column = [[self.tableView tableColumns] lastObject]; 482 | } 483 | 484 | column = [[NSTableColumn alloc] initWithIdentifier:kKey]; 485 | [[column headerCell] setStringValue:kKey]; 486 | [self.tableView addTableColumn:column]; 487 | 488 | if([selectedItem isKindOfClass:[NSArray class]]) 489 | { 490 | column = [[NSTableColumn alloc] initWithIdentifier:kKeyComment]; 491 | [[column headerCell] setStringValue:kKeyComment]; 492 | [self.tableView addTableColumn:column]; 493 | 494 | for(NSDictionary *dict in selectedItem) 495 | { 496 | column = [[NSTableColumn alloc] initWithIdentifier:[dict objectForKey:kKeyLanguage]]; 497 | [[column headerCell] setStringValue:[dict objectForKey:kKeyLanguage]]; 498 | [self.tableView addTableColumn:column]; 499 | 500 | SCReader *reader = [[SCReader alloc] initWithPath:[dict objectForKey:kKeyPath]]; 501 | NSString *comment, *key, *translation; 502 | while ([reader getNextComment:&comment key:&key translation:&translation]) { 503 | [[self.translationsDictionary objectForKey:key] setObject:translation forKey:column.identifier]; 504 | } 505 | } 506 | } 507 | else 508 | { 509 | column = [[NSTableColumn alloc] initWithIdentifier:[selectedItem objectForKey:kKeyLanguage]]; 510 | [[column headerCell] setStringValue:[selectedItem objectForKey:kKeyLanguage]]; 511 | [self.tableView addTableColumn:column]; 512 | 513 | SCReader *reader = [[SCReader alloc] initWithPath:[selectedItem objectForKey:kKeyPath]]; 514 | 515 | NSString *comment, *key, *translation; 516 | while ([reader getNextComment:&comment key:&key translation:&translation]) { 517 | [[self.translationsDictionary objectForKey:key] setObject:translation forKey:column.identifier]; 518 | } 519 | } 520 | 521 | for(NSTableColumn *column in self.tableView.tableColumns) 522 | [column setWidth:self.tableView.bounds.size.width/self.tableView.tableColumns.count]; 523 | 524 | [self.tableView reloadData]; 525 | } 526 | 527 | - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item 528 | { 529 | return (item == nil) ? self.translationFiles.allKeys.count : [item count]; 530 | } 531 | 532 | 533 | - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item 534 | { 535 | return [item isKindOfClass:[NSArray class]]; 536 | } 537 | 538 | 539 | - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item 540 | { 541 | if(item == nil) 542 | { 543 | NSString *key = [self.translationFiles.allKeys objectAtIndex:index]; 544 | return [self.translationFiles objectForKey:key]; 545 | } 546 | 547 | return [item objectAtIndex:index]; 548 | } 549 | 550 | 551 | - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item 552 | { 553 | if([item isKindOfClass:[NSArray class]]) 554 | return [[self.translationFiles allKeysForObject:item] lastObject]; 555 | else 556 | return [item objectForKey:kKeyLanguage]; 557 | } 558 | 559 | 560 | 561 | #pragma mark - NSTableView DataSource&Delegate 562 | 563 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView 564 | { 565 | return self.filteredTranslationsDictionary.allKeys.count; 566 | } 567 | 568 | - (void)tableView:(NSTableView *)tableView willDisplayCell:(NSTextFieldCell*)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 569 | { 570 | [cell setWraps:YES]; 571 | } 572 | 573 | - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 574 | { 575 | NSString *key = [self.filteredTranslationsDictionary keyAtIndex:row]; 576 | 577 | if([tableColumn.identifier isEqualToString:kKey]) 578 | return key; 579 | else 580 | return [[self.filteredTranslationsDictionary objectForKey:key] objectForKey:tableColumn.identifier]; 581 | } 582 | 583 | - (CGFloat)tableView:(NSTableView *)aTableView heightOfRow:(NSInteger)row 584 | { 585 | NSString *rootKey = [self.filteredTranslationsDictionary keyAtIndex:row]; 586 | NSTableColumn *tableColoumn = [aTableView tableColumnWithIdentifier:kKey]; 587 | 588 | CGFloat rowHeight = 5; 589 | if (tableColoumn) 590 | { 591 | NSRect myRect = NSMakeRect(0, 0, [tableColoumn width], CGFLOAT_MAX); 592 | NSCell *dataCell = [tableColoumn dataCell]; 593 | [dataCell setWraps:YES]; 594 | 595 | [dataCell setStringValue:rootKey]; 596 | rowHeight = MAX(rowHeight, [dataCell cellSizeForBounds:myRect].height); 597 | 598 | for(NSString *key in [[self.filteredTranslationsDictionary objectForKey:rootKey] allKeys]) 599 | { 600 | [dataCell setStringValue:[[self.filteredTranslationsDictionary objectForKey:rootKey] objectForKey:key]]; 601 | rowHeight = MAX(rowHeight, [dataCell cellSizeForBounds:myRect].height); 602 | } 603 | } 604 | return rowHeight; 605 | } 606 | 607 | - (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 608 | { 609 | if([tableColumn.identifier isEqualToString:kKey]) return NO; 610 | return YES; 611 | } 612 | 613 | - (void)tableView:(NSTableView *)someTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 614 | { 615 | NSString *key = [self.filteredTranslationsDictionary keyAtIndex:row]; 616 | 617 | NSString *translation = [[self.translationsDictionary objectForKey:key] objectForKey:tableColumn.identifier]; 618 | if(!translation) translation = @""; 619 | [[self.undoManager prepareWithInvocationTarget:self] tableView:someTableView setObjectValue:translation forTableColumn:tableColumn row:row]; 620 | 621 | [[self.translationsDictionary objectForKey:key] setObject:object forKey:tableColumn.identifier]; 622 | [someTableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:row] columnIndexes:[NSIndexSet indexSetWithIndex:[someTableView.tableColumns indexOfObject:tableColumn]]]; 623 | } 624 | 625 | @end 626 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . --------------------------------------------------------------------------------