KMBatchTableCellView.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. //
  2. // KMBatchTableCellView.swift
  3. // PDF Master
  4. //
  5. // Created by kdanmobile on 2023/10/27.
  6. //
  7. import Cocoa
  8. typealias batchTableCellComboboxMouseDownCallback = (_ mouseDown: Bool) -> ()
  9. typealias batchTableCellRemoveFileCallBack = (_ file: KMBatchOperateFile) -> ()
  10. @objc enum KMBatchTableCellViewType: Int {
  11. case Size = 0
  12. case PageRange
  13. case FileName
  14. case Status
  15. case DPI
  16. case Dimensions
  17. }
  18. class KMBatchTableCellCombobox: KMComboBox{
  19. var mouseDownCallback: batchTableCellComboboxMouseDownCallback?
  20. override func mouseDown(with event: NSEvent) {
  21. super.mouseDown(with: event)
  22. if (self.mouseDownCallback != nil) {
  23. self.mouseDownCallback!(true)
  24. }
  25. }
  26. }
  27. class KMBatchTableCellView: NSTableCellView,NSComboBoxDelegate{
  28. var removeFileCallBack: batchTableCellRemoveFileCallBack?
  29. var pageRangeCombobox: KMBatchTableCellCombobox?
  30. var errorTextField: NSTextField?
  31. var statusView: NSView?
  32. var removeButton: KMButton?
  33. var progressIndicator: NSProgressIndicator?
  34. var type: KMBatchTableCellViewType?
  35. var file: KMBatchOperateFile?
  36. var DPIComboBox: NSComboBox?
  37. var indicateImageView: NSImageView?
  38. var comboBoxContent: String?
  39. convenience init(type: KMBatchTableCellViewType) {
  40. self.init()
  41. switch type {
  42. case .Size:
  43. configuUIForSize()
  44. case .Dimensions:
  45. configuUIForSize()
  46. case .PageRange:
  47. configuUIForPageRange()
  48. case .FileName:
  49. configuUIForFileName()
  50. case .Status:
  51. configuUIForStatus()
  52. case .DPI:
  53. configuUIForDPI()
  54. default:
  55. break
  56. }
  57. DistributedNotificationCenter.default().addObserver(self, selector: #selector(themeChanged(notification:)), name: NSNotification.Name("AppleInterfaceThemeChangedNotification"), object: nil)
  58. self.type = type
  59. }
  60. func configuUIForSize() {
  61. self.textField = NSTextField(frame: .zero)
  62. self.textField!.isBordered = false
  63. self.textField!.drawsBackground = true
  64. self.textField!.backgroundColor = NSColor.clear
  65. self.textField!.isEditable = false
  66. self.addSubview(self.textField!)
  67. self.textField!.translatesAutoresizingMaskIntoConstraints = false
  68. NSLayoutConstraint.activate([
  69. self.textField!.topAnchor.constraint(equalTo: self.topAnchor, constant: 13),
  70. self.textField!.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0)
  71. ])
  72. self.textField!.textColor = KMAppearance.Layout.h0Color()
  73. self.textField!.font = NSFont.systemFont(ofSize: 12)
  74. }
  75. func configuUIForPageRange() {
  76. self.pageRangeCombobox = KMBatchTableCellCombobox(frame: .zero)
  77. self.pageRangeCombobox!.focusRingType = .none
  78. self.pageRangeCombobox!.type = .none
  79. self.pageRangeCombobox!.wantsLayer = true
  80. self.pageRangeCombobox!.layer?.backgroundColor = KMAppearance.Layout.l1Color().cgColor
  81. self.pageRangeCombobox!.layer?.borderWidth = 1.0
  82. self.pageRangeCombobox!.layer?.borderColor = KMAppearance.Interactive.s0Color().cgColor
  83. self.pageRangeCombobox!.layer?.cornerRadius = 1.0
  84. self.pageRangeCombobox!.backgroundColor = KMAppearance.Layout.l1Color()
  85. self.addSubview(self.pageRangeCombobox!)
  86. self.pageRangeCombobox!.translatesAutoresizingMaskIntoConstraints = false
  87. NSLayoutConstraint.activate([
  88. self.pageRangeCombobox!.leadingAnchor.constraint(equalTo: self.leadingAnchor),
  89. self.pageRangeCombobox!.widthAnchor.constraint(equalToConstant: 144),
  90. self.pageRangeCombobox!.topAnchor.constraint(equalTo: self.topAnchor, constant: 10)
  91. ])
  92. let choiceArray = [
  93. " \(NSLocalizedString("All Pages", comment: ""))",
  94. " \(NSLocalizedString("Odd Pages Only", comment: ""))",
  95. " \(NSLocalizedString("Even Pages Only", comment: ""))",
  96. " \(NSLocalizedString("e.g. 1,3-5,10", comment: ""))"
  97. ]
  98. self.pageRangeCombobox!.addItems(withObjectValues: choiceArray)
  99. let placeholderString = " \(NSLocalizedString("e.g. 1,3-5,10", comment: ""))"
  100. // self.pageRangeCombobox!.cell.placeholderString = placeholderString
  101. self.pageRangeCombobox!.selectItem(at: 0)
  102. self.pageRangeCombobox!.isEditable = false
  103. self.pageRangeCombobox!.delegate = self
  104. self.comboBoxContent = placeholderString
  105. self.updateViewColor()
  106. weak var weakSelf = self
  107. self.pageRangeCombobox!.mouseDownCallback = { mouseDown in
  108. let itemIdex = weakSelf!.pageRangeCombobox!.indexOfSelectedItem
  109. if itemIdex == 3 {//KMBatchOperatePageChoice.Input
  110. if weakSelf?.comboBoxContent == placeholderString {
  111. weakSelf?.pageRangeCombobox!.stringValue = ""
  112. } else {
  113. weakSelf?.pageRangeCombobox!.stringValue = (weakSelf?.comboBoxContent)!
  114. }
  115. }
  116. }
  117. }
  118. func configuUIForFileName() {
  119. self.textField = NSTextField(frame: .zero)
  120. self.textField!.isBordered = false
  121. self.textField!.drawsBackground = true
  122. self.textField!.backgroundColor = NSColor.clear
  123. self.textField!.isEditable = false
  124. self.textField!.maximumNumberOfLines = 1
  125. self.textField!.lineBreakMode = .byTruncatingMiddle
  126. self.addSubview(self.textField!)
  127. self.textField!.translatesAutoresizingMaskIntoConstraints = false
  128. NSLayoutConstraint.activate([
  129. self.textField!.topAnchor.constraint(equalTo: self.topAnchor, constant: 13),
  130. self.textField!.leadingAnchor.constraint(equalTo: self.leadingAnchor),
  131. self.textField!.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10)
  132. ])
  133. self.textField!.font = NSFont.systemFont(ofSize: 12)
  134. self.textField!.textColor = KMAppearance.Layout.h0Color()
  135. self.errorTextField = NSTextField(frame: .zero)
  136. self.errorTextField!.isBordered = false
  137. self.errorTextField!.drawsBackground = true
  138. self.errorTextField!.backgroundColor = NSColor.clear
  139. self.errorTextField!.isEditable = false
  140. self.errorTextField!.font = NSFont.systemFont(ofSize: 12)
  141. self.errorTextField!.textColor = KMAppearance.Status.errColor()
  142. self.addSubview(self.errorTextField!)
  143. self.errorTextField!.translatesAutoresizingMaskIntoConstraints = false
  144. NSLayoutConstraint.activate([
  145. self.errorTextField!.topAnchor.constraint(equalTo: self.textField!.bottomAnchor),
  146. self.errorTextField!.leadingAnchor.constraint(equalTo: self.leadingAnchor),
  147. self.errorTextField!.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -11),
  148. self.errorTextField!.trailingAnchor.constraint(equalTo: self.trailingAnchor)
  149. ])
  150. }
  151. func configuUIForStatus() {
  152. self.statusView = NSView(frame: .zero)
  153. self.statusView!.wantsLayer = true
  154. self.addSubview(self.statusView!)
  155. self.statusView!.translatesAutoresizingMaskIntoConstraints = false
  156. NSLayoutConstraint.activate([
  157. self.statusView!.leadingAnchor.constraint(equalTo: self.leadingAnchor),
  158. self.statusView!.topAnchor.constraint(equalTo: self.topAnchor, constant: 12),
  159. self.statusView!.widthAnchor.constraint(equalToConstant: 16),
  160. self.statusView!.heightAnchor.constraint(equalToConstant: 16)
  161. ])
  162. self.indicateImageView = NSImageView(frame: .zero)
  163. self.statusView!.addSubview(self.indicateImageView!)
  164. self.indicateImageView!.translatesAutoresizingMaskIntoConstraints = false
  165. NSLayoutConstraint.activate([
  166. self.indicateImageView!.leadingAnchor.constraint(equalTo: self.statusView!.leadingAnchor),
  167. self.indicateImageView!.trailingAnchor.constraint(equalTo: self.statusView!.trailingAnchor),
  168. self.indicateImageView!.topAnchor.constraint(equalTo: self.statusView!.topAnchor),
  169. self.indicateImageView!.bottomAnchor.constraint(equalTo: self.statusView!.bottomAnchor)
  170. ])
  171. self.progressIndicator = NSProgressIndicator(frame: .zero)
  172. self.statusView!.addSubview(self.progressIndicator!)
  173. self.progressIndicator!.translatesAutoresizingMaskIntoConstraints = false
  174. NSLayoutConstraint.activate([
  175. self.progressIndicator!.leadingAnchor.constraint(equalTo: self.statusView!.leadingAnchor),
  176. self.progressIndicator!.trailingAnchor.constraint(equalTo: self.statusView!.trailingAnchor),
  177. self.progressIndicator!.topAnchor.constraint(equalTo: self.statusView!.topAnchor),
  178. self.progressIndicator!.bottomAnchor.constraint(equalTo: self.statusView!.bottomAnchor)
  179. ])
  180. self.progressIndicator!.maxValue = 1.0
  181. self.progressIndicator!.minValue = 0
  182. self.progressIndicator!.style = .spinning
  183. self.progressIndicator!.controlSize = .small
  184. self.removeButton = KMButton(image: NSImage(named: KMImageNameUXIconBtnCloseNor)!, target: self, action: #selector(deleteFile(sender:)))
  185. self.removeButton!.isBordered = false
  186. self.removeButton!.isHidden = true
  187. self.addSubview(self.removeButton!)
  188. self.removeButton!.translatesAutoresizingMaskIntoConstraints = false
  189. NSLayoutConstraint.activate([
  190. self.removeButton!.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
  191. self.removeButton!.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -5),
  192. self.removeButton!.widthAnchor.constraint(equalToConstant: 20),
  193. self.removeButton!.heightAnchor.constraint(equalToConstant: 20)
  194. ])
  195. weak var weakSelf = self
  196. self.removeButton!.mouseMoveCallback = { mouseEntered in
  197. if mouseEntered {
  198. weakSelf?.removeButton!.image = NSImage(named: KMImageNameUXIconBtnCloseHov)
  199. } else {
  200. weakSelf?.removeButton!.image = NSImage(named: KMImageNameUXIconBtnCloseNor)
  201. }
  202. }
  203. }
  204. func configuUIForDPI() {
  205. self.DPIComboBox = NSComboBox(frame: .zero)
  206. self.addSubview(self.DPIComboBox!)
  207. self.DPIComboBox!.translatesAutoresizingMaskIntoConstraints = false
  208. let DPIArray = ["50 dpi", "72 dpi", "96 dpi", "150 dpi", "300 dpi", "600 dpi"]
  209. self.DPIComboBox!.addItems(withObjectValues: DPIArray)
  210. self.DPIComboBox!.selectItem(at: 0)
  211. self.DPIComboBox!.isEditable = false
  212. self.DPIComboBox!.delegate = self
  213. NSLayoutConstraint.activate([
  214. self.DPIComboBox!.leadingAnchor.constraint(equalTo: self.leadingAnchor),
  215. self.DPIComboBox!.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
  216. self.DPIComboBox!.centerXAnchor.constraint(equalTo: self.centerXAnchor)
  217. ])
  218. }
  219. @objc func themeChanged(notification: NSNotification) {
  220. DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
  221. self.updateViewColor()
  222. }
  223. }
  224. @objc func deleteFile(sender: Any) {
  225. if (self.removeFileCallBack != nil) {
  226. self.removeFileCallBack!(self.file!)
  227. }
  228. }
  229. func updateViewColor() {
  230. if KMAppearance.isDarkMode() {
  231. self.pageRangeCombobox!.backgroundColor = NSColor(red: 0.224, green: 0.235, blue: 0.243, alpha: 1)
  232. self.pageRangeCombobox!.layer!.backgroundColor = NSColor(red: 0.224, green: 0.235, blue: 0.243, alpha: 1).cgColor
  233. self.pageRangeCombobox!.layer!.borderColor = NSColor(red: 0.337, green: 0.345, blue: 0.353, alpha: 1).cgColor
  234. } else {
  235. self.pageRangeCombobox!.backgroundColor = NSColor.white
  236. self.pageRangeCombobox!.layer!.backgroundColor = NSColor.white.cgColor
  237. self.pageRangeCombobox!.layer!.borderColor = NSColor(red: 0.855, green: 0.859, blue: 0.871, alpha: 1).cgColor
  238. }
  239. }
  240. func updateInterface(_ file: KMBatchOperateFile) {
  241. self.file = file
  242. if self.type == .Size {
  243. self.textField!.stringValue = file.sizeString
  244. } else if self.type == .FileName {
  245. if (file.error != nil) {
  246. self.errorTextField!.stringValue = file.error!.localizedDescription
  247. self.errorTextField!.isHidden = false
  248. } else {
  249. self.errorTextField!.isHidden = true
  250. }
  251. self.textField!.stringValue = file.filePath.lastPathComponent
  252. } else if self.type == .PageRange {
  253. self.pageRangeCombobox!.isEnabled = self.file!.status != .processing
  254. self.pageRangeCombobox!.delegate = nil
  255. self.pageRangeCombobox!.selectItem(at: file.currentOperateInfo!.pageChoice?.rawValue ?? 0)
  256. self.pageRangeCombobox!.isEditable = false
  257. if file.currentOperateInfo!.pageChoice == .Input {
  258. self.pageRangeCombobox!.isEditable = true
  259. self.pageRangeCombobox!.stringValue = file.currentOperateInfo!.pageRangeString ?? ""
  260. }
  261. self.pageRangeCombobox!.delegate = self
  262. } else if self.type == .Status {
  263. if file.status == .Waiting {
  264. self.indicateImageView!.isHidden = false
  265. self.progressIndicator!.isHidden = true
  266. self.indicateImageView!.image = NSImage(named: KMImageNameUXIconProgressWaiting)
  267. } else if file.status == .Success {
  268. self.indicateImageView!.isHidden = false
  269. self.progressIndicator!.isHidden = true
  270. self.indicateImageView!.image = NSImage(named: "KMImageNameUXIconProgressComplete")
  271. } else if file.status == .Failed {
  272. self.indicateImageView!.isHidden = false
  273. self.progressIndicator!.isHidden = true
  274. self.indicateImageView!.image = NSImage(named: "KMImageNameUXIconProgressFailure")
  275. } else if file.status == .processing {
  276. self.indicateImageView!.isHidden = true
  277. self.progressIndicator!.isHidden = false
  278. self.progressIndicator!.startAnimation(nil)
  279. }
  280. } else if self.type == .DPI {
  281. switch self.file?.dpi {
  282. case 50:
  283. self.DPIComboBox!.selectItem(at: 0)
  284. case 72:
  285. self.DPIComboBox!.selectItem(at: 1)
  286. case 96:
  287. self.DPIComboBox!.selectItem(at: 2)
  288. case 150:
  289. self.DPIComboBox!.selectItem(at: 3)
  290. case 300:
  291. self.DPIComboBox!.selectItem(at: 4)
  292. case 600:
  293. self.DPIComboBox!.selectItem(at: 5)
  294. default:
  295. break
  296. }
  297. self.DPIComboBox!.isEnabled = self.file?.status != .processing
  298. } else if self.type == .Dimensions {
  299. }
  300. }
  301. func updateInterface( file: KMBatchOperateFile, progress: Float) {
  302. updateInterface(file)
  303. if progress > 0 && file.status == .processing {
  304. self.progressIndicator!.doubleValue = Double(progress)
  305. self.progressIndicator!.startAnimation(nil)
  306. self.progressIndicator!.isIndeterminate = false
  307. }
  308. }
  309. func updateInterface(isProgress progress: Int) {
  310. if progress == -1 {
  311. self.indicateImageView!.isHidden = false
  312. self.progressIndicator!.isHidden = true
  313. self.indicateImageView!.image = NSImage(named: "KMImageNameUXIconProgressFailure")
  314. } else if progress == 0 {
  315. self.indicateImageView!.isHidden = false
  316. self.progressIndicator!.isHidden = true
  317. self.indicateImageView!.image = NSImage(named: KMImageNameUXIconProgressWaiting)
  318. } else {
  319. self.indicateImageView!.isHidden = false
  320. self.progressIndicator!.isHidden = true
  321. self.indicateImageView!.image = NSImage(named: "KMImageNameUXIconProgressComplete")
  322. }
  323. }
  324. func controlTextDidEndEditing(_ obj: Notification) {
  325. if let object = self.pageRangeCombobox?.isEqual(obj.object) {
  326. self.file!.currentOperateInfo!.pageRangeString = self.pageRangeCombobox!.stringValue
  327. if self.file!.currentOperateInfo?.pagesArray == nil {
  328. let alert = NSAlert()
  329. alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
  330. alert.alertStyle = .critical
  331. alert.messageText = self.file!.filePath.lastPathComponent.lastPathComponent + NSLocalizedString("Invalid page range or the page number is out of range. Please try again.", comment: "")
  332. alert.beginSheetModal(for: self.window!, completionHandler: nil)
  333. self.pageRangeCombobox?.selectItem(at: 0)
  334. self.file?.currentOperateInfo!.pageChoice = .All
  335. }
  336. }
  337. }
  338. func controlTextDidChange(_ obj: Notification) {
  339. let comboBox: NSComboBox = obj.object as! NSComboBox;
  340. if comboBox.isEqual(self.pageRangeCombobox) {
  341. self.comboBoxContent = comboBox.stringValue;
  342. }
  343. }
  344. func comboBoxSelectionDidChange(_ notification: Notification) {
  345. if notification.object as? NSComboBox == self.pageRangeCombobox {
  346. self.pageRangeCombobox!.delegate = nil
  347. self.file?.currentOperateInfo!.pageChoice = self.tromsformPageChoice(index: self.pageRangeCombobox?.indexOfSelectedItem ?? 0)
  348. if self.pageRangeCombobox?.indexOfSelectedItem == 0 {
  349. self.pageRangeCombobox?.isEditable = true
  350. self.pageRangeCombobox?.isSelectable = true
  351. self.window?.makeFirstResponder(self.pageRangeCombobox)
  352. } else {
  353. self.pageRangeCombobox?.resignFirstResponder()
  354. self.pageRangeCombobox?.isEditable = false
  355. self.pageRangeCombobox?.isSelectable = false
  356. }
  357. self.pageRangeCombobox?.delegate = self
  358. } else if notification.object as? NSComboBox == self.DPIComboBox {
  359. switch self.DPIComboBox?.indexOfSelectedItem
  360. {
  361. case 0:
  362. self.file?.currentConvertParameter?.dpi = 50
  363. case 1:
  364. self.file?.currentConvertParameter?.dpi = 72
  365. case 2:
  366. self.file?.currentConvertParameter?.dpi = 96
  367. case 3:
  368. self.file?.currentConvertParameter?.dpi = 150
  369. case 4:
  370. self.file?.currentConvertParameter?.dpi = 300
  371. case 5:
  372. self.file?.currentConvertParameter?.dpi = 600
  373. default:
  374. break
  375. }
  376. }
  377. }
  378. func tromsformPageChoice(index: Int) -> KMBatchOperatePageChoice {
  379. var pageChoice: KMBatchOperatePageChoice?
  380. switch index {
  381. case 0:
  382. pageChoice = .All
  383. case 1:
  384. pageChoice = .Odd
  385. case 2:
  386. pageChoice = .Even
  387. case 3:
  388. pageChoice = .Input
  389. default:
  390. pageChoice = .All
  391. }
  392. return pageChoice ?? .All
  393. }
  394. }