Przeglądaj źródła

【综合】 - 大纲列表,编辑大纲功能

lxy 2 lat temu
rodzic
commit
fdbd97a406
15 zmienionych plików z 1819 dodań i 12 usunięć
  1. 76 0
      PDF Office/PDF Office.xcodeproj/project.pbxproj
  2. 8 0
      PDF Office/PDF Office.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  3. 2 2
      PDF Office/PDF Office/Class/Home/ViewController/KMHomePopViewController.swift
  4. 99 0
      PDF Office/PDF Office/Class/PDFWindowController/Foundation Categories/NSString_SKExtensions.h
  5. 457 0
      PDF Office/PDF Office/Class/PDFWindowController/Foundation Categories/NSString_SKExtensions.m
  6. 1 0
      PDF Office/PDF Office/Class/PDFWindowController/MainWindowController/MainWindowController.swift
  7. 2 2
      PDF Office/PDF Office/Class/PDFWindowController/MainWindowController/MainWindowController.xib
  8. 2 1
      PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/KMLeftSideViewController.swift
  9. 156 0
      PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/KMOutlineEditViewController.swift
  10. 181 0
      PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/KMOutlineEditViewController.xib
  11. 624 2
      PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/KMOutlineViewController.swift
  12. 98 5
      PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/KMOutlineViewController.xib
  13. 66 0
      PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/View/KMCustomTableRowView.swift
  14. 24 0
      PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/View/KMOutlineCellView.swift
  15. 23 0
      PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/View/KMOutlineView.swift

+ 76 - 0
PDF Office/PDF Office.xcodeproj/project.pbxproj

@@ -58,6 +58,9 @@
 		8997012E28F42F15009AF911 /* Biochemistry.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8997012D28F42F15009AF911 /* Biochemistry.pdf */; };
 		8997012F28F42F15009AF911 /* Biochemistry.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8997012D28F42F15009AF911 /* Biochemistry.pdf */; };
 		8997013028F42F15009AF911 /* Biochemistry.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8997012D28F42F15009AF911 /* Biochemistry.pdf */; };
+		899E0B092919244500B13D34 /* KMCustomTableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 899E0B082919244500B13D34 /* KMCustomTableRowView.swift */; };
+		899E0B0A2919244500B13D34 /* KMCustomTableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 899E0B082919244500B13D34 /* KMCustomTableRowView.swift */; };
+		899E0B0B2919244500B13D34 /* KMCustomTableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 899E0B082919244500B13D34 /* KMCustomTableRowView.swift */; };
 		89D9895E28FD21E6003A3E87 /* KMAnnotationCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D9895728FD21E6003A3E87 /* KMAnnotationCollectionView.swift */; };
 		89D9895F28FD21E6003A3E87 /* KMAnnotationCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D9895728FD21E6003A3E87 /* KMAnnotationCollectionView.swift */; };
 		89D9896028FD21E6003A3E87 /* KMAnnotationCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D9895728FD21E6003A3E87 /* KMAnnotationCollectionView.swift */; };
@@ -70,6 +73,14 @@
 		89D9896F28FD50EF003A3E87 /* KMAnnotationCollectionViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 89D9896B28FD50EF003A3E87 /* KMAnnotationCollectionViewItem.xib */; };
 		89D9897028FD50EF003A3E87 /* KMAnnotationCollectionViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 89D9896B28FD50EF003A3E87 /* KMAnnotationCollectionViewItem.xib */; };
 		89D9897128FD50EF003A3E87 /* KMAnnotationCollectionViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = 89D9896B28FD50EF003A3E87 /* KMAnnotationCollectionViewItem.xib */; };
+		89D9897428FE743E003A3E87 /* KMOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D9897328FE743E003A3E87 /* KMOutlineView.swift */; };
+		89D9897528FE743E003A3E87 /* KMOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D9897328FE743E003A3E87 /* KMOutlineView.swift */; };
+		89D9897628FE743E003A3E87 /* KMOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D9897328FE743E003A3E87 /* KMOutlineView.swift */; };
+		89D9897828FE895A003A3E87 /* KMOutlineCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D9897728FE895A003A3E87 /* KMOutlineCellView.swift */; };
+		89D9897928FE895A003A3E87 /* KMOutlineCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D9897728FE895A003A3E87 /* KMOutlineCellView.swift */; };
+		89D9897A28FE895A003A3E87 /* KMOutlineCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D9897728FE895A003A3E87 /* KMOutlineCellView.swift */; };
+		89DB5DA9291B8DE70029624F /* KMOutlineEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89DB5DA7291B8DE70029624F /* KMOutlineEditViewController.swift */; };
+		89DB5DAA291B8DE70029624F /* KMOutlineEditViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 89DB5DA8291B8DE70029624F /* KMOutlineEditViewController.xib */; };
 		9F705F732918E02C005199AD /* ceshi.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 9F705F722918E02C005199AD /* ceshi.pdf */; };
 		9F78EFBB28F7C1CC001E66F4 /* KMHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F78EFB928F7C1CC001E66F4 /* KMHomeViewController.swift */; };
 		9F78EFBC28F7C1CC001E66F4 /* KMHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F78EFB928F7C1CC001E66F4 /* KMHomeViewController.swift */; };
@@ -361,10 +372,15 @@
 		8997011D28F41AB8009AF911 /* KMLeftSideViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMLeftSideViewController.swift; sourceTree = "<group>"; };
 		8997011E28F41AB8009AF911 /* KMLeftSideViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMLeftSideViewController.xib; sourceTree = "<group>"; };
 		8997012D28F42F15009AF911 /* Biochemistry.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = Biochemistry.pdf; sourceTree = "<group>"; };
+		899E0B082919244500B13D34 /* KMCustomTableRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMCustomTableRowView.swift; sourceTree = "<group>"; };
 		89D9895728FD21E6003A3E87 /* KMAnnotationCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KMAnnotationCollectionView.swift; sourceTree = "<group>"; };
 		89D9896228FD23AE003A3E87 /* PDFKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PDFKit.framework; path = System/Library/Frameworks/PDFKit.framework; sourceTree = SDKROOT; };
 		89D9896A28FD50EF003A3E87 /* KMAnnotationCollectionViewItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMAnnotationCollectionViewItem.swift; sourceTree = "<group>"; };
 		89D9896B28FD50EF003A3E87 /* KMAnnotationCollectionViewItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMAnnotationCollectionViewItem.xib; sourceTree = "<group>"; };
+		89D9897328FE743E003A3E87 /* KMOutlineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMOutlineView.swift; sourceTree = "<group>"; };
+		89D9897728FE895A003A3E87 /* KMOutlineCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMOutlineCellView.swift; sourceTree = "<group>"; };
+		89DB5DA7291B8DE70029624F /* KMOutlineEditViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMOutlineEditViewController.swift; sourceTree = "<group>"; };
+		89DB5DA8291B8DE70029624F /* KMOutlineEditViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMOutlineEditViewController.xib; sourceTree = "<group>"; };
 		9F705F722918E02C005199AD /* ceshi.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ceshi.pdf; sourceTree = "<group>"; };
 		9F78EFB928F7C1CC001E66F4 /* KMHomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KMHomeViewController.swift; sourceTree = "<group>"; };
 		9F78EFBA28F7C1CC001E66F4 /* KMHomeViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = KMHomeViewController.xib; sourceTree = "<group>"; };
@@ -637,6 +653,9 @@
 			children = (
 				8997010C28F40842009AF911 /* KMOutlineViewController.swift */,
 				8997010D28F40842009AF911 /* KMOutlineViewController.xib */,
+				89DB5DA7291B8DE70029624F /* KMOutlineEditViewController.swift */,
+				89DB5DA8291B8DE70029624F /* KMOutlineEditViewController.xib */,
+				89D9897228FE741A003A3E87 /* View */,
 			);
 			path = Outline;
 			sourceTree = "<group>";
@@ -650,6 +669,13 @@
 			path = Side;
 			sourceTree = "<group>";
 		};
