mirror of
https://github.com/TermiT/Flycut.git
synced 2025-10-06 11:56:43 +08:00
Divide AppController into two files, one for UI and one for management of clipping stores.
This commit is contained in:
parent
7027778271
commit
f383482e4a
4 changed files with 746 additions and 459 deletions
|
@ -9,12 +9,15 @@
|
||||||
// at <https://github.com/TermiT/Flycut> for details.
|
// at <https://github.com/TermiT/Flycut> for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// AppController owns and interacts with the FlycutOperator, providing a user
|
||||||
|
// interface and platform-specific mechanisms.
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import <ApplicationServices/ApplicationServices.h>
|
#import <ApplicationServices/ApplicationServices.h>
|
||||||
#import "BezelWindow.h"
|
#import "BezelWindow.h"
|
||||||
#import "SRRecorderControl.h"
|
#import "SRRecorderControl.h"
|
||||||
#import "SRKeyCodeTransformer.h"
|
#import "SRKeyCodeTransformer.h"
|
||||||
#import "FlycutStore.h"
|
#import "FlycutOperator.h"
|
||||||
#import "SGHotKey.h"
|
#import "SGHotKey.h"
|
||||||
|
|
||||||
@class SGHotKey;
|
@class SGHotKey;
|
||||||
|
@ -30,14 +33,9 @@
|
||||||
BOOL isBezelDisplayed;
|
BOOL isBezelDisplayed;
|
||||||
BOOL isBezelPinned; // Currently not used
|
BOOL isBezelPinned; // Currently not used
|
||||||
NSString *currentKeycodeCharacter;
|
NSString *currentKeycodeCharacter;
|
||||||
int stackPosition;
|
|
||||||
int favoritesStackPosition;
|
|
||||||
int stashedStackPosition;
|
|
||||||
NSDateFormatter* dateFormat;
|
NSDateFormatter* dateFormat;
|
||||||
|
|
||||||
FlycutStore *clippingStore;
|
FlycutOperator *flycutOperator;
|
||||||
FlycutStore *favoritesStore;
|
|
||||||
FlycutStore *stashedStore;
|
|
||||||
|
|
||||||
// Status item -- the little icon in the menu bar
|
// Status item -- the little icon in the menu bar
|
||||||
NSStatusItem *statusItem;
|
NSStatusItem *statusItem;
|
||||||
|
@ -60,7 +58,6 @@
|
||||||
NSPasteboard *jcPasteboard;
|
NSPasteboard *jcPasteboard;
|
||||||
// Track the clipboard count so we only act when its contents change
|
// Track the clipboard count so we only act when its contents change
|
||||||
NSNumber *pbCount;
|
NSNumber *pbCount;
|
||||||
BOOL disableStore;
|
|
||||||
//stores PasteboardCount for internal Flycut pasteboard actions so they don't trigger any events
|
//stores PasteboardCount for internal Flycut pasteboard actions so they don't trigger any events
|
||||||
NSNumber *pbBlockCount;
|
NSNumber *pbBlockCount;
|
||||||
//Preferences
|
//Preferences
|
||||||
|
@ -71,27 +68,16 @@
|
||||||
|
|
||||||
// Basic functionality
|
// Basic functionality
|
||||||
-(void) pollPB:(NSTimer *)timer;
|
-(void) pollPB:(NSTimer *)timer;
|
||||||
-(BOOL) addClipToPasteboardFromCount:(int)indexInt;
|
-(void) addClipToPasteboard:(NSString*)pbFullText;
|
||||||
-(void) setPBBlockCount:(NSNumber *)newPBBlockCount;
|
-(void) setPBBlockCount:(NSNumber *)newPBBlockCount;
|
||||||
-(void) hideApp;
|
-(void) hideApp;
|
||||||
-(void) pasteFromStack;
|
|
||||||
-(void) saveFromStack;
|
|
||||||
-(void) fakeCommandV;
|
-(void) fakeCommandV;
|
||||||
-(void) stackUp;
|
|
||||||
-(void) stackDown;
|
|
||||||
-(IBAction)clearClippingList:(id)sender;
|
-(IBAction)clearClippingList:(id)sender;
|
||||||
-(IBAction)mergeClippingList:(id)sender;
|
-(IBAction)mergeClippingList:(id)sender;
|
||||||
-(void)controlTextDidChange:(NSNotification *)aNotification;
|
-(void)controlTextDidChange:(NSNotification *)aNotification;
|
||||||
-(BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector;
|
-(BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector;
|
||||||
-(IBAction)searchItems:(id)sender;
|
-(IBAction)searchItems:(id)sender;
|
||||||
|
|
||||||
// Stack related
|
|
||||||
-(BOOL) isValidClippingNumber:(NSNumber *)number;
|
|
||||||
-(NSString *) clippingStringWithCount:(int)count;
|
|
||||||
// Save and load
|
|
||||||
-(void) saveEngine;
|
|
||||||
-(void) loadEngineFromPList;
|
|
||||||
|
|
||||||
// Hotkey related
|
// Hotkey related
|
||||||
-(void)hitMainHotKey:(SGHotKey *)hotKey;
|
-(void)hitMainHotKey:(SGHotKey *)hotKey;
|
||||||
|
|
||||||
|
|
531
AppController.m
531
AppController.m
|
@ -9,6 +9,9 @@
|
||||||
// at <https://github.com/TermiT/Flycut> for details.
|
// at <https://github.com/TermiT/Flycut> for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// AppController owns and interacts with the FlycutOperator, providing a user
|
||||||
|
// interface and platform-specific mechanisms.
|
||||||
|
|
||||||
#import "AppController.h"
|
#import "AppController.h"
|
||||||
#import "SGHotKey.h"
|
#import "SGHotKey.h"
|
||||||
#import "SGHotKeyCenter.h"
|
#import "SGHotKeyCenter.h"
|
||||||
|
@ -17,23 +20,13 @@
|
||||||
#import "NSWindow+TrueCenter.h"
|
#import "NSWindow+TrueCenter.h"
|
||||||
#import "NSWindow+ULIZoomEffect.h"
|
#import "NSWindow+ULIZoomEffect.h"
|
||||||
|
|
||||||
#define _DISPLENGTH 40
|
|
||||||
|
|
||||||
@implementation AppController
|
@implementation AppController
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
|
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||||
[NSNumber numberWithInt:10],
|
|
||||||
@"displayNum",
|
|
||||||
[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithInt:9],[NSNumber numberWithLong:1179648],nil] forKeys:[NSArray arrayWithObjects:@"keyCode",@"modifierFlags",nil]],
|
[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSNumber numberWithInt:9],[NSNumber numberWithLong:1179648],nil] forKeys:[NSArray arrayWithObjects:@"keyCode",@"modifierFlags",nil]],
|
||||||
@"ShortcutRecorder mainHotkey",
|
@"ShortcutRecorder mainHotkey",
|
||||||
[NSNumber numberWithInt:40],
|
|
||||||
@"rememberNum",
|
|
||||||
[NSNumber numberWithInt:40],
|
|
||||||
@"favoritesRememberNum",
|
|
||||||
[NSNumber numberWithInt:1],
|
|
||||||
@"savePreference",
|
|
||||||
[NSNumber numberWithInt:0],
|
[NSNumber numberWithInt:0],
|
||||||
@"menuIcon",
|
@"menuIcon",
|
||||||
[NSNumber numberWithFloat:.25],
|
[NSNumber numberWithFloat:.25],
|
||||||
|
@ -51,30 +44,8 @@
|
||||||
@"bezelWidth",
|
@"bezelWidth",
|
||||||
[NSNumber numberWithFloat:320.0],
|
[NSNumber numberWithFloat:320.0],
|
||||||
@"bezelHeight",
|
@"bezelHeight",
|
||||||
[NSDictionary dictionary],
|
|
||||||
@"store",
|
|
||||||
[NSNumber numberWithBool:YES],
|
|
||||||
@"skipPasswordFields",
|
|
||||||
[NSNumber numberWithBool:YES],
|
|
||||||
@"skipPboardTypes",
|
|
||||||
@"PasswordPboardType",
|
|
||||||
@"skipPboardTypesList",
|
|
||||||
[NSNumber numberWithBool:NO],
|
|
||||||
@"skipPasswordLengths",
|
|
||||||
@"12, 20, 32",
|
|
||||||
@"skipPasswordLengthsList",
|
|
||||||
[NSNumber numberWithBool:NO],
|
|
||||||
@"revealPasteboardTypes",
|
|
||||||
[NSNumber numberWithBool:NO],
|
|
||||||
@"removeDuplicates",
|
|
||||||
[NSNumber numberWithBool:NO],
|
|
||||||
@"saveForgottenClippings",
|
|
||||||
[NSNumber numberWithBool:YES],
|
|
||||||
@"saveForgottenFavorites",
|
|
||||||
[NSNumber numberWithBool:NO],
|
[NSNumber numberWithBool:NO],
|
||||||
@"popUpAnimation",
|
@"popUpAnimation",
|
||||||
[NSNumber numberWithBool:NO],
|
|
||||||
@"pasteMovesToTop",
|
|
||||||
[NSNumber numberWithBool:YES],
|
[NSNumber numberWithBool:YES],
|
||||||
@"displayClippingSource",
|
@"displayClippingSource",
|
||||||
nil]];
|
nil]];
|
||||||
|
@ -91,14 +62,11 @@
|
||||||
[[[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"ShortcutRecorder mainHotkey"] objectForKey:@"modifierFlags"] intValue] )
|
[[[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"ShortcutRecorder mainHotkey"] objectForKey:@"modifierFlags"] intValue] )
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
// Initialize the FlycutStore
|
|
||||||
clippingStore = [[FlycutStore alloc] initRemembering:[[NSUserDefaults standardUserDefaults] integerForKey:@"rememberNum"]
|
// Initialize the FlycutOperator
|
||||||
displaying:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"]
|
flycutOperator = [[FlycutOperator alloc] init];
|
||||||
withDisplayLength:_DISPLENGTH];
|
[flycutOperator awakeFromNib];
|
||||||
favoritesStore = [[FlycutStore alloc] initRemembering:[[NSUserDefaults standardUserDefaults] integerForKey:@"favoritesRememberNum"]
|
|
||||||
displaying:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"]
|
|
||||||
withDisplayLength:_DISPLENGTH];
|
|
||||||
stashedStore = NULL;
|
|
||||||
[bezel setColor:NO];
|
[bezel setColor:NO];
|
||||||
|
|
||||||
// Set up the bezel window
|
// Set up the bezel window
|
||||||
|
@ -108,7 +76,6 @@
|
||||||
dateFormat = [[NSDateFormatter alloc] init];
|
dateFormat = [[NSDateFormatter alloc] init];
|
||||||
[dateFormat setDateFormat:@"EEEE, MMMM dd 'at' h:mm a"];
|
[dateFormat setDateFormat:@"EEEE, MMMM dd 'at' h:mm a"];
|
||||||
|
|
||||||
|
|
||||||
// Create our pasteboard interface
|
// Create our pasteboard interface
|
||||||
jcPasteboard = [NSPasteboard generalPasteboard];
|
jcPasteboard = [NSPasteboard generalPasteboard];
|
||||||
[jcPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
|
[jcPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
|
||||||
|
@ -124,11 +91,12 @@
|
||||||
jcMenuBaseItemsCount = [[[[jcMenu itemArray] reverseObjectEnumerator] allObjects] count];
|
jcMenuBaseItemsCount = [[[[jcMenu itemArray] reverseObjectEnumerator] allObjects] count];
|
||||||
[statusItem setEnabled:YES];
|
[statusItem setEnabled:YES];
|
||||||
|
|
||||||
// If our preferences indicate that we are saving, load the dictionary from the saved plist
|
// If our preferences indicate that we are saving, we may have loaded the dictionary from the
|
||||||
// and use it to get everything set up.
|
// saved plist and should update the menu.
|
||||||
if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) {
|
if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) {
|
||||||
[self loadEngineFromPList];
|
[self updateMenu];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build our listener timer
|
// Build our listener timer
|
||||||
pollPBTimer = [[NSTimer scheduledTimerWithTimeInterval:(1.0)
|
pollPBTimer = [[NSTimer scheduledTimerWithTimeInterval:(1.0)
|
||||||
target:self
|
target:self
|
||||||
|
@ -141,10 +109,6 @@
|
||||||
pbBlockCount = [[NSNumber numberWithInt:0] retain];
|
pbBlockCount = [[NSNumber numberWithInt:0] retain];
|
||||||
[pollPBTimer fire];
|
[pollPBTimer fire];
|
||||||
|
|
||||||
// Stack position starts @ 0 by default
|
|
||||||
stackPosition = favoritesStackPosition = stashedStackPosition = 0;
|
|
||||||
|
|
||||||
|
|
||||||
// The load-on-startup check can be really slow, so this will be dispatched out so our thread isn't blocked.
|
// The load-on-startup check can be really slow, so this will be dispatched out so our thread isn't blocked.
|
||||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||||
dispatch_async(queue, ^{
|
dispatch_async(queue, ^{
|
||||||
|
@ -167,7 +131,8 @@
|
||||||
NSEvent *event = [NSApp currentEvent];
|
NSEvent *event = [NSApp currentEvent];
|
||||||
if([event modifierFlags] & NSAlternateKeyMask) {
|
if([event modifierFlags] & NSAlternateKeyMask) {
|
||||||
[menu cancelTracking];
|
[menu cancelTracking];
|
||||||
if (disableStore)
|
bool disableStore = [self toggleMenuIconDisabled];
|
||||||
|
if (!disableStore)
|
||||||
{
|
{
|
||||||
// Update the pbCount so we don't enable and have it immediately copy the thing the user was trying to avoid.
|
// Update the pbCount so we don't enable and have it immediately copy the thing the user was trying to avoid.
|
||||||
// Code copied from pollPB, which is disabled at this point, so the "should be okay" should still be okay.
|
// Code copied from pollPB, which is disabled at this point, so the "should be okay" should still be okay.
|
||||||
|
@ -177,7 +142,7 @@
|
||||||
[pbCount release];
|
[pbCount release];
|
||||||
pbCount = [[NSNumber numberWithInt:[jcPasteboard changeCount]] retain];
|
pbCount = [[NSNumber numberWithInt:[jcPasteboard changeCount]] retain];
|
||||||
}
|
}
|
||||||
disableStore = [self toggleMenuIconDisabled];
|
[flycutOperator setDisableStoreTo:disableStore];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -336,7 +301,7 @@
|
||||||
{
|
{
|
||||||
int choice;
|
int choice;
|
||||||
int newRemember = [sender intValue];
|
int newRemember = [sender intValue];
|
||||||
if ( newRemember < [clippingStore jcListCount] &&
|
if ( newRemember < [flycutOperator jcListCount] &&
|
||||||
! issuedRememberResizeWarning &&
|
! issuedRememberResizeWarning &&
|
||||||
! [[NSUserDefaults standardUserDefaults] boolForKey:@"stifleRememberResizeWarning"]
|
! [[NSUserDefaults standardUserDefaults] boolForKey:@"stifleRememberResizeWarning"]
|
||||||
) {
|
) {
|
||||||
|
@ -344,7 +309,12 @@
|
||||||
@"Resizing the stack to a value below its present size will cause clippings to be lost.",
|
@"Resizing the stack to a value below its present size will cause clippings to be lost.",
|
||||||
@"Resize", @"Cancel", @"Don't Warn Me Again");
|
@"Resize", @"Cancel", @"Don't Warn Me Again");
|
||||||
if ( choice == NSAlertAlternateReturn ) {
|
if ( choice == NSAlertAlternateReturn ) {
|
||||||
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:[clippingStore jcListCount]]
|
// User selected Cancel. This appears to set the user default to
|
||||||
|
// the current clipping count while not updating the clippingStore,
|
||||||
|
// resulting in truncation in the future. This condition dates back
|
||||||
|
// to snarkout's original creation of setRememberNumPref on May 17,
|
||||||
|
// 2006.
|
||||||
|
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:[flycutOperator jcListCount]]
|
||||||
forKey:@"rememberNum"];
|
forKey:@"rememberNum"];
|
||||||
[self updateMenu];
|
[self updateMenu];
|
||||||
return;
|
return;
|
||||||
|
@ -355,20 +325,24 @@
|
||||||
issuedRememberResizeWarning = YES;
|
issuedRememberResizeWarning = YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trim down the number displayed in the menu if it is greater than the new
|
||||||
|
// number to remember.
|
||||||
if ( newRemember < [[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] ) {
|
if ( newRemember < [[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] ) {
|
||||||
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:newRemember]
|
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:newRemember]
|
||||||
forKey:@"displayNum"];
|
forKey:@"displayNum"];
|
||||||
}
|
}
|
||||||
[clippingStore setRememberNum:newRemember];
|
|
||||||
|
// Set the value.
|
||||||
|
[flycutOperator setRememberNum: newRemember];
|
||||||
[self updateMenu];
|
[self updateMenu];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(IBAction) setFavoritesRememberNumPref:(id)sender
|
-(IBAction) setFavoritesRememberNumPref:(id)sender
|
||||||
{
|
{
|
||||||
FlycutStore *primary = clippingStore;
|
[flycutOperator switchToFavoritesStore];
|
||||||
clippingStore = favoritesStore;
|
|
||||||
[self setRememberNumPref: sender];
|
[self setRememberNumPref: sender];
|
||||||
clippingStore = primary;
|
[flycutOperator restoreStashedStore];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(IBAction) setDisplayNumPref:(id)sender
|
-(IBAction) setDisplayNumPref:(id)sender
|
||||||
|
@ -571,24 +545,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void)switchToFavoritesStore
|
- (void)restoreStashedStoreAndUpdate
|
||||||
{
|
{
|
||||||
stashedStore = clippingStore;
|
if ([flycutOperator restoreStashedStore])
|
||||||
clippingStore = favoritesStore;
|
|
||||||
stashedStackPosition = stackPosition;
|
|
||||||
stackPosition = favoritesStackPosition;
|
|
||||||
[bezel setColor:YES];
|
|
||||||
[self updateBezel];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)restoreStashedStore
|
|
||||||
{
|
|
||||||
if (NULL != stashedStore)
|
|
||||||
{
|
{
|
||||||
clippingStore = stashedStore;
|
|
||||||
stashedStore = NULL;
|
|
||||||
favoritesStackPosition = stackPosition;
|
|
||||||
stackPosition = stashedStackPosition;
|
|
||||||
[bezel setColor:NO];
|
[bezel setColor:NO];
|
||||||
[self updateBezel];
|
[self updateBezel];
|
||||||
}
|
}
|
||||||
|
@ -596,105 +556,40 @@
|
||||||
|
|
||||||
- (void)pasteFromStack
|
- (void)pasteFromStack
|
||||||
{
|
{
|
||||||
if ( [clippingStore jcListCount] > stackPosition ) {
|
NSString *content = [flycutOperator getPasteFromStackPosition];
|
||||||
[self pasteIndex: stackPosition];
|
if ( nil != content ) {
|
||||||
|
[self addClipToPasteboard:content];
|
||||||
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
||||||
[self performSelector:@selector(fakeCommandV) withObject:nil afterDelay:0.2];
|
[self performSelector:@selector(fakeCommandV) withObject:nil afterDelay:0.2];
|
||||||
} else {
|
} else {
|
||||||
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
||||||
}
|
}
|
||||||
[self restoreStashedStore];
|
[self restoreStashedStoreAndUpdate];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)saveFromStack
|
- (void)moveItemAtStackPositionToTopOfStack
|
||||||
{
|
{
|
||||||
[self saveFromStackWithPrefix:@""];
|
if ( [flycutOperator stackPositionIsInBounds] ) {
|
||||||
}
|
[self pasteIndexAndUpdate: [flycutOperator stackPosition]];
|
||||||
|
|
||||||
- (void)saveFromStackWithPrefix:(NSString*) prefix
|
|
||||||
{
|
|
||||||
if ( [clippingStore jcListCount] > stackPosition ) {
|
|
||||||
// Get text from clipping store.
|
|
||||||
NSString *pbFullText = [self clippingStringWithCount:stackPosition];
|
|
||||||
pbFullText = [pbFullText stringByReplacingOccurrencesOfString:@"\r" withString:@"\r\n"];
|
|
||||||
|
|
||||||
// Get the Desktop directory:
|
|
||||||
NSArray *paths = NSSearchPathForDirectoriesInDomains
|
|
||||||
(NSDesktopDirectory, NSUserDomainMask, YES);
|
|
||||||
NSString *desktopDirectory = [paths objectAtIndex:0];
|
|
||||||
|
|
||||||
// Get the timestamp string:
|
|
||||||
NSDate *currentDate = [NSDate date];
|
|
||||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
|
||||||
[dateFormatter setDateFormat:@"YYYY-MM-dd 'at' HH.mm.ss"];
|
|
||||||
NSString *dateString = [dateFormatter stringFromDate:currentDate];
|
|
||||||
|
|
||||||
// Make a file name to write the data to using the Desktop directory:
|
|
||||||
NSString *fileName = [NSString stringWithFormat:@"%@/%@%@Clipping %@.txt",
|
|
||||||
desktopDirectory, prefix, clippingStore == favoritesStore ? @"Favorite " : @"", dateString];
|
|
||||||
|
|
||||||
// Save content to the file
|
|
||||||
[pbFullText writeToFile:fileName
|
|
||||||
atomically:NO
|
|
||||||
encoding:NSNonLossyASCIIStringEncoding
|
|
||||||
error:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
|
||||||
[self restoreStashedStore];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)saveFromStackToFavorites
|
|
||||||
{
|
|
||||||
if ( clippingStore != favoritesStore && [clippingStore jcListCount] > stackPosition ) {
|
|
||||||
if ( [favoritesStore rememberNum] == [favoritesStore jcListCount]
|
|
||||||
&& [[[NSUserDefaults standardUserDefaults] valueForKey:@"saveForgottenFavorites"] boolValue] )
|
|
||||||
{
|
|
||||||
// favoritesStore is full, so save the last entry before it gets lost.
|
|
||||||
[self switchToFavoritesStore];
|
|
||||||
|
|
||||||
// Set to last item, save, and restore position.
|
|
||||||
stackPosition = [favoritesStore rememberNum]-1;
|
|
||||||
[self saveFromStackWithPrefix:@"Autosave "];
|
|
||||||
stackPosition = favoritesStackPosition;
|
|
||||||
|
|
||||||
// Restore prior state.
|
|
||||||
[self restoreStashedStore];
|
|
||||||
}
|
|
||||||
// Get text from clipping store.
|
|
||||||
[favoritesStore addClipping:[clippingStore clippingAtPosition:stackPosition] ];
|
|
||||||
[clippingStore clearItem:stackPosition];
|
|
||||||
[self updateBezel];
|
|
||||||
[self updateMenu];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)changeStack
|
|
||||||
{
|
|
||||||
if ( [clippingStore jcListCount] > stackPosition ) {
|
|
||||||
[self pasteIndex: stackPosition];
|
|
||||||
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
||||||
} else {
|
} else {
|
||||||
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)pasteIndex:(int) position {
|
- (void)pasteIndexAndUpdate:(int) position {
|
||||||
// If there is an active search, we need to map the menu index to the stack position.
|
// If there is an active search, we need to map the menu index to the stack position.
|
||||||
NSString* search = [searchBox stringValue];
|
NSString* search = [searchBox stringValue];
|
||||||
if ( nil != search && 0 != search.length )
|
if ( nil != search && 0 != search.length )
|
||||||
{
|
{
|
||||||
NSArray *mapping = [clippingStore previousIndexes:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] containing:search];
|
NSArray *mapping = [flycutOperator previousIndexes:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] containing:search];
|
||||||
position = [mapping[position] intValue];
|
position = [mapping[position] intValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self addClipToPasteboardFromCount:position];
|
NSString *content = [flycutOperator getPasteFromIndex: position];
|
||||||
|
if ( nil != content )
|
||||||
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"pasteMovesToTop"] ) {
|
{
|
||||||
[clippingStore clippingMoveToTop:position];
|
[self addClipToPasteboard:content];
|
||||||
stackPosition = 0;
|
|
||||||
[self updateMenu];
|
[self updateMenu];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -769,78 +664,10 @@
|
||||||
return NO; // Default handling of the command
|
return NO; // Default handling of the command
|
||||||
}
|
}
|
||||||
|
|
||||||
-(BOOL)shouldSkip:(NSString *)contents
|
|
||||||
{
|
|
||||||
NSString *type = [jcPasteboard availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]];
|
|
||||||
|
|
||||||
// Check to see if we are skipping passwords based on length and characters.
|
|
||||||
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"skipPasswordFields"] )
|
|
||||||
{
|
|
||||||
// Check to see if they want a little help figuring out what types to enter.
|
|
||||||
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"revealPasteboardTypes"] )
|
|
||||||
[clippingStore addClipping:type ofType:type fromAppLocalizedName:@"Flycut" fromAppBundleURL:nil atTimestamp:0];
|
|
||||||
|
|
||||||
__block bool skipClipping = NO;
|
|
||||||
|
|
||||||
// Check the array of types to skip.
|
|
||||||
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"skipPboardTypes"] )
|
|
||||||
{
|
|
||||||
NSArray *typesArray = [[[[NSUserDefaults standardUserDefaults] stringForKey:@"skipPboardTypesList"] stringByReplacingOccurrencesOfString:@" " withString:@""] componentsSeparatedByString: @","];
|
|
||||||
[typesArray enumerateObjectsUsingBlock:^(id typeString, NSUInteger idx, BOOL *stop)
|
|
||||||
{
|
|
||||||
if ( [type isEqualToString:typeString] )
|
|
||||||
{
|
|
||||||
skipClipping = YES;
|
|
||||||
*stop = YES;
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
if (skipClipping)
|
|
||||||
return YES;
|
|
||||||
|
|
||||||
// Check the array of lengths to skip for suspicious strings.
|
|
||||||
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"skipPasswordLengths"] )
|
|
||||||
{
|
|
||||||
int contentsLength = [contents length];
|
|
||||||
NSArray *lengthsArray = [[[[NSUserDefaults standardUserDefaults] stringForKey:@"skipPasswordLengthsList"] stringByReplacingOccurrencesOfString:@" " withString:@""] componentsSeparatedByString: @","];
|
|
||||||
[lengthsArray enumerateObjectsUsingBlock:^(id lengthString, NSUInteger idx, BOOL *stop)
|
|
||||||
{
|
|
||||||
if ( [lengthString integerValue] == contentsLength )
|
|
||||||
{
|
|
||||||
NSRange uppercaseLetter = [contents rangeOfCharacterFromSet: [NSCharacterSet uppercaseLetterCharacterSet]];
|
|
||||||
NSRange lowercaseLetter = [contents rangeOfCharacterFromSet: [NSCharacterSet lowercaseLetterCharacterSet]];
|
|
||||||
NSRange decimalDigit = [contents rangeOfCharacterFromSet: [NSCharacterSet decimalDigitCharacterSet]];
|
|
||||||
NSRange punctuation = [contents rangeOfCharacterFromSet: [NSCharacterSet punctuationCharacterSet]];
|
|
||||||
NSRange symbol = [contents rangeOfCharacterFromSet: [NSCharacterSet symbolCharacterSet]];
|
|
||||||
NSRange control = [contents rangeOfCharacterFromSet: [NSCharacterSet controlCharacterSet]];
|
|
||||||
NSRange illegal = [contents rangeOfCharacterFromSet: [NSCharacterSet illegalCharacterSet]];
|
|
||||||
NSRange whitespaceAndNewline = [contents rangeOfCharacterFromSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
||||||
if ( NSNotFound == control.location
|
|
||||||
&& NSNotFound == illegal.location
|
|
||||||
&& NSNotFound == whitespaceAndNewline.location
|
|
||||||
&& NSNotFound != uppercaseLetter.location
|
|
||||||
&& NSNotFound != lowercaseLetter.location
|
|
||||||
&& NSNotFound != decimalDigit.location
|
|
||||||
&& ( NSNotFound != punctuation.location
|
|
||||||
|| NSNotFound != symbol.location ) )
|
|
||||||
{
|
|
||||||
skipClipping = YES;
|
|
||||||
*stop = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
if (skipClipping)
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)pollPB:(NSTimer *)timer
|
-(void)pollPB:(NSTimer *)timer
|
||||||
{
|
{
|
||||||
NSString *type = [jcPasteboard availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]];
|
NSString *type = [jcPasteboard availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]];
|
||||||
if ( [pbCount intValue] != [jcPasteboard changeCount] && !disableStore ) {
|
if ( [pbCount intValue] != [jcPasteboard changeCount] && ![flycutOperator storeDisabled] ) {
|
||||||
// Reload pbCount with the current changeCount
|
// Reload pbCount with the current changeCount
|
||||||
// Probably poor coding technique, but pollPB should be the only thing messing with pbCount, so it should be okay
|
// Probably poor coding technique, but pollPB should be the only thing messing with pbCount, so it should be okay
|
||||||
[pbCount release];
|
[pbCount release];
|
||||||
|
@ -867,35 +694,10 @@
|
||||||
if (largeCopyRisk)
|
if (largeCopyRisk)
|
||||||
[self toggleMenuIconDisabled];
|
[self toggleMenuIconDisabled];
|
||||||
|
|
||||||
if ( contents == nil || [self shouldSkip:contents] ) {
|
if ( contents == nil || [flycutOperator shouldSkip:contents ofType:[jcPasteboard availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]] ) {
|
||||||
DLog(@"Contents: Empty or skipped");
|
DLog(@"Contents: Empty or skipped");
|
||||||
} else {
|
} else if ( ! [pbCount isEqualTo:pbBlockCount] ) {
|
||||||
if (( [clippingStore jcListCount] == 0 || ! [contents isEqualToString:[clippingStore clippingContentsAtPosition:0]])
|
[flycutOperator addClipping:contents ofType:type fromApp:[currRunningApp localizedName] withAppBundleURL:currRunningApp.bundleURL.path target:self clippingAddedSelector:@selector(updateMenu)];
|
||||||
&& ! [pbCount isEqualTo:pbBlockCount] ) {
|
|
||||||
|
|
||||||
if ( [clippingStore rememberNum] == [clippingStore jcListCount]
|
|
||||||
&& [[[NSUserDefaults standardUserDefaults] valueForKey:@"saveForgottenClippings"] boolValue] )
|
|
||||||
{
|
|
||||||
// clippingStore is full, so save the last entry before it gets lost.
|
|
||||||
// Set to last item, save, and restore position.
|
|
||||||
int savePosition = stackPosition;
|
|
||||||
stackPosition = [clippingStore rememberNum]-1;
|
|
||||||
[self saveFromStackWithPrefix:@"Autosave "];
|
|
||||||
stackPosition = savePosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
[clippingStore addClipping:contents
|
|
||||||
ofType:type
|
|
||||||
fromAppLocalizedName:[currRunningApp localizedName]
|
|
||||||
fromAppBundleURL:currRunningApp.bundleURL.path
|
|
||||||
atTimestamp:[[NSDate date] timeIntervalSince1970]];
|
|
||||||
// The below tracks our position down down down... Maybe as an option?
|
|
||||||
// if ( [clippingStore jcListCount] > 1 ) stackPosition++;
|
|
||||||
stackPosition = 0;
|
|
||||||
[self updateMenu];
|
|
||||||
if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 2 )
|
|
||||||
[self saveEngine];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -921,7 +723,7 @@
|
||||||
[self pasteFromStack];
|
[self pasteFromStack];
|
||||||
break;
|
break;
|
||||||
case 0x3:
|
case 0x3:
|
||||||
[self changeStack];
|
[self moveItemAtStackPositionToTopOfStack];
|
||||||
break;
|
break;
|
||||||
case 0x2C: // Comma
|
case 0x2C: // Comma
|
||||||
if ( modifiers & NSCommandKeyMask ) {
|
if ( modifiers & NSCommandKeyMask ) {
|
||||||
|
@ -939,37 +741,31 @@
|
||||||
[self stackDown];
|
[self stackDown];
|
||||||
break;
|
break;
|
||||||
case NSHomeFunctionKey:
|
case NSHomeFunctionKey:
|
||||||
if ( [clippingStore jcListCount] > 0 ) {
|
if ( [flycutOperator setStackPositionToFirstItem] ) {
|
||||||
stackPosition = 0;
|
|
||||||
[self updateBezel];
|
[self updateBezel];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NSEndFunctionKey:
|
case NSEndFunctionKey:
|
||||||
if ( [clippingStore jcListCount] > 0 ) {
|
if ( [flycutOperator setStackPositionToLastItem] ) {
|
||||||
stackPosition = [clippingStore jcListCount] - 1;
|
|
||||||
[self updateBezel];
|
[self updateBezel];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NSPageUpFunctionKey:
|
case NSPageUpFunctionKey:
|
||||||
if ( [clippingStore jcListCount] > 0 ) {
|
if ( [flycutOperator setStackPositionToTenMoreRecent] ) {
|
||||||
stackPosition = stackPosition - 10; if ( stackPosition < 0 ) stackPosition = 0;
|
|
||||||
[self updateBezel];
|
[self updateBezel];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NSPageDownFunctionKey:
|
case NSPageDownFunctionKey:
|
||||||
if ( [clippingStore jcListCount] > 0 ) {
|
if ( [flycutOperator setStackPositionToTenLessRecent] ) {
|
||||||
stackPosition = stackPosition + 10; if ( stackPosition >= [clippingStore jcListCount] ) stackPosition = [clippingStore jcListCount] - 1;
|
|
||||||
[self updateBezel];
|
[self updateBezel];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NSBackspaceCharacter:
|
case NSBackspaceCharacter:
|
||||||
case NSDeleteCharacter:
|
case NSDeleteCharacter:
|
||||||
if ([clippingStore jcListCount] == 0)
|
if ( [flycutOperator clearItemAtStackPosition] ) {
|
||||||
return;
|
[self updateBezel];
|
||||||
|
[self updateMenu];
|
||||||
[clippingStore clearItem:stackPosition];
|
}
|
||||||
[self updateBezel];
|
|
||||||
[self updateMenu];
|
|
||||||
break;
|
break;
|
||||||
case NSDeleteFunctionKey: break;
|
case NSDeleteFunctionKey: break;
|
||||||
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // Numeral
|
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // Numeral
|
||||||
|
@ -977,32 +773,42 @@
|
||||||
// We'll currently ignore the possibility that the user wants to do something with shift.
|
// We'll currently ignore the possibility that the user wants to do something with shift.
|
||||||
// First, let's set the new stack count to "10" if the user pressed "0"
|
// First, let's set the new stack count to "10" if the user pressed "0"
|
||||||
newStackPosition = pressed == 0x30 ? 9 : [[NSString stringWithCharacters:&pressed length:1] intValue] - 1;
|
newStackPosition = pressed == 0x30 ? 9 : [[NSString stringWithCharacters:&pressed length:1] intValue] - 1;
|
||||||
if ( [clippingStore jcListCount] >= newStackPosition ) {
|
if ( [flycutOperator setStackPositionTo: newStackPosition] ) {
|
||||||
stackPosition = newStackPosition;
|
|
||||||
[self fillBezel];
|
[self fillBezel];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 's': case 'S': // Save / Save-and-delete
|
case 's': case 'S': // Save / Save-and-delete
|
||||||
if ([clippingStore jcListCount] == 0)
|
{
|
||||||
return;
|
bool success = [flycutOperator saveFromStack];
|
||||||
|
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
||||||
|
[self restoreStashedStoreAndUpdate];
|
||||||
|
|
||||||
[self saveFromStack];
|
if ( success ) {
|
||||||
if ( modifiers & NSShiftKeyMask ) {
|
if ( modifiers & NSShiftKeyMask ) {
|
||||||
[clippingStore clearItem:stackPosition];
|
[flycutOperator clearItemAtStackPosition];
|
||||||
[self updateBezel];
|
[self updateBezel];
|
||||||
[self updateMenu];
|
[self updateMenu];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
if (NULL != stashedStore)
|
[flycutOperator toggleToFromFavoritesStore];
|
||||||
[self restoreStashedStore];
|
[bezel setColor:[flycutOperator favoritesStoreIsSelected]];
|
||||||
else
|
[self updateBezel];
|
||||||
[self switchToFavoritesStore];
|
|
||||||
[self hideBezel];
|
[self hideBezel];
|
||||||
[self showBezel];
|
[self showBezel];
|
||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
[self saveFromStackToFavorites];
|
if ( [flycutOperator saveFromStackToFavorites] )
|
||||||
|
{
|
||||||
|
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
||||||
|
[self restoreStashedStoreAndUpdate];
|
||||||
|
[self updateBezel];
|
||||||
|
[self updateMenu];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self performSelector:@selector(hideApp) withObject:nil afterDelay:0.2];
|
||||||
break;
|
break;
|
||||||
default: // It's not a navigation/application-defined thing, so let's figure out what to do with it.
|
default: // It's not a navigation/application-defined thing, so let's figure out what to do with it.
|
||||||
DLog(@"PRESSED %d", pressed);
|
DLog(@"PRESSED %d", pressed);
|
||||||
|
@ -1032,10 +838,8 @@
|
||||||
|
|
||||||
- (void) updateBezel
|
- (void) updateBezel
|
||||||
{
|
{
|
||||||
if (stackPosition >= [clippingStore jcListCount] && stackPosition != 0) { // deleted last item
|
[flycutOperator adjustStackPositionIfOutOfBounds];
|
||||||
stackPosition = [clippingStore jcListCount] - 1;
|
if ([flycutOperator jcListCount] == 0) { // empty
|
||||||
}
|
|
||||||
if (stackPosition == 0 && [clippingStore jcListCount] == 0) { // empty
|
|
||||||
[bezel setText:@""];
|
[bezel setText:@""];
|
||||||
[bezel setCharString:@"Empty"];
|
[bezel setCharString:@"Empty"];
|
||||||
[bezel setSource:@""];
|
[bezel setSource:@""];
|
||||||
|
@ -1049,7 +853,7 @@
|
||||||
|
|
||||||
- (void) showBezel
|
- (void) showBezel
|
||||||
{
|
{
|
||||||
if ( [clippingStore jcListCount] > 0 && [clippingStore jcListCount] > stackPosition ) {
|
if ( [flycutOperator stackPositionIsInBounds] ) {
|
||||||
[self fillBezel];
|
[self fillBezel];
|
||||||
}
|
}
|
||||||
NSRect mainScreenRect = [NSScreen mainScreen].visibleFrame;
|
NSRect mainScreenRect = [NSScreen mainScreen].visibleFrame;
|
||||||
|
@ -1128,18 +932,18 @@
|
||||||
|
|
||||||
// on clear, zap the list and redraw the menu
|
// on clear, zap the list and redraw the menu
|
||||||
if ( choice == NSAlertDefaultReturn ) {
|
if ( choice == NSAlertDefaultReturn ) {
|
||||||
[self restoreStashedStore]; // Only clear the clipping store. Never the favorites.
|
[self restoreStashedStoreAndUpdate]; // Only clear the clipping store. Never the favorites.
|
||||||
[clippingStore clearList];
|
[flycutOperator clearList];
|
||||||
[self updateMenu];
|
[self updateMenu];
|
||||||
if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) {
|
if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) {
|
||||||
[self saveEngine];
|
[flycutOperator saveEngine];
|
||||||
}
|
}
|
||||||
[bezel setText:@""];
|
[bezel setText:@""];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-(IBAction)mergeClippingList:(id)sender {
|
-(IBAction)mergeClippingList:(id)sender {
|
||||||
[clippingStore mergeList];
|
[flycutOperator mergeList];
|
||||||
[self updateMenu];
|
[self updateMenu];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1153,7 +957,7 @@
|
||||||
- (void)updateMenuContaining:(NSString*)search {
|
- (void)updateMenuContaining:(NSString*)search {
|
||||||
[jcMenu setMenuChangedMessagesEnabled:NO];
|
[jcMenu setMenuChangedMessagesEnabled:NO];
|
||||||
|
|
||||||
NSArray *returnedDisplayStrings = [clippingStore previousDisplayStrings:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] containing:search];
|
NSArray *returnedDisplayStrings = [flycutOperator previousDisplayStrings:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"] containing:search];
|
||||||
|
|
||||||
NSArray *menuItems = [[[jcMenu itemArray] reverseObjectEnumerator] allObjects];
|
NSArray *menuItems = [[[jcMenu itemArray] reverseObjectEnumerator] allObjects];
|
||||||
|
|
||||||
|
@ -1197,7 +1001,7 @@
|
||||||
-(IBAction)processMenuClippingSelection:(id)sender
|
-(IBAction)processMenuClippingSelection:(id)sender
|
||||||
{
|
{
|
||||||
int index=[[sender menu] indexOfItem:sender];
|
int index=[[sender menu] indexOfItem:sender];
|
||||||
[self pasteIndex:index];
|
[self pasteIndexAndUpdate:index];
|
||||||
|
|
||||||
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"menuSelectionPastes"] ) {
|
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"menuSelectionPastes"] ) {
|
||||||
[self performSelector:@selector(hideApp) withObject:nil];
|
[self performSelector:@selector(hideApp) withObject:nil];
|
||||||
|
@ -1205,18 +1009,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-(BOOL) isValidClippingNumber:(NSNumber *)number {
|
|
||||||
return ( ([number intValue] + 1) <= [clippingStore jcListCount] );
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString *) clippingStringWithCount:(int)count {
|
|
||||||
if ( [self isValidClippingNumber:[NSNumber numberWithInt:count]] ) {
|
|
||||||
return [clippingStore clippingContentsAtPosition:count];
|
|
||||||
} else { // It fails -- we shouldn't be passed this, but...
|
|
||||||
return @"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) setPBBlockCount:(NSNumber *)newPBBlockCount
|
-(void) setPBBlockCount:(NSNumber *)newPBBlockCount
|
||||||
{
|
{
|
||||||
[newPBBlockCount retain];
|
[newPBBlockCount retain];
|
||||||
|
@ -1224,94 +1016,29 @@
|
||||||
pbBlockCount = newPBBlockCount;
|
pbBlockCount = newPBBlockCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(BOOL)addClipToPasteboardFromCount:(int)indexInt
|
-(void)addClipToPasteboard:(NSString*)pbFullText
|
||||||
{
|
{
|
||||||
NSString *pbFullText;
|
|
||||||
NSArray *pbTypes;
|
NSArray *pbTypes;
|
||||||
if ( (indexInt + 1) > [clippingStore jcListCount] ) {
|
|
||||||
// We're asking for a clipping that isn't there yet
|
|
||||||
// This only tends to happen immediately on startup when not saving, as the entire list is empty.
|
|
||||||
DLog(@"Out of bounds request to jcList ignored.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pbFullText = [self clippingStringWithCount:indexInt];
|
|
||||||
pbTypes = [NSArray arrayWithObjects:@"NSStringPboardType",NULL];
|
pbTypes = [NSArray arrayWithObjects:@"NSStringPboardType",NULL];
|
||||||
|
|
||||||
[jcPasteboard declareTypes:pbTypes owner:NULL];
|
[jcPasteboard declareTypes:pbTypes owner:NULL];
|
||||||
|
|
||||||
[jcPasteboard setString:pbFullText forType:@"NSStringPboardType"];
|
[jcPasteboard setString:pbFullText forType:@"NSStringPboardType"];
|
||||||
[self setPBBlockCount:[NSNumber numberWithInt:[jcPasteboard changeCount]]];
|
[self setPBBlockCount:[NSNumber numberWithInt:[jcPasteboard changeCount]]];
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) loadEngineFromPList
|
|
||||||
{
|
|
||||||
NSDictionary *loadDict = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"store"] copy];
|
|
||||||
NSArray *savedJCList;
|
|
||||||
NSRange loadRange;
|
|
||||||
|
|
||||||
int rangeCap;
|
|
||||||
|
|
||||||
if ( loadDict != nil ) {
|
|
||||||
|
|
||||||
savedJCList = [loadDict objectForKey:@"jcList"];
|
|
||||||
|
|
||||||
if ( [savedJCList isKindOfClass:[NSArray class]] ) {
|
|
||||||
int rememberNumPref = [[NSUserDefaults standardUserDefaults]
|
|
||||||
integerForKey:@"rememberNum"];
|
|
||||||
// There's probably a nicer way to prevent the range from going out of bounds, but this works.
|
|
||||||
rangeCap = [savedJCList count] < rememberNumPref ? [savedJCList count] : rememberNumPref;
|
|
||||||
loadRange = NSMakeRange(0, rangeCap);
|
|
||||||
NSArray *toBeRestoredClips = [[[savedJCList subarrayWithRange:loadRange] reverseObjectEnumerator] allObjects];
|
|
||||||
for( NSDictionary *aSavedClipping in toBeRestoredClips)
|
|
||||||
[clippingStore addClipping:[aSavedClipping objectForKey:@"Contents"]
|
|
||||||
ofType:[aSavedClipping objectForKey:@"Type"]
|
|
||||||
fromAppLocalizedName:[aSavedClipping objectForKey:@"AppLocalizedName"]
|
|
||||||
fromAppBundleURL:[aSavedClipping objectForKey:@"AppBundleURL"]
|
|
||||||
atTimestamp:[[aSavedClipping objectForKey:@"Timestamp"] integerValue]];
|
|
||||||
|
|
||||||
// Now for the favorites, same thing.
|
|
||||||
savedJCList =[loadDict objectForKey:@"favoritesList"];
|
|
||||||
if ( [savedJCList isKindOfClass:[NSArray class]] ) {
|
|
||||||
rememberNumPref = [[NSUserDefaults standardUserDefaults]
|
|
||||||
integerForKey:@"favoritesRememberNum"];
|
|
||||||
rangeCap = [savedJCList count] < rememberNumPref ? [savedJCList count] : rememberNumPref;
|
|
||||||
loadRange = NSMakeRange(0, rangeCap);
|
|
||||||
toBeRestoredClips = [[[savedJCList subarrayWithRange:loadRange] reverseObjectEnumerator] allObjects];
|
|
||||||
for( NSDictionary *aSavedClipping in toBeRestoredClips)
|
|
||||||
[favoritesStore addClipping:[aSavedClipping objectForKey:@"Contents"]
|
|
||||||
ofType:[aSavedClipping objectForKey:@"Type"]
|
|
||||||
fromAppLocalizedName:[aSavedClipping objectForKey:@"AppLocalizedName"]
|
|
||||||
fromAppBundleURL:[aSavedClipping objectForKey:@"AppBundleURL"]
|
|
||||||
atTimestamp:[[aSavedClipping objectForKey:@"Timestamp"] integerValue]];
|
|
||||||
}
|
|
||||||
} else DLog(@"Not array");
|
|
||||||
[self updateMenu];
|
|
||||||
[loadDict release];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-(void) stackDown
|
-(void) stackDown
|
||||||
{
|
{
|
||||||
stackPosition++;
|
if ( [flycutOperator setStackPositionToOneLessRecent] ) {
|
||||||
if ( [clippingStore jcListCount] > stackPosition ) {
|
|
||||||
[self fillBezel];
|
[self fillBezel];
|
||||||
} else {
|
|
||||||
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"wraparoundBezel"] ) {
|
|
||||||
stackPosition = 0;
|
|
||||||
[self fillBezel];
|
|
||||||
} else {
|
|
||||||
stackPosition--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) fillBezel
|
-(void) fillBezel
|
||||||
{
|
{
|
||||||
FlycutClipping* clipping = [clippingStore clippingAtPosition:stackPosition];
|
FlycutClipping* clipping = [flycutOperator clippingAtStackPosition];
|
||||||
[bezel setText:[NSString stringWithFormat:@"%@", [clipping contents]]];
|
[bezel setText:[NSString stringWithFormat:@"%@", [clipping contents]]];
|
||||||
[bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]];
|
[bezel setCharString:[NSString stringWithFormat:@"%d of %d", [flycutOperator stackPosition] + 1, [flycutOperator jcListCount]]];
|
||||||
NSString *localizedName = [clipping appLocalizedName];
|
NSString *localizedName = [clipping appLocalizedName];
|
||||||
if ( nil == localizedName )
|
if ( nil == localizedName )
|
||||||
localizedName = @"";
|
localizedName = @"";
|
||||||
|
@ -1328,67 +1055,11 @@
|
||||||
|
|
||||||
-(void) stackUp
|
-(void) stackUp
|
||||||
{
|
{
|
||||||
stackPosition--;
|
if ( [flycutOperator setStackPositionToOneMoreRecent] ) {
|
||||||
if ( stackPosition < 0 ) {
|
|
||||||
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"wraparoundBezel"] ) {
|
|
||||||
stackPosition = [clippingStore jcListCount] - 1;
|
|
||||||
[self fillBezel];
|
|
||||||
} else {
|
|
||||||
stackPosition = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( [clippingStore jcListCount] > stackPosition ) {
|
|
||||||
[self fillBezel];
|
[self fillBezel];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)saveStore:(FlycutStore *)store toKey:(NSString *)key onDict:(NSMutableDictionary *)saveDict {
|
|
||||||
NSMutableArray *jcListArray = [NSMutableArray array];
|
|
||||||
for ( int i = 0 ; i < [store jcListCount] ; i++ )
|
|
||||||
{
|
|
||||||
FlycutClipping *clipping = [store clippingAtPosition:i];
|
|
||||||
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
|
||||||
[clipping contents], @"Contents",
|
|
||||||
[clipping type], @"Type",
|
|
||||||
[NSNumber numberWithInt:i], @"Position",nil];
|
|
||||||
|
|
||||||
NSString *val = [clipping appLocalizedName];
|
|
||||||
if ( nil != val )
|
|
||||||
[dict setObject:val forKey:@"AppLocalizedName"];
|
|
||||||
|
|
||||||
val = [clipping appBundleURL];
|
|
||||||
if ( nil != val )
|
|
||||||
[dict setObject:val forKey:@"AppBundleURL"];
|
|
||||||
|
|
||||||
int timestamp = [clipping timestamp];
|
|
||||||
if ( timestamp > 0 )
|
|
||||||
[dict setObject:[NSNumber numberWithInt:timestamp] forKey:@"Timestamp"];
|
|
||||||
|
|
||||||
[jcListArray addObject:dict];
|
|
||||||
}
|
|
||||||
[saveDict setObject:jcListArray forKey:key];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) saveEngine {
|
|
||||||
NSMutableDictionary *saveDict;
|
|
||||||
saveDict = [NSMutableDictionary dictionaryWithCapacity:3];
|
|
||||||
[saveDict setObject:@"0.7" forKey:@"version"];
|
|
||||||
[saveDict setObject:[NSNumber numberWithInt:[[NSUserDefaults standardUserDefaults] integerForKey:@"rememberNum"]]
|
|
||||||
forKey:@"rememberNum"];
|
|
||||||
[saveDict setObject:[NSNumber numberWithInt:[[NSUserDefaults standardUserDefaults] integerForKey:@"favoritesRememberNum"]]
|
|
||||||
forKey:@"favoritesRememberNum"];
|
|
||||||
[saveDict setObject:[NSNumber numberWithInt:_DISPLENGTH]
|
|
||||||
forKey:@"displayLen"];
|
|
||||||
[saveDict setObject:[NSNumber numberWithInt:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"]]
|
|
||||||
forKey:@"displayNum"];
|
|
||||||
|
|
||||||
[self saveStore:clippingStore toKey:@"jcList" onDict:saveDict];
|
|
||||||
[self saveStore:favoritesStore toKey:@"favoritesList" onDict:saveDict];
|
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:saveDict forKey:@"store"];
|
|
||||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setHotKeyPreferenceForRecorder:(SRRecorderControl *)aRecorder {
|
- (void)setHotKeyPreferenceForRecorder:(SRRecorderControl *)aRecorder {
|
||||||
if (aRecorder == mainRecorder) {
|
if (aRecorder == mainRecorder) {
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:
|
[[NSUserDefaults standardUserDefaults] setObject:
|
||||||
|
@ -1410,15 +1081,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationWillTerminate:(NSNotification *)notification {
|
- (void)applicationWillTerminate:(NSNotification *)notification {
|
||||||
if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) {
|
[flycutOperator applicationWillTerminate];
|
||||||
DLog(@"Saving on exit");
|
|
||||||
[self saveEngine];
|
|
||||||
} else {
|
|
||||||
// Remove clips from store
|
|
||||||
[[NSUserDefaults standardUserDefaults] setValue:[NSDictionary dictionary] forKey:@"store"];
|
|
||||||
DLog(@"Saving preferences on exit");
|
|
||||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
|
||||||
}
|
|
||||||
//Unregister our hot key (not required)
|
//Unregister our hot key (not required)
|
||||||
[[SGHotKeyCenter sharedCenter] unregisterHotKey: mainHotKey];
|
[[SGHotKeyCenter sharedCenter] unregisterHotKey: mainHotKey];
|
||||||
[mainHotKey release];
|
[mainHotKey release];
|
||||||
|
|
|
@ -2,12 +2,84 @@
|
||||||
// FlycutOperator.h
|
// FlycutOperator.h
|
||||||
// Flycut
|
// Flycut
|
||||||
//
|
//
|
||||||
// Created by Mark Jerde on 7/10/17.
|
// Flycut by Gennadiy Potapov and contributors. Based on Jumpcut by Steve Cook.
|
||||||
|
// Copyright 2011 General Arcade. All rights reserved.
|
||||||
//
|
//
|
||||||
|
// This code is open-source software subject to the MIT License; see the homepage
|
||||||
|
// at <https://github.com/TermiT/Flycut> for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// FlycutOperator owns and interacts with the FlycutStores, providing
|
||||||
|
// manipulation of the stores.
|
||||||
|
|
||||||
#ifndef FlycutOperator_h
|
#ifndef FlycutOperator_h
|
||||||
#define FlycutOperator_h
|
#define FlycutOperator_h
|
||||||
|
|
||||||
|
#import "FlycutStore.h"
|
||||||
|
|
||||||
|
@interface FlycutOperator : NSObject {
|
||||||
|
int stackPosition;
|
||||||
|
int favoritesStackPosition;
|
||||||
|
int stashedStackPosition;
|
||||||
|
|
||||||
|
FlycutStore *clippingStore;
|
||||||
|
FlycutStore *favoritesStore;
|
||||||
|
FlycutStore *stashedStore;
|
||||||
|
|
||||||
|
BOOL disableStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic functionality
|
||||||
|
-(bool)addClipping:(NSString*)contents ofType:(NSString*)type fromApp:(NSString *)appName withAppBundleURL:(NSString *)bundleURL target:(id)selectorTarget clippingAddedSelector:(SEL)clippingAddedSelectorclippingAddedSelector;
|
||||||
|
-(int)stackPosition;
|
||||||
|
-(NSString*)getPasteFromStackPosition;
|
||||||
|
-(NSString*)getPasteFromIndex:(int) position;
|
||||||
|
-(bool) saveFromStack;
|
||||||
|
-(bool)clearItemAtStackPosition;
|
||||||
|
-(void)clearList;
|
||||||
|
-(void)mergeList;
|
||||||
|
|
||||||
|
// Stack position manipulation functionality
|
||||||
|
-(bool)setStackPositionToOneMoreRecent;
|
||||||
|
-(bool)setStackPositionToOneLessRecent;
|
||||||
|
-(bool)setStackPositionToFirstItem;
|
||||||
|
-(bool)setStackPositionToLastItem;
|
||||||
|
-(bool)setStackPositionToTenMoreRecent;
|
||||||
|
-(bool)setStackPositionToTenLessRecent;
|
||||||
|
-(bool)setStackPositionTo:(int) newStackPosition;
|
||||||
|
-(void)adjustStackPositionIfOutOfBounds;
|
||||||
|
-(bool)stackPositionIsInBounds;
|
||||||
|
|
||||||
|
// Stack related
|
||||||
|
-(BOOL) isValidClippingNumber:(NSNumber *)number;
|
||||||
|
-(NSString *) clippingStringWithCount:(int)count;
|
||||||
|
|
||||||
|
// Save and load
|
||||||
|
-(void) saveEngine;
|
||||||
|
-(bool) loadEngineFromPList;
|
||||||
|
|
||||||
|
// Preference related
|
||||||
|
-(void) setRememberNum:(int)newRemember;
|
||||||
|
|
||||||
|
// Initialization / cleanup related
|
||||||
|
-(void)applicationWillTerminate;;
|
||||||
|
-(void)awakeFromNib;
|
||||||
|
|
||||||
|
// Favorites Store related
|
||||||
|
-(bool)favoritesStoreIsSelected;
|
||||||
|
-(void)switchToFavoritesStore;
|
||||||
|
-(bool)restoreStashedStore;
|
||||||
|
-(void)toggleToFromFavoritesStore;
|
||||||
|
-(bool)saveFromStackToFavorites;
|
||||||
|
|
||||||
|
// Clippings Store related
|
||||||
|
-(int)jcListCount;
|
||||||
|
-(FlycutClipping*)clippingAtStackPosition;
|
||||||
|
-(NSArray *) previousDisplayStrings:(int)howMany containing:(NSString*)search;
|
||||||
|
-(NSArray *) previousIndexes:(int)howMany containing:(NSString*)search; // This method is in newest-first order.
|
||||||
|
-(void)setDisableStoreTo:(bool) value;
|
||||||
|
-(bool)storeDisabled;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
#endif /* FlycutOperator_h */
|
#endif /* FlycutOperator_h */
|
||||||
|
|
568
FlycutOperator.m
568
FlycutOperator.m
|
@ -2,8 +2,574 @@
|
||||||
// FlycutOperator.m
|
// FlycutOperator.m
|
||||||
// Flycut
|
// Flycut
|
||||||
//
|
//
|
||||||
// Created by Mark Jerde on 7/10/17.
|
// Flycut by Gennadiy Potapov and contributors. Based on Jumpcut by Steve Cook.
|
||||||
|
// Copyright 2011 General Arcade. All rights reserved.
|
||||||
//
|
//
|
||||||
|
// This code is open-source software subject to the MIT License; see the homepage
|
||||||
|
// at <https://github.com/TermiT/Flycut> for details.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// FlycutOperator owns and interacts with the FlycutStores, providing
|
||||||
|
// manipulation of the stores.
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "FlycutOperator.h"
|
||||||
|
|
||||||
|
@implementation FlycutOperator
|
||||||
|
|
||||||
|
- (id)init
|
||||||
|
{
|
||||||
|
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||||
|
[NSNumber numberWithInt:10],
|
||||||
|
@"displayNum",
|
||||||
|
[NSNumber numberWithInt:40],
|
||||||
|
@"displayLen",
|
||||||
|
[NSNumber numberWithInt:40],
|
||||||
|
@"rememberNum",
|
||||||
|
[NSNumber numberWithInt:40],
|
||||||
|
@"favoritesRememberNum",
|
||||||
|
[NSNumber numberWithInt:1],
|
||||||
|
@"savePreference",
|
||||||
|
[NSDictionary dictionary],
|
||||||
|
@"store",
|
||||||
|
[NSNumber numberWithBool:YES],
|
||||||
|
@"skipPasswordFields",
|
||||||
|
[NSNumber numberWithBool:YES],
|
||||||
|
@"skipPboardTypes",
|
||||||
|
@"PasswordPboardType",
|
||||||
|
@"skipPboardTypesList",
|
||||||
|
[NSNumber numberWithBool:NO],
|
||||||
|
@"skipPasswordLengths",
|
||||||
|
@"12, 20, 32",
|
||||||
|
@"skipPasswordLengthsList",
|
||||||
|
[NSNumber numberWithBool:NO],
|
||||||
|
@"revealPasteboardTypes",
|
||||||
|
[NSNumber numberWithBool:NO],
|
||||||
|
@"removeDuplicates",
|
||||||
|
[NSNumber numberWithBool:NO],
|
||||||
|
@"saveForgottenClippings",
|
||||||
|
[NSNumber numberWithBool:YES],
|
||||||
|
@"saveForgottenFavorites",
|
||||||
|
[NSNumber numberWithBool:NO],
|
||||||
|
@"pasteMovesToTop",
|
||||||
|
nil]];
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)awakeFromNib
|
||||||
|
{
|
||||||
|
// Initialize the FlycutStore
|
||||||
|
clippingStore = [[FlycutStore alloc] initRemembering:[[NSUserDefaults standardUserDefaults] integerForKey:@"rememberNum"]
|
||||||
|
displaying:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"]
|
||||||
|
withDisplayLength:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayLen"]];
|
||||||
|
favoritesStore = [[FlycutStore alloc] initRemembering:[[NSUserDefaults standardUserDefaults] integerForKey:@"favoritesRememberNum"]
|
||||||
|
displaying:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"]
|
||||||
|
withDisplayLength:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayLen"]];
|
||||||
|
stashedStore = NULL;
|
||||||
|
|
||||||
|
// If our preferences indicate that we are saving, load the dictionary from the saved plist
|
||||||
|
// and use it to get everything set up.
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) {
|
||||||
|
[self loadEngineFromPList];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stack position starts @ 0 by default
|
||||||
|
stackPosition = favoritesStackPosition = stashedStackPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) setRememberNum:(int) newRemember
|
||||||
|
{
|
||||||
|
[clippingStore setRememberNum:newRemember];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)toggleToFromFavoritesStore
|
||||||
|
{
|
||||||
|
if (NULL != stashedStore)
|
||||||
|
[self restoreStashedStore];
|
||||||
|
else
|
||||||
|
[self switchToFavoritesStore];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)favoritesStoreIsSelected
|
||||||
|
{
|
||||||
|
return clippingStore == favoritesStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)switchToFavoritesStore
|
||||||
|
{
|
||||||
|
stashedStore = clippingStore;
|
||||||
|
clippingStore = favoritesStore;
|
||||||
|
stashedStackPosition = stackPosition;
|
||||||
|
stackPosition = favoritesStackPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)restoreStashedStore
|
||||||
|
{
|
||||||
|
if (NULL != stashedStore)
|
||||||
|
{
|
||||||
|
clippingStore = stashedStore;
|
||||||
|
stashedStore = NULL;
|
||||||
|
favoritesStackPosition = stackPosition;
|
||||||
|
stackPosition = stashedStackPosition;
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)getPasteFromStackPosition
|
||||||
|
{
|
||||||
|
if ( [clippingStore jcListCount] > stackPosition ) {
|
||||||
|
return [self getPasteFromIndex: stackPosition];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)saveFromStack
|
||||||
|
{
|
||||||
|
return [self saveFromStackWithPrefix:@""];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)saveFromStackWithPrefix:(NSString*) prefix
|
||||||
|
{
|
||||||
|
if ( [clippingStore jcListCount] > stackPosition ) {
|
||||||
|
// Get text from clipping store.
|
||||||
|
NSString *pbFullText = [self clippingStringWithCount:stackPosition];
|
||||||
|
pbFullText = [pbFullText stringByReplacingOccurrencesOfString:@"\r" withString:@"\r\n"];
|
||||||
|
|
||||||
|
// Get the Desktop directory:
|
||||||
|
NSArray *paths = NSSearchPathForDirectoriesInDomains
|
||||||
|
(NSDesktopDirectory, NSUserDomainMask, YES);
|
||||||
|
NSString *desktopDirectory = [paths objectAtIndex:0];
|
||||||
|
|
||||||
|
// Get the timestamp string:
|
||||||
|
NSDate *currentDate = [NSDate date];
|
||||||
|
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||||
|
[dateFormatter setDateFormat:@"YYYY-MM-dd 'at' HH.mm.ss"];
|
||||||
|
NSString *dateString = [dateFormatter stringFromDate:currentDate];
|
||||||
|
|
||||||
|
// Make a file name to write the data to using the Desktop directory:
|
||||||
|
NSString *fileName = [NSString stringWithFormat:@"%@/%@%@Clipping %@.txt",
|
||||||
|
desktopDirectory, prefix, clippingStore == favoritesStore ? @"Favorite " : @"", dateString];
|
||||||
|
|
||||||
|
// Save content to the file
|
||||||
|
[pbFullText writeToFile:fileName
|
||||||
|
atomically:NO
|
||||||
|
encoding:NSNonLossyASCIIStringEncoding
|
||||||
|
error:nil];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (bool)saveFromStackToFavorites
|
||||||
|
{
|
||||||
|
if ( clippingStore != favoritesStore && [clippingStore jcListCount] > stackPosition ) {
|
||||||
|
if ( [favoritesStore rememberNum] == [favoritesStore jcListCount]
|
||||||
|
&& [[[NSUserDefaults standardUserDefaults] valueForKey:@"saveForgottenFavorites"] boolValue] )
|
||||||
|
{
|
||||||
|
// favoritesStore is full, so save the last entry before it gets lost.
|
||||||
|
[self switchToFavoritesStore];
|
||||||
|
|
||||||
|
// Set to last item, save, and restore position.
|
||||||
|
stackPosition = [favoritesStore rememberNum]-1;
|
||||||
|
[self saveFromStackWithPrefix:@"Autosave "];
|
||||||
|
stackPosition = favoritesStackPosition;
|
||||||
|
|
||||||
|
// Restore prior state.
|
||||||
|
[self restoreStashedStore];
|
||||||
|
}
|
||||||
|
// Get text from clipping store.
|
||||||
|
[favoritesStore addClipping:[clippingStore clippingAtPosition:stackPosition] ];
|
||||||
|
[clippingStore clearItem:stackPosition];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*)getPasteFromIndex:(int) position {
|
||||||
|
NSString *clipping = [self getClipFromCount:position];
|
||||||
|
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"pasteMovesToTop"] ) {
|
||||||
|
[clippingStore clippingMoveToTop:position];
|
||||||
|
stackPosition = 0;
|
||||||
|
}
|
||||||
|
return clipping;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString*)getClipFromCount:(int)indexInt
|
||||||
|
{
|
||||||
|
NSString *pbFullText;
|
||||||
|
NSArray *pbTypes;
|
||||||
|
if ( (indexInt + 1) > [clippingStore jcListCount] ) {
|
||||||
|
// We're asking for a clipping that isn't there yet
|
||||||
|
// This only tends to happen immediately on startup when not saving, as the entire list is empty.
|
||||||
|
DLog(@"Out of bounds request to jcList ignored.");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return [self clippingStringWithCount:indexInt];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL)shouldSkip:(NSString *)contents ofType:(NSString *)type
|
||||||
|
{
|
||||||
|
// Check to see if we are skipping passwords based on length and characters.
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"skipPasswordFields"] )
|
||||||
|
{
|
||||||
|
// Check to see if they want a little help figuring out what types to enter.
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"revealPasteboardTypes"] )
|
||||||
|
[clippingStore addClipping:type ofType:type fromAppLocalizedName:@"Flycut" fromAppBundleURL:nil atTimestamp:0];
|
||||||
|
|
||||||
|
__block bool skipClipping = NO;
|
||||||
|
|
||||||
|
// Check the array of types to skip.
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"skipPboardTypes"] )
|
||||||
|
{
|
||||||
|
NSArray *typesArray = [[[[NSUserDefaults standardUserDefaults] stringForKey:@"skipPboardTypesList"] stringByReplacingOccurrencesOfString:@" " withString:@""] componentsSeparatedByString: @","];
|
||||||
|
[typesArray enumerateObjectsUsingBlock:^(id typeString, NSUInteger idx, BOOL *stop)
|
||||||
|
{
|
||||||
|
if ( [type isEqualToString:typeString] )
|
||||||
|
{
|
||||||
|
skipClipping = YES;
|
||||||
|
*stop = YES;
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
if (skipClipping)
|
||||||
|
return YES;
|
||||||
|
|
||||||
|
// Check the array of lengths to skip for suspicious strings.
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"skipPasswordLengths"] )
|
||||||
|
{
|
||||||
|
int contentsLength = [contents length];
|
||||||
|
NSArray *lengthsArray = [[[[NSUserDefaults standardUserDefaults] stringForKey:@"skipPasswordLengthsList"] stringByReplacingOccurrencesOfString:@" " withString:@""] componentsSeparatedByString: @","];
|
||||||
|
[lengthsArray enumerateObjectsUsingBlock:^(id lengthString, NSUInteger idx, BOOL *stop)
|
||||||
|
{
|
||||||
|
if ( [lengthString integerValue] == contentsLength )
|
||||||
|
{
|
||||||
|
NSRange uppercaseLetter = [contents rangeOfCharacterFromSet: [NSCharacterSet uppercaseLetterCharacterSet]];
|
||||||
|
NSRange lowercaseLetter = [contents rangeOfCharacterFromSet: [NSCharacterSet lowercaseLetterCharacterSet]];
|
||||||
|
NSRange decimalDigit = [contents rangeOfCharacterFromSet: [NSCharacterSet decimalDigitCharacterSet]];
|
||||||
|
NSRange punctuation = [contents rangeOfCharacterFromSet: [NSCharacterSet punctuationCharacterSet]];
|
||||||
|
NSRange symbol = [contents rangeOfCharacterFromSet: [NSCharacterSet symbolCharacterSet]];
|
||||||
|
NSRange control = [contents rangeOfCharacterFromSet: [NSCharacterSet controlCharacterSet]];
|
||||||
|
NSRange illegal = [contents rangeOfCharacterFromSet: [NSCharacterSet illegalCharacterSet]];
|
||||||
|
NSRange whitespaceAndNewline = [contents rangeOfCharacterFromSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||||
|
if ( NSNotFound == control.location
|
||||||
|
&& NSNotFound == illegal.location
|
||||||
|
&& NSNotFound == whitespaceAndNewline.location
|
||||||
|
&& NSNotFound != uppercaseLetter.location
|
||||||
|
&& NSNotFound != lowercaseLetter.location
|
||||||
|
&& NSNotFound != decimalDigit.location
|
||||||
|
&& ( NSNotFound != punctuation.location
|
||||||
|
|| NSNotFound != symbol.location ) )
|
||||||
|
{
|
||||||
|
skipClipping = YES;
|
||||||
|
*stop = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (skipClipping)
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)setDisableStoreTo:(bool) value
|
||||||
|
{
|
||||||
|
disableStore = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)storeDisabled
|
||||||
|
{
|
||||||
|
return disableStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)addClipping:(NSString*)contents ofType:(NSString*)type fromApp:(NSString *)appName withAppBundleURL:(NSString *)bundleURL target:(id)selectorTarget clippingAddedSelector:(SEL)clippingAddedSelector
|
||||||
|
{
|
||||||
|
if ( [clippingStore jcListCount] == 0 || ! [contents isEqualToString:[clippingStore clippingContentsAtPosition:0]]) {
|
||||||
|
if ( [clippingStore rememberNum] == [clippingStore jcListCount]
|
||||||
|
&& [[[NSUserDefaults standardUserDefaults] valueForKey:@"saveForgottenClippings"] boolValue] )
|
||||||
|
{
|
||||||
|
// clippingStore is full, so save the last entry before it gets lost.
|
||||||
|
// Set to last item, save, and restore position.
|
||||||
|
int savePosition = stackPosition;
|
||||||
|
stackPosition = [clippingStore rememberNum]-1;
|
||||||
|
[self saveFromStackWithPrefix:@"Autosave "];
|
||||||
|
stackPosition = savePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
[clippingStore addClipping:contents
|
||||||
|
ofType:type
|
||||||
|
fromAppLocalizedName:appName
|
||||||
|
fromAppBundleURL:bundleURL
|
||||||
|
atTimestamp:[[NSDate date] timeIntervalSince1970]];
|
||||||
|
// The below tracks our position down down down... Maybe as an option?
|
||||||
|
// if ( [clippingStore jcListCount] > 1 ) stackPosition++;
|
||||||
|
stackPosition = 0;
|
||||||
|
[selectorTarget performSelector:clippingAddedSelector];
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 2 )
|
||||||
|
[self saveEngine];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int)jcListCount
|
||||||
|
{
|
||||||
|
return [clippingStore jcListCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(int)stackPosition
|
||||||
|
{
|
||||||
|
return stackPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)setStackPositionToFirstItem
|
||||||
|
{
|
||||||
|
if ( [clippingStore jcListCount] > 0 ) {
|
||||||
|
stackPosition = 0;
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)setStackPositionToLastItem
|
||||||
|
{
|
||||||
|
if ( [clippingStore jcListCount] > 0 ) {
|
||||||
|
stackPosition = [clippingStore jcListCount] - 1;
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)setStackPositionToTenMoreRecent
|
||||||
|
{
|
||||||
|
if ( [clippingStore jcListCount] > 0 ) {
|
||||||
|
stackPosition = stackPosition - 10; if ( stackPosition < 0 ) stackPosition = 0;
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)setStackPositionToTenLessRecent
|
||||||
|
{
|
||||||
|
if ( [clippingStore jcListCount] > 0 ) {
|
||||||
|
stackPosition = stackPosition + 10; if ( stackPosition >= [clippingStore jcListCount] ) stackPosition = [clippingStore jcListCount] - 1;
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)clearItemAtStackPosition
|
||||||
|
{
|
||||||
|
if ([clippingStore jcListCount] == 0)
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
[clippingStore clearItem:stackPosition];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)setStackPositionTo:(int) newStackPosition
|
||||||
|
{
|
||||||
|
if ( [clippingStore jcListCount] >= newStackPosition ) {
|
||||||
|
stackPosition = newStackPosition;
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Would probably be good to just prevent this scenario where it originates and
|
||||||
|
// delete this check.
|
||||||
|
-(void)adjustStackPositionIfOutOfBounds
|
||||||
|
{
|
||||||
|
if (stackPosition >= [clippingStore jcListCount] && stackPosition != 0) { // deleted last item
|
||||||
|
stackPosition = [clippingStore jcListCount] - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)stackPositionIsInBounds
|
||||||
|
{
|
||||||
|
return ( [clippingStore jcListCount] > 0 && [clippingStore jcListCount] > stackPosition );
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)clearList
|
||||||
|
{
|
||||||
|
[clippingStore clearList];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)mergeList
|
||||||
|
{
|
||||||
|
[clippingStore mergeList];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(BOOL) isValidClippingNumber:(NSNumber *)number {
|
||||||
|
return ( ([number intValue] + 1) <= [clippingStore jcListCount] );
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSString *) clippingStringWithCount:(int)count {
|
||||||
|
if ( [self isValidClippingNumber:[NSNumber numberWithInt:count]] ) {
|
||||||
|
return [clippingStore clippingContentsAtPosition:count];
|
||||||
|
} else { // It fails -- we shouldn't be passed this, but...
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool) loadEngineFromPList
|
||||||
|
{
|
||||||
|
NSDictionary *loadDict = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"store"] copy];
|
||||||
|
NSArray *savedJCList;
|
||||||
|
NSRange loadRange;
|
||||||
|
|
||||||
|
int rangeCap;
|
||||||
|
|
||||||
|
if ( loadDict != nil ) {
|
||||||
|
|
||||||
|
savedJCList = [loadDict objectForKey:@"jcList"];
|
||||||
|
|
||||||
|
if ( [savedJCList isKindOfClass:[NSArray class]] ) {
|
||||||
|
int rememberNumPref = [[NSUserDefaults standardUserDefaults]
|
||||||
|
integerForKey:@"rememberNum"];
|
||||||
|
// There's probably a nicer way to prevent the range from going out of bounds, but this works.
|
||||||
|
rangeCap = [savedJCList count] < rememberNumPref ? [savedJCList count] : rememberNumPref;
|
||||||
|
loadRange = NSMakeRange(0, rangeCap);
|
||||||
|
NSArray *toBeRestoredClips = [[[savedJCList subarrayWithRange:loadRange] reverseObjectEnumerator] allObjects];
|
||||||
|
for( NSDictionary *aSavedClipping in toBeRestoredClips)
|
||||||
|
[clippingStore addClipping:[aSavedClipping objectForKey:@"Contents"]
|
||||||
|
ofType:[aSavedClipping objectForKey:@"Type"]
|
||||||
|
fromAppLocalizedName:[aSavedClipping objectForKey:@"AppLocalizedName"]
|
||||||
|
fromAppBundleURL:[aSavedClipping objectForKey:@"AppBundleURL"]
|
||||||
|
atTimestamp:[[aSavedClipping objectForKey:@"Timestamp"] integerValue]];
|
||||||
|
|
||||||
|
// Now for the favorites, same thing.
|
||||||
|
savedJCList =[loadDict objectForKey:@"favoritesList"];
|
||||||
|
if ( [savedJCList isKindOfClass:[NSArray class]] ) {
|
||||||
|
rememberNumPref = [[NSUserDefaults standardUserDefaults]
|
||||||
|
integerForKey:@"favoritesRememberNum"];
|
||||||
|
rangeCap = [savedJCList count] < rememberNumPref ? [savedJCList count] : rememberNumPref;
|
||||||
|
loadRange = NSMakeRange(0, rangeCap);
|
||||||
|
toBeRestoredClips = [[[savedJCList subarrayWithRange:loadRange] reverseObjectEnumerator] allObjects];
|
||||||
|
for( NSDictionary *aSavedClipping in toBeRestoredClips)
|
||||||
|
[favoritesStore addClipping:[aSavedClipping objectForKey:@"Contents"]
|
||||||
|
ofType:[aSavedClipping objectForKey:@"Type"]
|
||||||
|
fromAppLocalizedName:[aSavedClipping objectForKey:@"AppLocalizedName"]
|
||||||
|
fromAppBundleURL:[aSavedClipping objectForKey:@"AppBundleURL"]
|
||||||
|
atTimestamp:[[aSavedClipping objectForKey:@"Timestamp"] integerValue]];
|
||||||
|
}
|
||||||
|
} else DLog(@"Not array");
|
||||||
|
[loadDict release];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)setStackPositionToOneLessRecent
|
||||||
|
{
|
||||||
|
stackPosition++;
|
||||||
|
if ( [clippingStore jcListCount] > stackPosition ) {
|
||||||
|
return YES;
|
||||||
|
} else {
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"wraparoundBezel"] ) {
|
||||||
|
stackPosition = 0;
|
||||||
|
return YES;
|
||||||
|
} else {
|
||||||
|
stackPosition--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(bool)setStackPositionToOneMoreRecent
|
||||||
|
{
|
||||||
|
stackPosition--;
|
||||||
|
if ( stackPosition < 0 ) {
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] boolForKey:@"wraparoundBezel"] ) {
|
||||||
|
stackPosition = [clippingStore jcListCount] - 1;
|
||||||
|
return YES;
|
||||||
|
} else {
|
||||||
|
stackPosition = 0;
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( [clippingStore jcListCount] > stackPosition ) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(FlycutClipping*)clippingAtStackPosition
|
||||||
|
{
|
||||||
|
return [clippingStore clippingAtPosition:stackPosition];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)saveStore:(FlycutStore *)store toKey:(NSString *)key onDict:(NSMutableDictionary *)saveDict {
|
||||||
|
NSMutableArray *jcListArray = [NSMutableArray array];
|
||||||
|
for ( int i = 0 ; i < [store jcListCount] ; i++ )
|
||||||
|
{
|
||||||
|
FlycutClipping *clipping = [store clippingAtPosition:i];
|
||||||
|
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||||
|
[clipping contents], @"Contents",
|
||||||
|
[clipping type], @"Type",
|
||||||
|
[NSNumber numberWithInt:i], @"Position",nil];
|
||||||
|
|
||||||
|
NSString *val = [clipping appLocalizedName];
|
||||||
|
if ( nil != val )
|
||||||
|
[dict setObject:val forKey:@"AppLocalizedName"];
|
||||||
|
|
||||||
|
val = [clipping appBundleURL];
|
||||||
|
if ( nil != val )
|
||||||
|
[dict setObject:val forKey:@"AppBundleURL"];
|
||||||
|
|
||||||
|
int timestamp = [clipping timestamp];
|
||||||
|
if ( timestamp > 0 )
|
||||||
|
[dict setObject:[NSNumber numberWithInt:timestamp] forKey:@"Timestamp"];
|
||||||
|
|
||||||
|
[jcListArray addObject:dict];
|
||||||
|
}
|
||||||
|
[saveDict setObject:jcListArray forKey:key];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void) saveEngine {
|
||||||
|
NSMutableDictionary *saveDict;
|
||||||
|
saveDict = [NSMutableDictionary dictionaryWithCapacity:3];
|
||||||
|
[saveDict setObject:@"0.7" forKey:@"version"];
|
||||||
|
[saveDict setObject:[NSNumber numberWithInt:[[NSUserDefaults standardUserDefaults] integerForKey:@"rememberNum"]]
|
||||||
|
forKey:@"rememberNum"];
|
||||||
|
[saveDict setObject:[NSNumber numberWithInt:[[NSUserDefaults standardUserDefaults] integerForKey:@"favoritesRememberNum"]]
|
||||||
|
forKey:@"favoritesRememberNum"];
|
||||||
|
[saveDict setObject:[NSNumber numberWithInt:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayLen"]]
|
||||||
|
forKey:@"displayLen"];
|
||||||
|
[saveDict setObject:[NSNumber numberWithInt:[[NSUserDefaults standardUserDefaults] integerForKey:@"displayNum"]]
|
||||||
|
forKey:@"displayNum"];
|
||||||
|
|
||||||
|
[self saveStore:clippingStore toKey:@"jcList" onDict:saveDict];
|
||||||
|
[self saveStore:favoritesStore toKey:@"favoritesList" onDict:saveDict];
|
||||||
|
|
||||||
|
[[NSUserDefaults standardUserDefaults] setObject:saveDict forKey:@"store"];
|
||||||
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applicationWillTerminate {
|
||||||
|
if ( [[NSUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) {
|
||||||
|
DLog(@"Saving on exit");
|
||||||
|
[self saveEngine];
|
||||||
|
} else {
|
||||||
|
// Remove clips from store
|
||||||
|
[[NSUserDefaults standardUserDefaults] setValue:[NSDictionary dictionary] forKey:@"store"];
|
||||||
|
DLog(@"Saving preferences on exit");
|
||||||
|
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSArray *) previousIndexes:(int)howMany containing:(NSString*)search // This method is in newest-first order.
|
||||||
|
{
|
||||||
|
return [clippingStore previousIndexes:howMany containing:search];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSArray *) previousDisplayStrings:(int)howMany containing:(NSString*)search
|
||||||
|
{
|
||||||
|
return [clippingStore previousDisplayStrings:howMany containing:search];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
Loading…
Add table
Reference in a new issue