diff --git a/CHANGELOG.md b/CHANGELOG.md index b158d0f..d35e9c0 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,25 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - Refactor application. +## [0.8.0] - 2021-09-02 + +### Added + +- Develop additional `Finder extension` which allows to create a `hard links` for selected folders and files via contextual menu. + +### Changed + +- Change build os version to 10.10. + +### Fixed + +- Fix soft link replace with function. + ## [0.7.0] - 2021-08-22 ### Added -- Added new classes `Path` for working with path's and creating relative path from specified directory. +- Add new classes `Path` for working with path's and creating relative path from specified directory. - Add unit-tests cases for the `Path` class. ### Changed @@ -25,13 +39,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Changed -- Changed the activity property of some menu items according to the rule: +- Change the activity property of some menu items according to the rule: - if no object was copied, then the menu items "Paste link to here" an "Move it here and replace with a link" are not active. - if at least one object was not copied, then the menu items "Paste link to here" an "Move it here and replace with a link" are inactive. ### Added -- Added cleaning clipboard after inserting links. +- Add cleaning clipboard after inserting links. ## [0.5.0] - 2021-08-02 diff --git a/README.md b/README.md index e09474a..6d58702 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ The Quick Symlink is a `Finder extension` which provides a `contextual menu item` for the symbolic links creation on macOS. -[![status](https://img.shields.io/badge/status-active-active?style=flat-square)](BADGES_GUIDE.md#status) [![version](https://img.shields.io/badge/version-0.6.0-informational?style=flat-square)](BADGES_GUIDE.md#version) [![oss lifecycle](https://img.shields.io/badge/oss_lifecycle-active-important?style=flat-square)](BADGES_GUIDE.md#oss-lifecycle) [![maintenance](https://img.shields.io/badge/maintenance-yes-informational?style=flat-square)](BADGES_GUIDE.md#maintenance) [![last release](https://img.shields.io/badge/last_release-August_22,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-August_22,_2021-informational?style=flat-square)](BADGES_GUIDE.md#commit-date) +[![status](https://img.shields.io/badge/status-active-active?style=flat-square)](BADGES_GUIDE.md#status) [![version](https://img.shields.io/badge/version-0.8.0-informational?style=flat-square)](BADGES_GUIDE.md#version) [![oss lifecycle](https://img.shields.io/badge/oss_lifecycle-active-important?style=flat-square)](BADGES_GUIDE.md#oss-lifecycle) [![maintenance](https://img.shields.io/badge/maintenance-yes-informational?style=flat-square)](BADGES_GUIDE.md#maintenance) [![last release](https://img.shields.io/badge/last_release-September_02,_2021-informational?style=flat-square)](BADGES_GUIDE.md#release-date) [![last commit](https://img.shields.io/badge/last_commit-September_02,_2021-informational?style=flat-square)](BADGES_GUIDE.md#commit-date) [![license](https://img.shields.io/badge/license-MIT-informational?style=flat-square)](LICENSE) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg?style=flat-square)](CODE_OF_CONDUCT.md) -[![platform](https://img.shields.io/badge/platform-OS_X_10.11+-important?style=flat-square)](https://en.wikipedia.org/wiki/Computing_platform) +[![platform](https://img.shields.io/badge/platform-OS_X_10.10+-important?style=flat-square)](https://en.wikipedia.org/wiki/Computing_platform) --- @@ -55,10 +55,14 @@ This GIF demonstrates how the `Quick Symlink` allows quite simple to copy files ## 🎚 Features - Create a symbolic links in a several clicks via the context menu instead of the terminal promt: - - Select files or folders and create symlink for them. - - Copy files or folders and paste symlink somewhere. + - Select files or folders and create symlinks for them. + - Copy files or folders and paste symlinks somewhere. - Copy files or folders, paste them somewhere, and replace them with symlinks. +- Create a hard links in a several clicks via the context menu instead of the terminal promt: + - Select files or folders and create hard links for them. + - Copy files or folders and paste hard links somewhere. + ### To Do - For more information on an upcoming development, please read the [todo](TODO.md) list. @@ -75,12 +79,12 @@ These instructions allow to get a copy of this project and run it on a local mac Before using it, make sure that follows software are installed on the local machine: -- **[OS X 10.11+](https://www.apple.com/ru/macos/what-is/)** - the operating system under which the extention is executing. +- **[OS X 10.10+](https://www.apple.com/ru/macos/what-is/)** - the operating system under which the extention is executing. If any of the listed programs is not installed, then it can be installed by instruction as described below. -1. #### OS X 10.11+ - - Install macOS 10.11+ by [this](https://support.apple.com/ht201372) instruction. +1. #### OS X 10.10+ + - Install macOS 10.10+ by [this](https://support.apple.com/ht201372) instruction. ### Installing @@ -105,7 +109,9 @@ In order to install it is quite simple to: open quick-symlink.app ``` -4. Open up `System Preferences > Extensions` and enable the extension `quick-symlink`. +4. Open up `System Preferences > Extensions` and enable the follows extensions: + 4.1. `quick-symlink-extension` - for the symlink actions. + 4.2. `hard-link-actions-extension` - for the hard link actions. **Otherwise, it's possible to install and remove the extention using the actual extension bundled into the app.** @@ -113,12 +119,14 @@ In order to install it is quite simple to: ```bash pluginkit -a quick-symlink.app/Contents/PlugIns/quick-symlink-extension.appex/ +pluginkit -a quick-symlink.app/Contents/PlugIns/hard-link-action-extension.appex/ ``` 2. To remove it, run this: ```bash pluginkit -r quick-symlink.app/Contents/PlugIns/quick-symlink-extension.appex/ +pluginkit -r quick-symlink.app/Contents/PlugIns/hard-link-action-extension.appex/ ``` ### Downloading @@ -144,27 +152,44 @@ This tool allows to: Create symlinks in the current directory 1. Select folders or files for which a symbolic link is needed.
2. Call the contextual menu by the right-clicking on selected.
-3. Select menu item `Quick Symlink --> Create symlink for`.
+3. Select menu item `Symlink actions --> Create symlink for`.
Create symlinks in another directory 1. Select folders or files for which a symbolic link is needed.
2. Call the contextual menu by the right-clicking on selected.
-3. Select menu item `Quick Symlink --> Copy path from here`.
+3. Select menu item `Symlink actions --> Copy path from here`.
4. Go to a destination folder.
5. Call the contextual menu by right-clicking on the filder.
-6. Select menu item `Quick Symlink --> Paste to here`.
+6. Select menu item `Symlink actions --> Paste to here`.
Replace objects with symbolic links 1. Select folders or files for which a symbolic link is needed.
2. Call the contextual menu by the right-clicking on selected.
-3. Select menu item `Quick Symlink --> Copy to clipboard`.
+3. Select menu item `Symlink actions --> Copy to clipboard`.
+4. Go to a destination folder.
+5. Call the contextual menu by right-clicking on the filder.
+6. Select menu item `Symlink actions --> Move it here and replace with a link`.
+
+ +
+ Create hard links in the current directory +1. Select folders or files for which a symbolic link is needed.
+2. Call the contextual menu by the right-clicking on selected.
+3. Select menu item `Hard link actions --> Create symlink for`.
+
+ +
+ Create symlinks in another directory +1. Select folders or files for which a symbolic link is needed.
+2. Call the contextual menu by the right-clicking on selected.
+3. Select menu item `Hard link actions --> Copy path from here`.
4. Go to a destination folder.
5. Call the contextual menu by right-clicking on the filder.
-6. Select menu item `Quick Symlink --> Move it here and replace with a link`.
+6. Select menu item `Hard link actions --> Paste to here`.
## πŸ›  Built With diff --git a/TODO.md b/TODO.md index a3ffbc5..914d65f 100755 --- a/TODO.md +++ b/TODO.md @@ -7,7 +7,7 @@ - [x] Add the new menu item for creating symlink in a parent directory (parent for target objects). - [x] Optional feature - use relative path instead absolute path in symlink target URL (if target and link located in one volume) - [ ] Refactor code in `commons/*` part and add unit-tests -- [ ] Develop additional `Finder extension` which allows to create a `hard links` for selected folders and files via contextual menu +- [x] Develop additional `Finder extension` which allows to create a `hard links` for selected folders and files via contextual menu - [ ] Develop the action panel for created symbolic links and hard lonks (in the app window): - [ ] a) (if broken) process to browse finder for 'Find/fix missing target' - [ ] b) Modify existing paths and symbolic link features diff --git a/commons/QuickSymlinkActions/QuickSymlinkAction.swift b/commons/QuickSymlinkActions/QuickSymlinkAction.swift deleted file mode 100644 index 12e81aa..0000000 --- a/commons/QuickSymlinkActions/QuickSymlinkAction.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// QuickSymlinkAction.swift -// quick-symlink -// -// Created by Alexander A. Kropotin on 15/07/2021. -// Copyright Β© 2021 Alexander A. Kropotin. All rights reserved. -// - -import Foundation - -public protocol QuickSymlinkAction { - - func execute(); -} - -internal extension QuickSymlinkAction { - - internal func getTargetPath(_ from: URL!, to: URL!) -> URL! { - let originSourceName = from.absoluteURL.deletingPathExtension().lastPathComponent; - let fileType = from.absoluteURL.pathExtension; - - var fileExtention = fileType; - if !fileType.isEmpty { - fileExtention = ".\(fileType)" - } - - var fileName = "\(originSourceName)\(fileExtention)"; - var counter = 1 - var targetPath = to; - targetPath = targetPath?.appendingPathComponent(fileName); - - while FileManager.default.fileExists(atPath: (targetPath?.path)!) { - fileName = "\(originSourceName)-\(counter)\(fileExtention)"; - counter += 1; - targetPath = to.appendingPathComponent(fileName); - } - - return targetPath!; - } -} diff --git a/commons/link/FileLinkManager.swift b/commons/link/FileLinkManager.swift new file mode 100644 index 0000000..359e268 --- /dev/null +++ b/commons/link/FileLinkManager.swift @@ -0,0 +1,89 @@ +// +// FileManagerAdapter.swift +// quick-symlink +// +// Created by Alexander A. Kropotin on 01/09/2021. +// Copyright Β© 2021 Alexander A. Kropotin. All rights reserved. +// + +import Foundation +import FinderSync + +public protocol FileLinkManager { + + func linkWith(of: URL!, with: URL!); + + func replaceWith(of: URL!, with: URL!); +} + +public extension FileLinkManager { + + public func getTargetPath(_ from: URL!, to: URL!) -> URL! { + let originSourceName = from.absoluteURL.deletingPathExtension().lastPathComponent; + let fileType = from.absoluteURL.pathExtension; + + var fileExtention = fileType; + if !fileType.isEmpty { + fileExtention = ".\(fileType)" + } + + var fileName = "\(originSourceName)\(fileExtention)"; + var counter = 1 + var targetPath = to; + targetPath = targetPath?.appendingPathComponent(fileName); + + while FileManager.default.fileExists(atPath: (targetPath?.path)!) { + fileName = "\(originSourceName)-\(counter)\(fileExtention)"; + counter += 1; + targetPath = to.appendingPathComponent(fileName); + } + + return targetPath!; + } +} + +public class SoftLinkManager: FileLinkManager { + + public func linkWith(of: URL!, with: URL!) { + do { + try FileManager.default.createSymbolicLink(at: with!, withDestinationURL: ResourcePath.of(url: of).relativize(to: ResourcePath.of(url: with?.deletingLastPathComponent())).toUrl()!); + } catch let error as NSError { + NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); + } + } + + public func replaceWith(of: URL!, with: URL!) { + do { + //FIXME: Add checking for existance of file & resolving this case with symply pastle link + try FileManager.default.moveItem(at: of, to: with); + try FileManager.default.createSymbolicLink(at: of, withDestinationURL: ResourcePath.of(url: with).relativize(to: ResourcePath.of(url: of.deletingLastPathComponent())).toUrl()!); + } catch let error as NSError { + NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); + } + } +} + +public class HardLinkManager: FileLinkManager { + + public func linkWith(of: URL!, with: URL!) { + + let pasteboard = NSPasteboard.general; + pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil); + + do { + try FileManager.default.linkItem(at: of, to: with); + + } catch let error as NSError { + NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); + } + } + + public func replaceWith(of: URL!, with: URL!) { + do { + try FileManager.default.moveItem(at: of, to: with); + try FileManager.default.linkItem(at: with, to: of); + } catch let error as NSError { + NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); + } + } +} diff --git a/commons/QuickSymlinkActions/CopyPathAction.swift b/commons/link/action/CopyPathAction.swift similarity index 100% rename from commons/QuickSymlinkActions/CopyPathAction.swift rename to commons/link/action/CopyPathAction.swift diff --git a/commons/QuickSymlinkActions/CreateLinkAction.swift b/commons/link/action/CreateLinkAction.swift similarity index 57% rename from commons/QuickSymlinkActions/CreateLinkAction.swift rename to commons/link/action/CreateLinkAction.swift index ac97835..2dcffed 100644 --- a/commons/QuickSymlinkActions/CreateLinkAction.swift +++ b/commons/link/action/CreateLinkAction.swift @@ -13,8 +13,11 @@ public class CreateLinkAction: QuickSymlinkAction { private var finderController: FIFinderSyncController; - public init() { + private var fileLinkManager: FileLinkManager; + + public init(fileLinkManager: FileLinkManager!) { self.finderController = FIFinderSyncController.default(); + self.fileLinkManager = fileLinkManager; } public func execute() { @@ -26,14 +29,8 @@ public class CreateLinkAction: QuickSymlinkAction { } for path in target { - let targetPath = self.getTargetPath(path, to: path.deletingLastPathComponent()); - - do { - try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: ResourcePath.of(url: path).relativize(to: ResourcePath.of(url: targetPath?.deletingLastPathComponent())).toUrl()!); - } catch let error as NSError { - NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); - } + let targetPath = self.fileLinkManager.getTargetPath(path, to: path.deletingLastPathComponent()); + self.fileLinkManager.linkWith(of: path, with: targetPath); } } } - diff --git a/commons/QuickSymlinkActions/PasteLinkAction.swift b/commons/link/action/PasteLinkAction.swift similarity index 69% rename from commons/QuickSymlinkActions/PasteLinkAction.swift rename to commons/link/action/PasteLinkAction.swift index 991fbbe..aee04bb 100644 --- a/commons/QuickSymlinkActions/PasteLinkAction.swift +++ b/commons/link/action/PasteLinkAction.swift @@ -13,8 +13,11 @@ public class PasteLinkAction: QuickSymlinkAction { private var finderController: FIFinderSyncController; - public init() { + private var fileLinkManager: FileLinkManager; + + public init(fileLinkManager: FileLinkManager!) { self.finderController = FIFinderSyncController.default(); + self.fileLinkManager = fileLinkManager; } public func execute() { @@ -35,13 +38,8 @@ public class PasteLinkAction: QuickSymlinkAction { let paths = pathsFromClipboard.components(separatedBy: ";"); for path in paths { let pathUrl = URL(fileURLWithPath: path); - let targetPath = self.getTargetPath(pathUrl, to: target); - - do { - try FileManager.default.createSymbolicLink(at: targetPath!, withDestinationURL: ResourcePath.of(url: pathUrl).relativize(to: ResourcePath.of(url: targetPath?.deletingLastPathComponent())).toUrl()!); - } catch let error as NSError { - NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); - } + let targetPath = self.fileLinkManager.getTargetPath(pathUrl, to: target); + self.fileLinkManager.linkWith(of: pathUrl, with: targetPath); } } } diff --git a/commons/link/action/QuickSymlinkAction.swift b/commons/link/action/QuickSymlinkAction.swift new file mode 100644 index 0000000..2b99d99 --- /dev/null +++ b/commons/link/action/QuickSymlinkAction.swift @@ -0,0 +1,14 @@ +// +// QuickSymlinkAction.swift +// quick-symlink +// +// Created by Alexander A. Kropotin on 15/07/2021. +// Copyright Β© 2021 Alexander A. Kropotin. All rights reserved. +// + +import Foundation + +public protocol QuickSymlinkAction { + + func execute(); +} diff --git a/commons/QuickSymlinkActions/ReplaceWithLinkAction.swift b/commons/link/action/ReplaceWithLinkAction.swift similarity index 66% rename from commons/QuickSymlinkActions/ReplaceWithLinkAction.swift rename to commons/link/action/ReplaceWithLinkAction.swift index d2a9c1b..f852cb8 100644 --- a/commons/QuickSymlinkActions/ReplaceWithLinkAction.swift +++ b/commons/link/action/ReplaceWithLinkAction.swift @@ -13,8 +13,11 @@ public class ReplaceWithLinkAction: QuickSymlinkAction { private var finderController: FIFinderSyncController; - public init() { + private var fileLinkManager: FileLinkManager; + + public init(fileLinkManager: FileLinkManager!) { self.finderController = FIFinderSyncController.default(); + self.fileLinkManager = fileLinkManager; } public func execute() { @@ -35,14 +38,8 @@ public class ReplaceWithLinkAction: QuickSymlinkAction { let paths = pathsFromClipboard.components(separatedBy: ";"); for path in paths { let pathUrl = URL(fileURLWithPath: path); - let targetPath = self.getTargetPath(pathUrl, to: target); - - do { - try FileManager.default.moveItem(at: pathUrl, to: targetPath!); - try FileManager.default.createSymbolicLink(at: pathUrl, withDestinationURL: ResourcePath.of(url: targetPath).relativize(to: ResourcePath.of(url: pathUrl.deletingLastPathComponent())).toUrl()!); - } catch let error as NSError { - NSLog("FileManager.createSymbolicLink() failed to create file: %@", error.description as NSString); - } + let targetPath = self.fileLinkManager.getTargetPath(pathUrl, to: target); + self.fileLinkManager.replaceWith(of: pathUrl, with: targetPath); } } } diff --git a/commons/FileSystem/Path.swift b/commons/path/Path.swift similarity index 100% rename from commons/FileSystem/Path.swift rename to commons/path/Path.swift diff --git a/commons/FileSystem/ResourcePath.swift b/commons/path/ResourcePath.swift similarity index 96% rename from commons/FileSystem/ResourcePath.swift rename to commons/path/ResourcePath.swift index fa4fb6c..f05468c 100644 --- a/commons/FileSystem/ResourcePath.swift +++ b/commons/path/ResourcePath.swift @@ -50,8 +50,6 @@ public class ResourcePath: Path { destinationPath.appendPathComponent(pathFragment); } - //pathFragments!.append(contentsOf: targetPathFragments!); - return ResourcePath.of(url: destinationPath); } diff --git a/hard-link-action-extension/Assets.xcassets/Contents.json b/hard-link-action-extension/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/hard-link-action-extension/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/Contents.json b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/Contents.json new file mode 100644 index 0000000..1553d71 --- /dev/null +++ b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/Contents.json @@ -0,0 +1,33 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "quick-symlink-app-icon-28.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "quick-symlink-app-icon-56.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "quick-symlink-app-icon-84.png", + "scale" : "3x" + }, + { + "idiom" : "mac", + "filename" : "quick-symlink-app-icon-29.png", + "scale" : "1x" + }, + { + "idiom" : "mac", + "filename" : "quick-symlink-app-icon-57.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-28.png b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-28.png new file mode 100644 index 0000000..84841b0 Binary files /dev/null and b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-28.png differ diff --git a/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-29.png b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-29.png new file mode 100644 index 0000000..84841b0 Binary files /dev/null and b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-29.png differ diff --git a/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-56.png b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-56.png new file mode 100644 index 0000000..be4fb4c Binary files /dev/null and b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-56.png differ diff --git a/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-57.png b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-57.png new file mode 100644 index 0000000..be4fb4c Binary files /dev/null and b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-57.png differ diff --git a/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-84.png b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-84.png new file mode 100644 index 0000000..7dbe657 Binary files /dev/null and b/hard-link-action-extension/Assets.xcassets/quick-symlink-toolbar-item-image.imageset/quick-symlink-app-icon-84.png differ diff --git a/hard-link-action-extension/FinderSync.swift b/hard-link-action-extension/FinderSync.swift new file mode 100644 index 0000000..6320399 --- /dev/null +++ b/hard-link-action-extension/FinderSync.swift @@ -0,0 +1,127 @@ +// +// FinderSync.swift +// hard-link-action-extension +// +// Created by Alexander A. Kropotin on 02/09/2021. +// Copyright Β© 2021 Alexander A. Kropotin. All rights reserved. +// + +import Cocoa +import FinderSync + +class FinderSync: FIFinderSync { + + let quickSymlinkToolbarItemImage = NSImage(named:NSImage.Name(rawValue: "quick-symlink-toolbar-item-image")); + + let copyPathAction = CopyPathAction.init(); + let pasteLinkAction = PasteLinkAction.init(fileLinkManager: HardLinkManager.init()); + let createSymlink = CreateLinkAction.init(fileLinkManager: HardLinkManager.init()); + + override init() { + super.init() + + NSLog("FinderSync() launched from %@", Bundle.main.bundlePath as NSString) + + // Set up the directory we are syncing. + let finderSync = FIFinderSyncController.default(); + + // Shared group preferences required + _ = UserDefaults.init(suiteName: "org.ololx.quick-symlink") + + if let mountedVolumes = FileManager.default.mountedVolumeURLs(includingResourceValuesForKeys: nil, + options: .skipHiddenVolumes) { + finderSync.directoryURLs = Set(mountedVolumes); + } + + let notificationCenter = NSWorkspace.shared.notificationCenter + notificationCenter.addObserver(forName: NSWorkspace.didMountNotification, object: nil, queue: .main) { + (notification) in + if let volumeURL = notification.userInfo?[NSWorkspace.volumeURLUserInfoKey] as? URL { + finderSync.directoryURLs.insert(volumeURL); + } + } + } + + // MARK: - Primary Finder Sync protocol methods + + override func beginObservingDirectory(at url: URL) { + // The user is now seeing the container's contents. + // If they see it in more than one view at a time, we're only told once. + NSLog("beginObservingDirectoryAtURL: %@", url.path as NSString) + } + + + override func endObservingDirectory(at url: URL) { + // The user is no longer seeing the container's contents. + NSLog("endObservingDirectoryAtURL: %@", url.path as NSString) + } + + // MARK: - Menu and toolbar item support + + override var toolbarItemName: String { + return NSLocalizedString("HARD_LINK_ACTIONS_EXTENTION_NAME", comment: ""); + } + + override var toolbarItemToolTip: String { + return NSLocalizedString("HARD_LINK_ACTIONS_EXTENTION_TOOL_TIP", comment: ""); + } + + override var toolbarItemImage: NSImage { + return quickSymlinkToolbarItemImage!; + } + + override func menu(for menuKind: FIMenuKind) -> NSMenu { + // Produce a menu for the extension (to be shown when right clicking a folder in Finder) + let quickSymlinkMenu = NSMenu(title: ""); + quickSymlinkMenu.addItem( + withTitle: NSLocalizedString("CREATE_LINK_ACTION_NAME", comment: ""), + action: #selector(createSymlink(_:)), + keyEquivalent: "" + ); + + quickSymlinkMenu.addItem( + withTitle: NSLocalizedString("COPY_PATH_ACTION_NAME", comment: ""), + action: #selector(copyPathToClipboard(_:)), + keyEquivalent: "" + ); + + let pastleSymlinkFromClipboardMenuItem = NSMenuItem.init( + title: NSLocalizedString("PASTE_LINK_ACTION_NAME", comment: ""), + action: #selector(pastleSymlinkFromClipboard(_:)), + keyEquivalent: "" + ); + quickSymlinkMenu.addItem(pastleSymlinkFromClipboardMenuItem); + + if (NSPasteboard.init(name: NSPasteboard.Name.init(rawValue: "qs")).string(forType: NSPasteboard.PasteboardType.string) ?? "").isEmpty { + pastleSymlinkFromClipboardMenuItem.isEnabled = false; + } + + if menuKind.rawValue == 3 { + return quickSymlinkMenu; + } else { + let quickSymLinkMainMenu = NSMenu(title: ""); + let quickSymlinkMenuItem = NSMenuItem( + title: NSLocalizedString("HARD_LINK_ACTIONS_EXTENTION_NAME", comment: ""), + action: nil, + keyEquivalent: "" + ); + quickSymLinkMainMenu.setSubmenu(quickSymlinkMenu, for: quickSymlinkMenuItem); + quickSymLinkMainMenu.addItem(quickSymlinkMenuItem); + + return quickSymLinkMainMenu; + } + } + + @IBAction func copyPathToClipboard(_ sender: AnyObject?) { + self.copyPathAction.execute(); + } + + @IBAction func pastleSymlinkFromClipboard(_ sender: AnyObject?) { + self.pasteLinkAction.execute(); + } + + @IBAction func createSymlink(_ sender: AnyObject?) { + self.createSymlink.execute(); + } +} + diff --git a/hard-link-action-extension/Info.plist b/hard-link-action-extension/Info.plist new file mode 100644 index 0000000..e03c840 --- /dev/null +++ b/hard-link-action-extension/Info.plist @@ -0,0 +1,41 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + hard-link-action-extension + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + LSUIElement + + NSExtension + + NSExtensionAttributes + + NSExtensionPointIdentifier + com.apple.FinderSync + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).FinderSync + + NSHumanReadableCopyright + Copyright Β© 2021 Alexander A. Kropotin. All rights reserved. + NSPrincipalClass + NSApplication + + diff --git a/hard-link-action-extension/hard-link-action-extension.entitlements b/hard-link-action-extension/hard-link-action-extension.entitlements new file mode 100644 index 0000000..604e60f --- /dev/null +++ b/hard-link-action-extension/hard-link-action-extension.entitlements @@ -0,0 +1,20 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.folders.user-selected.read-write + + com.apple.security.files.user-selected.read-write + + com.apple.security.temporary-exception.files.absolute-path.read-write + + / + + com.apple.security.temporary-exception.files.home-relative-path.read-write + + / + + + diff --git a/quick-symlink-extension/Base.lproj/Localizable.strings b/quick-symlink-extension/Base.lproj/Localizable.strings index 2a48615..1de47e6 100644 --- a/quick-symlink-extension/Base.lproj/Localizable.strings +++ b/quick-symlink-extension/Base.lproj/Localizable.strings @@ -7,10 +7,16 @@ */ /* Class = "NSMenuItem"; */ -"EXTENTION_NAME" = "Quick Symlink"; +"SOFT_LINK_ACTIONS_EXTENTION_NAME" = "Soft link actions"; /* Class = "NSMenuItem"; */ -"EXTENTION_TOOL_TIP" = "Create symbolic links for selected files and folders"; +"SOFT_LINK_ACTIONS_EXTENTION_TOOL_TIP" = "Create symbolic links for selected files and folders"; + +/* Class = "NSMenuItem"; */ +"HARD_LINK_ACTIONS_EXTENTION_NAME" = "Hard link actions"; + +/* Class = "NSMenuItem"; */ +"HARD_LINK_ACTIONS_EXTENTION_TOOL_TIP" = "Create hard links for selected files and folders"; /* Class = "NSMenuItem"; */ "COPY_PATH_ACTION_NAME" = "Copy path from here"; diff --git a/quick-symlink-extension/FinderSync.swift b/quick-symlink-extension/FinderSync.swift index 5b6626d..fe10be9 100644 --- a/quick-symlink-extension/FinderSync.swift +++ b/quick-symlink-extension/FinderSync.swift @@ -13,9 +13,9 @@ class FinderSync: FIFinderSync { let quickSymlinkToolbarItemImage = NSImage(named:NSImage.Name(rawValue: "quick-symlink-toolbar-item-image")); let copyPathAction = CopyPathAction.init(); - let pasteLinkAction = PasteLinkAction.init(); - let replaceWithLinkAction = ReplaceWithLinkAction.init(); - let createSymlink = CreateLinkAction.init(); + let pasteLinkAction = PasteLinkAction.init(fileLinkManager: SoftLinkManager.init()); + let replaceWithLinkAction = ReplaceWithLinkAction.init(fileLinkManager: SoftLinkManager.init()); + let createSymlink = CreateLinkAction.init(fileLinkManager: SoftLinkManager.init()); override init() { super.init() @@ -59,11 +59,11 @@ class FinderSync: FIFinderSync { // MARK: - Menu and toolbar item support override var toolbarItemName: String { - return NSLocalizedString("EXTENTION_NAME", comment: ""); + return NSLocalizedString("SOFT_LINK_ACTIONS_EXTENTION_NAME", comment: ""); } override var toolbarItemToolTip: String { - return NSLocalizedString("EXTENTION_TOOL_TIP", comment: ""); + return NSLocalizedString("SOFT_LINK_ACTIONS_EXTENTION_TOOL_TIP", comment: ""); } override var toolbarItemImage: NSImage { @@ -109,7 +109,7 @@ class FinderSync: FIFinderSync { } else { let quickSymLinkMainMenu = NSMenu(title: ""); let quickSymlinkMenuItem = NSMenuItem( - title: NSLocalizedString("EXTENTION_NAME", comment: ""), + title: NSLocalizedString("SOFT_LINK_ACTIONS_EXTENTION_NAME", comment: ""), action: nil, keyEquivalent: "" ); diff --git a/quick-symlink-extension/Info.plist b/quick-symlink-extension/Info.plist index 8f22540..99c4c3b 100644 --- a/quick-symlink-extension/Info.plist +++ b/quick-symlink-extension/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en_GB + $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName quick-symlink-extension CFBundleExecutable diff --git a/quick-symlink-extension/en-GB.lproj/Localizable.strings b/quick-symlink-extension/en-GB.lproj/Localizable.strings index 2a48615..1de47e6 100644 --- a/quick-symlink-extension/en-GB.lproj/Localizable.strings +++ b/quick-symlink-extension/en-GB.lproj/Localizable.strings @@ -7,10 +7,16 @@ */ /* Class = "NSMenuItem"; */ -"EXTENTION_NAME" = "Quick Symlink"; +"SOFT_LINK_ACTIONS_EXTENTION_NAME" = "Soft link actions"; /* Class = "NSMenuItem"; */ -"EXTENTION_TOOL_TIP" = "Create symbolic links for selected files and folders"; +"SOFT_LINK_ACTIONS_EXTENTION_TOOL_TIP" = "Create symbolic links for selected files and folders"; + +/* Class = "NSMenuItem"; */ +"HARD_LINK_ACTIONS_EXTENTION_NAME" = "Hard link actions"; + +/* Class = "NSMenuItem"; */ +"HARD_LINK_ACTIONS_EXTENTION_TOOL_TIP" = "Create hard links for selected files and folders"; /* Class = "NSMenuItem"; */ "COPY_PATH_ACTION_NAME" = "Copy path from here"; diff --git a/quick-symlink-extension/en.lproj/Localizable.strings b/quick-symlink-extension/en.lproj/Localizable.strings index 2a48615..1de47e6 100644 --- a/quick-symlink-extension/en.lproj/Localizable.strings +++ b/quick-symlink-extension/en.lproj/Localizable.strings @@ -7,10 +7,16 @@ */ /* Class = "NSMenuItem"; */ -"EXTENTION_NAME" = "Quick Symlink"; +"SOFT_LINK_ACTIONS_EXTENTION_NAME" = "Soft link actions"; /* Class = "NSMenuItem"; */ -"EXTENTION_TOOL_TIP" = "Create symbolic links for selected files and folders"; +"SOFT_LINK_ACTIONS_EXTENTION_TOOL_TIP" = "Create symbolic links for selected files and folders"; + +/* Class = "NSMenuItem"; */ +"HARD_LINK_ACTIONS_EXTENTION_NAME" = "Hard link actions"; + +/* Class = "NSMenuItem"; */ +"HARD_LINK_ACTIONS_EXTENTION_TOOL_TIP" = "Create hard links for selected files and folders"; /* Class = "NSMenuItem"; */ "COPY_PATH_ACTION_NAME" = "Copy path from here"; diff --git a/quick-symlink-extension/quick_symlink_extension.entitlements b/quick-symlink-extension/quick_symlink_extension.entitlements deleted file mode 100644 index 0c67376..0000000 --- a/quick-symlink-extension/quick_symlink_extension.entitlements +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/quick-symlink-extension/ru.lproj/Localizable.strings b/quick-symlink-extension/ru.lproj/Localizable.strings index 361f830..18900c5 100644 --- a/quick-symlink-extension/ru.lproj/Localizable.strings +++ b/quick-symlink-extension/ru.lproj/Localizable.strings @@ -7,10 +7,16 @@ */ /* Class = "NSMenuItem"; */ -"EXTENTION_NAME" = "Быстрый симлинк"; +"SOFT_LINK_ACTIONS_EXTENTION_NAME" = "ДСйствия с симлинками"; /* Class = "NSMenuItem"; */ -"EXTENTION_TOOL_TIP" = "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ ΡΠΈΠΌΠ²ΠΎΠ»ΡŒΠ½Ρ‹Π΅ ссылки для Π²Ρ‹Π±Ρ€Π°Π½Π½Ρ‹Ρ… Ρ„Π°ΠΉΠ»ΠΎΠ² ΠΈ ΠΏΠ°ΠΏΠΎΠΊ"; +"SOFT_LINK_ACTIONS_EXTENTION_TOOL_TIP" = "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ ΡΠΈΠΌΠ²ΠΎΠ»ΡŒΠ½Ρ‹Π΅ ссылки для Π²Ρ‹Π±Ρ€Π°Π½Π½Ρ‹Ρ… Ρ„Π°ΠΉΠ»ΠΎΠ² ΠΈ ΠΏΠ°ΠΏΠΎΠΊ"; + +/* Class = "NSMenuItem"; */ +"HARD_LINK_ACTIONS_EXTENTION_NAME" = "ДСйствия с псСвдонимами"; + +/* Class = "NSMenuItem"; */ +"HARD_LINK_ACTIONS_EXTENTION_TOOL_TIP" = "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ псСвдонимы для Π²Ρ‹Π±Ρ€Π°Π½Π½Ρ‹Ρ… Ρ„Π°ΠΉΠ»ΠΎΠ² ΠΈ ΠΏΠ°ΠΏΠΎΠΊ"; /* Class = "NSMenuItem"; */ "COPY_PATH_ACTION_NAME" = "Π‘ΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡƒΡ‚ΡŒ ΠΎΡ‚ΡΡŽΠ΄Π°"; diff --git a/quick-symlink.xcodeproj/project.pbxproj b/quick-symlink.xcodeproj/project.pbxproj index 1de801c..9853a2e 100644 --- a/quick-symlink.xcodeproj/project.pbxproj +++ b/quick-symlink.xcodeproj/project.pbxproj @@ -26,12 +26,26 @@ A30D4A4D26A0C18B00BA775B /* QuickSymlinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A345C9BE26A0B200004FBF0F /* QuickSymlinkAction.swift */; }; A316477726B7B403001DD969 /* CreateLinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A316477626B7B403001DD969 /* CreateLinkAction.swift */; }; A316477826B7B403001DD969 /* CreateLinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A316477626B7B403001DD969 /* CreateLinkAction.swift */; }; + A31904A426E104FA00D7F69D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A31904A326E104FA00D7F69D /* Assets.xcassets */; }; A32EE8E0265D4D05008648AA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A32EE8DF265D4D05008648AA /* Assets.xcassets */; }; A345C9BF26A0B200004FBF0F /* QuickSymlinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A345C9BE26A0B200004FBF0F /* QuickSymlinkAction.swift */; }; A345C9C226A0B30F004FBF0F /* CopyPathAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A345C9C126A0B30F004FBF0F /* CopyPathAction.swift */; }; A345C9C526A0B49C004FBF0F /* PasteLinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A345C9C426A0B49C004FBF0F /* PasteLinkAction.swift */; }; A345C9C826A0B552004FBF0F /* ReplaceWithLinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A345C9C726A0B552004FBF0F /* ReplaceWithLinkAction.swift */; }; + A36F9B0626DFAC27009E95CE /* FileLinkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36F9B0526DFAC27009E95CE /* FileLinkManager.swift */; }; + A36F9B0726DFB127009E95CE /* FileLinkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36F9B0526DFAC27009E95CE /* FileLinkManager.swift */; }; A3D93EB526A09B5A004E068D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A3D93EB726A09B5A004E068D /* Localizable.strings */; }; + A3E4D35526E0FD2900C9F175 /* FinderSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3E4D35426E0FD2900C9F175 /* FinderSync.swift */; }; + A3E4D35A26E0FD2900C9F175 /* hard-link-action-extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = A3E4D35226E0FD2900C9F175 /* hard-link-action-extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + A3E4D35E26E0FEE700C9F175 /* FileLinkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A36F9B0526DFAC27009E95CE /* FileLinkManager.swift */; }; + A3E4D35F26E0FEEA00C9F175 /* QuickSymlinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A345C9BE26A0B200004FBF0F /* QuickSymlinkAction.swift */; }; + A3E4D36026E0FEED00C9F175 /* CopyPathAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A345C9C126A0B30F004FBF0F /* CopyPathAction.swift */; }; + A3E4D36126E0FEEF00C9F175 /* PasteLinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A345C9C426A0B49C004FBF0F /* PasteLinkAction.swift */; }; + A3E4D36226E0FEF200C9F175 /* ReplaceWithLinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A345C9C726A0B552004FBF0F /* ReplaceWithLinkAction.swift */; }; + A3E4D36326E0FEF400C9F175 /* CreateLinkAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A316477626B7B403001DD969 /* CreateLinkAction.swift */; }; + A3E4D36426E0FF8100C9F175 /* ResourcePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41926D22116002EEF58 /* ResourcePath.swift */; }; + A3E4D36526E0FF8600C9F175 /* Path.swift in Sources */ = {isa = PBXBuildFile; fileRef = A307B41626D21E39002EEF58 /* Path.swift */; }; + A3E4D36626E101B000C9F175 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A3D93EB726A09B5A004E068D /* Localizable.strings */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -49,6 +63,13 @@ remoteGlobalIDString = A30B9ABD265CA68900ACAA63; remoteInfo = "quick-symlink-extension"; }; + A3E4D35826E0FD2900C9F175 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A30B9A9F265CA63300ACAA63 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A3E4D35126E0FD2900C9F175; + remoteInfo = "hard-link-action-extension"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -58,6 +79,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + A3E4D35A26E0FD2900C9F175 /* hard-link-action-extension.appex in Embed App Extensions */, A30B9AC6265CA68900ACAA63 /* quick-symlink-extension.appex in Embed App Extensions */, ); name = "Embed App Extensions"; @@ -81,14 +103,15 @@ A30B9ABE265CA68900ACAA63 /* quick-symlink-extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "quick-symlink-extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; A30B9AC0265CA68900ACAA63 /* FinderSync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinderSync.swift; sourceTree = ""; }; A30B9AC2265CA68900ACAA63 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A30B9AC3265CA68900ACAA63 /* quick_symlink_extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = quick_symlink_extension.entitlements; sourceTree = ""; }; A30B9ACB265CA78600ACAA63 /* quick-symlink-extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "quick-symlink-extension.entitlements"; sourceTree = ""; }; A316477626B7B403001DD969 /* CreateLinkAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateLinkAction.swift; sourceTree = ""; }; + A31904A326E104FA00D7F69D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; A32EE8DF265D4D05008648AA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; A345C9BE26A0B200004FBF0F /* QuickSymlinkAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickSymlinkAction.swift; sourceTree = ""; }; A345C9C126A0B30F004FBF0F /* CopyPathAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyPathAction.swift; sourceTree = ""; }; A345C9C426A0B49C004FBF0F /* PasteLinkAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteLinkAction.swift; sourceTree = ""; }; A345C9C726A0B552004FBF0F /* ReplaceWithLinkAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplaceWithLinkAction.swift; sourceTree = ""; }; + A36F9B0526DFAC27009E95CE /* FileLinkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileLinkManager.swift; sourceTree = ""; }; A3D93EA426A08EDF004E068D /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Main.strings; sourceTree = ""; }; A3D93EA826A0904F004E068D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = ""; }; A3D93EA926A09053004E068D /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Main.strings"; sourceTree = ""; }; @@ -96,6 +119,10 @@ A3D93EB826A09B61004E068D /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; A3D93EB926A09B72004E068D /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Localizable.strings"; sourceTree = ""; }; A3D93EBA26A09B73004E068D /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + A3E4D35226E0FD2900C9F175 /* hard-link-action-extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "hard-link-action-extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + A3E4D35426E0FD2900C9F175 /* FinderSync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FinderSync.swift; sourceTree = ""; }; + A3E4D35626E0FD2900C9F175 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A3E4D35726E0FD2900C9F175 /* hard-link-action-extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "hard-link-action-extension.entitlements"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -120,10 +147,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A3E4D34F26E0FD2900C9F175 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - A307B41426D21DBE002EEF58 /* QuickSymlinkActions */ = { + A307B41426D21DBE002EEF58 /* action */ = { isa = PBXGroup; children = ( A316477626B7B403001DD969 /* CreateLinkAction.swift */, @@ -132,16 +166,16 @@ A345C9C126A0B30F004FBF0F /* CopyPathAction.swift */, A345C9BE26A0B200004FBF0F /* QuickSymlinkAction.swift */, ); - path = QuickSymlinkActions; + path = action; sourceTree = ""; }; - A307B41526D21E23002EEF58 /* FileSystem */ = { + A307B41526D21E23002EEF58 /* path */ = { isa = PBXGroup; children = ( A307B41626D21E39002EEF58 /* Path.swift */, A307B41926D22116002EEF58 /* ResourcePath.swift */, ); - path = FileSystem; + path = path; sourceTree = ""; }; A307B42526D255FB002EEF58 /* quick-symlink-tests */ = { @@ -161,6 +195,7 @@ A30B9AA9265CA63300ACAA63 /* quick-symlink */, A30B9ABF265CA68900ACAA63 /* quick-symlink-extension */, A307B42526D255FB002EEF58 /* quick-symlink-tests */, + A3E4D35326E0FD2900C9F175 /* hard-link-action-extension */, A30B9AA8265CA63300ACAA63 /* Products */, ); sourceTree = ""; @@ -171,6 +206,7 @@ A30B9AA7265CA63300ACAA63 /* quick-symlink.app */, A30B9ABE265CA68900ACAA63 /* quick-symlink-extension.appex */, A307B42426D255FB002EEF58 /* quick-symlink-tests.xctest */, + A3E4D35226E0FD2900C9F175 /* hard-link-action-extension.appex */, ); name = Products; sourceTree = ""; @@ -195,7 +231,6 @@ A30B9AC0265CA68900ACAA63 /* FinderSync.swift */, A32EE8DF265D4D05008648AA /* Assets.xcassets */, A30B9AC2265CA68900ACAA63 /* Info.plist */, - A30B9AC3265CA68900ACAA63 /* quick_symlink_extension.entitlements */, ); path = "quick-symlink-extension"; sourceTree = ""; @@ -203,12 +238,32 @@ A345C9BD26A0B12C004FBF0F /* commons */ = { isa = PBXGroup; children = ( - A307B41526D21E23002EEF58 /* FileSystem */, - A307B41426D21DBE002EEF58 /* QuickSymlinkActions */, + A36F9B0426DFAA4B009E95CE /* link */, + A307B41526D21E23002EEF58 /* path */, ); path = commons; sourceTree = ""; }; + A36F9B0426DFAA4B009E95CE /* link */ = { + isa = PBXGroup; + children = ( + A307B41426D21DBE002EEF58 /* action */, + A36F9B0526DFAC27009E95CE /* FileLinkManager.swift */, + ); + path = link; + sourceTree = ""; + }; + A3E4D35326E0FD2900C9F175 /* hard-link-action-extension */ = { + isa = PBXGroup; + children = ( + A3E4D35426E0FD2900C9F175 /* FinderSync.swift */, + A3E4D35626E0FD2900C9F175 /* Info.plist */, + A3E4D35726E0FD2900C9F175 /* hard-link-action-extension.entitlements */, + A31904A326E104FA00D7F69D /* Assets.xcassets */, + ); + path = "hard-link-action-extension"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -243,6 +298,7 @@ ); dependencies = ( A30B9AC5265CA68900ACAA63 /* PBXTargetDependency */, + A3E4D35926E0FD2900C9F175 /* PBXTargetDependency */, ); name = "quick-symlink"; productName = "quick-symlink"; @@ -266,6 +322,23 @@ productReference = A30B9ABE265CA68900ACAA63 /* quick-symlink-extension.appex */; productType = "com.apple.product-type.app-extension"; }; + A3E4D35126E0FD2900C9F175 /* hard-link-action-extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = A3E4D35B26E0FD2900C9F175 /* Build configuration list for PBXNativeTarget "hard-link-action-extension" */; + buildPhases = ( + A3E4D34E26E0FD2900C9F175 /* Sources */, + A3E4D34F26E0FD2900C9F175 /* Frameworks */, + A3E4D35026E0FD2900C9F175 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "hard-link-action-extension"; + productName = "hard-link-action-extension"; + productReference = A3E4D35226E0FD2900C9F175 /* hard-link-action-extension.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -294,6 +367,10 @@ }; }; }; + A3E4D35126E0FD2900C9F175 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Automatic; + }; }; }; buildConfigurationList = A30B9AA2265CA63300ACAA63 /* Build configuration list for PBXProject "quick-symlink" */; @@ -314,6 +391,7 @@ A30B9AA6265CA63300ACAA63 /* quick-symlink */, A30B9ABD265CA68900ACAA63 /* quick-symlink-extension */, A307B42326D255FB002EEF58 /* quick-symlink-tests */, + A3E4D35126E0FD2900C9F175 /* hard-link-action-extension */, ); }; /* End PBXProject section */ @@ -344,6 +422,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A3E4D35026E0FD2900C9F175 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A31904A426E104FA00D7F69D /* Assets.xcassets in Resources */, + A3E4D36626E101B000C9F175 /* Localizable.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -370,6 +457,7 @@ A345C9BF26A0B200004FBF0F /* QuickSymlinkAction.swift in Sources */, A30B9AAB265CA63300ACAA63 /* AppDelegate.swift in Sources */, A345C9C526A0B49C004FBF0F /* PasteLinkAction.swift in Sources */, + A36F9B0626DFAC27009E95CE /* FileLinkManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -385,6 +473,23 @@ A316477826B7B403001DD969 /* CreateLinkAction.swift in Sources */, A30D4A4D26A0C18B00BA775B /* QuickSymlinkAction.swift in Sources */, A30D4A4A26A0C14400BA775B /* CopyPathAction.swift in Sources */, + A36F9B0726DFB127009E95CE /* FileLinkManager.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A3E4D34E26E0FD2900C9F175 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A3E4D36226E0FEF200C9F175 /* ReplaceWithLinkAction.swift in Sources */, + A3E4D36326E0FEF400C9F175 /* CreateLinkAction.swift in Sources */, + A3E4D36426E0FF8100C9F175 /* ResourcePath.swift in Sources */, + A3E4D35526E0FD2900C9F175 /* FinderSync.swift in Sources */, + A3E4D36026E0FEED00C9F175 /* CopyPathAction.swift in Sources */, + A3E4D35F26E0FEEA00C9F175 /* QuickSymlinkAction.swift in Sources */, + A3E4D36126E0FEEF00C9F175 /* PasteLinkAction.swift in Sources */, + A3E4D36526E0FF8600C9F175 /* Path.swift in Sources */, + A3E4D35E26E0FEE700C9F175 /* FileLinkManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -401,6 +506,11 @@ target = A30B9ABD265CA68900ACAA63 /* quick-symlink-extension */; targetProxy = A30B9AC4265CA68900ACAA63 /* PBXContainerItemProxy */; }; + A3E4D35926E0FD2900C9F175 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A3E4D35126E0FD2900C9F175 /* hard-link-action-extension */; + targetProxy = A3E4D35826E0FD2900C9F175 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -438,7 +548,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "quick-symlink-tests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink-tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; @@ -454,7 +564,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "quick-symlink-tests/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink-tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; @@ -511,7 +621,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -563,7 +673,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.10; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -580,7 +690,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "quick-symlink/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; @@ -597,7 +707,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "quick-symlink/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 4.0; @@ -612,7 +722,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "quick-symlink-extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink.quick-symlink-extension"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -628,7 +738,7 @@ COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = "quick-symlink-extension/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink.quick-symlink-extension"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -636,6 +746,38 @@ }; name = Release; }; + A3E4D35C26E0FD2900C9F175 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "hard-link-action-extension/hard-link-action-extension.entitlements"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "hard-link-action-extension/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink.hard-link-action-extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + }; + name = Debug; + }; + A3E4D35D26E0FD2900C9F175 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = "hard-link-action-extension/hard-link-action-extension.entitlements"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "hard-link-action-extension/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_BUNDLE_IDENTIFIER = "org.ololx.quick-symlink.hard-link-action-extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -675,6 +817,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + A3E4D35B26E0FD2900C9F175 /* Build configuration list for PBXNativeTarget "hard-link-action-extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A3E4D35C26E0FD2900C9F175 /* Debug */, + A3E4D35D26E0FD2900C9F175 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = A30B9A9F265CA63300ACAA63 /* Project object */;