+		899E0B012918FEAB00B13D34 /* Foundation Categories */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			path = "Foundation Categories";
+			sourceTree = "<group>";
+		};
 		89D9895428FD21E6003A3E87 /* View */ = {
 			isa = PBXGroup;
 			children = (
@@ -668,6 +694,16 @@
 			name = Frameworks;
 			sourceTree = "<group>";
 		};
+		89D9897228FE741A003A3E87 /* View */ = {
+			isa = PBXGroup;
+			children = (
+				89D9897328FE743E003A3E87 /* KMOutlineView.swift */,
+				89D9897728FE895A003A3E87 /* KMOutlineCellView.swift */,
+				899E0B082919244500B13D34 /* KMCustomTableRowView.swift */,
+			);
+			path = View;
+			sourceTree = "<group>";
+		};
 		9F78EFD228F81EF9001E66F4 /* View */ = {
 			isa = PBXGroup;
 			children = (
@@ -754,6 +790,7 @@
 				BB2C6AC628F4081E00478A33 /* PDFListView */,
 				BB2C6A7728F265AD00478A33 /* PDFSDK */,
 				8997011C28F41320009AF911 /* Side */,
+				899E0B012918FEAB00B13D34 /* Foundation Categories */,
 			);
 			path = PDFWindowController;
 			sourceTree = "<group>";
@@ -1220,6 +1257,7 @@
 					};
 					BBFBE6FB28DD7C21008B2335 = {
 						CreatedOnToolsVersion = 14.0;
+						LastSwiftMigration = 1400;
 						TestTargetID = BBFBE6EB28DD7C20008B2335;
 					};
 					BBFBE70528DD7C21008B2335 = {
@@ -1232,6 +1270,7 @@
 					};
 					BBFBE72A28DD7C43008B2335 = {
 						CreatedOnToolsVersion = 14.0;
+						LastSwiftMigration = 1400;
 						TestTargetID = BBFBE71A28DD7C43008B2335;
 					};
 					BBFBE73428DD7C43008B2335 = {
@@ -1292,6 +1331,7 @@
 				BB8B17362907B64D001C5EA5 /* CipherTextView.xib in Resources */,
 				8997010928F4082C009AF911 /* KMFromViewController.xib in Resources */,
 				8997010128F40710009AF911 /* KMBookMarkViewController.xib in Resources */,
+				89DB5DAA291B8DE70029624F /* KMOutlineEditViewController.xib in Resources */,
 				9FBA0F0129015A82001117AF /* KMFastToolCollectionViewItem.xib in Resources */,
 				BBFBE6C528DD7B98008B2335 /* Main.storyboard in Resources */,
 			);
@@ -1411,6 +1451,8 @@
 				BB86C21E28F561F8005AD968 /* SKRuntime.m in Sources */,
 				9FA607DE28FD4C9F00B46586 /* KMHomePopViewController.swift in Sources */,
 				BB4BD9CC2909026500A66A65 /* KMRightSideViewController.swift in Sources */,
+				89DB5DA9291B8DE70029624F /* KMOutlineEditViewController.swift in Sources */,
+				899E0B092919244500B13D34 /* KMCustomTableRowView.swift in Sources */,
 				899700F628F4051B009AF911 /* KMAnnotationViewController.swift in Sources */,
 				899700DF28F3A9F2009AF911 /* KMHomeWindowController.swift in Sources */,
 				BB8B17322907B63D001C5EA5 /* CipherTextView.swift in Sources */,
@@ -1418,9 +1460,11 @@
 				89D9896C28FD50EF003A3E87 /* KMAnnotationCollectionViewItem.swift in Sources */,
 				9FAAA332290BD0A20046FFCE /* KMHistoryFileCollectionViewItem.swift in Sources */,
 				8997010628F4082C009AF911 /* KMFromViewController.swift in Sources */,
+				89D9897428FE743E003A3E87 /* KMOutlineView.swift in Sources */,
 				9FA607D928F8227500B46586 /* KMBox.swift in Sources */,
 				BB86C25128F68594005AD968 /* CPDFTextAnnotation+Extensions.m in Sources */,
 				899700FE28F40710009AF911 /* KMBookMarkViewController.swift in Sources */,
+				89D9897828FE895A003A3E87 /* KMOutlineCellView.swift in Sources */,
 				BB86C1F228F54535005AD968 /* CPDFListView+KeyEvent.m in Sources */,
 				BB86C26B28F68603005AD968 /* CPDFPage+Extensions.m in Sources */,
 				BB246DA428F6B492005E8F52 /* NSBezierPath_SKExtensions.m in Sources */,
@@ -1498,9 +1542,11 @@
 				89D9896D28FD50EF003A3E87 /* KMAnnotationCollectionViewItem.swift in Sources */,
 				9FAAA333290BD0A20046FFCE /* KMHistoryFileCollectionViewItem.swift in Sources */,
 				8997010728F4082C009AF911 /* KMFromViewController.swift in Sources */,
+				89D9897528FE743E003A3E87 /* KMOutlineView.swift in Sources */,
 				9FA607DA28F8227500B46586 /* KMBox.swift in Sources */,
 				BB86C25228F68594005AD968 /* CPDFTextAnnotation+Extensions.m in Sources */,
 				BB2C6ACE28F41B9F00478A33 /* CPDFListView.m in Sources */,
+				89D9897928FE895A003A3E87 /* KMOutlineCellView.swift in Sources */,
 				BB86C1F328F54535005AD968 /* CPDFListView+KeyEvent.m in Sources */,
 				BB86C26C28F68603005AD968 /* CPDFPage+Extensions.m in Sources */,
 				BB246DA528F6B492005E8F52 /* NSBezierPath_SKExtensions.m in Sources */,
@@ -1548,6 +1594,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				BBFBE70128DD7C21008B2335 /* PDF_Office_ProTests.swift in Sources */,
+				899E0B0A2919244500B13D34 /* KMCustomTableRowView.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1578,9 +1625,11 @@
 				89D9896E28FD50EF003A3E87 /* KMAnnotationCollectionViewItem.swift in Sources */,
 				9FAAA334290BD0A20046FFCE /* KMHistoryFileCollectionViewItem.swift in Sources */,
 				8997010828F4082C009AF911 /* KMFromViewController.swift in Sources */,
+				89D9897628FE743E003A3E87 /* KMOutlineView.swift in Sources */,
 				9FA607DB28F8227500B46586 /* KMBox.swift in Sources */,
 				BB86C25328F68594005AD968 /* CPDFTextAnnotation+Extensions.m in Sources */,
 				BB2C6ACF28F41BA000478A33 /* CPDFListView.m in Sources */,
+				89D9897A28FE895A003A3E87 /* KMOutlineCellView.swift in Sources */,
 				BB86C1F428F54535005AD968 /* CPDFListView+KeyEvent.m in Sources */,
 				BB86C26D28F68603005AD968 /* CPDFPage+Extensions.m in Sources */,
 				BB246DA628F6B492005E8F52 /* NSBezierPath_SKExtensions.m in Sources */,
@@ -1628,6 +1677,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				BBFBE73028DD7C43008B2335 /* PDF_Office_DMGTests.swift in Sources */,
+				899E0B0B2919244500B13D34 /* KMCustomTableRowView.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -2061,15 +2111,22 @@
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 4GGQPGRTSV;
 				GENERATE_INFOPLIST_FILE = YES;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+					"@loader_path/../Frameworks",
+				);
 				MACOSX_DEPLOYMENT_TARGET = 12.3;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.kdanmobile.PDF-Office-ProTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_EMIT_LOC_STRINGS = NO;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PDF Office Pro.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PDF Office Pro";
 			};
@@ -2080,10 +2137,16 @@
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 4GGQPGRTSV;
 				GENERATE_INFOPLIST_FILE = YES;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+					"@loader_path/../Frameworks",
+				);
 				MACOSX_DEPLOYMENT_TARGET = 12.3;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.kdanmobile.PDF-Office-ProTests";
@@ -2216,15 +2279,22 @@
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 4GGQPGRTSV;
 				GENERATE_INFOPLIST_FILE = YES;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+					"@loader_path/../Frameworks",
+				);
 				MACOSX_DEPLOYMENT_TARGET = 12.3;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.kdanmobile.PDF-Office-DMGTests";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_EMIT_LOC_STRINGS = NO;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PDF Office DMG.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PDF Office DMG";
 			};
@@ -2235,10 +2305,16 @@
 			buildSettings = {
 				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
 				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ENABLE_MODULES = YES;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = 4GGQPGRTSV;
 				GENERATE_INFOPLIST_FILE = YES;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/../Frameworks",
+					"@loader_path/../Frameworks",
+				);
 				MACOSX_DEPLOYMENT_TARGET = 12.3;
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = "com.kdanmobile.PDF-Office-DMGTests";

+ 8 - 0
PDF Office/PDF Office.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>

+ 2 - 2
PDF Office/PDF Office/Class/Home/ViewController/KMHomePopViewController.swift

@@ -129,7 +129,7 @@ class KMHomePopViewController: NSViewController {
             return 0
         }
         let attributes = [NSAttributedString.Key.font : NSFont.systemFont(ofSize: 14.0)]
-        let rect : CGRect = content.boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: 18),options: .usesLineFragmentOrigin, attributes: attributes,context:nil)
+        let rect : CGRect = content.boundingRect(with: CGSize(width: 0, height: 18),options: .usesLineFragmentOrigin, attributes: attributes,context:nil)
         return Float(rect.size.width)
     }
     
@@ -139,7 +139,7 @@ class KMHomePopViewController: NSViewController {
             subView.removeFromSuperview()
         }
         
-        let dataMutableArr: NSMutableArray = NSMutableArray(array: (dataArr?.reverseObjectEnumerator().allObjects)!)
+        let dataMutableArr: NSMutableArray = dataArr?.reverseObjectEnumerator().allObjects as! NSMutableArray
         dataMutableArr[count] = content
         
         dataArr = dataMutableArr.reverseObjectEnumerator().allObjects as NSArray

+ 99 - 0
PDF Office/PDF Office/Class/PDFWindowController/Foundation Categories/NSString_SKExtensions.h

@@ -0,0 +1,99 @@
+//
+//  NSString_SKExtensions.h
+//  Skim
+//
+//  Created by Christiaan Hofman on 2/12/07.
+/*
+ This software is Copyright (c) 2007-2018
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface NSString (SKExtensions)
+
+- (NSComparisonResult)noteTypeCompare:(id)other;
+
+- (NSString *)stringByCollapsingWhitespaceAndNewlinesAndRemovingSurroundingWhitespaceAndNewlines;
+- (NSString *)stringByRemovingAliens;
+
+- (NSString *)stringByAppendingEllipsis;
+
+- (NSString *)stringByBackslashEscapingCharactersFromSet:(NSCharacterSet *)charSet;
+- (NSString *)stringByEscapingShellChars;
+- (NSString *)stringByEscapingParenthesis;
+
+- (NSComparisonResult)localizedCaseInsensitiveNumericCompare:(NSString *)aStr;
+
+- (BOOL)isCaseInsensitiveEqual:(NSString *)aString;
+
+- (NSString *)lossyStringUsingEncoding:(NSStringEncoding)encoding;
+
+- (NSString *)typeName;
+
+- (NSString *)rectString;
+- (NSString *)pointString;
+- (NSString *)originString;
+- (NSString *)sizeString;
+- (NSString *)midPointString;
+- (CGFloat)rectX;
+- (CGFloat)rectY;
+- (CGFloat)rectWidth;
+- (CGFloat)rectHeight;
+- (CGFloat)pointX;
+- (CGFloat)pointY;
+
+- (NSString *)stringBySurroundingWithSpacesIfNotEmpty;
+- (NSString *)stringByAppendingSpaceIfNotEmpty;
+- (NSString *)stringByAppendingDoubleSpaceIfNotEmpty;
+- (NSString *)stringByPrependingSpaceIfNotEmpty;
+- (NSString *)stringByAppendingCommaIfNotEmpty;
+- (NSString *)stringByAppendingFullStopIfNotEmpty;
+- (NSString *)stringByAppendingCommaAndSpaceIfNotEmpty;
+- (NSString *)stringByAppendingFullStopAndSpaceIfNotEmpty;
+- (NSString *)stringByPrependingCommaAndSpaceIfNotEmpty;
+- (NSString *)stringByPrependingFullStopAndSpaceIfNotEmpty;
+- (NSString *)parenthesizedStringIfNotEmpty;
+
+- (NSURL *)url;
+- (NSAttributedString *)icon;
+- (NSAttributedString *)smallIcon;
+
+- (NSAttributedString *)typeIcon;
+
+- (NSString *)xmlString;
+
+- (NSString *)getMaxStringWithBounds:(CGRect)bounds attributes:attributes;
+
++ (NSString *)stringFromDNwithSubjectName:(CFArrayRef)array;
+
+@end

+ 457 - 0
PDF Office/PDF Office/Class/PDFWindowController/Foundation Categories/NSString_SKExtensions.m

@@ -0,0 +1,457 @@
+//
+//  NSString_SKExtensions.m
+//  Skim
+//
+//  Created by Christiaan Hofman on 2/12/07.
+/*
+ This software is Copyright (c) 2007-2018
+ Christiaan Hofman. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ - Neither the name of Christiaan Hofman nor the names of any
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "NSString_SKExtensions.h"
+#import  "NSURL_SKExtensions.h"
+#import "NSImage_SKExtensions.h"
+#import "NSGeometry_SKExtensions.h"
+#import <CoreFoundation/CoreFoundation.h>
+
+#define ELLIPSIS_CHARACTER (unichar)0x2026
+
+#pragma mark CFString extensions
+
+#define STACK_BUFFER_SIZE 256
+
+static inline
+CFStringRef __SKStringCreateByCollapsingAndTrimmingWhitespaceAndNewlines(CFAllocatorRef allocator, CFStringRef aString)
+{
+    
+    CFIndex length = CFStringGetLength(aString);
+    
+    if(length == 0)
+        return CFRetain(CFSTR(""));
+    
+    // set up the buffer to fetch the characters
+    CFIndex cnt = 0;
+    CFStringInlineBuffer inlineBuffer;
+    CFStringInitInlineBuffer(aString, &inlineBuffer, CFRangeMake(0, length));
+    UniChar ch;
+    UniChar *buffer, stackBuffer[STACK_BUFFER_SIZE];
+    CFStringRef retStr;
+
+    allocator = (allocator == NULL) ? CFGetAllocator(aString) : allocator;
+
+    if(length >= STACK_BUFFER_SIZE) {
+        buffer = (UniChar *)CFAllocatorAllocate(allocator, length * sizeof(UniChar), 0);
+    } else {
+        bzero(stackBuffer, length * sizeof(UniChar));
+        buffer = stackBuffer;
+    }
+    
+    NSCAssert1(buffer != NULL, @"failed to allocate memory for string of length %ld", (long)length);
+    
+    CFCharacterSetRef wsnlCharSet = CFCharacterSetGetPredefined(kCFCharacterSetWhitespaceAndNewline);
+    CFCharacterSetRef nlCharSet = CFCharacterSetGetPredefined(kCFCharacterSetNewline);
+    BOOL isFirst = NO, wasHyphen = NO, found = NO;
+    CFIndex bufCnt = 0;
+    for(cnt = 0; cnt < length; cnt++){
+        ch = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, cnt);
+        if(NO == CFCharacterSetIsCharacterMember(wsnlCharSet, ch)){
+            wasHyphen = (ch == '-');
+            isFirst = YES;
+            buffer[bufCnt++] = ch; // not whitespace, so we want to keep it
+        } else {
+            if(isFirst){
+                if(wasHyphen && CFCharacterSetIsCharacterMember(nlCharSet, ch))
+                    bufCnt--; // ignore the last hyphen and current newline
+                else
+                    buffer[bufCnt++] = ' '; // if it's the first whitespace, we add a single space
+                wasHyphen = NO;
+                isFirst = NO;
+            }
+            found = YES;
+        }
+    }
+    
+    if (found){
+        if(buffer[(bufCnt-1)] == ' ') // we've collapsed any trailing whitespace, so disregard it
+            bufCnt--;
+        
+        retStr = CFStringCreateWithCharacters(allocator, buffer, bufCnt);
+    } else {
+        retStr = CFRetain(aString);
+    }
+    
+    if(buffer != stackBuffer) CFAllocatorDeallocate(allocator, buffer);
+
+    return retStr;
+}
+
+CFStringRef SKStringCreateByCollapsingAndTrimmingWhitespaceAndNewlines(CFAllocatorRef allocator, CFStringRef string){ return __SKStringCreateByCollapsingAndTrimmingWhitespaceAndNewlines(allocator, string); }
+
+#pragma mark NSString category
+
+@implementation NSString (SKExtensions)
+
+- (NSComparisonResult)noteTypeCompare:(id)other {
+    return [[self noteTypeOrder] compare:[other noteTypeOrder]];
+}
+
+- (NSString *)stringByCollapsingWhitespaceAndNewlinesAndRemovingSurroundingWhitespaceAndNewlines;
+{
+    return [(id)SKStringCreateByCollapsingAndTrimmingWhitespaceAndNewlines(CFAllocatorGetDefault(), (CFStringRef)self) autorelease];
+}
+
+// NS and CF character sets won't find these, due to the way CFString handles surrogate pairs.  The surrogate pair inlines were borrowed from CFCharacterSetPriv.h in CF-lite-476.13.
+static inline bool __SKIsSurrogateHighCharacter(const UniChar character) {
+    return ((character >= 0xD800UL) && (character <= 0xDBFFUL) ? true : false);
+}
+
+static inline bool __SKIsSurrogateLowCharacter(const UniChar character) {
+    return ((character >= 0xDC00UL) && (character <= 0xDFFFUL) ? true : false);
+}
+
+static inline bool __SKIsSurrogateCharacter(const UniChar character) {
+    return ((character >= 0xD800UL) && (character <= 0xDFFFUL) ? true : false);
+}
+
+static inline UTF32Char __SKGetLongCharacterForSurrogatePair(const UniChar surrogateHigh, const UniChar surrogateLow) {
+    return ((surrogateHigh - 0xD800UL) << 10) + (surrogateLow - 0xDC00UL) + 0x0010000UL;
+}
+
+static inline bool __SKIsPrivateUseCharacter(const UTF32Char ch)
+{
+    return ((ch >= 0xE000UL && ch <= 0xF8FFUL) ||    /* private use area */
+            (ch >= 0xF0000UL && ch <= 0xFFFFFUL) ||  /* supplementary private use A */
+            (ch >= 0x100000UL && ch <= 0x10FFFFUL)); /* supplementary private use B */
+}
+
+// Remove anything in the private use planes, and/or malformed surrogate pair sequences rdar://problem/6273932
+- (NSString *)stringByRemovingAliens {
+
+    // make a mutable copy only if needed
+    CFMutableStringRef theString = (void *)self;
+    
+    CFStringInlineBuffer inlineBuffer;
+    CFIndex length = CFStringGetLength(theString);
+    
+    // use the current mutable string with the inline buffer, but make a new mutable copy if needed
+    CFStringInitInlineBuffer(theString, &inlineBuffer, CFRangeMake(0, length));
+    UniChar ch;
+    
+#define DELETE_CHARACTERS(n) do{if((void*)self==theString){theString=(void*)[[self mutableCopyWithZone:[self zone]] autorelease];};CFStringDelete(theString, CFRangeMake(delIdx, n));} while(0)
+        
+    // idx is current index into the inline buffer, and delIdx is current index in the mutable string
+    CFIndex idx = 0, delIdx = 0;
+    while(idx < length){
+        ch = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, idx);
+        if (__SKIsPrivateUseCharacter(ch)) {
+            DELETE_CHARACTERS(1);
+        } else if (__SKIsSurrogateCharacter(ch)) {
+            
+            if ((idx + 1) < length) {
+                
+                UniChar highChar = ch;
+                UniChar lowChar = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, idx + 1);
+                // if we only have half of a surrogate pair, delete the offending character
+                if (__SKIsSurrogateLowCharacter(lowChar) == false || __SKIsSurrogateHighCharacter(highChar) == false) {
+                    DELETE_CHARACTERS(1);
+                    // only deleted a single char, so don't need to adjust idx
+                } else if (__SKIsPrivateUseCharacter(__SKGetLongCharacterForSurrogatePair(highChar, lowChar))) {
+                    // remove the pair; can't display private use characters
+                    DELETE_CHARACTERS(2);
+                    // adjust since we removed two characters...
+                    idx++;
+                } else {
+                    // valid surrogate pair, so we'll leave it alone
+                    delIdx += 2;
+                    idx++;
+                }
+                
+            } else {
+                // insufficient length for this to be a valid sequence, so it's only half of a surrogate pair
+                DELETE_CHARACTERS(1);
+            }
+            
+        } else {
+            // keep track of our index in the copy and the original
+            delIdx++;
+        }
+        idx++;
+    }
+
+    return (id)theString;
+}
+
+- (NSString *)stringByAppendingEllipsis;
+{
+    return [self stringByAppendingFormat:@"%C", ELLIPSIS_CHARACTER];
+}
+
+- (NSString *)stringByBackslashEscapingCharactersFromSet:(NSCharacterSet *)charSet {
+    NSUInteger location = [self rangeOfCharacterFromSet:charSet].location;
+    if (location == NSNotFound)
+        return self;
+    
+    NSRange range;
+    NSMutableString *string = [self mutableCopy];
+    
+    while (location != NSNotFound) {
+        [string insertString:@"\\" atIndex:location];
+        range = NSMakeRange(location + 2, [string length] - location - 2);
+        location = [string rangeOfCharacterFromSet:charSet options:0 range:range].location;
+    }
+    return [string autorelease];
+}
+
+// Escape those characters that are special, to the shell, inside a "quoted" string
+- (NSString *)stringByEscapingShellChars {
+    static NSCharacterSet *shellSpecialChars = nil;
+    if (shellSpecialChars == nil)
+        shellSpecialChars = [[NSCharacterSet characterSetWithCharactersInString:@"$\"`\\"] retain];
+    return [self stringByBackslashEscapingCharactersFromSet:shellSpecialChars];
+}
+
+- (NSString *)stringByEscapingParenthesis {
+    static NSCharacterSet *parenAndBackslashCharSet = nil;
+    if (parenAndBackslashCharSet == nil)
+        parenAndBackslashCharSet = [[NSCharacterSet characterSetWithCharactersInString:@"()\\"] retain];
+    return [self stringByBackslashEscapingCharactersFromSet:parenAndBackslashCharSet];
+}
+
+- (NSComparisonResult)localizedCaseInsensitiveNumericCompare:(NSString *)aStr{
+    return [self compare:aStr
+                 options:NSCaseInsensitiveSearch | NSNumericSearch
+                   range:NSMakeRange(0, [self length])
+                  locale:[NSLocale currentLocale]];
+}
+
+- (BOOL)isCaseInsensitiveEqual:(NSString *)aString {
+    return [self caseInsensitiveCompare:aString] == NSOrderedSame;
+}
+
+- (NSString *)lossyStringUsingEncoding:(NSStringEncoding)encoding {
+    return [[[NSString alloc] initWithData:[self dataUsingEncoding:encoding allowLossyConversion:YES] encoding:encoding] autorelease];
+}
+
+- (NSString *)typeName {
+    if ([self isEqualToString:@"FreeText"])
+        return NSLocalizedString(@"Text Box", @"Description for export");
+    else if ([self isEqualToString:@"Note"])
+        return NSLocalizedString(@"Anchored Note", @"Description for export");
+    else if ([self isEqualToString:@"Circle"])
+        return NSLocalizedString(@"Circle", @"Description for export");
+    else if ([self isEqualToString:@"Square"])
+        return NSLocalizedString(@"Rectangle", @"Description for export");
+    else if ([self isEqualToString:@"Highlight"])
+        return NSLocalizedString(@"Highlight", @"Description for export");
+    else if ([self isEqualToString:@"Underline"])
+        return NSLocalizedString(@"Underline", @"Description for export");
+    else if ([self isEqualToString:@"Strikeout"])
+        return NSLocalizedString(@"Strikethrough", @"Description for export");
+    else if ([self isEqualToString:@"Arrow"])
+        return NSLocalizedString(@"Arrow", @"Description for export");
+    else if ([self isEqualToString:@"Line"])
+        return NSLocalizedString(@"Line", @"Description for export");
+    else if ([self isEqualToString:@"Freehand"])
+        return NSLocalizedString(@"Freehand", @"Description for export");
+    else if([self isEqualToString:@"Stamp"])
+        return NSLocalizedString(@"Stamp", @"Description for export");
+    return NSLocalizedString(self, nil);
+}
+
+#pragma mark Templating support
+
+- (NSString *)rectString {
+    return NSStringFromRect(NSRectFromString(self));
+}
+
+- (NSString *)pointString {
+    return NSStringFromPoint(NSPointFromString(self));
+}
+
+- (NSString *)originString {
+    return NSStringFromPoint(NSRectFromString(self).origin);
+}
+
+- (NSString *)sizeString {
+    return NSStringFromSize(NSRectFromString(self).size);
+}
+
+- (NSString *)midPointString {
+    NSRect rect = NSRectFromString(self);
+    return NSStringFromPoint(NSMakePoint(NSMidX(rect), NSMidY(rect)));
+}
+
+- (CGFloat)rectX {
+    return NSRectFromString(self).origin.x;
+}
+
+- (CGFloat)rectY {
+    return NSRectFromString(self).origin.y;
+}
+
+- (CGFloat)rectWidth {
+    return NSRectFromString(self).size.width;
+}
+
+- (CGFloat)rectHeight {
+    return NSRectFromString(self).size.height;
+}
+
+- (CGFloat)pointX {
+    return NSPointFromString(self).x;
+}
+
+- (CGFloat)pointY {
+    return NSPointFromString(self).y;
+}
+
+- (NSString *)stringBySurroundingWithSpacesIfNotEmpty { 
+    return [self isEqualToString:@""] ? self : [NSString stringWithFormat:@" %@ ", self];
+}
+
+- (NSString *)stringByAppendingSpaceIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [self stringByAppendingString:@" "];
+}
+
+- (NSString *)stringByAppendingDoubleSpaceIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [self stringByAppendingString:@"  "];
+}
+
+- (NSString *)stringByPrependingSpaceIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [NSString stringWithFormat:@" %@", self];
+}
+
+- (NSString *)stringByAppendingCommaIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [self stringByAppendingString:@","];
+}
+
+- (NSString *)stringByAppendingFullStopIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [self stringByAppendingString:@"."];
+}
+
+- (NSString *)stringByAppendingCommaAndSpaceIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [self stringByAppendingString:@", "];
+}
+
+- (NSString *)stringByAppendingFullStopAndSpaceIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [self stringByAppendingString:@". "];
+}
+
+- (NSString *)stringByPrependingCommaAndSpaceIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [NSString stringWithFormat:@", %@", self];
+}
+
+- (NSString *)stringByPrependingFullStopAndSpaceIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [NSString stringWithFormat:@". %@", self];
+}
+
+- (NSString *)parenthesizedStringIfNotEmpty {
+    return [self isEqualToString:@""] ? self : [NSString stringWithFormat:@"(%@)", self];
+}
+
+- (NSURL *)url {
+    NSURL *url = nil;
+    if ([self rangeOfString:@"://"].location != NSNotFound)
+        url = [NSURL URLWithString:self];
+    else
+        url = [NSURL fileURLWithPath:[self stringByExpandingTildeInPath]];
+    return url;
+}
+
+- (NSAttributedString *)icon {
+    return [[self url] icon];
+}
+
+- (NSAttributedString *)smallIcon {
+    return [[self url] smallIcon];
+}
+
+- (NSString *)xmlString {
+    NSData *data = [NSPropertyListSerialization dataWithPropertyList:self format:NSPropertyListXMLFormat_v1_0 options:0 error:NULL];
+    NSMutableString *string = [[[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
+    NSInteger loc = NSMaxRange([string rangeOfString:@"<string>"]);
+    if (loc == NSNotFound)
+        return self;
+    [string deleteCharactersInRange:NSMakeRange(0, loc)];
+    loc = [string rangeOfString:@"</string>" options:NSBackwardsSearch].location;
+    if (loc == NSNotFound)
+        return self;
+    [string deleteCharactersInRange:NSMakeRange(loc, [string length] - loc)];
+    return string;
+}
+
+- (NSString *)getMaxStringWithBounds:(CGRect)bounds attributes:attributes
+{
+    if (self.length < 1) {
+        return @"";
+    }
+    NSString *tString = self;
+    CGRect rect = [tString boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
+    if (rect.size.width > bounds.size.width) {
+        do {
+            if(tString.length < 1) {
+                tString = @"";
+            } {
+                tString = [tString substringToIndex:(tString.length-1)];
+            }
+            rect = [tString boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT) options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
+        } while (rect.size.width > bounds.size.width);
+    }
+
+    return tString;
+}
+
++ (NSString *)stringFromDNwithSubjectName:(CFArrayRef)array
+{
+    NSMutableString * out = [[NSMutableString alloc] init];
+    const void *keys[] = { kSecOIDCommonName, kSecOIDOrganizationName,kSecOIDOrganizationalUnitName,kSecOIDEmailAddress, kSecOIDLocalityName, kSecOIDStateProvinceName, kSecOIDCountryName };
+    const void *labels[] = { "cn", "o", "ou", "email","l", "s", "c" };
+
+    for(int i = 0; i < sizeof(keys)/sizeof(keys[0]);  i++) {
+        for (CFIndex n = 0 ; n < CFArrayGetCount(array); n++) {
+            CFDictionaryRef dict = CFArrayGetValueAtIndex(array, n);
+            if (CFGetTypeID(dict) != CFDictionaryGetTypeID())
+                continue;
+            CFTypeRef dictkey = CFDictionaryGetValue(dict, kSecPropertyKeyLabel);
+            if (!CFEqual(dictkey, keys[i]))
+                continue;
+            CFStringRef str = (CFStringRef) CFDictionaryGetValue(dict, kSecPropertyKeyValue);
+            if (out.length< 1) {
+                [out appendFormat:@"%s=%@", labels[i], (__bridge NSString*)str];
+            } else {
+                [out appendFormat:@",%s=%@", labels[i], (__bridge NSString*)str];
+            }
+        }
+    }
+    return [NSString stringWithString:out];
+}
+
+@end

+ 1 - 0
PDF Office/PDF Office/Class/PDFWindowController/MainWindowController/MainWindowController.swift

@@ -46,6 +46,7 @@ class MainWindowController: NSWindowController,CPDFViewDelegate,CPDFListViewDele
         self.listView.pdfListViewDelegate = self
         self.PDFContendView.addSubview(self.listView)
         
+        leftSideViewController.mainWindowController = self
         leftSideViewController.view.frame = CGRect(x: 0, y: 0, width: 300, height: 300);
         leftSideViewController.view.autoresizingMask = [.height]
         self.window?.contentView?.addSubview(leftSideViewController.view)

+ 2 - 2
PDF Office/PDF Office/Class/PDFWindowController/MainWindowController/MainWindowController.xib

@@ -18,7 +18,7 @@
             <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
             <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
             <rect key="contentRect" x="196" y="240" width="480" height="270"/>
-            <rect key="screenRect" x="0.0" y="0.0" width="1512" height="944"/>
+            <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1055"/>
             <view key="contentView" id="se5-gp-TjO">
                 <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
                 <autoresizingMask key="autoresizingMask"/>
@@ -32,7 +32,7 @@
             <connections>
                 <outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
             </connections>
-            <point key="canvasLocation" x="-97" y="-74"/>
+            <point key="canvasLocation" x="132" y="-138"/>
         </window>
     </objects>
 </document>

+ 2 - 1
PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/KMLeftSideViewController.swift

@@ -16,7 +16,7 @@ enum BotaType : Int {
 }
 
 class KMLeftSideViewController: NSViewController {
-
+    var mainWindowController : MainWindowController!
     @IBOutlet weak var contentBox: NSBox!
     lazy var thumbnailViewController : KMThumbnailViewController = {
         let thumbnailViewController = KMThumbnailViewController()
@@ -47,6 +47,7 @@ class KMLeftSideViewController: NSViewController {
     
     override func viewDidLoad() {
         super.viewDidLoad()
+        self.outlineViewController.mainWindowController = self.mainWindowController
         self.updateViewButtonState(type: self.type, isDisPlay: false)
     }
     

+ 156 - 0
PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/KMOutlineEditViewController.swift

@@ -0,0 +1,156 @@
+//
+//  KMOutlineEditViewController.swift
+//  PDF Office
+//
+//  Created by lxy on 2022/11/9.
+//
+
+import Cocoa
+
+class KMOutlineEditViewController: NSViewController {
+    
+    @IBOutlet var outlineNameTextView: NSTextView!
+    @IBOutlet weak var outlineNameLabel: NSTextField!
+    @IBOutlet weak var outlineTargetPageIndexTextField: NSTextField!
+    @IBOutlet weak var pageButton: NSButton!
+    @IBOutlet weak var totalPageCountLabel: NSTextField!
+    @IBOutlet weak var outlineURLTextField: NSTextField!
+    @IBOutlet weak var urlButton: NSButton!
+    @IBOutlet weak var mailAddressTextField: NSTextField!
+    @IBOutlet weak var mailButton: NSButton!
+    var outline : CPDFOutline!
+    var pdfView : CPDFListView!
+    var originalURLString : String!
+    var originalDestination : CPDFDestination!
+    var originalLabel : String!
+    var currentPageIndex : Int!
+    convenience init(outline:CPDFOutline!,document:CPDFListView!) {
+        self.init()
+        self.outline = outline
+        self.pdfView = document
+    }
+    
+//MARK: Life cycle
+    override func loadView() {
+        super.loadView()
+        self.localizedLanguage()
+    }
+    
+    //MARK: Private Methods
+    private func localizedLanguage () {
+        self.outlineNameLabel.stringValue = "\(NSLocalizedString("Label", comment: ""))"
+        self.pageButton.title = "\(NSLocalizedString("Page", comment: ""))"
+        self.urlButton.title = "\(NSLocalizedString("URL:", comment: ""))"
+        self.outlineURLTextField.placeholderString = "\(NSLocalizedString("https://www.pdfreaderpro.com", comment: ""))"
+        self.mailButton.title = "Email:"
+        self.mailAddressTextField.placeholderString = "\(NSLocalizedString("support@pdfreaderpro.com", comment: ""))"
+        self.outlineNameTextView.string = self.outline.label 
+    }
+    
+    private func adjustUIWithAction(action:CPDFAction!) {
+        if action == nil {
+            self.totalPageCountLabel.stringValue = " \(self.pdfView.document.pageCount)"
+            self.outlineTargetPageIndexTextField.stringValue = " \(self.currentPageIndex + 1)"
+        } else {
+            if action.isKind(of: CPDFURLAction.self) {
+                var newAction : CPDFURLAction = self.outline.action as! CPDFURLAction
+                self.originalURLString = newAction.url()
+                var urlString : String = newAction.url()
+                if urlString.hasPrefix("mailto:") {
+                    self.setONButton(button: self.mailButton)
+                    self.enableTextField(textField: self.mailAddressTextField)
+                    urlString = String(urlString.suffix(from: "mailto:".endIndex))
+                    self.mailAddressTextField.stringValue = urlString
+                } else {
+                    self.setONButton(button: self.urlButton)
+                    self.enableTextField(textField: self.outlineURLTextField)
+                    self.outlineURLTextField.stringValue = urlString
+                }
+                self.currentPageIndex = Int(self.pdfView.document.index(for: self.pdfView.document.page(at: UInt(self.pdfView.currentPageIndex))))
+            } else {
+                self.setONButton(button: self.pageButton)
+                self.enableTextField(textField: self.outlineTargetPageIndexTextField)
+                self.currentPageIndex = Int(self.pdfView.document.index(for: self.pdfView.document.page(at: UInt(self.outline.destination.pageIndex))))
+                self.originalDestination = self.outline.destination
+            }
+            self.originalLabel = self.outline.label
+            self.totalPageCountLabel.stringValue = " /\(self.pdfView.document.pageCount)"
+            self.outlineTargetPageIndexTextField.stringValue = "\(self.currentPageIndex + 1)"
+        }
+    }
+    
+    private func setDelegateAndFormatter () {
+        self.outlineTargetPageIndexTextField.delegate = self
+        self.outlineNameTextView.delegate = self
+        
+        var formatter = NumberFormatter()
+        formatter.numberStyle = .none
+        formatter.maximum = NSNumber(value: self.pdfView.document.pageCount)
+        formatter.minimum = NSNumber(value: 1)
+        self.outlineTargetPageIndexTextField.formatter = formatter
+    }
+    
+    private func setONButton(button:NSButton) {
+        self.pageButton.state = NSControl.StateValue.off
+        self.urlButton.state = NSControl.StateValue.off
+        self.mailButton.state = NSControl.StateValue.off
+        button.state = NSControl.StateValue.on
+    }
+    
+    private func enableTextField(textField:NSTextField) {
+        self.outlineTargetPageIndexTextField.isEditable = false
+        self.outlineTargetPageIndexTextField.isSelectable = false
+        self.outlineURLTextField.isEditable = false
+        self.outlineURLTextField.isSelectable = false
+        self.mailAddressTextField.isEditable = false
+        self.mailAddressTextField.isSelectable = false
+        
+        self.outlineTargetPageIndexTextField.textColor = NSColor.red
+        self.outlineURLTextField.textColor = NSColor.red
+        self.mailAddressTextField.textColor = NSColor.red
+        
+        textField.isEditable = false
+        textField.isSelectable = false
+        textField.textColor = NSColor.black
+    }
+    
+    //MARK: Button Action
+    @IBAction func buttonClicked_GotoAction(_ sender: Any) {
+        self.setONButton(button: self.pageButton)
+        self.enableTextField(textField: self.outlineTargetPageIndexTextField)
+        self.view.window?.makeFirstResponder(self.outlineTargetPageIndexTextField)
+    }
+    
+    @IBAction func buttonClicked_URLAction(_ sender: Any) {
+        self.setONButton(button: self.urlButton)
+        self.enableTextField(textField: self.outlineURLTextField)
+        self.view.window?.makeFirstResponder(self.outlineURLTextField)
+        if (self.outlineTargetPageIndexTextField.stringValue == "0" || self.outlineTargetPageIndexTextField.stringValue.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) < 1){
+            self.outlineTargetPageIndexTextField.stringValue = "\(self.currentPageIndex + 1)"
+        }
+    }
+    
+    @IBAction func buttonClicked_MailAction(_ sender: Any) {
+        self.setONButton(button: self.mailButton)
+        self.enableTextField(textField: self.mailAddressTextField)
+        self.view.window?.makeFirstResponder(self.mailAddressTextField)
+        if (self.outlineTargetPageIndexTextField.stringValue == "0" || self.outlineTargetPageIndexTextField.stringValue.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) < 1){
+            self.outlineTargetPageIndexTextField.stringValue = "\(self.currentPageIndex + 1)"
+        }
+    }
+    
+}
+
+extension KMOutlineEditViewController : NSTextFieldDelegate,NSTextViewDelegate {
+    func controlTextDidChange(_ obj: Notification) {
+        if self.outlineTargetPageIndexTextField.isEqual(obj.object) {
+            let number = NSNumber(value: Int(self.outlineTargetPageIndexTextField.stringValue) ?? 0)
+            let string = self.outlineTargetPageIndexTextField.formatter?.string(for: number)
+            let s = Int(string ?? "")
+            if s != 0 {
+                self.currentPageIndex = (s ?? 0) - 1
+            }
+            self.outlineTargetPageIndexTextField.stringValue = string ?? ""
+        }
+    }
+}

+ 181 - 0
PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/KMOutlineEditViewController.xib

@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21225" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+    <dependencies>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21225"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <customObject id="-2" userLabel="File's Owner" customClass="KMOutlineEditViewController" customModule="PDF_Office" customModuleProvider="target">
+            <connections>
+                <outlet property="mailAddressTextField" destination="LfL-VK-ame" id="iwx-Zv-ExL"/>
+                <outlet property="mailButton" destination="gIo-HB-6zF" id="Kcm-XV-ved"/>
+                <outlet property="outlineNameLabel" destination="aG0-o0-MhR" id="ws9-bx-1xP"/>
+                <outlet property="outlineNameTextView" destination="huV-tG-y3p" id="zfe-Ed-suS"/>
+                <outlet property="outlineTargetPageIndexTextField" destination="i3a-28-DyO" id="zdu-c9-6Lf"/>
+                <outlet property="outlineURLTextField" destination="reA-N7-NIS" id="8hW-Gy-NVw"/>
+                <outlet property="pageButton" destination="dRu-ot-ego" id="1yF-sh-3X9"/>
+                <outlet property="totalPageCountLabel" destination="Pdh-kp-hBW" id="tvn-Ci-OHC"/>
+                <outlet property="urlButton" destination="Bmn-8m-uEP" id="MdM-hm-Y0Z"/>
+                <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
+            </connections>
+        </customObject>
+        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+        <customView id="Hz6-mo-xeY">
+            <rect key="frame" x="0.0" y="0.0" width="382" height="212"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+            <subviews>
+                <customView translatesAutoresizingMaskIntoConstraints="NO" id="2HR-Xb-3d3">
+                    <rect key="frame" x="0.0" y="0.0" width="382" height="212"/>
+                    <subviews>
+                        <scrollView horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="H1Q-WT-A3S">
+                            <rect key="frame" x="58" y="132" width="304" height="60"/>
+                            <clipView key="contentView" drawsBackground="NO" id="8dZ-VA-Txo">
+                                <rect key="frame" x="1" y="1" width="287" height="58"/>
+                                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                <subviews>
+                                    <textView importsGraphics="NO" verticallyResizable="YES" usesFontPanel="YES" findStyle="panel" continuousSpellChecking="YES" allowsUndo="YES" usesRuler="YES" quoteSubstitution="YES" dashSubstitution="YES" spellingCorrection="YES" smartInsertDelete="YES" id="huV-tG-y3p">
+                                        <rect key="frame" x="0.0" y="0.0" width="287" height="58"/>
+                                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                        <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
+                                        <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                        <size key="minSize" width="287" height="58"/>
+                                        <size key="maxSize" width="463" height="10000000"/>
+                                        <color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
+                                    </textView>
+                                </subviews>
+                            </clipView>
+                            <constraints>
+                                <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="60" id="976-2O-BUc"/>
+                                <constraint firstAttribute="height" constant="60" id="rJh-Fm-zId"/>
+                            </constraints>
+                            <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="YES" id="0Ne-K5-kTs">
+                                <rect key="frame" x="-100" y="-100" width="87" height="18"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                            </scroller>
+                            <scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="bgc-Yv-u3m">
+                                <rect key="frame" x="288" y="1" width="15" height="58"/>
+                                <autoresizingMask key="autoresizingMask"/>
+                            </scroller>
+                        </scrollView>
+                        <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aG0-o0-MhR">
+                            <rect key="frame" x="18" y="176" width="37" height="16"/>
+                            <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="AIL-g3-iqW">
+                                <font key="font" metaFont="system"/>
+                                <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                            </textFieldCell>
+                        </textField>
+                        <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="dRu-ot-ego">
+                            <rect key="frame" x="18" y="98" width="67" height="18"/>
+                            <buttonCell key="cell" type="radio" title="Pages:" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="hLI-d7-lCT">
+                                <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+                                <font key="font" metaFont="system"/>
+                            </buttonCell>
+                            <connections>
+                                <action selector="buttonClicked_GotoAction:" target="-2" id="lB6-DP-Q1M"/>
+                            </connections>
+                        </button>
+                        <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Bmn-8m-uEP">
+                            <rect key="frame" x="18" y="61" width="67" height="18"/>
+                            <buttonCell key="cell" type="radio" title="URL:" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="avP-xE-aUH">
+                                <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+                                <font key="font" metaFont="system"/>
+                            </buttonCell>
+                            <connections>
+                                <action selector="buttonClicked_URLAction:" target="-2" id="eSo-4x-q5G"/>
+                            </connections>
+                        </button>
+                        <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Pdh-kp-hBW">
+                            <rect key="frame" x="128" y="99" width="37" height="16"/>
+                            <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="8cd-8S-8Lj">
+                                <font key="font" metaFont="system"/>
+                                <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
+                            </textFieldCell>
+                        </textField>
+                        <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i3a-28-DyO">
+                            <rect key="frame" x="90" y="96" width="40" height="21"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="40" id="f93-yT-7Zh"/>
+                            </constraints>
+                            <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" alignment="center" drawsBackground="YES" id="0eM-5y-hEF">
+                                <font key="font" metaFont="system"/>
+                                <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                            </textFieldCell>
+                        </textField>
+                        <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gIo-HB-6zF">
+                            <rect key="frame" x="18" y="24" width="67" height="18"/>
+                            <buttonCell key="cell" type="radio" title="Email:" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="02u-XG-ocT">
+                                <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
+                                <font key="font" metaFont="system"/>
+                            </buttonCell>
+                            <connections>
+                                <action selector="buttonClicked_MailAction:" target="-2" id="5bI-0i-9ON"/>
+                            </connections>
+                        </button>
+                        <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="LfL-VK-ame">
+                            <rect key="frame" x="90" y="22" width="229" height="22"/>
+                            <constraints>
+                                <constraint firstAttribute="width" constant="229" id="SkY-9f-d1S"/>
+                            </constraints>
+                            <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="UiK-cT-XDc">
+                                <font key="font" metaFont="system"/>
+                                <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                            </textFieldCell>
+                        </textField>
+                        <textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="reA-N7-NIS">
+                            <rect key="frame" x="90" y="59" width="204" height="22"/>
+                            <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="cRg-1e-SZf">
+                                <font key="font" metaFont="system"/>
+                                <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                            </textFieldCell>
+                        </textField>
+                    </subviews>
+                    <constraints>
+                        <constraint firstItem="i3a-28-DyO" firstAttribute="centerY" secondItem="dRu-ot-ego" secondAttribute="centerY" id="2Vf-C3-CWZ"/>
+                        <constraint firstItem="Pdh-kp-hBW" firstAttribute="centerY" secondItem="dRu-ot-ego" secondAttribute="centerY" id="2YN-SS-lov"/>
+                        <constraint firstItem="Pdh-kp-hBW" firstAttribute="leading" secondItem="i3a-28-DyO" secondAttribute="trailing" id="7li-HH-yGA"/>
+                        <constraint firstAttribute="trailing" secondItem="reA-N7-NIS" secondAttribute="trailing" constant="88" id="8wp-D6-hVE"/>
+                        <constraint firstItem="reA-N7-NIS" firstAttribute="leading" secondItem="Bmn-8m-uEP" secondAttribute="trailing" constant="5" id="DrO-W1-Grz"/>
+                        <constraint firstItem="reA-N7-NIS" firstAttribute="baseline" secondItem="Bmn-8m-uEP" secondAttribute="firstBaseline" id="FXY-O5-Kea"/>
+                        <constraint firstItem="LfL-VK-ame" firstAttribute="top" secondItem="reA-N7-NIS" secondAttribute="bottom" constant="15" id="GnP-ST-d0J"/>
+                        <constraint firstItem="reA-N7-NIS" firstAttribute="centerY" secondItem="Bmn-8m-uEP" secondAttribute="centerY" id="GqL-eg-1vS"/>
+                        <constraint firstItem="i3a-28-DyO" firstAttribute="leading" secondItem="dRu-ot-ego" secondAttribute="trailing" constant="5" id="IRS-3q-Fcz"/>
+                        <constraint firstItem="aG0-o0-MhR" firstAttribute="top" secondItem="2HR-Xb-3d3" secondAttribute="top" constant="20" id="LZ2-8c-ZrF"/>
+                        <constraint firstItem="aG0-o0-MhR" firstAttribute="leading" secondItem="2HR-Xb-3d3" secondAttribute="leading" constant="20" id="NjP-2T-Tet"/>
+                        <constraint firstItem="gIo-HB-6zF" firstAttribute="leading" secondItem="Bmn-8m-uEP" secondAttribute="leading" id="VUd-4H-7jk"/>
+                        <constraint firstItem="Bmn-8m-uEP" firstAttribute="trailing" secondItem="dRu-ot-ego" secondAttribute="trailing" id="Xgo-lG-Uxy"/>
+                        <constraint firstItem="LfL-VK-ame" firstAttribute="baseline" secondItem="gIo-HB-6zF" secondAttribute="baseline" id="blz-kY-Ewx"/>
+                        <constraint firstItem="i3a-28-DyO" firstAttribute="top" secondItem="H1Q-WT-A3S" secondAttribute="bottom" constant="15" id="bwV-xf-VSM"/>
+                        <constraint firstItem="LfL-VK-ame" firstAttribute="baseline" secondItem="gIo-HB-6zF" secondAttribute="firstBaseline" id="fbT-xQ-IoX"/>
+                        <constraint firstItem="reA-N7-NIS" firstAttribute="top" secondItem="i3a-28-DyO" secondAttribute="bottom" constant="15" id="hGf-GD-LIr"/>
+                        <constraint firstItem="LfL-VK-ame" firstAttribute="centerY" secondItem="gIo-HB-6zF" secondAttribute="centerY" id="hZg-sQ-WOg"/>
+                        <constraint firstItem="reA-N7-NIS" firstAttribute="baseline" secondItem="Bmn-8m-uEP" secondAttribute="baseline" id="jxD-9G-9xQ"/>
+                        <constraint firstAttribute="trailing" secondItem="H1Q-WT-A3S" secondAttribute="trailing" constant="20" id="lhw-Ie-9l3"/>
+                        <constraint firstItem="LfL-VK-ame" firstAttribute="leading" secondItem="gIo-HB-6zF" secondAttribute="trailing" constant="5" id="oyg-fO-hcQ"/>
+                        <constraint firstItem="gIo-HB-6zF" firstAttribute="trailing" secondItem="dRu-ot-ego" secondAttribute="trailing" id="qV0-5i-8s4"/>
+                        <constraint firstItem="H1Q-WT-A3S" firstAttribute="leading" secondItem="aG0-o0-MhR" secondAttribute="trailing" constant="5" id="red-f4-MuW"/>
+                        <constraint firstItem="Bmn-8m-uEP" firstAttribute="leading" secondItem="aG0-o0-MhR" secondAttribute="leading" id="t4o-BH-M16"/>
+                        <constraint firstItem="reA-N7-NIS" firstAttribute="leading" secondItem="i3a-28-DyO" secondAttribute="leading" id="uMJ-IH-b9i"/>
+                        <constraint firstItem="H1Q-WT-A3S" firstAttribute="top" secondItem="aG0-o0-MhR" secondAttribute="top" id="w8i-x3-6uY"/>
+                        <constraint firstItem="LfL-VK-ame" firstAttribute="leading" secondItem="i3a-28-DyO" secondAttribute="leading" id="xEa-9R-dxa"/>
+                        <constraint firstItem="H1Q-WT-A3S" firstAttribute="leading" secondItem="aG0-o0-MhR" secondAttribute="trailing" constant="5" id="xrA-X3-fYr"/>
+                        <constraint firstItem="dRu-ot-ego" firstAttribute="leading" secondItem="aG0-o0-MhR" secondAttribute="leading" id="zkh-uz-Kd1"/>
+                    </constraints>
+                </customView>
+            </subviews>
+            <constraints>
+                <constraint firstAttribute="bottom" secondItem="2HR-Xb-3d3" secondAttribute="bottom" id="Jg7-po-obf"/>
+                <constraint firstItem="2HR-Xb-3d3" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="OI6-k7-tav"/>
+                <constraint firstAttribute="trailing" secondItem="2HR-Xb-3d3" secondAttribute="trailing" id="p9x-xt-qfY"/>
+                <constraint firstItem="2HR-Xb-3d3" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="tNP-YJ-RAN"/>
+            </constraints>
+            <point key="canvasLocation" x="22" y="70"/>
+        </customView>
+    </objects>
+</document>

+ 624 - 2
PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/KMOutlineViewController.swift

@@ -7,12 +7,634 @@
 
 import Cocoa
 
-class KMOutlineViewController: NSViewController {
-
+class KMOutlineViewController: NSViewController,NSMenuItemValidation {
+  
+    @IBOutlet weak var outlineView: KMOutlineView!
+    var dragPDFOutline : CPDFOutline!
+    var renameTextField : NSTextField!
+    var mainWindowController : MainWindowController!
+    var renamePDFOutline : CPDFOutline!
     override func viewDidLoad() {
         super.viewDidLoad()
         self.view.wantsLayer = true
         self.view.layer?.backgroundColor = NSColor.green.cgColor
+        
+        self.outlineView.registerForDraggedTypes([NSPasteboard.PasteboardType(rawValue: "kKMPDFViewOutlineDragDataType")])
+        self.outlineView.doubleAction = #selector(outlineViewDoubleAction)
+        self.outlineView.target = self
+        
+        let menus : NSMenu = NSMenu(title: "")
+        let addItem = self.menuItemWithTitle(title: NSLocalizedString("Add Item", comment: ""), action: #selector(addItemAction))
+        let addChildItem = self.menuItemWithTitle(title: NSLocalizedString("Add Sub-Item", comment: ""), action: #selector(addChildItemAction))
+        let addHigherItem = self.menuItemWithTitle(title: NSLocalizedString("Add A Higher Level", comment: ""), action: #selector(addHigherItemAction))
+        let deleteItem = self.menuItemWithTitle(title: NSLocalizedString("Delete", comment: ""), action: #selector(deleteItemAction))
+        let editItem = self.menuItemWithTitle(title: NSLocalizedString("Edit", comment: ""), action: #selector(editItemAction))
+        let renameItem = self.menuItemWithTitle(title: NSLocalizedString("Rename", comment: ""), action: #selector(renameItemAction))
+        let changeItem = self.menuItemWithTitle(title: NSLocalizedString("Change Destination", comment: ""), action: #selector(changeItemAction))
+        let promoteItem = self.menuItemWithTitle(title: NSLocalizedString("Promote", comment: ""), action: #selector(promoteItemAction))
+        let demoteItem = self.menuItemWithTitle(title: NSLocalizedString("Demote", comment: ""), action: #selector(demoteItemAction))
+        menus.addItem(addItem)
+        menus.addItem(addChildItem)
+        menus.addItem(addHigherItem)
+        menus.addItem(NSMenuItem.separator())
+        menus.addItem(deleteItem)
+        menus.addItem(NSMenuItem.separator())
+        menus.addItem(editItem)
+        menus.addItem(renameItem)
+        menus.addItem(changeItem)
+        menus.addItem(NSMenuItem.separator())
+        menus.addItem(promoteItem)
+        menus.addItem(demoteItem)
+        self.outlineView.menu = menus
+    }
+    
+    @objc func outlineViewDoubleAction() {
+        if(self.outlineView.clickedRow >= 0) {
+            self.renameItemAction()
+        } else {
+            __NSBeep()
+        }
+    }
+    
+    @objc func addItemAction() {
+        let selectRowIndexs = self.selectedRowIndexs()
+        if (selectRowIndexs.count == 0) {
+            var lastPDFOuline : CPDFOutline = self.outlineView.item(atRow: self.outlineView.numberOfRows - 1) as! CPDFOutline
+            var rootPDFOutline : CPDFOutline!
+            if(lastPDFOuline != nil) {
+                while ((lastPDFOuline.parent) != nil) {
+                    lastPDFOuline = lastPDFOuline.parent
+                }
+                rootPDFOutline = lastPDFOuline
+            } else {
+                rootPDFOutline = self.mainWindowController.listView.document.outlineRoot()
+                if(rootPDFOutline == nil) {
+                    rootPDFOutline = self.mainWindowController.listView.document.setNewOutlineRoot()
+                }
+            }
+            let index = rootPDFOutline.numberOfChildren
+            self.addPDFOutlineToIndex(index: NSInteger(index), atParent: rootPDFOutline)
+        }
+    }
+    
+    @objc func addChildItemAction() {
+        let selectedRowIndexes : IndexSet! = self.selectedRowIndexs()
+        if selectedRowIndexes.count == 0 {
+            __NSBeep()
+        } else {
+            let parent  = self.outlineView.item(atRow: selectedRowIndexes.last!) as! CPDFOutline
+            let index = parent.numberOfChildren
+            self.addPDFOutlineToIndex(index: NSInteger(index), atParent: parent)
+        }
+    }
+    
+    @objc func addHigherItemAction() {
+        let selectedRowIndexes : IndexSet! = self.selectedRowIndexs()
+        if selectedRowIndexes.count == 0 {
+            __NSBeep()
+        } else {
+            let currentPDFOutline  = self.outlineView.item(atRow: selectedRowIndexes.last!) as! CPDFOutline
+            let parent = currentPDFOutline.parent
+            let index = NSInteger(parent!.index) + 1
+            if parent != nil {
+                self.addPDFOutlineToIndex(index: index, atParent: parent)
+            } else {
+                __NSBeep()
+            }
+        }
+    }
+    
+    @objc func deleteItemAction() {
+        let selectedRowIndexes : IndexSet! = self.selectedRowIndexs()
+        if selectedRowIndexes.count == 0 {
+            __NSBeep()
+        } else {
+            var selectedPDFOutlineArr = [CPDFOutline]();
+            for (index,_) in selectedRowIndexes.enumerated() {
+                var outline : CPDFOutline = self.outlineView.item(atRow: index) as! CPDFOutline
+                selectedPDFOutlineArr.append(outline)
+            }
+            for tOutline in selectedPDFOutlineArr {
+                self.removePDFOutline(outline: tOutline, toIndex: NSInteger(tOutline.index), atParent: tOutline.parent)
+            }
+        }
+        
+    }
+    
+    @objc func editItemAction() {
+        if self.outlineView.clickedRow >= 0 {
+            let cell = self.outlineView.rowView(atRow: self.outlineView.clickedRow, makeIfNecessary: true)
+            let outline = self.outlineView.item(atRow: self.outlineView.clickedRow)
+            
+        }
+    }
+    
+    @objc func renameItemAction() {
+        if self.outlineView.clickedRow >= 0 {
+            self.renameOutlineWithRow(row: self.outlineView.clickedRow)
+        } else {
+            __NSBeep()
+        }
+    }
+    
+    @objc func changeItemAction() {
+        if self.outlineView.clickedRow >= 0 {
+            let outline = self.outlineView.item(atRow: self.outlineView.clickedRow)
+            let alter = NSAlert()
+            alter.alertStyle = NSAlert.Style.informational
+            alter.messageText = NSLocalizedString("Are you sure you want to set the destination as the current location?", comment: "")
+            alter.addButton(withTitle: NSLocalizedString("Yes", comment:""))
+            alter.addButton(withTitle: NSLocalizedString("No", comment:""))
+            let modlres = alter.runModal()
+            if modlres == NSApplication.ModalResponse.alertFirstButtonReturn {
+                self.setPDFOutline(outline: outline as? CPDFOutline, destination: self.mainWindowController.listView.currentDestination)
+            }
+        } else {
+            __NSBeep()
+        }
+    }
+    
+    @objc func promoteItemAction() {
+        if self.outlineView.clickedRow >= 0 {
+            let outline = self.outlineView.item(atRow: self.outlineView.clickedRow) as! CPDFOutline
+            var parent = outline.parent
+            let index = NSInteger(parent!.index) + 1
+            parent = parent?.parent
+            if parent != nil {
+                self.movePDFOutline(outline: outline, toIndex: index, atParent: parent)
+            } else {
+               __NSBeep()
+           }
+        } else {
+            __NSBeep()
+        }
+    }
+    
+    @objc func demoteItemAction() {
+        if self.outlineView.clickedRow >= 0 {
+            let outline = self.outlineView.item(atRow: self.outlineView.clickedRow) as! CPDFOutline
+            var parent = outline.parent
+            let newParent = parent?.child(at: UInt(outline.index - 1))
+            let index = newParent?.numberOfChildren
+        }
     }
     
+    //MARK: - Outline
+    
+    func renameOutlineWithRow(row:NSInteger) {
+        self.renamePDFOutline = self.outlineView.item(atRow: row) as? CPDFOutline
+        let cell : KMOutlineCellView = self.outlineView.view(atColumn: 0, row: row, makeIfNecessary: true) as! KMOutlineCellView
+        self.renameTextField = cell.titleTextField
+        self.renameTextField.delegate = self
+        self.renameTextField.isEditable = true
+        self.renameTextField.becomeFirstResponder()
+    }
+    
+    func renamePDFOutline(outline : CPDFOutline! , label:String) {
+        if outline.label == label {
+            return
+        }
+        
+        outline.label = label
+        self.outlineView.reloadData()
+        
+        var indexSet = IndexSet()
+        indexSet.insert(self.outlineView.row(forItem: outline))
+        self.outlineView.selectRowIndexes(indexSet, byExtendingSelection: false)
+    }
+    
+    func movePDFOutline(outline : CPDFOutline! , toIndex index : NSInteger , atParent parent : CPDFOutline!) {
+        outline.removeFromParent()
+        parent.insertChild(outline, at: UInt(index))
+        self.outlineView.reloadData()
+        self.outlineView.expandItem(parent)
+    }
+    
+    func addPDFOutlineCurrentSelections() {
+        var string : String = ""
+        for currentSelection in  self.mainWindowController.listView.currentSelection.selectionsByLine {
+            if string.count == 0 {
+                string = "\(string)+\("\n")"
+                string = "\(string)+\(String(describing: currentSelection.string()))"
+            } else {
+                string = currentSelection.string()
+            }
+        }
+        
+        var parent : CPDFOutline?
+        var index : Int = 0
+        let selectRowIndexes = self.selectedRowIndexs()
+        if selectRowIndexes.count == 0 {
+            var lastOutline = self.outlineView.item(atRow: self.outlineView.numberOfRows - 1) as? CPDFOutline
+            if lastOutline != nil {
+                while ((lastOutline?.parent) != nil) {
+                    lastOutline = lastOutline?.parent
+                }
+                parent = lastOutline
+            } else {
+                parent = self.mainWindowController.listView.document.outlineRoot()
+                if parent == nil {
+                    parent = self.mainWindowController.listView.document.setNewOutlineRoot()
+                }
+            }
+            index = Int(parent?.numberOfChildren ?? 0)
+        } else {
+            let currentPDFOutline = self.outlineView.item(atRow: selectRowIndexes.last ?? 0) as! CPDFOutline
+            parent = currentPDFOutline.parent
+            index = Int(currentPDFOutline.index + 1)
+        }
+        if parent != nil {
+            let outline = parent?.insertChild(at: UInt(index))
+            outline?.label = string
+            outline?.destination = self.mainWindowController.listView.currentDestination
+            self.addPDFOutline(outline: outline, toIndex: index, atParent: parent)
+        } else {
+            __NSBeep()
+        }
+    }
+    
+    func updateOutlineSelection() {
+        let currentPageIndex = self.mainWindowController.listView.currentPageIndex
+        let currentPage : CPDFPage = self.mainWindowController.listView.document.page(at: UInt(currentPageIndex))
+        let numRows = self.outlineView.numberOfRows
+        var arr = [[String : CPDFPage]]()
+        for i in 0..<numRows {
+            let tPDFOutline = self.outlineView.item(atRow: i) as! CPDFOutline
+            let tPage = tPDFOutline.destination.pageIndex
+            var page = self.mainWindowController.listView.document.page(at: UInt(tPage))!
+            var tDict : [String : CPDFPage] = ["\(i)":page]
+            arr.append(tDict)
+        }
+        DispatchQueue.global(qos: .utility).async{
+            var hasExistInOutlineView = false
+            var selectIndex = 0
+            for (index,value) in arr.enumerated() {
+                let dict : [String : CPDFPage] = value
+                let page: CPDFPage = dict.values.first ?? CPDFPage()
+                let index = Int(index)
+                if page.isEqual(currentPage) {
+                    selectIndex = index
+                    hasExistInOutlineView = true
+                    break
+                }
+            }
+            DispatchQueue.main.async {
+                if !hasExistInOutlineView {
+                    self.outlineView.deselectRow(self.outlineView.selectedRow)
+                } else {
+                    self.outlineView.selectRowIndexes([selectIndex], byExtendingSelection: false)
+                    self.outlineView.scrollRowToVisible(selectIndex)
+                }
+            }
+        }
+    }
+    
+    //MARK: - Accessors
+    
+    func menuItemWithTitle(title:String, action:Selector?) -> NSMenuItem {
+        let menuItem = NSMenuItem.init(title: title as String, action: action, keyEquivalent: "")
+        menuItem.target = self
+        return menuItem
+    }
+    
+    func selectedRowIndexs() -> IndexSet {
+        let clickRow = self.outlineView.clickedRow
+        var selectedRowIndexs = self.outlineView.selectedRowIndexes
+        if(clickRow != -1 && !selectedRowIndexs.contains(clickRow)) {
+            selectedRowIndexs = [clickRow]
+        }
+        return selectedRowIndexs
+    }
+    
+    func reloadData() {
+        self.outlineView.reloadData()
+    }
+    
+    func addPDFOutlineToIndex(index:NSInteger, atParent parent:CPDFOutline!) {
+        let currentPageIndex = self.mainWindowController.listView.currentPageIndex
+        var outline = parent.insertChild(at: UInt(index))
+        outline?.label =  "\(NSLocalizedString("Page", comment: ""))\(currentPageIndex+1)"
+        let des = self.mainWindowController.listView.currentDestination
+        if "\(des?.point.x ?? 0)" != "nan" {
+            outline?.destination = self.mainWindowController.listView.currentDestination
+        } else {
+            let destination : CPDFDestination = CPDFDestination(document: self.mainWindowController.listView.document, pageIndex: currentPageIndex, at: CGPoint(x: 0, y: 0), zoom: self.mainWindowController.listView.scaleFactor)
+            outline?.destination = destination
+        }
+        self.addPDFOutline(outline:outline,toIndex: index,atParent: parent)
+    }
+    
+    func addPDFOutline(outline:CPDFOutline! , toIndex index : NSInteger , atParent parent : CPDFOutline!) {
+        self.view.window?.makeFirstResponder(self.outlineView)
+        if outline.parent == nil {
+            parent.insertChild(outline, at: UInt(index))
+        }
+        self.outlineView.reloadData()
+        self.outlineView.expandItem(parent)
+        let indexSet : IndexSet = [self.outlineView.row(forItem: outline)]
+        self.outlineView.selectRowIndexes(indexSet, byExtendingSelection: false)
+        if(self.outlineView.selectedRow >= 0) {
+            self.renameOutlineWithRow(row: self.outlineView.selectedRow)
+        }
+        if Thread.current.isMainThread {
+            self.outlineView.scrollToVisible(self.outlineView.rect(ofRow: index))
+        } else {
+            DispatchQueue.main.async {
+                self.outlineView.scrollToVisible(self.outlineView.rect(ofRow: index))
+            }
+        }
+    }
+    
+    func removePDFOutline(outline:CPDFOutline! , toIndex index : NSInteger , atParent parent : CPDFOutline!) {
+        outline.removeFromParent()
+        self.outlineView.reloadData()
+        self.outlineView.expandItem(parent)
+    }
+    
+    func setPDFOutline(outline:CPDFOutline! , destination : CPDFDestination!) {
+        outline.destination = destination
+        self.outlineView.reloadData()
+    }
+    
+    func editOutlineUI(editVC : KMOutlineEditViewController!) {
+        if editVC.pageButton.state == NSControl.StateValue.on {
+            let index = Int(editVC.outlineTargetPageIndexTextField.stringValue)!
+            if editVC.originalDestination.pageIndex != index {
+                let page = editVC.pdfView.document.page(at: UInt(index))
+                if page != nil {
+                    let destination = CPDFDestination.init(document: editVC.pdfView.document, pageIndex: index)
+                    editVC.outline.destination = destination
+                } else {
+                    __NSBeep()
+                }
+            }
+        } else if editVC.urlButton.state == NSControl.StateValue.on {
+            if editVC.originalURLString != editVC.outlineURLTextField.stringValue {
+                var urlString = editVC.outlineURLTextField.stringValue
+                let tLowerUrl = urlString.lowercased()
+                if !tLowerUrl.hasPrefix("https://") && !tLowerUrl.hasPrefix("pf]://")  && !urlString.hasPrefix("https://") &&
+                    urlString.lengthOfBytes(using: String.Encoding(rawValue: String.Encoding.utf16.rawValue)) > 0 {
+                    urlString = "http://\(urlString)"
+                }
+                let action = CPDFURLAction.init(url: urlString)
+                editVC.outline.action = action
+            }
+        } else if editVC.mailButton.state == NSControl.StateValue.on {
+            var mailString = editVC.mailAddressTextField.stringValue
+            let tLowerStr = mailString.lowercased()
+            if !tLowerStr.hasPrefix("mailto:") {
+                mailString = "mailto:\(mailString)"
+            }
+            if mailString != editVC.originalURLString {
+                var action = CPDFURLAction.init(url: mailString)
+                if action?.url == nil {
+                    action = CPDFURLAction.init(url: "mailto:")
+                }
+                editVC.outline.action = action
+            }
+        }
+//        self.mainWindowController.document?.undo()
+        if editVC.outlineNameTextView.string != editVC.originalLabel {
+            self.renamePDFOutline(outline: editVC.outline, label: editVC.outlineNameTextView.string)
+        }
+    }
+    
+    //MARK: NSMenuItemValidation
+    
+    func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
+        let action = menuItem.action
+        if action == #selector(addItemAction) ||
+            action == #selector(addChildItemAction) ||
+            action == #selector(addHigherItemAction) ||
+            action == #selector(deleteItemAction) ||
+            action == #selector(editItemAction) ||
+            action == #selector(changeItemAction) ||
+            action == #selector(renameItemAction) ||
+            action == #selector(promoteItemAction) ||
+            action == #selector(demoteItemAction) {
+            if self.outlineView.selectedRowIndexes.count > 1 {
+                if action == #selector(deleteItemAction) {
+                    return true
+                }
+                return false
+            } else if self.outlineView.selectedRowIndexes.count > 0 {
+                if action == #selector(addChildItemAction) || action == #selector(changeItemAction) {
+                    return true
+                }
+            }
+            if self.outlineView.clickedRow == -1 {
+                if action == #selector(addItemAction) {
+                    return true
+                } else {
+                    return false
+                }
+            } else {
+                let outline : CPDFOutline = self.outlineView.item(atRow: self.outlineView.clickedRow) as! CPDFOutline
+                if outline.index > 0 {
+                    if action == #selector(demoteItemAction) {
+                        return true
+                    }
+                } else {
+                    if action == #selector(demoteItemAction) {
+                        return false
+                    }
+                }
+                
+                let parentOutline = outline.parent
+                let grandparentOutline = parentOutline?.parent
+                if grandparentOutline != nil {
+                    if action == #selector(addHigherItemAction) {
+                        return true
+                    } else if action == #selector(promoteItemAction) {
+                        return true
+                    }
+                } else {
+                    if action == #selector(addHigherItemAction) {
+                        return false
+                    } else if action == #selector(promoteItemAction) {
+                        return false
+                    }
+                }
+            }
+            return true
+        }
+        return true
+    }
+    
+}
+
+// MARK - NSOutlineViewDataSource,NSOutlineViewDelegate
+
+extension KMOutlineViewController : NSOutlineViewDataSource,NSOutlineViewDelegate {
+    func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
+        if (item == nil) {
+            var newitem : CPDFOutline? = self.mainWindowController.listView.document.outlineRoot()
+            if(newitem?.numberOfChildren == 0) { //无数据时的图
+                
+            } else {
+                
+            }
+        }
+        return Int((item as? CPDFOutline)?.numberOfChildren ?? 0)
+    }
+    
+    func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
+        var newItem : CPDFOutline = item as! CPDFOutline
+        if (newItem == nil) {
+            newItem = self.mainWindowController.listView.document.outlineRoot()
+        }
+        var child : CPDFOutline = newItem.child(at: UInt(index))
+        return child
+    }
+    
+    func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
+        var newItem : CPDFOutline = item as! CPDFOutline
+        if (newItem == nil) {
+            newItem = self.mainWindowController.listView.document.outlineRoot()
+        }
+        return newItem.numberOfChildren > 0
+    }
+    
+    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
+        let cell : KMOutlineCellView = outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "KMOutlineCellView"), owner: self) as! KMOutlineCellView
+        var outline : CPDFOutline = item as! CPDFOutline
+        cell.titleTextField.stringValue = outline.label
+        var destination = outline.destination
+        if destination == nil {
+            let action = outline.action
+            if (action != nil) && (action is CPDFGoToAction) {
+                destination = (action as? CPDFGoToAction)?.destination()
+            }
+        }
+        if destination != nil {
+            cell.pageTextField.stringValue = "\((destination?.pageIndex ?? 0)+1)"
+        } else {
+            cell.pageTextField.stringValue = ""
+        }
+        
+        let font : NSFont = NSFont.systemFont(ofSize: CGFloat(UserDefaults.standard.float(forKey: "SKOfficeTableFontSize")))
+        if (font != nil) {
+            cell.titleTextField.font = NSFont.init(name: cell.titleTextField.font!.fontName, size: font.pointSize)
+            cell.pageTextField.font = NSFont.init(name: cell.pageTextField.font!.fontName, size: font.pointSize)
+        }
+        
+        return cell
+    }
+    
+    func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
+        let rowView = KMCustomTableRowView()
+        return rowView
+    }
+    
+    func outlineView(_ outlineView: NSOutlineView, heightOfRowByItem item: Any) -> CGFloat {
+        let font : NSFont = NSFont.systemFont(ofSize: CGFloat(UserDefaults.standard.float(forKey: "SKOfficeTableFontSize")))
+        return 27 + font.pointSize
+    }
+    
+    func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool {
+        if self.outlineView.selectedRowIndexes.count > 1 {
+            return true
+        }
+        let outline : CPDFOutline = item as! CPDFOutline
+        if outline.destination != nil {
+            self.mainWindowController.listView.go(to: outline.destination)
+        } else if outline.action != nil {
+            self.mainWindowController.listView.perform(outline.action)
+        }
+        return true
+    }
+    
+    func outlineView(_ outlineView: NSOutlineView, writeItems items: [Any], to pasteboard: NSPasteboard) -> Bool {
+        if self.outlineView.selectedRowIndexes.count > 1 {
+            return true
+        }
+        
+        self.dragPDFOutline = items.first as? CPDFOutline
+        
+        let indexSet = [self.outlineView.clickedRow]
+        let indexSetData : Data = NSKeyedArchiver.archivedData(withRootObject: indexSet) as Data
+        pasteboard.declareTypes([NSPasteboard.PasteboardType(rawValue: "kKMPDFViewOutlineDragDataType")], owner: self)
+        pasteboard.setData(indexSetData, forType: NSPasteboard.PasteboardType(rawValue: NSPasteboard.PasteboardType.RawValue("kKMPDFViewOutlineDragDataType")))
+        return true
+    }
+    
+    func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
+        var dragOperation = NSDragOperation.init(rawValue: 0)
+        if index > 0 {
+            dragOperation = NSDragOperation.move
+        }
+        return dragOperation
+    }
+    
+    func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
+        if ((self.dragPDFOutline == nil) || (index < 0)) {
+            return false
+        }
+        let outline = item as! CPDFOutline
+        if outline.parent == nil {
+            var root = self.dragPDFOutline
+            while root?.parent != nil {
+                root = root?.parent
+            }
+            if self.dragPDFOutline.parent.isEqual(root) {
+                if self.dragPDFOutline.index > index {
+                    self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index, atParent: root)
+                } else {
+                    self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index - 1, atParent: root)
+                }
+            } else {
+                self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index, atParent: root)
+            }
+        } else {
+            if self.dragPDFOutline.parent.isEqual(item) {
+                if self.dragPDFOutline.index != 0 {
+                    if self.dragPDFOutline.index > index {
+                        self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index, atParent: outline)
+                    } else {
+                        self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index - 1, atParent: outline)
+                    }
+                } else {
+                    return false
+                }
+            } else {
+                var tOutline = outline
+                var isContains = false
+                while tOutline != nil {
+                    if tOutline.isEqual(self.dragPDFOutline) {
+                        isContains = true
+                        break
+                    }
+                    tOutline = tOutline.parent
+                }
+                if isContains == false {
+                    self.movePDFOutline(outline: self.dragPDFOutline, toIndex: index, atParent: outline)
+                }
+            }
+        }
+        
+        return true
+    }
+    
+}
+
+// MARK - NSTextFieldDelegate
+
+extension KMOutlineViewController : NSTextFieldDelegate {
+    func controlTextDidEndEditing(_ obj: Notification) {
+        if (self.renameTextField.isEqual(obj.object)) {
+            let textField : NSTextField = obj.object as! NSTextField
+            self.renamePDFOutline(outline: self.renamePDFOutline, label: textField.stringValue)
+        }
+    }
+}
+
+// MARK - NSTextFieldDelegate
+
+extension KMOutlineViewController : NSPopoverDelegate {
+    func popoverWillClose(_ notification: Notification) {
+        let popover : NSPopover = notification.object as! NSPopover
+        if popover.contentViewController!.isKind(of: KMOutlineEditViewController.self) {
+            
+        }
+    }
 }

