├── .gitmodules ├── CloudNotes.xcodeproj └── project.pbxproj ├── CloudNotes ├── Base.lproj │ └── MainMenu.xib ├── CloudNotes-Info.plist ├── CloudNotes-Prefix.pch ├── Images.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon-128.png │ │ ├── icon-128@2x.png │ │ ├── icon-16.png │ │ ├── icon-16@2x.png │ │ ├── icon-256.png │ │ ├── icon-256@2x.png │ │ ├── icon-32.png │ │ ├── icon-32@2x.png │ │ ├── icon-512.png │ │ └── icon-512@2x.png ├── NSDictionary+HandleNull.h ├── NSDictionary+HandleNull.m ├── NSSplitView+SaveLayout.h ├── NSSplitView+SaveLayout.m ├── OCAPIClient.h ├── OCAPIClient.m ├── OCAppDelegate.h ├── OCAppDelegate.m ├── OCDateTransformer.h ├── OCDateTransformer.m ├── OCEditorSettings.h ├── OCEditorSettings.m ├── OCNote.h ├── OCNote.m ├── OCNotesHelper.h ├── OCNotesHelper.m ├── OCNotesViewController.h ├── OCNotesViewController.m ├── OCNotesViewController.xib ├── OCNotesWindowController.h ├── OCNotesWindowController.m ├── OCNotesWindowController.xib ├── OCPrefsWindowController.h ├── OCPrefsWindowController.m ├── OCPrefsWindowController.xib ├── en.lproj │ ├── Credits.rtf │ └── InfoPlist.strings └── main.m ├── CloudNotesTests ├── CloudNotesTests-Info.plist ├── CloudNotesTests.m └── en.lproj │ └── InfoPlist.strings ├── LICENSE ├── README.md └── svg └── cloudnotes.svg /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "AFNetworking"] 2 | path = AFNetworking 3 | url = https://github.com/AFNetworking/AFNetworking.git 4 | [submodule "PDKeychainBindingsController"] 5 | path = PDKeychainBindingsController 6 | url = https://github.com/carlbrown/PDKeychainBindingsController.git 7 | [submodule "FMDB"] 8 | path = FMDB 9 | url = https://github.com/ccgus/fmdb.git 10 | [submodule "FCModel"] 11 | path = FCModel 12 | url = https://github.com/marcoarment/FCModel.git 13 | -------------------------------------------------------------------------------- /CloudNotes.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D01D201B18FF2DE2008407C4 /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = D01D201118FF2DE2008407C4 /* FMDatabase.m */; }; 11 | D01D201C18FF2DE2008407C4 /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = D01D201318FF2DE2008407C4 /* FMDatabaseAdditions.m */; }; 12 | D01D201D18FF2DE2008407C4 /* FMDatabasePool.m in Sources */ = {isa = PBXBuildFile; fileRef = D01D201518FF2DE2008407C4 /* FMDatabasePool.m */; }; 13 | D01D201E18FF2DE2008407C4 /* FMDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D01D201718FF2DE2008407C4 /* FMDatabaseQueue.m */; }; 14 | D01D201F18FF2DE2008407C4 /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = D01D201A18FF2DE2008407C4 /* FMResultSet.m */; }; 15 | D01D202718FF2DED008407C4 /* FCModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D01D202218FF2DED008407C4 /* FCModel.m */; }; 16 | D01D202818FF2DED008407C4 /* FCModelCachedObject.m in Sources */ = {isa = PBXBuildFile; fileRef = D01D202418FF2DED008407C4 /* FCModelCachedObject.m */; }; 17 | D01D202918FF2DED008407C4 /* FCModelDatabaseQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D01D202618FF2DED008407C4 /* FCModelDatabaseQueue.m */; }; 18 | D01D202C18FF3200008407C4 /* OCNote.m in Sources */ = {isa = PBXBuildFile; fileRef = D01D202A18FF3200008407C4 /* OCNote.m */; }; 19 | D01D202E18FF34C2008407C4 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D01D202D18FF34C2008407C4 /* libsqlite3.dylib */; }; 20 | D022578E189F20F90038232A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D022578D189F20F90038232A /* Cocoa.framework */; }; 21 | D0225798189F20F90038232A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D0225796189F20F90038232A /* InfoPlist.strings */; }; 22 | D022579A189F20F90038232A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D0225799189F20F90038232A /* main.m */; }; 23 | D022579E189F20F90038232A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = D022579C189F20F90038232A /* Credits.rtf */; }; 24 | D02257A1189F20F90038232A /* OCAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257A0189F20F90038232A /* OCAppDelegate.m */; }; 25 | D02257A4189F20F90038232A /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D02257A2189F20F90038232A /* MainMenu.xib */; }; 26 | D02257A9189F20F90038232A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D02257A8189F20F90038232A /* Images.xcassets */; }; 27 | D02257B0189F20F90038232A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D02257AF189F20F90038232A /* XCTest.framework */; }; 28 | D02257B1189F20F90038232A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D022578D189F20F90038232A /* Cocoa.framework */; }; 29 | D02257B9189F20F90038232A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D02257B7189F20F90038232A /* InfoPlist.strings */; }; 30 | D02257BB189F20F90038232A /* CloudNotesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257BA189F20F90038232A /* CloudNotesTests.m */; }; 31 | D02257C718A1C3EF0038232A /* OCNotesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257C518A1C3EF0038232A /* OCNotesViewController.m */; }; 32 | D02257C818A1C3EF0038232A /* OCNotesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D02257C618A1C3EF0038232A /* OCNotesViewController.xib */; }; 33 | D02257D218A1D2F90038232A /* OCAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257CD18A1D2F90038232A /* OCAPIClient.m */; }; 34 | D02257D318A1D2F90038232A /* OCNotesHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257CF18A1D2F90038232A /* OCNotesHelper.m */; }; 35 | D02257E818A1D3640038232A /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257D618A1D3640038232A /* AFHTTPRequestOperation.m */; }; 36 | D02257E918A1D3640038232A /* AFHTTPRequestOperationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257D818A1D3640038232A /* AFHTTPRequestOperationManager.m */; }; 37 | D02257EA18A1D3640038232A /* AFHTTPSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257DA18A1D3640038232A /* AFHTTPSessionManager.m */; }; 38 | D02257EB18A1D3640038232A /* AFNetworkReachabilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257DD18A1D3640038232A /* AFNetworkReachabilityManager.m */; }; 39 | D02257EC18A1D3640038232A /* AFSecurityPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257DF18A1D3640038232A /* AFSecurityPolicy.m */; }; 40 | D02257ED18A1D3640038232A /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257E118A1D3640038232A /* AFURLConnectionOperation.m */; }; 41 | D02257EE18A1D3640038232A /* AFURLRequestSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257E318A1D3640038232A /* AFURLRequestSerialization.m */; }; 42 | D02257EF18A1D3640038232A /* AFURLResponseSerialization.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257E518A1D3640038232A /* AFURLResponseSerialization.m */; }; 43 | D02257F018A1D3640038232A /* AFURLSessionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257E718A1D3640038232A /* AFURLSessionManager.m */; }; 44 | D02257F618A1D9AA0038232A /* NSDictionary+HandleNull.m in Sources */ = {isa = PBXBuildFile; fileRef = D02257F518A1D9AA0038232A /* NSDictionary+HandleNull.m */; }; 45 | D02257F818A1DB6E0038232A /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D02257F718A1DB6E0038232A /* SystemConfiguration.framework */; }; 46 | D02257FA18A1DB7A0038232A /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D02257F918A1DB7A0038232A /* Security.framework */; }; 47 | D02257FC18A1DB840038232A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D02257FB18A1DB840038232A /* CoreServices.framework */; }; 48 | D022580218A5B4260038232A /* NSSplitView+SaveLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = D022580118A5B4260038232A /* NSSplitView+SaveLayout.m */; }; 49 | D022581018A6CC950038232A /* OCPrefsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = D022580E18A6CC950038232A /* OCPrefsWindowController.m */; }; 50 | D022581118A6CC950038232A /* OCPrefsWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D022580F18A6CC950038232A /* OCPrefsWindowController.xib */; }; 51 | D022581718A6F2CD0038232A /* PDKeychainBindings.m in Sources */ = {isa = PBXBuildFile; fileRef = D022581418A6F2CD0038232A /* PDKeychainBindings.m */; }; 52 | D022581818A6F2CD0038232A /* PDKeychainBindingsController.m in Sources */ = {isa = PBXBuildFile; fileRef = D022581618A6F2CD0038232A /* PDKeychainBindingsController.m */; }; 53 | D022581B18A70E010038232A /* OCDateTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = D022581A18A70E010038232A /* OCDateTransformer.m */; }; 54 | D0EB38E818B0577A00379F3F /* OCEditorSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EB38E718B0577A00379F3F /* OCEditorSettings.m */; }; 55 | D0EB38EF18B175D900379F3F /* OCNotesWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = D0EB38ED18B175D900379F3F /* OCNotesWindowController.m */; }; 56 | D0EB38F018B175D900379F3F /* OCNotesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D0EB38EE18B175D900379F3F /* OCNotesWindowController.xib */; }; 57 | /* End PBXBuildFile section */ 58 | 59 | /* Begin PBXContainerItemProxy section */ 60 | D02257B2189F20F90038232A /* PBXContainerItemProxy */ = { 61 | isa = PBXContainerItemProxy; 62 | containerPortal = D0225782189F20F90038232A /* Project object */; 63 | proxyType = 1; 64 | remoteGlobalIDString = D0225789189F20F90038232A; 65 | remoteInfo = CloudNotes; 66 | }; 67 | /* End PBXContainerItemProxy section */ 68 | 69 | /* Begin PBXFileReference section */ 70 | D01D201018FF2DE2008407C4 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = ""; }; 71 | D01D201118FF2DE2008407C4 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = ""; }; 72 | D01D201218FF2DE2008407C4 /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseAdditions.h; sourceTree = ""; }; 73 | D01D201318FF2DE2008407C4 /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseAdditions.m; sourceTree = ""; }; 74 | D01D201418FF2DE2008407C4 /* FMDatabasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabasePool.h; sourceTree = ""; }; 75 | D01D201518FF2DE2008407C4 /* FMDatabasePool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabasePool.m; sourceTree = ""; }; 76 | D01D201618FF2DE2008407C4 /* FMDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseQueue.h; sourceTree = ""; }; 77 | D01D201718FF2DE2008407C4 /* FMDatabaseQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseQueue.m; sourceTree = ""; }; 78 | D01D201818FF2DE2008407C4 /* FMDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDB.h; sourceTree = ""; }; 79 | D01D201918FF2DE2008407C4 /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMResultSet.h; sourceTree = ""; }; 80 | D01D201A18FF2DE2008407C4 /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMResultSet.m; sourceTree = ""; }; 81 | D01D202118FF2DED008407C4 /* FCModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCModel.h; sourceTree = ""; }; 82 | D01D202218FF2DED008407C4 /* FCModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FCModel.m; sourceTree = ""; }; 83 | D01D202318FF2DED008407C4 /* FCModelCachedObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCModelCachedObject.h; sourceTree = ""; }; 84 | D01D202418FF2DED008407C4 /* FCModelCachedObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FCModelCachedObject.m; sourceTree = ""; }; 85 | D01D202518FF2DED008407C4 /* FCModelDatabaseQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FCModelDatabaseQueue.h; sourceTree = ""; }; 86 | D01D202618FF2DED008407C4 /* FCModelDatabaseQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FCModelDatabaseQueue.m; sourceTree = ""; }; 87 | D01D202A18FF3200008407C4 /* OCNote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCNote.m; sourceTree = ""; }; 88 | D01D202B18FF3200008407C4 /* OCNote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCNote.h; sourceTree = ""; }; 89 | D01D202D18FF34C2008407C4 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; 90 | D022578A189F20F90038232A /* CloudNotes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CloudNotes.app; sourceTree = BUILT_PRODUCTS_DIR; }; 91 | D022578D189F20F90038232A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 92 | D0225790189F20F90038232A /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 93 | D0225792189F20F90038232A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 94 | D0225795189F20F90038232A /* CloudNotes-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CloudNotes-Info.plist"; sourceTree = ""; }; 95 | D0225797189F20F90038232A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 96 | D0225799189F20F90038232A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97 | D022579B189F20F90038232A /* CloudNotes-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CloudNotes-Prefix.pch"; sourceTree = ""; }; 98 | D022579D189F20F90038232A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 99 | D022579F189F20F90038232A /* OCAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCAppDelegate.h; sourceTree = ""; }; 100 | D02257A0189F20F90038232A /* OCAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCAppDelegate.m; sourceTree = ""; }; 101 | D02257A3189F20F90038232A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 102 | D02257A8189F20F90038232A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 103 | D02257AE189F20F90038232A /* CloudNotesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CloudNotesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 104 | D02257AF189F20F90038232A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 105 | D02257B6189F20F90038232A /* CloudNotesTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CloudNotesTests-Info.plist"; sourceTree = ""; }; 106 | D02257B8189F20F90038232A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 107 | D02257BA189F20F90038232A /* CloudNotesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CloudNotesTests.m; sourceTree = ""; }; 108 | D02257C418A1C3EF0038232A /* OCNotesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCNotesViewController.h; sourceTree = ""; }; 109 | D02257C518A1C3EF0038232A /* OCNotesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCNotesViewController.m; sourceTree = ""; }; 110 | D02257C618A1C3EF0038232A /* OCNotesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OCNotesViewController.xib; sourceTree = ""; }; 111 | D02257CC18A1D2F90038232A /* OCAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCAPIClient.h; sourceTree = ""; }; 112 | D02257CD18A1D2F90038232A /* OCAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCAPIClient.m; sourceTree = ""; }; 113 | D02257CE18A1D2F90038232A /* OCNotesHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCNotesHelper.h; sourceTree = ""; }; 114 | D02257CF18A1D2F90038232A /* OCNotesHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCNotesHelper.m; sourceTree = ""; }; 115 | D02257D518A1D3640038232A /* AFHTTPRequestOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPRequestOperation.h; sourceTree = ""; }; 116 | D02257D618A1D3640038232A /* AFHTTPRequestOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPRequestOperation.m; sourceTree = ""; }; 117 | D02257D718A1D3640038232A /* AFHTTPRequestOperationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPRequestOperationManager.h; sourceTree = ""; }; 118 | D02257D818A1D3640038232A /* AFHTTPRequestOperationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPRequestOperationManager.m; sourceTree = ""; }; 119 | D02257D918A1D3640038232A /* AFHTTPSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFHTTPSessionManager.h; sourceTree = ""; }; 120 | D02257DA18A1D3640038232A /* AFHTTPSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFHTTPSessionManager.m; sourceTree = ""; }; 121 | D02257DB18A1D3640038232A /* AFNetworking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworking.h; sourceTree = ""; }; 122 | D02257DC18A1D3640038232A /* AFNetworkReachabilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFNetworkReachabilityManager.h; sourceTree = ""; }; 123 | D02257DD18A1D3640038232A /* AFNetworkReachabilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFNetworkReachabilityManager.m; sourceTree = ""; }; 124 | D02257DE18A1D3640038232A /* AFSecurityPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFSecurityPolicy.h; sourceTree = ""; }; 125 | D02257DF18A1D3640038232A /* AFSecurityPolicy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFSecurityPolicy.m; sourceTree = ""; }; 126 | D02257E018A1D3640038232A /* AFURLConnectionOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLConnectionOperation.h; sourceTree = ""; }; 127 | D02257E118A1D3640038232A /* AFURLConnectionOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLConnectionOperation.m; sourceTree = ""; }; 128 | D02257E218A1D3640038232A /* AFURLRequestSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLRequestSerialization.h; sourceTree = ""; }; 129 | D02257E318A1D3640038232A /* AFURLRequestSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLRequestSerialization.m; sourceTree = ""; }; 130 | D02257E418A1D3640038232A /* AFURLResponseSerialization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLResponseSerialization.h; sourceTree = ""; }; 131 | D02257E518A1D3640038232A /* AFURLResponseSerialization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLResponseSerialization.m; sourceTree = ""; }; 132 | D02257E618A1D3640038232A /* AFURLSessionManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AFURLSessionManager.h; sourceTree = ""; }; 133 | D02257E718A1D3640038232A /* AFURLSessionManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AFURLSessionManager.m; sourceTree = ""; }; 134 | D02257F418A1D9AA0038232A /* NSDictionary+HandleNull.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+HandleNull.h"; sourceTree = ""; }; 135 | D02257F518A1D9AA0038232A /* NSDictionary+HandleNull.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+HandleNull.m"; sourceTree = ""; }; 136 | D02257F718A1DB6E0038232A /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 137 | D02257F918A1DB7A0038232A /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 138 | D02257FB18A1DB840038232A /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; 139 | D022580018A5B4260038232A /* NSSplitView+SaveLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSSplitView+SaveLayout.h"; sourceTree = ""; }; 140 | D022580118A5B4260038232A /* NSSplitView+SaveLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSSplitView+SaveLayout.m"; sourceTree = ""; }; 141 | D022580D18A6CC950038232A /* OCPrefsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCPrefsWindowController.h; sourceTree = ""; }; 142 | D022580E18A6CC950038232A /* OCPrefsWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCPrefsWindowController.m; sourceTree = ""; }; 143 | D022580F18A6CC950038232A /* OCPrefsWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OCPrefsWindowController.xib; sourceTree = ""; }; 144 | D022581318A6F2CD0038232A /* PDKeychainBindings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PDKeychainBindings.h; sourceTree = ""; }; 145 | D022581418A6F2CD0038232A /* PDKeychainBindings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PDKeychainBindings.m; sourceTree = ""; }; 146 | D022581518A6F2CD0038232A /* PDKeychainBindingsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PDKeychainBindingsController.h; sourceTree = ""; }; 147 | D022581618A6F2CD0038232A /* PDKeychainBindingsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PDKeychainBindingsController.m; sourceTree = ""; }; 148 | D022581918A70E010038232A /* OCDateTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCDateTransformer.h; sourceTree = ""; }; 149 | D022581A18A70E010038232A /* OCDateTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCDateTransformer.m; sourceTree = ""; }; 150 | D0EB38E618B0577A00379F3F /* OCEditorSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCEditorSettings.h; sourceTree = ""; }; 151 | D0EB38E718B0577A00379F3F /* OCEditorSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCEditorSettings.m; sourceTree = ""; }; 152 | D0EB38EC18B175D900379F3F /* OCNotesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCNotesWindowController.h; sourceTree = ""; }; 153 | D0EB38ED18B175D900379F3F /* OCNotesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCNotesWindowController.m; sourceTree = ""; }; 154 | D0EB38EE18B175D900379F3F /* OCNotesWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OCNotesWindowController.xib; sourceTree = ""; }; 155 | /* End PBXFileReference section */ 156 | 157 | /* Begin PBXFrameworksBuildPhase section */ 158 | D0225787189F20F90038232A /* Frameworks */ = { 159 | isa = PBXFrameworksBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | D01D202E18FF34C2008407C4 /* libsqlite3.dylib in Frameworks */, 163 | D02257FC18A1DB840038232A /* CoreServices.framework in Frameworks */, 164 | D02257FA18A1DB7A0038232A /* Security.framework in Frameworks */, 165 | D02257F818A1DB6E0038232A /* SystemConfiguration.framework in Frameworks */, 166 | D022578E189F20F90038232A /* Cocoa.framework in Frameworks */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | D02257AB189F20F90038232A /* Frameworks */ = { 171 | isa = PBXFrameworksBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | D02257B1189F20F90038232A /* Cocoa.framework in Frameworks */, 175 | D02257B0189F20F90038232A /* XCTest.framework in Frameworks */, 176 | ); 177 | runOnlyForDeploymentPostprocessing = 0; 178 | }; 179 | /* End PBXFrameworksBuildPhase section */ 180 | 181 | /* Begin PBXGroup section */ 182 | D01D200F18FF2DE2008407C4 /* fmdb */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | D01D201018FF2DE2008407C4 /* FMDatabase.h */, 186 | D01D201118FF2DE2008407C4 /* FMDatabase.m */, 187 | D01D201218FF2DE2008407C4 /* FMDatabaseAdditions.h */, 188 | D01D201318FF2DE2008407C4 /* FMDatabaseAdditions.m */, 189 | D01D201418FF2DE2008407C4 /* FMDatabasePool.h */, 190 | D01D201518FF2DE2008407C4 /* FMDatabasePool.m */, 191 | D01D201618FF2DE2008407C4 /* FMDatabaseQueue.h */, 192 | D01D201718FF2DE2008407C4 /* FMDatabaseQueue.m */, 193 | D01D201818FF2DE2008407C4 /* FMDB.h */, 194 | D01D201918FF2DE2008407C4 /* FMResultSet.h */, 195 | D01D201A18FF2DE2008407C4 /* FMResultSet.m */, 196 | ); 197 | name = fmdb; 198 | path = FMDB/src/fmdb; 199 | sourceTree = ""; 200 | }; 201 | D01D202018FF2DED008407C4 /* FCModel */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | D01D202118FF2DED008407C4 /* FCModel.h */, 205 | D01D202218FF2DED008407C4 /* FCModel.m */, 206 | D01D202318FF2DED008407C4 /* FCModelCachedObject.h */, 207 | D01D202418FF2DED008407C4 /* FCModelCachedObject.m */, 208 | D01D202518FF2DED008407C4 /* FCModelDatabaseQueue.h */, 209 | D01D202618FF2DED008407C4 /* FCModelDatabaseQueue.m */, 210 | ); 211 | name = FCModel; 212 | path = FCModel/FCModel; 213 | sourceTree = ""; 214 | }; 215 | D0225781189F20F90038232A = { 216 | isa = PBXGroup; 217 | children = ( 218 | D01D202018FF2DED008407C4 /* FCModel */, 219 | D01D200F18FF2DE2008407C4 /* fmdb */, 220 | D022581218A6F2CD0038232A /* PDKeychainBindingsController */, 221 | D02257D418A1D3640038232A /* AFNetworking */, 222 | D0225793189F20F90038232A /* CloudNotes */, 223 | D02257B4189F20F90038232A /* CloudNotesTests */, 224 | D022578C189F20F90038232A /* Frameworks */, 225 | D022578B189F20F90038232A /* Products */, 226 | ); 227 | sourceTree = ""; 228 | }; 229 | D022578B189F20F90038232A /* Products */ = { 230 | isa = PBXGroup; 231 | children = ( 232 | D022578A189F20F90038232A /* CloudNotes.app */, 233 | D02257AE189F20F90038232A /* CloudNotesTests.xctest */, 234 | ); 235 | name = Products; 236 | sourceTree = ""; 237 | }; 238 | D022578C189F20F90038232A /* Frameworks */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | D01D202D18FF34C2008407C4 /* libsqlite3.dylib */, 242 | D02257FB18A1DB840038232A /* CoreServices.framework */, 243 | D02257F918A1DB7A0038232A /* Security.framework */, 244 | D02257F718A1DB6E0038232A /* SystemConfiguration.framework */, 245 | D022578D189F20F90038232A /* Cocoa.framework */, 246 | D02257AF189F20F90038232A /* XCTest.framework */, 247 | D022578F189F20F90038232A /* Other Frameworks */, 248 | ); 249 | name = Frameworks; 250 | sourceTree = ""; 251 | }; 252 | D022578F189F20F90038232A /* Other Frameworks */ = { 253 | isa = PBXGroup; 254 | children = ( 255 | D0225790189F20F90038232A /* AppKit.framework */, 256 | D0225792189F20F90038232A /* Foundation.framework */, 257 | ); 258 | name = "Other Frameworks"; 259 | sourceTree = ""; 260 | }; 261 | D0225793189F20F90038232A /* CloudNotes */ = { 262 | isa = PBXGroup; 263 | children = ( 264 | D01D202A18FF3200008407C4 /* OCNote.m */, 265 | D01D202B18FF3200008407C4 /* OCNote.h */, 266 | D02257CC18A1D2F90038232A /* OCAPIClient.h */, 267 | D02257CD18A1D2F90038232A /* OCAPIClient.m */, 268 | D02257CE18A1D2F90038232A /* OCNotesHelper.h */, 269 | D02257CF18A1D2F90038232A /* OCNotesHelper.m */, 270 | D022579F189F20F90038232A /* OCAppDelegate.h */, 271 | D02257A0189F20F90038232A /* OCAppDelegate.m */, 272 | D02257A2189F20F90038232A /* MainMenu.xib */, 273 | D0EB38EC18B175D900379F3F /* OCNotesWindowController.h */, 274 | D0EB38ED18B175D900379F3F /* OCNotesWindowController.m */, 275 | D0EB38EE18B175D900379F3F /* OCNotesWindowController.xib */, 276 | D02257C418A1C3EF0038232A /* OCNotesViewController.h */, 277 | D02257C518A1C3EF0038232A /* OCNotesViewController.m */, 278 | D02257C618A1C3EF0038232A /* OCNotesViewController.xib */, 279 | D022581918A70E010038232A /* OCDateTransformer.h */, 280 | D022581A18A70E010038232A /* OCDateTransformer.m */, 281 | D022580D18A6CC950038232A /* OCPrefsWindowController.h */, 282 | D022580E18A6CC950038232A /* OCPrefsWindowController.m */, 283 | D022580F18A6CC950038232A /* OCPrefsWindowController.xib */, 284 | D02257F418A1D9AA0038232A /* NSDictionary+HandleNull.h */, 285 | D02257F518A1D9AA0038232A /* NSDictionary+HandleNull.m */, 286 | D022580018A5B4260038232A /* NSSplitView+SaveLayout.h */, 287 | D022580118A5B4260038232A /* NSSplitView+SaveLayout.m */, 288 | D0EB38E618B0577A00379F3F /* OCEditorSettings.h */, 289 | D0EB38E718B0577A00379F3F /* OCEditorSettings.m */, 290 | D02257A8189F20F90038232A /* Images.xcassets */, 291 | D0225794189F20F90038232A /* Supporting Files */, 292 | ); 293 | path = CloudNotes; 294 | sourceTree = ""; 295 | }; 296 | D0225794189F20F90038232A /* Supporting Files */ = { 297 | isa = PBXGroup; 298 | children = ( 299 | D0225795189F20F90038232A /* CloudNotes-Info.plist */, 300 | D0225796189F20F90038232A /* InfoPlist.strings */, 301 | D0225799189F20F90038232A /* main.m */, 302 | D022579B189F20F90038232A /* CloudNotes-Prefix.pch */, 303 | D022579C189F20F90038232A /* Credits.rtf */, 304 | ); 305 | name = "Supporting Files"; 306 | sourceTree = ""; 307 | }; 308 | D02257B4189F20F90038232A /* CloudNotesTests */ = { 309 | isa = PBXGroup; 310 | children = ( 311 | D02257BA189F20F90038232A /* CloudNotesTests.m */, 312 | D02257B5189F20F90038232A /* Supporting Files */, 313 | ); 314 | path = CloudNotesTests; 315 | sourceTree = ""; 316 | }; 317 | D02257B5189F20F90038232A /* Supporting Files */ = { 318 | isa = PBXGroup; 319 | children = ( 320 | D02257B6189F20F90038232A /* CloudNotesTests-Info.plist */, 321 | D02257B7189F20F90038232A /* InfoPlist.strings */, 322 | ); 323 | name = "Supporting Files"; 324 | sourceTree = ""; 325 | }; 326 | D02257D418A1D3640038232A /* AFNetworking */ = { 327 | isa = PBXGroup; 328 | children = ( 329 | D02257D518A1D3640038232A /* AFHTTPRequestOperation.h */, 330 | D02257D618A1D3640038232A /* AFHTTPRequestOperation.m */, 331 | D02257D718A1D3640038232A /* AFHTTPRequestOperationManager.h */, 332 | D02257D818A1D3640038232A /* AFHTTPRequestOperationManager.m */, 333 | D02257D918A1D3640038232A /* AFHTTPSessionManager.h */, 334 | D02257DA18A1D3640038232A /* AFHTTPSessionManager.m */, 335 | D02257DB18A1D3640038232A /* AFNetworking.h */, 336 | D02257DC18A1D3640038232A /* AFNetworkReachabilityManager.h */, 337 | D02257DD18A1D3640038232A /* AFNetworkReachabilityManager.m */, 338 | D02257DE18A1D3640038232A /* AFSecurityPolicy.h */, 339 | D02257DF18A1D3640038232A /* AFSecurityPolicy.m */, 340 | D02257E018A1D3640038232A /* AFURLConnectionOperation.h */, 341 | D02257E118A1D3640038232A /* AFURLConnectionOperation.m */, 342 | D02257E218A1D3640038232A /* AFURLRequestSerialization.h */, 343 | D02257E318A1D3640038232A /* AFURLRequestSerialization.m */, 344 | D02257E418A1D3640038232A /* AFURLResponseSerialization.h */, 345 | D02257E518A1D3640038232A /* AFURLResponseSerialization.m */, 346 | D02257E618A1D3640038232A /* AFURLSessionManager.h */, 347 | D02257E718A1D3640038232A /* AFURLSessionManager.m */, 348 | ); 349 | name = AFNetworking; 350 | path = AFNetworking/AFNetworking; 351 | sourceTree = ""; 352 | }; 353 | D022581218A6F2CD0038232A /* PDKeychainBindingsController */ = { 354 | isa = PBXGroup; 355 | children = ( 356 | D022581318A6F2CD0038232A /* PDKeychainBindings.h */, 357 | D022581418A6F2CD0038232A /* PDKeychainBindings.m */, 358 | D022581518A6F2CD0038232A /* PDKeychainBindingsController.h */, 359 | D022581618A6F2CD0038232A /* PDKeychainBindingsController.m */, 360 | ); 361 | name = PDKeychainBindingsController; 362 | path = PDKeychainBindingsController/PDKeychainBindingsController; 363 | sourceTree = ""; 364 | }; 365 | /* End PBXGroup section */ 366 | 367 | /* Begin PBXNativeTarget section */ 368 | D0225789189F20F90038232A /* CloudNotes */ = { 369 | isa = PBXNativeTarget; 370 | buildConfigurationList = D02257BE189F20F90038232A /* Build configuration list for PBXNativeTarget "CloudNotes" */; 371 | buildPhases = ( 372 | D0225786189F20F90038232A /* Sources */, 373 | D0225787189F20F90038232A /* Frameworks */, 374 | D0225788189F20F90038232A /* Resources */, 375 | ); 376 | buildRules = ( 377 | ); 378 | dependencies = ( 379 | ); 380 | name = CloudNotes; 381 | productName = CloudNotes; 382 | productReference = D022578A189F20F90038232A /* CloudNotes.app */; 383 | productType = "com.apple.product-type.application"; 384 | }; 385 | D02257AD189F20F90038232A /* CloudNotesTests */ = { 386 | isa = PBXNativeTarget; 387 | buildConfigurationList = D02257C1189F20F90038232A /* Build configuration list for PBXNativeTarget "CloudNotesTests" */; 388 | buildPhases = ( 389 | D02257AA189F20F90038232A /* Sources */, 390 | D02257AB189F20F90038232A /* Frameworks */, 391 | D02257AC189F20F90038232A /* Resources */, 392 | ); 393 | buildRules = ( 394 | ); 395 | dependencies = ( 396 | D02257B3189F20F90038232A /* PBXTargetDependency */, 397 | ); 398 | name = CloudNotesTests; 399 | productName = CloudNotesTests; 400 | productReference = D02257AE189F20F90038232A /* CloudNotesTests.xctest */; 401 | productType = "com.apple.product-type.bundle.unit-test"; 402 | }; 403 | /* End PBXNativeTarget section */ 404 | 405 | /* Begin PBXProject section */ 406 | D0225782189F20F90038232A /* Project object */ = { 407 | isa = PBXProject; 408 | attributes = { 409 | CLASSPREFIX = OC; 410 | LastUpgradeCheck = 0500; 411 | ORGANIZATIONNAME = "Peter Hedlund"; 412 | TargetAttributes = { 413 | D02257AD189F20F90038232A = { 414 | TestTargetID = D0225789189F20F90038232A; 415 | }; 416 | }; 417 | }; 418 | buildConfigurationList = D0225785189F20F90038232A /* Build configuration list for PBXProject "CloudNotes" */; 419 | compatibilityVersion = "Xcode 3.2"; 420 | developmentRegion = English; 421 | hasScannedForEncodings = 0; 422 | knownRegions = ( 423 | en, 424 | Base, 425 | ); 426 | mainGroup = D0225781189F20F90038232A; 427 | productRefGroup = D022578B189F20F90038232A /* Products */; 428 | projectDirPath = ""; 429 | projectRoot = ""; 430 | targets = ( 431 | D0225789189F20F90038232A /* CloudNotes */, 432 | D02257AD189F20F90038232A /* CloudNotesTests */, 433 | ); 434 | }; 435 | /* End PBXProject section */ 436 | 437 | /* Begin PBXResourcesBuildPhase section */ 438 | D0225788189F20F90038232A /* Resources */ = { 439 | isa = PBXResourcesBuildPhase; 440 | buildActionMask = 2147483647; 441 | files = ( 442 | D022581118A6CC950038232A /* OCPrefsWindowController.xib in Resources */, 443 | D0225798189F20F90038232A /* InfoPlist.strings in Resources */, 444 | D02257A9189F20F90038232A /* Images.xcassets in Resources */, 445 | D022579E189F20F90038232A /* Credits.rtf in Resources */, 446 | D0EB38F018B175D900379F3F /* OCNotesWindowController.xib in Resources */, 447 | D02257A4189F20F90038232A /* MainMenu.xib in Resources */, 448 | D02257C818A1C3EF0038232A /* OCNotesViewController.xib in Resources */, 449 | ); 450 | runOnlyForDeploymentPostprocessing = 0; 451 | }; 452 | D02257AC189F20F90038232A /* Resources */ = { 453 | isa = PBXResourcesBuildPhase; 454 | buildActionMask = 2147483647; 455 | files = ( 456 | D02257B9189F20F90038232A /* InfoPlist.strings in Resources */, 457 | ); 458 | runOnlyForDeploymentPostprocessing = 0; 459 | }; 460 | /* End PBXResourcesBuildPhase section */ 461 | 462 | /* Begin PBXSourcesBuildPhase section */ 463 | D0225786189F20F90038232A /* Sources */ = { 464 | isa = PBXSourcesBuildPhase; 465 | buildActionMask = 2147483647; 466 | files = ( 467 | D02257ED18A1D3640038232A /* AFURLConnectionOperation.m in Sources */, 468 | D02257C718A1C3EF0038232A /* OCNotesViewController.m in Sources */, 469 | D02257A1189F20F90038232A /* OCAppDelegate.m in Sources */, 470 | D02257EF18A1D3640038232A /* AFURLResponseSerialization.m in Sources */, 471 | D01D202918FF2DED008407C4 /* FCModelDatabaseQueue.m in Sources */, 472 | D01D202C18FF3200008407C4 /* OCNote.m in Sources */, 473 | D02257EE18A1D3640038232A /* AFURLRequestSerialization.m in Sources */, 474 | D022580218A5B4260038232A /* NSSplitView+SaveLayout.m in Sources */, 475 | D02257EC18A1D3640038232A /* AFSecurityPolicy.m in Sources */, 476 | D02257E818A1D3640038232A /* AFHTTPRequestOperation.m in Sources */, 477 | D02257F018A1D3640038232A /* AFURLSessionManager.m in Sources */, 478 | D0EB38EF18B175D900379F3F /* OCNotesWindowController.m in Sources */, 479 | D022581B18A70E010038232A /* OCDateTransformer.m in Sources */, 480 | D01D201D18FF2DE2008407C4 /* FMDatabasePool.m in Sources */, 481 | D01D201B18FF2DE2008407C4 /* FMDatabase.m in Sources */, 482 | D022579A189F20F90038232A /* main.m in Sources */, 483 | D0EB38E818B0577A00379F3F /* OCEditorSettings.m in Sources */, 484 | D01D201E18FF2DE2008407C4 /* FMDatabaseQueue.m in Sources */, 485 | D01D201F18FF2DE2008407C4 /* FMResultSet.m in Sources */, 486 | D02257D318A1D2F90038232A /* OCNotesHelper.m in Sources */, 487 | D01D202718FF2DED008407C4 /* FCModel.m in Sources */, 488 | D022581718A6F2CD0038232A /* PDKeychainBindings.m in Sources */, 489 | D02257F618A1D9AA0038232A /* NSDictionary+HandleNull.m in Sources */, 490 | D02257EA18A1D3640038232A /* AFHTTPSessionManager.m in Sources */, 491 | D02257D218A1D2F90038232A /* OCAPIClient.m in Sources */, 492 | D01D201C18FF2DE2008407C4 /* FMDatabaseAdditions.m in Sources */, 493 | D02257EB18A1D3640038232A /* AFNetworkReachabilityManager.m in Sources */, 494 | D022581018A6CC950038232A /* OCPrefsWindowController.m in Sources */, 495 | D01D202818FF2DED008407C4 /* FCModelCachedObject.m in Sources */, 496 | D022581818A6F2CD0038232A /* PDKeychainBindingsController.m in Sources */, 497 | D02257E918A1D3640038232A /* AFHTTPRequestOperationManager.m in Sources */, 498 | ); 499 | runOnlyForDeploymentPostprocessing = 0; 500 | }; 501 | D02257AA189F20F90038232A /* Sources */ = { 502 | isa = PBXSourcesBuildPhase; 503 | buildActionMask = 2147483647; 504 | files = ( 505 | D02257BB189F20F90038232A /* CloudNotesTests.m in Sources */, 506 | ); 507 | runOnlyForDeploymentPostprocessing = 0; 508 | }; 509 | /* End PBXSourcesBuildPhase section */ 510 | 511 | /* Begin PBXTargetDependency section */ 512 | D02257B3189F20F90038232A /* PBXTargetDependency */ = { 513 | isa = PBXTargetDependency; 514 | target = D0225789189F20F90038232A /* CloudNotes */; 515 | targetProxy = D02257B2189F20F90038232A /* PBXContainerItemProxy */; 516 | }; 517 | /* End PBXTargetDependency section */ 518 | 519 | /* Begin PBXVariantGroup section */ 520 | D0225796189F20F90038232A /* InfoPlist.strings */ = { 521 | isa = PBXVariantGroup; 522 | children = ( 523 | D0225797189F20F90038232A /* en */, 524 | ); 525 | name = InfoPlist.strings; 526 | sourceTree = ""; 527 | }; 528 | D022579C189F20F90038232A /* Credits.rtf */ = { 529 | isa = PBXVariantGroup; 530 | children = ( 531 | D022579D189F20F90038232A /* en */, 532 | ); 533 | name = Credits.rtf; 534 | sourceTree = ""; 535 | }; 536 | D02257A2189F20F90038232A /* MainMenu.xib */ = { 537 | isa = PBXVariantGroup; 538 | children = ( 539 | D02257A3189F20F90038232A /* Base */, 540 | ); 541 | name = MainMenu.xib; 542 | sourceTree = ""; 543 | }; 544 | D02257B7189F20F90038232A /* InfoPlist.strings */ = { 545 | isa = PBXVariantGroup; 546 | children = ( 547 | D02257B8189F20F90038232A /* en */, 548 | ); 549 | name = InfoPlist.strings; 550 | sourceTree = ""; 551 | }; 552 | /* End PBXVariantGroup section */ 553 | 554 | /* Begin XCBuildConfiguration section */ 555 | D02257BC189F20F90038232A /* Debug */ = { 556 | isa = XCBuildConfiguration; 557 | buildSettings = { 558 | ALWAYS_SEARCH_USER_PATHS = NO; 559 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 560 | CLANG_CXX_LIBRARY = "libc++"; 561 | CLANG_ENABLE_OBJC_ARC = YES; 562 | CLANG_WARN_BOOL_CONVERSION = YES; 563 | CLANG_WARN_CONSTANT_CONVERSION = YES; 564 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 565 | CLANG_WARN_EMPTY_BODY = YES; 566 | CLANG_WARN_ENUM_CONVERSION = YES; 567 | CLANG_WARN_INT_CONVERSION = YES; 568 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 569 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 570 | COPY_PHASE_STRIP = NO; 571 | GCC_C_LANGUAGE_STANDARD = gnu99; 572 | GCC_DYNAMIC_NO_PIC = NO; 573 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 574 | GCC_OPTIMIZATION_LEVEL = 0; 575 | GCC_PREPROCESSOR_DEFINITIONS = ( 576 | "DEBUG=1", 577 | "$(inherited)", 578 | ); 579 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 580 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 581 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 582 | GCC_WARN_UNDECLARED_SELECTOR = YES; 583 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 584 | GCC_WARN_UNUSED_FUNCTION = YES; 585 | GCC_WARN_UNUSED_VARIABLE = YES; 586 | MACOSX_DEPLOYMENT_TARGET = 10.8; 587 | ONLY_ACTIVE_ARCH = YES; 588 | SDKROOT = macosx; 589 | }; 590 | name = Debug; 591 | }; 592 | D02257BD189F20F90038232A /* Release */ = { 593 | isa = XCBuildConfiguration; 594 | buildSettings = { 595 | ALWAYS_SEARCH_USER_PATHS = NO; 596 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 597 | CLANG_CXX_LIBRARY = "libc++"; 598 | CLANG_ENABLE_OBJC_ARC = YES; 599 | CLANG_WARN_BOOL_CONVERSION = YES; 600 | CLANG_WARN_CONSTANT_CONVERSION = YES; 601 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 602 | CLANG_WARN_EMPTY_BODY = YES; 603 | CLANG_WARN_ENUM_CONVERSION = YES; 604 | CLANG_WARN_INT_CONVERSION = YES; 605 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 606 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 607 | COPY_PHASE_STRIP = YES; 608 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 609 | ENABLE_NS_ASSERTIONS = NO; 610 | GCC_C_LANGUAGE_STANDARD = gnu99; 611 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 612 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 613 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 614 | GCC_WARN_UNDECLARED_SELECTOR = YES; 615 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 616 | GCC_WARN_UNUSED_FUNCTION = YES; 617 | GCC_WARN_UNUSED_VARIABLE = YES; 618 | MACOSX_DEPLOYMENT_TARGET = 10.8; 619 | SDKROOT = macosx; 620 | }; 621 | name = Release; 622 | }; 623 | D02257BF189F20F90038232A /* Debug */ = { 624 | isa = XCBuildConfiguration; 625 | buildSettings = { 626 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 627 | CLANG_ENABLE_MODULES = YES; 628 | COMBINE_HIDPI_IMAGES = YES; 629 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 630 | GCC_PREFIX_HEADER = "CloudNotes/CloudNotes-Prefix.pch"; 631 | INFOPLIST_FILE = "CloudNotes/CloudNotes-Info.plist"; 632 | MACOSX_DEPLOYMENT_TARGET = 10.9; 633 | PRODUCT_NAME = "$(TARGET_NAME)"; 634 | WRAPPER_EXTENSION = app; 635 | }; 636 | name = Debug; 637 | }; 638 | D02257C0189F20F90038232A /* Release */ = { 639 | isa = XCBuildConfiguration; 640 | buildSettings = { 641 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 642 | CLANG_ENABLE_MODULES = YES; 643 | COMBINE_HIDPI_IMAGES = YES; 644 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 645 | GCC_PREFIX_HEADER = "CloudNotes/CloudNotes-Prefix.pch"; 646 | INFOPLIST_FILE = "CloudNotes/CloudNotes-Info.plist"; 647 | MACOSX_DEPLOYMENT_TARGET = 10.9; 648 | PRODUCT_NAME = "$(TARGET_NAME)"; 649 | WRAPPER_EXTENSION = app; 650 | }; 651 | name = Release; 652 | }; 653 | D02257C2189F20F90038232A /* Debug */ = { 654 | isa = XCBuildConfiguration; 655 | buildSettings = { 656 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/CloudNotes.app/Contents/MacOS/CloudNotes"; 657 | COMBINE_HIDPI_IMAGES = YES; 658 | FRAMEWORK_SEARCH_PATHS = ( 659 | "$(DEVELOPER_FRAMEWORKS_DIR)", 660 | "$(inherited)", 661 | ); 662 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 663 | GCC_PREFIX_HEADER = "CloudNotes/CloudNotes-Prefix.pch"; 664 | GCC_PREPROCESSOR_DEFINITIONS = ( 665 | "DEBUG=1", 666 | "$(inherited)", 667 | ); 668 | INFOPLIST_FILE = "CloudNotesTests/CloudNotesTests-Info.plist"; 669 | PRODUCT_NAME = "$(TARGET_NAME)"; 670 | TEST_HOST = "$(BUNDLE_LOADER)"; 671 | WRAPPER_EXTENSION = xctest; 672 | }; 673 | name = Debug; 674 | }; 675 | D02257C3189F20F90038232A /* Release */ = { 676 | isa = XCBuildConfiguration; 677 | buildSettings = { 678 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/CloudNotes.app/Contents/MacOS/CloudNotes"; 679 | COMBINE_HIDPI_IMAGES = YES; 680 | FRAMEWORK_SEARCH_PATHS = ( 681 | "$(DEVELOPER_FRAMEWORKS_DIR)", 682 | "$(inherited)", 683 | ); 684 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 685 | GCC_PREFIX_HEADER = "CloudNotes/CloudNotes-Prefix.pch"; 686 | INFOPLIST_FILE = "CloudNotesTests/CloudNotesTests-Info.plist"; 687 | PRODUCT_NAME = "$(TARGET_NAME)"; 688 | TEST_HOST = "$(BUNDLE_LOADER)"; 689 | WRAPPER_EXTENSION = xctest; 690 | }; 691 | name = Release; 692 | }; 693 | /* End XCBuildConfiguration section */ 694 | 695 | /* Begin XCConfigurationList section */ 696 | D0225785189F20F90038232A /* Build configuration list for PBXProject "CloudNotes" */ = { 697 | isa = XCConfigurationList; 698 | buildConfigurations = ( 699 | D02257BC189F20F90038232A /* Debug */, 700 | D02257BD189F20F90038232A /* Release */, 701 | ); 702 | defaultConfigurationIsVisible = 0; 703 | defaultConfigurationName = Release; 704 | }; 705 | D02257BE189F20F90038232A /* Build configuration list for PBXNativeTarget "CloudNotes" */ = { 706 | isa = XCConfigurationList; 707 | buildConfigurations = ( 708 | D02257BF189F20F90038232A /* Debug */, 709 | D02257C0189F20F90038232A /* Release */, 710 | ); 711 | defaultConfigurationIsVisible = 0; 712 | defaultConfigurationName = Release; 713 | }; 714 | D02257C1189F20F90038232A /* Build configuration list for PBXNativeTarget "CloudNotesTests" */ = { 715 | isa = XCConfigurationList; 716 | buildConfigurations = ( 717 | D02257C2189F20F90038232A /* Debug */, 718 | D02257C3189F20F90038232A /* Release */, 719 | ); 720 | defaultConfigurationIsVisible = 0; 721 | defaultConfigurationName = Release; 722 | }; 723 | /* End XCConfigurationList section */ 724 | }; 725 | rootObject = D0225782189F20F90038232A /* Project object */; 726 | } 727 | -------------------------------------------------------------------------------- /CloudNotes/CloudNotes-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.peterandlinda.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 0.8.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 16 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2014 Peter Hedlund. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /CloudNotes/CloudNotes-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon-16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon-16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon-32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon-32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon-128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon-128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon-256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon-256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon-512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon-512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-128.png -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-128@2x.png -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-16.png -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-16@2x.png -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-256.png -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-256@2x.png -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-32.png -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-32@2x.png -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-512.png -------------------------------------------------------------------------------- /CloudNotes/Images.xcassets/AppIcon.appiconset/icon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owncloud-archive/notes-OSX-App/c63969fa58c03cc02ca2b5ca711c576480bfe8a4/CloudNotes/Images.xcassets/AppIcon.appiconset/icon-512@2x.png -------------------------------------------------------------------------------- /CloudNotes/NSDictionary+HandleNull.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+HandleNull.h 3 | // iOCNews 4 | // 5 | 6 | /************************************************************************ 7 | 8 | Copyright 2013 Peter Hedlund peter.hedlund@me.com 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions 12 | are met: 13 | 14 | 1. Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *************************************************************************/ 32 | 33 | #import 34 | 35 | @interface NSDictionary (HandleNull) 36 | 37 | - (id)objectForKeyNotNull:(id)key fallback:(id)fallback; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /CloudNotes/NSDictionary+HandleNull.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+HandleNull.m 3 | // iOCNews 4 | // 5 | 6 | /************************************************************************ 7 | 8 | Copyright 2013 Peter Hedlund peter.hedlund@me.com 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions 12 | are met: 13 | 14 | 1. Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *************************************************************************/ 32 | 33 | #import "NSDictionary+HandleNull.h" 34 | 35 | @implementation NSDictionary (HandleNull) 36 | 37 | - (id)objectForKeyNotNull:(id)key fallback:(id)fallback { 38 | id object = [self objectForKey:key]; 39 | if (object == [NSNull null]) { 40 | return fallback; 41 | } else { 42 | return object; 43 | } 44 | } 45 | @end 46 | -------------------------------------------------------------------------------- /CloudNotes/NSSplitView+SaveLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSSplitView+SaveLayout.h 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/7/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSSplitView (SaveLayout) 12 | 13 | - (void)saveLayoutWithName:(NSString *)defaultName; 14 | - (void)loadLayoutWithName:(NSString *)defaultName; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /CloudNotes/NSSplitView+SaveLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSSplitView+SaveLayout.m 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/7/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import "NSSplitView+SaveLayout.h" 10 | 11 | @implementation NSSplitView (SaveLayout) 12 | 13 | - (void)saveLayoutWithName:(NSString *)defaultName { 14 | NSMutableArray *rectStrings = [NSMutableArray array]; 15 | [self.subviews enumerateObjectsUsingBlock:^(NSView *view, NSUInteger idx, BOOL *stop) { 16 | [rectStrings addObject: NSStringFromRect(view.frame)]; 17 | }]; 18 | [[NSUserDefaults standardUserDefaults] setObject:rectStrings forKey:defaultName]; 19 | [[NSUserDefaults standardUserDefaults] synchronize]; 20 | } 21 | 22 | - (void)loadLayoutWithName:(NSString *)defaultName { 23 | NSMutableArray *rectStrings = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:defaultName]]; 24 | if (rectStrings.count) { 25 | [self.subviews enumerateObjectsUsingBlock:^(NSView *view, NSUInteger idx, BOOL *stop) { 26 | view.frame = NSRectFromString([rectStrings objectAtIndex:idx]); 27 | }]; 28 | } 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /CloudNotes/OCAPIClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPIClient.h 3 | // iOCNews 4 | // 5 | 6 | /************************************************************************ 7 | 8 | Copyright 2013 Peter Hedlund peter.hedlund@me.com 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions 12 | are met: 13 | 14 | 1. Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *************************************************************************/ 32 | 33 | #import 34 | #import "AFHTTPSessionManager.h" 35 | 36 | @interface OCAPIClient : AFHTTPSessionManager 37 | 38 | +(OCAPIClient *)sharedClient; 39 | +(void)setSharedClient:(OCAPIClient *)client; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /CloudNotes/OCAPIClient.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAPIClient.m 3 | // iOCNews 4 | // 5 | 6 | /************************************************************************ 7 | 8 | Copyright 2013 Peter Hedlund peter.hedlund@me.com 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions 12 | are met: 13 | 14 | 1. Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *************************************************************************/ 32 | 33 | #import "OCAPIClient.h" 34 | #import "PDKeychainBindings.h" 35 | #import "NSDictionary+HandleNull.h" 36 | 37 | //See http://twobitlabs.com/2013/01/objective-c-singleton-pattern-unit-testing/ 38 | //Being able to reinitialize a singleton is a no no, but should happen so rarely 39 | //we can live with it? 40 | 41 | static const NSString *rootPath = @"index.php/apps/notes/api/v0.2/"; 42 | 43 | static OCAPIClient *_sharedClient = nil; 44 | static dispatch_once_t oncePredicate = 0; 45 | 46 | @implementation OCAPIClient 47 | 48 | +(OCAPIClient *)sharedClient { 49 | //static dispatch_once_t oncePredicate; 50 | dispatch_once(&oncePredicate, ^{ 51 | NSString *serverURLString = [[NSUserDefaults standardUserDefaults] stringForKey:@"Server"]; 52 | if (serverURLString.length > 0) { 53 | _sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", serverURLString, rootPath]]]; 54 | } 55 | }); 56 | return _sharedClient; 57 | } 58 | 59 | -(id)initWithBaseURL:(NSURL *)url { 60 | self = [super initWithBaseURL:url]; 61 | if (!self) { 62 | return nil; 63 | } 64 | BOOL allowInvalid = [[NSUserDefaults standardUserDefaults] boolForKey:@"AllowInvalidSSLCertificate"]; 65 | self.securityPolicy.allowInvalidCertificates = allowInvalid; 66 | 67 | [self setRequestSerializer:[AFJSONRequestSerializer serializer]]; 68 | [self.requestSerializer setAuthorizationHeaderFieldWithUsername:[[PDKeychainBindings sharedKeychainBindings] objectForKey:@"Username"] password:[[PDKeychainBindings sharedKeychainBindings] objectForKey:@"Password"]]; 69 | self.operationQueue.maxConcurrentOperationCount = 1; 70 | [self.reachabilityManager startMonitoring]; 71 | 72 | return self; 73 | } 74 | 75 | +(void)setSharedClient:(OCAPIClient *)client { 76 | oncePredicate = 0; // resets the once_token so dispatch_once will run again 77 | _sharedClient = client; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /CloudNotes/OCAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCAppDelegate.h 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/2/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCNotesViewController.h" 11 | #import "OCPrefsWindowController.h" 12 | 13 | @interface OCAppDelegate : NSObject 14 | 15 | @property (assign) IBOutlet NSWindow *window; 16 | 17 | @property (strong, nonatomic) OCNotesViewController *notesViewController; 18 | @property (strong, nonatomic, readonly) OCPrefsWindowController *prefsWindowController; 19 | 20 | - (IBAction)doPreferences:(id)sender; 21 | - (IBAction)doNew:(id)sender; 22 | - (IBAction)doDelete:(id)sender; 23 | - (IBAction)doSync:(id)sender; 24 | - (IBAction)doImport:(id)sender; 25 | - (IBAction)doExport:(id)sender; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /CloudNotes/OCAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCAppDelegate.m 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/2/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import "OCAppDelegate.h" 10 | #import "OCNotesWindowController.h" 11 | #import "OCNotesHelper.h" 12 | #import "NSSplitView+SaveLayout.h" 13 | #import "OCEditorSettings.h" 14 | #import "OCNote.h" 15 | #import "OCAPIClient.h" 16 | 17 | @interface OCAppDelegate () 18 | 19 | @property (strong) OCNotesWindowController *notesWindowController; 20 | 21 | @end 22 | 23 | @implementation OCAppDelegate 24 | 25 | @synthesize prefsWindowController; 26 | 27 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender 28 | { 29 | return YES; 30 | } 31 | 32 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 33 | { 34 | // Insert code here to initialize your application 35 | 36 | //Clear prefs for debug. 37 | //NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; 38 | //[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain]; 39 | 40 | self.notesWindowController = [[OCNotesWindowController alloc] initWithWindowNibName:@"OCNotesWindowController"]; 41 | self.notesViewController = [[OCNotesViewController alloc] initWithNibName:@"OCNotesViewController" bundle:nil]; 42 | NSView *mySubview = self.notesViewController.view; 43 | [self.notesWindowController.window.contentView addSubview:mySubview]; 44 | [mySubview setTranslatesAutoresizingMaskIntoConstraints:NO]; 45 | [[self.notesWindowController.window contentView] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[mySubview]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(mySubview)]]; 46 | [[self.notesWindowController.window contentView] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[mySubview]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(mySubview)]]; 47 | //self.notesViewController.context = [[OCNotesHelper sharedHelper] context]; 48 | [self.notesViewController.notesArrayController setContent:[OCNote allInstances]]; 49 | [self.notesViewController.tableView reloadData]; 50 | [self.notesViewController.tableView setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleSourceList]; 51 | NSLog(@"Server: %@", [[NSUserDefaults standardUserDefaults] stringForKey:@"Server"]); 52 | if ([[NSUserDefaults standardUserDefaults] stringForKey:@"Server"].length == 0) { 53 | [self doPreferences:nil]; 54 | } 55 | 56 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"SyncOnStart"]) { 57 | [self.notesViewController doSync:nil]; 58 | } 59 | 60 | [self.notesWindowController.window setAutorecalculatesContentBorderThickness:YES forEdge:NSMinYEdge]; 61 | [self.notesWindowController.window setContentBorderThickness:30 forEdge:NSMinYEdge]; 62 | 63 | [OCAPIClient sharedClient]; 64 | [OCNotesHelper sharedHelper]; 65 | 66 | [NSFontManager sharedFontManager].action = @selector(changeEditorFont:); 67 | [self.notesWindowController showWindow:self]; 68 | 69 | } 70 | 71 | - (IBAction)doPreferences:(id)sender { 72 | [self.prefsWindowController showWindow:nil]; 73 | } 74 | 75 | - (IBAction)doNew:(id)sender { 76 | [self.notesViewController doAdd:sender]; 77 | } 78 | 79 | - (IBAction)doDelete:(id)sender { 80 | [self.notesViewController doDelete:sender]; 81 | } 82 | 83 | - (IBAction)doSync:(id)sender { 84 | [self.notesViewController doSync:sender]; 85 | } 86 | 87 | - (IBAction)doImport:(id)sender { 88 | NSOpenPanel* openPanel = [NSOpenPanel openPanel]; 89 | 90 | openPanel.title = @"Select text file(s) to import"; 91 | openPanel.showsResizeIndicator = YES; 92 | openPanel.showsHiddenFiles = NO; 93 | openPanel.canChooseDirectories = NO; 94 | openPanel.canCreateDirectories = YES; 95 | openPanel.allowsMultipleSelection = YES; 96 | openPanel.allowedFileTypes = @[@"txt"]; 97 | 98 | [openPanel beginSheetModalForWindow:self.window 99 | completionHandler:^(NSInteger result) { 100 | 101 | if (result == NSFileHandlingPanelOKButton) { 102 | 103 | [openPanel.URLs enumerateObjectsUsingBlock:^(NSURL *url, NSUInteger idx, BOOL *stop) { 104 | NSString *fileText = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; 105 | if (fileText) { 106 | NSLog(@"Content: %@", fileText); 107 | [[OCNotesHelper sharedHelper] addNote:fileText]; 108 | } 109 | }]; 110 | } 111 | }]; 112 | } 113 | 114 | - (IBAction)doExport:(id)sender { 115 | [self.notesViewController doExport:sender]; 116 | } 117 | 118 | - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender 119 | { 120 | // Save changes in the application's managed object context before the application terminates. 121 | [self.notesViewController.splitView saveLayoutWithName:@"SplitViewLayout"]; 122 | 123 | return NSTerminateNow; 124 | } 125 | 126 | - (OCPrefsWindowController*)prefsWindowController { 127 | if (!prefsWindowController) { 128 | prefsWindowController = [[OCPrefsWindowController alloc] initWithWindowNibName:@"OCPrefsWindowController"]; 129 | } 130 | return prefsWindowController; 131 | } 132 | 133 | - (void)changeEditorFont:(id)sender { 134 | NSFont *newFont = [sender convertFont:[sender selectedFont]]; 135 | OCEditorSettings *settings = [[OCEditorSettings alloc] init]; 136 | settings.font = newFont; 137 | 138 | NSLog(@"Saving myself"); 139 | NSURL *saveUrl = [[OCNotesHelper sharedHelper] documentsDirectoryURL]; 140 | 141 | saveUrl = [saveUrl URLByAppendingPathComponent:@"settings" isDirectory:NO]; 142 | saveUrl = [saveUrl URLByAppendingPathExtension:@"plist"]; 143 | [NSKeyedArchiver archiveRootObject:settings toFile:[saveUrl path]]; 144 | 145 | self.notesViewController.contentTextView.textStorage.font = settings.font; 146 | self.notesViewController.contentTextView.font = settings.font; 147 | } 148 | 149 | @end 150 | -------------------------------------------------------------------------------- /CloudNotes/OCDateTransformer.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCDateTransformer.h 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/8/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OCDateTransformer : NSValueTransformer 12 | 13 | +(Class)transformedValueClass; 14 | -(id)transformedValue:(id)value; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /CloudNotes/OCDateTransformer.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCDateTransformer.m 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/8/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import "OCDateTransformer.h" 10 | 11 | @implementation OCDateTransformer 12 | 13 | +(Class)transformedValueClass { 14 | return [NSString class]; 15 | } 16 | 17 | -(id)transformedValue:(id)value { 18 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:[value doubleValue]]; 19 | if (date) { 20 | NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; 21 | dateFormat.dateStyle = NSDateFormatterShortStyle; 22 | dateFormat.timeStyle = NSDateFormatterNoStyle; 23 | dateFormat.doesRelativeDateFormatting = YES; 24 | return [dateFormat stringFromDate:date]; 25 | } 26 | return value; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /CloudNotes/OCEditorSettings.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCEditorSettings.h 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/15/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OCEditorSettings : NSObject { 12 | NSFont *font; 13 | } 14 | 15 | @property (nonatomic, copy) NSFont *font; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /CloudNotes/OCEditorSettings.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCEditorSettings.m 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/15/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import "OCEditorSettings.h" 10 | 11 | @implementation OCEditorSettings 12 | 13 | @synthesize font; 14 | 15 | - (id) init { 16 | if (self = [super init]) { 17 | 18 | } 19 | return self; 20 | } 21 | 22 | #pragma mark - NSCoding Protocol 23 | 24 | - (id)initWithCoder:(NSCoder *)decoder { 25 | if ((self = [super init])) { 26 | font = [decoder decodeObjectForKey:@"font"]; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)encodeWithCoder:(NSCoder *)encoder { 32 | [encoder encodeObject:font forKey:@"font"]; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /CloudNotes/OCNote.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCNote.h 3 | // iOCNotes 4 | // 5 | // Created by Peter Hedlund on 3/10/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "FCModel.h" 11 | 12 | @interface OCNote : FCModel 13 | 14 | // database columns: 15 | @property (nonatomic, copy) NSString *guid; 16 | @property (nonatomic, assign) int32_t id; 17 | @property (nonatomic, copy) NSString *title; 18 | @property (nonatomic, copy) NSString *content; 19 | @property (nonatomic, assign) double modified; 20 | 21 | // non-columns: 22 | @property (nonatomic) NSInteger status; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /CloudNotes/OCNote.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCNote.m 3 | // iOCNotes 4 | // 5 | // Created by Peter Hedlund on 3/10/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import "OCNote.h" 10 | 11 | @implementation OCNote 12 | 13 | - (BOOL)shouldInsert { 14 | return YES; 15 | } 16 | 17 | - (BOOL)shouldUpdate { 18 | return YES; 19 | } 20 | 21 | + (id)primaryKeyValueForNewInstance { 22 | NSString * result; 23 | CFUUIDRef theUUID = CFUUIDCreate(NULL); 24 | CFStringRef string = CFUUIDCreateString(NULL, theUUID); 25 | CFRelease(theUUID); 26 | 27 | result =[NSString stringWithFormat:@"%@", string]; 28 | assert(result != nil); 29 | 30 | //NSLog(@"%@",result); 31 | return result; 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /CloudNotes/OCNotesHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCNewsHelper.h 3 | // iOCNews 4 | // 5 | 6 | /************************************************************************ 7 | 8 | Copyright 2013 Peter Hedlund peter.hedlund@me.com 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions 12 | are met: 13 | 14 | 1. Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *************************************************************************/ 32 | 33 | #import 34 | #import 35 | #import "OCNote.h" 36 | 37 | @interface OCNotesHelper : NSObject 38 | 39 | + (OCNotesHelper *)sharedHelper; 40 | - (void)sync; 41 | - (void)addNote:(NSString*)content; 42 | - (void)getNote:(OCNote*)note; 43 | - (void)updateNote:(OCNote*)note; 44 | - (void)deleteNote:(OCNote*)note; 45 | - (NSURL*) documentsDirectoryURL; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /CloudNotes/OCNotesHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCNotesHelper.m 3 | // iOCNotes 4 | // 5 | 6 | /************************************************************************ 7 | 8 | Copyright 2014 Peter Hedlund peter.hedlund@me.com 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions 12 | are met: 13 | 14 | 1. Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *************************************************************************/ 32 | 33 | #import "OCNotesHelper.h" 34 | #import "OCAPIClient.h" 35 | #import "NSDictionary+HandleNull.h" 36 | //#import "OCNotesSettings.h" 37 | #import "FCModel.h" 38 | #import "OCNote.h" 39 | 40 | @interface OCNotesHelper () { 41 | NSMutableSet *notesToAdd; 42 | NSMutableSet *notesToDelete; 43 | NSMutableSet *notesToUpdate; 44 | BOOL online; 45 | } 46 | 47 | @end 48 | 49 | @implementation OCNotesHelper 50 | 51 | + (OCNotesHelper*)sharedHelper { 52 | static dispatch_once_t once_token; 53 | static id sharedHelper; 54 | dispatch_once(&once_token, ^{ 55 | sharedHelper = [[OCNotesHelper alloc] init]; 56 | }); 57 | return sharedHelper; 58 | } 59 | 60 | - (OCNotesHelper*)init { 61 | self = [super init]; 62 | if (!self) { 63 | return nil; 64 | } 65 | 66 | NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; 67 | notesToAdd = [NSMutableSet setWithArray:[[prefs arrayForKey:@"NotesToAdd"] mutableCopy]]; 68 | notesToDelete = [NSMutableSet setWithArray:[[prefs arrayForKey:@"NotesToDelete"] mutableCopy]]; 69 | notesToUpdate = [NSMutableSet setWithArray:[[prefs arrayForKey:@"NotesToUpdate"] mutableCopy]]; 70 | 71 | [self setupDatabase]; 72 | 73 | online = [[OCAPIClient sharedClient] reachabilityManager].isReachable; 74 | 75 | [[NSNotificationCenter defaultCenter] addObserver:self 76 | selector:@selector(appDidBecomeActive:) 77 | name:NSApplicationDidBecomeActiveNotification 78 | object:nil]; 79 | 80 | [[NSNotificationCenter defaultCenter] addObserver:self 81 | selector:@selector(reachabilityChanged:) 82 | name:AFNetworkingReachabilityDidChangeNotification 83 | object:nil]; 84 | 85 | return self; 86 | } 87 | 88 | - (void)reachabilityChanged:(NSNotification *)n { 89 | NSNumber *s = n.userInfo[AFNetworkingReachabilityNotificationStatusItem]; 90 | AFNetworkReachabilityStatus status = [s integerValue]; 91 | 92 | if (status == AFNetworkReachabilityStatusNotReachable) { 93 | online = NO; 94 | } 95 | if (status > AFNetworkReachabilityStatusNotReachable) { 96 | online = YES; 97 | } 98 | } 99 | 100 | - (void)appDidBecomeActive:(NSNotification*)n { 101 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"dbReset"]) { 102 | notesToAdd = [NSMutableSet new]; 103 | notesToDelete = [NSMutableSet new]; 104 | notesToUpdate = [NSMutableSet new]; 105 | 106 | NSURL *dbURL = [self documentsDirectoryURL]; 107 | dbURL = [dbURL URLByAppendingPathComponent:@"Notes" isDirectory:NO]; 108 | dbURL = [dbURL URLByAppendingPathExtension:@"db"]; 109 | [NSFileManager.defaultManager removeItemAtPath:dbURL.path error:nil]; 110 | [self setupDatabase]; 111 | [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"dbReset"]; 112 | [[NSNotificationCenter defaultCenter] postNotificationName:FCModelUpdateNotification object:OCNote.class]; 113 | [self savePrefs]; 114 | } 115 | } 116 | 117 | - (void)setupDatabase { 118 | NSURL *dbURL = [self documentsDirectoryURL]; 119 | dbURL = [dbURL URLByAppendingPathComponent:@"Notes" isDirectory:NO]; 120 | dbURL = [dbURL URLByAppendingPathExtension:@"db"]; 121 | //[NSFileManager.defaultManager removeItemAtPath:dbURL.path error:nil]; 122 | 123 | [FCModel openDatabaseAtPath:dbURL.path withSchemaBuilder:^(FMDatabase *db, int *schemaVersion) { 124 | [db setCrashOnErrors:NO]; 125 | db.traceExecution = YES; // Log every query (useful to learn what FCModel is doing or analyze performance) 126 | [db beginTransaction]; 127 | 128 | void (^failedAt)(int statement) = ^(int statement){ 129 | int lastErrorCode = db.lastErrorCode; 130 | NSString *lastErrorMessage = db.lastErrorMessage; 131 | [db rollback]; 132 | NSAssert3(0, @"Migration statement %d failed, code %d: %@", statement, lastErrorCode, lastErrorMessage); 133 | }; 134 | 135 | if (*schemaVersion < 1) { 136 | if (! [db executeUpdate: 137 | @"CREATE TABLE OCNote (" 138 | @" id INTEGER PRIMARY KEY," // Autoincrement is optional. Just demonstrating that it works. 139 | @" title TEXT NOT NULL DEFAULT ''," 140 | @" content TEXT NOT NULL DEFAULT ''," 141 | @" modified INTEGER NOT NULL" 142 | @");" 143 | ]) failedAt(1); 144 | /*if (! [db executeUpdate:@"CREATE UNIQUE INDEX IF NOT EXISTS title ON Note (title);"]) failedAt(2); 145 | 146 | if (! [db executeUpdate: 147 | @"CREATE TABLE Color (" 148 | @" name TEXT NOT NULL PRIMARY KEY," 149 | @" hex TEXT NOT NULL" 150 | @");" 151 | ]) failedAt(3); 152 | */ 153 | // Create any other tables... 154 | 155 | *schemaVersion = 1; 156 | } 157 | 158 | // If you wanted to change the schema in a later app version, you'd add something like this here: 159 | 160 | if (*schemaVersion < 2) { 161 | if (! [db executeUpdate:@"ALTER TABLE OCNote RENAME TO OCNote_temp"]) failedAt(3); 162 | 163 | if (! [db executeUpdate: 164 | @"CREATE TABLE OCNote (" 165 | @" guid TEXT PRIMARY KEY," 166 | @" id INTEGER," 167 | @" title TEXT NOT NULL DEFAULT ''," 168 | @" content TEXT NOT NULL DEFAULT ''," 169 | @" modified INTEGER NOT NULL" 170 | @");" 171 | ]) failedAt(4); 172 | 173 | FMResultSet *rs = [db executeQuery:@"SELECT * FROM OCNote_temp"]; 174 | while ([rs next]) { 175 | NSString *guid = [OCNote primaryKeyValueForNewInstance]; 176 | int myID = [rs intForColumn:@"id"]; 177 | NSString *title = [rs stringForColumn:@"title"]; 178 | NSString *content = [rs stringForColumn:@"content"]; 179 | int modified = [rs doubleForColumn:@"modified"]; 180 | NSString *keys = @"(guid, id, title, content, modified)"; 181 | NSArray *args = @[guid, 182 | [NSNumber numberWithInteger:myID], 183 | title, 184 | content, 185 | [NSNumber numberWithDouble:modified]]; 186 | 187 | NSString *sql = [NSString stringWithFormat:@"INSERT INTO OCNote %@ VALUES (?, ?, ?, ?, ?);", keys]; 188 | if (! [db executeUpdate:sql withArgumentsInArray:args]) failedAt(5); 189 | 190 | } 191 | 192 | if (! [db executeUpdate:@"DROP TABLE OCNote_temp"]) failedAt(6); 193 | 194 | *schemaVersion = 2; 195 | } 196 | 197 | [db commit]; 198 | }]; 199 | } 200 | 201 | - (NSURL*) documentsDirectoryURL { 202 | // Returns the directory the application uses to store the Core Data store file. This code uses a directory named "com.peterandlinda.CloudNotes" in the user's Application Support directory. 203 | NSFileManager *fileManager = [NSFileManager defaultManager]; 204 | NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject]; 205 | appSupportURL = [appSupportURL URLByAppendingPathComponent:@"com.peterandlinda.CloudNotes"]; 206 | 207 | BOOL isFolder; 208 | NSLog(@"URL: %@", appSupportURL); 209 | if (![fileManager fileExistsAtPath:appSupportURL.path isDirectory:&isFolder]) 210 | { 211 | NSLog(@"IsFolder: %d", isFolder); 212 | [[NSFileManager defaultManager] createDirectoryAtURL:appSupportURL withIntermediateDirectories:YES attributes:nil error:nil]; 213 | } 214 | return appSupportURL; 215 | } 216 | 217 | - (void)savePrefs { 218 | NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; 219 | [prefs setObject:notesToAdd.allObjects forKey:@"NotesToAdd"]; 220 | [prefs setObject:notesToDelete.allObjects forKey:@"NotesToDelete"]; 221 | [prefs setObject:notesToUpdate.allObjects forKey:@"NotesToUpdate"]; 222 | [prefs synchronize]; 223 | } 224 | 225 | /* 226 | Get all notes 227 | 228 | Status: Implemented 229 | Method: GET 230 | Route: /notes 231 | Parameters: none 232 | Returns: 233 | 234 | [ 235 | { 236 | id: 76, 237 | modified: 1376753464, 238 | title: "New note" 239 | content: "New note\n and something more", 240 | }, // etc 241 | ] 242 | */ 243 | - (void) sync { 244 | if (online) { 245 | 246 | NSDictionary *params = @{@"exclude": @""}; 247 | [[OCAPIClient sharedClient] GET:@"notes" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 248 | 249 | NSArray *serverNotesDictArray = (NSArray *)responseObject; 250 | if (serverNotesDictArray) { 251 | [serverNotesDictArray enumerateObjectsUsingBlock:^(NSDictionary *noteDict, NSUInteger idx, BOOL *stop) { 252 | OCNote *ocNote = [OCNote firstInstanceWhere:[NSString stringWithFormat:@"id=%@", [noteDict objectForKey:@"id"]]]; 253 | //OCNote *ocNote = [OCNote instanceWithPrimaryKey:[noteDict objectForKey:@"id"] createIfNonexistent:YES]; 254 | if (!ocNote) { //don't re-add a deleted note (it will be deleted from the server below). 255 | if (![notesToDelete containsObject:[noteDict objectForKey:@"id"]]) { 256 | ocNote = [OCNote new]; 257 | ocNote.id = [[noteDict objectForKey:@"id"] intValue]; 258 | ocNote.modified = [[noteDict objectForKey:@"modified"] doubleValue]; 259 | ocNote.title = [noteDict objectForKey:@"title"]; 260 | ocNote.content = [noteDict objectForKeyNotNull:@"content" fallback:@""]; 261 | [ocNote save]; 262 | } 263 | } else { 264 | if (ocNote.modified > [[noteDict objectForKey:@"modified"] doubleValue]) { 265 | [notesToUpdate addObject:[noteDict objectForKey:@"id"]]; 266 | [self savePrefs]; 267 | } else { 268 | ocNote.modified = [[noteDict objectForKey:@"modified"] doubleValue]; 269 | ocNote.title = [noteDict objectForKey:@"title"]; 270 | ocNote.content = [noteDict objectForKeyNotNull:@"content" fallback:@""]; 271 | if ([ocNote existsInDatabase]) { 272 | [ocNote save]; 273 | } 274 | } 275 | } 276 | }]; 277 | 278 | NSArray *serverIds = [serverNotesDictArray valueForKey:@"id"]; 279 | 280 | NSArray *knownIds = [[OCNote resultDictionariesFromQuery:@"SELECT * FROM $T WHERE id > 0"] valueForKey:@"id"]; 281 | 282 | NSLog(@"Count: %lu", (unsigned long)knownIds.count); 283 | 284 | NSMutableArray *deletedOnServer = [NSMutableArray arrayWithArray:knownIds]; 285 | [deletedOnServer removeObjectsInArray:serverIds]; 286 | //TODO: Fix [deletedOnServer removeObjectsInArray:notesToAdd]; 287 | NSLog(@"Deleted on server: %@", deletedOnServer); 288 | while (deletedOnServer.count > 0) { 289 | OCNote *ocNote = [OCNote firstInstanceWhere:[NSString stringWithFormat:@"id=%@", [deletedOnServer lastObject]]]; 290 | [notesToAdd removeObject:ocNote.guid]; 291 | if (ocNote.id > 0) { 292 | [notesToDelete removeObject:@(ocNote.id)]; 293 | [notesToUpdate removeObject:@(ocNote.id)]; 294 | } 295 | [ocNote delete]; 296 | [deletedOnServer removeLastObject]; 297 | } 298 | } 299 | [self deleteNotesFromServer]; 300 | [self addNotesToServer]; 301 | [self updateNotesOnServer]; 302 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkSuccess" object:self userInfo:nil]; 303 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 304 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Updating Notes", @"The title of an error message"), 305 | @"Message": [error localizedDescription]}; 306 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 307 | }]; 308 | } else { 309 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Unable to Reach Server", @"The title of an error message"), 310 | @"Message": @"Please check network connection and login."}; 311 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 312 | } 313 | } 314 | 315 | /* 316 | Get a note 317 | 318 | Status: Implemented 319 | Method: GET 320 | Route: /notes/{noteId} 321 | Parameters: none 322 | Return codes: 323 | HTTP 404: If the note does not exist 324 | Returns: 325 | 326 | { 327 | id: 76, 328 | modified: 1376753464, 329 | title: "New note" 330 | content: "New note\n and something more", 331 | } 332 | */ 333 | 334 | - (void)getNote:(OCNote *)note { 335 | if (online) { 336 | if (note.id > 0) { 337 | NSString *path = [NSString stringWithFormat:@"notes/%@", [NSNumber numberWithLongLong:note.id].stringValue]; 338 | __block OCNote *noteToGet = [OCNote firstInstanceWhere:[NSString stringWithFormat:@"id=%@", [NSNumber numberWithLongLong:note.id]]]; 339 | if (noteToGet) { 340 | NSDictionary *params = @{@"exclude": @"title,content"}; 341 | [[OCAPIClient sharedClient] GET:path parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 342 | //NSLog(@"Note: %@", responseObject); 343 | NSDictionary *noteDict = (NSDictionary*)responseObject; 344 | NSLog(@"NoteDict: %@", noteDict); 345 | if ([[NSNumber numberWithLongLong:noteToGet.id] isEqualToNumber:[noteDict objectForKey:@"id"]]) { 346 | if ([[noteDict objectForKey:@"modified"] doubleValue] > noteToGet.modified) { 347 | //The server has a newer version. We need to get it. 348 | [[OCAPIClient sharedClient] GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 349 | //NSLog(@"Note: %@", responseObject); 350 | NSDictionary *noteDict = (NSDictionary*)responseObject; 351 | NSLog(@"NoteDict: %@", noteDict); 352 | if ([[NSNumber numberWithLongLong:note.id] isEqualToNumber:[noteDict objectForKey:@"id"]]) { 353 | if ([[noteDict objectForKey:@"modified"] doubleValue] > noteToGet.modified) { 354 | noteToGet.title = [noteDict objectForKeyNotNull:@"title" fallback:@""]; 355 | noteToGet.content = [noteDict objectForKeyNotNull:@"content" fallback:@""]; 356 | noteToGet.modified = [[noteDict objectForKey:@"modified"] doubleValue]; 357 | } 358 | if ([noteToGet existsInDatabase]) { 359 | [noteToGet save]; 360 | } 361 | } 362 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 363 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 364 | NSString *message; 365 | switch (response.statusCode) { 366 | case 404: 367 | message = NSLocalizedString(@"The note does not exist", @"An error message"); 368 | break; 369 | default: 370 | message = [error localizedDescription]; 371 | break; 372 | } 373 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Getting Note", @"The title of an error message"), 374 | @"Message": message}; 375 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 376 | }]; 377 | 378 | } 379 | 380 | } 381 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 382 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 383 | NSString *message; 384 | switch (response.statusCode) { 385 | case 404: 386 | message = NSLocalizedString(@"The note does not exist", @"An error message"); 387 | break; 388 | default: 389 | message = [error localizedDescription]; 390 | break; 391 | } 392 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Getting Note", @"The title of an error message"), 393 | @"Message": message}; 394 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 395 | }]; 396 | } 397 | } 398 | else 399 | { 400 | NSDictionary *params = @{@"content": note.content}; 401 | [[OCAPIClient sharedClient] POST:@"notes" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 402 | //NSLog(@"Note: %@", responseObject); 403 | NSDictionary *noteDict = (NSDictionary*)responseObject; 404 | note.id = [[noteDict objectForKey:@"id"] intValue]; 405 | note.title = [noteDict objectForKey:@"title"]; 406 | note.content = [noteDict objectForKeyNotNull:@"content" fallback:@""]; 407 | note.modified = [[noteDict objectForKey:@"modified"] doubleValue]; 408 | if ([note existsInDatabase]) { 409 | [note save]; 410 | } 411 | [notesToAdd removeObject:note.guid]; 412 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 413 | //TODO: Determine what to do with failures. 414 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 415 | NSString *message; 416 | switch (response.statusCode) { 417 | default: 418 | message = [error localizedDescription]; 419 | break; 420 | } 421 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Adding Note", @"The title of an error message"), 422 | @"Message": message}; 423 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 424 | }]; 425 | } 426 | } else { 427 | //offline 428 | } 429 | } 430 | 431 | 432 | /* 433 | Create a note 434 | 435 | Creates a new note and returns the note. The title is generated from the first line of the content. If no content is passed, a translated string New note will be returned as title 436 | 437 | Status: Implemented 438 | Method: POST 439 | Route: /notes 440 | Parameters: 441 | 442 | { 443 | content: "New content" 444 | } 445 | 446 | Returns: 447 | 448 | { 449 | id: 76, 450 | content: "", 451 | modified: 1376753464, 452 | title: "" 453 | } 454 | */ 455 | 456 | - (void)addNote:(NSString*)content { 457 | __block OCNote *newNote = [OCNote new]; 458 | newNote.title = NSLocalizedString(@"New note", @"The title of a new note"); 459 | newNote.content = content; 460 | newNote.modified = [[NSDate date] timeIntervalSince1970]; 461 | [newNote save]; 462 | 463 | if (online) { 464 | NSDictionary *params = @{@"content": newNote.content}; 465 | [[OCAPIClient sharedClient] POST:@"notes" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 466 | NSDictionary *noteDict = (NSDictionary*)responseObject; 467 | newNote.id = [[noteDict objectForKey:@"id"] intValue]; 468 | newNote.modified = [[noteDict objectForKey:@"modified"] doubleValue]; 469 | newNote.title = [noteDict objectForKey:@"title"]; 470 | newNote.content = [noteDict objectForKeyNotNull:@"content" fallback:@""]; 471 | [newNote save]; 472 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 473 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 474 | NSString *message; 475 | switch (response.statusCode) { 476 | default: 477 | message = [error localizedDescription]; 478 | break; 479 | } 480 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Adding Note", @"The title of an error message"), 481 | @"Message": message}; 482 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 483 | [notesToAdd addObject:newNote.guid]; 484 | [self savePrefs]; 485 | }]; 486 | 487 | } else { 488 | [notesToAdd addObject:newNote.guid]; 489 | [self savePrefs]; 490 | } 491 | } 492 | 493 | /* 494 | Update a note 495 | 496 | Updates a note with the id noteId. Always update your app with the returned title because the title can be renamed if there are collisions on the server. The title is generated from the first line of the content. If no content is passed, a translated string New note will be returned as title 497 | 498 | Status: Implemented 499 | Method: PUT 500 | Route: /notes/{noteId} 501 | Parameters: 502 | 503 | { 504 | content: "New content", 505 | } 506 | 507 | Return codes: 508 | HTTP 404: If the note does not exist 509 | Returns: 510 | 511 | { 512 | id: 76, 513 | content: "New content", 514 | modified: 1376753464, 515 | title: "New title" 516 | } 517 | */ 518 | 519 | - (void)updateNote:(OCNote*)note { 520 | if (online) { 521 | //online 522 | if (note.id > 0) { 523 | NSDictionary *params = @{@"content": note.content}; 524 | NSString *path = [NSString stringWithFormat:@"notes/%@",[NSNumber numberWithLongLong:note.id]]; 525 | __block OCNote *noteToUpdate = [OCNote instanceWithPrimaryKey:note.guid]; 526 | 527 | [[OCAPIClient sharedClient] PUT:path parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 528 | //NSLog(@"Note: %@", responseObject); 529 | NSDictionary *noteDict = (NSDictionary*)responseObject; 530 | if ([[NSNumber numberWithLongLong:note.id] isEqualToNumber:[noteDict objectForKey:@"id"]]) { 531 | noteToUpdate.title = [noteDict objectForKey:@"title"]; 532 | noteToUpdate.content = [noteDict objectForKeyNotNull:@"content" fallback:@""];; 533 | noteToUpdate.modified = [[noteDict objectForKey:@"modified"] doubleValue]; 534 | if (noteToUpdate.existsInDatabase) { 535 | [noteToUpdate save]; 536 | } 537 | } 538 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 539 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 540 | NSString *message; 541 | switch (response.statusCode) { 542 | case 404: 543 | message = NSLocalizedString(@"The note does not exist", @"An error message"); 544 | break; 545 | default: 546 | message = [error localizedDescription]; 547 | break; 548 | } 549 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Updating Note", @"The title of an error message"), 550 | @"Message": message}; 551 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 552 | noteToUpdate.modified = [[NSDate date] timeIntervalSince1970]; 553 | if (noteToUpdate.existsInDatabase) { 554 | [noteToUpdate save]; 555 | } 556 | }]; 557 | } else { 558 | NSDictionary *params = @{@"content": note.content}; 559 | [[OCAPIClient sharedClient] POST:@"notes" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 560 | //NSLog(@"Note: %@", responseObject); 561 | NSDictionary *noteDict = (NSDictionary*)responseObject; 562 | note.id = [[noteDict objectForKey:@"id"] intValue]; 563 | note.title = [noteDict objectForKey:@"title"]; 564 | note.content = [noteDict objectForKeyNotNull:@"content" fallback:@""]; 565 | note.modified = [[noteDict objectForKey:@"modified"] doubleValue]; 566 | if ([note existsInDatabase]) { 567 | [note save]; 568 | } 569 | [notesToAdd removeObject:note.guid]; 570 | [notesToUpdate removeObject:@(note.id)]; 571 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 572 | //TODO: Determine what to do with failures. 573 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 574 | NSString *message; 575 | switch (response.statusCode) { 576 | default: 577 | message = [error localizedDescription]; 578 | break; 579 | } 580 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Adding Note", @"The title of an error message"), 581 | @"Message": message}; 582 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 583 | }]; 584 | } 585 | 586 | } else { 587 | //offline 588 | note.modified = [[NSDate date] timeIntervalSince1970]; 589 | if (note.existsInDatabase) { 590 | [note save]; 591 | } 592 | if (note.id > 0) { // Has been synced at least once and is not included in notesToAdd 593 | [notesToUpdate addObject:[NSNumber numberWithLongLong:note.id]]; 594 | } 595 | [self savePrefs]; 596 | } 597 | } 598 | 599 | /* 600 | Delete a note 601 | 602 | Deletes a note with the id noteId 603 | 604 | Status: Implemented 605 | Method: DELETE 606 | Route: /notes/{noteId} 607 | Parameters: none 608 | Return codes: 609 | HTTP 404: If the note does not exist 610 | Returns: nothing 611 | */ 612 | 613 | - (void) deleteNote:(OCNote *)note { 614 | __block OCNote *noteToDelete = [OCNote instanceWithPrimaryKey:note.guid]; 615 | __block int noteId = noteToDelete.id; 616 | [noteToDelete delete]; 617 | 618 | if (noteId > 0) { 619 | if (online) { 620 | NSString *path = [NSString stringWithFormat:@"notes/%@", [NSNumber numberWithInt:noteId]]; 621 | [[OCAPIClient sharedClient] DELETE:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 622 | NSLog(@"Success deleting note"); 623 | //[noteToDelete delete]; 624 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 625 | NSLog(@"Failure to delete note"); 626 | [notesToDelete addObject:[NSNumber numberWithInt:noteId]]; 627 | [self savePrefs]; 628 | //[noteToDelete delete]; 629 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Deleting Note", @"The title of an error message"), 630 | @"Message": [error localizedDescription]}; 631 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 632 | }]; 633 | } else { 634 | //offline 635 | [notesToDelete addObject:[NSNumber numberWithInt:noteId]]; 636 | [self savePrefs]; 637 | } 638 | } 639 | } 640 | 641 | - (void)updateNotesOnServer { 642 | __block NSMutableArray *successfulUpdates = [NSMutableArray new]; 643 | __block NSMutableArray *failedUpdates = [NSMutableArray new]; 644 | 645 | dispatch_group_t updateGroup = dispatch_group_create(); 646 | [notesToUpdate enumerateObjectsUsingBlock:^(NSNumber *noteId, BOOL *stop) { 647 | __block OCNote *noteToUpdate = [OCNote firstInstanceWhere:[NSString stringWithFormat:@"id=%@", noteId]]; 648 | if (noteToUpdate) { 649 | dispatch_group_enter(updateGroup); 650 | NSDictionary *params = @{@"content": noteToUpdate.content}; 651 | NSString *path = [NSString stringWithFormat:@"notes/%@", noteId]; 652 | [[OCAPIClient sharedClient] PUT:path parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 653 | //NSLog(@"Note: %@", responseObject); 654 | @synchronized(successfulUpdates) { 655 | NSDictionary *noteDict = (NSDictionary*)responseObject; 656 | if ([noteId isEqualToNumber:[noteDict objectForKey:@"id"]]) { 657 | noteToUpdate.title = [noteDict objectForKey:@"title"]; 658 | noteToUpdate.content = [noteDict objectForKeyNotNull:@"content" fallback:@""];; 659 | noteToUpdate.modified = [[noteDict objectForKey:@"modified"] doubleValue]; 660 | [noteToUpdate save]; 661 | [successfulUpdates addObject:[noteDict objectForKey:@"id"]]; 662 | } 663 | } 664 | dispatch_group_leave(updateGroup); 665 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 666 | //TODO: Determine what to do with failures. 667 | NSString *failedId = [task.originalRequest.URL lastPathComponent]; 668 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 669 | NSString *message; 670 | switch (response.statusCode) { 671 | case 404: 672 | NSLog(@"Id %@ no longer exists", failedId); 673 | @synchronized(successfulUpdates) { 674 | [successfulUpdates addObject:[NSNumber numberWithInteger:[failedId integerValue]]]; 675 | } 676 | break; 677 | default: 678 | @synchronized(failedUpdates) { 679 | [failedUpdates addObject:[NSNumber numberWithInteger:[failedId integerValue]]]; 680 | } 681 | message = [error localizedDescription]; 682 | break; 683 | } 684 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Updating Note", @"The title of an error message"), 685 | @"Message": message}; 686 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 687 | dispatch_group_leave(updateGroup); 688 | }]; 689 | } 690 | }]; 691 | dispatch_group_notify(updateGroup, dispatch_get_main_queue(), ^{ 692 | [successfulUpdates enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 693 | [notesToUpdate removeObject:obj]; 694 | }]; 695 | }); 696 | } 697 | 698 | - (void)addNotesToServer { 699 | __block NSMutableArray *successfulAdditions = [NSMutableArray new]; 700 | __block NSMutableArray *failedAdditions = [NSMutableArray new]; 701 | 702 | dispatch_group_t addGroup = dispatch_group_create(); 703 | [notesToAdd enumerateObjectsUsingBlock:^(NSString *noteGuid, BOOL *stop) { 704 | 705 | __block OCNote *ocNote = [OCNote instanceWithPrimaryKey:noteGuid]; 706 | if (ocNote) { 707 | dispatch_group_enter(addGroup); 708 | if (ocNote.content.length <= 0) { 709 | //Don't add empty notes 710 | [successfulAdditions addObject:ocNote.guid]; 711 | } else if (ocNote.id > 0) { 712 | //Already added for some reason 713 | [successfulAdditions addObject:ocNote.guid]; 714 | } else { 715 | NSDictionary *params = @{@"content": ocNote.content}; 716 | [[OCAPIClient sharedClient] POST:@"notes" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 717 | //NSLog(@"Note: %@", responseObject); 718 | @synchronized(successfulAdditions) { 719 | NSDictionary *noteDict = (NSDictionary*)responseObject; 720 | ocNote.id = [[noteDict objectForKey:@"id"] intValue]; 721 | ocNote.title = [noteDict objectForKey:@"title"]; 722 | ocNote.content = [noteDict objectForKeyNotNull:@"content" fallback:@""]; 723 | ocNote.modified = [[noteDict objectForKey:@"modified"] doubleValue]; 724 | [ocNote save]; 725 | [successfulAdditions addObject:ocNote.guid]; 726 | } 727 | dispatch_group_leave(addGroup); 728 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 729 | //TODO: Determine what to do with failures. 730 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 731 | NSString *message; 732 | switch (response.statusCode) { 733 | default: 734 | message = [error localizedDescription]; 735 | break; 736 | } 737 | NSDictionary *userInfo = @{@"Title": NSLocalizedString(@"Error Adding Note", @"The title of an error message"), 738 | @"Message": message}; 739 | [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkError" object:self userInfo:userInfo]; 740 | [failedAdditions addObject:ocNote.guid]; 741 | dispatch_group_leave(addGroup); 742 | }]; 743 | } 744 | } 745 | }]; 746 | dispatch_group_notify(addGroup, dispatch_get_main_queue(), ^{ 747 | [successfulAdditions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 748 | [notesToAdd removeObject:obj]; 749 | }]; 750 | }); 751 | } 752 | 753 | - (void)deleteNotesFromServer { 754 | __block NSMutableArray *successfulDeletions = [NSMutableArray new]; 755 | __block NSMutableArray *failedDeletions = [NSMutableArray new]; 756 | 757 | dispatch_group_t deleteGroup = dispatch_group_create(); 758 | [notesToDelete enumerateObjectsUsingBlock:^(NSNumber *noteId, BOOL *stop) { 759 | dispatch_group_enter(deleteGroup); 760 | NSString *path = [NSString stringWithFormat:@"notes/%@", noteId]; 761 | [[OCAPIClient sharedClient] DELETE:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) { 762 | NSLog(@"Success deleting from server"); 763 | @synchronized(successfulDeletions) { 764 | NSString *successId = [task.originalRequest.URL lastPathComponent]; 765 | [successfulDeletions addObject:[NSNumber numberWithInteger:[successId integerValue]]]; 766 | } 767 | dispatch_group_leave(deleteGroup); 768 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 769 | NSLog(@"Failure to delete from server"); 770 | NSString *failedId = [task.originalRequest.URL lastPathComponent]; 771 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 772 | switch (response.statusCode) { 773 | case 404: 774 | NSLog(@"Id %@ no longer exists", failedId); 775 | @synchronized(successfulDeletions) { 776 | [successfulDeletions addObject:[NSNumber numberWithInteger:[failedId integerValue]]]; 777 | } 778 | break; 779 | default: 780 | NSLog(@"Status code: %ld", (long)response.statusCode); 781 | @synchronized(failedDeletions) { 782 | [failedDeletions addObject:[NSNumber numberWithInteger:[failedId integerValue]]]; 783 | } 784 | break; 785 | } 786 | dispatch_group_leave(deleteGroup); 787 | }]; 788 | }]; 789 | dispatch_group_notify(deleteGroup, dispatch_get_main_queue(), ^{ 790 | [successfulDeletions enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 791 | [notesToDelete removeObject:obj]; 792 | }]; 793 | }); 794 | } 795 | 796 | @end 797 | -------------------------------------------------------------------------------- /CloudNotes/OCNotesViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCNotesViewController.h 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/4/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "OCDateTransformer.h" 11 | 12 | @interface OCNotesViewController : NSViewController 13 | 14 | @property (strong) IBOutlet NSTableView *tableView; 15 | @property (strong, nonatomic) NSManagedObjectContext *context; 16 | @property (strong) IBOutlet NSArrayController *notesArrayController; 17 | @property (strong) IBOutlet NSSplitView *splitView; 18 | @property (strong, readonly) NSArray *idSortDescriptor; 19 | @property (strong) IBOutlet NSTextView *contentTextView; 20 | @property (strong) IBOutlet NSTableCellView *tableCellView; 21 | 22 | - (IBAction)doSync:(id)sender; 23 | - (IBAction)doAdd:(id)sender; 24 | - (IBAction)doDelete:(id)sender; 25 | - (IBAction)doExport:(id)sender; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /CloudNotes/OCNotesViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCNotesViewController.m 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/4/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import "OCNotesViewController.h" 10 | #import "OCNotesHelper.h" 11 | #import "OCNote.h" 12 | #import "OCEditorSettings.h" 13 | #import "NSSplitView+SaveLayout.h" 14 | 15 | @interface OCNotesViewController () { 16 | NSTimer *editingTimer; 17 | NSRange selection; 18 | CGPoint clipOrigin; 19 | NSResponder *currentResponder; 20 | NSInteger currentNoteId; 21 | } 22 | 23 | @end 24 | 25 | @implementation OCNotesViewController 26 | 27 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 28 | { 29 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 30 | if (self) { 31 | // Initialization code here. 32 | currentNoteId = 0; 33 | } 34 | return self; 35 | } 36 | 37 | - (void)awakeFromNib { 38 | [super awakeFromNib]; 39 | [self.splitView loadLayoutWithName:@"SplitViewLayout"]; 40 | 41 | self.contentTextView.textContainerInset = NSMakeSize(25, 25); 42 | 43 | [self updateFont]; 44 | [self reloadNotes:nil]; 45 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:0]; 46 | [self.tableView selectRowIndexes:indexSet byExtendingSelection:NO]; 47 | [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(reloadNotes:) name:FCModelAnyChangeNotification object:OCNote.class]; 48 | } 49 | 50 | - (void)reloadNotes:(NSNotification *)notification 51 | { 52 | [self.notesArrayController setContent:[OCNote instancesOrderedBy:@"modified DESC"]]; 53 | //NSLog(@"Reloading with %lu notes", (unsigned long) self.notesArrayController..count); 54 | [self.tableView reloadData]; 55 | [self noteUpdated:notification]; 56 | } 57 | 58 | 59 | - (IBAction)doSync:(id)sender { 60 | [[OCNotesHelper sharedHelper] sync]; 61 | } 62 | 63 | - (IBAction)doAdd:(id)sender { 64 | [[OCNotesHelper sharedHelper] addNote:@""]; 65 | NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:0]; 66 | [self.tableView selectRowIndexes:indexSet byExtendingSelection:NO]; 67 | [self.contentTextView.window makeFirstResponder:self.contentTextView]; 68 | } 69 | 70 | - (IBAction)doDelete:(id)sender { 71 | OCNote *noteToDelete = nil; 72 | if ([[self.notesArrayController selectedObjects] count] > 0) { 73 | noteToDelete = (OCNote*)[[self.notesArrayController selectedObjects] objectAtIndex:0]; 74 | [[OCNotesHelper sharedHelper] deleteNote:noteToDelete]; 75 | } 76 | } 77 | 78 | - (IBAction)doExport:(id)sender { 79 | if ([[self.notesArrayController selectedObjects] count] > 0) { 80 | __block OCNote *noteToExport = (OCNote*)[[self.notesArrayController selectedObjects] objectAtIndex:0]; 81 | 82 | NSSavePanel* savePanel = [NSSavePanel savePanel]; 83 | savePanel.title = @"Export note as..."; 84 | savePanel.showsResizeIndicator = YES; 85 | savePanel.showsHiddenFiles = NO; 86 | savePanel.canCreateDirectories = YES; 87 | savePanel.allowedFileTypes = @[@"txt"]; 88 | savePanel.nameFieldStringValue = noteToExport.title; 89 | 90 | [savePanel beginSheetModalForWindow:self.view.window 91 | completionHandler:^(NSInteger result) { 92 | if (result == NSFileHandlingPanelOKButton) { 93 | NSString *contentToExport = noteToExport.content; 94 | [contentToExport writeToURL:savePanel.URL atomically:YES encoding:NSUTF8StringEncoding error:nil]; 95 | } 96 | }]; 97 | } 98 | } 99 | 100 | - (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview { 101 | return NO; 102 | } 103 | 104 | #pragma mark - Split View Delegate 105 | 106 | - (CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedCoordinate ofSubviewAt:(int)index 107 | { 108 | return proposedCoordinate + 120; 109 | } 110 | 111 | - (CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedCoordinate ofSubviewAt:(int)index 112 | { 113 | return proposedCoordinate - 120; 114 | } 115 | 116 | - (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize 117 | { 118 | NSRect newFrame = [sender frame]; // get the new size of the whole splitView 119 | NSView *left = [[sender subviews] objectAtIndex:0]; 120 | NSRect leftFrame = [left frame]; 121 | NSView *right = [[sender subviews] objectAtIndex:1]; 122 | NSRect rightFrame = [right frame]; 123 | 124 | CGFloat dividerThickness = [sender dividerThickness]; 125 | 126 | leftFrame.size.height = newFrame.size.height; 127 | 128 | rightFrame.size.width = newFrame.size.width - leftFrame.size.width - dividerThickness; 129 | rightFrame.size.height = newFrame.size.height; 130 | rightFrame.origin.x = leftFrame.size.width + dividerThickness; 131 | 132 | [left setFrame:leftFrame]; 133 | [right setFrame:rightFrame]; 134 | } 135 | 136 | #pragma mark - Table View Delegate 137 | 138 | - (void)tableViewSelectionDidChange:(NSNotification *)aNotification { 139 | [self.tableCellView bind:NSSelectedObjectBinding toObject:self.notesArrayController withKeyPath:@"selection" options:nil]; 140 | OCNote *note = [[self.notesArrayController selectedObjects] firstObject]; 141 | NSLog(@"Selected Row: %ld Title %@", (long)self.tableView.selectedRow, note.title); 142 | 143 | [[OCNotesHelper sharedHelper] getNote:note]; 144 | self.contentTextView.string = note.content; 145 | if (currentNoteId != note.id) { 146 | selection = NSMakeRange(0, 0); 147 | } 148 | currentNoteId = note.id; 149 | [self updateFont]; 150 | } 151 | 152 | - (NSArray *)idSortDescriptor { 153 | return @[[NSSortDescriptor sortDescriptorWithKey:@"modified" ascending:NO]]; 154 | } 155 | 156 | #pragma mark - Text View Delegate 157 | 158 | - (void)textDidChange:(NSNotification *)obj { 159 | if (editingTimer) { 160 | [editingTimer invalidate]; 161 | editingTimer = nil; 162 | } 163 | editingTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(updateText:) userInfo:nil repeats:NO]; 164 | } 165 | 166 | - (void)updateText:(NSTimer*)timer { 167 | NSLog(@"Ready to update text"); 168 | selection = self.contentTextView.selectedRange; 169 | clipOrigin = self.contentTextView.enclosingScrollView.contentView.bounds.origin; 170 | currentResponder = self.contentTextView.window.firstResponder; 171 | OCNote *noteToUpdate = nil; 172 | if ([[self.notesArrayController selectedObjects] count] > 0) { 173 | noteToUpdate = (OCNote*)[[self.notesArrayController selectedObjects] objectAtIndex:0]; 174 | [[OCNotesHelper sharedHelper] updateNote:noteToUpdate]; 175 | } 176 | } 177 | 178 | - (void)noteUpdated:(NSNotification *)notification { 179 | [self updateFont]; 180 | self.contentTextView.selectedRange = selection; 181 | [self.contentTextView.enclosingScrollView.contentView scrollToPoint: clipOrigin]; 182 | [self.contentTextView.enclosingScrollView reflectScrolledClipView:self.contentTextView.enclosingScrollView.contentView]; 183 | if (currentResponder) { 184 | [self.contentTextView.window makeFirstResponder:currentResponder]; 185 | } 186 | } 187 | 188 | - (void)updateFont { 189 | NSURL *saveUrl = [[OCNotesHelper sharedHelper] documentsDirectoryURL]; 190 | saveUrl = [saveUrl URLByAppendingPathComponent:@"settings" isDirectory:NO]; 191 | saveUrl = [saveUrl URLByAppendingPathExtension:@"plist"]; 192 | if ([[NSFileManager defaultManager] fileExistsAtPath:[saveUrl path]]) { 193 | OCEditorSettings *settings = [NSKeyedUnarchiver unarchiveObjectWithFile:[saveUrl path]]; 194 | self.contentTextView.textStorage.font = settings.font; 195 | self.contentTextView.font = settings.font; 196 | } 197 | } 198 | 199 | @end 200 | -------------------------------------------------------------------------------- /CloudNotes/OCNotesViewController.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 | 38 | 52 | 66 | 67 | 68 | 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 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | OCDateTransformer 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 161 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 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 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /CloudNotes/OCNotesWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCNotesWindowController.h 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/16/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OCNotesWindowController : NSWindowController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CloudNotes/OCNotesWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCNotesWindowController.m 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/16/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import "OCNotesWindowController.h" 10 | 11 | @implementation OCNotesWindowController 12 | 13 | - (id)initWithWindow:(NSWindow *)window 14 | { 15 | self = [super initWithWindow:window]; 16 | if (self) { 17 | // Initialization code here. 18 | } 19 | return self; 20 | } 21 | 22 | - (void)windowDidLoad 23 | { 24 | [super windowDidLoad]; 25 | 26 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /CloudNotes/OCNotesWindowController.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 | -------------------------------------------------------------------------------- /CloudNotes/OCPrefsWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCPrefsWindowController.h 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/8/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface OCPrefsWindowController : NSWindowController 12 | 13 | @property (strong) IBOutlet NSTextField *usernameTextField; 14 | @property (strong) IBOutlet NSSecureTextField *passwordTextField; 15 | @property (strong) IBOutlet NSTextField *statusLabel; 16 | @property (strong) IBOutlet NSTextField *serverTextField; 17 | @property (strong) IBOutlet NSProgressIndicator *connectionActivityIndicator; 18 | @property (strong) IBOutlet NSTabView *tabView; 19 | 20 | - (IBAction)doConnect:(id)sender; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /CloudNotes/OCPrefsWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCPrefsWindowController.m 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/8/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import "OCPrefsWindowController.h" 10 | #import "PDKeychainBindingsController.h" 11 | #import "OCAPIClient.h" 12 | 13 | static const NSString *rootPath = @"index.php/apps/notes/api/v0.2/"; 14 | 15 | @interface OCPrefsWindowController () 16 | 17 | @end 18 | 19 | @implementation OCPrefsWindowController 20 | 21 | - (id)initWithWindow:(NSWindow *)window 22 | { 23 | self = [super initWithWindow:window]; 24 | if (self) { 25 | // Initialization code here. 26 | } 27 | return self; 28 | } 29 | 30 | - (void)windowDidLoad 31 | { 32 | [super windowDidLoad]; 33 | 34 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 35 | [self.usernameTextField bind:@"value" 36 | toObject:[PDKeychainBindingsController sharedKeychainBindingsController] 37 | withKeyPath:[NSString stringWithFormat:@"values.%@",@"Username"] 38 | options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] 39 | forKey:@"NSContinuouslyUpdatesValue"]]; 40 | 41 | [self.passwordTextField bind:@"value" 42 | toObject:[PDKeychainBindingsController sharedKeychainBindingsController] 43 | withKeyPath:[NSString stringWithFormat:@"values.%@",@"Password"] 44 | options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] 45 | forKey:@"NSContinuouslyUpdatesValue"]]; 46 | 47 | NSString *status; 48 | NSString *server = [[NSUserDefaults standardUserDefaults] stringForKey:@"Server"]; 49 | if ([OCAPIClient sharedClient].reachabilityManager.isReachable) { 50 | status = [NSString stringWithFormat:@"Connected to an ownCloud Notes server at\n \"%@\".", server]; 51 | } else { 52 | status = @"Currently not connected to an ownCloud Notes server"; 53 | } 54 | 55 | self.statusLabel.stringValue = status; 56 | self.serverTextField.stringValue = server; 57 | if (!server || server.length == 0) { 58 | [self.tabView selectLastTabViewItem:nil]; 59 | } 60 | } 61 | 62 | - (IBAction)doConnect:(id)sender { 63 | [self.connectionActivityIndicator startAnimation:nil]; 64 | __block NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; 65 | PDKeychainBindings *keychain = [PDKeychainBindings sharedKeychainBindings]; 66 | 67 | OCAPIClient *client = [[OCAPIClient alloc] initWithBaseURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", self.serverTextField.stringValue, rootPath]]]; 68 | [client setRequestSerializer:[AFJSONRequestSerializer serializer]]; 69 | [client.requestSerializer setAuthorizationHeaderFieldWithUsername:[keychain stringForKey:@"Username"] password:[keychain stringForKey:@"Password"]]; 70 | 71 | BOOL allowInvalid = [prefs boolForKey:@"AllowInvalidSSLCertificate"]; 72 | client.securityPolicy.allowInvalidCertificates = allowInvalid; 73 | NSDictionary *params = @{@"exclude": @"content"}; 74 | 75 | [client GET:@"notes" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) { 76 | NSLog(@"notes: %@", responseObject); 77 | [prefs setObject:self.serverTextField.stringValue forKey:@"Server"]; 78 | [OCAPIClient setSharedClient:nil]; 79 | int status = [[OCAPIClient sharedClient].reachabilityManager networkReachabilityStatus]; 80 | NSLog(@"Server status: %i", status); 81 | self.statusLabel.stringValue = [NSString stringWithFormat:@"Connected to an ownCloud Notes server at\n \"%@\".", self.serverTextField.stringValue]; 82 | 83 | [self.connectionActivityIndicator stopAnimation:nil]; 84 | 85 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 86 | NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response; 87 | NSLog(@"Error: %@, response: %ld", [error localizedDescription], (long)[response statusCode]); 88 | self.statusLabel.stringValue = @"Failed to connect to a server. Check your settings."; 89 | [self.connectionActivityIndicator stopAnimation:nil]; 90 | }]; 91 | 92 | } 93 | @end 94 | -------------------------------------------------------------------------------- /CloudNotes/OCPrefsWindowController.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 | 37 | 38 | 39 | 40 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 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 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | NSAllRomanInputSourcesLocaleIdentifier 144 | 145 | 146 | 147 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /CloudNotes/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1265 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\b\fs24 \cf0 Engineering: 8 | \b0 \ 9 | Peter Hedlund\ 10 | \ 11 | 12 | \b Human Interface Design: 13 | \b0 \ 14 | Peter Hedlund\ 15 | } -------------------------------------------------------------------------------- /CloudNotes/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /CloudNotes/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CloudNotes 4 | // 5 | // Created by Peter Hedlund on 2/2/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) 12 | { 13 | return NSApplicationMain(argc, argv); 14 | } 15 | -------------------------------------------------------------------------------- /CloudNotesTests/CloudNotesTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.peterandlinda.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /CloudNotesTests/CloudNotesTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CloudNotesTests.m 3 | // CloudNotesTests 4 | // 5 | // Created by Peter Hedlund on 2/2/14. 6 | // Copyright (c) 2014 Peter Hedlund. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CloudNotesTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation CloudNotesTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /CloudNotesTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Peter Hedlund peter.hedlund@me.com 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 14 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | notes-OSX-App 2 | ============= 3 | --------------------------------------------------------------------------------