├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── drag.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── drag.xcscheme └── drag ├── AppDelegate.h ├── AppDelegate.m ├── DragView.h ├── DragView.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .DS_Store 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | xcuserdata 12 | *.xccheckout 13 | *.moved-aside 14 | DerivedData 15 | *.xcuserstate 16 | Carthage 17 | Components.plist 18 | drag.pkg 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | xcode_project: drag.xcodeproj 3 | xcode_scheme: drag 4 | osx_image: xcode7.1 5 | before_install: true 6 | install: true 7 | git: 8 | submodules: false 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | INSTALL_DIRECTORY=/usr/local/bin 2 | TEMPORARY_DIRECTORY?=/tmp/drag-build.dst 3 | TEMPORARY_PACKAGE_DIRECTORY?=/tmp/drag-package.dst 4 | 5 | XCODE_COMMAND=$(shell { command -v xctool || command -v xcodebuild; } 2>/dev/null) 6 | XCODE_FLAGS=-project 'drag.xcodeproj' -scheme 'drag' -configuration 'Release' DSTROOT=$(TEMPORARY_DIRECTORY) 7 | 8 | PACKAGE_NAME=drag.pkg 9 | TEMPORARY_PACKAGE_NAME=$(TEMPORARY_PACKAGE_DIRECTORY)/temp.pkg 10 | COMPONENTS_PLIST_NAME=$(TEMPORARY_PACKAGE_DIRECTORY)/Components.plist 11 | 12 | VERSION=0.1 13 | 14 | .PHONY: all clean install package uninstall 15 | 16 | all: 17 | $(XCODE_COMMAND) $(XCODE_FLAGS) build 18 | 19 | clean: 20 | rm -f "$(COMPONENTS_PLIST_NAME)" 21 | rm -f "$(PACKAGE_NAME)" 22 | rm -rf "$(TEMPORARY_DIRECTORY)" 23 | $(XCODE_COMMAND) $(XCODE_FLAGS) clean 24 | 25 | install: package 26 | sudo installer -pkg drag.pkg -target / 27 | 28 | package: clean 29 | $(XCODE_COMMAND) $(XCODE_FLAGS) install 30 | 31 | mkdir -p "$(TEMPORARY_PACKAGE_DIRECTORY)" 32 | 33 | pkgbuild \ 34 | --analyze \ 35 | --root "$(TEMPORARY_DIRECTORY)" \ 36 | "$(COMPONENTS_PLIST_NAME)" 37 | 38 | pkgbuild \ 39 | --component-plist "$(COMPONENTS_PLIST_NAME)" \ 40 | --identifier "com.natestedman.drag" \ 41 | --install-location "/" \ 42 | --root "$(TEMPORARY_DIRECTORY)" \ 43 | --version "$(VERSION)" \ 44 | "$(TEMPORARY_PACKAGE_NAME)" 45 | 46 | productsign \ 47 | --sign "Developer ID Installer" \ 48 | "$(TEMPORARY_PACKAGE_NAME)" \ 49 | "$(PACKAGE_NAME)" 50 | 51 | rm "$(TEMPORARY_PACKAGE_NAME)" 52 | rm "$(COMPONENTS_PLIST_NAME)" 53 | 54 | uninstall: 55 | rm -f "$(INSTALL_DIRECTORY)/drag" 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `drag` 2 | [![Build Status](https://travis-ci.org/natestedman/drag.svg?branch=master)](https://travis-ci.org/natestedman/Attributed) 3 | [![License](https://img.shields.io/badge/license-Creative%20Commons%20Zero%20v1.0%20Universal-blue.svg)](https://creativecommons.org/publicdomain/zero/1.0/) 4 | 5 | A command-line utility for OS X drag-and-drop. 6 | 7 | ![Preview Video](https://zippy.gfycat.com/SkeletalGoldenFlyinglemur.gif) 8 | 9 | ## Installation 10 | Download `drag.pkg` from [Releases](https://github.com/natestedman/drag/releases). 11 | 12 | ## Usage 13 | ```bash 14 | drag [files to drag]... 15 | ``` 16 | 17 | This creates a window with a drag source for the files passed as arguments, centered on the current cursor position. To drag, just click. 18 | 19 | The program will exit and the window will disappear when dragging completes, or if the escape key is pressed. 20 | 21 | ## Building a Package 22 | A `.pkg` installer can be built with `make package`. This requires that a Developer ID is installed. 23 | -------------------------------------------------------------------------------- /drag.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 438C5A881BB0FE960099062E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 438C5A871BB0FE960099062E /* main.m */; }; 11 | 438C5A921BB0FED50099062E /* DragView.m in Sources */ = {isa = PBXBuildFile; fileRef = 438C5A911BB0FED50099062E /* DragView.m */; }; 12 | 438C5A951BB107A70099062E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 438C5A941BB107A70099062E /* AppDelegate.m */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXCopyFilesBuildPhase section */ 16 | 438C5A821BB0FE960099062E /* CopyFiles */ = { 17 | isa = PBXCopyFilesBuildPhase; 18 | buildActionMask = 2147483647; 19 | dstPath = /usr/share/man/man1/; 20 | dstSubfolderSpec = 0; 21 | files = ( 22 | ); 23 | runOnlyForDeploymentPostprocessing = 1; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 438C5A841BB0FE960099062E /* drag */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = drag; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 438C5A871BB0FE960099062E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 30 | 438C5A901BB0FED50099062E /* DragView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DragView.h; sourceTree = ""; }; 31 | 438C5A911BB0FED50099062E /* DragView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DragView.m; sourceTree = ""; }; 32 | 438C5A931BB107A70099062E /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 33 | 438C5A941BB107A70099062E /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | 438C5A811BB0FE960099062E /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | 438C5A7B1BB0FE960099062E = { 48 | isa = PBXGroup; 49 | children = ( 50 | 438C5A861BB0FE960099062E /* drag */, 51 | 438C5A851BB0FE960099062E /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 438C5A851BB0FE960099062E /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 438C5A841BB0FE960099062E /* drag */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 438C5A861BB0FE960099062E /* drag */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 438C5A871BB0FE960099062E /* main.m */, 67 | 438C5A931BB107A70099062E /* AppDelegate.h */, 68 | 438C5A941BB107A70099062E /* AppDelegate.m */, 69 | 438C5A901BB0FED50099062E /* DragView.h */, 70 | 438C5A911BB0FED50099062E /* DragView.m */, 71 | ); 72 | path = drag; 73 | sourceTree = ""; 74 | }; 75 | /* End PBXGroup section */ 76 | 77 | /* Begin PBXNativeTarget section */ 78 | 438C5A831BB0FE960099062E /* drag */ = { 79 | isa = PBXNativeTarget; 80 | buildConfigurationList = 438C5A8B1BB0FE960099062E /* Build configuration list for PBXNativeTarget "drag" */; 81 | buildPhases = ( 82 | 438C5A801BB0FE960099062E /* Sources */, 83 | 438C5A811BB0FE960099062E /* Frameworks */, 84 | 438C5A821BB0FE960099062E /* CopyFiles */, 85 | ); 86 | buildRules = ( 87 | ); 88 | dependencies = ( 89 | ); 90 | name = drag; 91 | productName = drag; 92 | productReference = 438C5A841BB0FE960099062E /* drag */; 93 | productType = "com.apple.product-type.tool"; 94 | }; 95 | /* End PBXNativeTarget section */ 96 | 97 | /* Begin PBXProject section */ 98 | 438C5A7C1BB0FE960099062E /* Project object */ = { 99 | isa = PBXProject; 100 | attributes = { 101 | LastSwiftUpdateCheck = 0700; 102 | LastUpgradeCheck = 0700; 103 | ORGANIZATIONNAME = "Nate Stedman"; 104 | TargetAttributes = { 105 | 438C5A831BB0FE960099062E = { 106 | CreatedOnToolsVersion = 7.0; 107 | }; 108 | }; 109 | }; 110 | buildConfigurationList = 438C5A7F1BB0FE960099062E /* Build configuration list for PBXProject "drag" */; 111 | compatibilityVersion = "Xcode 3.2"; 112 | developmentRegion = English; 113 | hasScannedForEncodings = 0; 114 | knownRegions = ( 115 | en, 116 | ); 117 | mainGroup = 438C5A7B1BB0FE960099062E; 118 | productRefGroup = 438C5A851BB0FE960099062E /* Products */; 119 | projectDirPath = ""; 120 | projectRoot = ""; 121 | targets = ( 122 | 438C5A831BB0FE960099062E /* drag */, 123 | ); 124 | }; 125 | /* End PBXProject section */ 126 | 127 | /* Begin PBXSourcesBuildPhase section */ 128 | 438C5A801BB0FE960099062E /* Sources */ = { 129 | isa = PBXSourcesBuildPhase; 130 | buildActionMask = 2147483647; 131 | files = ( 132 | 438C5A921BB0FED50099062E /* DragView.m in Sources */, 133 | 438C5A881BB0FE960099062E /* main.m in Sources */, 134 | 438C5A951BB107A70099062E /* AppDelegate.m in Sources */, 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | /* End PBXSourcesBuildPhase section */ 139 | 140 | /* Begin XCBuildConfiguration section */ 141 | 438C5A891BB0FE960099062E /* Debug */ = { 142 | isa = XCBuildConfiguration; 143 | buildSettings = { 144 | ALWAYS_SEARCH_USER_PATHS = NO; 145 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 146 | CLANG_CXX_LIBRARY = "libc++"; 147 | CLANG_ENABLE_MODULES = YES; 148 | CLANG_ENABLE_OBJC_ARC = YES; 149 | CLANG_WARN_BOOL_CONVERSION = YES; 150 | CLANG_WARN_CONSTANT_CONVERSION = YES; 151 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 152 | CLANG_WARN_EMPTY_BODY = YES; 153 | CLANG_WARN_ENUM_CONVERSION = YES; 154 | CLANG_WARN_INT_CONVERSION = YES; 155 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 156 | CLANG_WARN_UNREACHABLE_CODE = YES; 157 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 158 | COPY_PHASE_STRIP = NO; 159 | DEBUG_INFORMATION_FORMAT = dwarf; 160 | ENABLE_STRICT_OBJC_MSGSEND = YES; 161 | ENABLE_TESTABILITY = YES; 162 | GCC_C_LANGUAGE_STANDARD = gnu99; 163 | GCC_DYNAMIC_NO_PIC = NO; 164 | GCC_NO_COMMON_BLOCKS = YES; 165 | GCC_OPTIMIZATION_LEVEL = 0; 166 | GCC_PREPROCESSOR_DEFINITIONS = ( 167 | "DEBUG=1", 168 | "$(inherited)", 169 | ); 170 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 171 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 172 | GCC_WARN_UNDECLARED_SELECTOR = YES; 173 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 174 | GCC_WARN_UNUSED_FUNCTION = YES; 175 | GCC_WARN_UNUSED_VARIABLE = YES; 176 | MACOSX_DEPLOYMENT_TARGET = 10.10; 177 | MTL_ENABLE_DEBUG_INFO = YES; 178 | ONLY_ACTIVE_ARCH = YES; 179 | SDKROOT = macosx; 180 | }; 181 | name = Debug; 182 | }; 183 | 438C5A8A1BB0FE960099062E /* Release */ = { 184 | isa = XCBuildConfiguration; 185 | buildSettings = { 186 | ALWAYS_SEARCH_USER_PATHS = NO; 187 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 188 | CLANG_CXX_LIBRARY = "libc++"; 189 | CLANG_ENABLE_MODULES = YES; 190 | CLANG_ENABLE_OBJC_ARC = YES; 191 | CLANG_WARN_BOOL_CONVERSION = YES; 192 | CLANG_WARN_CONSTANT_CONVERSION = YES; 193 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 194 | CLANG_WARN_EMPTY_BODY = YES; 195 | CLANG_WARN_ENUM_CONVERSION = YES; 196 | CLANG_WARN_INT_CONVERSION = YES; 197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 198 | CLANG_WARN_UNREACHABLE_CODE = YES; 199 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 200 | COPY_PHASE_STRIP = NO; 201 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 202 | ENABLE_NS_ASSERTIONS = NO; 203 | ENABLE_STRICT_OBJC_MSGSEND = YES; 204 | GCC_C_LANGUAGE_STANDARD = gnu99; 205 | GCC_NO_COMMON_BLOCKS = YES; 206 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 207 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 208 | GCC_WARN_UNDECLARED_SELECTOR = YES; 209 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 210 | GCC_WARN_UNUSED_FUNCTION = YES; 211 | GCC_WARN_UNUSED_VARIABLE = YES; 212 | MACOSX_DEPLOYMENT_TARGET = 10.10; 213 | MTL_ENABLE_DEBUG_INFO = NO; 214 | SDKROOT = macosx; 215 | }; 216 | name = Release; 217 | }; 218 | 438C5A8C1BB0FE960099062E /* Debug */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | CLANG_ENABLE_MODULES = YES; 222 | CODE_SIGN_IDENTITY = ""; 223 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 224 | PRODUCT_NAME = "$(TARGET_NAME)"; 225 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 226 | }; 227 | name = Debug; 228 | }; 229 | 438C5A8D1BB0FE960099062E /* Release */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | CLANG_ENABLE_MODULES = YES; 233 | CODE_SIGN_IDENTITY = ""; 234 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 235 | PRODUCT_NAME = "$(TARGET_NAME)"; 236 | }; 237 | name = Release; 238 | }; 239 | /* End XCBuildConfiguration section */ 240 | 241 | /* Begin XCConfigurationList section */ 242 | 438C5A7F1BB0FE960099062E /* Build configuration list for PBXProject "drag" */ = { 243 | isa = XCConfigurationList; 244 | buildConfigurations = ( 245 | 438C5A891BB0FE960099062E /* Debug */, 246 | 438C5A8A1BB0FE960099062E /* Release */, 247 | ); 248 | defaultConfigurationIsVisible = 0; 249 | defaultConfigurationName = Release; 250 | }; 251 | 438C5A8B1BB0FE960099062E /* Build configuration list for PBXNativeTarget "drag" */ = { 252 | isa = XCConfigurationList; 253 | buildConfigurations = ( 254 | 438C5A8C1BB0FE960099062E /* Debug */, 255 | 438C5A8D1BB0FE960099062E /* Release */, 256 | ); 257 | defaultConfigurationIsVisible = 0; 258 | defaultConfigurationName = Release; 259 | }; 260 | /* End XCConfigurationList section */ 261 | }; 262 | rootObject = 438C5A7C1BB0FE960099062E /* Project object */; 263 | } 264 | -------------------------------------------------------------------------------- /drag.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /drag.xcodeproj/xcshareddata/xcschemes/drag.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /drag/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // drag 2 | // Written in 2015 by Nate Stedman 3 | // 4 | // To the extent possible under law, the author(s) have dedicated all copyright and 5 | // related and neighboring rights to this software to the public domain worldwide. 6 | // This software is distributed without any warranty. 7 | // 8 | // You should have received a copy of the CC0 Public Domain Dedication along with 9 | // this software. If not, see . 10 | 11 | @import Cocoa; 12 | 13 | @interface AppDelegate : NSObject 14 | 15 | @property (nonatomic, strong) NSArray *fileURLs; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /drag/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // drag 2 | // Written in 2015 by Nate Stedman 3 | // 4 | // To the extent possible under law, the author(s) have dedicated all copyright and 5 | // related and neighboring rights to this software to the public domain worldwide. 6 | // This software is distributed without any warranty. 7 | // 8 | // You should have received a copy of the CC0 Public Domain Dedication along with 9 | // this software. If not, see . 10 | 11 | #import "AppDelegate.h" 12 | #import "DragView.h" 13 | 14 | static NSImage *MaskImage(NSScreen *screen, CGSize size, CGFloat cornerRadius, CGFloat white) 15 | { 16 | CGFloat scale = screen.backingScaleFactor; 17 | 18 | NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] 19 | initWithBitmapDataPlanes:NULL 20 | pixelsWide:(NSInteger)(size.width * scale) 21 | pixelsHigh:(NSInteger)(size.height * scale) 22 | bitsPerSample:8 23 | samplesPerPixel:4 24 | hasAlpha:YES 25 | isPlanar:NO 26 | colorSpaceName:NSDeviceRGBColorSpace 27 | bitmapFormat:NSAlphaFirstBitmapFormat 28 | bytesPerRow:0 29 | bitsPerPixel:0]; 30 | 31 | NSGraphicsContext *current = [NSGraphicsContext currentContext]; 32 | NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:rep]; 33 | [NSGraphicsContext setCurrentContext:context]; 34 | 35 | [[NSColor colorWithWhite:white alpha:1.0] set]; 36 | [[NSBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, size.width * scale, size.height * scale) 37 | xRadius:cornerRadius 38 | yRadius:cornerRadius] fill]; 39 | 40 | [NSGraphicsContext setCurrentContext:current]; 41 | 42 | NSImage *image = [[NSImage alloc] initWithSize:size]; 43 | [image addRepresentation:rep]; 44 | 45 | return image; 46 | } 47 | 48 | static NSScreen *ScreenForPoint(NSPoint point) 49 | { 50 | for (NSScreen *screen in [NSScreen screens]) 51 | { 52 | if (NSPointInRect(point, screen.frame)) 53 | { 54 | return screen; 55 | } 56 | } 57 | 58 | return [NSScreen mainScreen]; 59 | } 60 | 61 | static CGFloat const WindowHeight = 100; 62 | 63 | @interface AppDelegate () 64 | 65 | @property (nonatomic, strong) NSWindow *window; 66 | @property (nonatomic, strong) DragView *dragView; 67 | 68 | @end 69 | 70 | @implementation AppDelegate 71 | 72 | -(void)applicationDidFinishLaunching:(NSNotification *)notification 73 | { 74 | // create a window 75 | CGPoint mouseLocation = [NSEvent mouseLocation]; 76 | 77 | DragViewSize dragViewSize = DragViewSizeForCount(_fileURLs.count); 78 | CGFloat aspect = (CGFloat)dragViewSize.width / (CGFloat)dragViewSize.height; 79 | CGSize windowSize = CGSizeMake(WindowHeight * aspect, WindowHeight); 80 | 81 | CGRect windowFrame = { 82 | .origin.x = mouseLocation.x - windowSize.width / 2, 83 | .origin.y = mouseLocation.y - windowSize.height / 2, 84 | .size = windowSize 85 | }; 86 | 87 | _window = [[NSWindow alloc] initWithContentRect:windowFrame 88 | styleMask:NSBorderlessWindowMask 89 | backing:NSBackingStoreBuffered 90 | defer:YES]; 91 | _window.level = CGShieldingWindowLevel(); 92 | _window.hasShadow = YES; 93 | _window.opaque = NO; 94 | _window.backgroundColor = [NSColor clearColor]; 95 | 96 | // views 97 | CGRect viewFrame = { .origin = CGPointZero, .size = windowSize }; 98 | 99 | // create visual effect view as the root container 100 | NSImageView *view = [[NSImageView alloc] initWithFrame:viewFrame]; 101 | NSScreen *screen = ScreenForPoint(mouseLocation); 102 | view.image = MaskImage(screen, windowSize, 10, 0.2); 103 | _window.contentView = view; 104 | 105 | // add drag view 106 | _dragView = [[DragView alloc] initWithFrame:viewFrame]; 107 | _dragView.fileURLs = _fileURLs; 108 | [view addSubview:_dragView]; 109 | 110 | [_window makeKeyAndOrderFront:nil]; 111 | 112 | // drag view behavior 113 | __weak typeof(self) weakSelf = self; 114 | 115 | _dragView.startDrag = ^(NSEvent *event) { 116 | view.image = MaskImage(screen, windowSize, 10, 0.05); 117 | 118 | NSMutableArray *items = [NSMutableArray arrayWithCapacity:weakSelf.fileURLs.count]; 119 | 120 | for (NSURL *URL in weakSelf.fileURLs) 121 | { 122 | NSPasteboardItem *pasteboardItem = [NSPasteboardItem new]; 123 | NSData *data = [URL.absoluteString dataUsingEncoding:NSUTF8StringEncoding]; 124 | [pasteboardItem setData:data forType:(__bridge NSString*)kUTTypeFileURL]; 125 | 126 | NSDraggingItem *item = [[NSDraggingItem alloc] initWithPasteboardWriter:pasteboardItem]; 127 | 128 | item.imageComponentsProvider = ^{ 129 | NSDraggingImageComponent *component = [NSDraggingImageComponent new]; 130 | component.contents = [[NSWorkspace sharedWorkspace] iconForFile:URL.path]; 131 | return @[component]; 132 | }; 133 | 134 | [items addObject:item]; 135 | } 136 | 137 | NSDraggingSession *session = [weakSelf.dragView beginDraggingSessionWithItems:items event:event source:weakSelf]; 138 | session.animatesToStartingPositionsOnCancelOrFail = YES; 139 | session.draggingFormation = NSDraggingFormationPile; 140 | }; 141 | } 142 | 143 | #pragma mark - Dragging Source 144 | -(void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation 145 | { 146 | exit(EXIT_SUCCESS); 147 | } 148 | 149 | -(NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context 150 | { 151 | switch (context) 152 | { 153 | case NSDraggingContextOutsideApplication: 154 | return NSDragOperationCopy; 155 | case NSDraggingContextWithinApplication: 156 | default: 157 | return NSDragOperationNone; 158 | } 159 | } 160 | 161 | @end 162 | -------------------------------------------------------------------------------- /drag/DragView.h: -------------------------------------------------------------------------------- 1 | // drag 2 | // Written in 2015 by Nate Stedman 3 | // 4 | // To the extent possible under law, the author(s) have dedicated all copyright and 5 | // related and neighboring rights to this software to the public domain worldwide. 6 | // This software is distributed without any warranty. 7 | // 8 | // You should have received a copy of the CC0 Public Domain Dedication along with 9 | // this software. If not, see . 10 | 11 | #import 12 | 13 | typedef struct 14 | { 15 | NSUInteger width; 16 | NSUInteger height; 17 | } DragViewSize; 18 | 19 | FOUNDATION_EXTERN DragViewSize DragViewSizeForCount(NSUInteger count); 20 | 21 | @interface DragView : NSView 22 | 23 | @property (nonatomic, nullable, copy) NSArray *fileURLs; 24 | 25 | @property (nonatomic, nullable, copy) void(^startDrag)(NSEvent *__nonnull mouseDownEvent); 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /drag/DragView.m: -------------------------------------------------------------------------------- 1 | // drag 2 | // Written in 2015 by Nate Stedman 3 | // 4 | // To the extent possible under law, the author(s) have dedicated all copyright and 5 | // related and neighboring rights to this software to the public domain worldwide. 6 | // This software is distributed without any warranty. 7 | // 8 | // You should have received a copy of the CC0 Public Domain Dedication along with 9 | // this software. If not, see . 10 | 11 | #import "DragView.h" 12 | 13 | DragViewSize DragViewSizeForCount(NSUInteger count) 14 | { 15 | // determine the width 16 | DragViewSize size = { 1, 0 }; 17 | for (; size.width * size.width < count; size.width++); 18 | 19 | // determine height based on width 20 | size.height = count / size.width + ((count % size.width) == 0 ? 0 : 1); 21 | 22 | return size; 23 | } 24 | 25 | static inline CGSize FitSizeToSize(CGSize size, CGSize fitToSize) 26 | { 27 | CGFloat ratio = MIN(1.0, MIN(fitToSize.width / size.width, fitToSize.height / size.height)); 28 | size.width = roundf(size.width * ratio); 29 | size.height = roundf(size.height * ratio); 30 | 31 | return size; 32 | } 33 | 34 | @implementation DragView 35 | 36 | -(void)mouseDown:(NSEvent *)event 37 | { 38 | if (_startDrag) 39 | { 40 | _startDrag(event); 41 | } 42 | else 43 | { 44 | [super mouseDown:event]; 45 | } 46 | } 47 | 48 | -(BOOL)isFlipped 49 | { 50 | return YES; 51 | } 52 | 53 | -(void)drawRect:(NSRect)dirtyRect 54 | { 55 | [super drawRect:dirtyRect]; 56 | 57 | NSInteger count = _fileURLs.count; 58 | DragViewSize dragViewSize = DragViewSizeForCount(count); 59 | 60 | CGRect rect = CGRectInset(self.bounds, 5, 5); 61 | CGRect scaled = CGRectMake(rect.origin.x, 62 | rect.origin.y, 63 | rect.size.width / dragViewSize.width, 64 | rect.size.height / dragViewSize.height); 65 | 66 | for (NSInteger i = 0; i < count; i++) 67 | { 68 | // inset rects for separators 69 | CGRect inset = CGRectInset(scaled, 2, 2); 70 | 71 | // base origins before centering image 72 | CGFloat originX = inset.origin.x + scaled.size.width * (i % dragViewSize.width); 73 | CGFloat originY = inset.origin.y + scaled.size.height * (i / dragViewSize.width); 74 | 75 | // load image for file 76 | NSImage *image = [[NSWorkspace sharedWorkspace] iconForFile:_fileURLs[i].path]; 77 | CGSize size = FitSizeToSize(image.size, inset.size); 78 | 79 | [image drawInRect:CGRectMake(originX + (inset.size.width - size.width) / 2.0, 80 | originY + (inset.size.height - size.height) / 2.0, 81 | size.width, 82 | size.height)]; 83 | } 84 | } 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /drag/main.m: -------------------------------------------------------------------------------- 1 | // drag 2 | // Written in 2015 by Nate Stedman 3 | // 4 | // To the extent possible under law, the author(s) have dedicated all copyright and 5 | // related and neighboring rights to this software to the public domain worldwide. 6 | // This software is distributed without any warranty. 7 | // 8 | // You should have received a copy of the CC0 Public Domain Dedication along with 9 | // this software. If not, see . 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, const char * argv[]) 14 | { 15 | @autoreleasepool 16 | { 17 | NSArray *const arguments = [NSProcessInfo processInfo].arguments; 18 | 19 | if (arguments.count < 2) 20 | { 21 | fprintf(stderr, "Usage: drag path/to/file..."); 22 | return EXIT_FAILURE; 23 | } 24 | else 25 | { 26 | NSFileManager *fm = [NSFileManager defaultManager]; 27 | NSURL *working = [NSURL fileURLWithPath:fm.currentDirectoryPath]; 28 | 29 | NSMutableArray *fileURLs = [NSMutableArray arrayWithCapacity:arguments.count - 1]; 30 | 31 | for (NSUInteger i = 1; i < argc; i++) 32 | { 33 | NSString *path = arguments[i].stringByStandardizingPath; 34 | NSURL *URL = [[NSURL alloc] initWithString:path relativeToURL:working]; 35 | 36 | if (![fm fileExistsAtPath:URL.path]) 37 | { 38 | fprintf(stderr, "File does not exist: “%s”", arguments[i].UTF8String); 39 | return EXIT_FAILURE; 40 | } 41 | 42 | [fileURLs addObject:URL]; 43 | } 44 | 45 | AppDelegate *appDelegate = [AppDelegate new]; 46 | appDelegate.fileURLs = fileURLs; 47 | 48 | NSApplication *app = [NSApplication sharedApplication]; 49 | app.delegate = appDelegate; 50 | 51 | [app run]; 52 | 53 | NSLog(@"%@", appDelegate); // retain the app delegate, -run will never return 54 | 55 | return EXIT_SUCCESS; 56 | } 57 | } 58 | } 59 | --------------------------------------------------------------------------------