diff --git a/AppController.h b/AppController.h index 18714a1..7eca0cb 100755 --- a/AppController.h +++ b/AppController.h @@ -27,6 +27,7 @@ SGHotKey *mainHotKey; IBOutlet SRRecorderControl *mainRecorder; IBOutlet NSPanel *prefsPanel; + IBOutlet NSTextView *acknowledgementsView; IBOutlet NSBox *appearancePanel; int mainHotkeyModifiers; SRKeyCodeTransformer *srTransformer; @@ -66,7 +67,6 @@ //Preferences NSDictionary *standardPreferences; int jcDisplayNum; - BOOL issuedRememberResizeWarning; BOOL needBezelUpdate; BOOL needMenuUpdate; } diff --git a/AppController.m b/AppController.m index 7bf1515..2ee8d26 100755 --- a/AppController.m +++ b/AppController.m @@ -385,52 +385,30 @@ [self checkRememberNumPref:[sender intValue] forPrimaryStore:YES]; } --(void) checkRememberNumPref:(int)newRemember forPrimaryStore:(BOOL) isPrimaryStore +-(int) checkRememberNumPref:(int)newRemember forPrimaryStore:(BOOL) isPrimaryStore { - int oldRemeber = [flycutOperator rememberNum]; - if ( newRemember < [flycutOperator jcListCount] && - ! issuedRememberResizeWarning && - ! [[NSUserDefaults standardUserDefaults] boolForKey:@"stifleRememberResizeWarning"] - ) { - int choice = NSRunAlertPanel(@"Resize Stack", - @"Resizing the stack to a value below its present size will cause clippings to be lost.", - @"Resize", @"Cancel", @"Don't Warn Me Again"); - if ( choice == NSAlertAlternateReturn ) { - // Cancel - Change to prior setting. - newRemember = oldRemeber; - if ( isPrimaryStore ) { - [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:newRemember] - forKey:@"rememberNum"]; - [self updateMenu]; - } else { - [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:newRemember] - forKey:@"favoritesRememberNum"]; - } - } else if ( choice == NSAlertOtherReturn ) { - // Don't Warn Me Again - [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:YES] - forKey:@"stifleRememberResizeWarning"]; - } else { - // Resize - issuedRememberResizeWarning = YES; - } - } + int oldRemember = [flycutOperator rememberNum]; + int setRemember = [flycutOperator setRememberNum:newRemember forPrimaryStore:YES]; - if ( newRemember < oldRemeber ) + if ( isPrimaryStore ) { - // Trim down the number displayed in the menu if it is greater than the new - // number to remember. - if ( isPrimaryStore ) { - if ( newRemember < [[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] ) { - [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:newRemember] - forKey:@"displayNum"]; - [self updateMenu]; + if ( setRemember == oldRemember ) + { + [self updateMenu]; + } + else if ( setRemember < oldRemember ) + { + // Trim down the number displayed in the menu if it is greater than the new + // number to remember. + if ( isPrimaryStore ) { + if ( setRemember < [[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] ) { + [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:setRemember] + forKey:@"displayNum"]; + [self updateMenu]; + } } } } - - // Set the value. - [flycutOperator setRememberNum: newRemember]; } -(IBAction) setFavoritesRememberNumPref:(id)sender @@ -634,7 +612,12 @@ [prefsPanel setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]; [NSApp activateIgnoringOtherApps: YES]; [prefsPanel makeKeyAndOrderFront:self]; - issuedRememberResizeWarning = NO; + NSString *fileRoot = [[NSBundle mainBundle] pathForResource:@"acknowledgements" ofType:@"txt"]; + NSString *contents = [NSString stringWithContentsOfFile:fileRoot + encoding:NSUTF8StringEncoding + error:NULL]; + [acknowledgementsView setString:contents]; + [flycutOperator willShowPreferences]; } -(IBAction)toggleLoadOnStartup:(id)sender { diff --git a/English.lproj/MainMenu.nib/designable.nib b/English.lproj/MainMenu.nib/designable.nib index d7ebd08..b252349 100644 --- a/English.lproj/MainMenu.nib/designable.nib +++ b/English.lproj/MainMenu.nib/designable.nib @@ -96,6 +96,7 @@ + @@ -580,42 +581,7 @@ - - Flycut developed by General Arcade (http://www.generalarcade.com/). Flycut is a fork of Jumpcut. Source code avilable on GitHub: http://github.com/TermiT/Flycut -© General Arcade, 2011. - - - - - - - - - - -FhZKdW1wY3V0IGlzIG9wZW4gc291cmNlIGNvZGUgYnVpbHQgd2l0aCBvcGVuIHNvdXJjZSBjb2RlLiBK -dW1wY3V0IGluY29ycG9yYXRlcyB0aGUKZm9sbG93aW5nIGxpYnJhcmllcywgdXNlZCB3aXRoIGdyYXRp -dHVkZToKCeKAoiBQVEhvdEtleSBieSBRdWVudGluIENhcm5pY2VsbGkgKDxodHRwOi8vcm9ndWVhbW9l -YmEuY29tLz4pCgnigKIgU2hvcnRjdXRSZWNvcmRlciBieSBKZXNwZXIgZXQgYWwuICg8aHR0cDovL3dh -ZmZsZXNvZnR3YXJlLm5ldC8+KQoJ4oCiIFNwYXJrbGUgYnkgQW5keSBNYXR1c2NoYWsgKDxodHRwOi8v -d3d3LmFuZHltYXR1c2NoYWsub3JnLz4pCgnigKIgYSBtb2RpZmllZCB2ZXJzaW9uIG9mIFVLUHJlZnNQ -YW5lbCBieSBVbGkgS3VzdGVyZXIgKDxodHRwOi8vd3d3LnphdGhyYXMuZGUvPikgCgpBZGRpdGlvbmFs -bHksIG11Y2ggb2YgdGhlIG9yaWdpbmFsIGNvZGUgZm9yIEp1bXBjdXQgd2FzIGRlcml2ZWQgZnJvbSBC -cmV0dCBTaW1tb25zJyAKVGlnZXJMYXVuY2ggKDxodHRwOi8vcmFuY2hlcm8uY29tL3RpZ2VybGF1bmNo -Lz4pIGFuZCBjb25zdGFudCByZWZlcnJhbCB0byB0aGUgZXhhbXBsZXMgYW5kCmRpc2N1c3Npb24gYXQg -Q29jb2FEZXYuY29tLgoKSnVtcGN1dCBpcyBzdWJqZWN0IHRvIHRoZSBNSVQgTGljZW5zZS4gU291cmNl -IGNvZGUgYW5kIGxpY2Vuc2UgZGV0YWlscyBhcmUgYXZhaWxhYmxlIGF0IGl0cwpob21lcGFnZSwgKDxo -dHRwOi8vanVtcGN1dC5zb3VyY2Vmb3JnZS5uZXQ+KS4gVW5sZXNzIG90aGVyd2lzZSBub3RlZCwgSnVt -cGN1dCBpcyAKY29weXJpZ2h0IMKpIFN0ZXZlIENvb2ssIDIwMDIg4oCTIDIwMDguCg - - - - - - - - -Thanks to Clare Bates Congdon, Joshua Davis, Brad Graham, David Jacobs, John Kenzie, Adam Rice, Finn Smith, Vera Tobin, and Andre Torrez for early and ongoing feedback, advice, and encouragement. + diff --git a/English.lproj/MainMenu.nib/keyedobjects.nib b/English.lproj/MainMenu.nib/keyedobjects.nib index 76a1ad3..c06620d 100644 Binary files a/English.lproj/MainMenu.nib/keyedobjects.nib and b/English.lproj/MainMenu.nib/keyedobjects.nib differ diff --git a/Flycut-iOS-Bridging-Header.h b/Flycut-iOS-Bridging-Header.h index 4452113..3965657 100644 --- a/Flycut-iOS-Bridging-Header.h +++ b/Flycut-iOS-Bridging-Header.h @@ -7,3 +7,4 @@ #import "MGSwipeTableCell/MGSwipeTableCell/MGSwipeTableCell.h" #import "MGSwipeTableCell/MGSwipeTableCell/MGSwipeButton.h" #import "MJCloudKitUserDefaultsSync/MJCloudKitUserDefaultsSync.h" +#import "IASKAppSettingsViewController.h" diff --git a/Flycut-iOS/AppDelegate.swift b/Flycut-iOS/AppDelegate.swift index f0c55e5..6e17430 100644 --- a/Flycut-iOS/AppDelegate.swift +++ b/Flycut-iOS/AppDelegate.swift @@ -22,6 +22,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } application.registerForRemoteNotifications() + let fileRoot = Bundle.main.path(forResource: "acknowledgements", ofType: "txt") + let contents = try? String.init(contentsOfFile: fileRoot!, encoding: String.Encoding.utf8) + UserDefaults.standard.set(contents, forKey: "acknowledgementsText") + return true } diff --git a/Flycut-iOS/Base.lproj/Main.storyboard b/Flycut-iOS/Base.lproj/Main.storyboard index 17e321a..b056d2a 100644 --- a/Flycut-iOS/Base.lproj/Main.storyboard +++ b/Flycut-iOS/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -22,7 +22,7 @@ - + @@ -39,10 +39,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Flycut-iOS/Settings.bundle/Acknowledgements.plist b/Flycut-iOS/Settings.bundle/Acknowledgements.plist new file mode 100644 index 0000000..218874f --- /dev/null +++ b/Flycut-iOS/Settings.bundle/Acknowledgements.plist @@ -0,0 +1,22 @@ + + + + + StringsTable + Root + PreferenceSpecifiers + + + Type + PSGroupSpecifier + Title + + Key + acknowledgementsText + FooterText + © General Arcade, 2011 - 2017. +© Steve Cook, 2002 – 2008. + + + + diff --git a/Flycut-iOS/Settings.bundle/Root.plist b/Flycut-iOS/Settings.bundle/Root.plist new file mode 100644 index 0000000..da79037 --- /dev/null +++ b/Flycut-iOS/Settings.bundle/Root.plist @@ -0,0 +1,83 @@ + + + + + StringsTable + Root + PreferenceSpecifiers + + + Type + PSTextFieldSpecifier + Title + Remember Limit + Key + rememberNum + KeyboardType + NumberPad + DefaultValue + 40 + + + Type + PSToggleSwitchSpecifier + Title + Remove duplicates + Key + removeDuplicates + DefaultValue + + + + Type + PSToggleSwitchSpecifier + Title + Move pasted item to top + Key + pasteMovesToTop + DefaultValue + + + + Type + PSGroupSpecifier + Title + iCloud Sync + + + Type + PSToggleSwitchSpecifier + Title + Settings + Key + syncSettingsViaICloud + DefaultValue + + + + Type + PSToggleSwitchSpecifier + Title + Clippings + Key + syncClippingsViaICloud + DefaultValue + + + + Type + PSGroupSpecifier + Title + + + + Type + PSChildPaneSpecifier + Title + Acknowledgements + File + Acknowledgements + + + + diff --git a/Flycut-iOS/Settings.bundle/en.lproj/Root.strings b/Flycut-iOS/Settings.bundle/en.lproj/Root.strings new file mode 100644 index 0000000..8cd87b9 Binary files /dev/null and b/Flycut-iOS/Settings.bundle/en.lproj/Root.strings differ diff --git a/Flycut-iOS/SettingsViewController.swift b/Flycut-iOS/SettingsViewController.swift new file mode 100644 index 0000000..69cab50 --- /dev/null +++ b/Flycut-iOS/SettingsViewController.swift @@ -0,0 +1,22 @@ +// +// SettingsView.swift +// Flycut +// +// Created by Mark Jerde on 10/24/17. +// +// + +import Foundation + +class SettingsViewController: IASKAppSettingsViewController { + required init?(coder aDecoder: NSCoder) { + super.init(style: .grouped) + super.showCreditsFooter = false + } + + // - (id)initWithStyle:(UITableViewStyle)style { + required override init(style:UITableViewStyle) { + super.init(style: style) + super.showCreditsFooter = false + } +} diff --git a/Flycut-iOS/ViewController.swift b/Flycut-iOS/ViewController.swift index 0926c9d..e394bcf 100644 --- a/Flycut-iOS/ViewController.swift +++ b/Flycut-iOS/ViewController.swift @@ -15,9 +15,12 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour var tableView:UITableView! var currentAnimation = UITableViewRowAnimation.none var pbCount:Int = -1 + var rememberedSyncSettings:Bool = false + var rememberedSyncClippings:Bool = false let pasteboardInteractionQueue = DispatchQueue(label: "com.Flycut.pasteboardInteractionQueue") let alertHandlingQueue = DispatchQueue(label: "com.Flycut.alertHandlingQueue") + let defaultsChangeHandlingQueue = DispatchQueue(label: "com.Flycut.defaultsChangeHandlingQueue") // Some buttons we will reuse. @@ -91,6 +94,36 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour name: notification, object: nil) } + + NotificationCenter.default.addObserver(self, selector: #selector(self.defaultsChanged), name: UserDefaults.didChangeNotification, object: nil) + } + + func defaultsChanged() { + // This seems to be the only way to respond to Settings changes, though it doesn't inform us what changed so we will have to check each to see if they were the one(s). + + // Don't use DispatchQueue.main.async since that will still end up blocking the UI draw until the user responds to what hasn't been drawn yet. + // Use async on a sequential queue to avoid concurrent response to the same change. This allows enqueuing of defaultsChanged calls in reponse to changes made within the handling, but using sync causes EXC_BAD_ACCESS in this case. + defaultsChangeHandlingQueue.async { + let newRememberNum = Int32(UserDefaults.standard.integer(forKey: "rememberNum")) + if ( UserDefaults.standard.value(forKey: "rememberNum") is String ) + { + // Reset the value, since TextField will make it a String and CloudKit sync will object to changing the type. Check this independent of value change, since the type could be changed without a change in value and we don't want it left around causing confusion. + UserDefaults.standard.set(newRememberNum, forKey: "rememberNum") + } + if ( self.flycut.rememberNum() != newRememberNum ) { + self.flycut.setRememberNum(newRememberNum, forPrimaryStore: true) + } + + let syncSettings = UserDefaults.standard.bool(forKey: "syncSettingsViaICloud") + let syncClippings = UserDefaults.standard.bool(forKey: "syncClippingsViaICloud") + if ( syncSettings != self.rememberedSyncSettings + || syncClippings != self.rememberedSyncClippings ) + { + self.rememberedSyncSettings = syncSettings + self.rememberedSyncClippings = syncClippings + self.flycut.registerOrDeregisterICloudSync() + } + } } override func viewDidAppear(_ animated: Bool) { @@ -112,6 +145,9 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour self.flycut.registerOrDeregisterICloudSync() } } + + // This is a suitable place to prepare to possible eventual display of preferences, resetting values that should reset before each display of preferences. + flycut.willShowPreferences() } func savePreferences(toDict: NSMutableDictionary) @@ -207,11 +243,21 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour }) } - // Transform the asynchronous UIAlertController into a synchronous alert by suspending a GCD serial queue before presenting then placing an empty sync on that queue to block until it is resumed, and resuming after selection. The GCD sync can't complete until the selection resumes the queue. + if var topController = UIApplication.shared.keyWindow?.rootViewController { + while let presentedViewController = topController.presentedViewController { + topController = presentedViewController + } - alertHandlingQueue.suspend() - self.present(alertController, animated: true) - alertHandlingQueue.sync { } // To wait for queue to resume. + // topController should now be your topmost view controller + + // Transform the asynchronous UIAlertController into a synchronous alert by suspending a GCD serial queue before presenting then placing an empty sync on that queue to block until it is resumed, and resuming after selection. The GCD sync can't complete until the selection resumes the queue. + + alertHandlingQueue.suspend() + DispatchQueue.main.async { + topController.present(alertController, animated: true) + } + alertHandlingQueue.sync { } // To wait for queue to resume. + } return selection } @@ -219,6 +265,9 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour func checkForClippingAddedToClipboard() { pasteboardInteractionQueue.async { + // This is a suitable place to prepare to possible eventual display of preferences, resetting values that should reset before each display of preferences. + self.flycut.willShowPreferences() + if ( UIPasteboard.general.changeCount != self.pbCount ) { self.pbCount = UIPasteboard.general.changeCount; diff --git a/Flycut.xcodeproj/project.pbxproj b/Flycut.xcodeproj/project.pbxproj index 0a0e31d..59d1850 100755 --- a/Flycut.xcodeproj/project.pbxproj +++ b/Flycut.xcodeproj/project.pbxproj @@ -71,7 +71,14 @@ 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; + 8D151CC21FAC2B38005132F0 /* acknowledgements.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8D151CC11FAC2B38005132F0 /* acknowledgements.txt */; }; + 8D151CC51FAC2F25005132F0 /* acknowledgements.txt in Resources */ = {isa = PBXBuildFile; fileRef = 8D151CC11FAC2B38005132F0 /* acknowledgements.txt */; }; 8D153A0B1F54CD0C006A5815 /* MJCloudKitUserDefaultsSync.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D153A081F54CCF7006A5815 /* MJCloudKitUserDefaultsSync.m */; }; + 8D180DFE1F9CD0580001F912 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 8D180DFD1F9CD0580001F912 /* Settings.bundle */; }; + 8D180E221F9FD3D70001F912 /* InAppSettingsKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8D180E211F9FD3320001F912 /* InAppSettingsKit.framework */; }; + 8D180E231F9FD3D70001F912 /* InAppSettingsKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8D180E211F9FD3320001F912 /* InAppSettingsKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 8D180E271F9FD4B70001F912 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D180E261F9FD4B70001F912 /* SettingsViewController.swift */; }; + 8D180E291FA8CECD0001F912 /* 20-gear2.png in Resources */ = {isa = PBXBuildFile; fileRef = 8D180E281FA8CECD0001F912 /* 20-gear2.png */; }; 8D1817F71F72D35400F80B7A /* MJCloudKitUserDefaultsSync.m in Sources */ = {isa = PBXBuildFile; fileRef = 8D153A081F54CCF7006A5815 /* MJCloudKitUserDefaultsSync.m */; }; 8D2E28861B0669F500AE62C8 /* com.generalarcade.flycut.black.16.png in Resources */ = {isa = PBXBuildFile; fileRef = 8D2E28821B0669F500AE62C8 /* com.generalarcade.flycut.black.16.png */; }; 8D2E28871B0669F500AE62C8 /* com.generalarcade.flycut.black.32.png in Resources */ = {isa = PBXBuildFile; fileRef = 8D2E28831B0669F500AE62C8 /* com.generalarcade.flycut.black.32.png */; }; @@ -116,6 +123,34 @@ remoteGlobalIDString = C63EE56A1BBA07ED008F46BB; remoteInfo = MGSwipeTableCell; }; + 8D180E1C1F9FD3320001F912 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D180E161F9FD3320001F912 /* InAppSettingsKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = E42ADA8F168202D700295D85; + remoteInfo = InAppSettingsKit; + }; + 8D180E1E1F9FD3320001F912 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D180E161F9FD3320001F912 /* InAppSettingsKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = E42ADAA2168202D700295D85; + remoteInfo = InAppSettingsKitTests; + }; + 8D180E201F9FD3320001F912 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D180E161F9FD3320001F912 /* InAppSettingsKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = DAC156231B54533D00655031; + remoteInfo = InAppSettingsKitFramework; + }; + 8D180E241F9FD3D70001F912 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8D180E161F9FD3320001F912 /* InAppSettingsKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = DAC156221B54533D00655031; + remoteInfo = InAppSettingsKitFramework; + }; 8DCE826A1F4550AA00124117 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8D0DC8391F3D4B6A00A2A8AF /* MGSwipeTableCell.xcodeproj */; @@ -133,6 +168,7 @@ dstSubfolderSpec = 10; files = ( 8DCE82691F4550AA00124117 /* MGSwipeTableCell.framework in Embed Frameworks */, + 8D180E231F9FD3D70001F912 /* InAppSettingsKit.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -217,9 +253,14 @@ 8D0C11D51F1882B30046FAA0 /* Flycut-iOS_Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Flycut-iOS_Prefix.pch"; sourceTree = ""; }; 8D0DC8391F3D4B6A00A2A8AF /* MGSwipeTableCell.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MGSwipeTableCell.xcodeproj; path = MGSwipeTableCell/MGSwipeTableCell.xcodeproj; sourceTree = ""; }; 8D1107320486CEB800E47090 /* Flycut.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Flycut.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D151CC11FAC2B38005132F0 /* acknowledgements.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = acknowledgements.txt; sourceTree = ""; }; 8D153A071F54CCF7006A5815 /* MJCloudKitUserDefaultsSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MJCloudKitUserDefaultsSync.h; path = MJCloudKitUserDefaultsSync/MJCloudKitUserDefaultsSync.h; sourceTree = ""; }; 8D153A081F54CCF7006A5815 /* MJCloudKitUserDefaultsSync.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MJCloudKitUserDefaultsSync.m; path = MJCloudKitUserDefaultsSync/MJCloudKitUserDefaultsSync.m; sourceTree = ""; }; 8D153A0C1F5BB249006A5815 /* Flycut.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Flycut.entitlements; sourceTree = ""; }; + 8D180DFD1F9CD0580001F912 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; + 8D180E161F9FD3320001F912 /* InAppSettingsKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = InAppSettingsKit.xcodeproj; path = InAppSettingsKit/InAppSettingsKit.xcodeproj; sourceTree = ""; }; + 8D180E261F9FD4B70001F912 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; + 8D180E281FA8CECD0001F912 /* 20-gear2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "20-gear2.png"; path = "InAppSettingsKit/InAppSettingsKitSampleApp/20-gear2.png"; sourceTree = ""; }; 8D2E28821B0669F500AE62C8 /* com.generalarcade.flycut.black.16.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = com.generalarcade.flycut.black.16.png; path = Resources/com.generalarcade.flycut.black.16.png; sourceTree = ""; }; 8D2E28831B0669F500AE62C8 /* com.generalarcade.flycut.black.32.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = com.generalarcade.flycut.black.32.png; path = Resources/com.generalarcade.flycut.black.32.png; sourceTree = ""; }; 8D2E28841B0669F500AE62C8 /* com.generalarcade.flycut.xout.16.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = com.generalarcade.flycut.xout.16.png; path = Resources/com.generalarcade.flycut.xout.16.png; sourceTree = ""; }; @@ -243,6 +284,7 @@ 8D84B63B1F7412E600DB58F9 /* CloudKit.framework in Frameworks */, 8DCE82681F4550AA00124117 /* MGSwipeTableCell.framework in Frameworks */, 8D0DC85A1F3D697000A2A8AF /* MGSwipeTableCell.framework in Frameworks */, + 8D180E221F9FD3D70001F912 /* InAppSettingsKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -276,7 +318,6 @@ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( - 8D153A061F54CCD8006A5815 /* MJCloudKitUserDefaultsSync */, AABE497A09FF9CD000A6A239 /* AppController.h */, AABE497B09FF9CD000A6A239 /* AppController.m */, 8D0C11A01F147E3C0046FAA0 /* FlycutOperator.h */, @@ -288,6 +329,8 @@ 7761C880139BDE94000FB3AB /* LoginItem */, 7761C877139BDE7F000FB3AB /* FlycutEngine */, 77A4F3A4139BD72300F39666 /* SGHotKeysLib */, + 8D180E161F9FD3320001F912 /* InAppSettingsKit.xcodeproj */, + 8D153A061F54CCD8006A5815 /* MJCloudKitUserDefaultsSync */, 8D0DC8391F3D4B6A00A2A8AF /* MGSwipeTableCell.xcodeproj */, ); name = Classes; @@ -360,6 +403,7 @@ 8D2E28831B0669F500AE62C8 /* com.generalarcade.flycut.black.32.png */, 8D2E28841B0669F500AE62C8 /* com.generalarcade.flycut.xout.16.png */, 8D2E28851B0669F500AE62C8 /* com.generalarcade.flycut.xout.32.png */, + 8D180E281FA8CECD0001F912 /* 20-gear2.png */, 7761C8D1139BE06B000FB3AB /* jumpcut.icns */, 7761C8EB139BE09D000FB3AB /* Info.plist */, 7761C8D2139BE06B000FB3AB /* net.sf.jumpcut.ghost_scissors_small.png */, @@ -372,6 +416,7 @@ 7761C8D9139BE06B000FB3AB /* RemoveShortcutPressed.tif */, 7761C8DA139BE06B000FB3AB /* RemoveShortcutRollover.tif */, 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, + 8D151CC11FAC2B38005132F0 /* acknowledgements.txt */, ); name = Resources; sourceTree = ""; @@ -472,6 +517,7 @@ 8D0C11A61F171B140046FAA0 /* Flycut-iOS */ = { isa = PBXGroup; children = ( + 8D180DFD1F9CD0580001F912 /* Settings.bundle */, 8D153A0C1F5BB249006A5815 /* Flycut.entitlements */, 8D0C11A71F171B140046FAA0 /* AppDelegate.swift */, 8D0C11A91F171B140046FAA0 /* ViewController.swift */, @@ -479,6 +525,7 @@ 8D0C11AE1F171B140046FAA0 /* Assets.xcassets */, 8D0C11B01F171B140046FAA0 /* LaunchScreen.storyboard */, 8D0C11B31F171B140046FAA0 /* Info.plist */, + 8D180E261F9FD4B70001F912 /* SettingsViewController.swift */, ); path = "Flycut-iOS"; sourceTree = ""; @@ -518,6 +565,16 @@ name = MJCloudKitUserDefaultsSync; sourceTree = ""; }; + 8D180E171F9FD3320001F912 /* Products */ = { + isa = PBXGroup; + children = ( + 8D180E1D1F9FD3320001F912 /* libInAppSettingsKit.a */, + 8D180E1F1F9FD3320001F912 /* InAppSettingsKitTests.xctest */, + 8D180E211F9FD3320001F912 /* InAppSettingsKit.framework */, + ); + name = Products; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -564,6 +621,7 @@ dependencies = ( 8D0DC8591F3D696B00A2A8AF /* PBXTargetDependency */, 8DCE826B1F4550AA00124117 /* PBXTargetDependency */, + 8D180E251F9FD3D70001F912 /* PBXTargetDependency */, ); name = "Flycut-iOS"; productName = "Flycut-iOS"; @@ -706,6 +764,10 @@ mainGroup = 29B97314FDCFA39411CA2CEA /* Jumpcut */; projectDirPath = ""; projectReferences = ( + { + ProductGroup = 8D180E171F9FD3320001F912 /* Products */; + ProjectRef = 8D180E161F9FD3320001F912 /* InAppSettingsKit.xcodeproj */; + }, { ProductGroup = 8D0DC8531F3D682800A2A8AF /* Products */; ProjectRef = 8D0DC8391F3D4B6A00A2A8AF /* MGSwipeTableCell.xcodeproj */; @@ -730,6 +792,27 @@ remoteRef = 8D0DC8561F3D682800A2A8AF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D180E1D1F9FD3320001F912 /* libInAppSettingsKit.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libInAppSettingsKit.a; + remoteRef = 8D180E1C1F9FD3320001F912 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8D180E1F1F9FD3320001F912 /* InAppSettingsKitTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = InAppSettingsKitTests.xctest; + remoteRef = 8D180E1E1F9FD3320001F912 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8D180E211F9FD3320001F912 /* InAppSettingsKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = InAppSettingsKit.framework; + remoteRef = 8D180E201F9FD3320001F912 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -737,9 +820,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8D180DFE1F9CD0580001F912 /* Settings.bundle in Resources */, 8D0C11B21F171B140046FAA0 /* LaunchScreen.storyboard in Resources */, 8D0C11AF1F171B140046FAA0 /* Assets.xcassets in Resources */, 8D0C11AD1F171B140046FAA0 /* Main.storyboard in Resources */, + 8D180E291FA8CECD0001F912 /* 20-gear2.png in Resources */, + 8D151CC51FAC2F25005132F0 /* acknowledgements.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -777,6 +863,7 @@ 8D2E28861B0669F500AE62C8 /* com.generalarcade.flycut.black.16.png in Resources */, 7761C8E6139BE06B000FB3AB /* RemoveShortcut.tif in Resources */, 7761C8E7139BE06B000FB3AB /* RemoveShortcutPressed.tif in Resources */, + 8D151CC21FAC2B38005132F0 /* acknowledgements.txt in Resources */, 7761C8E8139BE06B000FB3AB /* RemoveShortcutRollover.tif in Resources */, 7794D3D5139BF28B0047E862 /* flycut.icns in Resources */, ); @@ -805,6 +892,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8D180E271F9FD4B70001F912 /* SettingsViewController.swift in Sources */, 8D0C11D61F18835F0046FAA0 /* FlycutStore.m in Sources */, 8D0C11AA1F171B140046FAA0 /* ViewController.swift in Sources */, 8D0C11D71F1883620046FAA0 /* FlycutClipping.m in Sources */, @@ -885,6 +973,11 @@ name = MGSwipeTableCell; targetProxy = 8D0DC8581F3D696B00A2A8AF /* PBXContainerItemProxy */; }; + 8D180E251F9FD3D70001F912 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = InAppSettingsKitFramework; + targetProxy = 8D180E241F9FD3D70001F912 /* PBXContainerItemProxy */; + }; 8DCE826B1F4550AA00124117 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = MGSwipeTableCell; @@ -967,6 +1060,7 @@ GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; + HEADER_SEARCH_PATHS = "InAppSettingsKit/InAppSettingsKit/**"; INFOPLIST_FILE = "Flycut-iOS/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1023,6 +1117,7 @@ GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; + HEADER_SEARCH_PATHS = "InAppSettingsKit/InAppSettingsKit/**"; INFOPLIST_FILE = "Flycut-iOS/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; diff --git a/FlycutEngine/FlycutStore.m b/FlycutEngine/FlycutStore.m index 9f6070c..499e59b 100755 --- a/FlycutEngine/FlycutStore.m +++ b/FlycutEngine/FlycutStore.m @@ -26,6 +26,10 @@ displaying:(int)nowDisplaying withDisplayLength:(int)displayLength { + // Ensure that we don't remember zero or fewer clippings. Very unlikely, but this provides a safeguard just in case. + if ( nowRemembering <= 0 ) + nowRemembering = 40; + [super init]; jcList = [[NSMutableArray alloc] init]; insertionJournal = [[NSMutableArray alloc] init]; diff --git a/FlycutOperator.h b/FlycutOperator.h index 0a82a4a..41bd80b 100644 --- a/FlycutOperator.h +++ b/FlycutOperator.h @@ -42,6 +42,9 @@ BOOL inhibitSaveEngineAfterListModification; BOOL firstClippingsSyncAfterEnabling; BOOL inhibitAutosaveClippings; + + //Preferences + BOOL issuedRememberResizeWarning; } // Basic functionality @@ -77,7 +80,8 @@ -(void) checkCloudKitUpdates; // Preference related --(void) setRememberNum:(int)newRemember; +-(void) willShowPreferences; +-(int) setRememberNum:(int)newRemember forPrimaryStore:(BOOL) isPrimaryStore; // Initialization / cleanup related -(void)applicationWillTerminate;; diff --git a/FlycutOperator.m b/FlycutOperator.m index 4b4ec95..e651af3 100644 --- a/FlycutOperator.m +++ b/FlycutOperator.m @@ -128,9 +128,55 @@ } } --(void) setRememberNum:(int) newRemember +-(void) willShowPreferences { + issuedRememberResizeWarning = NO; +} + +-(int) setRememberNum:(int)newRemember forPrimaryStore:(BOOL) isPrimaryStore +{ + int oldRemeber = [self rememberNum]; + + // Ensure that we don't remember zero or fewer clippings. + if ( newRemember <= 0 ) + { + newRemember = oldRemeber; + if ( newRemember <= 0 ) + newRemember = 40; + } + + if ( newRemember < [self jcListCount] && + ! issuedRememberResizeWarning && + ! [[NSUserDefaults standardUserDefaults] boolForKey:@"stifleRememberResizeWarning"] + ) { + + NSString *choice = [self delegateAlertWithMessageText:@"Resize Stack" + informationText:@"Resizing the stack to a value below its present size will cause clippings to be lost." + buttonsTexts:@[@"Resize", @"Cancel", @"Don't Warn Me Again"]]; + if ( [choice isEqualToString:@"Cancel"] ) { + // Cancel - Change to prior setting. + newRemember = oldRemeber; + if ( isPrimaryStore ) { + [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:newRemember] + forKey:@"rememberNum"]; + } else { + [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:newRemember] + forKey:@"favoritesRememberNum"]; + } + } else if ( [choice isEqualToString:@"Don't Warn Me Again"] ) { + // Don't Warn Me Again + [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:YES] + forKey:@"stifleRememberResizeWarning"]; + } else { + // Resize + issuedRememberResizeWarning = YES; + } + } + + // Set the value. [clippingStore setRememberNum:newRemember]; + + return newRemember; } -(void)toggleToFromFavoritesStore @@ -524,8 +570,9 @@ BOOL changedSyncClippings = ( ![[NSUserDefaults standardUserDefaults] objectForKey:@"previousSyncClippingsViaICloud"] || syncClippings != [[NSUserDefaults standardUserDefaults] boolForKey:@"previousSyncClippingsViaICloud"] ); - [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:syncClippings] - forKey:@"previousSyncClippingsViaICloud"]; + if ( changedSyncClippings ) + [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:syncClippings] + forKey:@"previousSyncClippingsViaICloud"]; // We will enable / disable regardless of changedSyncClippings because this gets called at app launch, where the feature was previously enabled but needs to be registered. if ( syncClippings ) { diff --git a/acknowledgements.txt b/acknowledgements.txt new file mode 100644 index 0000000..c99acdc --- /dev/null +++ b/acknowledgements.txt @@ -0,0 +1,25 @@ +Flycut, developed by General Arcade (http://www.generalarcade.com/). Flycut is a fork of Jumpcut. Source code avilable on GitHub: http://github.com/TermiT/Flycut +© General Arcade, 2011 - 2017. + +Flycut macOS incorporates the following libraries, used with gratitude: + • ShortcutRecorder by Jesper et al. () + • a modified version of UKPrefsPanel by Uli Kusterer () + • SGHotKeysLib by Justin Williams () +Flycut iOS incorporates the following libraries, used with gratitude: + • MGSwipeTableCell by Imanol Fernandez () + • a modified version of InAppSettingsKit by Luc Vandal and Ortwin Gentz () +Flycut macOS & iOS incorporates the following libraries, used with gratitude: + • MJCloudKitUserDefaultsSync by Mark Jerde () + +Jumpcut is open source code built with open source code. Jumpcut incorporates the +following libraries, used with gratitude: + • PTHotKey by Quentin Carnicelli () + • ShortcutRecorder by Jesper et al. () + • Sparkle by Andy Matuschak () + • a modified version of UKPrefsPanel by Uli Kusterer () + +Additionally, much of the original code for Jumpcut was derived from Brett Simmons' TigerLaunch () and constant referral to the examples and discussion at CocoaDev.com. + +Jumpcut is subject to the MIT License. Source code and license details are available at its homepage, (). Unless otherwise noted, Jumpcut is copyright © Steve Cook, 2002 – 2008. + +Thanks to Clare Bates Congdon, Joshua Davis, Brad Graham, David Jacobs, John Kenzie, Adam Rice, Finn Smith, Vera Tobin, and Andre Torrez for early and ongoing feedback, advice, and encouragement.