KMBatchTableCellView.swift 20 KB

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