Extend Settings access to Flycut iOS

Use InAppSettingsKit to display Settings within the app in addition to within
Settings.app.  This allows changes that initiate a user prompt to prompt the
user immediately when the change is made, rather than upon app launch as is the
case with using Settings.app, providing better user experience.

Move acknowledgements from preferences panel into a text file that is sourced into the preferences panel on macOS and the settings bundle (in-app only) on iOS.
This commit is contained in:
Mark Jerde 2017-10-31 08:58:34 -05:00
parent 6f72517444
commit 99d471f46a
17 changed files with 454 additions and 89 deletions

View file

@ -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;
}

View file

@ -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 {

View file

@ -96,6 +96,7 @@
</menu>
<customObject id="213" userLabel="AppController" customClass="AppController">
<connections>
<outlet property="acknowledgementsView" destination="357" id="2y8-wF-wPX"/>
<outlet property="appearancePanel" destination="EFy-pc-aKl" id="NYD-Y7-bfQ"/>
<outlet property="jcMenu" destination="206" id="215"/>
<outlet property="mainRecorder" destination="555" id="556"/>
@ -580,42 +581,7 @@
<size key="minSize" width="474" height="344"/>
<size key="maxSize" width="551" height="10000000"/>
<attributedString key="textStorage">
<fragment>
<string key="content">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.
</string>
<attributes>
<color key="NSColor" name="textColor" catalog="System" colorSpace="catalog"/>
<font key="NSFont" metaFont="user"/>
<paragraphStyle key="NSParagraphStyle" alignment="natural" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment>
<mutableString key="content" base64-UTF8="YES">
FhZKdW1wY3V0IGlzIG9wZW4gc291cmNlIGNvZGUgYnVpbHQgd2l0aCBvcGVuIHNvdXJjZSBjb2RlLiBK
dW1wY3V0IGluY29ycG9yYXRlcyB0aGUKZm9sbG93aW5nIGxpYnJhcmllcywgdXNlZCB3aXRoIGdyYXRp
dHVkZToKCeKAoiBQVEhvdEtleSBieSBRdWVudGluIENhcm5pY2VsbGkgKDxodHRwOi8vcm9ndWVhbW9l
YmEuY29tLz4pCgnigKIgU2hvcnRjdXRSZWNvcmRlciBieSBKZXNwZXIgZXQgYWwuICg8aHR0cDovL3dh
ZmZsZXNvZnR3YXJlLm5ldC8+KQoJ4oCiIFNwYXJrbGUgYnkgQW5keSBNYXR1c2NoYWsgKDxodHRwOi8v
d3d3LmFuZHltYXR1c2NoYWsub3JnLz4pCgnigKIgYSBtb2RpZmllZCB2ZXJzaW9uIG9mIFVLUHJlZnNQ
YW5lbCBieSBVbGkgS3VzdGVyZXIgKDxodHRwOi8vd3d3LnphdGhyYXMuZGUvPikgCgpBZGRpdGlvbmFs
bHksIG11Y2ggb2YgdGhlIG9yaWdpbmFsIGNvZGUgZm9yIEp1bXBjdXQgd2FzIGRlcml2ZWQgZnJvbSBC
cmV0dCBTaW1tb25zJyAKVGlnZXJMYXVuY2ggKDxodHRwOi8vcmFuY2hlcm8uY29tL3RpZ2VybGF1bmNo
Lz4pIGFuZCBjb25zdGFudCByZWZlcnJhbCB0byB0aGUgZXhhbXBsZXMgYW5kCmRpc2N1c3Npb24gYXQg
Q29jb2FEZXYuY29tLgoKSnVtcGN1dCBpcyBzdWJqZWN0IHRvIHRoZSBNSVQgTGljZW5zZS4gU291cmNl
IGNvZGUgYW5kIGxpY2Vuc2UgZGV0YWlscyBhcmUgYXZhaWxhYmxlIGF0IGl0cwpob21lcGFnZSwgKDxo
dHRwOi8vanVtcGN1dC5zb3VyY2Vmb3JnZS5uZXQ+KS4gVW5sZXNzIG90aGVyd2lzZSBub3RlZCwgSnVt
cGN1dCBpcyAKY29weXJpZ2h0IMKpIFN0ZXZlIENvb2ssIDIwMDIg4oCTIDIwMDguCg
</mutableString>
<attributes>
<color key="NSColor" name="textColor" catalog="System" colorSpace="catalog"/>
<font key="NSFont" metaFont="user"/>
</attributes>
</fragment>
<fragment>
<string key="content">
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.</string>
<fragment content="This text is loaded from acknowledgements.txt">
<attributes>
<color key="NSColor" name="textColor" catalog="System" colorSpace="catalog"/>
<font key="NSFont" metaFont="user"/>

Binary file not shown.

View file

@ -7,3 +7,4 @@
#import "MGSwipeTableCell/MGSwipeTableCell/MGSwipeTableCell.h"
#import "MGSwipeTableCell/MGSwipeTableCell/MGSwipeButton.h"
#import "MJCloudKitUserDefaultsSync/MJCloudKitUserDefaultsSync.h"
#import "IASKAppSettingsViewController.h"

View file

@ -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
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="15G1510" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="PQX-eG-eg7">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="15G1510" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="ZYN-k3-tW7">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
@ -22,7 +22,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="dqW-aG-gti">
<rect key="frame" x="0.0" y="28" width="375" height="639"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
@ -39,10 +39,70 @@
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" id="RQ1-iS-ooL">
<barButtonItem key="leftBarButtonItem" image="20-gear2.png" id="QXM-nx-VkL">
<connections>
<segue destination="0ch-Pn-DfM" kind="show" id="5u2-Bh-TQf"/>
</connections>
</barButtonItem>
</navigationItem>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="buM-Gl-9Lb" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1234.4000000000001" y="162.3688155922039"/>
</scene>
<!--Settings View Controller-->
<scene sceneID="KQo-DP-uEs">
<objects>
<tableViewController id="0ch-Pn-DfM" customClass="SettingsViewController" customModule="Flycut" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="bBK-zQ-h3K">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="BwM-pI-bs8">
<rect key="frame" x="0.0" y="56" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="BwM-pI-bs8" id="sCr-Hg-FLN">
<rect key="frame" x="0.0" y="0.0" width="375" height="43"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="0ch-Pn-DfM" id="t9i-3v-7jO"/>
<outlet property="delegate" destination="0ch-Pn-DfM" id="QdG-ed-sDo"/>
</connections>
</tableView>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="showDoneButton" value="NO"/>
<userDefinedRuntimeAttribute type="boolean" keyPath="neverShowPrivacySettings" value="NO"/>
</userDefinedRuntimeAttributes>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="b3f-4v-nae" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1990" y="162"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="7yJ-9R-Gjh">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="ZYN-k3-tW7" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="OfO-K0-UlJ">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="PQX-eG-eg7" kind="relationship" relationship="rootViewController" id="NCm-eY-8iU"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="0c6-O3-3Ck" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="295.19999999999999" y="162.3688155922039"/>
</scene>
</scenes>
<resources>
<image name="20-gear2.png" width="26" height="28"/>
</resources>
</document>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>StringsTable</key>
<string>Root</string>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string></string>
<key>Key</key>
<string>acknowledgementsText</string>
<key>FooterText</key>
<string>© General Arcade, 2011 - 2017.
© Steve Cook, 2002 2008.</string>
</dict>
</array>
</dict>
</plist>

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>StringsTable</key>
<string>Root</string>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Type</key>
<string>PSTextFieldSpecifier</string>
<key>Title</key>
<string>Remember Limit</string>
<key>Key</key>
<string>rememberNum</string>
<key>KeyboardType</key>
<string>NumberPad</string>
<key>DefaultValue</key>
<string>40</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Remove duplicates</string>
<key>Key</key>
<string>removeDuplicates</string>
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Move pasted item to top</string>
<key>Key</key>
<string>pasteMovesToTop</string>
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>iCloud Sync</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Settings</string>
<key>Key</key>
<string>syncSettingsViaICloud</string>
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Clippings</string>
<key>Key</key>
<string>syncClippingsViaICloud</string>
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string></string>
</dict>
<dict>
<key>Type</key>
<string>PSChildPaneSpecifier</string>
<key>Title</key>
<string>Acknowledgements</string>
<key>File</key>
<string>Acknowledgements</string>
</dict>
</array>
</dict>
</plist>

Binary file not shown.

View file

@ -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
}
}

