SavePanel.swift 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. //
  2. // Copyright © 2021 osy. All rights reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. import SwiftUI
  17. @available(macOS 11, *)
  18. struct SavePanel: NSViewRepresentable {
  19. @EnvironmentObject private var data: UTMData
  20. @Binding var isPresented: Bool
  21. var shareItem: VMShareItemModifier.ShareItem?
  22. func makeNSView(context: Context) -> some NSView {
  23. return NSView()
  24. }
  25. func updateNSView(_ nsView: NSViewType, context: Context) {
  26. if isPresented {
  27. guard let shareItem = shareItem else {
  28. return
  29. }
  30. guard let window = nsView.window else {
  31. return
  32. }
  33. // Initializing the SavePanel and setting its properties
  34. let savePanel = NSSavePanel()
  35. if let downloadsUrl = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first {
  36. savePanel.directoryURL = downloadsUrl
  37. }
  38. switch shareItem {
  39. case .debugLog:
  40. savePanel.title = NSLocalizedString("Select where to save debug log:", comment: "SavePanel")
  41. savePanel.nameFieldStringValue = "debug"
  42. savePanel.allowedContentTypes = [.appleLog]
  43. case .utmCopy(let vm), .utmMove(let vm):
  44. savePanel.title = NSLocalizedString("Select where to save UTM Virtual Machine:", comment: "SavePanel")
  45. savePanel.nameFieldStringValue = vm.pathUrl.lastPathComponent
  46. savePanel.allowedContentTypes = [.UTM]
  47. case .qemuCommand:
  48. savePanel.title = NSLocalizedString("Select where to export QEMU command:", comment: "SavePanel")
  49. savePanel.nameFieldStringValue = "command"
  50. savePanel.allowedContentTypes = [.plainText]
  51. }
  52. // Calling savePanel.begin with the appropriate completion handlers
  53. // SwiftUI BUG: if we don't wait, there is a crash due to an access issue
  54. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
  55. switch shareItem {
  56. case .debugLog(let sourceUrl):
  57. savePanel.beginSheetModal(for: window) { result in
  58. if result == .OK {
  59. if let destUrl = savePanel.url {
  60. data.busyWorkAsync {
  61. let fileManager = FileManager.default
  62. // All this mess is because FileManager.replaceItemAt deletes the source item
  63. let tempUrl = fileManager.temporaryDirectory.appendingPathComponent(sourceUrl.lastPathComponent)
  64. if fileManager.fileExists(atPath: tempUrl.path) {
  65. try fileManager.removeItem(at: tempUrl)
  66. }
  67. try fileManager.copyItem(at: sourceUrl, to: tempUrl)
  68. _ = try fileManager.replaceItemAt(destUrl, withItemAt: tempUrl)
  69. }
  70. }
  71. }
  72. isPresented = false
  73. }
  74. case .utmCopy(let vm), .utmMove(let vm):
  75. savePanel.beginSheetModal(for: window) { result in
  76. if result == .OK {
  77. if let destUrl = savePanel.url {
  78. data.busyWorkAsync {
  79. if case .utmMove(_) = shareItem {
  80. try await data.move(vm: vm, to: destUrl)
  81. } else {
  82. try await data.export(vm: vm, to: destUrl)
  83. }
  84. }
  85. }
  86. }
  87. isPresented = false
  88. }
  89. case .qemuCommand(let command):
  90. savePanel.beginSheetModal(for: window) { result in
  91. if result == .OK {
  92. if let destUrl = savePanel.url {
  93. data.busyWork {
  94. try command.write(to: destUrl, atomically: true, encoding: .utf8)
  95. }
  96. }
  97. }
  98. isPresented = false
  99. }
  100. }
  101. }
  102. }
  103. }
  104. }