+ 98 - 5
PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/KMOutlineViewController.xib

@@ -1,19 +1,112 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11134" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21225" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
     <dependencies>
-        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11134"/>
+        <deployment identifier="macosx"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21225"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
-        <customObject id="-2" userLabel="File's Owner" customClass="KMOutlineViewController" customModuleProvider="target">
+        <customObject id="-2" userLabel="File's Owner" customClass="KMOutlineViewController" customModule="PDF_Office" customModuleProvider="target">
             <connections>
+                <outlet property="outlineView" destination="ODD-oE-RFU" id="TEZ-XE-G41"/>
                 <outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
             </connections>
         </customObject>
         <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
         <customObject id="-3" userLabel="Application" customClass="NSObject"/>
         <customView id="Hz6-mo-xeY">
-            <rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
+            <rect key="frame" x="0.0" y="0.0" width="248" height="439"/>
             <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+            <subviews>
+                <scrollView autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="10" verticalLineScroll="24" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kbI-MU-S56">
+                    <rect key="frame" x="0.0" y="0.0" width="248" height="439"/>
+                    <clipView key="contentView" drawsBackground="NO" id="rcM-li-NVW">
+                        <rect key="frame" x="1" y="1" width="246" height="437"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" tableStyle="fullWidth" selectionHighlightStyle="sourceList" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" typeSelect="NO" rowHeight="24" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="13" outlineTableColumn="Tj9-i9-rhT" id="ODD-oE-RFU" customClass="KMOutlineView">
+                                <rect key="frame" x="0.0" y="0.0" width="246" height="437"/>
+                                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                <size key="intercellSpacing" width="3" height="0.0"/>
+                                <color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
+                                <tableColumns>
+                                    <tableColumn identifier="AutomaticTableColumnIdentifier.0" width="214" minWidth="40" maxWidth="1000" id="Tj9-i9-rhT">
+                                        <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
+                                            <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+                                            <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+                                        </tableHeaderCell>
+                                        <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="OUF-pI-P6f">
+                                            <font key="font" metaFont="system"/>
+                                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+                                            <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                        </textFieldCell>
+                                        <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+                                        <prototypeCellViews>
+                                            <tableCellView identifier="KMOutlineCellView" id="H0y-kc-PRN" customClass="KMOutlineCellView" customModule="PDF_Office" customModuleProvider="target">
+                                                <rect key="frame" x="11" y="0.0" width="223" height="24"/>
+                                                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                                <subviews>
+                                                    <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="d2q-sN-cbx">
+                                                        <rect key="frame" x="-2" y="4" width="180" height="16"/>
+                                                        <textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="Prh-0Z-Wce">
+                                                            <font key="font" usesAppearanceFont="YES"/>
+                                                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                                        </textFieldCell>
+                                                    </textField>
+                                                    <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Lls-BI-xXu">
+                                                        <rect key="frame" x="178" y="4" width="37" height="16"/>
+                                                        <constraints>
+                                                            <constraint firstAttribute="width" constant="33" id="zVC-MC-8Vv"/>
+                                                        </constraints>
+                                                        <textFieldCell key="cell" lineBreakMode="truncatingTail" alignment="right" title="1000" id="CQo-ko-fU0">
+                                                            <font key="font" usesAppearanceFont="YES"/>
+                                                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
+                                                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+                                                        </textFieldCell>
+                                                    </textField>
+                                                </subviews>
+                                                <constraints>
+                                                    <constraint firstAttribute="trailing" secondItem="Lls-BI-xXu" secondAttribute="trailing" constant="10" id="7Wu-xi-qI4"/>
+                                                    <constraint firstItem="Lls-BI-xXu" firstAttribute="leading" secondItem="d2q-sN-cbx" secondAttribute="trailing" constant="4" id="IHW-10-uFF"/>
+                                                    <constraint firstItem="d2q-sN-cbx" firstAttribute="centerY" secondItem="H0y-kc-PRN" secondAttribute="centerY" id="gaM-VJ-3N9"/>
+                                                    <constraint firstItem="Lls-BI-xXu" firstAttribute="centerY" secondItem="H0y-kc-PRN" secondAttribute="centerY" id="sCE-6N-oqU"/>
+                                                    <constraint firstItem="d2q-sN-cbx" firstAttribute="leading" secondItem="H0y-kc-PRN" secondAttribute="leading" id="z6K-Wf-2jg"/>
+                                                </constraints>
+                                                <connections>
+                                                    <outlet property="pageTextField" destination="Lls-BI-xXu" id="DQ6-W4-jHE"/>
+                                                    <outlet property="titleTextField" destination="d2q-sN-cbx" id="71v-C5-gbc"/>
+                                                </connections>
+                                            </tableCellView>
+                                        </prototypeCellViews>
+                                    </tableColumn>
+                                </tableColumns>
+                                <connections>
+                                    <outlet property="dataSource" destination="-2" id="ApK-Xb-UYF"/>
+                                    <outlet property="delegate" destination="-2" id="fPV-Dh-m7t"/>
+                                </connections>
+                            </outlineView>
+                        </subviews>
+                        <nil key="backgroundColor"/>
+                    </clipView>
+                    <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="XpC-N5-Tmm">
+                        <rect key="frame" x="1" y="119" width="223" height="15"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </scroller>
+                    <scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="vxI-0B-SC3">
+                        <rect key="frame" x="224" y="17" width="15" height="102"/>
+                        <autoresizingMask key="autoresizingMask"/>
+                    </scroller>
+                </scrollView>
+            </subviews>
+            <constraints>
+                <constraint firstAttribute="trailing" secondItem="kbI-MU-S56" secondAttribute="trailing" id="07i-98-1a7"/>
+                <constraint firstItem="kbI-MU-S56" firstAttribute="leading" secondItem="Hz6-mo-xeY" secondAttribute="leading" id="Akm-zN-9MC"/>
+                <constraint firstAttribute="bottom" secondItem="kbI-MU-S56" secondAttribute="bottom" id="Tkq-nD-8zc"/>
+                <constraint firstItem="kbI-MU-S56" firstAttribute="top" secondItem="Hz6-mo-xeY" secondAttribute="top" id="YdQ-sA-uvc"/>
+            </constraints>
+            <point key="canvasLocation" x="133" y="130.5"/>
         </customView>
     </objects>
 </document>

