├── .gitignore ├── .gitmodules ├── Artwork ├── BrowseCircle.png ├── BrowseCircle@2x.png ├── BrowseCircle_hover.png ├── BrowseCircle_hover@2x.png ├── Browse_Arrow.png ├── Browse_Arrow@2x.png ├── Browse_Arrow_Hover.png ├── Browse_Arrow_Hover@2x.png ├── MenubarIcon.png ├── MenubarIcon@2x.png ├── MenubarIcon_Highlighted.png ├── MenubarIcon_Highlighted@2x.png ├── PreferencesLogo.png ├── PreferencesLogo@2x.png ├── SettingsWhite.png ├── SettingsWhite@2x.png ├── Settings_DarkHover.png ├── Settings_DarkHover@2x.png ├── Settings_Normal.png ├── Settings_Normal@2x.png ├── SwitchCheckmark.png ├── SwitchCheckmark@2x.png ├── SwitchXMark.png ├── SwitchXMark@2x.png ├── TopPartArrow.png ├── TopPartArrow@2x.png ├── checkmark.png └── checkmark@2x.png ├── DotSnap.icns ├── DotSnap.iconset ├── icon_128x128.png ├── icon_128x128@2x.png ├── icon_16x16.png ├── icon_16x16@2x.png ├── icon_256x256.png ├── icon_256x256@2x.png ├── icon_32x32.png ├── icon_32x32@2x.png ├── icon_512x512.png └── icon_512x512@2x.png ├── DotSnap.xcodeproj └── project.pbxproj ├── DotSnap ├── Base.lproj │ └── MainMenu.xib ├── DSPApplicationDelegate.h ├── DSPApplicationDelegate.m ├── DSPBackgroundView.h ├── DSPBackgroundView.m ├── DSPDefaults.h ├── DSPDefaults.m ├── DSPDirectoryPickerButton.h ├── DSPDirectoryPickerButton.m ├── DSPFilenameTextField.h ├── DSPFilenameTextField.m ├── DSPGlowingNameButton.h ├── DSPGlowingNameButton.m ├── DSPHistoryRowView.h ├── DSPHistoryRowView.m ├── DSPHistoryTableView.h ├── DSPHistoryTableView.m ├── DSPLabel.h ├── DSPLabel.m ├── DSPLaunchServicesManager.h ├── DSPLaunchServicesManager.m ├── DSPLogoButton.h ├── DSPLogoButton.m ├── DSPMainView.h ├── DSPMainView.m ├── DSPMainViewController.h ├── DSPMainViewController.m ├── DSPMainViewModel.h ├── DSPMainViewModel.m ├── DSPMainWindow.h ├── DSPMainWindow.m ├── DSPMainWindowController.h ├── DSPMainWindowController.m ├── DSPMenuBarWindowIconView.h ├── DSPMenuBarWindowIconView.m ├── DSPPNGFormatter.h ├── DSPPNGFormatter.m ├── DSPPreferencesFieldBackgroundView.h ├── DSPPreferencesFieldBackgroundView.m ├── DSPPreferencesShadowBox.h ├── DSPPreferencesShadowBox.m ├── DSPPreferencesViewController.h ├── DSPPreferencesViewController.m ├── DSPPreferencesWindow.h ├── DSPPreferencesWindow.m ├── DSPShadowBox.h ├── DSPShadowBox.m ├── DSPSpinningSettingsButton.h ├── DSPSpinningSettingsButton.m ├── DSPSwitch.h ├── DSPSwitch.m ├── Defaults.plist ├── DotSnap-Info.plist ├── DotSnap-Prefix.pch ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── JNWAnimatableWindow.h ├── JNWAnimatableWindow.m ├── LIEffect.h ├── LIEffect.m ├── LIFlipEffect.h ├── LIFlipEffect.m ├── MAAttachedWindow.h ├── MAAttachedWindow.m ├── NSColor+DSPExtensions.h ├── NSColor+DSPExtensions.m ├── NSString+SymlinksAndAliases.h ├── NSString+SymlinksAndAliases.m ├── PFMoveApplication.h ├── PFMoveApplication.m ├── en.lproj │ ├── InfoPlist.strings │ └── MainMenu.xib └── main.m ├── DotSnapTests ├── DotSnapTests-Info.plist ├── DotSnapTests.m └── en.lproj │ └── InfoPlist.strings ├── External └── Lagrangian │ ├── .gitignore │ ├── External │ └── RXPreprocessing │ │ ├── .gitignore │ │ ├── README.md │ │ ├── RXCount.h │ │ ├── RXFold.h │ │ ├── RXInterpolation.h │ │ ├── RXPaste.h │ │ ├── RXPreprocessingTests.m │ │ ├── _RXFold.h │ │ └── _RXInterpolation.h │ ├── Lagrangian.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── Lagrangian (Mac OS X dynamic library).xcscheme │ │ ├── Lagrangian (Mac OS X framework).xcscheme │ │ ├── Lagrangian (iOS static framework).xcscheme │ │ └── Test runner.xcscheme │ ├── Lagrangian │ ├── L3Assertions.h │ ├── L3Collection.h │ ├── L3Collection.m │ ├── L3Configuration.h │ ├── L3EventObserver.h │ ├── L3FunctionalUtilities.h │ ├── L3FunctionalUtilities.m │ ├── L3Mock.h │ ├── L3Mock.m │ ├── L3OCUnitTestResultFormatter.h │ ├── L3OCUnitTestResultFormatter.m │ ├── L3PreprocessorUtilities.h │ ├── L3SourceReference.h │ ├── L3SourceReference.m │ ├── L3Stack.h │ ├── L3Stack.m │ ├── L3StringInflections.h │ ├── L3StringInflections.m │ ├── L3TRDynamicLibrary.h │ ├── L3TRDynamicLibrary.m │ ├── L3Test.h │ ├── L3TestCase.h │ ├── L3TestCase.m │ ├── L3TestResult.h │ ├── L3TestResult.m │ ├── L3TestResultBuilder.h │ ├── L3TestResultBuilder.m │ ├── L3TestResultFormatter.h │ ├── L3TestRunner.h │ ├── L3TestRunner.m │ ├── L3TestState.h │ ├── L3TestState.m │ ├── L3TestStep.h │ ├── L3TestStep.m │ ├── L3TestSuite.h │ ├── L3TestSuite.m │ ├── L3TestVisitor.h │ ├── L3Types.h │ ├── Lagrangian-Info.plist │ ├── Lagrangian-Prefix.pch │ ├── Lagrangian.h │ ├── Lagrangian.m │ ├── NSException+L3OCUnitCompatibility.h │ ├── NSException+L3OCUnitCompatibility.m │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── lagrangian-tool-Info.plist │ ├── lagrangian-tool.m │ └── lagrangian.1 │ ├── PLANNING.mdown │ ├── README.mdown │ └── Xcode │ └── Project Templates │ └── Lagrangian │ └── Lagrangian Xcode Integration Bundle.xctemplate │ ├── TemplateIcon.icns │ └── TemplateInfo.plist └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "External/ReactiveCocoa"] 2 | path = External/ReactiveCocoa 3 | url = https://github.com/ReactiveCocoa/ReactiveCocoa.git 4 | ignore = dirty 5 | [submodule "External/Lagrangian"] 6 | path = External/Lagrangian 7 | url = https://github.com/robrix/Lagrangian.git 8 | -------------------------------------------------------------------------------- /Artwork/BrowseCircle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/BrowseCircle.png -------------------------------------------------------------------------------- /Artwork/BrowseCircle@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/BrowseCircle@2x.png -------------------------------------------------------------------------------- /Artwork/BrowseCircle_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/BrowseCircle_hover.png -------------------------------------------------------------------------------- /Artwork/BrowseCircle_hover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/BrowseCircle_hover@2x.png -------------------------------------------------------------------------------- /Artwork/Browse_Arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/Browse_Arrow.png -------------------------------------------------------------------------------- /Artwork/Browse_Arrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/Browse_Arrow@2x.png -------------------------------------------------------------------------------- /Artwork/Browse_Arrow_Hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/Browse_Arrow_Hover.png -------------------------------------------------------------------------------- /Artwork/Browse_Arrow_Hover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/Browse_Arrow_Hover@2x.png -------------------------------------------------------------------------------- /Artwork/MenubarIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/MenubarIcon.png -------------------------------------------------------------------------------- /Artwork/MenubarIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/MenubarIcon@2x.png -------------------------------------------------------------------------------- /Artwork/MenubarIcon_Highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/MenubarIcon_Highlighted.png -------------------------------------------------------------------------------- /Artwork/MenubarIcon_Highlighted@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/MenubarIcon_Highlighted@2x.png -------------------------------------------------------------------------------- /Artwork/PreferencesLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/PreferencesLogo.png -------------------------------------------------------------------------------- /Artwork/PreferencesLogo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/PreferencesLogo@2x.png -------------------------------------------------------------------------------- /Artwork/SettingsWhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/SettingsWhite.png -------------------------------------------------------------------------------- /Artwork/SettingsWhite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/SettingsWhite@2x.png -------------------------------------------------------------------------------- /Artwork/Settings_DarkHover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/Settings_DarkHover.png -------------------------------------------------------------------------------- /Artwork/Settings_DarkHover@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/Settings_DarkHover@2x.png -------------------------------------------------------------------------------- /Artwork/Settings_Normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/Settings_Normal.png -------------------------------------------------------------------------------- /Artwork/Settings_Normal@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/Settings_Normal@2x.png -------------------------------------------------------------------------------- /Artwork/SwitchCheckmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/SwitchCheckmark.png -------------------------------------------------------------------------------- /Artwork/SwitchCheckmark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/SwitchCheckmark@2x.png -------------------------------------------------------------------------------- /Artwork/SwitchXMark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/SwitchXMark.png -------------------------------------------------------------------------------- /Artwork/SwitchXMark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/SwitchXMark@2x.png -------------------------------------------------------------------------------- /Artwork/TopPartArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/TopPartArrow.png -------------------------------------------------------------------------------- /Artwork/TopPartArrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/TopPartArrow@2x.png -------------------------------------------------------------------------------- /Artwork/checkmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/checkmark.png -------------------------------------------------------------------------------- /Artwork/checkmark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/Artwork/checkmark@2x.png -------------------------------------------------------------------------------- /DotSnap.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.icns -------------------------------------------------------------------------------- /DotSnap.iconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_128x128.png -------------------------------------------------------------------------------- /DotSnap.iconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /DotSnap.iconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_16x16.png -------------------------------------------------------------------------------- /DotSnap.iconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /DotSnap.iconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_256x256.png -------------------------------------------------------------------------------- /DotSnap.iconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /DotSnap.iconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_32x32.png -------------------------------------------------------------------------------- /DotSnap.iconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /DotSnap.iconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_512x512.png -------------------------------------------------------------------------------- /DotSnap.iconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/DotSnap.iconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /DotSnap/DSPApplicationDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPAppDelegate.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/24/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * The application delegate for DotSnap. 13 | */ 14 | @interface DSPApplicationDelegate : NSObject 15 | @end 16 | -------------------------------------------------------------------------------- /DotSnap/DSPApplicationDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPAppDelegate.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/24/13. 6 | // 7 | // 8 | 9 | #import "DSPApplicationDelegate.h" 10 | #import "DSPMainWindowController.h" 11 | #import "DSPLaunchServicesManager.h" 12 | #import "PFMoveApplication.h" 13 | 14 | @interface DSPApplicationDelegate () 15 | @property (nonatomic, strong) DSPMainWindowController *windowController; 16 | @end 17 | 18 | void DSPWarnLionIfNecessary(void); 19 | 20 | @implementation DSPApplicationDelegate 21 | 22 | + (void)load { 23 | [NSUserDefaults.standardUserDefaults registerDefaults:[NSDictionary dictionaryWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"Defaults" ofType:@"plist"]]]; 24 | } 25 | 26 | - (void)applicationWillFinishLaunching:(NSNotification *)notification { 27 | // Call this before any interface is shown to prevent confusion 28 | // and hopefully move DM to the main applications folder. 29 | if (getenv("DOTSNAP_TEST") == NULL) { 30 | DSPWarnLionIfNecessary(); 31 | PFMoveToApplicationsFolderIfNecessary(); 32 | } 33 | } 34 | 35 | - (void)applicationDidFinishLaunching:(NSNotification *)notification { 36 | if (![NSUserDefaults.standardUserDefaults boolForKey:DSPHasShownPanelAtFirstLaunchKey]) { 37 | NSInteger result = NSRunAlertPanel(@"DotSnap", @"Let DotSnap help you organize your desktop. Do you want to open DotSnap automatically when you login?", @"Yes, please", @"No", nil); 38 | if (result == NSAlertDefaultReturn) { 39 | [DSPLaunchServicesManager.defaultManager insertCurrentApplicationInStartupItems:NO]; 40 | [NSUserDefaults.standardUserDefaults setBool:YES forKey:DSPLoadDotSnapAtStartKey]; 41 | } 42 | [NSUserDefaults.standardUserDefaults setBool:YES forKey:DSPHasShownPanelAtFirstLaunchKey]; 43 | } 44 | self.windowController = [[DSPMainWindowController alloc]init]; 45 | [self.windowController close]; 46 | } 47 | 48 | - (void)applicationWillTerminate:(NSNotification *)notification { 49 | [NSUserDefaults.standardUserDefaults synchronize]; 50 | } 51 | 52 | void DSPWarnLionIfNecessary(void) { 53 | if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_7) { 54 | NSRunAlertPanel(@"Error", @"This version of DotSnap only runs on Mountain Lion or later. Sorry!", @"OK", nil, nil); 55 | [NSApplication.sharedApplication terminate:NSApp]; 56 | } 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /DotSnap/DSPBackgroundView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPBackgroundView.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/11/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DSPBackgroundView : NSView 12 | @property (nonatomic, strong) NSColor *backgroundColor; 13 | @end 14 | 15 | @interface DSPBackgroundTrackingView : DSPBackgroundView 16 | @end 17 | -------------------------------------------------------------------------------- /DotSnap/DSPBackgroundView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPBackgroundView.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/11/13. 6 | // 7 | // 8 | 9 | #import "DSPBackgroundView.h" 10 | 11 | @implementation DSPBackgroundView 12 | 13 | - (id)initWithFrame:(NSRect)frame { 14 | self = [super initWithFrame:frame]; 15 | 16 | self.layer = CALayer.layer; 17 | self.layer.doubleSided = YES; 18 | self.layer.delegate = self; 19 | self.wantsLayer = YES; 20 | self.backgroundColor = [NSColor colorWithCalibratedRed:0.260 green:0.663 blue:0.455 alpha:1.000]; 21 | 22 | return self; 23 | } 24 | 25 | - (void)setBackgroundColor:(NSColor *)backgroundColor { 26 | self.layer.backgroundColor = backgroundColor.dsp_CGColor; 27 | [self.layer setNeedsDisplay]; 28 | [self setNeedsDisplay:YES]; 29 | } 30 | 31 | - (void)viewDidMoveToWindow { 32 | [super viewDidMoveToWindow]; 33 | self.layer.contentsScale = self.window.backingScaleFactor; 34 | } 35 | 36 | @end 37 | 38 | @implementation DSPBackgroundTrackingView { 39 | NSTrackingArea *trackingArea; 40 | } 41 | 42 | - (void)mouseEntered:(NSEvent *)theEvent { 43 | self.backgroundColor = [NSColor colorWithCalibratedRed:0.231 green:0.682 blue:0.478 alpha:1.000]; 44 | [self.window.nextResponder mouseEntered:theEvent]; 45 | } 46 | 47 | - (void)mouseExited:(NSEvent *)theEvent { 48 | self.backgroundColor = [NSColor colorWithCalibratedRed:0.260 green:0.663 blue:0.455 alpha:1.000]; 49 | [self.window.nextResponder mouseExited:theEvent]; 50 | } 51 | 52 | - (void)ensureTrackingArea { 53 | if (!trackingArea) { 54 | trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingInVisibleRect | NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; 55 | } 56 | } 57 | 58 | - (void)updateTrackingAreas { 59 | [super updateTrackingAreas]; 60 | [self ensureTrackingArea]; 61 | if (![[self trackingAreas] containsObject:trackingArea]) { 62 | [self addTrackingArea:trackingArea]; 63 | } 64 | } 65 | 66 | - (void)resetCursorRects { 67 | [self addCursorRect:self.bounds cursor:NSCursor.pointingHandCursor]; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /DotSnap/DSPDefaults.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPDefaults.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/25/13. 6 | // 7 | // 8 | 9 | #ifndef DotSnap_DSPDefaults_h 10 | #define DotSnap_DSPDefaults_h 11 | 12 | #if defined(__cplusplus) 13 | #define DOTSNAP_EXTERN extern "C" 14 | #else 15 | #define DOTSNAP_EXTERN extern 16 | #endif 17 | 18 | #define DOTSNAP_EXPORT DOTSNAP_EXTERN 19 | 20 | DOTSNAP_EXPORT NSString *const DSPDefaultFilenameTemplateKey; 21 | 22 | DOTSNAP_EXPORT NSString *const DSPLoadDotSnapAtStartKey; 23 | 24 | DOTSNAP_EXPORT NSString *const DSPAddsTimestampKey; 25 | 26 | DOTSNAP_EXPORT NSString *const DSPFilenameHistoryKey; 27 | 28 | DOTSNAP_EXPORT NSString *const DSPAutosaveInputFieldKey; 29 | 30 | DOTSNAP_EXPORT NSString *const DSPDefaultFilePathKey; 31 | 32 | DOTSNAP_EXPORT NSString *const DSPHasShownPanelAtFirstLaunchKey; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /DotSnap/DSPDefaults.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPDefaults.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/25/13. 6 | // 7 | // 8 | 9 | #import "DSPDefaults.h" 10 | 11 | NSString *const DSPDefaultFilenameTemplateKey = @"DSPDefaultFilenameTemplate"; 12 | 13 | NSString *const DSPLoadDotSnapAtStartKey = @"DSPLoadDotSnapAtStart"; 14 | 15 | NSString *const DSPAddsTimestampKey = @"DSPAddsTimestamp"; 16 | 17 | NSString *const DSPFilenameHistoryKey = @"DSPFilenameHistory"; 18 | 19 | NSString *const DSPAutosaveInputFieldKey = @"DSPAutosaveInputField"; 20 | 21 | NSString *const DSPDefaultFilePathKey = @"DSPDefaultFilePathKey"; 22 | 23 | NSString *const DSPHasShownPanelAtFirstLaunchKey = @"DSPLaunchAtHotNoon"; 24 | -------------------------------------------------------------------------------- /DotSnap/DSPDirectoryPickerButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPDirectoryPickerButton.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/27/13. 6 | // 7 | // 8 | 9 | @interface DSPDirectoryPickerButton : NSButton 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /DotSnap/DSPDirectoryPickerButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPDirectoryPickerButton.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/27/13. 6 | // 7 | // 8 | 9 | #import "DSPDirectoryPickerButton.h" 10 | 11 | @interface DSPDirectoryPickerButton () 12 | @property (nonatomic, copy) void(^redrawBlock)(BOOL highlighted, BOOL hovering, NSEvent *event); 13 | @property (nonatomic, copy) void (^viewDidMoveToWindowBlock)(); 14 | @end 15 | 16 | @implementation DSPDirectoryPickerButton 17 | 18 | - (id)initWithFrame:(NSRect)frame { 19 | self = [super initWithFrame:frame]; 20 | self.layer = CALayer.layer; 21 | self.layer.masksToBounds = YES; 22 | self.wantsLayer = YES; 23 | 24 | CALayer *browseCircleLayer = CALayer.layer; 25 | browseCircleLayer.contents = [NSImage imageNamed:@"BrowseCircle"]; 26 | browseCircleLayer.frame = self.bounds; 27 | [self.layer addSublayer:browseCircleLayer]; 28 | 29 | CALayer *arrowLayer = CALayer.layer; 30 | arrowLayer.contents = [NSImage imageNamed:@"Browse_Arrow"]; 31 | arrowLayer.frame = self.bounds; 32 | [self.layer addSublayer:arrowLayer]; 33 | 34 | CALayer *hoverArrowLayer = CALayer.layer; 35 | hoverArrowLayer.contents = [NSImage imageNamed:@"Browse_Arrow_Hover"]; 36 | hoverArrowLayer.frame = self.bounds; 37 | hoverArrowLayer.frame = CGRectOffset(hoverArrowLayer.frame, -(NSWidth(browseCircleLayer.frame)/2) - 10, 0); 38 | [self.layer addSublayer:hoverArrowLayer]; 39 | 40 | self.autoresizingMask = NSViewMinYMargin; 41 | self.bordered = NO; 42 | self.buttonType = NSMomentaryChangeButton; 43 | 44 | @weakify(self); 45 | self.redrawBlock = ^(BOOL highlighted, BOOL hovering, NSEvent *event) { 46 | @strongify(self); 47 | if (hovering) { 48 | browseCircleLayer.contents = [NSImage imageNamed:@"BrowseCircle_Hover"]; 49 | arrowLayer.contents = [NSImage imageNamed:@"Browse_Arrow_Hover"]; 50 | arrowLayer.frame = CGRectOffset(arrowLayer.frame, NSWidth(browseCircleLayer.frame)/2 + 10, 0); 51 | hoverArrowLayer.frame = self.bounds; 52 | } else { 53 | browseCircleLayer.contents = [NSImage imageNamed:@"BrowseCircle"]; 54 | arrowLayer.contents = [NSImage imageNamed:@"Browse_Arrow"]; 55 | arrowLayer.frame = self.bounds; 56 | hoverArrowLayer.frame = CGRectOffset(hoverArrowLayer.frame, -(NSWidth(browseCircleLayer.frame)/2) - 10, 0); 57 | 58 | } 59 | }; 60 | 61 | self.viewDidMoveToWindowBlock = ^{ 62 | @strongify(self); 63 | CGFloat scaleFactor = self.window.backingScaleFactor; 64 | browseCircleLayer.contentsScale = scaleFactor; 65 | arrowLayer.contentsScale = scaleFactor; 66 | hoverArrowLayer.contentsScale = scaleFactor; 67 | }; 68 | 69 | return self; 70 | } 71 | 72 | - (void)mouseEntered:(NSEvent *)theEvent { 73 | self.redrawBlock(NO, YES, theEvent); 74 | } 75 | 76 | - (void)mouseExited:(NSEvent *)theEvent { 77 | self.redrawBlock(NO, NO, theEvent); 78 | } 79 | 80 | - (void)viewDidMoveToWindow { 81 | [super viewDidMoveToWindow]; 82 | self.viewDidMoveToWindowBlock(); 83 | } 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /DotSnap/DSPFilenameTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPFilenameTextField.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/8/13. 6 | // 7 | // 8 | 9 | @interface DSPFilenameTextField : NSTextField 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /DotSnap/DSPFilenameTextField.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPFilenameTextField.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/8/13. 6 | // 7 | // 8 | 9 | #import "DSPFilenameTextField.h" 10 | #import "DSPPNGFormatter.h" 11 | 12 | @implementation DSPFilenameTextField 13 | 14 | - (id)initWithFrame:(NSRect)frame 15 | { 16 | self = [super initWithFrame:frame]; 17 | 18 | self.bezeled = NO; 19 | self.drawsBackground = NO; 20 | self.font = [NSFont fontWithName:@"HelveticaNeue" size:16.f]; 21 | self.textColor = [NSColor colorWithCalibratedRed:0.437 green:0.517 blue:0.559 alpha:1.000]; 22 | self.focusRingType = NSFocusRingTypeNone; 23 | self.autoresizingMask = NSViewMinYMargin; 24 | self.enabled = NO; 25 | [self.cell setScrollable:YES]; 26 | [self.cell setLineBreakMode:NSLineBreakByClipping]; 27 | [self.cell setFormatter:[DSPPNGFormatter new]]; 28 | 29 | return self; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /DotSnap/DSPGlowingNameButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPGlowingNameButton.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/8/13. 6 | // 7 | // 8 | 9 | @interface DSPGlowingNameButton : NSButton 10 | 11 | - (id)initWithFrame:(NSRect)frameRect name:(NSString *)name; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /DotSnap/DSPGlowingNameButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPGlowingNameButton.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/8/13. 6 | // 7 | // 8 | 9 | #import "DSPGlowingNameButton.h" 10 | 11 | @interface DSPGlowingNameButton () 12 | @property (nonatomic, copy) void (^redrawBlock)(BOOL highlighted, BOOL hovering, NSEvent *event); 13 | @property (nonatomic, copy) void (^viewDidMoveToWindowBlock)(); 14 | @end 15 | 16 | @implementation DSPGlowingNameButton { 17 | NSTrackingArea *trackingArea; 18 | } 19 | 20 | - (id)initWithFrame:(NSRect)frameRect name:(NSString *)name { 21 | self = [super initWithFrame:frameRect]; 22 | 23 | self.layer = CALayer.layer; 24 | self.wantsLayer = YES; 25 | 26 | CTFontRef font = CTFontCreateWithName(CFSTR("HelveticaNeue"), 18.f, NULL); 27 | CATextLayer *nameLayer = CATextLayer.layer; 28 | nameLayer.frame = self.bounds; 29 | nameLayer.foregroundColor = [NSColor colorWithCalibratedRed:0.136 green:0.407 blue:0.264 alpha:1.000].dsp_CGColor; 30 | nameLayer.font = font; 31 | nameLayer.fontSize = 18.f; 32 | nameLayer.alignmentMode = @"left"; 33 | nameLayer.string = name; 34 | [self.layer addSublayer:nameLayer]; 35 | 36 | self.autoresizingMask = NSViewMinYMargin; 37 | self.bordered = NO; 38 | self.buttonType = NSMomentaryChangeButton; 39 | 40 | self.redrawBlock = ^(BOOL highlighted, BOOL hovering, NSEvent *event) { 41 | if (hovering) { 42 | nameLayer.foregroundColor = NSColor.whiteColor.dsp_CGColor; 43 | } else { 44 | nameLayer.foregroundColor = [NSColor colorWithCalibratedRed:0.136 green:0.407 blue:0.264 alpha:1.000].dsp_CGColor; 45 | 46 | } 47 | }; 48 | 49 | @weakify(self); 50 | self.viewDidMoveToWindowBlock = ^{ 51 | @strongify(self); 52 | nameLayer.contentsScale = self.window.backingScaleFactor; 53 | }; 54 | 55 | CFRelease(font); 56 | 57 | return self; 58 | } 59 | 60 | - (void)ensureTrackingArea { 61 | if (!trackingArea) { 62 | trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingInVisibleRect | NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; 63 | } 64 | } 65 | 66 | - (void)updateTrackingAreas { 67 | [super updateTrackingAreas]; 68 | [self ensureTrackingArea]; 69 | if (![self.trackingAreas containsObject:trackingArea]) { 70 | [self addTrackingArea:trackingArea]; 71 | } 72 | } 73 | 74 | - (void)resetCursorRects { 75 | [self addCursorRect:self.bounds cursor:NSCursor.pointingHandCursor]; 76 | } 77 | 78 | - (void)mouseEntered:(NSEvent *)theEvent { 79 | self.redrawBlock(NO, YES, theEvent); 80 | } 81 | 82 | - (void)mouseExited:(NSEvent *)theEvent { 83 | self.redrawBlock(NO, NO, theEvent); 84 | } 85 | 86 | - (void)viewDidMoveToWindow { 87 | [super viewDidMoveToWindow]; 88 | self.viewDidMoveToWindowBlock(); 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /DotSnap/DSPHistoryRowView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPHistoryCell.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/26/13. 6 | // 7 | // 8 | 9 | @interface DSPHistoryRowView : NSTableRowView 10 | 11 | @property (nonatomic, copy) NSString *title; 12 | @property (nonatomic) BOOL highlighted; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /DotSnap/DSPHistoryRowView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPHistoryCell.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/26/13. 6 | // 7 | // 8 | 9 | #import "DSPHistoryRowView.h" 10 | 11 | @interface DSPHistoryRowView () 12 | @property (nonatomic, strong) NSTextField *textField; 13 | @end 14 | 15 | @implementation DSPHistoryRowView { 16 | NSTrackingArea *trackingArea; 17 | } 18 | 19 | - (id)initWithFrame:(NSRect)frameRect { 20 | self = [super initWithFrame:frameRect]; 21 | self.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone; 22 | 23 | _textField = [[NSTextField alloc]initWithFrame:(NSRect){ .origin.x = 30, .origin.y = 16, .size = { NSWidth(self.bounds), 34 } }]; 24 | _textField.bezeled = NO; 25 | _textField.editable = NO; 26 | _textField.drawsBackground = NO; 27 | _textField.font = [NSFont fontWithName:@"HelveticaNeue" size:16.f]; 28 | _textField.textColor = [NSColor colorWithCalibratedRed:0.437 green:0.517 blue:0.559 alpha:1.000]; 29 | _textField.focusRingType = NSFocusRingTypeNone; 30 | [self addSubview:_textField]; 31 | 32 | [_textField rac_liftSelector:@selector(setStringValue:) withSignals:[RACObserve(self,title) filter:^BOOL(id value) { 33 | return value != nil; 34 | }], nil]; 35 | 36 | return self; 37 | } 38 | 39 | - (void)drawBackgroundInRect:(NSRect)dirtyRect { 40 | 41 | [self.highlighted ? NSColor.whiteColor : [NSColor colorWithCalibratedRed:0.917 green:0.936 blue:0.946 alpha:1.000]set]; 42 | NSRectFill(dirtyRect); 43 | } 44 | 45 | - (void)setHighlighted:(BOOL)highlighted { 46 | _highlighted = highlighted; 47 | if (highlighted) { 48 | self.textField.textColor = [NSColor colorWithCalibratedRed:0.160 green:0.181 blue:0.215 alpha:1.000]; 49 | [self.textField.animator setFrame:(NSRect){ .origin.x = 45, .origin.y = 16, .size = { NSWidth(self.bounds), 34 } }]; 50 | [self setNeedsDisplay:YES]; 51 | } else { 52 | self.textField.textColor = [NSColor colorWithCalibratedRed:0.437 green:0.517 blue:0.559 alpha:1.000]; 53 | [self.textField.animator setFrame:(NSRect){ .origin.x = 30, .origin.y = 16, .size = { NSWidth(self.bounds), 34 } }]; 54 | [self setNeedsDisplay:YES]; 55 | } 56 | } 57 | 58 | - (void)drawSeparatorInRect:(NSRect)dirtyRect { 59 | NSRect drawingRect = [self frame]; 60 | drawingRect.origin.y = drawingRect.size.height - 1.0; 61 | drawingRect.size.height = 1.0; 62 | 63 | [[NSColor colorWithCalibratedRed:0.766 green:0.807 blue:0.830 alpha:1.000] set]; 64 | NSRectFill(drawingRect); 65 | } 66 | 67 | - (void)ensureTrackingArea { 68 | if (!trackingArea) { 69 | trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingInVisibleRect | NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; 70 | } 71 | } 72 | 73 | - (void)updateTrackingAreas { 74 | [super updateTrackingAreas]; 75 | [self ensureTrackingArea]; 76 | if (![[self trackingAreas] containsObject:trackingArea]) { 77 | [self addTrackingArea:trackingArea]; 78 | } 79 | } 80 | 81 | - (void)mouseEntered:(NSEvent *)theEvent { 82 | self.highlighted = YES; 83 | [self setNeedsDisplay:YES]; 84 | } 85 | 86 | - (void)mouseExited:(NSEvent *)theEvent { 87 | self.highlighted = NO; 88 | [self setNeedsDisplay:YES]; 89 | } 90 | 91 | - (void)resetCursorRects { 92 | [self addCursorRect:self.bounds cursor:NSCursor.pointingHandCursor]; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /DotSnap/DSPHistoryTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPHistoryTableView.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/26/13. 6 | // 7 | // 8 | 9 | @interface DSPHistoryTableView : NSTableView 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /DotSnap/DSPHistoryTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPHistoryTableView.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/26/13. 6 | // 7 | // 8 | 9 | #import "DSPHistoryTableView.h" 10 | 11 | @implementation DSPHistoryTableView 12 | 13 | - (id)initWithFrame:(NSRect)frame { 14 | self = [super initWithFrame:frame]; 15 | 16 | self.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone; 17 | 18 | return self; 19 | } 20 | 21 | 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /DotSnap/DSPLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPLabel.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/8/13. 6 | // 7 | // 8 | 9 | @interface DSPLabel : NSTextField 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /DotSnap/DSPLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPLabel.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/8/13. 6 | // 7 | // 8 | 9 | #import "DSPLabel.h" 10 | 11 | @implementation DSPLabel 12 | 13 | - (id)initWithFrame:(NSRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | 17 | self.bezeled = NO; 18 | self.editable = NO; 19 | self.drawsBackground = NO; 20 | self.font = [NSFont fontWithName:@"HelveticaNeue" size:30.f]; 21 | self.textColor = NSColor.whiteColor; 22 | self.focusRingType = NSFocusRingTypeNone; 23 | 24 | return self; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /DotSnap/DSPLaunchServicesManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // DMLaunchServicesManager.h 3 | // Puissant 4 | // 5 | // Created by Robert Widmann on 7/12/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DSPLaunchServicesManager : NSObject 12 | 13 | + (instancetype)defaultManager; 14 | 15 | - (void)insertCurrentApplicationInStartupItems:(BOOL)hideAtLaunch; 16 | - (void)removeCurrentApplicationFromStartupItems; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /DotSnap/DSPLaunchServicesManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // DMLaunchServicesManager.m 3 | // Puissant 4 | // 5 | // Created by Robert Widmann on 7/12/13. 6 | // Copyright (c) 2013 CodaFi. All rights reserved. 7 | // 8 | 9 | #import "DSPLaunchServicesManager.h" 10 | #import 11 | 12 | #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 13 | static NSString * const kLSSharedFileListLoginItemHidden = @"com.apple.loginitem.HideOnLaunch"; 14 | #endif 15 | 16 | @interface DSPLaunchServicesManager () 17 | @property (nonatomic, copy) NSString *bundlePath; 18 | @property (nonatomic, copy) NSString *bundleID; 19 | @end 20 | 21 | @implementation DSPLaunchServicesManager 22 | 23 | + (instancetype)defaultManager { 24 | static DSPLaunchServicesManager *instance; 25 | static dispatch_once_t onceToken; 26 | dispatch_once(&onceToken, ^{ 27 | instance = [[DSPLaunchServicesManager alloc]init]; 28 | }); 29 | return instance; 30 | } 31 | 32 | - (id)init { 33 | self = [super init]; 34 | 35 | _bundlePath = NSBundle.mainBundle.bundlePath; 36 | _bundleID = NSBundle.mainBundle.bundleIdentifier; 37 | 38 | return self; 39 | } 40 | 41 | - (void)insertCurrentApplicationInStartupItems:(BOOL)hideAtLaunch { 42 | SMLoginItemSetEnabled((__bridge CFStringRef)(NSBundle.mainBundle.bundleIdentifier), true); 43 | } 44 | 45 | - (void)removeCurrentApplicationFromStartupItems { 46 | SMLoginItemSetEnabled((__bridge CFStringRef)(NSBundle.mainBundle.bundleIdentifier), false); 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /DotSnap/DSPLogoButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPLogoButton.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/18/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DSPLogoButton : NSButton 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /DotSnap/DSPLogoButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPLogoButton.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/18/13. 6 | // 7 | // 8 | 9 | #import "DSPLogoButton.h" 10 | 11 | @interface DSPLogoButton () 12 | @property (nonatomic, copy) void (^viewDidMoveToWindowBlock)(); 13 | @end 14 | 15 | @implementation DSPLogoButton 16 | 17 | - (id)initWithFrame:(NSRect)frameRect { 18 | self = [super initWithFrame:frameRect]; 19 | 20 | self.layer = CALayer.layer; 21 | self.wantsLayer = YES; 22 | 23 | self.bordered = NO; 24 | self.buttonType = NSMomentaryChangeButton; 25 | 26 | CALayer *logoLayer = CALayer.layer; 27 | logoLayer.frame = self.bounds; 28 | [self.layer addSublayer:logoLayer]; 29 | 30 | self.rac_command = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input) { 31 | return [RACSignal return:input]; 32 | }]; 33 | 34 | @weakify(self); 35 | self.viewDidMoveToWindowBlock = ^{ 36 | @strongify(self); 37 | CGFloat scaleFactor = self.window.backingScaleFactor; 38 | logoLayer.contentsScale = scaleFactor; 39 | logoLayer.contents = [NSImage imageNamed:@"PreferencesLogo"]; 40 | }; 41 | 42 | return self; 43 | } 44 | 45 | - (void)resetCursorRects { 46 | [self addCursorRect:self.bounds cursor:NSCursor.pointingHandCursor]; 47 | } 48 | 49 | - (void)viewDidMoveToWindow { 50 | [super viewDidMoveToWindow]; 51 | self.viewDidMoveToWindowBlock(); 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /DotSnap/DSPMainView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPView.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/24/13. 6 | // 7 | // 8 | 9 | @interface DSPMainView : NSView 10 | 11 | @property (nonatomic, readonly) CGRect contentRect; 12 | @property (nonatomic, copy) void(^viewDidMoveToWindowBlock)(); 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /DotSnap/DSPMainView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPView.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/24/13. 6 | // 7 | // 8 | 9 | #import "DSPMainView.h" 10 | 11 | @implementation DSPMainView 12 | 13 | - (void)drawRect:(NSRect)dirtyRect { 14 | CGRect slice, remainder; 15 | CGRectDivide(self.bounds, &slice, &remainder, 10, CGRectMaxYEdge); 16 | 17 | [[NSColor clearColor] set]; 18 | NSRectFill(slice); 19 | 20 | [[NSColor colorWithCalibratedRed:0.357 green:0.787 blue:0.572 alpha:1.000] set]; 21 | slice.size.height = 1; 22 | slice.origin.y = NSHeight(self.bounds) - 9; 23 | NSRectFill(slice); 24 | 25 | [[NSColor colorWithCalibratedRed:0.260 green:0.663 blue:0.455 alpha:1.000] set]; 26 | slice.size.height = 1; 27 | slice.origin.y = NSHeight(self.bounds) - 10; 28 | NSRectFill(slice); 29 | 30 | [[NSColor whiteColor] set]; 31 | NSRectFill(remainder); 32 | 33 | [[NSImage imageNamed:@"TopPartArrow"]drawAtPoint:NSMakePoint(((NSWidth(self.bounds) - 400)/2) - 6, NSHeight(self.bounds) - 10) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.f]; 34 | } 35 | 36 | - (CGRect)contentRect { 37 | CGRect rect = self.bounds; 38 | rect.size.height -= 10; 39 | return rect; 40 | } 41 | 42 | - (void)viewDidMoveToWindow { 43 | [super viewDidMoveToWindow]; 44 | if (self.viewDidMoveToWindowBlock != NULL) { 45 | self.viewDidMoveToWindowBlock(); 46 | } 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /DotSnap/DSPMainViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPMainViewController.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/24/13. 6 | // 7 | // 8 | 9 | @interface DSPMainViewController : NSViewController { 10 | CGRect _contentFrame; 11 | } 12 | 13 | - (id)initWithContentRect:(CGRect)rect; 14 | - (void)reset; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /DotSnap/DSPMainViewModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPMainViewModel.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/25/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DSPMainViewModel : NSObject 12 | 13 | @property (nonatomic, copy) NSString *filename; 14 | @property (nonatomic, copy) NSString *filepath; 15 | 16 | @property (nonatomic, strong, readonly) NSMutableArray *filenameHistory; 17 | 18 | - (void)addFilenameToHistory:(NSString *)filename; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /DotSnap/DSPMainViewModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPMainViewModel.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/25/13. 6 | // 7 | // 8 | 9 | #import "DSPMainViewModel.h" 10 | #import "DSPHistoryRowView.h" 11 | 12 | static NSUInteger const DPSUniqueFilenameDepthLimit = 500; 13 | 14 | @interface DSPMainViewModel () 15 | @property (nonatomic, strong) NSMutableArray *filenameHistory; 16 | @property (nonatomic, strong) NSDate *startDate; 17 | @property (nonatomic, strong) NSMetadataQuery *metadataQuery; 18 | @end 19 | 20 | @implementation DSPMainViewModel { 21 | NSUInteger previousChangeCount; 22 | } 23 | 24 | #pragma mark - Lifecycle 25 | 26 | - (id)init { 27 | self = [super init]; 28 | 29 | _filenameHistory = [NSUserDefaults.standardUserDefaults arrayForKey:DSPFilenameHistoryKey].mutableCopy; 30 | _startDate = NSDate.date; 31 | 32 | _metadataQuery = [[NSMetadataQuery alloc] init]; 33 | NSPredicate *predicate = [NSPredicate predicateWithFormat:@"kMDItemIsScreenCapture = YES"]; 34 | _metadataQuery.predicate = predicate; 35 | _metadataQuery.searchScopes = @[ DSPScreenCaptureLocation() ]; 36 | _metadataQuery.notificationBatchingInterval = 0.1f; 37 | _metadataQuery.delegate = self; 38 | [_metadataQuery startQuery]; 39 | 40 | return self; 41 | } 42 | 43 | - (id)metadataQuery:(NSMetadataQuery *)query replacementObjectForResultObject:(NSMetadataItem *)result { 44 | if (!result) return result; 45 | NSDate *fsCreationDate = [result valueForAttribute:(__bridge NSString *)kMDItemFSCreationDate]; 46 | NSDate *itemLastUseDate = [result valueForAttribute:(__bridge NSString *)kMDItemLastUsedDate]; 47 | if ([fsCreationDate timeIntervalSinceDate:_startDate] < 0.0f || itemLastUseDate != nil) { 48 | return result; 49 | } 50 | if (itemLastUseDate != nil) return result; 51 | NSString *name = [result valueForAttribute:@"kMDItemDisplayName"]; 52 | if (!name) return result; 53 | NSString *filePath = [result valueForAttribute:(__bridge NSString *)kMDItemPath]; 54 | if (!filePath) return result; 55 | if (![NSFileManager.defaultManager fileExistsAtPath:filePath]) { 56 | return result; 57 | } 58 | 59 | NSURL *newURL = [NSURL fileURLWithPath:DPSUniqueFilenameForDirectory(self.filepath, self.filename.stringByDeletingPathExtension, [NSUserDefaults.standardUserDefaults boolForKey:DSPAddsTimestampKey])]; 60 | NSURL *oldURL = [NSURL fileURLWithPath:filePath]; 61 | NSError *err = nil; 62 | [NSFileManager.defaultManager moveItemAtURL:oldURL toURL:newURL error:&err]; 63 | 64 | return result; 65 | } 66 | 67 | - (void)dealloc { 68 | [_metadataQuery stopQuery]; 69 | } 70 | 71 | #pragma mark - Actions 72 | 73 | - (void)addFilenameToHistory:(NSString *)filename { 74 | if (self.filenameHistory.count == 5) { 75 | [self.filenameHistory removeLastObject]; 76 | } 77 | __block NSUInteger c = NSUIntegerMax; 78 | [self.filenameHistory enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) { 79 | if ([filename isEqualToString:obj]) { 80 | *stop = YES; 81 | c = idx; 82 | } 83 | }]; 84 | if (c == NSUIntegerMax) [self.filenameHistory insertObject:filename.copy atIndex:0]; 85 | [NSUserDefaults.standardUserDefaults setObject:self.filenameHistory forKey:DSPFilenameHistoryKey]; 86 | } 87 | 88 | #pragma mark - Overrides 89 | 90 | - (void)setFilepath:(NSString *)filepath { 91 | _filepath = filepath; 92 | [NSUserDefaults.standardUserDefaults setObject:filepath forKey:DSPDefaultFilePathKey]; 93 | } 94 | 95 | #pragma mark - NSTableViewDatasource 96 | 97 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { 98 | return self.filenameHistory.count; 99 | } 100 | 101 | - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { 102 | return [self.filenameHistory objectAtIndex:row]; 103 | } 104 | 105 | #pragma mark - Internal 106 | 107 | + (NSDateFormatter *)dateFormatter { 108 | static NSDateFormatter *dateFormatter = nil; 109 | if (!dateFormatter) { 110 | dateFormatter = [[NSDateFormatter alloc] init]; 111 | dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 112 | dateFormatter.dateFormat = @" yyyy-MM-dd 'at' HH.mm.ss a"; 113 | } 114 | return dateFormatter; 115 | } 116 | 117 | static NSString *DPSUniqueFilenameForDirectory(NSString *relativePath, NSString *filename, BOOL timestamp) { 118 | NSError *error; 119 | BOOL directory; 120 | BOOL fileExists = [NSFileManager.defaultManager fileExistsAtPath:relativePath isDirectory:&directory]; 121 | if (!fileExists) [NSFileManager.defaultManager createDirectoryAtPath:relativePath withIntermediateDirectories:YES attributes:nil error:nil]; 122 | NSSet *allURLs = [NSSet setWithArray:[NSFileManager.defaultManager contentsOfDirectoryAtPath:relativePath error:&error]]; 123 | if (error) return nil; 124 | 125 | NSString *retVal = nil; 126 | if (timestamp) { 127 | filename = [filename stringByAppendingString:[[DSPMainViewModel dateFormatter] stringFromDate:NSDate.date]]; 128 | } 129 | if (![allURLs containsObject:[NSString stringWithFormat:@"%@.png", filename]]) { 130 | return [relativePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", filename]]; 131 | } 132 | for (NSUInteger i = 0; i < DPSUniqueFilenameDepthLimit; i++) { 133 | NSString *temp = [NSString stringWithFormat:@"%@-%lu.png", filename, i]; 134 | if (![allURLs containsObject:temp]) { 135 | retVal = temp; 136 | break; 137 | } 138 | } 139 | return [relativePath stringByAppendingPathComponent:retVal]; 140 | } 141 | 142 | static NSString *DSPScreenCaptureLocation(void) { 143 | NSString *screenCapturePrefs = [DSPScreenCapturePrefs() objectForKey:@"location"]; 144 | if (screenCapturePrefs) { 145 | if ([screenCapturePrefs.stringByExpandingTildeInPath hasSuffix:@"/"]) { 146 | return screenCapturePrefs; 147 | } 148 | return [screenCapturePrefs stringByAppendingString:@"/"]; 149 | } 150 | return [[@"~/Desktop" stringByExpandingTildeInPath] stringByAppendingString:@"/"]; 151 | } 152 | 153 | static NSDictionary *DSPScreenCapturePrefs(void) { 154 | return [NSUserDefaults.standardUserDefaults persistentDomainForName:@"com.apple.screencapture"]; 155 | } 156 | 157 | @end 158 | -------------------------------------------------------------------------------- /DotSnap/DSPMainWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPMainWindow.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/24/13. 6 | // 7 | // 8 | 9 | #import "MAAttachedWindow.h" 10 | 11 | @class DSPMenuBarWindowIconView; 12 | 13 | @interface DSPMainWindow : MAAttachedWindow 14 | 15 | - (instancetype)initWithView:(NSView *)view attachedToPoint:(NSPoint)point inWindow:(NSWindow *)window onSide:(MAWindowPosition)side atDistance:(float)distance mainWindow:(BOOL)flag; 16 | 17 | @property (nonatomic, assign) BOOL isInOpenPanel; 18 | @property (nonatomic, assign) BOOL isFlipping; 19 | 20 | @property (assign) CGFloat snapDistance; 21 | @property (nonatomic, strong) NSImage *menuBarIcon; 22 | @property (nonatomic, strong) NSImage *highlightedMenuBarIcon; 23 | @property (readonly) NSStatusItem *statusItem; 24 | @property (nonatomic, strong) DSPMenuBarWindowIconView *statusItemView; 25 | 26 | - (NSPoint)originForAttachedState; 27 | - (NSPoint)originForNewFrame:(NSRect)rect; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /DotSnap/DSPMainWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPMainWindow.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/24/13. 6 | // 7 | // 8 | 9 | #import "DSPMainWindow.h" 10 | #import "DSPMenuBarWindowIconView.h" 11 | #import 12 | 13 | //static CGFloat const DPSMenuBarWindowTitleBarHeight = 0.0; 14 | //static CGFloat const DPSMenuBarWindowArrowHeight = 10.0; 15 | //static CGFloat const DPSMenuBarWindowArrowWidth = 20.0; 16 | 17 | @implementation DSPMainWindow { 18 | BOOL _resized; 19 | } 20 | 21 | #pragma mark - Lifecycle 22 | 23 | - (instancetype)initWithView:(NSView *)view attachedToPoint:(NSPoint)point inWindow:(NSWindow *)window onSide:(MAWindowPosition)side atDistance:(float)distance mainWindow:(BOOL)flag { 24 | self = [super initWithView:view attachedToPoint:point inWindow:window onSide:side atDistance:distance]; 25 | 26 | if (flag) { 27 | _statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; 28 | CGFloat thickness = [[NSStatusBar systemStatusBar] thickness]; 29 | _statusItemView = [[DSPMenuBarWindowIconView alloc] initWithFrame:NSMakeRect(0, 0, (self.menuBarIcon ? self.menuBarIcon.size.width : thickness) + 6, thickness)]; 30 | _statusItemView.menuBarWindow = self; 31 | _statusItem.view = _statusItemView; 32 | [NSNotificationCenter.defaultCenter addObserverForName:NSWindowDidMoveNotification object:_statusItem.view.window queue:nil usingBlock:^(NSNotification *note) { 33 | self.frameOrigin = self.originForAttachedState; 34 | }]; 35 | [NSNotificationCenter.defaultCenter addObserverForName:NSWindowDidResignKeyNotification object:self queue:nil usingBlock:^(NSNotification *note) { 36 | if (!_isInOpenPanel && !_isFlipping) { 37 | [self orderOutWithDuration:0.3 timing:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] animations:^(CALayer *layer) { 38 | layer.transform = CATransform3DMakeTranslation(0.f, -50.f, 0.f); 39 | layer.opacity = 0.f; 40 | }]; 41 | _statusItemView.highlighted = NO; 42 | } 43 | }]; 44 | } 45 | 46 | return self; 47 | } 48 | 49 | - (NSPoint)originForAttachedState { 50 | if (_statusItemView) { 51 | NSRect statusItemFrame = _statusItemView.window.frame; 52 | NSPoint midPoint = (NSPoint){ NSMidX(statusItemFrame), NSMinY(statusItemFrame) }; 53 | return (NSPoint){ midPoint.x - (NSWidth(self.frame) / 2) + 1, midPoint.y - NSHeight(self.frame) + ((_resized) ? -2 : 10) }; 54 | } 55 | return NSZeroPoint; 56 | } 57 | 58 | - (NSPoint)originForNewFrame:(NSRect)rect { 59 | if (_statusItemView) { 60 | NSRect statusItemFrame = _statusItemView.window.frame; 61 | NSPoint midPoint = (NSPoint){ NSMidX(statusItemFrame), NSMinY(statusItemFrame) }; 62 | return (NSPoint){ midPoint.x - (NSWidth(rect) / 2) + 1, midPoint.y - NSHeight(rect) - 2 }; 63 | } 64 | return NSZeroPoint; 65 | } 66 | 67 | - (void)makeKeyAndOrderFrontWithDuration:(CFTimeInterval)duration timing:(CAMediaTimingFunction *)timingFunction setup:(void (^)(CALayer *))setup animations:(void (^)(CALayer *))animations { 68 | [self setFrameOrigin:[self originForAttachedState]]; 69 | [super makeKeyAndOrderFrontWithDuration:duration timing:timingFunction setup:setup animations:animations]; 70 | } 71 | 72 | - (void)setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag { 73 | if (!_resized && animateFlag) { 74 | _resized = YES; 75 | } 76 | [super setFrame:frameRect display:displayFlag animate:animateFlag]; 77 | [self setFrameOrigin:[self originForAttachedState]]; 78 | } 79 | 80 | #pragma mark - Active/key events 81 | 82 | - (BOOL)canBecomeKeyWindow { 83 | return YES; 84 | } 85 | 86 | - (BOOL)canBecomeMainWindow { 87 | return YES; 88 | } 89 | 90 | - (void)setMenuBarIcon:(NSImage *)image { 91 | _menuBarIcon = image; 92 | if (_statusItemView) { 93 | [_statusItemView setFrameSize:NSMakeSize(image.size.width + 6, _statusItemView.frame.size.height)]; 94 | [_statusItemView setNeedsDisplay:YES]; 95 | } 96 | } 97 | 98 | - (void)setHighlightedMenuBarIcon:(NSImage *)image { 99 | _highlightedMenuBarIcon = image; 100 | if (_statusItemView) 101 | { 102 | [_statusItemView setNeedsDisplay:YES]; 103 | } 104 | } 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /DotSnap/DSPMainWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPMainWindowController.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/24/13. 6 | // 7 | // 8 | 9 | @class DSPMainWindow; 10 | 11 | @interface DSPMainWindowController : NSWindowController 12 | 13 | - (DSPMainWindow *)window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /DotSnap/DSPMainWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPMainWindowController.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/24/13. 6 | // 7 | // 8 | 9 | #import "DSPMainWindowController.h" 10 | #import "DSPMainViewController.h" 11 | #import "DSPMainWindow.h" 12 | 13 | @interface DSPMainWindowController () 14 | @property (nonatomic, strong) DSPMainViewController *viewController; 15 | @end 16 | 17 | @implementation DSPMainWindowController 18 | 19 | - (id)init { 20 | self = [super init]; 21 | 22 | _viewController = [[DSPMainViewController alloc]initWithContentRect:(NSRect){ .size = { 400, 196 } }]; 23 | [self setNextResponder:_viewController]; 24 | 25 | self.window = [[DSPMainWindow alloc]initWithView:self.viewController.view attachedToPoint:(NSPoint){ } inWindow:nil onSide:MAPositionBottom atDistance:5.0 mainWindow:YES]; 26 | self.window.cornerRadius = 0.f; 27 | self.window.arrowHeight = 12.f; 28 | self.window.borderWidth = 0.f; 29 | self.window.backgroundColor = NSColor.clearColor; 30 | self.window.arrowBaseWidth = 20.f; 31 | self.window.menuBarIcon = [NSImage imageNamed:@"MenubarIcon"]; 32 | self.window.highlightedMenuBarIcon = [NSImage imageNamed:@"MenubarIcon_Highlighted"]; 33 | self.window.acceptsMouseMovedEvents = YES; 34 | self.window.collectionBehavior = NSWindowCollectionBehaviorCanJoinAllSpaces; 35 | 36 | NSRect rect = (NSRect){ .size = { 400, 214 } }; 37 | rect.origin = [(DSPMainWindow *)self.window originForNewFrame:rect]; 38 | [(DSPMainWindow *)self.window setFrame:rect display:YES animate:YES]; 39 | 40 | [NSNotificationCenter.defaultCenter addObserverForName:NSWindowDidResignKeyNotification object:self.window queue:nil usingBlock:^(NSNotification *note) { 41 | if (![NSUserDefaults.standardUserDefaults boolForKey:DSPAutosaveInputFieldKey]) { 42 | [self.viewController reset]; 43 | } 44 | }]; 45 | 46 | return self; 47 | } 48 | 49 | - (DSPMainWindow *)window { 50 | return (DSPMainWindow *)[super window]; 51 | } 52 | 53 | - (BOOL)validateProposedFirstResponder:(NSResponder *)responder forEvent:(NSEvent *)event { 54 | if ([responder isKindOfClass:NSTextField.class] && !event) { 55 | return NO; 56 | } 57 | return [super validateProposedFirstResponder:responder forEvent:event]; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /DotSnap/DSPMenuBarWindowIconView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DPSMenuBarWindowIconView.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/9/13. 6 | // 7 | // 8 | 9 | @class DSPMainWindow; 10 | 11 | @interface DSPMenuBarWindowIconView : NSView 12 | 13 | @property (assign) DSPMainWindow *menuBarWindow; 14 | @property (nonatomic) BOOL highlighted; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /DotSnap/DSPMenuBarWindowIconView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DPSMenuBarWindowIconView.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/9/13. 6 | // 7 | // 8 | 9 | #import "DSPMenuBarWindowIconView.h" 10 | #import "DSPMainWindow.h" 11 | 12 | static NSMenu *contextMenu(id delegate) { 13 | static NSMenu *fileMenu = nil; 14 | if (fileMenu == nil) { 15 | fileMenu = [[NSMenu alloc] init]; 16 | NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quit DotSnap" action:@selector(terminate:) keyEquivalent:@""]; 17 | [quitMenuItem setTarget:NSApp]; 18 | [fileMenu addItem:quitMenuItem]; 19 | } 20 | fileMenu.delegate = delegate; 21 | return fileMenu; 22 | } 23 | 24 | @implementation DSPMenuBarWindowIconView 25 | 26 | #pragma mark - Highlighting 27 | 28 | - (void)setHighlighted:(BOOL)flag { 29 | _highlighted = flag; 30 | [self setNeedsDisplay:YES]; 31 | } 32 | 33 | #pragma mark - Mouse events 34 | 35 | - (void)mouseDown:(NSEvent *)theEvent { 36 | self.highlighted = YES; 37 | if ((theEvent.modifierFlags & NSControlKeyMask) == NSControlKeyMask) { 38 | if ([NSApp keyWindow].isVisible) { 39 | [(DSPMainWindow *)[NSApp keyWindow] orderOutWithDuration:0.3 timing:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] animations:^(CALayer *layer) { 40 | layer.transform = CATransform3DMakeTranslation(0.f, -50.f, 0.f); 41 | layer.opacity = 0.f; 42 | }]; 43 | } 44 | [self.menuBarWindow.statusItem popUpStatusItemMenu:contextMenu(self)]; 45 | return; 46 | } 47 | 48 | if (([NSApp keyWindow].isMainWindow || [NSApp keyWindow].isVisible) && !self.menuBarWindow.isInOpenPanel) { 49 | [(DSPMainWindow *)[NSApp keyWindow] orderOutWithDuration:0.3 timing:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] animations:^(CALayer *layer) { 50 | layer.transform = CATransform3DMakeTranslation(0.f, -50.f, 0.f); 51 | layer.opacity = 0.f; 52 | }]; 53 | } else if (self.menuBarWindow.isInOpenPanel) { 54 | [NSApp endSheet:self.menuBarWindow.attachedSheet]; 55 | [self.menuBarWindow orderOutWithDuration:0.3 timing:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] animations:^(CALayer *layer) { 56 | layer.transform = CATransform3DMakeTranslation(0.f, -50.f, 0.f); 57 | layer.opacity = 0.f; 58 | }]; 59 | } else { 60 | [NSApp activateIgnoringOtherApps:YES]; 61 | [self.menuBarWindow makeKeyAndOrderFrontWithDuration:0.3 timing:nil setup:^(CALayer *layer) { 62 | layer.transform = CATransform3DMakeTranslation(0.f, -50., 0.f); 63 | layer.opacity = 0.f; 64 | } animations:^(CALayer *layer) { 65 | layer.transform = CATransform3DIdentity; 66 | layer.opacity = 1.f; 67 | }]; 68 | } 69 | } 70 | 71 | - (void)rightMouseDown:(NSEvent *)theEvent{ 72 | self.highlighted = YES; 73 | if ([NSApp keyWindow].isVisible) { 74 | [(DSPMainWindow *)[NSApp keyWindow] orderOutWithDuration:0.3 timing:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] animations:^(CALayer *layer) { 75 | layer.transform = CATransform3DMakeTranslation(0.f, -50.f, 0.f); 76 | layer.opacity = 0.f; 77 | }]; 78 | } 79 | [self.menuBarWindow.statusItem popUpStatusItemMenu:contextMenu(self)]; 80 | } 81 | 82 | - (void)mouseUp:(NSEvent *)theEvent { 83 | self.highlighted = NO; 84 | } 85 | 86 | - (void)menuDidClose:(NSMenu *)menu { 87 | self.highlighted = NO; 88 | } 89 | 90 | #pragma mark - Drawing 91 | 92 | - (void)drawRect:(NSRect)dirtyRect { 93 | NSRect b = self.bounds; 94 | if (self.highlighted) { 95 | [NSColor.selectedMenuItemColor set]; 96 | NSRectFill(b); 97 | } 98 | if (self.menuBarWindow && self.menuBarWindow.menuBarIcon) { 99 | NSRect rect = (NSRect){ .origin = { NSMinX(b) + 3, NSMinY(b) + 3 }, .size = { NSWidth(b) - 6, NSHeight(b) - 6 } }; 100 | if (self.highlighted && self.menuBarWindow.highlightedMenuBarIcon) { 101 | [self.menuBarWindow.highlightedMenuBarIcon drawInRect:rect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; 102 | } else { 103 | [self.menuBarWindow.menuBarIcon drawInRect:rect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; 104 | } 105 | } 106 | } 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /DotSnap/DSPPNGFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPPNGFormatter.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/28/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DSPPNGFormatter : NSFormatter 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /DotSnap/DSPPNGFormatter.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPPNGFormatter.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/28/13. 6 | // 7 | // 8 | 9 | #import "DSPPNGFormatter.h" 10 | 11 | @implementation DSPPNGFormatter 12 | 13 | - (NSString *)stringForObjectValue:(NSString *)string { 14 | if (string.length == 0) { 15 | string = @"Screen Shot"; 16 | } 17 | NSString *stringToAttribute = string; 18 | if (![string hasSuffix:@".png"]) { 19 | stringToAttribute = [string stringByAppendingPathExtension:@"png"]; 20 | } 21 | return stringToAttribute; 22 | } 23 | 24 | - (NSString *)editingStringForObjectValue:(NSString *)obj { 25 | return obj.stringByDeletingPathExtension; 26 | } 27 | 28 | - (NSAttributedString *)attributedStringForObjectValue:(NSString *)string withDefaultAttributes:(NSDictionary *)attrs { 29 | if (string.length == 0) { 30 | string = @"Screen Shot"; 31 | } 32 | NSString *stringToAttribute = string; 33 | if ([string hasPrefix:@".png"]) { 34 | stringToAttribute = [string stringByAppendingPathExtension:@"png"]; 35 | } 36 | NSAttributedString *attributedString = [[NSAttributedString alloc]initWithString:stringToAttribute attributes:nil]; 37 | 38 | return attributedString; 39 | } 40 | 41 | - (BOOL)getObjectValue:(out __autoreleasing id *)obj forString:(NSString *)string errorDescription:(out NSString *__autoreleasing *)error { 42 | *obj = [self appendSuffixIfNecessary:string]; 43 | return YES; 44 | } 45 | 46 | - (NSString *)appendSuffixIfNecessary:(NSString *)candidateString { 47 | NSString *stringToAttribute = candidateString; 48 | if (![candidateString hasSuffix:@".png"]) { 49 | stringToAttribute = [candidateString stringByAppendingPathExtension:@"png"]; 50 | } 51 | return stringToAttribute; 52 | } 53 | 54 | 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /DotSnap/DSPPreferencesFieldBackgroundView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPPreferencesFieldBackgroundView.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/14/13. 6 | // 7 | // 8 | 9 | #import "DSPBackgroundView.h" 10 | 11 | @interface DSPPreferencesFieldBackgroundView : DSPBackgroundView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /DotSnap/DSPPreferencesFieldBackgroundView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPPreferencesFieldBackgroundView.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/14/13. 6 | // 7 | // 8 | 9 | #import "DSPPreferencesFieldBackgroundView.h" 10 | 11 | @implementation DSPPreferencesFieldBackgroundView 12 | 13 | - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { 14 | CGContextSetRGBFillColor(ctx, 0.912, 0.905, 0.951, 1.000); 15 | CGContextFillRect(ctx, CGRectMake(0, NSHeight(self.bounds) - 2, NSWidth(self.bounds), 1)); 16 | 17 | CGContextSetRGBFillColor(ctx, 0.889, 0.927, 0.941, 1.000); 18 | CGContextFillRect(ctx, CGRectMake(1, 0, 1, NSHeight(self.bounds))); 19 | CGContextFillRect(ctx, CGRectMake(NSWidth(self.bounds) - 2, 0, 1, NSHeight(self.bounds))); 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /DotSnap/DSPPreferencesShadowBox.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPPreferencesShadowBox.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/14/13. 6 | // 7 | // 8 | 9 | #import "DSPShadowBox.h" 10 | 11 | @interface DSPPreferencesShadowBox : DSPShadowBox 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /DotSnap/DSPPreferencesShadowBox.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPPreferencesShadowBox.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/14/13. 6 | // 7 | // 8 | 9 | #import "DSPPreferencesShadowBox.h" 10 | 11 | @implementation DSPPreferencesShadowBox 12 | 13 | - (void)drawRect:(NSRect)dirtyRect { 14 | [[NSColor colorWithCalibratedRed:0.860 green:0.892 blue:0.912 alpha:1.000]drawSwatchInRect:(NSRect){ .origin = { 0, 3 }, .size = { NSWidth(self.bounds), 1 } }]; 15 | [[NSColor colorWithCalibratedRed:0.803 green:0.854 blue:0.878 alpha:1.000]drawSwatchInRect:(NSRect){ .origin = { 0, 2 }, .size = { NSWidth(self.bounds), 1 } }]; 16 | [[NSColor colorWithCalibratedRed:0.762 green:0.826 blue:0.854 alpha:1.000]drawSwatchInRect:(NSRect){ .origin = { 0, 1 }, .size = { NSWidth(self.bounds), 1 } }]; 17 | [[NSColor colorWithCalibratedRed:0.860 green:0.892 blue:0.912 alpha:1.000]drawSwatchInRect:(NSRect){ .origin = { 0, 0 }, .size = { NSWidth(self.bounds), 1 } }]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /DotSnap/DSPPreferencesViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPPreferencesViewController.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/25/13. 6 | // 7 | // 8 | 9 | @class RACSubject; 10 | 11 | @interface DSPPreferencesViewController : NSViewController 12 | 13 | - (id)initWithContentRect:(CGRect)rect; 14 | 15 | @property (nonatomic, unsafe_unretained) NSWindow *presentingWindow; 16 | @property (nonatomic) BOOL exemptForAnimation; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /DotSnap/DSPPreferencesWindow.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPPreferencesWindow.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/28/13. 6 | // 7 | // 8 | 9 | #import "DSPMainWindow.h" 10 | 11 | 12 | /** 13 | * The preferences window for DotSnap 14 | */ 15 | @interface DSPPreferencesWindow : DSPMainWindow 16 | @end 17 | -------------------------------------------------------------------------------- /DotSnap/DSPPreferencesWindow.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPPreferencesWindow.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/28/13. 6 | // 7 | // 8 | 9 | #import "DSPPreferencesWindow.h" 10 | #import "DSPMenuBarWindowIconView.h" 11 | 12 | @interface DSPPreferencesWindow () 13 | 14 | @end 15 | 16 | @implementation DSPPreferencesWindow 17 | 18 | - (MAAttachedWindow *)initWithView:(NSView *)view attachedToPoint:(NSPoint)point onSide:(MAWindowPosition)side { 19 | self = [super initWithView:view attachedToPoint:point onSide:side]; 20 | 21 | self.arrowHeight = 12.f; 22 | self.borderColor = [NSColor colorWithCalibratedRed:0.260 green:0.663 blue:0.455 alpha:1.000]; 23 | self.backgroundColor = [NSColor colorWithCalibratedRed:0.260 green:0.663 blue:0.455 alpha:1.000]; 24 | 25 | [NSNotificationCenter.defaultCenter addObserverForName:NSWindowDidResignKeyNotification object:self queue:nil usingBlock:^(NSNotification *note) { 26 | [self orderOutWithDuration:0.3 timing:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut] animations:^(CALayer *layer) { 27 | layer.transform = CATransform3DMakeTranslation(0.f, -50.f, 0.f); 28 | layer.opacity = 0.f; 29 | }]; 30 | self.statusItemView.highlighted = NO; 31 | }]; 32 | 33 | return self; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /DotSnap/DSPShadowBox.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPShadowBox.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/8/13. 6 | // 7 | // 8 | 9 | @interface DSPShadowBox : NSBox 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /DotSnap/DSPShadowBox.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPShadowBox.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/8/13. 6 | // 7 | // 8 | 9 | #import "DSPShadowBox.h" 10 | 11 | @implementation DSPShadowBox 12 | 13 | - (id)initWithFrame:(NSRect)frameRect { 14 | self = [super initWithFrame:frameRect]; 15 | 16 | self.autoresizingMask = NSViewWidthSizable | NSViewMinYMargin; 17 | self.borderType = NSLineBorder; 18 | self.borderColor = [NSColor colorWithCalibratedRed:0.361 green:0.787 blue:0.568 alpha:1.000]; 19 | self.fillColor = [NSColor colorWithCalibratedRed:0.361 green:0.787 blue:0.568 alpha:1.000]; 20 | self.borderWidth = 2.f; 21 | self.boxType = NSBoxCustom; 22 | 23 | return self; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /DotSnap/DSPSpinningSettingsButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPSpinningSettingsButton.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/27/13. 6 | // 7 | // 8 | 9 | typedef NS_ENUM(NSUInteger, DSPSpinningSettingsButtonStyle) { 10 | DSPSpinningSettingsButtonStyleGrey = 0, 11 | DSPSpinningSettingsButtonStyleWhite = 1 12 | }; 13 | 14 | @interface DSPSpinningSettingsButton : NSButton 15 | 16 | - (id)initWithFrame:(NSRect)frameRect style:(DSPSpinningSettingsButtonStyle)style; 17 | - (void)spinOut; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /DotSnap/DSPSpinningSettingsButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPSpinningSettingsButton.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/27/13. 6 | // 7 | // 8 | 9 | #import "DSPSpinningSettingsButton.h" 10 | 11 | @interface DSPSpinningSettingsButton () 12 | @property (nonatomic, copy) void (^redrawBlock)(BOOL highlighted, BOOL hovering, NSEvent *event); 13 | @property (nonatomic, copy) void (^spinOutBlock)(); 14 | @end 15 | 16 | @implementation DSPSpinningSettingsButton { 17 | NSTrackingArea *trackingArea; 18 | } 19 | 20 | - (id)initWithFrame:(NSRect)frameRect style:(DSPSpinningSettingsButtonStyle)style { 21 | self = [super initWithFrame:frameRect]; 22 | 23 | self.layer = CALayer.layer; 24 | self.wantsLayer = YES; 25 | 26 | CALayer *gearLayer = CALayer.layer; 27 | if (style == DSPSpinningSettingsButtonStyleGrey) { 28 | gearLayer.contents = [NSImage imageNamed:@"Settings_Normal"]; 29 | } else { 30 | gearLayer.contents = [NSImage imageNamed:@"SettingsWhite"]; 31 | } 32 | gearLayer.frame = self.bounds; 33 | [self.layer addSublayer:gearLayer]; 34 | 35 | CALayer *checkmarkLayer = CALayer.layer; 36 | checkmarkLayer.contents = [NSImage imageNamed:@"Checkmark"]; 37 | checkmarkLayer.frame = self.bounds; 38 | checkmarkLayer.opacity = 0.f; 39 | [self.layer addSublayer:checkmarkLayer]; 40 | 41 | self.autoresizingMask = NSViewMinYMargin; 42 | self.bordered = NO; 43 | self.buttonType = NSMomentaryChangeButton; 44 | 45 | self.redrawBlock = ^(BOOL highlighted, BOOL hovering, NSEvent *event) { 46 | if (hovering) { 47 | if (style == DSPSpinningSettingsButtonStyleGrey) { 48 | gearLayer.contents = [NSImage imageNamed:@"Settings_DarkHover"]; 49 | } else { 50 | gearLayer.contents = [NSImage imageNamed:@"Settings_Normal"]; 51 | } 52 | [CATransaction setDisableActions:YES]; 53 | CABasicAnimation *spinningAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; 54 | spinningAnimation.toValue = @(M_PI); 55 | spinningAnimation.duration = 0.8; 56 | spinningAnimation.repeatCount = HUGE_VALF; 57 | [gearLayer addAnimation:spinningAnimation forKey:nil]; 58 | } else { 59 | [gearLayer removeAllAnimations]; 60 | if (style == DSPSpinningSettingsButtonStyleGrey) { 61 | gearLayer.contents = [NSImage imageNamed:@"Settings_Normal"]; 62 | } else { 63 | gearLayer.contents = [NSImage imageNamed:@"SettingsWhite"]; 64 | } 65 | } 66 | }; 67 | 68 | @weakify(self); 69 | self.spinOutBlock = ^{ 70 | @strongify(self); 71 | CABasicAnimation *spinningAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; 72 | spinningAnimation.toValue = @(M_PI); 73 | spinningAnimation.duration = 0.5; 74 | 75 | CABasicAnimation *gearopacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 76 | gearopacityAnimation.toValue = @0; 77 | gearopacityAnimation.duration = 0.6; 78 | 79 | CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 80 | opacityAnimation.toValue = @1; 81 | opacityAnimation.duration = 0.6; 82 | spinningAnimation.delegate = self; 83 | 84 | [[self rac_signalForSelector:@selector(animationDidStop:finished:)]subscribeNext:^(id x) { 85 | 86 | double delayInSeconds = 0.5; 87 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 88 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 89 | if (style == DSPSpinningSettingsButtonStyleGrey) { 90 | gearLayer.contents = [NSImage imageNamed:@"Settings_Normal"]; 91 | } else { 92 | gearLayer.contents = [NSImage imageNamed:@"SettingsWhite"]; 93 | } 94 | }); 95 | }]; 96 | [gearLayer addAnimation:spinningAnimation forKey:nil]; 97 | [gearLayer addAnimation:gearopacityAnimation forKey:nil]; 98 | [checkmarkLayer addAnimation:opacityAnimation forKey:nil]; 99 | }; 100 | 101 | return self; 102 | } 103 | 104 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 105 | 106 | } 107 | 108 | - (void)resetCursorRects { 109 | [self addCursorRect:self.bounds cursor:NSCursor.pointingHandCursor]; 110 | } 111 | 112 | - (void)spinOut { 113 | self.spinOutBlock(); 114 | } 115 | 116 | - (void)ensureTrackingArea { 117 | if (trackingArea == nil) { 118 | trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingInVisibleRect | NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; 119 | } 120 | } 121 | 122 | - (void)updateTrackingAreas { 123 | [super updateTrackingAreas]; 124 | [self ensureTrackingArea]; 125 | if (![[self trackingAreas] containsObject:trackingArea]) { 126 | [self addTrackingArea:trackingArea]; 127 | } 128 | } 129 | 130 | - (void)mouseEntered:(NSEvent *)theEvent { 131 | self.redrawBlock(NO, YES, theEvent); 132 | } 133 | 134 | - (void)mouseExited:(NSEvent *)theEvent { 135 | self.redrawBlock(NO, NO, theEvent); 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /DotSnap/DSPSwitch.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSPSwitch.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/25/13. 6 | // 7 | // 8 | 9 | @interface DSPSwitch : NSControl 10 | 11 | - (id)initWithFrame:(NSRect)frameRect; 12 | - (void)setOn:(BOOL)on animated:(BOOL)animated; 13 | 14 | @property(nonatomic, getter=isOn) BOOL on; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /DotSnap/DSPSwitch.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSPSwitch.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/25/13. 6 | // 7 | // 8 | 9 | #import "DSPSwitch.h" 10 | 11 | @interface DSPSwitch () 12 | @property (nonatomic, strong) CALayer *offLayer; 13 | @property (nonatomic, strong) CALayer *onLayer; 14 | @property (nonatomic, strong) CALayer *switchCover; 15 | @end 16 | 17 | @implementation DSPSwitch { 18 | NSTrackingArea *trackingArea; 19 | BOOL _isTracking; 20 | BOOL _hasMoved; 21 | CGFloat _oldX; 22 | CGFloat _deltaX; 23 | BOOL _pushedClosedHandCursor; 24 | } 25 | 26 | - (id)initWithFrame:(NSRect)frame { 27 | self = [super initWithFrame:frame]; 28 | 29 | self.on = YES; 30 | 31 | self.layer = CALayer.layer; 32 | self.layer.cornerRadius = 4.f; 33 | self.layer.masksToBounds = YES; 34 | self.wantsLayer = YES; 35 | 36 | CALayer *underLayer = CALayer.layer; 37 | underLayer.frame = self.layer.bounds; 38 | underLayer.backgroundColor = [NSColor colorWithCalibratedRed:0.729 green:0.779 blue:0.811 alpha:1.000].dsp_CGColor; 39 | [self.layer addSublayer:underLayer]; 40 | 41 | CALayer *underLayer2 = CALayer.layer; 42 | underLayer2.frame = (NSRect){ .origin = NSMidX(self.layer.bounds) - 10, .size = { 20, 2} }; 43 | underLayer2.backgroundColor = [NSColor colorWithCalibratedRed:0.605 green:0.692 blue:0.737 alpha:1.000].dsp_CGColor; 44 | [self.layer addSublayer:underLayer2]; 45 | 46 | self.offLayer = CALayer.layer; 47 | self.offLayer.contents = [NSImage imageNamed:@"SwitchXMark"]; 48 | self.offLayer.frame = (NSRect){ .size = { CGRectGetWidth(frame)/2, CGRectGetHeight(frame) } }; 49 | self.offLayer.opacity = 0.f; 50 | [self.layer addSublayer:self.offLayer]; 51 | 52 | self.onLayer = CALayer.layer; 53 | self.onLayer.contents = [NSImage imageNamed:@"SwitchCheckmark"]; 54 | self.onLayer.frame = (NSRect){ .origin.x = CGRectGetWidth(frame)/2, .size = { CGRectGetWidth(frame)/2, CGRectGetHeight(frame) } }; 55 | [self.layer addSublayer:self.onLayer]; 56 | 57 | CALayer *topShadowLayer = CALayer.layer; 58 | topShadowLayer.frame = (NSRect){ .origin.y = CGRectGetHeight(frame) - 2, .size = { (CGRectGetWidth(frame)/2) + 1, 3 } }; 59 | topShadowLayer.backgroundColor = [NSColor colorWithCalibratedRed:0.762 green:0.821 blue:0.849 alpha:1.000].dsp_CGColor; 60 | [self.switchCover addSublayer:topShadowLayer]; 61 | 62 | self.switchCover = CALayer.layer; 63 | self.switchCover.backgroundColor = [NSColor colorWithCalibratedRed:0.730 green:0.793 blue:0.825 alpha:1.000].dsp_CGColor; 64 | self.switchCover.frame = (NSRect){ .size = { CGRectGetWidth(frame)/2, CGRectGetHeight(frame) } }; 65 | self.switchCover.cornerRadius = 4.f; 66 | [self.layer addSublayer:self.switchCover]; 67 | 68 | CALayer *shadowLayer = CALayer.layer; 69 | shadowLayer.frame = (NSRect){ .size = { CGRectGetWidth(self.switchCover.bounds), 2 } }; 70 | shadowLayer.backgroundColor = [NSColor colorWithCalibratedRed:0.575 green:0.665 blue:0.709 alpha:1.000].dsp_CGColor; 71 | [self.switchCover addSublayer:shadowLayer]; 72 | 73 | 74 | return self; 75 | } 76 | 77 | - (void)ensureTrackingArea { 78 | if (trackingArea == nil) { 79 | trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingInVisibleRect | NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited owner:self userInfo:nil]; 80 | } 81 | } 82 | 83 | - (void)updateTrackingAreas { 84 | [super updateTrackingAreas]; 85 | [self ensureTrackingArea]; 86 | if (![[self trackingAreas] containsObject:trackingArea]) { 87 | [self addTrackingArea:trackingArea]; 88 | } 89 | } 90 | 91 | - (void)mouseEntered:(NSEvent *)theEvent { 92 | if (CGRectContainsPoint(self.switchCover.bounds, [self.switchCover convertPoint:[theEvent locationInWindow] fromLayer:self.superview.layer])) { 93 | _isTracking = YES; 94 | _oldX = [self.switchCover convertPoint:[theEvent locationInWindow] fromLayer:self.superview.layer].x; 95 | } 96 | } 97 | 98 | - (void)mouseDown:(NSEvent *)theEvent { 99 | [super mouseDown:theEvent]; 100 | _hasMoved = NO; 101 | _deltaX = 0; 102 | if (CGRectContainsPoint(self.switchCover.bounds, [self.switchCover convertPoint:[theEvent locationInWindow] fromLayer:self.superview.layer])) { 103 | _isTracking = YES; 104 | _oldX = [self.switchCover convertPoint:[theEvent locationInWindow] fromLayer:self.superview.layer].x; 105 | } 106 | } 107 | 108 | - (void)mouseDragged:(NSEvent *)theEvent { 109 | if (_isTracking) { 110 | if (!_pushedClosedHandCursor) { 111 | _pushedClosedHandCursor = YES; 112 | [NSCursor.closedHandCursor push]; 113 | } 114 | _hasMoved = YES; 115 | CGRect frame = self.switchCover.frame; 116 | _deltaX = _oldX - [self.switchCover convertPoint:[theEvent locationInWindow] fromLayer:self.superview.layer].x; 117 | CGFloat frameX = frame.origin.x; 118 | frameX -= _deltaX; 119 | if (frameX >= 0 && frameX <= CGRectGetWidth(frame)) { 120 | frame.origin.x = frameX; 121 | } 122 | self.offLayer.opacity = frameX / CGRectGetWidth(frame); 123 | self.onLayer.opacity = 1 - ((2 * frameX) / CGRectGetWidth(frame)); 124 | self.switchCover.frame = frame; 125 | } 126 | } 127 | 128 | - (void)mouseUp:(NSEvent *)theEvent { 129 | [super mouseUp:theEvent]; 130 | if (_isTracking && !_hasMoved) { 131 | [self setOn:!self.on animated:YES]; 132 | } else if (_isTracking && _hasMoved) { 133 | [self setOn:!(_deltaX <= 0) animated:YES]; 134 | } else if (!_hasMoved) { 135 | [self setOn:!self.on animated:YES]; 136 | } 137 | if (_pushedClosedHandCursor) { 138 | _pushedClosedHandCursor = NO; 139 | [NSCursor.currentCursor pop]; 140 | } 141 | _isTracking = NO; 142 | _hasMoved = NO; 143 | } 144 | 145 | - (void)setOn:(BOOL)on { 146 | [self setOn:on animated:NO]; 147 | } 148 | 149 | - (void)setOn:(BOOL)on animated:(BOOL)animated { 150 | if (animated) { 151 | [NSAnimationContext beginGrouping]; 152 | [CATransaction begin]; 153 | [CATransaction setAnimationDuration:0.3]; 154 | [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]]; 155 | } 156 | 157 | self.switchCover.frame = (NSRect){ .origin.x = on ? 0 : CGRectGetWidth(self.frame)/2, .size = { CGRectGetWidth(self.frame)/2, CGRectGetHeight(self.frame) } }; 158 | self.onLayer.opacity = on ? 1.f : 0.f; 159 | self.offLayer.opacity = on ? 0.f : 1.f; 160 | 161 | if (animated) { 162 | [CATransaction commit]; 163 | [NSAnimationContext endGrouping]; 164 | } 165 | 166 | [self willChangeValueForKey:@"on"]; 167 | _on = on; 168 | [self didChangeValueForKey:@"on"]; 169 | } 170 | 171 | - (void)resetCursorRects { 172 | [self addCursorRect:self.bounds cursor:NSCursor.pointingHandCursor]; 173 | } 174 | 175 | @end 176 | -------------------------------------------------------------------------------- /DotSnap/Defaults.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DSPLoadDotSnapAtStart 6 | 7 | DSPAddsTimestamp 8 | 9 | DSPAutosaveInputField 10 | 11 | DSPDefaultFilePathKey 12 | @default 13 | DSPDefaultFilenameTemplate 14 | MyScreenShotName 15 | DSPLaunchAtHotNoon 16 | 17 | DSPFilenameHistory 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /DotSnap/DotSnap-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | DotSnap 11 | CFBundleIdentifier 12 | com.vanschneider.dotsnap 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSApplicationCategoryType 26 | public.app-category.utilities 27 | LSMinimumSystemVersion 28 | ${MACOSX_DEPLOYMENT_TARGET} 29 | LSUIElement 30 | 31 | NSHumanReadableCopyright 32 | Copyright © 2013 Tobias van Schneider 33 | NSMainNibFile 34 | MainMenu 35 | NSPrincipalClass 36 | NSApplication 37 | 38 | 39 | -------------------------------------------------------------------------------- /DotSnap/DotSnap-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'Popup' target in the 'Popup' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import "NSColor+DSPExtensions.h" 11 | #import "DSPDefaults.h" 12 | #endif 13 | -------------------------------------------------------------------------------- /DotSnap/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /DotSnap/JNWAnimatableWindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013, Jonathan Willing. All rights reserved. 3 | Licensed under the MIT license 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 8 | to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 13 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 14 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 15 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 16 | IN THE SOFTWARE. 17 | */ 18 | 19 | 20 | #import 21 | 22 | // Allows for an extremely flexible manipulation of a static representation of the window. 23 | // 24 | // Since it uses a visual representation of the window, the window cannot be interacted with 25 | // while a transform is applied, nor is it automatically updated to reflect the window's state. 26 | @interface JNWAnimatableWindow : NSWindow 27 | 28 | // This layer can be transformed as much as desired. As soon as the property is first used an image 29 | // representation of the current window's state will be grabbed and used for the layer's contents. 30 | // 31 | // Because it is a static image, it will not reflect the state of the window if it changes. 32 | // If the window needs to change content while still having a transformed state, 33 | // call `-updateImageRepresentation` to update the backing image. 34 | // 35 | // Important note: This layer is optimized with a shadow path. If you need to change the bounds 36 | // or the size of the layer, you will need to update the shadow path yourself to reflect this change. 37 | @property (nonatomic, assign, readonly) CALayer *layer; 38 | 39 | // Destroys the layer and fake window. Only necessary for use if the layer is animated manually. 40 | // If the convenience methods are used below, calling this is not necessary as it is done automatically. 41 | - (void)destroyTransformingWindow; 42 | 43 | // Order a window out with an animation. The `animations` block is wrapped in a `CATransaction`, so implicit 44 | // animations will be enabled. Pass in nil for the timing function to default to ease-in-out. 45 | // 46 | // The layer and the extra window will be destroyed automatically after the animation completes. 47 | - (void)orderOutWithDuration:(CFTimeInterval)duration timing:(CAMediaTimingFunction *)timingFunction 48 | animations:(void (^)(CALayer *windowLayer))animations; 49 | 50 | // Order a window out with an animation, automatically cleaning up after completion. 51 | // 52 | // The delegate of the animation will be changed. 53 | - (void)orderOutWithAnimation:(CAAnimation *)animation; 54 | 55 | // Make a window key and visible with an animation. The setup block will be performed with implicit animations 56 | // disabled, so it is an ideal time to set the initial state for your animation. The `animations` block is wrapped 57 | // in a `CATransaction`, so implicit animations will be enabled. Pass in nil for the timing function to default to ease-in-out. 58 | // 59 | // The layer and the extra window will be destroyed automatically after the animation completes. 60 | - (void)makeKeyAndOrderFrontWithDuration:(CFTimeInterval)duration timing:(CAMediaTimingFunction *)timingFunction 61 | setup:(void (^)(CALayer *windowLayer))setup animations:(void (^)(CALayer *layer))animations; 62 | 63 | 64 | // Make a window key and visible with an animation, automatically cleaning up after completion. 65 | // 66 | // The delegate of the animation will be changed. 67 | // 68 | // The opacity of the layer will be set to the passed in opacity before it is shown. 69 | - (void)makeKeyAndOrderFrontWithAnimation:(CAAnimation *)animation initialOpacity:(CGFloat)opacity; 70 | 71 | // Sets the window to the frame specified using a layer The animation behavior is the same as 72 | // NSWindow's full-screen animation, which cross-fades between the initial and final state images. 73 | // 74 | // The layer and the extra window will be destroyed automatically after the animation completes. 75 | - (void)setFrame:(NSRect)frameRect withDuration:(CFTimeInterval)duration timing:(CAMediaTimingFunction *)timing; 76 | 77 | @end -------------------------------------------------------------------------------- /DotSnap/LIEffect.h: -------------------------------------------------------------------------------- 1 | // 2 | // LIEffect.h 3 | // WindowEffects 4 | // 5 | // Created by Mark Onyschuk on 17/07/09. 6 | // Copyright 2009 Lorem & Ipsum. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class CALayer; 12 | 13 | @interface LIEffect : NSObject { 14 | NSWindow *animationWindow; 15 | } 16 | 17 | #pragma mark - 18 | #pragma mark Setup 19 | - (id)initWithAnimationWindowFrame:(NSRect)aFrame; 20 | 21 | #pragma mark - 22 | #pragma mark Properties 23 | @property (readonly) NSView *animationView; 24 | @property (readonly) CALayer *animationLayer; 25 | @property (readonly) NSWindow *animationWindow; 26 | 27 | #pragma mark - 28 | #pragma mark Actions 29 | - (void)run; 30 | 31 | #pragma mark - 32 | #pragma mark Cleanup 33 | - (void)dealloc; 34 | 35 | @end 36 | 37 | @interface NSView (LIEffects) 38 | - (NSBitmapImageRep *)imageRep; 39 | @end 40 | 41 | extern NSRect LIRectToScreen(NSRect aRect, NSView *aView); 42 | extern NSRect LIRectFromScreen(NSRect aRect, NSView *aView); 43 | extern NSRect LIRectFromViewToView(NSRect aRect, NSView *fromView, NSView *toView); 44 | 45 | -------------------------------------------------------------------------------- /DotSnap/LIEffect.m: -------------------------------------------------------------------------------- 1 | // 2 | // LIEffect.m 3 | // WindowEffects 4 | // 5 | // Created by Mark Onyschuk on 17/07/09. 6 | // Copyright 2009 Lorem & Ipsum. All rights reserved. 7 | // 8 | 9 | #import "LIEffect.h" 10 | 11 | #import 12 | 13 | @implementation LIEffect 14 | 15 | #pragma mark - 16 | #pragma mark Setup 17 | - (id)initWithAnimationWindowFrame:(NSRect)aFrame { 18 | if ((self = [super init])) { 19 | animationWindow = [[NSWindow alloc] initWithContentRect:aFrame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; 20 | 21 | [animationWindow setOpaque:NO]; 22 | [animationWindow setHasShadow:NO]; 23 | [animationWindow setBackgroundColor:[NSColor clearColor]]; 24 | 25 | [animationWindow.contentView setWantsLayer:YES]; 26 | 27 | CATransform3D transform = CATransform3DIdentity; 28 | transform.m34 = 1.0 / -850; 29 | 30 | [CATransaction begin]; 31 | CALayer *layer = [animationWindow.contentView layer]; 32 | layer.sublayerTransform = transform; 33 | [CATransaction commit]; 34 | } 35 | 36 | return self; 37 | } 38 | 39 | #pragma mark - 40 | #pragma mark Properties 41 | - (NSWindow *)animationWindow { 42 | return animationWindow; 43 | } 44 | - (NSView *)animationView { 45 | return [animationWindow contentView]; 46 | } 47 | - (CALayer *)animationLayer { 48 | return [animationWindow.contentView layer]; 49 | } 50 | 51 | #pragma mark - 52 | #pragma mark Actions 53 | - (void)run { 54 | [animationWindow orderFront:self]; 55 | } 56 | 57 | #pragma mark - 58 | #pragma mark Cleanup 59 | - (void)dealloc { 60 | [animationWindow release], animationWindow = nil; 61 | [super dealloc]; 62 | } 63 | 64 | @end 65 | 66 | @implementation NSView (LIEffects) 67 | - (NSBitmapImageRep *)imageRep { 68 | BOOL visible = self.window.isVisible; 69 | NSRect oldFrame = self.window.frame; 70 | 71 | if (! visible) { 72 | NSDisableScreenUpdates(); 73 | [self.window setFrame:NSOffsetRect(oldFrame, -10000, -10000) display:NO]; 74 | [self.window orderFront:self]; 75 | } 76 | 77 | NSBitmapImageRep *rep = [self bitmapImageRepForCachingDisplayInRect:self.bounds]; 78 | [self cacheDisplayInRect:self.bounds toBitmapImageRep:rep]; 79 | 80 | if (! visible) { 81 | [self.window orderOut:self]; 82 | [self.window setFrame:oldFrame display:NO]; 83 | NSEnableScreenUpdates(); 84 | } 85 | 86 | return rep; 87 | } 88 | @end 89 | 90 | NSRect LIRectToScreen(NSRect aRect, NSView *aView) { 91 | aRect = [aView convertRect:aRect toView:nil]; 92 | aRect.origin = [aView.window convertBaseToScreen:aRect.origin]; 93 | return aRect; 94 | } 95 | NSRect LIRectFromScreen(NSRect aRect, NSView *aView) { 96 | aRect.origin = [aView.window convertScreenToBase:aRect.origin]; 97 | aRect = [aView convertRect:aRect fromView:nil]; 98 | return aRect; 99 | } 100 | 101 | NSRect LIRectFromViewToView(NSRect aRect, NSView *fromView, NSView *toView) { 102 | aRect = LIRectToScreen(aRect, fromView); 103 | aRect = LIRectFromScreen(aRect, toView); 104 | 105 | return aRect; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /DotSnap/LIFlipEffect.h: -------------------------------------------------------------------------------- 1 | // 2 | // LIFlipEffect.h 3 | // WindowEffects 4 | // 5 | // Created by Mark Onyschuk on 17/07/09. 6 | // Copyright 2009 Lorem & Ipsum. All rights reserved. 7 | // 8 | 9 | #import "LIEffect.h" 10 | 11 | @interface LIFlipEffect : LIEffect { 12 | NSUInteger done; 13 | NSWindow *fromWindow, *toWindow; 14 | NSBitmapImageRep *fromImage, *toImage; 15 | } 16 | 17 | #pragma mark - 18 | #pragma mark Setup 19 | - (id)initFromWindow:(NSWindow *)aFromWindow toWindow:(NSWindow *)aToWindow; 20 | - (id)initFromWindow:(NSWindow *)aFromWindow toWindow:(NSWindow *)aToWindow flag:(BOOL)flag; 21 | 22 | #pragma mark - 23 | #pragma mark PRoperties 24 | @property (readonly) NSWindow *fromWindow, *toWindow; 25 | 26 | #pragma mark - 27 | #pragma mark Actions 28 | - (void)run; 29 | 30 | #pragma mark - 31 | #pragma mark Cleanup 32 | - (void)dealloc; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /DotSnap/LIFlipEffect.m: -------------------------------------------------------------------------------- 1 | // 2 | // LIFlipEffect.m 3 | // WindowEffects 4 | // 5 | // Created by Mark Onyschuk on 17/07/09. 6 | // Copyright 2009 Lorem & Ipsum. All rights reserved. 7 | // 8 | 9 | #import "LIFlipEffect.h" 10 | #import 11 | 12 | @implementation LIFlipEffect 13 | 14 | - (id)initFromWindow:(NSWindow *)w1 toWindow:(NSWindow *)w2 { 15 | CGFloat maxWidth = MAX(NSWidth(w1.frame), NSWidth(w2.frame)); 16 | CGFloat maxHeight = MAX(NSHeight(w1.frame), NSHeight(w2.frame)); 17 | 18 | // add some slop for our rotation 19 | maxWidth += 2; 20 | maxHeight += 200; 21 | 22 | NSRect animationFrame; 23 | animationFrame.origin.x = NSMidX(w1.frame) - (maxWidth / 2); 24 | animationFrame.origin.y = NSMidY(w1.frame) - (maxHeight / 2); 25 | animationFrame.size.width = maxWidth; 26 | animationFrame.size.height = maxHeight; 27 | 28 | if ((self = [super initWithAnimationWindowFrame:animationFrame])) { 29 | fromWindow = [w1 retain]; 30 | toWindow = [w2 retain]; 31 | 32 | // fix window positions so that toWindow is positioned similarly to fromWindow 33 | 34 | NSRect fromFrame = fromWindow.frame; 35 | NSRect toFrame = toWindow.frame; 36 | 37 | toFrame.origin.x = NSMidX(fromFrame) - (NSWidth(toFrame) / 2); 38 | toFrame.origin.y = NSMaxY(fromFrame) - NSHeight(toFrame); 39 | 40 | [toWindow setFrame:toFrame display:NO]; 41 | } 42 | return self; 43 | } 44 | 45 | - (id)initFromWindow:(NSWindow *)w1 toWindow:(NSWindow *)w2 flag:(BOOL)flag { 46 | CGFloat maxWidth = MAX(NSWidth(w1.frame), NSWidth(w2.frame)); 47 | CGFloat maxHeight = MAX(NSHeight(w1.frame), NSHeight(w2.frame)); 48 | 49 | // add some slop for our rotation 50 | maxWidth += 2; 51 | maxHeight += 200; 52 | 53 | NSRect animationFrame; 54 | animationFrame.origin.x = NSMidX(w1.frame) - (maxWidth / 2); 55 | animationFrame.origin.y = NSMidY(w1.frame) - (maxHeight / 2) + (flag ? -100 : -200); 56 | animationFrame.size.width = maxWidth; 57 | animationFrame.size.height = maxHeight; 58 | 59 | if ((self = [super initWithAnimationWindowFrame:animationFrame])) { 60 | fromWindow = [w1 retain]; 61 | toWindow = [w2 retain]; 62 | 63 | // fix window positions so that toWindow is positioned similarly to fromWindow 64 | 65 | NSRect fromFrame = fromWindow.frame; 66 | NSRect toFrame = toWindow.frame; 67 | 68 | toFrame.origin.x = NSMidX(fromFrame) - (NSWidth(toFrame) / 2); 69 | toFrame.origin.y = NSMaxY(fromFrame) - NSHeight(toFrame); 70 | 71 | [toWindow setFrame:toFrame display:flag]; 72 | } 73 | return self; 74 | } 75 | 76 | #pragma mark - 77 | #pragma mark Properties 78 | @synthesize fromWindow, toWindow; 79 | 80 | #pragma mark - 81 | #pragma mark Actions 82 | 83 | #define PI 3.14159 84 | 85 | - (void)run { 86 | NSView *fromView, *toView; 87 | fromView = [fromWindow.contentView superview]; 88 | toView = [toWindow.contentView superview]; 89 | 90 | fromImage = [fromView.imageRep retain]; 91 | 92 | [toWindow setAlphaValue:0.0]; 93 | [toWindow makeKeyAndOrderFront:self]; 94 | 95 | toImage = [toView.imageRep retain]; 96 | 97 | CATransform3D fromStart = CATransform3DMakeRotation(0, 0, 1, 0); 98 | CATransform3D fromEnd = CATransform3DMakeRotation(PI, 0, 1, 0); 99 | 100 | CATransform3D toStart = CATransform3DMakeRotation(PI, 0, 1, 0); 101 | CATransform3D toEnd = CATransform3DMakeRotation(PI * 2, 0, 1, 0); 102 | 103 | CALayer *fromLayer = [CALayer layer]; 104 | fromLayer.contents = (id)fromImage.CGImage; 105 | fromLayer.frame = NSRectToCGRect(LIRectFromViewToView(fromView.frame, fromView, self.animationView)); 106 | fromLayer.transform = fromStart; 107 | fromLayer.doubleSided = NO; 108 | 109 | [self.animationLayer addSublayer:fromLayer]; 110 | 111 | CALayer *toLayer = [CALayer layer]; 112 | toLayer.contents = (id)toImage.CGImage; 113 | toLayer.frame = NSRectToCGRect(LIRectFromViewToView(toView.frame, toView, self.animationView)); 114 | toLayer.transform = toStart; 115 | toLayer.doubleSided = NO; 116 | 117 | [self.animationLayer addSublayer:toLayer]; 118 | 119 | [super run]; 120 | 121 | [fromWindow orderOut:self]; 122 | 123 | CABasicAnimation *fromAnimation = [[CABasicAnimation animationWithKeyPath:@"transform"]retain]; 124 | fromAnimation.duration = 0.35; 125 | fromAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 126 | fromAnimation.toValue = [NSValue valueWithBytes:&fromEnd objCType:@encode(CATransform3D)]; 127 | 128 | fromAnimation.fillMode = kCAFillModeForwards; 129 | fromAnimation.removedOnCompletion = NO; 130 | 131 | CABasicAnimation *toAnimation = [[CABasicAnimation animationWithKeyPath:@"transform"]retain]; 132 | toAnimation.duration = 0.35; 133 | toAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 134 | toAnimation.toValue = [NSValue valueWithBytes:&toEnd objCType:@encode(CATransform3D)]; 135 | 136 | toAnimation.fillMode = kCAFillModeForwards; 137 | toAnimation.removedOnCompletion = NO; 138 | 139 | toAnimation.delegate = self; 140 | 141 | [CATransaction begin]; 142 | [fromLayer addAnimation:fromAnimation forKey:@"flip"]; 143 | [toLayer addAnimation:toAnimation forKey:@"flip"]; 144 | [CATransaction commit]; 145 | 146 | done = 0; 147 | } 148 | 149 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 150 | if (flag) { 151 | [toWindow setAlphaValue:1.0]; 152 | [self.animationWindow orderOut:self]; 153 | 154 | [self release]; 155 | } 156 | } 157 | 158 | #pragma mark - 159 | #pragma mark Cleanup 160 | - (void)dealloc { 161 | [fromWindow release], fromWindow = nil; 162 | [toWindow release], toWindow = nil; 163 | 164 | [fromImage release], fromImage = nil; 165 | [toImage release], toImage = nil; 166 | 167 | [super dealloc]; 168 | } 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /DotSnap/NSColor+DSPExtensions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSColor+DSPExtensions.h 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/25/13. 6 | // 7 | // 8 | 9 | @interface NSColor (DSPCGColor) 10 | 11 | // 12 | // The Quartz color reference that corresponds to the receiver's color. 13 | // 14 | @property (nonatomic, readonly) CGColorRef dsp_CGColor; 15 | 16 | // 17 | // Converts a Quartz color reference to its NSColor equivalent. 18 | // 19 | + (NSColor *)dsp_colorWithCGColor:(CGColorRef)color; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /DotSnap/NSColor+DSPExtensions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSColor+DSPExtensions.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 7/25/13. 6 | // 7 | // 8 | 9 | #import "NSColor+DSPExtensions.h" 10 | 11 | @implementation NSColor (CGColor) 12 | 13 | - (CGColorRef)dsp_CGColor { 14 | const NSInteger numberOfComponents = [self numberOfComponents]; 15 | CGFloat components[numberOfComponents]; 16 | CGColorSpaceRef colorSpace = [[self colorSpace] CGColorSpace]; 17 | 18 | [self getComponents:(CGFloat *)&components]; 19 | 20 | return (CGColorRef)[(id)CGColorCreate(colorSpace, components) autorelease]; 21 | } 22 | 23 | + (NSColor *)dsp_colorWithCGColor:(CGColorRef)CGColor { 24 | if (CGColor == NULL) return nil; 25 | return [NSColor colorWithCIColor:[CIColor colorWithCGColor:CGColor]]; 26 | } 27 | 28 | @end -------------------------------------------------------------------------------- /DotSnap/NSString+SymlinksAndAliases.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SymlinksAndAliases.h 3 | // ResolvePath 4 | // 5 | // Created by Matt Gallagher on 2010/02/22. 6 | // Copyright 2010 Matt Gallagher. All rights reserved. 7 | // 8 | // Permission is given to use this source code file, free of charge, in any 9 | // project, commercial or otherwise, entirely at your risk, with the condition 10 | // that any redistribution (in part or whole) of source code must retain 11 | // this copyright and permission notice. Attribution in compiled projects is 12 | // appreciated but not required. 13 | // 14 | 15 | #import 16 | 17 | @interface NSString (SymlinksAndAliases) 18 | 19 | - (NSString *)stringByResolvingSymlinksAndAliases; 20 | - (NSString *)stringByIterativelyResolvingSymlinkOrAlias; 21 | 22 | - (NSString *)stringByResolvingSymlink; 23 | - (NSString *)stringByConditionallyResolvingSymlink; 24 | 25 | - (NSString *)stringByResolvingAlias; 26 | - (NSString *)stringByConditionallyResolvingAlias; 27 | 28 | @end -------------------------------------------------------------------------------- /DotSnap/NSString+SymlinksAndAliases.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SymlinksAndAliases.m 3 | // ResolvePath 4 | // 5 | // Created by Matt Gallagher on 2010/02/22. 6 | // Copyright 2010 Matt Gallagher. All rights reserved. 7 | // 8 | // Permission is given to use this source code file, free of charge, in any 9 | // project, commercial or otherwise, entirely at your risk, with the condition 10 | // that any redistribution (in part or whole) of source code must retain 11 | // this copyright and permission notice. Attribution in compiled projects is 12 | // appreciated but not required. 13 | // 14 | 15 | #import "NSString+SymlinksAndAliases.h" 16 | #include 17 | 18 | @implementation NSString (SymlinksAndAliases) 19 | 20 | // 21 | // stringByResolvingSymlinksAndAliases 22 | // 23 | // Tries to make a standardized, absolute path from the current string, 24 | // resolving any aliases or symlinks in the path. 25 | // 26 | // returns the fully resolved path (if possible) or nil (if resolution fails) 27 | // 28 | - (NSString *)stringByResolvingSymlinksAndAliases { 29 | // 30 | // Convert to a standardized absolute path. 31 | // 32 | NSString *path = [self stringByStandardizingPath]; 33 | if (![path hasPrefix:@"/"]) { 34 | return nil; 35 | } 36 | 37 | // 38 | // Break into components. First component ("/") needs no resolution, so 39 | // we only need to handle subsequent components. 40 | // 41 | NSArray *pathComponents = [path pathComponents]; 42 | NSString *resolvedPath = [pathComponents objectAtIndex:0]; 43 | pathComponents = [pathComponents 44 | subarrayWithRange:NSMakeRange(1, [pathComponents count] - 1)]; 45 | 46 | // 47 | // Process all remaining components. 48 | // 49 | for (NSString *component in pathComponents) { 50 | resolvedPath = [resolvedPath stringByAppendingPathComponent:component]; 51 | resolvedPath = [resolvedPath stringByIterativelyResolvingSymlinkOrAlias]; 52 | if (!resolvedPath) { 53 | return nil; 54 | } 55 | } 56 | 57 | return resolvedPath; 58 | } 59 | 60 | // 61 | // stringByIterativelyResolvingSymlinkOrAlias 62 | // 63 | // Resolves the path where the final component could be a symlink and any 64 | // component could be an alias. 65 | // 66 | // returns the resolved path 67 | // 68 | - (NSString *)stringByIterativelyResolvingSymlinkOrAlias { 69 | NSString *path = self; 70 | NSString *aliasTarget = nil; 71 | struct stat fileInfo; 72 | 73 | // 74 | // Use lstat to determine if the file is a symlink 75 | // 76 | if (lstat([[NSFileManager defaultManager] 77 | fileSystemRepresentationWithPath:path], &fileInfo) < 0) { 78 | return nil; 79 | } 80 | 81 | // 82 | // While the file is a symlink or we can resolve aliases in the path, 83 | // keep resolving. 84 | // 85 | while (S_ISLNK(fileInfo.st_mode) || 86 | (!S_ISDIR(fileInfo.st_mode) && 87 | (aliasTarget = [path stringByConditionallyResolvingAlias]) != nil)) 88 | { 89 | if (S_ISLNK(fileInfo.st_mode)) 90 | { 91 | // 92 | // Resolve the symlink final component in the path 93 | // 94 | NSString *symlinkPath = [path stringByConditionallyResolvingSymlink]; 95 | if (!symlinkPath) 96 | { 97 | return nil; 98 | } 99 | path = symlinkPath; 100 | } 101 | else 102 | { 103 | path = aliasTarget; 104 | } 105 | 106 | // 107 | // Use lstat to determine if the file is a symlink 108 | // 109 | if (lstat([[NSFileManager defaultManager] 110 | fileSystemRepresentationWithPath:path], &fileInfo) < 0) 111 | { 112 | path = nil; 113 | continue; 114 | } 115 | } 116 | 117 | return path; 118 | } 119 | 120 | // 121 | // stringByResolvingAlias 122 | // 123 | // Attempts to resolve the single alias at the end of the path. 124 | // 125 | // returns the resolved alias or self if path wasn't an alias or couldn't be 126 | // resolved. 127 | // 128 | - (NSString *)stringByResolvingAlias 129 | { 130 | NSString *aliasTarget = [self stringByConditionallyResolvingAlias]; 131 | if (aliasTarget) 132 | { 133 | return aliasTarget; 134 | } 135 | return self; 136 | } 137 | 138 | // 139 | // stringByResolvingSymlink 140 | // 141 | // Attempts to resolve the single symlink at the end of the path. 142 | // 143 | // returns the resolved path or self if path wasn't a symlink or couldn't be 144 | // resolved. 145 | // 146 | - (NSString *)stringByResolvingSymlink 147 | { 148 | NSString *symlinkTarget = [self stringByConditionallyResolvingSymlink]; 149 | if (symlinkTarget) 150 | { 151 | return symlinkTarget; 152 | } 153 | return self; 154 | } 155 | 156 | // 157 | // stringByConditionallyResolvingSymlink 158 | // 159 | // Attempt to resolve the symlink pointed to by the path. 160 | // 161 | // returns the resolved path (if it was a symlink and resolution is possible) 162 | // otherwise nil 163 | // 164 | - (NSString *)stringByConditionallyResolvingSymlink 165 | { 166 | // 167 | // Resolve the symlink final component in the path 168 | // 169 | NSString *symlinkPath = 170 | [[NSFileManager defaultManager] 171 | destinationOfSymbolicLinkAtPath:self 172 | error:NULL]; 173 | if (!symlinkPath) 174 | { 175 | return nil; 176 | } 177 | if (![symlinkPath hasPrefix:@"/"]) 178 | { 179 | // 180 | // For relative path symlinks (common case), remove the 181 | // relative links 182 | // 183 | symlinkPath = 184 | [[self stringByDeletingLastPathComponent] 185 | stringByAppendingPathComponent:symlinkPath]; 186 | symlinkPath = [symlinkPath stringByStandardizingPath]; 187 | } 188 | return symlinkPath; 189 | } 190 | 191 | // 192 | // stringByConditionallyResolvingAlias 193 | // 194 | // Attempt to resolve the alias pointed to by the path. 195 | // 196 | // returns the resolved path (if it was an alias and resolution is possible) 197 | // otherwise nil 198 | // 199 | - (NSString *)stringByConditionallyResolvingAlias 200 | { 201 | NSString *resolvedPath = nil; 202 | 203 | CFURLRef url = CFURLCreateWithFileSystemPath 204 | (kCFAllocatorDefault, (CFStringRef)self, kCFURLPOSIXPathStyle, NO); 205 | if (url != NULL) 206 | { 207 | FSRef fsRef; 208 | if (CFURLGetFSRef(url, &fsRef)) 209 | { 210 | CFErrorRef err; 211 | CFDataRef bookmarkDataRef = CFURLCreateBookmarkDataFromFile(kCFAllocatorDefault, url, &err); 212 | if (err == NULL) 213 | { 214 | Boolean wasAliased; 215 | CFURLRef resolvedUrl = CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, bookmarkDataRef, kCFBookmarkResolutionWithoutUIMask, NULL, 0, &wasAliased, &err); 216 | if (resolvedUrl != NULL) 217 | { 218 | resolvedPath = 219 | [(id)NSMakeCollectable(CFURLCopyFileSystemPath(resolvedUrl, kCFURLPOSIXPathStyle)) 220 | autorelease]; 221 | CFRelease(resolvedUrl); 222 | } 223 | } 224 | CFRelease(bookmarkDataRef); 225 | } 226 | CFRelease(url); 227 | } 228 | 229 | return resolvedPath; 230 | } 231 | 232 | @end -------------------------------------------------------------------------------- /DotSnap/PFMoveApplication.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFMoveApplication.h, version 1.7.2 3 | // LetsMove 4 | // 5 | // Created by Andy Kim at Potion Factory LLC on 9/17/09 6 | // 7 | // The contents of this file are dedicated to the public domain. 8 | 9 | #import 10 | 11 | void PFMoveToApplicationsFolderIfNecessary(void); 12 | -------------------------------------------------------------------------------- /DotSnap/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /DotSnap/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DotSnap 4 | // 5 | // Created by Robert Widmann on 8/25/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /DotSnapTests/DotSnapTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.vanschneider.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /DotSnapTests/DotSnapTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // DotSnapTests.m 3 | // DotSnapTests 4 | // 5 | // Created by Robert Widmann on 8/25/13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface DotSnapTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation DotSnapTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /DotSnapTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /External/Lagrangian/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | xcuserdata 4 | *.mode* 5 | *.pbxuser 6 | *.xcuserdatad 7 | -------------------------------------------------------------------------------- /External/Lagrangian/External/RXPreprocessing/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build 3 | xcuserdata 4 | *.mode* 5 | *.pbxuser 6 | *.xcuserdatad 7 | -------------------------------------------------------------------------------- /External/Lagrangian/External/RXPreprocessing/README.md: -------------------------------------------------------------------------------- 1 | # RXPreprocessing 2 | 3 | A variety of C preprocessor-based utilities useful for a variety of purposes. Unless otherwise noted, these do not have any dependencies outside of the C preprocessor itself. 4 | 5 | 6 | ## RXPaste.h 7 | 8 | Simple token pasting, like grandma used to make. Primarily exists to support the other utilities. 9 | 10 | 11 | ## RXCount.h 12 | 13 | Counting the arguments passed to variadic macros. Primarily exists to support the other utilities. 14 | 15 | 16 | ## RXFold.h 17 | 18 | Folding a macro over a variadic list. This can make it easier to build variadic macros which do not need to defer their arguments to a function or method for processing. 19 | 20 | This is incredibly useful when you want to do something using a macro over a variable number of arguments: 21 | 22 | #define add(x, y) y + x 23 | int sum = rx_fold(add, 0, 1, 2, 3, 4, 5); → int sum = 0 + 1 + 2 + 3 + 4 + 5 + 6; 24 | 25 | 26 | ## RXInterpolation.h 27 | 28 | Preprocessor-based interpolation of NSStrings. This allows you to interpolate complex strings without requiring you to remember which format specifier you want for each. You can still, however, customize the formatting of each individual parameter to specify precision or width. 29 | 30 | Requires Foundation, and uses `__attribute__((overloadable))` functions to generate the type specifiers for the format strings, since clang’s support for C11’s `_Generic` expressions appears to have difficulty when both `char *` and `default` options are added. 31 | 32 | Some things you can do with it: 33 | 34 | NSUInteger waysILoveThee = kLotsOfWays; 35 | RXLog(@"How do I love thee? Let me count the ways: ", waysILoveThee); 36 | 37 | Inspired by @pgor, who nerd-sniped me one fine morning. 38 | -------------------------------------------------------------------------------- /External/Lagrangian/External/RXPreprocessing/RXCount.h: -------------------------------------------------------------------------------- 1 | #ifndef RX_COUNT_INCLUDED 2 | #define RX_COUNT_INCLUDED 3 | 4 | #pragma mark Variadic counting 5 | 6 | #define rx_count(...) \ 7 | rx_count_implementation(_0, ## __VA_ARGS__, rx_reverse_ordinals()) 8 | 9 | #define rx_count_implementation(...) \ 10 | rx_ordinals(__VA_ARGS__) 11 | 12 | #define rx_ordinals( \ 13 | _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, \ 14 | _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, \ 15 | _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, \ 16 | _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, \ 17 | _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, \ 18 | _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, \ 19 | _60, _61, _62, _63, N,...) N 20 | 21 | #define rx_reverse_ordinals() \ 22 | 63, 62, 61, 60, \ 23 | 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \ 24 | 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ 25 | 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \ 26 | 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \ 27 | 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \ 28 | 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /External/Lagrangian/External/RXPreprocessing/RXFold.h: -------------------------------------------------------------------------------- 1 | #ifndef RX_FOLD_INCLUDED 2 | #define RX_FOLD_INCLUDED 3 | 4 | #include "RXCount.h" 5 | #include "RXPaste.h" 6 | #include "_RXFold.h" 7 | 8 | /** 9 | `rx_fold(f, initial, ...)` 10 | 11 | Folds the varargs with the macro specified as the first argument. This macro must take two arguments: the first being the result thus far, and the second being the current element in the varargs. `initial` provides the value for the first invocation of `f` (when there are no results yet provided). 12 | 13 | Use it like this: 14 | 15 | #define add(x, y) y + x 16 | int sum = rx_fold(add, 0, 1, 2, 3, 4, 5); → int sum = 0 + 1 + 2 + 3 + 4 + 5 + 6; 17 | */ 18 | 19 | #define rx_fold(f, initial, ...) \ 20 | _rx_fold(f, initial, rx_count(__VA_ARGS__), __VA_ARGS__) 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /External/Lagrangian/External/RXPreprocessing/RXInterpolation.h: -------------------------------------------------------------------------------- 1 | #ifndef RX_INTERPOLATION_INCLUDED 2 | #define RX_INTERPOLATION_INCLUDED 3 | 4 | #import 5 | #import "RXFold.h" 6 | #import "_RXInterpolation.h" 7 | 8 | /** 9 | `rx_q(...)` 10 | 11 | Formats and concatenates its arguments as an NSString according to their types. For example, you can use this in places where you would have used +[NSString stringWithFormat:] and had to decide the types of the parameters for the format string manually. 12 | 13 | NSArray *objects = …; 14 | NSUInteger index = …; 15 | NSString *formatted = rx_q("The object at index ", index, " is: ", objects[index]); 16 | 17 | `rx_q` formats its arguments individually with `rx_f` (with no format qualifiers), and thus can format any type which `rx_f` supports. 18 | */ 19 | 20 | #define rx_q(...) \ 21 | [@[rx_fold(_rx_q_format_each, , __VA_ARGS__)] componentsJoinedByString:@""] 22 | 23 | 24 | /** 25 | `rx_f(format, value)` 26 | 27 | Formats `value` as an NSString according to its type and the qualifiers specified in format. For example, `rx_f(, "")` will produce an empty NSString, while rx_f(4., "") will produce a four-space string. 28 | 29 | `rx_f` can be used within `rx_q` to customize the formatting of a single value within the longer interpolation. 30 | 31 | `rx_f` can format values of the following types: 32 | 33 | - signed and unsigned integers, including int32_t, uint32_t, int64_t, uint64_t, NSInteger, and NSUInteger, with smaller integers automatically getting promoted to 32-bit by C’s rules 34 | - doubles and floats 35 | - Objective-C objects 36 | - C strings 37 | - pointers, which are formatted with %p (thanks to @boredzo for the correction) 38 | */ 39 | 40 | #define rx_f(format, value) \ 41 | (^NSString *{ \ 42 | __typeof__(value) _rx_cached_value = (value); \ 43 | return [NSString stringWithFormat:[NSString stringWithFormat:@"%%%s%@", #format, _rx_format_type_specifier_for_value(_rx_cached_value)], _rx_cached_value]; \ 44 | })() 45 | 46 | 47 | /** 48 | `RXLog(...)` 49 | 50 | Prints a logging message whose parameters are formatted with `rx_q`. Uses NSLog to actually print the message. 51 | */ 52 | #define RXLog(...) \ 53 | NSLog(@"%@", rx_q(__VA_ARGS__)) 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /External/Lagrangian/External/RXPreprocessing/RXPaste.h: -------------------------------------------------------------------------------- 1 | #ifndef RX_PASTE_INCLUDED 2 | #define RX_PASTE_INCLUDED 3 | 4 | /** 5 | `rx_paste(a, b)` 6 | 7 | Pastes the tokens a and b together into a single token. 8 | */ 9 | 10 | #define rx_paste(_1, _2) \ 11 | _rx_paste(_1, _2) 12 | 13 | #define _rx_paste(_1, _2) \ 14 | _1##_2 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /External/Lagrangian/External/RXPreprocessing/RXPreprocessingTests.m: -------------------------------------------------------------------------------- 1 | // clang -fmacro-backtrace-limit=0 -framework Foundation -o RXPreprocessingTests RXPreprocessingTests.m && ./RXPreprocessingTests && rm ./RXPreprocessingTests 2 | 3 | #import 4 | #import "RXInterpolation.h" 5 | 6 | int main(int argc, const char *argv[]) { 7 | @autoreleasepool { 8 | RXLog(@"look! I can log things!\n", @[@"Things like this!\n"][0], "And things like this too!"); 9 | } 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /External/Lagrangian/External/RXPreprocessing/_RXFold.h: -------------------------------------------------------------------------------- 1 | #ifndef _RX_FOLD_INCLUDED 2 | #define _RX_FOLD_INCLUDED 3 | 4 | #define _rx_fold(f, initial, n, ...) \ 5 | rx_paste(_rx_fold_, n)(f, initial, __VA_ARGS__) 6 | 7 | // ruby -e '2.upto(64).each {|n| puts "#define _rx_fold_#{n}(f, initial, each, ...)\t\tf(_rx_fold_#{n-1}(f, initial, __VA_ARGS__), each)"}' 8 | 9 | #define _rx_fold_1(f, initial, each) f(initial, each) 10 | #define _rx_fold_2(f, initial, each, ...) f(_rx_fold_1(f, initial, __VA_ARGS__), each) 11 | #define _rx_fold_3(f, initial, each, ...) f(_rx_fold_2(f, initial, __VA_ARGS__), each) 12 | #define _rx_fold_4(f, initial, each, ...) f(_rx_fold_3(f, initial, __VA_ARGS__), each) 13 | #define _rx_fold_5(f, initial, each, ...) f(_rx_fold_4(f, initial, __VA_ARGS__), each) 14 | #define _rx_fold_6(f, initial, each, ...) f(_rx_fold_5(f, initial, __VA_ARGS__), each) 15 | #define _rx_fold_7(f, initial, each, ...) f(_rx_fold_6(f, initial, __VA_ARGS__), each) 16 | #define _rx_fold_8(f, initial, each, ...) f(_rx_fold_7(f, initial, __VA_ARGS__), each) 17 | #define _rx_fold_9(f, initial, each, ...) f(_rx_fold_8(f, initial, __VA_ARGS__), each) 18 | #define _rx_fold_10(f, initial, each, ...) f(_rx_fold_9(f, initial, __VA_ARGS__), each) 19 | #define _rx_fold_11(f, initial, each, ...) f(_rx_fold_10(f, initial, __VA_ARGS__), each) 20 | #define _rx_fold_12(f, initial, each, ...) f(_rx_fold_11(f, initial, __VA_ARGS__), each) 21 | #define _rx_fold_13(f, initial, each, ...) f(_rx_fold_12(f, initial, __VA_ARGS__), each) 22 | #define _rx_fold_14(f, initial, each, ...) f(_rx_fold_13(f, initial, __VA_ARGS__), each) 23 | #define _rx_fold_15(f, initial, each, ...) f(_rx_fold_14(f, initial, __VA_ARGS__), each) 24 | #define _rx_fold_16(f, initial, each, ...) f(_rx_fold_15(f, initial, __VA_ARGS__), each) 25 | #define _rx_fold_17(f, initial, each, ...) f(_rx_fold_16(f, initial, __VA_ARGS__), each) 26 | #define _rx_fold_18(f, initial, each, ...) f(_rx_fold_17(f, initial, __VA_ARGS__), each) 27 | #define _rx_fold_19(f, initial, each, ...) f(_rx_fold_18(f, initial, __VA_ARGS__), each) 28 | #define _rx_fold_20(f, initial, each, ...) f(_rx_fold_19(f, initial, __VA_ARGS__), each) 29 | #define _rx_fold_21(f, initial, each, ...) f(_rx_fold_20(f, initial, __VA_ARGS__), each) 30 | #define _rx_fold_22(f, initial, each, ...) f(_rx_fold_21(f, initial, __VA_ARGS__), each) 31 | #define _rx_fold_23(f, initial, each, ...) f(_rx_fold_22(f, initial, __VA_ARGS__), each) 32 | #define _rx_fold_24(f, initial, each, ...) f(_rx_fold_23(f, initial, __VA_ARGS__), each) 33 | #define _rx_fold_25(f, initial, each, ...) f(_rx_fold_24(f, initial, __VA_ARGS__), each) 34 | #define _rx_fold_26(f, initial, each, ...) f(_rx_fold_25(f, initial, __VA_ARGS__), each) 35 | #define _rx_fold_27(f, initial, each, ...) f(_rx_fold_26(f, initial, __VA_ARGS__), each) 36 | #define _rx_fold_28(f, initial, each, ...) f(_rx_fold_27(f, initial, __VA_ARGS__), each) 37 | #define _rx_fold_29(f, initial, each, ...) f(_rx_fold_28(f, initial, __VA_ARGS__), each) 38 | #define _rx_fold_30(f, initial, each, ...) f(_rx_fold_29(f, initial, __VA_ARGS__), each) 39 | #define _rx_fold_31(f, initial, each, ...) f(_rx_fold_30(f, initial, __VA_ARGS__), each) 40 | #define _rx_fold_32(f, initial, each, ...) f(_rx_fold_31(f, initial, __VA_ARGS__), each) 41 | #define _rx_fold_33(f, initial, each, ...) f(_rx_fold_32(f, initial, __VA_ARGS__), each) 42 | #define _rx_fold_34(f, initial, each, ...) f(_rx_fold_33(f, initial, __VA_ARGS__), each) 43 | #define _rx_fold_35(f, initial, each, ...) f(_rx_fold_34(f, initial, __VA_ARGS__), each) 44 | #define _rx_fold_36(f, initial, each, ...) f(_rx_fold_35(f, initial, __VA_ARGS__), each) 45 | #define _rx_fold_37(f, initial, each, ...) f(_rx_fold_36(f, initial, __VA_ARGS__), each) 46 | #define _rx_fold_38(f, initial, each, ...) f(_rx_fold_37(f, initial, __VA_ARGS__), each) 47 | #define _rx_fold_39(f, initial, each, ...) f(_rx_fold_38(f, initial, __VA_ARGS__), each) 48 | #define _rx_fold_40(f, initial, each, ...) f(_rx_fold_39(f, initial, __VA_ARGS__), each) 49 | #define _rx_fold_41(f, initial, each, ...) f(_rx_fold_40(f, initial, __VA_ARGS__), each) 50 | #define _rx_fold_42(f, initial, each, ...) f(_rx_fold_41(f, initial, __VA_ARGS__), each) 51 | #define _rx_fold_43(f, initial, each, ...) f(_rx_fold_42(f, initial, __VA_ARGS__), each) 52 | #define _rx_fold_44(f, initial, each, ...) f(_rx_fold_43(f, initial, __VA_ARGS__), each) 53 | #define _rx_fold_45(f, initial, each, ...) f(_rx_fold_44(f, initial, __VA_ARGS__), each) 54 | #define _rx_fold_46(f, initial, each, ...) f(_rx_fold_45(f, initial, __VA_ARGS__), each) 55 | #define _rx_fold_47(f, initial, each, ...) f(_rx_fold_46(f, initial, __VA_ARGS__), each) 56 | #define _rx_fold_48(f, initial, each, ...) f(_rx_fold_47(f, initial, __VA_ARGS__), each) 57 | #define _rx_fold_49(f, initial, each, ...) f(_rx_fold_48(f, initial, __VA_ARGS__), each) 58 | #define _rx_fold_50(f, initial, each, ...) f(_rx_fold_49(f, initial, __VA_ARGS__), each) 59 | #define _rx_fold_51(f, initial, each, ...) f(_rx_fold_50(f, initial, __VA_ARGS__), each) 60 | #define _rx_fold_52(f, initial, each, ...) f(_rx_fold_51(f, initial, __VA_ARGS__), each) 61 | #define _rx_fold_53(f, initial, each, ...) f(_rx_fold_52(f, initial, __VA_ARGS__), each) 62 | #define _rx_fold_54(f, initial, each, ...) f(_rx_fold_53(f, initial, __VA_ARGS__), each) 63 | #define _rx_fold_55(f, initial, each, ...) f(_rx_fold_54(f, initial, __VA_ARGS__), each) 64 | #define _rx_fold_56(f, initial, each, ...) f(_rx_fold_55(f, initial, __VA_ARGS__), each) 65 | #define _rx_fold_57(f, initial, each, ...) f(_rx_fold_56(f, initial, __VA_ARGS__), each) 66 | #define _rx_fold_58(f, initial, each, ...) f(_rx_fold_57(f, initial, __VA_ARGS__), each) 67 | #define _rx_fold_59(f, initial, each, ...) f(_rx_fold_58(f, initial, __VA_ARGS__), each) 68 | #define _rx_fold_60(f, initial, each, ...) f(_rx_fold_59(f, initial, __VA_ARGS__), each) 69 | #define _rx_fold_61(f, initial, each, ...) f(_rx_fold_60(f, initial, __VA_ARGS__), each) 70 | #define _rx_fold_62(f, initial, each, ...) f(_rx_fold_61(f, initial, __VA_ARGS__), each) 71 | #define _rx_fold_63(f, initial, each, ...) f(_rx_fold_62(f, initial, __VA_ARGS__), each) 72 | #define _rx_fold_64(f, initial, each, ...) f(_rx_fold_63(f, initial, __VA_ARGS__), each) 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /External/Lagrangian/External/RXPreprocessing/_RXInterpolation.h: -------------------------------------------------------------------------------- 1 | #ifndef _RX_INTERPOLATION_INCLUDED 2 | #define _RX_INTERPOLATION_INCLUDED 3 | 4 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(NSUInteger x) { return @"lu"; } 5 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(NSInteger x) { return @"li"; } 6 | 7 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(uint64_t x) { return @"lu"; } 8 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(int64_t x) { return @"li"; } 9 | 10 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(uint32_t x) { return @"u"; } 11 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(int32_t x) { return @"i"; } 12 | 13 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(void *x) { return @"p"; } 14 | 15 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(double x) { return @"f"; } 16 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(float x) { return @"f"; } 17 | 18 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(char *x) { return @"s"; } 19 | 20 | __attribute__((overloadable)) static inline NSString *_rx_format_type_specifier_for_value(id x) { return @"@"; } 21 | 22 | #define _rx_value(x) \ 23 | (x) 24 | 25 | #define _rx_q_format_each(memo, each) rx_f(, each), memo 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian.xcodeproj/xcshareddata/xcschemes/Lagrangian (Mac OS X dynamic library).xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | 67 | 68 | 69 | 70 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian.xcodeproj/xcshareddata/xcschemes/Lagrangian (Mac OS X framework).xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 69 | 70 | 71 | 72 | 81 | 82 | 83 | 84 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian.xcodeproj/xcshareddata/xcschemes/Lagrangian (iOS static framework).xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian.xcodeproj/xcshareddata/xcschemes/Test runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 | 67 | 68 | 74 | 75 | 76 | 77 | 78 | 79 | 85 | 86 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Assertions.h: -------------------------------------------------------------------------------- 1 | // L3Assertions.h 2 | // Created by Rob Rix on 2012-11-10. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | #import 7 | #import 8 | 9 | #pragma mark Assertions 10 | 11 | #define l3_sourceReference(_subject, _subjectSource, _patternSource) \ 12 | [L3SourceReference referenceWithFile:@"" __FILE__ line:__LINE__ subjectSource:@"" _subjectSource subject:_subject patternSource:@"" _patternSource] 13 | 14 | #define l3_assert(subject, pattern) \ 15 | ^bool{ \ 16 | id subject_ = l3_to_object(subject); \ 17 | L3Pattern pattern_ = l3_to_pattern(pattern); \ 18 | return [self assertThat:subject_ matches:pattern_ sourceReference:l3_sourceReference(subject_, #subject, #pattern) eventObserver:test.eventObserver]; \ 19 | }() 20 | 21 | 22 | #pragma mark Equality patterns 23 | 24 | #define l3_not(...) \ 25 | (^bool(id x){ return !l3_to_pattern(__VA_ARGS__)(x); }) 26 | 27 | #define l3_is(...) (__VA_ARGS__) 28 | #define l3_equalTo(...) (__VA_ARGS__) 29 | #define l3_equals(...) (__VA_ARGS__) 30 | #define l3_equalsWithEpsilon(x, y) l3_to_pattern_f(x, y) 31 | 32 | 33 | #pragma mark Classification patterns 34 | 35 | #define l3_isKindOfClass(class) \ 36 | (^bool(id x){ return [x isKindOfClass:class]; }) 37 | 38 | 39 | #pragma mark Comparison patterns 40 | 41 | #define l3_ordered(object, ordering) \ 42 | (^bool(id x){ return [x compare:l3_to_object(object)] == ordering; }) 43 | #define l3_ordered_or_same(object, ordering) \ 44 | (^bool(id x){ NSComparisonResult comparison = [x compare:l3_to_object(object)]; return (comparison == ordering) || (comparison == NSOrderedSame); }) 45 | #define l3_greaterThan(object) l3_ordered(object, NSOrderedDescending) 46 | #define l3_greaterThanOrEqualTo(object) \ 47 | l3_ordered_or_same(object, NSOrderedDescending) 48 | #define l3_lessThan(object) l3_ordered(object, NSOrderedAscending) 49 | #define l3_lessThanOrEqualTo(object) \ 50 | l3_ordered_or_same(object, NSOrderedAscending) 51 | 52 | 53 | #pragma mark Asynchrony 54 | 55 | // there is no race condition between waiting and completing a test, but if you are not waiting explicitly, you must defer the test for it to succeed 56 | 57 | // l3_defer() is used to say that a test may complete after the test case returns 58 | #define l3_defer() [test deferCompletion] 59 | 60 | // l3_wait is used to explicitly wait until the completion signal has been received, with a default timeout of five seconds 61 | #define l3_wait() [test wait] 62 | 63 | // l3_wait_with_timeout allows you to specify the timeout explicitly 64 | #define l3_wait_with_timeout(x) [test waitWithTimeout:x] 65 | 66 | // l3_complete signals the completion of the asynchronous portion of the test 67 | #define l3_complete() [test complete] 68 | 69 | #define l3_did_not_timeout() l3_equals(YES) 70 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Collection.h: -------------------------------------------------------------------------------- 1 | // L3Collection.h 2 | // Created by Rob Rix on 2012-11-17. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @protocol L3Collection 8 | 9 | +(instancetype)l3_empty; 10 | +(instancetype)l3_wrap:(id)element; 11 | 12 | // what Objective-C can’t express here is that the other should have the same type 13 | -(instancetype)l3_appendCollection:(id)other; 14 | 15 | @end 16 | 17 | @interface NSArray (L3Collection) 18 | @end 19 | 20 | @interface NSSet (L3Collection) 21 | @end 22 | 23 | @interface NSDictionary (L3Collection) 24 | @end 25 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Collection.m: -------------------------------------------------------------------------------- 1 | // L3Collection.m 2 | // Created by Rob Rix on 2012-11-17. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3Collection.h" 6 | 7 | #pragma mark Arrays 8 | 9 | @implementation NSArray (L3Collection) 10 | 11 | +(instancetype)l3_empty { 12 | return @[]; 13 | } 14 | 15 | +(instancetype)l3_wrap:(id)element { 16 | NSParameterAssert(element != nil); 17 | 18 | return @[element]; 19 | } 20 | 21 | 22 | -(instancetype)l3_appendCollection:(id)other { 23 | NSParameterAssert([other isKindOfClass:[NSArray class]]); 24 | 25 | return [self arrayByAddingObjectsFromArray:(id)other]; 26 | } 27 | 28 | @end 29 | 30 | 31 | #pragma mark Sets 32 | 33 | @implementation NSSet (L3Collection) 34 | 35 | +(instancetype)l3_empty { 36 | return [NSSet set]; 37 | } 38 | 39 | +(instancetype)l3_wrap:(id)element { 40 | NSParameterAssert(element != nil); 41 | 42 | return [NSSet setWithObject:element]; 43 | } 44 | 45 | 46 | -(instancetype)l3_appendCollection:(id)other { 47 | NSParameterAssert([other isKindOfClass:[NSSet class]]); 48 | 49 | return [self setByAddingObjectsFromSet:(id)other]; 50 | } 51 | 52 | @end 53 | 54 | 55 | #pragma mark Dictionaries 56 | 57 | @implementation NSDictionary (L3Collection) 58 | 59 | +(instancetype)l3_empty { 60 | return @{}; 61 | } 62 | 63 | +(instancetype)l3_wrap:(id)element { 64 | NSParameterAssert(element != nil); 65 | 66 | // fixme: does this make any sense? 67 | return @{element: element}; 68 | } 69 | 70 | 71 | -(instancetype)l3_appendCollection:(id)other { 72 | NSMutableDictionary *all = [self mutableCopy]; 73 | [all addEntriesFromDictionary:(id)other]; 74 | return all; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Configuration.h: -------------------------------------------------------------------------------- 1 | // L3Configuration.h 2 | // Created by Rob Rix on 2012-12-24. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | #pragma mark Configuration macros 8 | 9 | #if DEBUG 10 | 11 | // DEBUG=1 implies L3_DEBUG=1, unless L3_DEBUG has already been set (e.g. in a -D compiler flag) 12 | #ifndef L3_DEBUG 13 | #define L3_DEBUG 1 14 | #endif 15 | 16 | #endif 17 | 18 | /* 19 | L3_DEBUG is intended to be defined in a Debug build configuration, for example when DEBUG=1 is defined (as is Xcode’s default). 20 | 21 | It automatically enables the compilation of tests. 22 | */ 23 | #if L3_DEBUG 24 | 25 | // L3_DEBUG=1 implies L3_INCLUDE_TESTS=1, unless L3_INCLUDE_TESTS has already been set (e.g. in a -D compiler flag) 26 | #ifndef L3_INCLUDE_TESTS 27 | #define L3_INCLUDE_TESTS 1 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3EventObserver.h: -------------------------------------------------------------------------------- 1 | // L3EventObserver.h 2 | // Created by Rob Rix on 2012-11-11. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @protocol L3Test; 8 | 9 | @class L3SourceReference; 10 | 11 | @protocol L3EventObserver 12 | 13 | -(void)testStartEventWithTest:(id)test date:(NSDate *)date; 14 | -(void)testEndEventWithTest:(id)test date:(NSDate *)date; 15 | 16 | -(void)assertionFailureWithSourceReference:(L3SourceReference *)sourceReference date:(NSDate *)date; 17 | -(void)assertionSuccessWithSourceReference:(L3SourceReference *)sourceReference date:(NSDate *)date; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3FunctionalUtilities.h: -------------------------------------------------------------------------------- 1 | // L3FunctionalUtilities.h 2 | // Created by Rob Rix on 2012-11-13. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | #pragma mark Let 8 | 9 | typedef id(^L3UnaryLetBlock)(id); 10 | typedef id(^L3BinaryLetBlock)(id, id); 11 | typedef id(^L3TernaryLetBlock)(id, id, id); 12 | 13 | #define l3_unary(type) (L3UnaryLetBlock)^id(type) 14 | #define l3_binary(typeA, typeB) (L3BinaryLetBlock)^id(typeA, typeB) 15 | 16 | __attribute__((overloadable)) static inline id l3_let(id x, L3UnaryLetBlock block) { 17 | return block(x); 18 | } 19 | 20 | __attribute__((overloadable)) static inline id l3_let(id x, id y, L3BinaryLetBlock block) { 21 | return block(x, y); 22 | } 23 | 24 | __attribute__((overloadable)) static inline id l3_let(id x, id y, id z, L3TernaryLetBlock block) { 25 | return block(x, y, z); 26 | } 27 | 28 | 29 | #pragma mark Fold 30 | 31 | typedef id (^L3FoldBlock)(id accumulation, id element); 32 | id L3Fold(id collection, id initialAccumulation, L3FoldBlock block); 33 | 34 | 35 | #pragma mark Map 36 | 37 | typedef id(^L3MapBlock)(id element); 38 | 39 | // returns a new collection of the same type as the first argument; typed as id because ObjC can’t convey that information in its type system 40 | id L3Map(id collection, L3MapBlock block); 41 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3FunctionalUtilities.m: -------------------------------------------------------------------------------- 1 | // L3FunctionalUtilities.m 2 | // Created by Rob Rix on 2012-11-13. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3Collection.h" 6 | #import "L3FunctionalUtilities.h" 7 | #import "Lagrangian.h" 8 | 9 | @l3_suite("Folds"); 10 | 11 | @l3_test("sequentially produce an object from a collection given a block") { 12 | NSString *concatenation = L3Fold(@[@"a", @"b", @"c"], @"", ^id(id accumulation, id element) { return [accumulation stringByAppendingString:element]; }); 13 | l3_assert(concatenation, l3_equals(@"abc")); 14 | } 15 | 16 | id L3Fold(id collection, id accumulation, L3FoldBlock block) { 17 | for (id element in collection) { 18 | accumulation = block(accumulation, element); 19 | } 20 | return accumulation; 21 | } 22 | 23 | 24 | @l3_suite("Maps"); 25 | 26 | @l3_test("produce a collection by mapping a block over a collection") { 27 | NSArray *appended = L3Map(@[@"a", @"b", @"c"], ^id(id element) { 28 | return [element stringByAppendingString:@"0"]; 29 | }); 30 | l3_assert(appended, l3_equals(@[@"a0", @"b0", @"c0"])); 31 | } 32 | 33 | id L3Map(id collection, L3MapBlock block) { 34 | return L3Fold(collection, [[collection class] l3_empty], ^id(id accumulation, id element) { 35 | return [accumulation l3_appendCollection:[[collection class] l3_wrap:block(element)]]; 36 | }); 37 | } -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Mock.h: -------------------------------------------------------------------------------- 1 | // L3Mock.h 2 | // Created by Rob Rix on 2013-05-30. 3 | // Copyright (c) 2013 Rob Rix. All rights reserved. 4 | 5 | #import 6 | #import 7 | 8 | @protocol L3Mock; 9 | 10 | @interface L3Mock : NSObject 11 | 12 | +(id)mockNamed:(NSString *)name initializer:(void(^)(id mock))initializer; 13 | 14 | @end 15 | 16 | @protocol L3Mock 17 | 18 | -(void)addMethodWithSelector:(SEL)selector types:(const char *)types block:(id)block; 19 | 20 | @end 21 | 22 | #define L3TypeSignatureEncode(_, type) @encode(type) 23 | #define L3TypeSignature(...) rx_fold(L3TypeSignatureEncode, _, __VA_ARGS__) 24 | const char *L3ConstructTypeSignature(char type[], ...); 25 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Mock.m: -------------------------------------------------------------------------------- 1 | // L3Mock.m 2 | // Created by Rob Rix on 2013-05-30. 3 | // Copyright (c) 2013 Rob Rix. All rights reserved. 4 | 5 | #import "L3Mock.h" 6 | 7 | #import 8 | #import 9 | 10 | @l3_suite("L3Mock"); 11 | 12 | @interface L3Mock () 13 | @end 14 | 15 | @implementation L3Mock 16 | 17 | +(id)mockNamed:(NSString *)name initializer:(void(^)(id mock))initializer { 18 | return [[self alloc] initWithName:name initializer:initializer]; 19 | } 20 | 21 | +(NSString *)classNameForName:(NSString *)name { 22 | return [NSStringFromClass(self.class) stringByAppendingFormat:@"_%@", name]; 23 | } 24 | 25 | -(instancetype)initWithName:(NSString *)name initializer:(void(^)(id mock))initializer { 26 | if ((self = [super init])) { 27 | Class class = objc_getClass([self.class classNameForName:name].UTF8String); 28 | if (!class) { 29 | class = objc_allocateClassPair(object_getClass(self), [self.class classNameForName:name].UTF8String, 0); 30 | 31 | initializer(self); 32 | 33 | objc_registerClassPair(class); 34 | } 35 | object_setClass(self, class); 36 | } 37 | return self; 38 | } 39 | 40 | 41 | @l3_test("mocking creates a class at runtime and adds methods to it, which are then available on the instance") { 42 | L3Mock *mock = [L3Mock mockNamed:@"Mock" initializer:^(id mock) { 43 | [mock addMethodWithSelector:@selector(description) types:L3TypeSignature(id, id, SEL) block:^{ 44 | return @"test"; 45 | }]; 46 | }]; 47 | l3_assert(mock.description, @"test"); 48 | } 49 | 50 | -(void)addMethodWithSelector:(SEL)selector types:(const char *)types block:(id)block { 51 | IMP implementation = imp_implementationWithBlock(block); 52 | class_addMethod(self.class, selector, implementation, types); 53 | } 54 | 55 | @end 56 | 57 | 58 | const char *L3ConstructTypeSignature(char type[], ...) { 59 | va_list types; 60 | va_start(types, type); 61 | NSMutableString *signature = [NSMutableString new]; 62 | 63 | do { 64 | [signature appendFormat:@"%s", type]; 65 | } while ((type = va_arg(types, char *))); 66 | 67 | va_end(types); 68 | return signature.UTF8String; 69 | } 70 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3OCUnitTestResultFormatter.h: -------------------------------------------------------------------------------- 1 | // L3OCUnitTestResultFormatter.h 2 | // Created by Rob Rix on 2012-11-11. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @interface L3OCUnitTestResultFormatter : NSObject 8 | @end 9 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3PreprocessorUtilities.h: -------------------------------------------------------------------------------- 1 | // L3PreprocessorUtilities.h 2 | // Created by Rob Rix on 2012-11-10. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | #pragma mark Macro utilities 8 | 9 | #define l3_domain com_antitypical_lagrangian_ 10 | #define l3_identifier(sym, line) rx_paste(rx_paste(l3_domain, sym), line) 11 | 12 | #define l3_string(x) l3_string_implementation(x) 13 | #define l3_string_implementation(x) #x 14 | 15 | #pragma mark bool 16 | 17 | #define l3_bool(x) l3_bool_implementation(x) 18 | #define l3_bool_implementation(x) rx_paste(l3_bool_, x) 19 | #define l3_bool_0 0 20 | #define l3_bool_1 1 21 | #define l3_bool_2 1 22 | #define l3_bool_3 1 23 | #define l3_bool_4 1 24 | #define l3_bool_5 1 25 | #define l3_bool_6 1 26 | #define l3_bool_7 1 27 | #define l3_bool_8 1 28 | #define l3_bool_9 1 29 | #define l3_bool_10 1 30 | #define l3_bool_11 1 31 | #define l3_bool_12 1 32 | #define l3_bool_13 1 33 | #define l3_bool_14 1 34 | #define l3_bool_15 1 35 | #define l3_bool_16 1 36 | #define l3_bool_17 1 37 | #define l3_bool_18 1 38 | #define l3_bool_19 1 39 | #define l3_bool_20 1 40 | #define l3_bool_21 1 41 | #define l3_bool_22 1 42 | #define l3_bool_23 1 43 | #define l3_bool_24 1 44 | #define l3_bool_25 1 45 | #define l3_bool_26 1 46 | #define l3_bool_27 1 47 | #define l3_bool_28 1 48 | #define l3_bool_29 1 49 | #define l3_bool_30 1 50 | #define l3_bool_31 1 51 | 52 | 53 | #pragma mark Conditional macro expansion 54 | 55 | #define l3_cond(cond, then, else) l3_cond_implementation(cond, then, else) 56 | #define l3_cond_implementation(cond, then, else) \ 57 | rx_paste(l3_cond_, l3_bool(cond))(then, else) 58 | #define l3_cond_0(x, y) y 59 | #define l3_cond_1(x, y) x 60 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3SourceReference.h: -------------------------------------------------------------------------------- 1 | // L3SourceReference.h 2 | // Created by Rob Rix on 2012-11-11. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @interface L3SourceReference : NSObject 8 | 9 | +(instancetype)referenceWithFile:(NSString *)file line:(NSUInteger)line subjectSource:(NSString *)subjectSource subject:(id)subject patternSource:(NSString *)patternSource; 10 | +(instancetype)referenceWithFile:(NSString *)file line:(NSUInteger)line reason:(NSString *)reason; 11 | 12 | @property (copy, nonatomic, readonly) NSString *file; 13 | @property (assign, nonatomic, readonly) NSUInteger line; 14 | 15 | @property (copy, nonatomic, readonly) NSString *reason; 16 | 17 | @property (copy, nonatomic, readonly) NSString *subjectSource; 18 | @property (strong, nonatomic, readonly) id subject; 19 | @property (copy, nonatomic, readonly) NSString *patternSource; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3SourceReference.m: -------------------------------------------------------------------------------- 1 | // L3SourceReference.m 2 | // Created by Rob Rix on 2012-11-11. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3SourceReference.h" 6 | 7 | @implementation L3SourceReference 8 | 9 | #pragma mark Constructors 10 | 11 | +(instancetype)referenceWithFile:(NSString *)file line:(NSUInteger)line subjectSource:(NSString *)subjectSource subject:(id)subject patternSource:(NSString *)patternSource { 12 | return [[self alloc] initWithFile:file line:line subjectSource:subjectSource subject:subject patternSource:patternSource]; 13 | } 14 | 15 | +(instancetype)referenceWithFile:(NSString *)file line:(NSUInteger)line reason:(NSString *)reason { 16 | return [[self alloc] initWithFile:file line:line reason:reason]; 17 | } 18 | 19 | -(instancetype)initWithFile:(NSString *)file line:(NSUInteger)line reason:(NSString *)reason { 20 | if ((self = [super init])) { 21 | _file = [file copy]; 22 | _line = line; 23 | _reason = [reason copy]; 24 | } 25 | return self; 26 | } 27 | 28 | -(instancetype)initWithFile:(NSString *)file line:(NSUInteger)line subjectSource:(NSString *)subjectSource subject:(id)subject patternSource:(NSString *)patternSource { 29 | if ((self = [self initWithFile:file line:line reason:[NSString stringWithFormat:@"'%@' was '%@' but should have matched '%@'", subjectSource, subject, patternSource]])) { 30 | _subjectSource = [subjectSource copy]; 31 | _subject = subject; 32 | _patternSource = [patternSource copy]; 33 | } 34 | return self; 35 | } 36 | 37 | 38 | #pragma mark NSCopying 39 | 40 | -(instancetype)copyWithZone:(NSZone *)zone { 41 | return self; 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Stack.h: -------------------------------------------------------------------------------- 1 | // L3Stack.h 2 | // Created by Rob Rix on 2012-11-13. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @interface L3Stack : NSObject 8 | 9 | -(void)pushObject:(id)object; 10 | -(id)popObject; 11 | 12 | @property (nonatomic, readonly) id topObject; 13 | 14 | @property (nonatomic, readonly) NSArray *objects; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Stack.m: -------------------------------------------------------------------------------- 1 | // L3Stack.m 2 | // Created by Rob Rix on 2012-11-13. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3Stack.h" 6 | #import "Lagrangian.h" 7 | 8 | @interface L3Stack () 9 | 10 | @property (nonatomic, readonly) NSMutableArray *mutableObjects; 11 | 12 | @end 13 | 14 | @implementation L3Stack 15 | 16 | @l3_suite("Stacks"); 17 | 18 | @l3_set_up { 19 | test[@"stack"] = [L3Stack new]; 20 | } 21 | 22 | #pragma mark Constructors 23 | 24 | @l3_test("are initialized with an empty array of objects") { 25 | l3_assert([test[@"stack"] mutableObjects], l3_is(@[])); 26 | } 27 | 28 | -(instancetype)init { 29 | if ((self = [super init])) { 30 | _mutableObjects = [NSMutableArray new]; 31 | } 32 | return self; 33 | } 34 | 35 | 36 | #pragma mark Public methods 37 | 38 | @l3_test("push objects by adding them to the end of their arrays") { 39 | [test[@"stack"] pushObject:self.name]; 40 | l3_assert([test[@"stack"] mutableObjects], l3_is(@[self.name])); 41 | } 42 | 43 | -(void)pushObject:(id)object { 44 | [self.mutableObjects addObject:object]; 45 | } 46 | 47 | @l3_test("pop objects by removing them from the end of their arrays and returning them") { 48 | [test[@"stack"] pushObject:self.name]; 49 | l3_assert([test[@"stack"] popObject], l3_is(self.name)); 50 | l3_assert([test[@"stack"] mutableObjects], @[]); 51 | } 52 | 53 | -(id)popObject { 54 | id object = self.topObject; 55 | [self.mutableObjects removeLastObject]; 56 | return object; 57 | } 58 | 59 | 60 | @l3_test("use the last object in their arrays as the top object") { 61 | [test[@"stack"] pushObject:self.name]; 62 | l3_assert([test[@"stack"] topObject], self.name); 63 | } 64 | 65 | -(id)topObject { 66 | return self.mutableObjects.lastObject; 67 | } 68 | 69 | 70 | @l3_test("return their contents") { 71 | [test[@"stack"] pushObject:self.name]; 72 | [test[@"stack"] pushObject:self.name]; 73 | l3_assert([test[@"stack"] objects], l3_equals(@[self.name, self.name])); 74 | } 75 | 76 | -(NSArray *)objects { 77 | return self.mutableObjects; 78 | } 79 | 80 | @end 81 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3StringInflections.h: -------------------------------------------------------------------------------- 1 | // L3StringInflections.h 2 | // Created by Rob Rix on 2012-11-14. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @interface L3StringInflections : NSObject 8 | 9 | +(NSString *)pluralizeNoun:(NSString *)noun count:(NSUInteger)count; 10 | +(NSString *)cardinalizeNoun:(NSString *)noun count:(NSUInteger)count; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3StringInflections.m: -------------------------------------------------------------------------------- 1 | // L3StringInflections.m 2 | // Created by Rob Rix on 2012-11-14. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3StringInflections.h" 6 | #import "Lagrangian.h" 7 | 8 | @implementation L3StringInflections 9 | 10 | @l3_suite("string inflections"); 11 | 12 | 13 | @l3_test("pluralize nouns when their cardinality is 0") { 14 | l3_assert([L3StringInflections pluralizeNoun:@"dog" count:0], l3_is(@"dogs")); 15 | } 16 | 17 | @l3_test("do not pluralize nouns when their cardinality is 1") { 18 | l3_assert([L3StringInflections pluralizeNoun:@"cat" count:1], l3_is(@"cat")); 19 | } 20 | 21 | @l3_test("pluralize nouns when their cardinality is 2 or greater") { 22 | l3_assert([L3StringInflections pluralizeNoun:@"bird" count:2], l3_is(@"birds")); 23 | } 24 | 25 | +(NSString *)pluralizeNoun:(NSString *)noun count:(NSUInteger)count { 26 | return count == 1? 27 | noun 28 | : [noun stringByAppendingString:@"s"]; 29 | } 30 | 31 | 32 | @l3_test("cardinalize nouns with their plurals when cardinality is not 1") { 33 | l3_assert([L3StringInflections cardinalizeNoun:@"plural" count:0], @"0 plurals"); 34 | l3_assert([L3StringInflections cardinalizeNoun:@"cardinal" count:1], @"1 cardinal"); 35 | l3_assert([L3StringInflections cardinalizeNoun:@"ordinal" count:2], @"2 ordinals"); 36 | } 37 | 38 | +(NSString *)cardinalizeNoun:(NSString *)noun count:(NSUInteger)count { 39 | return [NSString stringWithFormat:@"%lu %@", (unsigned long)count, [self pluralizeNoun:noun count:count]]; 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TRDynamicLibrary.h: -------------------------------------------------------------------------------- 1 | // L3TRDynamicLibrary.h 2 | // Created by Rob Rix on 2012-12-21. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @interface L3TRDynamicLibrary : NSObject 8 | 9 | +(instancetype)openLibraryAtPath:(NSString *)path error:(NSError **)error; 10 | 11 | @property (nonatomic, copy, readonly) NSString *path; 12 | 13 | -(void *)loadSymbolWithName:(NSString *)symbolName error:(NSError **)error; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TRDynamicLibrary.m: -------------------------------------------------------------------------------- 1 | // L3TRDynamicLibrary.m 2 | // Created by Rob Rix on 2012-12-21. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3TRDynamicLibrary.h" 6 | 7 | #import 8 | 9 | @interface L3TRDynamicLibrary () 10 | @property (nonatomic, assign, readonly) void *handle; 11 | @end 12 | 13 | @implementation L3TRDynamicLibrary 14 | 15 | +(NSError *)errorWithDYLDErrorString:(const char *)string { 16 | return [NSError errorWithDomain:@"com.antitypical.lagrangian" code:1 userInfo:@{ 17 | NSLocalizedDescriptionKey: [NSString stringWithUTF8String:string] 18 | }]; 19 | } 20 | 21 | +(void *)validateDYLDPointer:(void *)pointer error:(NSError **)error { 22 | if (!pointer) { 23 | const char *errorMessage = dlerror(); 24 | if (error) 25 | *error = [self errorWithDYLDErrorString:errorMessage]; 26 | } 27 | return pointer; 28 | } 29 | 30 | +(instancetype)openLibraryAtPath:(NSString *)path error:(NSError **)error { 31 | L3TRDynamicLibrary *library = nil; 32 | void *handle = [self validateDYLDPointer:dlopen(path.fileSystemRepresentation, RTLD_NOW | RTLD_LOCAL) error:error]; 33 | if (handle) { 34 | library = [self new]; 35 | library->_path = [path copy]; 36 | library->_handle = handle; 37 | } 38 | return library; 39 | } 40 | 41 | -(void)dealloc { 42 | dlclose(_handle); 43 | } 44 | 45 | 46 | -(void *)loadSymbolWithName:(NSString *)symbolName error:(NSError **)error { 47 | return [self.class validateDYLDPointer:dlsym(self.handle, symbolName.UTF8String) error:error]; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Test.h: -------------------------------------------------------------------------------- 1 | // L3Test.h 2 | // Created by Rob Rix on 2012-11-11. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | #import 7 | 8 | @class L3TestSuite; 9 | 10 | @protocol L3Test 11 | 12 | @property (copy, nonatomic, readonly) NSString *name; 13 | 14 | 15 | #pragma mark Source reference 16 | 17 | @property (copy, nonatomic, readonly) NSString *file; 18 | @property (assign, nonatomic, readonly) NSUInteger line; 19 | 20 | 21 | #pragma mark Test composition 22 | 23 | @property (nonatomic, readonly, getter = isComposite) bool composite; 24 | 25 | 26 | #pragma mark Visiting 27 | 28 | -(void)acceptVisitor:(id)visitor inTestSuite:(L3TestSuite *)parentSuite; 29 | // assumes nil parent 30 | -(void)acceptVisitor:(id)visitor; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestCase.h: -------------------------------------------------------------------------------- 1 | // L3TestCase.h 2 | // Created by Rob Rix on 2012-11-08. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | #import 7 | #import 8 | #import 9 | 10 | @class L3SourceReference; 11 | @class L3TestSuite; 12 | 13 | @interface L3TestCase : NSObject 14 | 15 | #pragma mark Constructors 16 | 17 | +(instancetype)testCaseWithName:(NSString *)name file:(NSString *)file line:(NSUInteger)line function:(L3TestCaseFunction)function; 18 | 19 | @property (assign, nonatomic, readonly) L3TestCaseFunction function; 20 | 21 | 22 | #pragma mark Steps 23 | 24 | -(void)setUp:(L3TestStep *)step withState:(L3TestState *)state; 25 | -(void)tearDown:(L3TestStep *)step withState:(L3TestState *)state; 26 | -(bool)performStep:(L3TestStep *)step withState:(L3TestState *)state; 27 | 28 | 29 | #pragma mark Assertions 30 | 31 | @property (strong, nonatomic, readonly) L3SourceReference *sourceReferenceForCaseEvents; 32 | 33 | -(bool)assertThat:(id)object matches:(L3Pattern)pattern sourceReference:(L3SourceReference *)assertion eventObserver:(id)eventObserver; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestResult.h: -------------------------------------------------------------------------------- 1 | // L3TestResult.h 2 | // Created by Rob Rix on 2012-11-12. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @protocol L3TestResult 8 | 9 | +(id)testResultWithName:(NSString *)name file:(NSString *)file line:(NSUInteger)line startDate:(NSDate *)startDate; 10 | 11 | @property (assign, nonatomic, readonly, getter = isComposite) bool composite; 12 | 13 | @property (weak, nonatomic) id parent; 14 | 15 | @property (copy, nonatomic, readonly) NSString *name; 16 | @property (copy, nonatomic, readwrite) NSString *file; 17 | @property (assign, nonatomic, readonly) NSUInteger line; 18 | 19 | 20 | @property (strong, nonatomic, readonly) NSDate *startDate; 21 | @property (strong, nonatomic) NSDate *endDate; 22 | @property (assign, nonatomic, readonly) NSTimeInterval totalDuration; // span between startDate and endDate 23 | @property (assign, nonatomic, readonly) NSTimeInterval duration; // sum of children’s durations if composite; otherwise totalDuration 24 | 25 | @property (assign, nonatomic, readonly) NSUInteger testCaseCount; 26 | 27 | @property (assign, nonatomic) NSUInteger assertionCount; 28 | @property (assign, nonatomic) NSUInteger assertionFailureCount; 29 | @property (assign, nonatomic) NSUInteger exceptionCount; 30 | 31 | @property (nonatomic, readonly) bool succeeded; 32 | @property (nonatomic, readonly) bool failed; 33 | 34 | @property (nonatomic, readonly) NSArray *testResults; 35 | -(void)addTestResult:(id)testResult; 36 | 37 | @end 38 | 39 | @interface L3AbstractTestResult : NSObject 40 | @end 41 | 42 | @interface L3TestResult : L3AbstractTestResult 43 | @end 44 | 45 | @interface L3CompositeTestResult : L3AbstractTestResult 46 | @end 47 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestResult.m: -------------------------------------------------------------------------------- 1 | // L3TestResult.m 2 | // Created by Rob Rix on 2012-11-12. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3TestResult.h" 6 | #import "Lagrangian.h" 7 | 8 | @l3_suite_interface(L3TestResult, "Test results") 9 | @property L3TestResult *result; 10 | @property L3CompositeTestResult *compositeResult; 11 | @end 12 | 13 | @l3_set_up { 14 | test.result = [L3TestResult testResultWithName:self.name file:self.file line:self.line startDate:[NSDate date]]; 15 | test.compositeResult = [L3CompositeTestResult testResultWithName:self.name file:self.file line:self.line startDate:[NSDate date]]; 16 | } 17 | 18 | 19 | @interface L3AbstractTestResult (L3TestResult) 20 | @end 21 | 22 | @interface L3AbstractTestResult () 23 | 24 | -(instancetype)initWithName:(NSString *)name file:(NSString *)file line:(NSUInteger)line startDate:(NSDate *)startDate; 25 | 26 | @property (weak, nonatomic) id parent; 27 | 28 | @property (copy, nonatomic, readonly) NSString *name; 29 | @property (copy, nonatomic, readonly) NSString *file; 30 | @property (assign, nonatomic, readonly) NSUInteger line; 31 | @property (strong, nonatomic, readonly) NSDate *startDate; 32 | @property (strong, nonatomic) NSDate *endDate; 33 | @property (assign, nonatomic) NSTimeInterval totalDuration; 34 | 35 | @end 36 | 37 | @implementation L3AbstractTestResult 38 | 39 | #pragma mark Constructors 40 | 41 | +(id)testResultWithName:(NSString *)name file:(NSString *)file line:(NSUInteger)line startDate:(NSDate *)startDate { 42 | return [[self alloc] initWithName:name file:file line:line startDate:startDate]; 43 | } 44 | 45 | -(instancetype)initWithName:(NSString *)name file:(NSString *)file line:(NSUInteger)line startDate:(NSDate *)startDate { 46 | NSParameterAssert(name != nil); 47 | NSParameterAssert(startDate != nil); 48 | if ((self = [super init])) { 49 | _name = name; 50 | _file = file; 51 | _line = line; 52 | _startDate = startDate; 53 | } 54 | return self; 55 | } 56 | 57 | 58 | #pragma mark Total duration 59 | 60 | -(NSTimeInterval)totalDuration { 61 | return [self.endDate timeIntervalSinceDate:self.startDate]; 62 | } 63 | 64 | 65 | #pragma mark Success/failure 66 | 67 | @l3_test("succeed when no exceptions or assertion failures occurred") { 68 | l3_assert(test.result.succeeded, l3_is(YES)); 69 | } 70 | 71 | -(bool)succeeded { 72 | return !self.failed; 73 | } 74 | 75 | @l3_test("fail when assertion failures occur") { 76 | test.result.assertionFailureCount = 1; 77 | l3_assert(test.result.failed, l3_is(YES)); 78 | } 79 | 80 | @l3_test("fail when unexpected exceptions occur") { 81 | test.result.exceptionCount = 1; 82 | l3_assert(test.result.failed, l3_is(YES)); 83 | } 84 | 85 | -(bool)failed { 86 | return (self.assertionFailureCount + self.exceptionCount) > 0; 87 | } 88 | 89 | 90 | #pragma mark Composition 91 | 92 | -(NSArray *)testResults { 93 | [self doesNotRecognizeSelector:_cmd]; 94 | return nil; 95 | } 96 | 97 | -(void)addTestResult:(L3TestResult *)child { 98 | [self doesNotRecognizeSelector:_cmd]; 99 | } 100 | 101 | @end 102 | 103 | 104 | @interface L3TestResult () 105 | @end 106 | 107 | @implementation L3TestResult 108 | 109 | #pragma mark Properties 110 | 111 | -(bool)isComposite { 112 | return NO; 113 | } 114 | 115 | 116 | @l3_test("atomic results return the span between their start and end dates as their duration") { 117 | test.result.endDate = [NSDate dateWithTimeInterval:1 sinceDate:test.result.startDate]; 118 | l3_assert(test.result.duration, l3_equals(1.0)); 119 | } 120 | 121 | -(NSTimeInterval)duration { 122 | return self.totalDuration; 123 | } 124 | 125 | 126 | -(NSUInteger)testCaseCount { 127 | return 1; 128 | } 129 | 130 | 131 | @synthesize assertionCount = _assertionCount; 132 | @synthesize assertionFailureCount = _assertionFailureCount; 133 | @synthesize exceptionCount = _exceptionCount; 134 | 135 | 136 | #pragma mark Inherited properties 137 | 138 | @dynamic parent; 139 | @dynamic name; 140 | @dynamic startDate; 141 | @dynamic endDate; 142 | @dynamic totalDuration; 143 | @dynamic testResults; 144 | @dynamic succeeded; 145 | @dynamic failed; 146 | 147 | @end 148 | 149 | 150 | @interface L3CompositeTestResult () 151 | 152 | @property (strong, nonatomic, readonly) NSMutableArray *mutableTestResults; 153 | 154 | @end 155 | 156 | @implementation L3CompositeTestResult 157 | 158 | #pragma mark Constructors 159 | 160 | -(instancetype)initWithName:(NSString *)name file:(NSString *)file line:(NSUInteger)line startDate:(NSDate *)startDate { 161 | if ((self = [super initWithName:name file:file line:line startDate:startDate])) { 162 | _mutableTestResults = [NSMutableArray new]; 163 | } 164 | return self; 165 | } 166 | 167 | 168 | #pragma mark Properties 169 | 170 | -(bool)isComposite { 171 | return YES; 172 | } 173 | 174 | 175 | #pragma mark Recursive properties 176 | 177 | @l3_test("composite results sum their children’s durations") { 178 | L3TestResult *child = [L3TestResult testResultWithName:@"child" file:@"" __FILE__ line:__LINE__ startDate:[NSDate dateWithTimeIntervalSinceNow:-1]]; 179 | child.endDate = [NSDate date]; 180 | [test.compositeResult addTestResult:child]; 181 | child = [L3TestResult testResultWithName:@"child" file:@"" __FILE__ line:__LINE__ startDate:[NSDate dateWithTimeIntervalSinceNow:-1]]; 182 | child.endDate = [NSDate date]; 183 | [test.compositeResult addTestResult:child]; 184 | l3_assert(test.compositeResult.duration, l3_equalsWithEpsilon(2.0, 0.0001)); 185 | } 186 | 187 | -(NSTimeInterval)duration { 188 | return [[self.testResults valueForKeyPath:@"@sum.duration"] doubleValue]; 189 | } 190 | 191 | -(NSUInteger)testCaseCount { 192 | return [[self.testResults valueForKeyPath:@"@sum.testCaseCount"] unsignedIntegerValue]; 193 | } 194 | 195 | 196 | -(NSUInteger)assertionCount { 197 | return [[self.testResults valueForKeyPath:@"@sum.assertionCount"] unsignedIntegerValue]; 198 | } 199 | 200 | -(void)setAssertionCount:(NSUInteger)assertionCount {} 201 | 202 | -(NSUInteger)assertionFailureCount { 203 | return [[self.testResults valueForKeyPath:@"@sum.assertionFailureCount"] unsignedIntegerValue]; 204 | } 205 | 206 | -(void)setAssertionFailureCount:(NSUInteger)assertionFailureCount {} 207 | 208 | -(NSUInteger)exceptionCount { 209 | return [[self.testResults valueForKeyPath:@"@sum.exceptionCount"] unsignedIntegerValue]; 210 | } 211 | 212 | -(void)setExceptionCount:(NSUInteger)exceptionCount {} 213 | 214 | 215 | #pragma mark Inherited properties 216 | 217 | @dynamic parent; 218 | @dynamic name; 219 | @dynamic startDate; 220 | @dynamic endDate; 221 | @dynamic totalDuration; 222 | @dynamic succeeded; 223 | @dynamic failed; 224 | 225 | 226 | #pragma mark Composite 227 | 228 | -(NSArray *)testResults { 229 | return _mutableTestResults; 230 | } 231 | 232 | -(void)addTestResult:(L3TestResult *)child { 233 | [self.mutableTestResults addObject:child]; 234 | } 235 | 236 | @end 237 | 238 | 239 | @l3_suite_implementation (L3TestResult) 240 | @end 241 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestResultBuilder.h: -------------------------------------------------------------------------------- 1 | // L3TestResultBuilder.h 2 | // Created by Rob Rix on 2012-11-13. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @class L3SourceReference; 8 | @class L3TestResult; 9 | @protocol L3TestResultBuilderDelegate; 10 | 11 | @interface L3TestResultBuilder : NSObject 12 | 13 | @property (weak, nonatomic) id delegate; 14 | 15 | @end 16 | 17 | @protocol L3TestResultBuilderDelegate 18 | 19 | -(void)testResultBuilder:(L3TestResultBuilder *)builder testResultDidStart:(L3TestResult *)result; 20 | -(void)testResultBuilder:(L3TestResultBuilder *)builder testResult:(L3TestResult *)result assertionDidSucceedWithSourceReference:(L3SourceReference *)sourceReference; 21 | -(void)testResultBuilder:(L3TestResultBuilder *)builder testResult:(L3TestResult *)result assertionDidFailWithSourceReference:(L3SourceReference *)sourceReference; 22 | -(void)testResultBuilder:(L3TestResultBuilder *)builder testResultDidFinish:(L3TestResult *)result; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestResultFormatter.h: -------------------------------------------------------------------------------- 1 | // L3TestResultFormatter.h 2 | // Created by Rob Rix on 2012-11-11. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @protocol L3TestResultFormatterDelegate; 8 | 9 | @protocol L3TestResultFormatter 10 | 11 | @property (weak, nonatomic) id delegate; 12 | 13 | @end 14 | 15 | @class L3TestResult; 16 | 17 | @protocol L3TestResultFormatterDelegate 18 | 19 | -(void)formatter:(id)formatter didFormatResult:(L3TestResult *)result asString:(NSString *)string; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestRunner.h: -------------------------------------------------------------------------------- 1 | // L3TestRunner.h 2 | // Created by Rob Rix on 2012-11-09. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | #import 7 | 8 | extern NSString * const L3TestRunnerRunTestsOnLaunchEnvironmentVariableName; 9 | extern NSString * const L3TestRunnerSuitePredicateEnvironmentVariableName; 10 | 11 | @class L3TestSuite; 12 | 13 | #if L3_DEBUG 14 | 15 | #define l3_main(argc, argv) \ 16 | do { \ 17 | if ([NSClassFromString(@"L3TestRunner") shouldRunTestsAtLaunch]) { \ 18 | dispatch_main(); \ 19 | } \ 20 | } while(0) 21 | 22 | #else 23 | 24 | #define l3_main(argc, argv) \ 25 | do {} while(0) 26 | 27 | #endif 28 | 29 | @interface L3TestRunner : NSObject 30 | 31 | +(bool)shouldRunTestsAtLaunch; 32 | +(bool)isRunningInApplication; 33 | 34 | +(instancetype)runner; 35 | 36 | @property (strong, nonatomic) NSPredicate *testSuitePredicate; 37 | 38 | -(void)run; // starts running asynchronously 39 | -(void)waitForTestsToComplete; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestState.h: -------------------------------------------------------------------------------- 1 | // L3TestState.h 2 | // Created by Rob Rix on 2012-11-10. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | extern const NSTimeInterval L3TestStateDefaultTimeout; 8 | 9 | @class L3TestSuite; 10 | 11 | @interface L3TestState : NSObject 12 | 13 | -(instancetype)initWithSuite:(L3TestSuite *)suite eventObserver:(id)eventObserver; 14 | 15 | @property (strong, nonatomic, readonly) L3TestSuite *suite; 16 | @property (strong, nonatomic, readonly) id eventObserver; 17 | 18 | #pragma mark Test state 19 | 20 | // subscripting support for arbitrary object state 21 | -(id)objectForKeyedSubscript:(NSString *)key; 22 | -(void)setObject:(id)object forKeyedSubscript:(NSString *)key; 23 | 24 | 25 | #pragma mark Asynchrony 26 | 27 | -(void)deferCompletion; 28 | @property (assign, nonatomic, readonly, getter = isDeferred) bool deferred; 29 | @property (assign, nonatomic) NSTimeInterval timeout; 30 | -(void)complete; 31 | -(bool)wait; 32 | -(bool)waitWithTimeout:(NSTimeInterval)interval; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestState.m: -------------------------------------------------------------------------------- 1 | // L3TestState.m 2 | // Created by Rob Rix on 2012-11-10. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3TestState.h" 6 | #import "Lagrangian.h" 7 | 8 | @l3_suite("Test state"); 9 | 10 | const NSTimeInterval L3TestStateDefaultTimeout = 5.; 11 | 12 | @interface L3TestState () 13 | 14 | @property (nonatomic, readonly) NSMutableDictionary *contents; 15 | 16 | @property (strong, nonatomic, readonly) dispatch_semaphore_t completionSemaphore; 17 | 18 | @property (assign, nonatomic, readwrite, getter = isDeferred) bool deferred; 19 | 20 | @end 21 | 22 | @implementation L3TestState 23 | 24 | #pragma mark Constructors 25 | 26 | -(instancetype)initWithSuite:(L3TestSuite *)suite eventObserver:(id)eventObserver { 27 | if((self = [super init])) { 28 | _contents = [NSMutableDictionary new]; 29 | _completionSemaphore = dispatch_semaphore_create(0); 30 | 31 | _suite = suite; 32 | _eventObserver = eventObserver; 33 | 34 | _timeout = L3TestStateDefaultTimeout; 35 | } 36 | return self; 37 | } 38 | 39 | 40 | #pragma mark Test state 41 | 42 | -(id)objectForKeyedSubscript:(NSString *)key { 43 | NSParameterAssert(key != nil); 44 | return self.contents[key]; 45 | } 46 | 47 | -(void)setObject:(id)object forKeyedSubscript:(NSString *)key { 48 | NSParameterAssert(object != nil); 49 | NSParameterAssert(key != nil); 50 | self.contents[key] = object; 51 | } 52 | 53 | 54 | #pragma mark Asynchrony 55 | 56 | @l3_test("can explicitly wait for asynchronous results to complete") { 57 | __block NSString *text = nil; 58 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 59 | text = @"text"; 60 | l3_complete(); 61 | }); 62 | l3_assert(l3_wait(), l3_did_not_timeout()); 63 | l3_assert(text, l3_equals(@"text")); 64 | } 65 | 66 | @l3_test("can implicitly wait for asynchronous results to complete") { 67 | l3_defer(); 68 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 69 | l3_assert(test.isDeferred, YES); 70 | l3_complete(); 71 | }); 72 | } 73 | 74 | -(void)deferCompletion { 75 | self.deferred = YES; 76 | } 77 | 78 | -(void)complete { 79 | dispatch_semaphore_signal(self.completionSemaphore); 80 | } 81 | 82 | -(bool)wait { 83 | return [self waitWithTimeout:self.timeout]; 84 | } 85 | 86 | -(bool)waitWithTimeout:(NSTimeInterval)interval { 87 | bool didTimeout = dispatch_semaphore_wait(self.completionSemaphore, dispatch_time(DISPATCH_TIME_NOW, interval * NSEC_PER_SEC)) != 0; 88 | self.deferred = NO; 89 | return !didTimeout; 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestStep.h: -------------------------------------------------------------------------------- 1 | // L3TestStep.h 2 | // Created by Rob Rix on 2012-11-17. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @interface L3TestStep : NSObject 8 | 9 | +(instancetype)stepWithName:(NSString *)name function:(L3TestStepFunction)function; 10 | 11 | @property (copy, nonatomic, readonly) NSString *name; 12 | 13 | @property (assign, nonatomic, readonly) L3TestStepFunction function; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestStep.m: -------------------------------------------------------------------------------- 1 | // L3TestStep.m 2 | // Created by Rob Rix on 2012-11-17. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3TestStep.h" 6 | 7 | @implementation L3TestStep 8 | 9 | #pragma mark Constructors 10 | 11 | +(instancetype)stepWithName:(NSString *)name function:(L3TestStepFunction)function { 12 | return [[self alloc] initWithName:name function:function]; 13 | } 14 | 15 | -(instancetype)initWithName:(NSString *)name function:(L3TestStepFunction)function { 16 | NSParameterAssert(name != nil); 17 | NSParameterAssert(function != nil); 18 | if ((self = [super init])) { 19 | _name = name; 20 | _function = function; 21 | } 22 | return self; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestSuite.h: -------------------------------------------------------------------------------- 1 | // L3TestSuite.h 2 | // Created by Rob Rix on 2012-11-07. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | #import 7 | #import 8 | 9 | @class L3TestCase, L3TestStep; 10 | 11 | extern NSString * const L3TestSuiteSetUpStepName; 12 | extern NSString * const L3TestSuiteTearDownStepName; 13 | 14 | extern NSString *L3MachOImagePathForAddress(void *address) __attribute__((weak_import)); 15 | 16 | 17 | @interface L3TestSuite : NSObject 18 | 19 | #pragma mark Constructors 20 | 21 | +(instancetype)defaultSuite; 22 | 23 | +(instancetype)testSuiteWithName:(NSString *)name file:(NSString *)file line:(NSUInteger)line; 24 | +(instancetype)testSuiteWithName:(NSString *)name; 25 | 26 | 27 | #pragma mark Mach-O image path 28 | 29 | @property (strong, nonatomic) NSString *imagePath; 30 | 31 | 32 | #pragma mark State 33 | 34 | @property (strong, nonatomic) Class stateClass; 35 | 36 | 37 | #pragma mark Tests 38 | 39 | // must be unique by name within this suite 40 | -(void)addTest:(id)test; 41 | 42 | @property (copy, nonatomic, readonly) NSArray *tests; 43 | @property (copy, nonatomic, readonly) NSDictionary *testsByName; 44 | 45 | 46 | #pragma mark Steps 47 | 48 | -(void)addStep:(L3TestStep *)step; 49 | 50 | @property (copy, nonatomic, readonly) NSDictionary *steps; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestSuite.m: -------------------------------------------------------------------------------- 1 | // L3TestSuite.m 2 | // Created by Rob Rix on 2012-11-07. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "L3TestCase.h" 6 | #import "L3TestSuite.h" 7 | #import "L3TestState.h" 8 | #import "Lagrangian.h" 9 | #import 10 | 11 | @l3_suite_interface (L3TestSuite) 12 | @property NSMutableArray *visitedTests; 13 | @end 14 | 15 | @l3_set_up { 16 | test.visitedTests = [NSMutableArray new]; 17 | } 18 | 19 | 20 | NSString * const L3TestSuiteSetUpStepName = @"set up"; 21 | NSString * const L3TestSuiteTearDownStepName = @"tear down"; 22 | 23 | 24 | @interface L3TestSuite () 25 | 26 | @property (copy, nonatomic, readwrite) NSString *name; 27 | 28 | @property (copy, nonatomic, readwrite) NSString *file; 29 | @property (assign, nonatomic, readwrite) NSUInteger line; 30 | 31 | @property (strong, nonatomic, readonly) NSMutableArray *mutableTests; 32 | @property (strong, nonatomic, readonly) NSMutableDictionary *mutableTestsByName; 33 | 34 | @property (strong, nonatomic, readonly) NSMutableDictionary *mutableSteps; 35 | 36 | @end 37 | 38 | @implementation L3TestSuite 39 | 40 | #pragma mark Constructors 41 | 42 | +(instancetype)defaultSuite { 43 | static L3TestSuite *defaultSuite = nil; 44 | static dispatch_once_t onceToken; 45 | dispatch_once(&onceToken, ^{ 46 | defaultSuite = [self testSuiteWithName:[NSBundle mainBundle].bundlePath.lastPathComponent ?: [NSProcessInfo processInfo].processName]; 47 | }); 48 | return defaultSuite; 49 | } 50 | 51 | 52 | +(instancetype)testSuiteWithName:(NSString *)name file:(NSString *)file line:(NSUInteger)line { 53 | return [[self alloc] initWithName:name file:file line:line]; 54 | } 55 | 56 | +(instancetype)testSuiteWithName:(NSString *)name { 57 | return [[self alloc] initWithName:name file:nil line:0]; 58 | } 59 | 60 | -(instancetype)initWithName:(NSString *)name file:(NSString *)file line:(NSUInteger)line { 61 | NSParameterAssert(name != nil); 62 | if ((self = [super init])) { 63 | _name = [name copy]; 64 | 65 | _file = [file copy]; 66 | _line = line; 67 | 68 | _stateClass = [L3TestState class]; 69 | 70 | _mutableTests = [NSMutableArray new]; 71 | _mutableTestsByName = [NSMutableDictionary new]; 72 | 73 | _mutableSteps = [NSMutableDictionary new]; 74 | } 75 | return self; 76 | } 77 | 78 | 79 | #pragma mark Tests 80 | 81 | -(void)addTest:(id)test { 82 | NSParameterAssert(test != nil); 83 | NSParameterAssert([self.testsByName objectForKey:test.name] == nil); 84 | 85 | [self.mutableTests addObject:test]; 86 | [self.mutableTestsByName setObject:test forKey:test.name]; 87 | } 88 | 89 | 90 | -(NSArray *)tests { 91 | return self.mutableTests; 92 | } 93 | 94 | -(NSDictionary *)testsByName { 95 | return self.mutableTestsByName; 96 | } 97 | 98 | 99 | -(void)setStateClass:(Class)stateClass { 100 | NSAssert(stateClass != nil, @"No state class found for suite ‘%@’. Did you forget to add a @l3_suite_implementation for this suite? Did you add one but give it the wrong identifier?", self.name); 101 | _stateClass = stateClass; 102 | } 103 | 104 | 105 | #pragma mark Steps 106 | 107 | -(void)addStep:(L3TestStep *)step { 108 | NSParameterAssert(step != nil); 109 | NSParameterAssert([self.steps objectForKey:step.name] == nil); 110 | 111 | [self.mutableSteps setObject:step forKey:step.name]; 112 | } 113 | 114 | 115 | -(NSDictionary *)steps { 116 | return self.mutableSteps; 117 | } 118 | 119 | 120 | #pragma mark L3Test 121 | 122 | -(bool)isComposite { 123 | return YES; 124 | } 125 | 126 | 127 | #pragma mark Visitors 128 | 129 | @l3_test("visits its child tests lazily") { 130 | L3TestSuite *parent = [L3TestSuite testSuiteWithName:@"parent"]; 131 | L3TestSuite *child = [L3TestSuite testSuiteWithName:@"child"]; 132 | [parent addTest:child]; 133 | [parent acceptVisitor:test]; 134 | l3_assert(test.visitedTests, l3_equals(@[parent, child])); 135 | } 136 | 137 | -(void)acceptVisitor:(id)visitor inTestSuite:(L3TestSuite *)parentSuite { 138 | [visitor testSuite:self inTestSuite:parentSuite withChildren:^{ 139 | for (id test in self.tests) { 140 | [test acceptVisitor:visitor inTestSuite:self]; 141 | } 142 | }]; 143 | 144 | } 145 | 146 | -(void)acceptVisitor:(id)visitor { 147 | [self acceptVisitor:visitor inTestSuite:nil]; 148 | } 149 | 150 | @end 151 | 152 | 153 | @l3_suite_implementation (L3TestSuite) 154 | 155 | -(void)testCase:(L3TestCase *)testCase inTestSuite:(L3TestSuite *)suite { 156 | [self.visitedTests addObject:testCase]; 157 | } 158 | 159 | -(void)testSuite:(L3TestSuite *)testSuite inTestSuite:(L3TestSuite *)suite withChildren:(void (^)())block { 160 | [self.visitedTests addObject:testSuite]; 161 | 162 | block(); 163 | } 164 | 165 | @end 166 | 167 | 168 | NSString *L3MachOImagePathForAddress(void *address) { 169 | NSString *imagePath = nil; 170 | 171 | Dl_info info = {}; 172 | if (dladdr(address, &info)) { 173 | imagePath = @(info.dli_fname); 174 | } 175 | 176 | return imagePath; 177 | } 178 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3TestVisitor.h: -------------------------------------------------------------------------------- 1 | // L3TestVisitor.h 2 | // Created by Rob Rix on 2012-12-15. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @class L3TestCase; 8 | @class L3TestSuite; 9 | 10 | @protocol L3TestVisitor 11 | 12 | -(void)testCase:(L3TestCase *)testCase inTestSuite:(L3TestSuite *)suite; 13 | -(void)testSuite:(L3TestSuite *)testSuite inTestSuite:(L3TestSuite *)suite withChildren:(void(^)())block; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/L3Types.h: -------------------------------------------------------------------------------- 1 | // L3Types.h 2 | // Created by Rob Rix on 2012-11-08. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | #if TARGET_OS_IPHONE 8 | #import 9 | #endif 10 | 11 | @class L3TestCase, L3TestState, L3TestStep; 12 | 13 | #pragma mark Test functions 14 | 15 | typedef void(*L3TestCaseFunction)(L3TestState *, L3TestCase *); 16 | typedef void(*L3TestStepFunction)(L3TestState *, L3TestCase *, L3TestStep *); 17 | 18 | 19 | #pragma mark Assert patterns 20 | 21 | typedef bool(^L3Pattern)(id); 22 | 23 | 24 | #pragma mark Object conversion 25 | 26 | #import 27 | #import 28 | 29 | // start off by assuming all conversions to be unavailable (is this necessary?) 30 | static inline id l3_to_object(...) __attribute__((overloadable, unavailable)); 31 | 32 | #define l3_define_to_object_by_boxing_with_type(memo, type) \ 33 | __attribute__((overloadable)) static inline id l3_to_object(type x) { return @(x); }; \ 34 | memo 35 | 36 | // box these types automatically 37 | rx_fold(l3_define_to_object_by_boxing_with_type, , 38 | uint64_t, uint32_t, uint16_t, uint8_t, 39 | int64_t, int32_t, int16_t, int8_t, 40 | unsigned long, signed long, 41 | double, float, 42 | bool, 43 | char *) 44 | __attribute__((overloadable)) static inline id l3_to_object(id x) { return x; } 45 | __attribute__((overloadable)) static inline id l3_to_object(void *x) { return [NSValue valueWithPointer:0]; } 46 | __attribute__((overloadable)) static inline id l3_to_object(NSRange r) { return [NSValue valueWithRange:r]; } 47 | 48 | // Core Graphics 49 | #if TARGET_OS_IPHONE 50 | __attribute__((overloadable)) static inline id l3_to_object(CGPoint p) { return [NSValue valueWithCGPoint:p]; } 51 | __attribute__((overloadable)) static inline id l3_to_object(CGRect r) { return [NSValue valueWithCGRect:r]; } 52 | __attribute__((overloadable)) static inline id l3_to_object(CGSize s) { return [NSValue valueWithCGSize:s]; } 53 | #else 54 | __attribute__((overloadable)) static inline id l3_to_object(CGPoint p) { return [NSValue valueWithPoint:p]; } 55 | __attribute__((overloadable)) static inline id l3_to_object(CGRect r) { return [NSValue valueWithRect:r]; } 56 | __attribute__((overloadable)) static inline id l3_to_object(CGSize s) { return [NSValue valueWithSize:s]; } 57 | #endif 58 | 59 | #pragma mark Pattern conversion 60 | 61 | #define l3_to_pattern(x) \ 62 | (^L3Pattern{ __typeof__(x) y = x; return l3_to_pattern_f(y); }()) 63 | 64 | 65 | static inline L3Pattern l3_to_pattern_f(...) __attribute__((overloadable, unavailable)); 66 | 67 | #define l3_define_to_pattern_by_equality_with_type(memo, type) \ 68 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(type x) { \ 69 | return ^bool(id y){ return [y isEqual:l3_to_object(x)]; }; \ 70 | } \ 71 | memo 72 | 73 | rx_fold(l3_define_to_pattern_by_equality_with_type, , 74 | uint64_t, uint32_t, uint16_t, uint8_t, 75 | int64_t, int32_t, int16_t, int8_t, 76 | unsigned long, signed long, 77 | double, float, 78 | bool, 79 | char *, 80 | id) 81 | 82 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(L3Pattern x) { return x; } 83 | 84 | // nil comparisons 85 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(void *x) { return ^bool(id y){ return (__bridge void *)y == x; }; } 86 | 87 | // floating point comparisons with epsilon 88 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(double x, double epsilon) { return ^bool(id y){ return [y isKindOfClass:[NSNumber class]] && fabs(x - [y doubleValue]) < epsilon; }; } 89 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(float x, float epsilon) { return ^bool(id y){ return [y isKindOfClass:[NSNumber class]] && fabsf(x - [y doubleValue]) < epsilon; }; } 90 | 91 | // NSRange 92 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(NSRange r) { 93 | return ^bool(id o){ return NSEqualRanges(r, [o rangeValue]); }; 94 | } 95 | 96 | // Core Graphics 97 | #if TARGET_OS_IPHONE 98 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(CGPoint p) { 99 | return ^bool(id o) { return CGPointEqualToPoint(p, [o CGPointValue]); }; 100 | } 101 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(CGRect r) { 102 | return ^bool(id o) { return CGRectEqualToRect(r, [o CGRectValue]); }; 103 | } 104 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(CGSize s) { 105 | return ^bool(id o) { return CGSizeEqualToSize(s, [o CGSizeValue]); }; 106 | } 107 | #else 108 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(CGPoint p) { 109 | return ^bool(id o) { return CGPointEqualToPoint(p, [o pointValue]); }; 110 | } 111 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(CGRect r) { 112 | return ^bool(id o) { return CGRectEqualToRect(r, [o rectValue]); }; 113 | } 114 | __attribute__((overloadable)) static inline L3Pattern l3_to_pattern_f(CGSize s) { 115 | return ^bool(id o) { return CGSizeEqualToSize(s, [o sizeValue]); }; 116 | } 117 | #endif 118 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/Lagrangian-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.antitypical.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2013 Rob Rix. All rights reserved. 27 | NSPrincipalClass 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/Lagrangian-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'lagrangian' target in the 'lagrangian' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/Lagrangian.m: -------------------------------------------------------------------------------- 1 | // Lagrangian.m 2 | // Created by Rob Rix on 2012-11-05. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import "Lagrangian.h" 6 | 7 | @l3_suite("Lagrangian"); 8 | 9 | static void l3_dummy_test_case_function(L3TestState *test, L3TestCase *self); 10 | 11 | 12 | #pragma mark Suites 13 | 14 | @l3_test("suites record the file and line number where they were defined") { 15 | l3_assert(test.suite.file.lastPathComponent, l3_equals(@"Lagrangian.m")); 16 | l3_assert(test.suite.line, l3_equals(7)); 17 | } 18 | 19 | @l3_test("suites refer to the mach-o image path that defined them") { 20 | NSSet *images = [NSSet setWithObjects:@"Lagrangian.dylib", @"Lagrangian", nil]; 21 | l3_assert([images containsObject:test.suite.imagePath.lastPathComponent], l3_equals(YES)); 22 | } 23 | 24 | 25 | @l3_test("set up functions are used to define state to be available during each test") { 26 | l3_assert(test[@"case"], l3_equalTo(self)); 27 | } 28 | 29 | @l3_set_up { 30 | test[@"case"] = self; 31 | } 32 | 33 | 34 | #pragma mark Cases 35 | 36 | @l3_test("test cases take a reference to their test case") { 37 | l3_assert(self, l3_not(nil)); 38 | } 39 | 40 | @l3_test("test cases take a reference to their test state") { 41 | l3_assert(test, l3_not(nil)); 42 | } 43 | 44 | 45 | #pragma mark State 46 | 47 | @l3_test("tests have a reference to their suite via their state") { 48 | l3_assert(test.suite, l3_not(nil)); 49 | l3_assert(test.suite, l3_isKindOfClass([L3TestSuite class])); 50 | l3_assert(test.suite.name, l3_equals(@"Lagrangian")); 51 | } 52 | 53 | 54 | #pragma mark Steps 55 | 56 | @l3_step("Create a step") { 57 | test[@"step"] = step; 58 | } 59 | 60 | @l3_test("steps are not run automatically") { 61 | l3_assert(test[@"step"], l3_equals(nil)); 62 | } 63 | 64 | @l3_test("steps are reified and collected in the suite") { 65 | l3_assert(test.suite.steps[@"Create a step"], l3_not(nil)); 66 | } 67 | 68 | @l3_test("steps can be performed individually") { 69 | [self performStep:test.suite.steps[@"Create a step"] withState:test]; 70 | l3_assert([test[@"step"] name], l3_equals(@"Create a step")); 71 | } 72 | 73 | 74 | @l3_step("Create a step indirectly") { 75 | [self performStep:test.suite.steps[@"Create a step"] withState:test]; 76 | } 77 | 78 | @l3_test("steps can perform other steps") { 79 | [self performStep:test.suite.steps[@"Create a step indirectly"] withState:test]; 80 | l3_assert([test[@"step"] name], @"Create a step"); 81 | } 82 | 83 | 84 | #pragma mark Step assertions 85 | 86 | @l3_step("Passing assertions") { 87 | l3_assert(YES, YES); 88 | } 89 | 90 | @l3_step("Failing assertions") { 91 | l3_assert(NO, YES); 92 | } 93 | 94 | 95 | @l3_test("steps return the success/failure of their assertions (if any) when they are run") { 96 | l3_assert(l3_perform_step("Passing assertions"), YES); 97 | 98 | L3TestState *state = [[L3TestState alloc] initWithSuite:test.suite eventObserver:nil]; 99 | 100 | L3TestCase *testCase = [L3TestCase testCaseWithName:@"failures" file:@"" __FILE__ line:__LINE__ function:l3_dummy_test_case_function]; 101 | l3_assert([testCase performStep:test.suite.steps[@"Failing assertions"] withState:state], l3_equals(NO)); 102 | } 103 | 104 | //@l3_tear_down { 105 | // 106 | //} 107 | 108 | 109 | //@l3_precondition { 110 | // 111 | //} 112 | 113 | //@l3_postcondition { 114 | // 115 | //} 116 | 117 | //@l3_invariant { 118 | // 119 | //} 120 | 121 | //@l3_benchmark { 122 | // 123 | //} 124 | 125 | static void l3_dummy_test_case_function(L3TestState *test, L3TestCase *self) {} 126 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/NSException+L3OCUnitCompatibility.h: -------------------------------------------------------------------------------- 1 | // NSException+L3OCUnitCompatibility.h 2 | // Created by Rob Rix on 7/22/2013. 3 | // Copyright (c) 2013 Rob Rix. All rights reserved. 4 | 5 | #import 6 | 7 | @interface NSException (L3OCUnitCompatibility) 8 | 9 | @property (nonatomic, readonly) NSString *filename; 10 | @property (nonatomic, readonly) NSNumber *lineNumber; 11 | 12 | +(NSException *)failureInFile:(NSString *)filename atLine:(int)lineNumber withDescription:(NSString *)formatString, ... NS_FORMAT_FUNCTION(3, 4); 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/NSException+L3OCUnitCompatibility.m: -------------------------------------------------------------------------------- 1 | // NSException+L3OCUnitCompatibility.m 2 | // Created by Rob Rix on 7/22/2013. 3 | // Copyright (c) 2013 Rob Rix. All rights reserved. 4 | 5 | #import "NSException+L3OCUnitCompatibility.h" 6 | 7 | @implementation NSException (L3OCUnitCompatibility) 8 | 9 | -(NSString *)filename { 10 | return self.userInfo[NSStringFromSelector(_cmd)]; 11 | } 12 | 13 | -(NSNumber *)lineNumber { 14 | return self.userInfo[NSStringFromSelector(_cmd)]; 15 | } 16 | 17 | 18 | +(NSException *)failureInFile:(NSString *)filename atLine:(int)lineNumber withDescription:(NSString *)formatString, ... { 19 | NSString *description = nil; 20 | if (formatString) { 21 | va_list arguments; 22 | va_start(arguments, formatString); 23 | description = [[NSString alloc] initWithFormat:formatString arguments:arguments]; 24 | va_end(arguments); 25 | } 26 | 27 | return [NSException exceptionWithName:@"L3Exception" reason:description userInfo:@{ 28 | @"filename": filename, 29 | @"lineNumber": @(lineNumber), 30 | }]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/lagrangian-tool-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.antitypical.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSApplicationCategoryType 26 | public.app-category.developer-tools 27 | LSMinimumSystemVersion 28 | ${MACOSX_DEPLOYMENT_TARGET} 29 | NSHumanReadableCopyright 30 | Copyright © 2012 Rob Rix. All rights reserved. 31 | 32 | 33 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/lagrangian-tool.m: -------------------------------------------------------------------------------- 1 | // lagrangian-tool.m 2 | // Created by Rob Rix on 2012-11-07. 3 | // Copyright (c) 2012 Rob Rix. All rights reserved. 4 | 5 | #import 6 | #import 7 | #import 8 | #import 9 | 10 | #import "L3TRDynamicLibrary.h" 11 | 12 | #import "Lagrangian.h" 13 | 14 | @l3_suite("lagrangian"); 15 | 16 | @l3_test("runs tests in dynamic libraries") { 17 | // give it a library 18 | // assert that it was loaded 19 | 20 | // give it a library 21 | // assert that the library’s tests were run 22 | 23 | NSLog(@"running a test: %@", self.name); 24 | } 25 | 26 | @l3_test("runs tests in applications") { 27 | NSLog(@"running a test: %@", self.name); 28 | } 29 | 30 | static void L3TRLogString(FILE *file, NSString *string) { 31 | fprintf(file, "%s", [string UTF8String]); 32 | fflush(stderr); 33 | } 34 | 35 | static void L3TRFailWithError(NSError *error) { 36 | L3TRLogString(stderr, @"lagrangian: fatal error: "); 37 | L3TRLogString(stderr, error.localizedDescription); 38 | L3TRLogString(stderr, @"\n"); 39 | exit(EXIT_FAILURE); 40 | } 41 | 42 | static NSString *L3TRPathListByAddingPath(NSString *list, NSString *path) { 43 | return list? 44 | [list stringByAppendingFormat:@":%@", path] 45 | : path; 46 | } 47 | 48 | NSString * const L3TRLagrangianFrameworkPathArgumentName = @"lagrangian-framework-path"; 49 | 50 | NSString * const L3TRDynamicLibraryPathEnvironmentVariableName = @"DYLD_LIBRARY_PATH"; 51 | 52 | #define L3TRTry(x) \ 53 | (^{ \ 54 | NSError *error = nil; \ 55 | __typeof__(x) _result = (x); \ 56 | if (!_result) \ 57 | L3TRFailWithError(error); \ 58 | return _result; \ 59 | }()) 60 | 61 | int main(int argc, const char *argv[]) { 62 | int result = EXIT_SUCCESS; 63 | @autoreleasepool { 64 | l3_main(argc, argv); 65 | 66 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 67 | NSProcessInfo *processInfo = [NSProcessInfo processInfo]; 68 | 69 | [defaults registerDefaults:@{ 70 | L3TRLagrangianFrameworkPathArgumentName: [[processInfo.arguments[0] stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"Lagrangian.framework"] 71 | }]; 72 | 73 | if (!NSClassFromString(@"L3TestRunner")) { 74 | NSBundle *frameworkBundle = [NSBundle bundleWithPath:[defaults stringForKey:L3TRLagrangianFrameworkPathArgumentName]]; 75 | L3TRTry([frameworkBundle loadAndReturnError:&error]); 76 | } 77 | 78 | NSString *frameworkPath = [defaults stringForKey:@"framework"]; 79 | NSString *libraryPath = [defaults stringForKey:@"library"]; 80 | NSString *command = [defaults stringForKey:@"command"]; 81 | 82 | if (frameworkPath) { 83 | NSBundle *frameworkBundle = [NSBundle bundleWithPath:frameworkPath]; 84 | L3TRTry([frameworkBundle loadAndReturnError:&error]); 85 | 86 | L3TestRunner *runner = [NSClassFromString(@"L3TestRunner") new]; 87 | 88 | runner.testSuitePredicate = [NSPredicate predicateWithFormat:@"(imagePath = NULL) || (imagePath CONTAINS[cd] %@)", frameworkPath.lastPathComponent]; 89 | 90 | [runner run]; 91 | 92 | [runner waitForTestsToComplete]; 93 | } else if (libraryPath) { 94 | L3TRTry([L3TRDynamicLibrary openLibraryAtPath:libraryPath error:&error]); 95 | 96 | L3TestRunner *runner = [NSClassFromString(@"L3TestRunner") new]; 97 | 98 | runner.testSuitePredicate = [NSPredicate predicateWithFormat:@"(imagePath = NULL) || (imagePath ENDSWITH[cd] %@)", libraryPath.lastPathComponent]; 99 | 100 | [runner run]; 101 | 102 | [runner waitForTestsToComplete]; 103 | } else if (command) { 104 | NSTask *task = [NSTask new]; 105 | 106 | task.launchPath = command; // fixme: this should probably include args or something 107 | 108 | NSMutableDictionary *environment = [processInfo.environment mutableCopy]; 109 | 110 | environment[L3TRDynamicLibraryPathEnvironmentVariableName] = L3TRPathListByAddingPath(environment[L3TRDynamicLibraryPathEnvironmentVariableName], [NSFileManager defaultManager].currentDirectoryPath); 111 | environment[@"L3_RUN_TESTS_ON_LAUNCH"] = @"YES"; 112 | // fixme: there’s no possible way that passing the predicate format as the literal body of an environment variable could ever go wrong 113 | environment[@"L3_SUITE_PREDICATE"] = [NSString stringWithFormat:@"(imagePath = NULL) || (imagePath ENDSWITH[cd] '%@')", command.lastPathComponent]; 114 | 115 | task.environment = environment; 116 | 117 | [task launch]; 118 | [task waitUntilExit]; 119 | } 120 | } 121 | return result; 122 | } 123 | -------------------------------------------------------------------------------- /External/Lagrangian/Lagrangian/lagrangian.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 2012-11-07 \" DATE 7 | .Dt lagrangian 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm lagrangian, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /External/Lagrangian/PLANNING.mdown: -------------------------------------------------------------------------------- 1 | # Planning 2 | 3 | This document is an overall todo/fixme list for the project. 4 | 5 | ## Major goals 6 | 7 | - @l3_benchmark 8 | - @l3_invariant 9 | - @l3_precondition 10 | - @l3_postcondition 11 | 12 | - documentation generation (as a visitor?) 13 | 14 | - iOS support (how will this work? Xcode-style bundle injection?) 15 | - for apps 16 | - for static libraries 17 | - for static frameworks 18 | - ship as a static framework for iOS 19 | 20 | - ship as a framework for OS X 21 | 22 | - assertions to test exceptions 23 | - handling of exceptions (as unexpected failures) 24 | - a better entry point for debugging failed assertions (analogous to objc_exception_throw, perhaps l3_assertion_fail) 25 | 26 | - tests concurrent with one another 27 | - tests run only on the main thread 28 | 29 | - in debug builds, when enabled, add some way to list and trigger test suites and individual cases 30 | 31 | ## Configuration 32 | 33 | - a configuration macro to remove the prefixes on definition names, e.g. l3_assert 34 | 35 | ## Tags 36 | 37 | - tags for tests: 38 | 39 | @l3_test("use of tags", @tag, @other_tag) { … } 40 | 41 | - use tags to define suites 42 | - include a tag for the Mach-O image (by name?) by default 43 | 44 | ## Test runners 45 | 46 | - don’t pass predicate formats in an env var, that’s insane 47 | - switch back to Xcode by means of the tool (not L3TestRunner), and only do that if running from Xcode in the first place (inspect parent process stack?) 48 | 49 | ## Open questions 50 | 51 | - should @l3_test, @l3_suite, etc, drop the @? 52 | - @jspahrsummers uses @ only when it feels like something should be a language feature. 53 | - how do preconditions, postconditions, and invariants work? predicates which are matched before/after/both for invocations of a given method? how? dynamic subclass + override during tests? (that sounds like fun!) 54 | - differential-prototypal suite inheritance? 55 | - mess around with multiple suites per file; does it make any sense? is it useful? 56 | - declaring common test steps in a suite in another file sounds more useful 57 | - can tests be defined in headers? is that sensible at all? 58 | 59 | ## Fixmes 60 | 61 | - test the assertion patterns 62 | - exit() with EXIT_SUCCESS or EXIT_FAILURE depending on whether the tests succeed or fail -------------------------------------------------------------------------------- /External/Lagrangian/README.mdown: -------------------------------------------------------------------------------- 1 | # Lagrangian 2 | 3 | Unit tests inextricably linked to your project’s mass. 4 | 5 | A function that summarizes the dynamics of the system. -------------------------------------------------------------------------------- /External/Lagrangian/Xcode/Project Templates/Lagrangian/Lagrangian Xcode Integration Bundle.xctemplate/TemplateIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotMail/DotSnap/c18a41ab9ad7e28f353e67d9c98b3deffc5ea526/External/Lagrangian/Xcode/Project Templates/Lagrangian/Lagrangian Xcode Integration Bundle.xctemplate/TemplateIcon.icns -------------------------------------------------------------------------------- /External/Lagrangian/Xcode/Project Templates/Lagrangian/Lagrangian Xcode Integration Bundle.xctemplate/TemplateInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Kind 6 | Xcode.Xcode3.ProjectTemplateUnitKind 7 | Identifier 8 | com.antitypical.lagrangian.xcodeIntegrationBundle 9 | 13 | 14 | Description 15 | This target exists to convince Xcode to running Lagrangian’s tests. You only need one of these per project, and you don’t need to add any sources or resources to it. 16 | Concrete 17 | 18 | 19 | SortOrder 20 | 1 21 | 22 | 41 | 42 | TargetOnly 43 | 44 | Targets 45 | 46 | 47 | ProductType 48 | com.apple.product-type.bundle 49 | 50 | Name 51 | ___PACKAGENAME___ 52 | 53 | SharedSettings 54 | 55 | PRODUCT_NAME 56 | Lagrangian 57 | WRAPPER_EXTENSION 58 | octest 59 | TEST_HOST 60 | $(BUILT_PRODUCTS_DIR)/lagrangian 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DotSnap 2 | ======= 3 | 4 | DotSnap helps you to finally see your beautiful desktop wallpaper again. Quickly change your default screenshot folder where ever you are, as well as give your screenshots a real name even before you take them to stay organized. 5 | 6 | - Set your default folder once, or change it as often as you want with quick access in your top bar. 7 | 8 | - Give your screenshots a default prefix name, or change it as often as you want. We even save your 5 last used names. Optional add a date stamp to it. 9 | 10 | Read more about it & See how it works: 11 | http://bit.ly/1qeoMZr 12 | 13 | ## Installation 14 | 15 | - Open a terminal prompt and execute: 16 | 17 | `$ git clone --recursive https://github.com/DotMail/DotSnap.git ; cd DotSnap ; git submodule update -i --recursive` 18 | 19 | - After git finishes pulling all of the project's dependencies, open the provided Xcode project and click Run. 20 | - Profit 21 | 22 | ## Known Issues 23 | 24 | - 1. For users using CloudApp: If CloudApp auto-upload is active, CloudApp sometimes uploads broken screenshots because DotSnap already moved the screenshot into a different folder. Since CloudApp became very slow since the past few months, this issue occurs more often than expected. 25 | 26 | 27 | - 2. Since Dropbox introduced an option to move screenshots automatically to your Dropbox folder, DotSnap runs into some issues here as well. 28 | 29 | 30 | ## How DotSnap works 31 | DotSnap works in the background and is compatible with your default Mac screenshot shortcut. It essentially just moves your files to the folder you set inside DotSnap as well as renames the file according to your setting in DotSnap. If you haven't activated the TimeStamp option, files with the same name will get a unique number added to the end. 32 | 33 | ## Contact 34 | If you have any questions, comments, or want to contribute, you can reach us on Twitter [@CodaFi_](https://twitter.com/CodaFi_) and [@schneidertobias](https://twitter.com/schneidertobias) 35 | 36 | ## Download latest Build here (if you just want to install it) 37 | https://github.com/DotMail/DotSnap/releases/tag/1.0 38 | 39 | --------------------------------------------------------------------------------