├── Dock Manager.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── grad.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── grad.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── Dock Manager ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── 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 │ ├── Contents.json │ └── Dock.imageset │ │ ├── Contents.json │ │ ├── icons8-mac-dock-24-2.png │ │ ├── icons8-mac-dock-48-2.png │ │ └── icons8-mac-dock-72-2.png ├── Base.lproj │ └── Main.storyboard ├── Dock_Manager.entitlements ├── Info.plist ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── Stores │ └── SnapshotStore.swift └── Views │ ├── AddSnapshotView.swift │ ├── RemoveSnapshotView.swift │ ├── RenameSnapshotView.swift │ └── ReplaceSnapshotView.swift ├── LICENSE ├── README.md └── screenshot.png /Dock Manager.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A51C439B242A5CF200ADACC7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51C439A242A5CF200ADACC7 /* AppDelegate.swift */; }; 11 | A51C439F242A5CF300ADACC7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A51C439E242A5CF300ADACC7 /* Assets.xcassets */; }; 12 | A51C43A2242A5CF300ADACC7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A51C43A1242A5CF300ADACC7 /* Preview Assets.xcassets */; }; 13 | A51C43A5242A5CF300ADACC7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A51C43A3242A5CF300ADACC7 /* Main.storyboard */; }; 14 | A51C43B2242A5D5900ADACC7 /* ReplaceSnapshotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51C43AE242A5D5900ADACC7 /* ReplaceSnapshotView.swift */; }; 15 | A51C43B3242A5D5900ADACC7 /* RemoveSnapshotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51C43AF242A5D5900ADACC7 /* RemoveSnapshotView.swift */; }; 16 | A51C43B4242A5D5900ADACC7 /* AddSnapshotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51C43B0242A5D5900ADACC7 /* AddSnapshotView.swift */; }; 17 | A51C43B5242A5D5900ADACC7 /* RenameSnapshotView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51C43B1242A5D5900ADACC7 /* RenameSnapshotView.swift */; }; 18 | A51C43B8242A5D6E00ADACC7 /* SnapshotStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51C43B7242A5D6E00ADACC7 /* SnapshotStore.swift */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | A51C4397242A5CF200ADACC7 /* Dock Manager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Dock Manager.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | A51C439A242A5CF200ADACC7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 24 | A51C439E242A5CF300ADACC7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 25 | A51C43A1242A5CF300ADACC7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 26 | A51C43A4242A5CF300ADACC7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | A51C43A6242A5CF300ADACC7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | A51C43A7242A5CF300ADACC7 /* Dock_Manager.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Dock_Manager.entitlements; sourceTree = ""; }; 29 | A51C43AE242A5D5900ADACC7 /* ReplaceSnapshotView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaceSnapshotView.swift; sourceTree = ""; }; 30 | A51C43AF242A5D5900ADACC7 /* RemoveSnapshotView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoveSnapshotView.swift; sourceTree = ""; }; 31 | A51C43B0242A5D5900ADACC7 /* AddSnapshotView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddSnapshotView.swift; sourceTree = ""; }; 32 | A51C43B1242A5D5900ADACC7 /* RenameSnapshotView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RenameSnapshotView.swift; sourceTree = ""; }; 33 | A51C43B7242A5D6E00ADACC7 /* SnapshotStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotStore.swift; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | A51C4394242A5CF200ADACC7 /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | A51C438E242A5CF200ADACC7 = { 48 | isa = PBXGroup; 49 | children = ( 50 | A51C4399242A5CF200ADACC7 /* Dock Manager */, 51 | A51C4398242A5CF200ADACC7 /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | A51C4398242A5CF200ADACC7 /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | A51C4397242A5CF200ADACC7 /* Dock Manager.app */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | A51C4399242A5CF200ADACC7 /* Dock Manager */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | A51C439A242A5CF200ADACC7 /* AppDelegate.swift */, 67 | A51C43AD242A5D5900ADACC7 /* Views */, 68 | A51C43B6242A5D6E00ADACC7 /* Stores */, 69 | A51C439E242A5CF300ADACC7 /* Assets.xcassets */, 70 | A51C43A3242A5CF300ADACC7 /* Main.storyboard */, 71 | A51C43A6242A5CF300ADACC7 /* Info.plist */, 72 | A51C43A7242A5CF300ADACC7 /* Dock_Manager.entitlements */, 73 | A5803465242B476800161D90 /* Supporting Files */, 74 | A51C43A0242A5CF300ADACC7 /* Preview Content */, 75 | ); 76 | path = "Dock Manager"; 77 | sourceTree = ""; 78 | }; 79 | A51C43A0242A5CF300ADACC7 /* Preview Content */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | A51C43A1242A5CF300ADACC7 /* Preview Assets.xcassets */, 83 | ); 84 | path = "Preview Content"; 85 | sourceTree = ""; 86 | }; 87 | A51C43AD242A5D5900ADACC7 /* Views */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | A51C43B0242A5D5900ADACC7 /* AddSnapshotView.swift */, 91 | A51C43B1242A5D5900ADACC7 /* RenameSnapshotView.swift */, 92 | A51C43AE242A5D5900ADACC7 /* ReplaceSnapshotView.swift */, 93 | A51C43AF242A5D5900ADACC7 /* RemoveSnapshotView.swift */, 94 | ); 95 | path = Views; 96 | sourceTree = ""; 97 | }; 98 | A51C43B6242A5D6E00ADACC7 /* Stores */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | A51C43B7242A5D6E00ADACC7 /* SnapshotStore.swift */, 102 | ); 103 | path = Stores; 104 | sourceTree = ""; 105 | }; 106 | A5803465242B476800161D90 /* Supporting Files */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | ); 110 | path = "Supporting Files"; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXNativeTarget section */ 116 | A51C4396242A5CF200ADACC7 /* Dock Manager */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = A51C43AA242A5CF300ADACC7 /* Build configuration list for PBXNativeTarget "Dock Manager" */; 119 | buildPhases = ( 120 | A51C4393242A5CF200ADACC7 /* Sources */, 121 | A51C4394242A5CF200ADACC7 /* Frameworks */, 122 | A51C4395242A5CF200ADACC7 /* Resources */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = "Dock Manager"; 129 | productName = "Dock Manager"; 130 | productReference = A51C4397242A5CF200ADACC7 /* Dock Manager.app */; 131 | productType = "com.apple.product-type.application"; 132 | }; 133 | /* End PBXNativeTarget section */ 134 | 135 | /* Begin PBXProject section */ 136 | A51C438F242A5CF200ADACC7 /* Project object */ = { 137 | isa = PBXProject; 138 | attributes = { 139 | LastSwiftUpdateCheck = 1130; 140 | LastUpgradeCheck = 1140; 141 | ORGANIZATIONNAME = "Alex Grad"; 142 | TargetAttributes = { 143 | A51C4396242A5CF200ADACC7 = { 144 | CreatedOnToolsVersion = 11.3.1; 145 | }; 146 | }; 147 | }; 148 | buildConfigurationList = A51C4392242A5CF200ADACC7 /* Build configuration list for PBXProject "Dock Manager" */; 149 | compatibilityVersion = "Xcode 9.3"; 150 | developmentRegion = en; 151 | hasScannedForEncodings = 0; 152 | knownRegions = ( 153 | en, 154 | Base, 155 | ); 156 | mainGroup = A51C438E242A5CF200ADACC7; 157 | productRefGroup = A51C4398242A5CF200ADACC7 /* Products */; 158 | projectDirPath = ""; 159 | projectRoot = ""; 160 | targets = ( 161 | A51C4396242A5CF200ADACC7 /* Dock Manager */, 162 | ); 163 | }; 164 | /* End PBXProject section */ 165 | 166 | /* Begin PBXResourcesBuildPhase section */ 167 | A51C4395242A5CF200ADACC7 /* Resources */ = { 168 | isa = PBXResourcesBuildPhase; 169 | buildActionMask = 2147483647; 170 | files = ( 171 | A51C43A5242A5CF300ADACC7 /* Main.storyboard in Resources */, 172 | A51C43A2242A5CF300ADACC7 /* Preview Assets.xcassets in Resources */, 173 | A51C439F242A5CF300ADACC7 /* Assets.xcassets in Resources */, 174 | ); 175 | runOnlyForDeploymentPostprocessing = 0; 176 | }; 177 | /* End PBXResourcesBuildPhase section */ 178 | 179 | /* Begin PBXSourcesBuildPhase section */ 180 | A51C4393242A5CF200ADACC7 /* Sources */ = { 181 | isa = PBXSourcesBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | A51C43B4242A5D5900ADACC7 /* AddSnapshotView.swift in Sources */, 185 | A51C439B242A5CF200ADACC7 /* AppDelegate.swift in Sources */, 186 | A51C43B3242A5D5900ADACC7 /* RemoveSnapshotView.swift in Sources */, 187 | A51C43B5242A5D5900ADACC7 /* RenameSnapshotView.swift in Sources */, 188 | A51C43B2242A5D5900ADACC7 /* ReplaceSnapshotView.swift in Sources */, 189 | A51C43B8242A5D6E00ADACC7 /* SnapshotStore.swift in Sources */, 190 | ); 191 | runOnlyForDeploymentPostprocessing = 0; 192 | }; 193 | /* End PBXSourcesBuildPhase section */ 194 | 195 | /* Begin PBXVariantGroup section */ 196 | A51C43A3242A5CF300ADACC7 /* Main.storyboard */ = { 197 | isa = PBXVariantGroup; 198 | children = ( 199 | A51C43A4242A5CF300ADACC7 /* Base */, 200 | ); 201 | name = Main.storyboard; 202 | sourceTree = ""; 203 | }; 204 | /* End PBXVariantGroup section */ 205 | 206 | /* Begin XCBuildConfiguration section */ 207 | A51C43A8242A5CF300ADACC7 /* Debug */ = { 208 | isa = XCBuildConfiguration; 209 | buildSettings = { 210 | ALWAYS_SEARCH_USER_PATHS = NO; 211 | CLANG_ANALYZER_NONNULL = YES; 212 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 213 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 214 | CLANG_CXX_LIBRARY = "libc++"; 215 | CLANG_ENABLE_MODULES = YES; 216 | CLANG_ENABLE_OBJC_ARC = YES; 217 | CLANG_ENABLE_OBJC_WEAK = YES; 218 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 219 | CLANG_WARN_BOOL_CONVERSION = YES; 220 | CLANG_WARN_COMMA = YES; 221 | CLANG_WARN_CONSTANT_CONVERSION = YES; 222 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 223 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 224 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 225 | CLANG_WARN_EMPTY_BODY = YES; 226 | CLANG_WARN_ENUM_CONVERSION = YES; 227 | CLANG_WARN_INFINITE_RECURSION = YES; 228 | CLANG_WARN_INT_CONVERSION = YES; 229 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 230 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 231 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 232 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 233 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 234 | CLANG_WARN_STRICT_PROTOTYPES = YES; 235 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 236 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 237 | CLANG_WARN_UNREACHABLE_CODE = YES; 238 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 239 | COPY_PHASE_STRIP = NO; 240 | DEBUG_INFORMATION_FORMAT = dwarf; 241 | ENABLE_STRICT_OBJC_MSGSEND = YES; 242 | ENABLE_TESTABILITY = YES; 243 | GCC_C_LANGUAGE_STANDARD = gnu11; 244 | GCC_DYNAMIC_NO_PIC = NO; 245 | GCC_NO_COMMON_BLOCKS = YES; 246 | GCC_OPTIMIZATION_LEVEL = 0; 247 | GCC_PREPROCESSOR_DEFINITIONS = ( 248 | "DEBUG=1", 249 | "$(inherited)", 250 | ); 251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 253 | GCC_WARN_UNDECLARED_SELECTOR = YES; 254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 255 | GCC_WARN_UNUSED_FUNCTION = YES; 256 | GCC_WARN_UNUSED_VARIABLE = YES; 257 | MACOSX_DEPLOYMENT_TARGET = 10.15; 258 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 259 | MTL_FAST_MATH = YES; 260 | ONLY_ACTIVE_ARCH = YES; 261 | SDKROOT = macosx; 262 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 263 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 264 | }; 265 | name = Debug; 266 | }; 267 | A51C43A9242A5CF300ADACC7 /* Release */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | ALWAYS_SEARCH_USER_PATHS = NO; 271 | CLANG_ANALYZER_NONNULL = YES; 272 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 273 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 274 | CLANG_CXX_LIBRARY = "libc++"; 275 | CLANG_ENABLE_MODULES = YES; 276 | CLANG_ENABLE_OBJC_ARC = YES; 277 | CLANG_ENABLE_OBJC_WEAK = YES; 278 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 279 | CLANG_WARN_BOOL_CONVERSION = YES; 280 | CLANG_WARN_COMMA = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 283 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 284 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 285 | CLANG_WARN_EMPTY_BODY = YES; 286 | CLANG_WARN_ENUM_CONVERSION = YES; 287 | CLANG_WARN_INFINITE_RECURSION = YES; 288 | CLANG_WARN_INT_CONVERSION = YES; 289 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 290 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 291 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 292 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 293 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 294 | CLANG_WARN_STRICT_PROTOTYPES = YES; 295 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 296 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 297 | CLANG_WARN_UNREACHABLE_CODE = YES; 298 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 299 | COPY_PHASE_STRIP = NO; 300 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 301 | ENABLE_NS_ASSERTIONS = NO; 302 | ENABLE_STRICT_OBJC_MSGSEND = YES; 303 | GCC_C_LANGUAGE_STANDARD = gnu11; 304 | GCC_NO_COMMON_BLOCKS = YES; 305 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 306 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 307 | GCC_WARN_UNDECLARED_SELECTOR = YES; 308 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 309 | GCC_WARN_UNUSED_FUNCTION = YES; 310 | GCC_WARN_UNUSED_VARIABLE = YES; 311 | MACOSX_DEPLOYMENT_TARGET = 10.15; 312 | MTL_ENABLE_DEBUG_INFO = NO; 313 | MTL_FAST_MATH = YES; 314 | SDKROOT = macosx; 315 | SWIFT_COMPILATION_MODE = wholemodule; 316 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 317 | }; 318 | name = Release; 319 | }; 320 | A51C43AB242A5CF300ADACC7 /* Debug */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 324 | CODE_SIGN_ENTITLEMENTS = "Dock Manager/Dock_Manager.entitlements"; 325 | CODE_SIGN_IDENTITY = "-"; 326 | CODE_SIGN_STYLE = Automatic; 327 | COMBINE_HIDPI_IMAGES = YES; 328 | DEVELOPMENT_ASSET_PATHS = "\"Dock Manager/Preview Content\""; 329 | DEVELOPMENT_TEAM = AP6KQ2LX49; 330 | ENABLE_HARDENED_RUNTIME = YES; 331 | ENABLE_PREVIEWS = YES; 332 | INFOPLIST_FILE = "Dock Manager/Info.plist"; 333 | LD_RUNPATH_SEARCH_PATHS = ( 334 | "$(inherited)", 335 | "@executable_path/../Frameworks", 336 | ); 337 | MACOSX_DEPLOYMENT_TARGET = 10.15; 338 | PRODUCT_BUNDLE_IDENTIFIER = "dev.grad.dock-manager"; 339 | PRODUCT_NAME = "$(TARGET_NAME)"; 340 | SWIFT_VERSION = 5.0; 341 | }; 342 | name = Debug; 343 | }; 344 | A51C43AC242A5CF300ADACC7 /* Release */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 348 | CODE_SIGN_ENTITLEMENTS = "Dock Manager/Dock_Manager.entitlements"; 349 | CODE_SIGN_IDENTITY = "-"; 350 | CODE_SIGN_STYLE = Automatic; 351 | COMBINE_HIDPI_IMAGES = YES; 352 | DEVELOPMENT_ASSET_PATHS = "\"Dock Manager/Preview Content\""; 353 | DEVELOPMENT_TEAM = AP6KQ2LX49; 354 | ENABLE_HARDENED_RUNTIME = YES; 355 | ENABLE_PREVIEWS = YES; 356 | INFOPLIST_FILE = "Dock Manager/Info.plist"; 357 | LD_RUNPATH_SEARCH_PATHS = ( 358 | "$(inherited)", 359 | "@executable_path/../Frameworks", 360 | ); 361 | MACOSX_DEPLOYMENT_TARGET = 10.15; 362 | PRODUCT_BUNDLE_IDENTIFIER = "dev.grad.dock-manager"; 363 | PRODUCT_NAME = "$(TARGET_NAME)"; 364 | SWIFT_VERSION = 5.0; 365 | }; 366 | name = Release; 367 | }; 368 | /* End XCBuildConfiguration section */ 369 | 370 | /* Begin XCConfigurationList section */ 371 | A51C4392242A5CF200ADACC7 /* Build configuration list for PBXProject "Dock Manager" */ = { 372 | isa = XCConfigurationList; 373 | buildConfigurations = ( 374 | A51C43A8242A5CF300ADACC7 /* Debug */, 375 | A51C43A9242A5CF300ADACC7 /* Release */, 376 | ); 377 | defaultConfigurationIsVisible = 0; 378 | defaultConfigurationName = Release; 379 | }; 380 | A51C43AA242A5CF300ADACC7 /* Build configuration list for PBXNativeTarget "Dock Manager" */ = { 381 | isa = XCConfigurationList; 382 | buildConfigurations = ( 383 | A51C43AB242A5CF300ADACC7 /* Debug */, 384 | A51C43AC242A5CF300ADACC7 /* Release */, 385 | ); 386 | defaultConfigurationIsVisible = 0; 387 | defaultConfigurationName = Release; 388 | }; 389 | /* End XCConfigurationList section */ 390 | }; 391 | rootObject = A51C438F242A5CF200ADACC7 /* Project object */; 392 | } 393 | -------------------------------------------------------------------------------- /Dock Manager.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Dock Manager.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Dock Manager.xcodeproj/project.xcworkspace/xcuserdata/grad.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager.xcodeproj/project.xcworkspace/xcuserdata/grad.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Dock Manager.xcodeproj/xcuserdata/grad.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Dock Manager.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Dock Manager/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import SwiftUI 3 | 4 | @NSApplicationMain 5 | class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate, NSUserNotificationCenterDelegate { 6 | var statusItem: NSStatusItem! 7 | 8 | var appName: String { 9 | get { 10 | return Bundle.main.infoDictionary![kCFBundleNameKey as String] as! String 11 | } 12 | } 13 | 14 | var snapshotStore: SnapshotStore! 15 | 16 | var addSnapshotWindow: NSWindow! 17 | var removeSnapshotWindow: NSWindow! 18 | var renameSnapshotWindow: NSWindow! 19 | var replaceSnapshotWindow: NSWindow! 20 | 21 | func renderMenu() { 22 | let menu = NSMenu() 23 | 24 | snapshotStore.snapshotNames.enumerated().forEach { (index, snapshotName) in 25 | let menuItem = NSMenuItem( 26 | title: snapshotName, 27 | action: #selector(activateSnapshot(sender:)), 28 | keyEquivalent: String(index + 1) 29 | ) 30 | menuItem.representedObject = snapshotName 31 | 32 | let activateMenuItem = NSMenuItem( 33 | title: "Activate", 34 | action: #selector(activateSnapshot(sender:)), 35 | keyEquivalent: "" 36 | ) 37 | activateMenuItem.representedObject = snapshotName 38 | 39 | let renameMenuItem = NSMenuItem( 40 | title: "Rename", 41 | action: #selector(renameSnapshot(sender:)), 42 | keyEquivalent: "" 43 | ) 44 | renameMenuItem.representedObject = snapshotName 45 | 46 | let removeMenuItem = NSMenuItem( 47 | title: "Remove", 48 | action: #selector(removeSnapshot(sender:)), 49 | keyEquivalent: "" 50 | ) 51 | removeMenuItem.representedObject = snapshotName 52 | 53 | menuItem.submenu = NSMenu() 54 | menuItem.submenu!.addItem(activateMenuItem) 55 | menuItem.submenu!.addItem(renameMenuItem) 56 | menuItem.submenu!.addItem(removeMenuItem) 57 | menu.addItem(menuItem) 58 | } 59 | if (snapshotStore.snapshotNames.isEmpty) { 60 | menu.addItem(withTitle: "No snapshots added...", action: nil, keyEquivalent: "") 61 | } 62 | 63 | menu.addItem(NSMenuItem.separator()) 64 | 65 | let addMenuItem = NSMenuItem( 66 | title: "Add a current snapshot...", 67 | action: #selector(addSnapshot(sender:)), 68 | keyEquivalent: "" 69 | ) 70 | menu.addItem(addMenuItem) 71 | 72 | menu.addItem(NSMenuItem.separator()) 73 | 74 | menu.addItem( 75 | withTitle: "About \(appName)", 76 | action: #selector(showAboutWindow(sender:)), 77 | keyEquivalent: "a" 78 | ) 79 | 80 | menu.addItem( 81 | withTitle: "Quit \(appName)", 82 | action: #selector(closeApplication(sender:)), 83 | keyEquivalent: "q" 84 | ) 85 | 86 | statusItem.menu = menu 87 | } 88 | 89 | @objc func addSnapshot(sender: NSMenuItem) { 90 | let addSnapshotView = AddSnapshotView() 91 | .environmentObject(snapshotStore) 92 | addSnapshotWindow.contentView = NSHostingView(rootView: addSnapshotView) 93 | NSApp.runModal(for: addSnapshotWindow) 94 | addSnapshotWindow.close() 95 | } 96 | 97 | @objc func removeSnapshot(sender: NSMenuItem) { 98 | let snapshotName = sender.representedObject as! String 99 | 100 | let removeSnapshotView = RemoveSnapshotView(snapshotName: snapshotName) 101 | .environmentObject(snapshotStore) 102 | removeSnapshotWindow.contentView = NSHostingView(rootView: removeSnapshotView) 103 | NSApp.runModal(for: removeSnapshotWindow) 104 | removeSnapshotWindow.close() 105 | } 106 | 107 | @objc func renameSnapshot(sender: NSMenuItem) { 108 | let snapshotName = sender.representedObject as! String 109 | 110 | let renameSnapshotView = RenameSnapshotView(snapshotName: snapshotName) 111 | .environmentObject(snapshotStore) 112 | renameSnapshotWindow.contentView = NSHostingView(rootView: renameSnapshotView) 113 | NSApp.runModal(for: renameSnapshotWindow) 114 | renameSnapshotWindow.close() 115 | } 116 | 117 | @objc func activateSnapshot(sender: NSMenuItem) { 118 | let snapshotName = sender.representedObject as! String 119 | snapshotStore.activateSnapshot(snapshotName) 120 | } 121 | 122 | @objc func showAboutWindow(sender: NSMenuItem) { 123 | NSApp.activate(ignoringOtherApps: true) 124 | NSApp.orderFrontStandardAboutPanel(self) 125 | } 126 | 127 | @objc func closeApplication(sender: NSMenuItem) { 128 | NSApp.activate(ignoringOtherApps: true) 129 | NSApp.terminate(self) 130 | } 131 | 132 | func showNotification(_ text: String) { 133 | NSUserNotificationCenter.default.removeAllDeliveredNotifications() 134 | 135 | let notification = NSUserNotification() 136 | notification.title = appName 137 | notification.informativeText = text 138 | NSUserNotificationCenter.default.deliver(notification) 139 | } 140 | 141 | /** 142 | # NSApplicationDelegate 143 | */ 144 | func applicationWillFinishLaunching(_ notification: Notification) { 145 | NSApp.setActivationPolicy(.accessory) 146 | 147 | NSUserNotificationCenter.default.delegate = self 148 | } 149 | 150 | func applicationDidFinishLaunching(_ aNotification: Notification) { 151 | statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) 152 | statusItem.button?.image = NSImage(named: "Dock") 153 | 154 | snapshotStore = SnapshotStore() 155 | 156 | addSnapshotWindow = NSWindow( 157 | contentRect: NSRect(x: 0, y: 0, width: 320, height: 120), 158 | styleMask: [.closable, .titled, .fullSizeContentView], 159 | backing: .buffered, 160 | defer: false 161 | ) 162 | addSnapshotWindow.isReleasedWhenClosed = false 163 | addSnapshotWindow.title = appName 164 | addSnapshotWindow.center() 165 | addSnapshotWindow.delegate = self 166 | 167 | removeSnapshotWindow = NSWindow( 168 | contentRect: NSRect(x: 0, y: 0, width: 320, height: 120), 169 | styleMask: [.closable, .titled, .fullSizeContentView], 170 | backing: .buffered, 171 | defer: false 172 | ) 173 | removeSnapshotWindow.isReleasedWhenClosed = false 174 | removeSnapshotWindow.title = appName 175 | removeSnapshotWindow.center() 176 | removeSnapshotWindow.delegate = self 177 | 178 | renameSnapshotWindow = NSWindow( 179 | contentRect: NSRect(x: 0, y: 0, width: 320, height: 120), 180 | styleMask: [.closable, .titled, .fullSizeContentView], 181 | backing: .buffered, 182 | defer: false 183 | ) 184 | renameSnapshotWindow.isReleasedWhenClosed = false 185 | renameSnapshotWindow.title = appName 186 | renameSnapshotWindow.center() 187 | renameSnapshotWindow.delegate = self 188 | 189 | replaceSnapshotWindow = NSWindow( 190 | contentRect: NSRect(x: 0, y: 0, width: 320, height: 120), 191 | styleMask: [.closable, .titled, .fullSizeContentView], 192 | backing: .buffered, 193 | defer: false 194 | ) 195 | replaceSnapshotWindow.isReleasedWhenClosed = false 196 | replaceSnapshotWindow.title = appName 197 | replaceSnapshotWindow.center() 198 | replaceSnapshotWindow.delegate = self 199 | 200 | renderMenu() 201 | } 202 | 203 | /** 204 | # NSWindowDelegate 205 | */ 206 | func windowWillClose(_ notification: Notification) { 207 | NSApp.stopModal() 208 | } 209 | 210 | /** 211 | # NSUserNotificationCenterDelegate 212 | */ 213 | func userNotificationCenter(_ center: NSUserNotificationCenter, shouldPresent notification: NSUserNotification) -> Bool { 214 | return true 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/Dock.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icons8-mac-dock-24-2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icons8-mac-dock-48-2.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icons8-mac-dock-72-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/Dock.imageset/icons8-mac-dock-24-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/Dock.imageset/icons8-mac-dock-24-2.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/Dock.imageset/icons8-mac-dock-48-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/Dock.imageset/icons8-mac-dock-48-2.png -------------------------------------------------------------------------------- /Dock Manager/Assets.xcassets/Dock.imageset/icons8-mac-dock-72-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/Dock Manager/Assets.xcassets/Dock.imageset/icons8-mac-dock-72-2.png -------------------------------------------------------------------------------- /Dock Manager/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Dock Manager/Dock_Manager.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Dock Manager/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSApplicationCategoryType 24 | public.app-category.productivity 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2020 Alex Grad. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | NSSupportsAutomaticTermination 34 | 35 | NSSupportsSuddenTermination 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Dock Manager/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Dock Manager/Stores/SnapshotStore.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | class SnapshotStore: ObservableObject { 4 | @Published var snapshotNames: [String] = [] 5 | 6 | init() { 7 | snapshotNames = getSnapshotNames() 8 | } 9 | 10 | private var fileManager: FileManager { 11 | get { 12 | return FileManager.default 13 | } 14 | } 15 | 16 | private var applicationSupportURL: URL { 17 | get { 18 | let appName = Bundle.main.infoDictionary![kCFBundleNameKey as String] as! String 19 | 20 | let applicationSupportURL = fileManager.urls( 21 | for: .applicationSupportDirectory, 22 | in: .userDomainMask 23 | ) 24 | .first! 25 | .appendingPathComponent(appName) 26 | 27 | if (!fileManager.fileExists(atPath: applicationSupportURL.path)) { 28 | try! fileManager.createDirectory(at: applicationSupportURL, withIntermediateDirectories: false) 29 | } 30 | 31 | return applicationSupportURL 32 | } 33 | } 34 | 35 | private var dockPreferencesURL: URL { 36 | get { 37 | let libraryURL = fileManager.urls( 38 | for: .libraryDirectory, 39 | in: .userDomainMask 40 | ) 41 | .first! 42 | 43 | let dockPreferencesURL = libraryURL 44 | .appendingPathComponent("Preferences", isDirectory: true) 45 | .appendingPathComponent("com.apple.dock.plist", isDirectory: false) 46 | 47 | return dockPreferencesURL 48 | } 49 | } 50 | 51 | private func getSnapshotNames() -> [String] { 52 | let fileNames = try! fileManager.contentsOfDirectory(atPath: applicationSupportURL.path) 53 | 54 | var snapshotNames = fileNames.map { fileName in 55 | return fileName.replacingOccurrences( 56 | of: "\\.plist$", 57 | with: "", 58 | options: [.regularExpression] 59 | ) 60 | } 61 | snapshotNames.sort() 62 | 63 | return snapshotNames 64 | } 65 | 66 | func addSnapshot(_ snapshotName: String) { 67 | let snapshotURL = applicationSupportURL 68 | .appendingPathComponent("\(snapshotName).plist") 69 | 70 | try? fileManager.removeItem(at: snapshotURL) 71 | 72 | try! fileManager.copyItem( 73 | at: dockPreferencesURL, 74 | to: snapshotURL 75 | ) 76 | 77 | snapshotNames = getSnapshotNames() 78 | } 79 | 80 | func removeSnapshot(_ snapshotName: String) { 81 | let snapshotURL = applicationSupportURL 82 | .appendingPathComponent("\(snapshotName).plist") 83 | 84 | try! fileManager.removeItem(at: snapshotURL) 85 | 86 | snapshotNames = getSnapshotNames() 87 | } 88 | 89 | func renameSnapshot(_ oldSnapshotName: String, _ newSnapshotName: String) { 90 | let oldSnapshotURL = applicationSupportURL 91 | .appendingPathComponent("\(oldSnapshotName).plist") 92 | let newSnapshotURL = applicationSupportURL 93 | .appendingPathComponent("\(newSnapshotName).plist") 94 | 95 | try? fileManager.removeItem(at: newSnapshotURL) 96 | try! fileManager.copyItem(at: oldSnapshotURL, to: newSnapshotURL) 97 | try! fileManager.removeItem(at: oldSnapshotURL) 98 | 99 | snapshotNames = getSnapshotNames() 100 | } 101 | 102 | func activateSnapshot(_ snapshotName: String) { 103 | let snapshotURL = applicationSupportURL 104 | .appendingPathComponent("\(snapshotName).plist") 105 | 106 | try? fileManager.removeItem(at: dockPreferencesURL) 107 | try! fileManager.copyItem(at: snapshotURL, to: dockPreferencesURL) 108 | 109 | let task = Process() 110 | task.launchPath = "/usr/bin/killall" 111 | task.arguments = ["Dock"] 112 | task.launch() 113 | task.waitUntilExit() 114 | } 115 | 116 | func isSnapshotNameValid(_ snapshotName: String) -> Bool { 117 | return snapshotName 118 | .range( 119 | of: "^[a-zA-Z0-9 .,]+$", 120 | options: .regularExpression, 121 | range: nil, 122 | locale: nil 123 | ) != nil 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Dock Manager/Views/AddSnapshotView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct AddSnapshotView: View { 4 | @EnvironmentObject var snapshotStore: SnapshotStore 5 | 6 | @State var snapshotName = "" 7 | @State var validationError = "" 8 | 9 | var body: some View { 10 | VStack { 11 | Text("Please enter a name of the new snapshot:") 12 | .frame(maxWidth: .infinity, alignment: .leading) 13 | TextField( 14 | "Snapshot name", 15 | text: $snapshotName, 16 | onCommit: save 17 | ) 18 | if !validationError.isEmpty { 19 | Text(validationError) 20 | .foregroundColor(Color.red) 21 | } 22 | Spacer() 23 | HStack { 24 | Button(action: cancel) { 25 | Text("Cancel") 26 | } 27 | Button(action: save) { 28 | Text("Save") 29 | } 30 | } 31 | .frame(maxWidth: .infinity, alignment: .trailing) 32 | } 33 | .frame( 34 | maxWidth: .infinity, 35 | maxHeight: .infinity, 36 | alignment: .topLeading 37 | ) 38 | .padding() 39 | } 40 | 41 | func cancel() { 42 | NSApp.stopModal() 43 | } 44 | 45 | func save() { 46 | let appDelegate = NSApp.delegate as! AppDelegate 47 | 48 | snapshotName = snapshotName.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 49 | if (!snapshotStore.isSnapshotNameValid(snapshotName)) { 50 | validationError = "The snapshot name is invalid" 51 | return 52 | } 53 | 54 | if (snapshotStore.snapshotNames.contains(snapshotName)) { 55 | let replaceSnapshotView = ReplaceSnapshotView(snapshotName: snapshotName) 56 | .environmentObject(snapshotStore) 57 | appDelegate.replaceSnapshotWindow.contentView = NSHostingView(rootView: replaceSnapshotView) 58 | let modalResponse = NSApp.runModal(for: appDelegate.replaceSnapshotWindow) 59 | appDelegate.replaceSnapshotWindow.close() 60 | 61 | if (modalResponse != .alertFirstButtonReturn) { 62 | return 63 | } 64 | } 65 | 66 | validationError = "" 67 | 68 | snapshotStore.addSnapshot(snapshotName) 69 | 70 | NSApp.stopModal() 71 | 72 | appDelegate.renderMenu() 73 | appDelegate.showNotification("The \"\(snapshotName)\" snapshot was succussfully added") 74 | 75 | snapshotName = "" 76 | } 77 | } 78 | 79 | struct AddSnapshotView_Previews: PreviewProvider { 80 | static var previews: some View { 81 | AddSnapshotView() 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Dock Manager/Views/RemoveSnapshotView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct RemoveSnapshotView: View { 4 | @EnvironmentObject var snapshotStore: SnapshotStore 5 | 6 | var snapshotName: String 7 | 8 | var body: some View { 9 | VStack { 10 | Text("Are you sure you want to remove the \"\(snapshotName.replacingOccurrences(of: " ", with: "\u{00a0}"))\" snapshot?") 11 | .frame(maxWidth: .infinity, alignment: .leading) 12 | Spacer() 13 | HStack { 14 | Button(action: cancel) { 15 | Text("Cancel") 16 | } 17 | Button(action: remove) { 18 | Text("Yes") 19 | } 20 | } 21 | .frame(maxWidth: .infinity, alignment: .trailing) 22 | } 23 | .frame( 24 | maxWidth: .infinity, 25 | maxHeight: .infinity, 26 | alignment: .topLeading 27 | ) 28 | .padding() 29 | } 30 | 31 | func cancel() { 32 | NSApp.stopModal() 33 | } 34 | 35 | func remove() { 36 | snapshotStore.removeSnapshot(snapshotName) 37 | 38 | NSApp.stopModal() 39 | 40 | let appDelegate = NSApp.delegate as! AppDelegate 41 | appDelegate.renderMenu() 42 | appDelegate.showNotification("The \"\(snapshotName)\" snapshot was succussfully removed") 43 | } 44 | } 45 | 46 | struct RemoveSnapshotView_Previews: PreviewProvider { 47 | static var previews: some View { 48 | RemoveSnapshotView(snapshotName: "Test") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Dock Manager/Views/RenameSnapshotView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct RenameSnapshotView: View { 4 | @EnvironmentObject var snapshotStore: SnapshotStore 5 | 6 | var snapshotName: String 7 | @State var newSnapshotName = "" 8 | @State var validationError = "" 9 | 10 | 11 | var body: some View { 12 | VStack { 13 | Text("Please enter a new name of the \"\(snapshotName.replacingOccurrences(of: " ", with: "\u{00a0}"))\" snapshot:") 14 | .frame(maxWidth: .infinity, alignment: .leading) 15 | TextField( 16 | "Snapshot name", 17 | text: $newSnapshotName, 18 | onCommit: save 19 | ) 20 | if !validationError.isEmpty { 21 | Text(validationError) 22 | .foregroundColor(Color.red) 23 | } 24 | Spacer() 25 | HStack { 26 | Button(action: cancel) { 27 | Text("Cancel") 28 | } 29 | Button(action: save) { 30 | Text("Save") 31 | } 32 | } 33 | .frame(maxWidth: .infinity, alignment: .trailing) 34 | } 35 | .frame( 36 | maxWidth: .infinity, 37 | maxHeight: .infinity, 38 | alignment: .topLeading 39 | ) 40 | .padding() 41 | .onAppear(perform: { 42 | self.newSnapshotName = self.snapshotName 43 | }) 44 | } 45 | 46 | func cancel() { 47 | NSApp.stopModal() 48 | } 49 | 50 | func save() { 51 | let appDelegate = NSApp.delegate as! AppDelegate 52 | 53 | newSnapshotName = newSnapshotName.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 54 | 55 | if (!snapshotStore.isSnapshotNameValid(newSnapshotName)) { 56 | validationError = "The snapshot name is invalid" 57 | return 58 | } 59 | 60 | if (snapshotStore.snapshotNames.contains(newSnapshotName)) { 61 | let replaceSnapshotView = ReplaceSnapshotView(snapshotName: newSnapshotName) 62 | .environmentObject(snapshotStore) 63 | appDelegate.replaceSnapshotWindow.contentView = NSHostingView(rootView: replaceSnapshotView) 64 | let modalResponse = NSApp.runModal(for: appDelegate.replaceSnapshotWindow) 65 | appDelegate.replaceSnapshotWindow.close() 66 | 67 | if (modalResponse != .alertFirstButtonReturn) { 68 | return 69 | } 70 | } 71 | 72 | validationError = "" 73 | 74 | snapshotStore.renameSnapshot(snapshotName, newSnapshotName) 75 | 76 | NSApp.stopModal() 77 | 78 | appDelegate.renderMenu() 79 | appDelegate.showNotification("The snapshot was successfully renamed to \"\(newSnapshotName)\"") 80 | 81 | newSnapshotName = "" 82 | } 83 | } 84 | 85 | struct RenameSnapshotView_Previews: PreviewProvider { 86 | static var previews: some View { 87 | RenameSnapshotView(snapshotName: "Test") 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Dock Manager/Views/ReplaceSnapshotView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ReplaceSnapshotView: View { 4 | @EnvironmentObject var snapshotStore: SnapshotStore 5 | 6 | var snapshotName: String 7 | 8 | var body: some View { 9 | VStack { 10 | Text("The \"\(snapshotName.replacingOccurrences(of: " ", with: "\u{00a0}"))\" snapshot already exists. Do you want to replace it?") 11 | .frame(maxWidth: .infinity, alignment: .leading) 12 | Spacer() 13 | HStack { 14 | Button(action: cancel) { 15 | Text("Cancel") 16 | } 17 | Button(action: replace) { 18 | Text("Yes") 19 | } 20 | } 21 | .frame(maxWidth: .infinity, alignment: .trailing) 22 | } 23 | .frame( 24 | maxWidth: .infinity, 25 | maxHeight: .infinity, 26 | alignment: .topLeading 27 | ) 28 | .padding() 29 | } 30 | 31 | func cancel() { 32 | NSApp.stopModal(withCode: .alertSecondButtonReturn) 33 | } 34 | 35 | func replace() { 36 | NSApp.stopModal(withCode: .alertFirstButtonReturn) 37 | } 38 | } 39 | 40 | struct ReplaceSnapshotView_Previews: PreviewProvider { 41 | static var previews: some View { 42 | ReplaceSnapshotView(snapshotName: "Test") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alex Grad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dock Manager 2 | 3 | The macOS application that allows you to switch between profiles of the macOS Dock. 4 | 5 | ![Dock Manager](screenshot.png) 6 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graddotdev/DockManager/f07bb680be324403b0715ea878ef696827948490/screenshot.png --------------------------------------------------------------------------------