+ 66 - 0
PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/View/KMCustomTableRowView.swift

@@ -0,0 +1,66 @@
+//
+//  KMCustomTableRowView.swift
+//  PDF Office
+//
+//  Created by lxy on 2022/11/7.
+//
+
+import Cocoa
+
+class KMCustomTableRowView: NSTableRowView {
+    var highlightColor : NSColor = NSColor()
+    var selectionColor : NSColor = NSColor()
+    var isHaveRadius = false
+    var color : NSColor = NSColor()
+        
+    // MARK - Draw
+    override func drawBackground(in dirtyRect: NSRect) {
+        if(self.color != NSColor()) {
+            self.color.setFill()
+            if self.isHaveRadius == true {
+                let selectionPath = NSBezierPath.init(roundedRect: dirtyRect, xRadius: 6.0, yRadius: 6.0)
+                selectionPath.fill()
+            } else {
+                __NSRectFill(dirtyRect)
+            }
+        } else {
+            super.drawBackground(in: dirtyRect)
+        }
+    }
+    
+    override func drawSelection(in dirtyRect: NSRect) {
+        if self.selectionHighlightStyle == NSTableView.SelectionHighlightStyle.none {
+            super.drawSelection(in: dirtyRect)
+        } else {
+            if(self.selectionColor != NSColor()) {
+                self.color.setFill()
+                if self.isHaveRadius == true {
+                    let selectionPath = NSBezierPath.init(roundedRect: dirtyRect, xRadius: 6.0, yRadius: 6.0)
+                    selectionPath.fill()
+                } else {
+                    __NSRectFill(dirtyRect)
+                }
+            }  else {
+                super.drawSelection(in: dirtyRect)
+            }
+        }
+    }
+    
+    // MARK - Event
+    private func addTrackingArea() {
+         let opt = (NSTrackingArea.Options.mouseEnteredAndExited.rawValue | NSTrackingArea.Options.inVisibleRect.rawValue | NSTrackingArea.Options.activeInKeyWindow.rawValue)
+        let trackingArea = NSTrackingArea.init(rect: self.bounds,options:NSTrackingArea.Options(rawValue: opt), owner: self, userInfo:nil)
+        self.addTrackingArea(trackingArea)
+    }
+    
+    override func mouseEntered(with event: NSEvent) {
+        super.mouseEntered(with: event)
+        self.color = self.highlightColor
+        self.needsDisplay = false
+    }
+    
+    override func mouseExited(with event: NSEvent) {
+        super.mouseExited(with: event)
+        self.color = NSColor()
+    }
+}

