// // KMLineInspector.swift // PDF Reader Pro // // Created by tangchao on 2023/11/10. // import Cocoa enum KMLineChangeAction: Int { case no = 0 case lineWidth case style case dashPattern case startLineStyle case endLineStyle } class KMLineInspector: NSWindowController { private let LINEWIDTH_KEY = "style" private let STYLE_KEY = "lineWidth" private let DASHPATTERN_KEY = "dashPattern" private let STARTLINESTYLE_KEY = "startLineStyle" private let ENDLINESTYLE_KEY = "endLineStyle" private let ACTION_KEY = "action" private let SKLineInspectorFrameAutosaveName = "SKLineInspector" let SKLineInspectorLineAttributeDidChangeNotification = "SKLineInspectorLineAttributeDidChangeNotification" /* + (BOOL)sharedLineInspectorExists; - (void)setAnnotationStyle:(PDFAnnotation *)annotation; */ private var _currentLineChangeAction: KMLineChangeAction = .lineWidth var currentLineChangeAction: KMLineChangeAction { get { return self._currentLineChangeAction } } @IBOutlet var lineWidthSlider: NSSlider! @IBOutlet var lineWidthField: NSTextField! @IBOutlet var dashPatternField: NSTextField! @IBOutlet var styleButton: NSSegmentedControl! @IBOutlet var startLineStyleButton: NSSegmentedControl! @IBOutlet var endLineStyleButton: NSSegmentedControl! @IBOutlet var lineWidthLabelField: NSTextField! @IBOutlet var styleLabelField: NSTextField! @IBOutlet var dashPatternLabelField: NSTextField! @IBOutlet var startLineStyleLabelField: NSTextField! @IBOutlet var endLineStyleLabelField: NSTextField! @IBOutlet var lineWell: KMLineWell! @IBOutlet weak var labelField1: NSTextField! @IBOutlet weak var labelField2: NSTextField! @IBOutlet weak var labelField3: NSTextField! @IBOutlet weak var labelField4: NSTextField! @IBOutlet weak var labelField5: NSTextField! var labelFields: [NSTextField] = [] private var _lineWidth: CGFloat = 0 var lineWidth: CGFloat { get { return self._lineWidth } set { if (abs(self.lineWidth - newValue) > 0.00001) { self._lineWidth = newValue self._notifyChangeAction(.lineWidth) } } } private var _style: Int = CPDFBorderStyle.solid.rawValue var style: Int { get { return self._style } set { if (newValue != self._style) { self._style = newValue self._notifyChangeAction(.style) } } } private var _startLineStyle: Int = CPDFLineStyle.none.rawValue var startLineStyle: Int { get { return self._startLineStyle } set { if (newValue != self.startLineStyle) { self._startLineStyle = newValue self._notifyChangeAction(.startLineStyle) } } } var _endLineStyle: Int = CPDFLineStyle.none.rawValue var endLineStyle: Int { get { return self._endLineStyle } set { if (newValue != self.endLineStyle) { self._endLineStyle = newValue self._notifyChangeAction(.endLineStyle) } } } private var _dashPattern: [CGFloat] = [] var dashPattern: [CGFloat] { get { return self._dashPattern } set { if newValue != self._dashPattern && (newValue.isEmpty == false || self._dashPattern.isEmpty == false) { self._dashPattern = newValue self._notifyChangeAction(.dashPattern) } } } static let shared = KMLineInspector(windowNibName: "LineInspector") /* - (void)setAnnotationStyle:(PDFAnnotation *)annotation { NSString *type = [annotation type]; if ([type isEqualToString:SKNFreeTextString] || [type isEqualToString:SKNCircleString] || [type isEqualToString:SKNSquareString] || [type isEqualToString:SKNLineString] || [type isEqualToString:SKNInkString]) { [self setLineWidth:[annotation border] ? [[annotation border] lineWidth] : 0.0]; [self setDashPattern:[[annotation border] dashPattern]]; [self setStyle:[annotation border] ? [[annotation border] style] : 0]; } if ([type isEqualToString:SKNLineString]) { [self setStartLineStyle:[(PDFAnnotationLine *)annotation startLineStyle]]; [self setEndLineStyle:[(PDFAnnotationLine *)annotation endLineStyle]]; } } */ override func windowDidLoad() { super.windowDidLoad() self.labelFields = [self.labelField1, self.labelField2, self.labelField3, self.labelField4, self.labelField5] self.style = CPDFBorderStyle.solid.rawValue self.lineWidth = 1.0 self.dashPattern = [] self.startLineStyle = CPDFLineStyle.none.rawValue self.endLineStyle = CPDFLineStyle.none.rawValue self._currentLineChangeAction = .no self.lineWell.canActivate = false // self.lineWell.bind(NSBindingName(rawValue: KMLineWell.widthKey), to: self, withKeyPath: LINEWIDTH_KEY) // self.lineWell.bind(NSBindingName(KMLineWell.styleKey), to: self, withKeyPath: STYLE_KEY) // self.lineWell.bind(NSBindingName(KMLineWell.dashPatternKey), to: self, withKeyPath: DASHPATTERN_KEY) // self.lineWell.bind(NSBindingName(KMLineWell.startLineStyleKey), to: self, withKeyPath: STARTLINESTYLE_KEY) // self.lineWell.bind(NSBindingName(KMLineWell.endLineStyleKey), to: self, withKeyPath: ENDLINESTYLE_KEY) self.styleButton.setHelp(KMLocalizedString("Solid line style", "Tool tip message"), for: CPDFBorderStyle.solid.rawValue) self.styleButton.setHelp(KMLocalizedString("Dashed line style", "Tool tip message"), for: CPDFBorderStyle.dashed.rawValue) self.styleButton.setHelp(KMLocalizedString("Beveled line style", "Tool tip message"), for: CPDFBorderStyle.beveled.rawValue) self.styleButton.setHelp(KMLocalizedString("Inset line style", "Tool tip message"), for: CPDFBorderStyle.inset.rawValue) self.styleButton.setHelp(KMLocalizedString("Underline line style", "Tool tip message"), for: CPDFBorderStyle.underline.rawValue) self.startLineStyleButton.setHelp(KMLocalizedString("No start line style", "Tool tip message"), for: CPDFLineStyle.none.rawValue) self.startLineStyleButton.setHelp(KMLocalizedString("Square start line style", "Tool tip message"), for: CPDFLineStyle.square.rawValue) self.startLineStyleButton.setHelp(KMLocalizedString("Circle start line style", "Tool tip message"), for: CPDFLineStyle.circle.rawValue) self.startLineStyleButton.setHelp(KMLocalizedString("Diamond start line style", "Tool tip message"), for: CPDFLineStyle.diamond.rawValue) self.startLineStyleButton.setHelp(KMLocalizedString("Open arrow start line style", "Tool tip message"), for: CPDFLineStyle.openArrow.rawValue) self.startLineStyleButton.setHelp(KMLocalizedString("Closed arrow start line style", "Tool tip message"), for: CPDFLineStyle.closedArrow.rawValue) self.endLineStyleButton.setHelp(KMLocalizedString("No end line style", "Tool tip message"), for: CPDFLineStyle.none.rawValue) self.endLineStyleButton.setHelp(KMLocalizedString("Square end line style", "Tool tip message"), for: CPDFLineStyle.square.rawValue) self.endLineStyleButton.setHelp(KMLocalizedString("Circle end line style", "Tool tip message"), for: CPDFLineStyle.circle.rawValue) self.endLineStyleButton.setHelp(KMLocalizedString("Diamond end line style", "Tool tip message"), for: CPDFLineStyle.diamond.rawValue) self.endLineStyleButton.setHelp(KMLocalizedString("Open arrow end line style", "Tool tip message"), for: CPDFLineStyle.openArrow.rawValue) self.endLineStyleButton.setHelp(KMLocalizedString("Closed arrow end line style", "Tool tip message"), for: CPDFLineStyle.closedArrow.rawValue) let dw = KMAutoSizeLabelFields(labelFields, [lineWidthSlider, lineWidthField, styleButton, dashPatternField, startLineStyleButton, endLineStyleButton], false) if (fabs(dw) > 0.0) { KMResizeWindow(self.window!, dw) } self.windowFrameAutosaveName = SKLineInspectorFrameAutosaveName var image: NSImage? var size = NSMakeSize(29.0, 12.0) image = NSImage.image(with: size, drawingHandler: { rect in let path = NSBezierPath(rect: NSMakeRect(6.0, 3.0, 17.0, 6.0)) path.lineWidth = 2.0 NSColor.black.setStroke() path.stroke() return true }) self.styleButton.setImage(image, forSegment: CPDFBorderStyle.solid.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(6.0, 5.0)) path.line(to: NSMakePoint(6.0, 3.0)) path.line(to: NSMakePoint(9.0, 3.0)) path.move(to: NSMakePoint(12.0, 3.0)) path.line(to: NSMakePoint(17.0, 3.0)) path.move(to: NSMakePoint(20.0, 3.0)) path.line(to: NSMakePoint(23.0, 3.0)) path.line(to: NSMakePoint(23.0, 5.0)) path.move(to: NSMakePoint(23.0, 7.0)) path.line(to: NSMakePoint(23.0, 9.0)) path.line(to: NSMakePoint(20.0, 9.0)) path.move(to: NSMakePoint(17.0, 9.0)) path.line(to: NSMakePoint(12.0, 9.0)) path.move(to: NSMakePoint(9.0, 9.0)) path.line(to: NSMakePoint(6.0, 9.0)) path.line(to: NSMakePoint(6.0, 7.0)) path.lineWidth = 2.0 NSColor.black.setStroke() path.stroke() return true }) self.styleButton.setImage(image, forSegment: CPDFBorderStyle.dashed.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in var path = NSBezierPath(rect: NSMakeRect(6.0, 3.0, 17.0, 6.0)) path.lineWidth = 2.0 NSColor(calibratedWhite: 0, alpha: 0.25).setStroke() path.stroke() path = NSBezierPath() path.move(to: NSMakePoint(7.0, 3.0)) path.line(to: NSMakePoint(23.0, 3.0)) path.line(to: NSMakePoint(23.0, 8.0)) path.lineWidth = 2.0 NSColor(calibratedWhite: 0, alpha: 0.35).set() path.stroke() path = NSBezierPath() path.move(to: NSMakePoint(5.0, 2.0)) path.line(to: NSMakePoint(7.0, 4.0)) path.line(to: NSMakePoint(7.0, 2.0)) path.close() path.move(to: NSMakePoint(24.0, 10.0)) path.line(to: NSMakePoint(22.0, 8.0)) path.line(to: NSMakePoint(24.0, 8.0)) path.close() path.fill() return true }) self.styleButton.setImage(image, forSegment: CPDFBorderStyle.beveled.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in var path = NSBezierPath(rect: NSMakeRect(6.0, 3.0, 17.0, 6.0)) path.lineWidth = 2.0 NSColor(calibratedWhite: 0, alpha: 0.25).setStroke() path.stroke() path = NSBezierPath() path.move(to: NSMakePoint(6.0, 4.0)) path.line(to: NSMakePoint(6.0, 9.0)) path.line(to: NSMakePoint(22.0, 9.0)) path.lineWidth = 2.0 NSColor(calibratedWhite: 0, alpha: 0.35).set() path.stroke() path = NSBezierPath() path.move(to: NSMakePoint(5.0, 2.0)) path.line(to: NSMakePoint(7.0, 4.0)) path.line(to: NSMakePoint(5.0, 4.0)) path.close() path.move(to: NSMakePoint(24.0, 10.0)) path.line(to: NSMakePoint(22.0, 8.0)) path.line(to: NSMakePoint(22.0, 10.0)) path.close() path.fill() return true }) self.styleButton.setImage(image, forSegment: CPDFBorderStyle.inset.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(6.0, 3.0)) path.line(to: NSMakePoint(23.0, 3.0)) path.lineWidth = 2.0 NSColor(calibratedWhite: 0, alpha: 0.5).setStroke() path.stroke() return true }) self.styleButton.setImage(image, forSegment: CPDFBorderStyle.underline.rawValue) size = NSMakeSize(24.0, 12.0) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(20.0, 6.0)) path.line(to: NSMakePoint(8.0, 6.0)) path.lineWidth = 2.0 NSColor.black.setStroke() path.stroke() return true }) self.startLineStyleButton.setImage(image, forSegment: CPDFLineStyle.none.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(4.0, 6.0)) path.line(to: NSMakePoint(16.0, 6.0)) path.lineWidth = 2.0 NSColor.black.setStroke() path.stroke() return true }) self.endLineStyleButton.setImage(image, forSegment: CPDFLineStyle.none.rawValue) image = NSImage.image(with: size, drawingHandler: { rect in let path = NSBezierPath() path.move(to: NSMakePoint(20.0, 6.0)) path.line(to: NSMakePoint(8.0, 6.0)) path.appendRect(NSMakeRect(5.0, 3.0, 6.0, 6.0)) path.lineWidth = 2.0 NSColor.black.setStroke() path.stroke() return true }) self.startLineStyleButton.setImage(image, forSegment: CPDFLineStyle.square.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(4.0, 6.0)) path.line(to: NSMakePoint(16.0, 6.0)) path.appendRect(NSMakeRect(13.0, 3.0, 6.0, 6.0)) path.lineWidth = 2.0 NSColor.black.setStroke() path.stroke() return true }) self.endLineStyleButton.setImage(image, forSegment: CPDFLineStyle.square.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(20.0, 6.0)) path.line(to: NSMakePoint(8.0, 6.0)) path.appendOval(in: NSMakeRect(5.0, 3.0, 6.0, 6.0)) path.lineWidth = 2.0 NSColor.black.setStroke() path.stroke() return true }) self.startLineStyleButton.setImage(image, forSegment: CPDFLineStyle.circle.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(4.0, 6.0)) path.line(to: NSMakePoint(16.0, 6.0)) path.appendOval(in: NSMakeRect(13.0, 3.0, 6.0, 6.0)) path.lineWidth = 2.0 NSColor.black.setStroke() path.stroke() return true }) self.endLineStyleButton.setImage(image, forSegment: CPDFLineStyle.circle.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(20.0, 6.0)) path.line(to: NSMakePoint(8.0, 6.0)) path.move(to: NSMakePoint(12.0, 6.0)) path.line(to: NSMakePoint(8.0, 10.0)) path.line(to: NSMakePoint(4.0, 6.0)) path.line(to: NSMakePoint(8.0, 2.0)) path.close() path.lineWidth = 2.0 path.stroke() return true }) self.startLineStyleButton.setImage(image, forSegment: CPDFLineStyle.diamond.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(4.0, 6.0)) path.line(to: NSMakePoint(16.0, 6.0)) path.move(to: NSMakePoint(12.0, 6.0)) path.line(to: NSMakePoint(16.0, 10.0)) path.line(to: NSMakePoint(20.0, 6.0)) path.line(to: NSMakePoint(16.0, 2.0)) path.close() path.lineWidth = 2.0 path.stroke() return true }) self.endLineStyleButton.setImage(image, forSegment: CPDFLineStyle.diamond.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(20.0, 6.0)) path.line(to: NSMakePoint(8.0, 6.0)) path.move(to: NSMakePoint(14.0, 3.0)) path.line(to: NSMakePoint(8.0, 6.0)) path.line(to: NSMakePoint(14.0, 9.0)) path.lineWidth = 2.0 NSColor.black.setStroke() path.stroke() return true }) self.startLineStyleButton.setImage(image, forSegment: CPDFLineStyle.openArrow.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(4.0, 6.0)) path.line(to: NSMakePoint(16.0, 6.0)) path.move(to: NSMakePoint(10.0, 3.0)) path.line(to: NSMakePoint(16.0, 6.0)) path.line(to: NSMakePoint(10.0, 9.0)) path.lineWidth = 2 NSColor.black.setStroke() path.stroke() return true }) self.endLineStyleButton.setImage(image, forSegment: CPDFLineStyle.openArrow.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(20.0, 6.0)) path.line(to: NSMakePoint(8.0, 6.0)) path.move(to: NSMakePoint(14.0, 3.0)) path.line(to: NSMakePoint(8.0, 6.0)) path.line(to: NSMakePoint(14.0, 9.0)) path.close() path.lineWidth = 2 NSColor.black.setStroke() path.stroke() return true }) self.startLineStyleButton.setImage(image, forSegment: CPDFLineStyle.closedArrow.rawValue) image = NSImage.image(with: size, drawingHandler: { dstRect in let path = NSBezierPath() path.move(to: NSMakePoint(4.0, 6.0)) path.line(to: NSMakePoint(16.0, 6.0)) path.move(to: NSMakePoint(10.0, 3.0)) path.line(to: NSMakePoint(16.0, 6.0)) path.line(to: NSMakePoint(10.0, 9.0)) path.close() path.lineWidth = 2 NSColor.black.setStroke() path.stroke() return true }) self.endLineStyleButton.setImage(image, forSegment: CPDFLineStyle.closedArrow.rawValue) } /* - (void)setNilValueForKey:(NSString *)key { if ([key isEqualToString:LINEWIDTH_KEY]) { [self setValue:[NSNumber numberWithDouble:0.0] forKey:key]; } else if ([key isEqualToString:STYLE_KEY] || [key isEqualToString:STARTLINESTYLE_KEY] || [key isEqualToString:ENDLINESTYLE_KEY]) { [self setValue:[NSNumber numberWithInteger:0] forKey:key]; } else { [super setNilValueForKey:key]; } } */ override func value(forUndefinedKey key: String) -> Any? { KMPrint("forUndefinedKey: \(key)") } } // MARK: - Private Methods extension KMLineInspector { private func _notifyChangeAction(_ action: KMLineChangeAction) { self._currentLineChangeAction = action let selector = NSSelectorFromString("changeLineAttribute:") let mainWindow = NSApp.mainWindow var responder = mainWindow?.firstResponder while (responder != nil && responder!.responds(to: selector) == false) { responder = responder!.nextResponder } responder?.perform(selector, with: self) let userInfo = [ACTION_KEY : NSNumber(value: action.rawValue)] NotificationCenter.default.post(name: NSNotification.Name(rawValue: SKLineInspectorLineAttributeDidChangeNotification), object: self, userInfo: userInfo) self._currentLineChangeAction = .no } }