diff --git a/AppController.h b/AppController.h index b9be1ed..4dd8a4b 100755 --- a/AppController.h +++ b/AppController.h @@ -13,6 +13,7 @@ #import "SRKeyCodeTransformer.h" #import "JumpcutStore.h" #import "SGHotKey.h" +#import "DBSyncPromptDelegate.h" @class SGHotKey; @@ -51,8 +52,13 @@ NSDictionary *standardPreferences; int jcDisplayNum; BOOL issuedRememberResizeWarning; + BOOL dropboxSync; + + IBOutlet NSButtonCell * dropboxCheckbox; } +//@property(retain, nonatomic) IBOutlet NSButtonCell * dropboxCheckbox; + // Basic functionality -(void) pollPB:(NSTimer *)timer; -(BOOL) addClipToPasteboardFromCount:(int)indexInt; @@ -86,6 +92,9 @@ -(IBAction) processMenuClippingSelection:(id)sender; -(IBAction) activateAndOrderFrontStandardAboutPanel:(id)sender; +-(BOOL) dropboxSync; +-(void)setDropboxSync:(BOOL)enable; + // Preference related -(IBAction) showPreferencePanel:(id)sender; -(IBAction) setRememberNumPref:(id)sender; @@ -98,6 +107,4 @@ -(IBAction) toggleMainHotKey:(id)sender; -(void) setHotKeyPreferenceForRecorder:(SRRecorderControl *)aRecorder; -- (IBAction)enableDropboxButtonClicked:(NSButton*)sender; - @end diff --git a/AppController.m b/AppController.m index fef655f..4b7c532 100755 --- a/AppController.m +++ b/AppController.m @@ -14,6 +14,7 @@ #import "SRRecorderCell.h" #import "UKLoginItemRegistry.h" #import "NSWindow+TrueCenter.h" +#import "NSWindow+ULIZoomEffect.h" #import "DBUserDefaults.h" #define _DISPLENGTH 40 @@ -49,7 +50,14 @@ [NSNumber numberWithFloat:320.0], @"bezelHeight", [NSDictionary dictionary], - @"store", nil]]; + @"store", + [NSNumber numberWithBool:YES], + @"skipPasswordFields", + [NSNumber numberWithBool:NO], + @"removeDuplicates", + [NSNumber numberWithBool:YES], + @"popUpAnimation", + nil]]; return [super init]; } @@ -121,6 +129,13 @@ // Stack position starts @ 0 by default stackPosition = 0; + [[NSNotificationCenter defaultCenter] addObserverForName:@"DBSyncPromptUserDidCancelNotification" + object:nil queue:nil usingBlock:^(NSNotification *notification) { + [self setDropboxSync:NO]; + + //[[DBUserDefaults standardUserDefaults] setDropboxSyncEnabled:NO]; + + }]; [NSApp activateIgnoringOtherApps: YES]; } @@ -283,8 +298,8 @@ pbCount = [[NSNumber numberWithInt:[jcPasteboard changeCount]] retain]; if ( type != nil ) { NSString *contents = [jcPasteboard stringForType:type]; - if ( contents == nil ) { -// NSLog(@"Contents: Empty"); + if ( contents == nil || ([jcPasteboard stringForType:@"PasswordPboardType"] && [[DBUserDefaults standardUserDefaults] boolForKey:@"skipPasswordFields"]) ) { + NSLog(@"Contents: Empty"); } else { if (( [clippingStore jcListCount] == 0 || ! [contents isEqualToString:[clippingStore clippingContentsAtPosition:0]]) && ! [pbCount isEqualTo:pbBlockCount] ) { @@ -294,37 +309,26 @@ // if ( [clippingStore jcListCount] > 1 ) stackPosition++; stackPosition = 0; [self updateMenu]; - if ( [[DBUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 2 ) { + if ( [[DBUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 2 ) [self saveEngine]; - } } } - } else { - // NSLog(@"Contents: Non-string"); - } + } } - } -- (void)processBezelKeyDown:(NSEvent *)theEvent -{ +- (void)processBezelKeyDown:(NSEvent *)theEvent { int newStackPosition; // AppControl should only be getting these directly from bezel via delegation - if ( [theEvent type] == NSKeyDown ) - { - if ( [theEvent keyCode] == [mainRecorder keyCombo].code ) - { - if ( [theEvent modifierFlags] & NSShiftKeyMask ) - { - [self stackUp]; - } else { - [self stackDown]; - } + if ([theEvent type] == NSKeyDown) { + if ([theEvent keyCode] == [mainRecorder keyCombo].code ) { + if ([theEvent modifierFlags] & NSShiftKeyMask) [self stackUp]; + else [self stackDown]; return; } unichar pressed = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]; NSUInteger modifiers = [theEvent modifierFlags]; - switch ( pressed ) { + switch (pressed) { case 0x1B: [self hideApp]; break; @@ -407,26 +411,30 @@ - (void) updateBezel { - if (stackPosition >= [clippingStore jcListCount] && stackPosition != 0) { // deleted last item - stackPosition = [clippingStore jcListCount] - 1; - } - if (stackPosition == 0 && [clippingStore jcListCount] == 0) { // empty - [bezel setText:@""]; - [bezel setCharString:@"Empty"]; - } - else { // normal - [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; - [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; - } + if (stackPosition >= [clippingStore jcListCount] && stackPosition != 0) { // deleted last item + stackPosition = [clippingStore jcListCount] - 1; + } + if (stackPosition == 0 && [clippingStore jcListCount] == 0) { // empty + [bezel setText:@""]; + [bezel setCharString:@"Empty"]; + } + else { // normal + [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; + [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; + } } - (void) showBezel { - [self updateBezel]; - if ([bezel respondsToSelector:@selector(setCollectionBehavior:)]) { + if ( [clippingStore jcListCount] > 0 && [clippingStore jcListCount] > stackPosition ) { + [bezel setCharString:[NSString stringWithFormat:@"%d of %d", stackPosition + 1, [clippingStore jcListCount]]]; + [bezel setText:[clippingStore clippingContentsAtPosition:stackPosition]]; + } + if ([bezel respondsToSelector:@selector(setCollectionBehavior:)]) [bezel setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]; - [bezel makeKeyAndOrderFront:nil]; - } + if ([[DBUserDefaults standardUserDefaults] boolForKey:@"popUpAnimation"]) + [bezel makeKeyAndOrderFrontWithPopEffect]; + else [bezel makeKeyAndOrderFront:self]; isBezelDisplayed = YES; } @@ -439,14 +447,14 @@ -(void)hideApp { - [self hideBezel]; + [self hideBezel]; isBezelPinned = NO; [NSApp hide:self]; } - (void) applicationWillResignActive:(NSApplication *)app; { // This should be hidden anyway, but just in case it's not. - [self hideBezel]; + [self hideBezel]; } @@ -644,8 +652,7 @@ } } --(void) saveEngine -{ +-(void) saveEngine { NSMutableDictionary *saveDict; NSMutableArray *jcListArray = [NSMutableArray array]; saveDict = [NSMutableDictionary dictionaryWithCapacity:3]; @@ -686,13 +693,15 @@ NSLog(@"code: %d, flags: %u", newKeyCombo.code, newKeyCombo.flags); } -- (IBAction)enableDropboxButtonClicked:(NSButton*)sender -{ +- (IBAction)toggleDropboxSync:(NSButtonCell*)sender { + DBUserDefaults * defaults = [DBUserDefaults standardUserDefaults]; // First, let's check to make sure Dropbox is available on this machine - if([DBUserDefaults isDropboxAvailable]) - [defaults promptDropboxUnavailable]; - else [[DBUserDefaults standardUserDefaults] setDropboxSyncEnabled:YES]; + if (sender.state == 1) { + if([DBUserDefaults isDropboxAvailable]) + [defaults promptDropboxUnavailable]; + else [[DBUserDefaults standardUserDefaults] setDropboxSyncEnabled:YES]; + } else [[DBUserDefaults standardUserDefaults] setDropboxSyncEnabled:NO]; } @@ -700,6 +709,9 @@ if ( [[DBUserDefaults standardUserDefaults] integerForKey:@"savePreference"] >= 1 ) { NSLog(@"Saving on exit"); [self saveEngine]; + } else { + // Remove clips from store + [[DBUserDefaults standardUserDefaults] setValue:[NSDictionary dictionary] forKey:@"store"]; } //Unregister our hot key (not required) [[SGHotKeyCenter sharedCenter] unregisterHotKey: mainHotKey]; @@ -716,10 +728,25 @@ object:nil]; } +-(BOOL) dropboxSync { + return [DBUserDefaults isDropboxSyncEnabled]; +} +-(void)setDropboxSync:(BOOL)enable { + DBUserDefaults * defaults = [DBUserDefaults standardUserDefaults]; + if (enable) { + if([DBUserDefaults isDropboxAvailable]) + [defaults promptDropboxUnavailable]; + else [[DBUserDefaults standardUserDefaults] setDropboxSyncEnabled:YES]; + } else { + [[DBUserDefaults standardUserDefaults] setDropboxSyncEnabled:NO]; + [dropboxCheckbox setState:NSOffState]; + } +} + - (void) dealloc { [bezel release]; [srTransformer release]; [super dealloc]; } -@end \ No newline at end of file +@end diff --git a/DBUserDefaults.framework/Versions/A/DBUserDefaults b/DBUserDefaults.framework/Versions/A/DBUserDefaults index 61b468e..074c9ff 100755 Binary files a/DBUserDefaults.framework/Versions/A/DBUserDefaults and b/DBUserDefaults.framework/Versions/A/DBUserDefaults differ diff --git a/DBUserDefaults.framework/Versions/A/Resources/DBSyncPrompt.nib b/DBUserDefaults.framework/Versions/A/Resources/DBSyncPrompt.nib index d53f640..5bbed4b 100644 Binary files a/DBUserDefaults.framework/Versions/A/Resources/DBSyncPrompt.nib and b/DBUserDefaults.framework/Versions/A/Resources/DBSyncPrompt.nib differ diff --git a/English.lproj/MainMenu.nib/designable.nib b/English.lproj/MainMenu.nib/designable.nib index 84576d3..0c9493f 100644 --- a/English.lproj/MainMenu.nib/designable.nib +++ b/English.lproj/MainMenu.nib/designable.nib @@ -141,7 +141,7 @@ 3 2 - {{542, 303}, {549, 471}} + {{542, 303}, {507, 471}} 1886912512 Preferences @@ -159,16 +159,16 @@ 256 - {549, 471} + {507, 471} - + YES net.sf.jumpcut.preferences.general.tiff - + 256 YES @@ -185,9 +185,8 @@ 256 - {{14, 198}, {97, 18}} + {{14, 275}, {97, 18}} - YES @@ -218,9 +217,8 @@ 256 - {{134, 98}, {152, 26}} + {{79, 133}, {189, 26}} - YES @@ -295,10 +293,9 @@ 256 - {{14, 137}, {204, 18}} + {{14, 214}, {204, 18}} - - + YES 67239424 @@ -316,18 +313,86 @@ 25 + + + 256 + {{14, 194}, {247, 18}} + + + YES + + 67239424 + 0 + Dropbox Sync (settings & clippings) + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 256 + {{14, 77}, {247, 18}} + + + YES + + 67239424 + 0 + Don't copy from password fields + + + 1211912703 + 2 + + + + + 200 + 25 + + + + + 256 + {{14, 57}, {247, 18}} + + + YES + + 67239424 + 0 + Remove duplicates + + + 1211912703 + 2 + + + + + 200 + 25 + + 256 - {{13, 105}, {64, 17}} + {{13, 161}, {76, 23}} - YES 67239424 272629760 - Q2xpcHBpbmdzCg + Q2xpcHBpbmdzOgo @@ -353,9 +418,8 @@ 256 - {{99, 104}, {33, 14}} + {{13, 139}, {33, 14}} - YES @@ -375,9 +439,8 @@ 256 - {{70, 76}, {62, 14}} + {{13, 113}, {62, 14}} - YES @@ -393,9 +456,8 @@ 256 - {{191, 76}, {95, 14}} + {{134, 113}, {95, 14}} - YES @@ -411,9 +473,8 @@ 256 - {{136, 71}, {25, 22}} + {{79, 108}, {25, 22}} - YES @@ -502,9 +563,8 @@ 256 - {{281, 71}, {24, 22}} + {{224, 108}, {24, 22}} - YES @@ -570,9 +630,8 @@ 256 - {{161, 69}, {19, 27}} + {{104, 106}, {19, 27}} - YES @@ -590,10 +649,9 @@ 256 - {{306, 69}, {19, 27}} + {{249, 106}, {19, 27}} - - + YES 917024 @@ -610,9 +668,8 @@ 256 - {{14, 178}, {162, 18}} + {{14, 255}, {162, 18}} - YES @@ -633,9 +690,8 @@ 256 - {{14, 158}, {162, 18}} + {{14, 235}, {162, 18}} - YES @@ -653,38 +709,14 @@ 25 - - - 268 - {{9, 35}, {174, 32}} - - - - YES - - 67239424 - 134217728 - Enable Dropbox Sync - - - -2038284033 - 129 - - - 200 - 25 - - - {555, 228} + {555, 305} - - {{-3, 257}, {555, 228}} + {{-3, 180}, {555, 305}} - {0, 0} @@ -705,9 +737,7 @@ NO - {549, 471} - - + {507, 471} General @@ -755,9 +785,9 @@ 256 - {{247, 48}, {281, 61}} + {{247, 48}, {255, 61}} - + YES 67239424 @@ -805,7 +835,7 @@ NO - {549, 471} + {507, 471} Hotkeys @@ -815,7 +845,7 @@ net.sf.jumpcut.preferences.appearance.tiff - + 256 YES @@ -832,8 +862,9 @@ 256 - {{119, 221}, {306, 25}} + {{119, 279}, {306, 25}} + YES @@ -861,8 +892,9 @@ 256 - {{121, 204}, {80, 13}} + {{121, 262}, {80, 13}} + YES @@ -878,8 +910,9 @@ 256 - {{381, 204}, {42, 13}} + {{381, 262}, {42, 13}} + YES @@ -895,8 +928,9 @@ 256 - {{119, 158}, {306, 25}} + {{119, 216}, {306, 25}} + YES @@ -920,8 +954,9 @@ 256 - {{121, 141}, {80, 13}} + {{121, 199}, {80, 13}} + YES @@ -937,8 +972,9 @@ 256 - {{381, 141}, {42, 13}} + {{381, 199}, {42, 13}} + YES @@ -954,9 +990,10 @@ 256 - {{118, 42}, {130, 26}} + {{118, 100}, {130, 26}} - + + YES -2076049856 @@ -1025,8 +1062,9 @@ 256 - {{16, 48}, {80, 13}} + {{16, 106}, {80, 13}} + YES @@ -1042,8 +1080,9 @@ 256 - {{16, 164}, {80, 17}} + {{16, 222}, {80, 17}} + YES @@ -1059,8 +1098,9 @@ 256 - {{119, 101}, {306, 25}} + {{119, 159}, {306, 25}} + YES @@ -1084,8 +1124,9 @@ 256 - {{121, 84}, {80, 13}} + {{121, 142}, {80, 13}} + YES @@ -1101,8 +1142,9 @@ 256 - {{381, 84}, {42, 13}} + {{381, 142}, {42, 13}} + YES @@ -1118,8 +1160,9 @@ 256 - {{16, 107}, {80, 17}} + {{16, 165}, {80, 17}} + YES @@ -1135,8 +1178,9 @@ 256 - {{16, 229}, {97, 13}} + {{16, 287}, {97, 13}} + YES @@ -1149,14 +1193,39 @@ + + + 256 + {{14, 68}, {189, 18}} + + + + YES + + 67239424 + 0 + Animate bezel appearance + + + 1211912703 + 2 + + + + 200 + 25 + + - {555, 258} + {555, 316} + - {{-3, 231}, {555, 258}} + {{-3, 173}, {555, 316}} + {0, 0} @@ -1177,7 +1246,9 @@ NO - {549, 471} + {507, 471} + + Appearance @@ -1214,7 +1285,7 @@ 2322 - {517, 308} + {398, 406} @@ -1294,7 +1365,7 @@ Y291cmFnZW1lbnQuA - 517 + 398 1 @@ -1345,12 +1416,12 @@ Y291cmFnZW1lbnQuA 6 - {517, 1e+07} - {517, 198} + {551, 1e+07} + {398, 198} - {{1, 1}, {517, 344}} + {{1, 1}, {474, 344}} @@ -1389,6 +1460,7 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA {{-100, -100}, {15, 170}} + YES _doScroller: 0.4805194805194804 @@ -1407,10 +1479,10 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA 0.94565218687057495 - {{16, 16}, {519, 346}} + {{16, 16}, {476, 346}} - 2 + 66 @@ -1444,7 +1516,7 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA NO - {549, 471} + {507, 471} Acknowledgements @@ -1452,18 +1524,18 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA - + 6 YES YES YES - + - {{7, 11}, {549, 471}} + {{7, 11}, {507, 471}} @@ -1860,12 +1932,76 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA 853 - - enableDropboxButtonClicked: - - + + value: self.dropboxSync + + + + + + value: self.dropboxSync + value + self.dropboxSync + 2 + - 856 + 895 + + + + dropboxCheckbox + + + + 896 + + + + value: values.skipPasswordFields + + + + + + value: values.skipPasswordFields + value + values.skipPasswordFields + 2 + + + 902 + + + + value: values.removeDuplicates + + + + + + value: values.removeDuplicates + value + values.removeDuplicates + 2 + + + 906 + + + + value: values.popUpAnimation + + + + + + value: values.popUpAnimation + value + values.popUpAnimation + 2 + + + 913 @@ -1910,36 +2046,6 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA Menu Extra Menu - - 207 - - - - - 208 - - - - - 209 - - - - - 210 - - - - - 211 - - - - - 212 - - - 213 @@ -2057,9 +2163,12 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA YES - + + + + @@ -2067,9 +2176,8 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA - - - + + @@ -2110,8 +2218,10 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA + + Box - Box 264 @@ -2256,20 +2366,6 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA - - 416 - - - YES - - - - - - 754 - - - 418 @@ -2646,18 +2742,104 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA - 854 - + 857 + YES - + - 855 - - + 858 + + + + + 416 + + + YES + + + + + + 754 + + + + + 212 + + + + + 211 + + + + + 210 + + + + + 208 + + + + + 207 + + + + + 209 + + + + + 897 + + + YES + + + + + + 898 + + + + + 903 + + + YES + + + + + + 904 + + + + + 910 + + + YES + + + + + + 911 + + @@ -2776,8 +2958,15 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA 791.IBPluginDependency 808.CustomClassName 808.IBPluginDependency - 854.IBPluginDependency - 855.IBPluginDependency + 857.IBPluginDependency + 858.IBPluginDependency + 897.IBPluginDependency + 898.IBPluginDependency + 903.IBPluginDependency + 904.IBPluginDependency + 910.IBAttributePlaceholdersKey + 910.IBPluginDependency + 911.IBPluginDependency YES @@ -2929,6 +3118,17 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + YES + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin @@ -2943,7 +3143,7 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA - 856 + 913 @@ -2957,7 +3157,6 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA YES activateAndOrderFrontStandardAboutPanel: clearClippingList: - enableDropboxButtonClicked: processMenuClippingSelection: setBezelAlpha: setBezelHeight: @@ -2973,7 +3172,6 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA YES id id - NSButton id id id @@ -2992,7 +3190,6 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA YES activateAndOrderFrontStandardAboutPanel: clearClippingList: - enableDropboxButtonClicked: processMenuClippingSelection: setBezelAlpha: setBezelHeight: @@ -3014,10 +3211,6 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA clearClippingList: id - - enableDropboxButtonClicked: - NSButton - processMenuClippingSelection: id @@ -3064,6 +3257,7 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA YES YES + dropboxCheckbox heightSlider jcMenu mainRecorder @@ -3072,6 +3266,7 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA YES + NSButtonCell NSSlider NSMenu SRRecorderControl @@ -3083,6 +3278,7 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA YES YES + dropboxCheckbox heightSlider jcMenu mainRecorder @@ -3091,6 +3287,10 @@ AAEAAQAAAT0AAwAAAAEAAgAAAVIAAwAAAAEAAQAAAVMAAwAAAAIAAQABAAAAAA YES + + dropboxCheckbox + NSButtonCell + heightSlider NSSlider diff --git a/English.lproj/MainMenu.nib/keyedobjects.nib b/English.lproj/MainMenu.nib/keyedobjects.nib index 61612dc..4cbe67a 100644 Binary files a/English.lproj/MainMenu.nib/keyedobjects.nib and b/English.lproj/MainMenu.nib/keyedobjects.nib differ diff --git a/Flycut.xcodeproj/project.pbxproj b/Flycut.xcodeproj/project.pbxproj index f838687..d4d6199 100755 --- a/Flycut.xcodeproj/project.pbxproj +++ b/Flycut.xcodeproj/project.pbxproj @@ -67,6 +67,8 @@ DB46BEF114346B2F0025EA0E /* DBUserDefaults.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = DB46BEEA143466ED0025EA0E /* DBUserDefaults.framework */; }; DB46BEF314346C660025EA0E /* DBSyncPromptDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = DB46BEF214346C660025EA0E /* DBSyncPromptDelegate.h */; }; DB46BEF514346C6D0025EA0E /* DBUserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = DB46BEF414346C6D0025EA0E /* DBUserDefaults.h */; }; + DBEB0C9C1442F2AE0080D24E /* NSWindow+ULIZoomEffect.h in Headers */ = {isa = PBXBuildFile; fileRef = DBEB0C9A1442F2AE0080D24E /* NSWindow+ULIZoomEffect.h */; }; + DBEB0C9D1442F2AE0080D24E /* NSWindow+ULIZoomEffect.m in Sources */ = {isa = PBXBuildFile; fileRef = DBEB0C9B1442F2AE0080D24E /* NSWindow+ULIZoomEffect.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -168,6 +170,8 @@ DB46BEEE14346A7C0025EA0E /* DBUserDefaultsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DBUserDefaultsController.m; sourceTree = ""; }; DB46BEF214346C660025EA0E /* DBSyncPromptDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DBSyncPromptDelegate.h; path = ../DBUserDefaults.framework/Headers/DBSyncPromptDelegate.h; sourceTree = ""; }; DB46BEF414346C6D0025EA0E /* DBUserDefaults.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DBUserDefaults.h; path = ../DBUserDefaults.framework/Headers/DBUserDefaults.h; sourceTree = ""; }; + DBEB0C9A1442F2AE0080D24E /* NSWindow+ULIZoomEffect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSWindow+ULIZoomEffect.h"; sourceTree = ""; }; + DBEB0C9B1442F2AE0080D24E /* NSWindow+ULIZoomEffect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSWindow+ULIZoomEffect.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -314,6 +318,8 @@ 7761C88F139BDEAF000FB3AB /* RoundRecTextField.m */, 773ABFAF13E9AA1A00AE3969 /* NSWindow+TrueCenter.h */, 773ABFB013E9AA1A00AE3969 /* NSWindow+TrueCenter.m */, + DBEB0C9A1442F2AE0080D24E /* NSWindow+ULIZoomEffect.h */, + DBEB0C9B1442F2AE0080D24E /* NSWindow+ULIZoomEffect.m */, ); path = UI; sourceTree = ""; @@ -398,6 +404,7 @@ 7761C8B2139BDF12000FB3AB /* SRValidator.h in Headers */, 773ABFB113E9AA1A00AE3969 /* NSWindow+TrueCenter.h in Headers */, DB46BEEF14346A7C0025EA0E /* DBUserDefaultsController.h in Headers */, + DBEB0C9C1442F2AE0080D24E /* NSWindow+ULIZoomEffect.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -503,6 +510,7 @@ 7761C8B3139BDF12000FB3AB /* SRValidator.m in Sources */, 773ABFB213E9AA1A00AE3969 /* NSWindow+TrueCenter.m in Sources */, DB46BEF014346A7C0025EA0E /* DBUserDefaultsController.m in Sources */, + DBEB0C9D1442F2AE0080D24E /* NSWindow+ULIZoomEffect.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/JumpcutEngine/JumpcutClipping.m b/JumpcutEngine/JumpcutClipping.m index faa81a7..1f8b6c9 100755 --- a/JumpcutEngine/JumpcutClipping.m +++ b/JumpcutEngine/JumpcutClipping.m @@ -121,18 +121,19 @@ -(void) resetDisplayString { - NSString *newDisplayString, *firstLineOfClipping; + NSString *newDisplayString, *firstLineOfClipping, *trimmedString; NSUInteger start, lineEnd, contentsEnd; NSRange startRange = NSMakeRange(0,0); NSRange contentsRange; // We're resetting the display string, so release the old one. [clipDisplayString release]; // We want to restrict the display string to the clipping contents through the first line break. - [clipContents getLineStart:&start end:&lineEnd contentsEnd:&contentsEnd forRange:startRange]; + trimmedString = [clipContents stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + [trimmedString getLineStart:&start end:&lineEnd contentsEnd:&contentsEnd forRange:startRange]; contentsRange = NSMakeRange(0, contentsEnd); - firstLineOfClipping = [clipContents substringWithRange:contentsRange]; + firstLineOfClipping = [trimmedString substringWithRange:contentsRange]; if ( [firstLineOfClipping length] > clipDisplayLength ) { - newDisplayString = [[NSString stringWithString:[firstLineOfClipping substringToIndex:clipDisplayLength]] stringByAppendingString:@"..."]; + newDisplayString = [[NSString stringWithString:[firstLineOfClipping substringToIndex:clipDisplayLength]] stringByAppendingString:@"…"]; } else { newDisplayString = [NSString stringWithString:firstLineOfClipping]; } @@ -180,6 +181,20 @@ return clipHasName; } +- (BOOL)isEqual:(id)other { + if (other == self) + return YES; + if (!other || ![other isKindOfClass:[self class]]) + return NO; + JumpcutClipping * otherClip = (JumpcutClipping *)other; + return ([self.type isEqualToString:otherClip.type] && + [self.displayString isEqualToString:otherClip.displayString] && + (self.displayLength == otherClip.displayLength) && + [self.contents isEqualToString:otherClip.contents]); +} + + + -(void) dealloc { [clipContents release]; diff --git a/JumpcutEngine/JumpcutStore.m b/JumpcutEngine/JumpcutStore.m index 33170a7..e756210 100755 --- a/JumpcutEngine/JumpcutStore.m +++ b/JumpcutEngine/JumpcutStore.m @@ -26,6 +26,7 @@ #import "JumpcutStore.h" #import "JumpcutClipping.h" +#import "DBUserDefaults.h" @implementation JumpcutStore @@ -56,7 +57,11 @@ newClipping = [[JumpcutClipping alloc] initWithContents:clipping withType:type withDisplayLength:[self displayLen]]; - // Push it onto our recent clippings stack + + if ([jcList containsObject:newClipping] && [[[DBUserDefaults standardUserDefaults] valueForKey:@"removeDuplicates"] boolValue]) { + [jcList removeObject:newClipping]; + } + // Push it onto our recent clippings stack [jcList insertObject:newClipping atIndex:0]; // Delete clippings older than jcRememberNum while ( [jcList count] > jcRememberNum ) { diff --git a/UI/NSWindow+ULIZoomEffect.h b/UI/NSWindow+ULIZoomEffect.h new file mode 100644 index 0000000..24cfa00 --- /dev/null +++ b/UI/NSWindow+ULIZoomEffect.h @@ -0,0 +1,50 @@ +// +// NSWindow+ULIZoomEffect.h +// Stacksmith +// +// Created by Uli Kusterer on 05.03.11. +// Copyright 2011 Uli Kusterer. All rights reserved. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// + +/* + This category implements a transition effect where a small thumbnail of the + window flies from the given rectangle to where the window is then shown, + like when opening a folder window in Finder, plus a reverse variant for + ordering out a window. + + It also implements another effect where the window just "pops", i.e. seems to + grow larger for a moment, like the highlight when you use the "Find" command. + */ + +#import + + +@interface NSWindow (ULIZoomEffect) + +-(void) makeKeyAndOrderFrontWithPopEffect; // Grab user's attention. + +-(void) makeKeyAndOrderFrontWithZoomEffectFromRect: (NSRect)globalStartPoint; // Open window related to that rectangle. +-(void) orderFrontWithZoomEffectFromRect: (NSRect)globalStartPoint; + +-(void) orderOutWithZoomEffectToRect: (NSRect)globalEndPoint; // Reverse of -orderFrontWithZoomEffectFromRect: + +@end \ No newline at end of file diff --git a/UI/NSWindow+ULIZoomEffect.m b/UI/NSWindow+ULIZoomEffect.m new file mode 100644 index 0000000..a0ccad7 --- /dev/null +++ b/UI/NSWindow+ULIZoomEffect.m @@ -0,0 +1,356 @@ +// +// NSWindow+ULIZoomEffect.m +// Stacksmith +// +// Created by Uli Kusterer on 05.03.11. +// Copyright 2011 Uli Kusterer. All rights reserved. +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// + +// ----------------------------------------------------------------------------- +// Headers: +// ----------------------------------------------------------------------------- + +#import "NSWindow+ULIZoomEffect.h" + + +// On 10.6 and lower, these 10.7 methods/symbols aren't declared, so we declare +// them here and call them conditionally when available: +// Otherwise 10.7's built-in animations get in the way of ours, which are cooler +// because they can come from a certain rectangle and thus convey information. + +#ifndef MAC_OS_X_VERSION_10_7 +#define MAC_OS_X_VERSION_10_7 1070 +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 + +enum { + NSWindowAnimationBehaviorDefault = 0, + NSWindowAnimationBehaviorNone = 2, + NSWindowAnimationBehaviorDocumentWindow = 3, + NSWindowAnimationBehaviorUtilityWindow = 4, + NSWindowAnimationBehaviorAlertPanel = 5 +}; + +typedef NSInteger NSWindowAnimationBehavior; + +@interface NSWindow (ULITenSevenAnimationBehaviour) + +-(void) setAnimationBehavior: (NSWindowAnimationBehavior)animBehaviour; +-(NSWindowAnimationBehavior) animationBehavior; + +@end + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_6 + + +// ----------------------------------------------------------------------------- +// ULIQuicklyAnimatingWindow: +// ----------------------------------------------------------------------------- + +// Window we use for our animations on which we can adjust the animation duration easily: + +@interface ULIQuicklyAnimatingWindow : NSWindow +{ + CGFloat mAnimationResizeTime; +} + +@property (assign) CGFloat animationResizeTime; + +- (NSTimeInterval)animationResizeTime:(NSRect)newFrame; + +@end + + +@implementation ULIQuicklyAnimatingWindow + +@synthesize animationResizeTime = mAnimationResizeTime; + +-(id) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag screen:(NSScreen *)screen +{ + if(( self = [super initWithContentRect:contentRect styleMask:aStyle backing:bufferingType defer:flag screen: screen] )) + { + mAnimationResizeTime = 0.2; + } + + return self; +} + + +-(id) initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag +{ + if(( self = [super initWithContentRect:contentRect styleMask:aStyle backing:bufferingType defer:flag] )) + { + mAnimationResizeTime = 0.2; + } + + return self; +} + + +- (NSTimeInterval)animationResizeTime:(NSRect)newFrame +{ +#if 0 && DEBUG + // Only turn this on temporarily for debugging. Otherwise it'll trigger for + // menu items that include the shift key, which is *not* what you want. + return ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) ? (mAnimationResizeTime * 10.0) : mAnimationResizeTime; +#else + return mAnimationResizeTime; +#endif +} + +@end + + +// ----------------------------------------------------------------------------- +// NSWindow (ULIZoomEffect) +// ----------------------------------------------------------------------------- + +@implementation NSWindow (ULIZoomEffect) + +// Calculate a sensible default start rect for the animation, depending on what +// screen it is. We don't want the zoom to come from another screen by accident. + +-(NSRect) uli_startRectForScreen: (NSScreen*)theScreen +{ + NSRect screenBox = NSZeroRect; + NSScreen * menuBarScreen = [[NSScreen screens] objectAtIndex: 0]; + if( theScreen == nil || menuBarScreen == theScreen ) + { + // Use menu bar screen: + screenBox = [menuBarScreen frame]; + + // Take a rect in the upper left, which should be the menu bar: + // (Like Finder in ye olde days) + screenBox.origin.y += screenBox.size.height -16; + screenBox.size.height = 16; + screenBox.size.width = 16; + } + else + { + // On all other screens, pick a box in the center: + screenBox = [theScreen frame]; + screenBox.origin.y += truncf(screenBox.size.height /2) -8; + screenBox.origin.x += truncf(screenBox.size.width /2) -8; + screenBox.size.height = 16; + screenBox.size.width = 16; + } + + return screenBox; +} + + +// Create a "screen shot" of the given window which we use for our fake window +// that we can animate. + +-(NSImage*) uli_imageWithSnapshotForceActive: (BOOL)doForceActive +{ + NSDisableScreenUpdates(); + BOOL wasVisible = [self isVisible]; + + if( doForceActive ) + [self makeKeyAndOrderFront: nil]; + else + [self orderFront: nil]; + + // snag the image + CGImageRef windowImage = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, [self windowNumber], kCGWindowImageBoundsIgnoreFraming); + + if( !wasVisible ) + [self orderOut: nil]; + NSEnableScreenUpdates(); + + // little bit of error checking + if(CGImageGetWidth(windowImage) <= 1) + { + CGImageRelease(windowImage); + return nil; + } + + // Create a bitmap rep from the window and convert to NSImage... + NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage: windowImage]; + NSImage *image = [[NSImage alloc] initWithSize: NSMakeSize(CGImageGetWidth(windowImage),CGImageGetHeight(windowImage))]; + [image addRepresentation: bitmapRep]; + [bitmapRep release]; + CGImageRelease(windowImage); + + return [image autorelease]; +} + + +// Create a borderless window that shows and contains the given image: + +-(NSWindow*) uli_animationWindowForZoomEffectWithImage: (NSImage*)snapshotImage +{ + NSRect myFrame = [self frame]; + myFrame.size = [snapshotImage size]; + NSWindow * animationWindow = [[ULIQuicklyAnimatingWindow alloc] initWithContentRect: myFrame styleMask: NSBorderlessWindowMask backing: NSBackingStoreBuffered defer: NO]; + [animationWindow setOpaque: NO]; + + if( [animationWindow respondsToSelector: @selector(setAnimationBehavior:)] ) + [animationWindow setAnimationBehavior: NSWindowAnimationBehaviorNone]; + + NSImageView * imageView = [[NSImageView alloc] initWithFrame: NSMakeRect(0,0,myFrame.size.width,myFrame.size.height)]; + [imageView setImageScaling: NSImageScaleAxesIndependently]; + [imageView setImageFrameStyle: NSImageFrameNone]; + [imageView setImageAlignment: NSImageAlignCenter]; + [imageView setImage: snapshotImage]; + [imageView setAutoresizingMask: NSViewMinXMargin | NSViewMaxXMargin | NSViewMinYMargin | NSViewMaxYMargin | NSViewWidthSizable | NSViewHeightSizable]; + [[animationWindow contentView] addSubview: imageView]; + + [imageView release]; + + [animationWindow setHasShadow: YES]; + [animationWindow display]; + + return animationWindow; +} + + +// Effect like the "Find" highlight does, to grab user's attention (e.g when +// bringing a window to front that was already visible but might have been +// covered by other windows. + +-(void) makeKeyAndOrderFrontWithPopEffect +{ + BOOL haveAnimBehaviour = [NSWindow instancesRespondToSelector: @selector(animationBehavior)]; + NSWindowAnimationBehavior oldAnimationBehaviour = haveAnimBehaviour ? [self animationBehavior] : 0; + if( haveAnimBehaviour ) + [self setAnimationBehavior: NSWindowAnimationBehaviorNone]; // Prevent system animations from interfering. + + NSImage * snapshotImage = [self uli_imageWithSnapshotForceActive: YES]; + NSRect myFrame = [self frame]; + NSRect poppedFrame = NSInsetRect(myFrame, -20, -20); + myFrame.size = snapshotImage.size; + NSWindow * animationWindow = [self uli_animationWindowForZoomEffectWithImage: snapshotImage]; + [animationWindow setAnimationResizeTime: 0.025]; + [animationWindow setFrame: myFrame display: YES]; + [animationWindow orderFront: nil]; + [animationWindow setFrame: poppedFrame display: YES animate: YES]; + [animationWindow setFrame: myFrame display: YES animate: YES]; + + NSDisableScreenUpdates(); + [animationWindow close]; + + [self makeKeyAndOrderFront: nil]; + NSEnableScreenUpdates(); + + if( haveAnimBehaviour ) + [self setAnimationBehavior: oldAnimationBehaviour]; +} + + +// Zoom the window out from a given rect, to indicate what it belongs to: +// If the rect is tiny, we'll use a default starting rectangle. + +-(void) makeKeyAndOrderFrontWithZoomEffectFromRect: (NSRect)globalStartPoint +{ + if( globalStartPoint.size.width < 1 || globalStartPoint.size.height < 1 ) + globalStartPoint = [self uli_startRectForScreen: [self screen]]; + + BOOL haveAnimBehaviour = [NSWindow instancesRespondToSelector: @selector(animationBehavior)]; + NSWindowAnimationBehavior oldAnimationBehaviour = haveAnimBehaviour ? [self animationBehavior] : 0; + if( haveAnimBehaviour ) + [self setAnimationBehavior: NSWindowAnimationBehaviorNone]; // Prevent system animations from interfering. + + NSImage * snapshotImage = [self uli_imageWithSnapshotForceActive: YES]; + NSRect myFrame = [self frame]; + myFrame.size = snapshotImage.size; + NSWindow * animationWindow = [self uli_animationWindowForZoomEffectWithImage: snapshotImage]; + [animationWindow setFrame: globalStartPoint display: YES]; + [animationWindow orderFront: nil]; + [animationWindow setFrame: myFrame display: YES animate: YES]; + + NSDisableScreenUpdates(); + [animationWindow close]; + + [self makeKeyAndOrderFront: nil]; + NSEnableScreenUpdates(); + + if( haveAnimBehaviour ) + [self setAnimationBehavior: oldAnimationBehaviour]; +} + + +// Same as -makeKeyAndOrderFrontWithZoomEffectFromRect: But doesn't make the window key: + +-(void) orderFrontWithZoomEffectFromRect: (NSRect)globalStartPoint +{ + if( globalStartPoint.size.width < 1 || globalStartPoint.size.height < 1 ) + globalStartPoint = [self uli_startRectForScreen: [self screen]]; + + BOOL haveAnimBehaviour = [NSWindow instancesRespondToSelector: @selector(animationBehavior)]; + NSWindowAnimationBehavior oldAnimationBehaviour = haveAnimBehaviour ? [self animationBehavior] : 0; + if( haveAnimBehaviour ) + [self setAnimationBehavior: NSWindowAnimationBehaviorNone]; // Prevent system animations from interfering. + + NSImage * snapshotImage = [self uli_imageWithSnapshotForceActive: NO]; + NSRect myFrame = [self frame]; + myFrame.size = snapshotImage.size; + NSWindow * animationWindow = [self uli_animationWindowForZoomEffectWithImage: snapshotImage]; + [animationWindow setFrame: globalStartPoint display: YES]; + [animationWindow orderFront: nil]; + [animationWindow setFrame: myFrame display: YES animate: YES]; + + NSDisableScreenUpdates(); + [animationWindow close]; + + [self orderFront: nil]; + NSEnableScreenUpdates(); + + if( haveAnimBehaviour ) + [self setAnimationBehavior: oldAnimationBehaviour]; +} + + +// The reverse of -makeKeyAndOrderFrontWithZoomEffectFromRect: + +-(void) orderOutWithZoomEffectToRect: (NSRect)globalEndPoint +{ + if( globalEndPoint.size.width < 1 || globalEndPoint.size.height < 1 ) + globalEndPoint = [self uli_startRectForScreen: [self screen]]; + + BOOL haveAnimBehaviour = [NSWindow instancesRespondToSelector: @selector(animationBehavior)]; + NSWindowAnimationBehavior oldAnimationBehaviour = haveAnimBehaviour ? [self animationBehavior] : 0; + if( haveAnimBehaviour ) + [self setAnimationBehavior: NSWindowAnimationBehaviorNone]; // Prevent system animations from interfering. + + NSImage * snapshotImage = [self uli_imageWithSnapshotForceActive: NO]; + NSRect myFrame = [self frame]; + myFrame.size = snapshotImage.size; + NSWindow * animationWindow = [self uli_animationWindowForZoomEffectWithImage: snapshotImage]; + [animationWindow setFrame: myFrame display: YES]; + + NSDisableScreenUpdates(); + [animationWindow orderFront: nil]; + [self orderOut: nil]; + NSEnableScreenUpdates(); + + [animationWindow setFrame: globalEndPoint display: YES animate: YES]; + + [animationWindow close]; + + if( haveAnimBehaviour ) + [self setAnimationBehavior: oldAnimationBehaviour]; +} + +@end \ No newline at end of file