+ 24 - 0
PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/View/KMOutlineCellView.swift

@@ -0,0 +1,24 @@
+//
+//  KMOutlineCellView.swift
+//  PDF Office
+//
+//  Created by lxy on 2022/10/18.
+//
+
+import Cocoa
+
+class KMOutlineCellView: NSTableCellView {
+
+    @IBOutlet weak var titleTextField: NSTextField!
+    @IBOutlet weak var pageTextField: NSTextField!
+    override func draw(_ dirtyRect: NSRect) {
+        super.draw(dirtyRect)
+
+    }
+    
+    override class func awakeFromNib() {
+        super.awakeFromNib()
+        
+    }
+    
+}

+ 23 - 0
PDF Office/PDF Office/Class/PDFWindowController/Side/LeftSide/Outline/View/KMOutlineView.swift

@@ -0,0 +1,23 @@
+//
+//  KMOutlineView.swift
+//  PDF Office
+//
+//  Created by lxy on 2022/10/18.
+//
+
+import Cocoa
+
+class KMOutlineView: NSOutlineView {
+    
+    override func makeView(withIdentifier identifier: NSUserInterfaceItemIdentifier, owner: Any?) -> NSView? {
+        let view = super.makeView(withIdentifier: identifier, owner: owner)
+        if identifier == NSOutlineView.disclosureButtonIdentifier {
+            (view as! NSButton).image = NSImage(named: "KMImageNameSliderTriRightNor")
+            (view as! NSButton).alternateImage = NSImage(named: "KMImageNameSliderTriRightNor")
+            (view as! NSButton).isBordered = false
+            (view as! NSButton).title = ""
+        }
+        return view
+    }
+    
+}