View file

@ -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;

View file

@ -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 = "<group>"; };
8D0DC8391F3D4B6A00A2A8AF /* MGSwipeTableCell.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MGSwipeTableCell.xcodeproj; path = MGSwipeTableCell/MGSwipeTableCell.xcodeproj; sourceTree = "<group>"; };
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 = "<group>"; };
8D153A071F54CCF7006A5815 /* MJCloudKitUserDefaultsSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MJCloudKitUserDefaultsSync.h; path = MJCloudKitUserDefaultsSync/MJCloudKitUserDefaultsSync.h; sourceTree = "<group>"; };
8D153A081F54CCF7006A5815 /* MJCloudKitUserDefaultsSync.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MJCloudKitUserDefaultsSync.m; path = MJCloudKitUserDefaultsSync/MJCloudKitUserDefaultsSync.m; sourceTree = "<group>"; };
8D153A0C1F5BB249006A5815 /* Flycut.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Flycut.entitlements; sourceTree = "<group>"; };
8D180DFD1F9CD0580001F912 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
8D180E161F9FD3320001F912 /* InAppSettingsKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = InAppSettingsKit.xcodeproj; path = InAppSettingsKit/InAppSettingsKit.xcodeproj; sourceTree = "<group>"; };
8D180E261F9FD4B70001F912 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
8D180E281FA8CECD0001F912 /* 20-gear2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "20-gear2.png"; path = "InAppSettingsKit/InAppSettingsKitSampleApp/20-gear2.png"; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
@ -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 = "<group>";
@ -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 = "<group>";
@ -518,6 +565,16 @@
name = MJCloudKitUserDefaultsSync;
sourceTree = "<group>";
};
8D180E171F9FD3320001F912 /* Products */ = {
isa = PBXGroup;
children = (
8D180E1D1F9FD3320001F912 /* libInAppSettingsKit.a */,
8D180E1F1F9FD3320001F912 /* InAppSettingsKitTests.xctest */,
8D180E211F9FD3320001F912 /* InAppSettingsKit.framework */,
);
name = Products;
sourceTree = "<group>";
};
/* 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";

View file

@ -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];

View file

@ -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;;

View file

@ -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 ) {

25
acknowledgements.txt Normal file
View file

@ -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. (<http://wafflesoftware.net/>)
• a modified version of UKPrefsPanel by Uli Kusterer (<http://www.zathras.de/>)
• SGHotKeysLib by Justin Williams (<https://github.com/agilebits/SGHotKeysLib>)
Flycut iOS incorporates the following libraries, used with gratitude:
• MGSwipeTableCell by Imanol Fernandez (<https://github.com/MortimerGoro/MGSwipeTableCell>)
• a modified version of InAppSettingsKit by Luc Vandal and Ortwin Gentz (<https://github.com/futuretap/InAppSettingsKit>)
Flycut macOS & iOS incorporates the following libraries, used with gratitude:
• MJCloudKitUserDefaultsSync by Mark Jerde (<https://github.com/MarkJerde/MJCloudKitUserDefaultsSync>)
Jumpcut is open source code built with open source code. Jumpcut incorporates the
following libraries, used with gratitude:
• PTHotKey by Quentin Carnicelli (<http://rogueamoeba.com/>)
• ShortcutRecorder by Jesper et al. (<http://wafflesoftware.net/>)
• Sparkle by Andy Matuschak (<http://www.andymatuschak.org/>)
• a modified version of UKPrefsPanel by Uli Kusterer (<http://www.zathras.de/>)
Additionally, much of the original code for Jumpcut was derived from Brett Simmons' TigerLaunch (<http://ranchero.com/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, (<http://jumpcut.sourceforge.net>). 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.