Plist Editor for Cover.app

Introduced a real Property List Editor.
Fixed Theme manager engine.
Other minor fixes
This commit is contained in:
vectorsigma72 2020-04-07 13:48:12 +02:00
parent 5a85883180
commit c8969630f3
143 changed files with 17366 additions and 1234 deletions

View File

@ -7,11 +7,17 @@
objects = {
/* Begin PBXBuildFile section */
952B4D202426A3FF00BFB8AB /* PETypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952B4D1F2426A3FF00BFB8AB /* PETypes.swift */; };
952EB6AF24018B4500BEB0F8 /* gfxutil.c in Sources */ = {isa = PBXBuildFile; fileRef = 952EB6A924018B4400BEB0F8 /* gfxutil.c */; };
952EB6B024018B4500BEB0F8 /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 952EB6AA24018B4400BEB0F8 /* utils.c */; };
952EB6B124018B4500BEB0F8 /* efidevp.c in Sources */ = {isa = PBXBuildFile; fileRef = 952EB6AE24018B4500BEB0F8 /* efidevp.c */; };
9533718323709517003F1AF4 /* bootsectors-install in Resources */ = {isa = PBXBuildFile; fileRef = 9533718223709517003F1AF4 /* bootsectors-install */; };
9533718523709A36003F1AF4 /* bootsectors-install in Copy CloverV2 */ = {isa = PBXBuildFile; fileRef = 9533718223709517003F1AF4 /* bootsectors-install */; };
9534B79B242CDC53003F323E /* Replace.png in Resources */ = {isa = PBXBuildFile; fileRef = 9534B799242CDC52003F323E /* Replace.png */; };
9534B79C242CDC53003F323E /* Find.png in Resources */ = {isa = PBXBuildFile; fileRef = 9534B79A242CDC52003F323E /* Find.png */; };
9534B79F242CDC9B003F323E /* remove.png in Resources */ = {isa = PBXBuildFile; fileRef = 9534B79D242CDC9B003F323E /* remove.png */; };
9534B7A0242CDC9B003F323E /* add.png in Resources */ = {isa = PBXBuildFile; fileRef = 9534B79E242CDC9B003F323E /* add.png */; };
953596692422A77E00DE2D5B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 953596682422A77D00DE2D5B /* QuartzCore.framework */; };
953BC20323720C0A0039755D /* FixedWidthViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 953BC20223720C0A0039755D /* FixedWidthViews.swift */; };
9542ABC02373783400DC03E6 /* boot1-install in Copy tools */ = {isa = PBXBuildFile; fileRef = 9542ABBD2373780C00DC03E6 /* boot1-install */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
9542ABC22373786700DC03E6 /* CloverV2 in Copy CloverV2 */ = {isa = PBXBuildFile; fileRef = 9542ABC12373786700DC03E6 /* CloverV2 */; };
@ -19,13 +25,36 @@
954BBE99238196EE0032425F /* Locale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954BBE98238196EE0032425F /* Locale.swift */; };
954DECD523899F5F006A9876 /* Bootmanager.png in Resources */ = {isa = PBXBuildFile; fileRef = 954DECD423899F5F006A9876 /* Bootmanager.png */; };
954FCFD92391818300C9273C /* NSWindowFix.m in Sources */ = {isa = PBXBuildFile; fileRef = 954FCFD82391818300C9273C /* NSWindowFix.m */; };
955005ED242531E300AB3979 /* PE_Undo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955005EC242531E300AB3979 /* PE_Undo.swift */; };
95509171238B1E3A00933A7E /* daemonInstaller in Copy tools */ = {isa = PBXBuildFile; fileRef = 9550916E238B1DFA00933A7E /* daemonInstaller */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
9552D748236F33CA00C93377 /* CloverDaemonNew in Copy tools */ = {isa = PBXBuildFile; fileRef = 9552D745236F33A700C93377 /* CloverDaemonNew */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
955500E1241ACCCA00618F57 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500E0241ACCCA00618F57 /* Document.swift */; };
955500E7241AD12800618F57 /* PEFormatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500E6241AD12800618F57 /* PEFormatters.swift */; };
955500E9241AD1E700618F57 /* PlistParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500E8241AD1E700618F57 /* PlistParser.swift */; };
955500EB241AD31900618F57 /* PEValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500EA241AD31900618F57 /* PEValueTransformer.swift */; };
955500ED241AD38500618F57 /* PENode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500EC241AD38500618F57 /* PENode.swift */; };
955500EF241AD48200618F57 /* TagData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500EE241AD48100618F57 /* TagData.swift */; };
95550103241AD7C300618F57 /* PETableCellViewPop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500F4241AD7C200618F57 /* PETableCellViewPop.swift */; };
95550104241AD7C300618F57 /* PESearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500F5241AD7C200618F57 /* PESearchField.swift */; };
95550106241AD7C300618F57 /* PETableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500F7241AD7C200618F57 /* PETableCellView.swift */; };
95550107241AD7C300618F57 /* PEPopUpButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500F8241AD7C200618F57 /* PEPopUpButton.swift */; };
95550108241AD7C300618F57 /* PEFindButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500F9241AD7C200618F57 /* PEFindButton.swift */; };
95550109241AD7C300618F57 /* PEMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500FA241AD7C200618F57 /* PEMenuItem.swift */; };
9555010A241AD7C300618F57 /* ReplaceButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500FB241AD7C200618F57 /* ReplaceButton.swift */; };
9555010B241AD7C300618F57 /* PEReplaceField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500FC241AD7C200618F57 /* PEReplaceField.swift */; };
9555010C241AD7C300618F57 /* PEOutlineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500FD241AD7C200618F57 /* PEOutlineView.swift */; };
9555010D241AD7C300618F57 /* PETableRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500FE241AD7C200618F57 /* PETableRowView.swift */; };
9555010E241AD7C300618F57 /* PETextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955500FF241AD7C200618F57 /* PETextField.swift */; };
95550110241BEEF300618F57 /* PE_Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9555010F241BEEF200618F57 /* PE_Notifications.swift */; };
95550112241BEF4F00618F57 /* PE_Serialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95550111241BEF4F00618F57 /* PE_Serialize.swift */; };
95557D6D2436808D00C95FDE /* PE_Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95557D6C2436808D00C95FDE /* PE_Utils.swift */; };
9555AF26238EE53300108C33 /* InstallerOutline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9555AF25238EE53300108C33 /* InstallerOutline.swift */; };
9555AF28238EFDAD00108C33 /* DriversCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9555AF27238EFDAD00108C33 /* DriversCollection.swift */; };
955689DB23A2728000AD323C /* IO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955689DA23A2728000AD323C /* IO.swift */; };
9556CAF5238DF75600082671 /* InstallerOutline.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9556CAF3238DF75600082671 /* InstallerOutline.xib */; };
9558518E23BA3486002CB3D8 /* Speaker.png in Resources */ = {isa = PBXBuildFile; fileRef = 9558518D23BA3485002CB3D8 /* Speaker.png */; };
9559D524243540ED0007FCF3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9559D523243540ED0007FCF3 /* Cocoa.framework */; };
9559D52624355A810007FCF3 /* PEConstraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9559D52524355A800007FCF3 /* PEConstraints.swift */; };
955BEE1523C6B49C00425AB0 /* ThemeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955BEE1223C6B49C00425AB0 /* ThemeView.swift */; };
955BEE1623C6B49C00425AB0 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955BEE1323C6B49C00425AB0 /* ThemeManager.swift */; };
955BEE1723C6B49C00425AB0 /* ThemeManagerVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955BEE1423C6B49C00425AB0 /* ThemeManagerVC.swift */; };
@ -34,8 +63,15 @@
955F7C6D238DCD170019D088 /* DiskArbitration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 955F7C6C238DCD160019D088 /* DiskArbitration.framework */; };
9560906A238C61E200ACD7F7 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 95609068238C61E200ACD7F7 /* MainMenu.xib */; };
956090B7238C890600ACD7F7 /* Installer.xib in Resources */ = {isa = PBXBuildFile; fileRef = 956090B9238C890600ACD7F7 /* Installer.xib */; };
9567B29B242D95A200DAD128 /* PE_Conversions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9567B29A242D95A200DAD128 /* PE_Conversions.swift */; };
95696B7B2401829800AFAD37 /* GengConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95696B7A2401829800AFAD37 /* GengConfig.swift */; };
9569EC42238DD772003AD72C /* Settings.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9569EC44238DD772003AD72C /* Settings.xib */; };
9571A224241BFF44000D6645 /* PlistEditorVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9571A223241BFF44000D6645 /* PlistEditorVC.swift */; };
9571A226241C01DB000D6645 /* PlistEditorWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9571A225241C01DB000D6645 /* PlistEditorWC.swift */; };
9571A228241C2B0C000D6645 /* PEAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9571A227241C2B0C000D6645 /* PEAlert.swift */; };
9571A22A241C2E7F000D6645 /* PlistEditor.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9571A229241C2E7F000D6645 /* PlistEditor.storyboard */; };
9571A22F241CF089000D6645 /* PE_Localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9571A22E241CF088000D6645 /* PE_Localized.swift */; };
957CDBF0242E4845006FD266 /* HTTPErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 957CDBEF242E4845006FD266 /* HTTPErrors.swift */; };
9580402D2413F63700F09F2C /* kmeans.c in Sources */ = {isa = PBXBuildFile; fileRef = 9580400A2413F63400F09F2C /* kmeans.c */; };
9580402E2413F63700F09F2C /* blur.c in Sources */ = {isa = PBXBuildFile; fileRef = 9580400B2413F63400F09F2C /* blur.c */; };
958040302413F63700F09F2C /* nearest.c in Sources */ = {isa = PBXBuildFile; fileRef = 958040132413F63400F09F2C /* nearest.c */; };
@ -44,7 +80,7 @@
958040372413F63700F09F2C /* mediancut.c in Sources */ = {isa = PBXBuildFile; fileRef = 9580401E2413F63600F09F2C /* mediancut.c */; };
958040392413F63700F09F2C /* mempool.c in Sources */ = {isa = PBXBuildFile; fileRef = 958040232413F63600F09F2C /* mempool.c */; };
9580403D2413F8CA00F09F2C /* lodepng.c in Sources */ = {isa = PBXBuildFile; fileRef = 9580403C2413F8C900F09F2C /* lodepng.c */; };
958040402414070F00F09F2C /* PNG8Image.m in Sources */ = {isa = PBXBuildFile; fileRef = 9580403F2414070F00F09F2C /* PNG8Image.m */; };
958040402414070F00F09F2C /* ThemeImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 9580403F2414070F00F09F2C /* ThemeImage.m */; };
958861DA235F75FB00B64173 /* Driver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 958861D9235F75FB00B64173 /* Driver.swift */; };
95C515222369BAF500E4A3A8 /* NVRAM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C515212369BAF500E4A3A8 /* NVRAM.swift */; };
95C5152F236A0A7400E4A3A8 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95C5152E236A0A7400E4A3A8 /* SettingsView.swift */; };
@ -188,6 +224,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
952B4D1F2426A3FF00BFB8AB /* PETypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PETypes.swift; sourceTree = "<group>"; };
952EB6A924018B4400BEB0F8 /* gfxutil.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gfxutil.c; sourceTree = "<group>"; };
952EB6AA24018B4400BEB0F8 /* utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utils.c; sourceTree = "<group>"; };
952EB6AB24018B4500BEB0F8 /* gfxutil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gfxutil.h; sourceTree = "<group>"; };
@ -195,6 +232,11 @@
952EB6AD24018B4500BEB0F8 /* efidevp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = efidevp.h; sourceTree = "<group>"; };
952EB6AE24018B4500BEB0F8 /* efidevp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = efidevp.c; sourceTree = "<group>"; };
9533718223709517003F1AF4 /* bootsectors-install */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "bootsectors-install"; sourceTree = SOURCE_ROOT; };
9534B799242CDC52003F323E /* Replace.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Replace.png; sourceTree = "<group>"; };
9534B79A242CDC52003F323E /* Find.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Find.png; sourceTree = "<group>"; };
9534B79D242CDC9B003F323E /* remove.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = remove.png; sourceTree = "<group>"; };
9534B79E242CDC9B003F323E /* add.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = add.png; sourceTree = "<group>"; };
953596682422A77D00DE2D5B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
953BC20223720C0A0039755D /* FixedWidthViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FixedWidthViews.swift; sourceTree = "<group>"; };
9542ABB82373780C00DC03E6 /* boot1-inst.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "boot1-inst.xcodeproj"; path = "../CloverPackage/utils/boot1-install/boot1-inst.xcodeproj"; sourceTree = "<group>"; };
9542ABC12373786700DC03E6 /* CloverV2 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = CloverV2; path = ../CloverPackage/CloverV2; sourceTree = "<group>"; };
@ -204,14 +246,37 @@
954FCFD023917A8A00C9273C /* Clover-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Clover-Bridging-Header.h"; sourceTree = "<group>"; };
954FCFD72391818300C9273C /* NSWindowFix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSWindowFix.h; sourceTree = "<group>"; };
954FCFD82391818300C9273C /* NSWindowFix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSWindowFix.m; sourceTree = "<group>"; };
955005EC242531E300AB3979 /* PE_Undo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PE_Undo.swift; sourceTree = "<group>"; };
95509169238B1DFA00933A7E /* daemonInstaller.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = daemonInstaller.xcodeproj; path = daemonInstaller/daemonInstaller.xcodeproj; sourceTree = "<group>"; };
95524B83238051F3005F6425 /* CloverLogOut.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CloverLogOut.xcodeproj; path = CloverLogOut/CloverLogOut.xcodeproj; sourceTree = "<group>"; };
9552D740236F33A700C93377 /* CloverDaemonNew.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CloverDaemonNew.xcodeproj; path = CloverDaemonNew/CloverDaemonNew.xcodeproj; sourceTree = "<group>"; };
955500E0241ACCCA00618F57 /* Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = "<group>"; };
955500E6241AD12800618F57 /* PEFormatters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PEFormatters.swift; sourceTree = "<group>"; };
955500E8241AD1E700618F57 /* PlistParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlistParser.swift; sourceTree = "<group>"; };
955500EA241AD31900618F57 /* PEValueTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PEValueTransformer.swift; sourceTree = "<group>"; };
955500EC241AD38500618F57 /* PENode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PENode.swift; sourceTree = "<group>"; };
955500EE241AD48100618F57 /* TagData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TagData.swift; sourceTree = "<group>"; };
955500F4241AD7C200618F57 /* PETableCellViewPop.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PETableCellViewPop.swift; sourceTree = "<group>"; };
955500F5241AD7C200618F57 /* PESearchField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PESearchField.swift; sourceTree = "<group>"; };
955500F7241AD7C200618F57 /* PETableCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PETableCellView.swift; sourceTree = "<group>"; };
955500F8241AD7C200618F57 /* PEPopUpButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PEPopUpButton.swift; sourceTree = "<group>"; };
955500F9241AD7C200618F57 /* PEFindButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PEFindButton.swift; sourceTree = "<group>"; };
955500FA241AD7C200618F57 /* PEMenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PEMenuItem.swift; sourceTree = "<group>"; };
955500FB241AD7C200618F57 /* ReplaceButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReplaceButton.swift; sourceTree = "<group>"; };
955500FC241AD7C200618F57 /* PEReplaceField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PEReplaceField.swift; sourceTree = "<group>"; };
955500FD241AD7C200618F57 /* PEOutlineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PEOutlineView.swift; sourceTree = "<group>"; };
955500FE241AD7C200618F57 /* PETableRowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PETableRowView.swift; sourceTree = "<group>"; };
955500FF241AD7C200618F57 /* PETextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PETextField.swift; sourceTree = "<group>"; };
9555010F241BEEF200618F57 /* PE_Notifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PE_Notifications.swift; sourceTree = "<group>"; };
95550111241BEF4F00618F57 /* PE_Serialize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PE_Serialize.swift; sourceTree = "<group>"; };
95557D6C2436808D00C95FDE /* PE_Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PE_Utils.swift; sourceTree = "<group>"; };
9555AF25238EE53300108C33 /* InstallerOutline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstallerOutline.swift; sourceTree = "<group>"; };
9555AF27238EFDAD00108C33 /* DriversCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DriversCollection.swift; sourceTree = "<group>"; };
955689DA23A2728000AD323C /* IO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IO.swift; sourceTree = "<group>"; };
9556CAF4238DF75600082671 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/InstallerOutline.xib; sourceTree = "<group>"; };
9558518D23BA3485002CB3D8 /* Speaker.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Speaker.png; sourceTree = "<group>"; };
9559D523243540ED0007FCF3 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
9559D52524355A800007FCF3 /* PEConstraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PEConstraints.swift; sourceTree = "<group>"; };
955BEE1223C6B49C00425AB0 /* ThemeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeView.swift; sourceTree = "<group>"; };
955BEE1323C6B49C00425AB0 /* ThemeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = "<group>"; };
955BEE1423C6B49C00425AB0 /* ThemeManagerVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeManagerVC.swift; sourceTree = "<group>"; };
@ -296,6 +361,7 @@
95609101238C89C300ACD7F7 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Installer.strings; sourceTree = "<group>"; };
95609103238C89C600ACD7F7 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Installer.strings; sourceTree = "<group>"; };
95609105238C89C900ACD7F7 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Installer.strings; sourceTree = "<group>"; };
9567B29A242D95A200DAD128 /* PE_Conversions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PE_Conversions.swift; sourceTree = "<group>"; };
95696B7A2401829800AFAD37 /* GengConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GengConfig.swift; sourceTree = "<group>"; };
9569EC43238DD772003AD72C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Settings.xib; sourceTree = "<group>"; };
9569EC46238DD77A003AD72C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Settings.strings; sourceTree = "<group>"; };
@ -336,6 +402,12 @@
9569EC8C238DD7C6003AD72C /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Settings.strings; sourceTree = "<group>"; };
9569EC8E238DD7C8003AD72C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Settings.strings; sourceTree = "<group>"; };
9569EC90238DD7CA003AD72C /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Settings.strings; sourceTree = "<group>"; };
9571A223241BFF44000D6645 /* PlistEditorVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlistEditorVC.swift; sourceTree = "<group>"; };
9571A225241C01DB000D6645 /* PlistEditorWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlistEditorWC.swift; sourceTree = "<group>"; };
9571A227241C2B0C000D6645 /* PEAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PEAlert.swift; sourceTree = "<group>"; };
9571A229241C2E7F000D6645 /* PlistEditor.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = PlistEditor.storyboard; sourceTree = "<group>"; };
9571A22E241CF088000D6645 /* PE_Localized.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PE_Localized.swift; sourceTree = "<group>"; };
957CDBEF242E4845006FD266 /* HTTPErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPErrors.swift; sourceTree = "<group>"; };
9580400A2413F63400F09F2C /* kmeans.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = kmeans.c; path = "libimagequant-2.12.6/kmeans.c"; sourceTree = SOURCE_ROOT; };
9580400B2413F63400F09F2C /* blur.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = blur.c; path = "libimagequant-2.12.6/blur.c"; sourceTree = SOURCE_ROOT; };
958040132413F63400F09F2C /* nearest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nearest.c; path = "libimagequant-2.12.6/nearest.c"; sourceTree = SOURCE_ROOT; };
@ -352,8 +424,8 @@
958040232413F63600F09F2C /* mempool.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mempool.c; path = "libimagequant-2.12.6/mempool.c"; sourceTree = SOURCE_ROOT; };
9580403B2413F86A00F09F2C /* lodepng.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lodepng.h; sourceTree = "<group>"; };
9580403C2413F8C900F09F2C /* lodepng.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lodepng.c; sourceTree = "<group>"; };
9580403E2414070F00F09F2C /* PNG8Image.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PNG8Image.h; sourceTree = "<group>"; };
9580403F2414070F00F09F2C /* PNG8Image.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PNG8Image.m; sourceTree = "<group>"; };
9580403E2414070F00F09F2C /* ThemeImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ThemeImage.h; sourceTree = "<group>"; };
9580403F2414070F00F09F2C /* ThemeImage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThemeImage.m; sourceTree = "<group>"; };
958861D9235F75FB00B64173 /* Driver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Driver.swift; sourceTree = "<group>"; };
95C515212369BAF500E4A3A8 /* NVRAM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NVRAM.swift; sourceTree = "<group>"; };
95C5152E236A0A7400E4A3A8 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
@ -380,6 +452,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9559D524243540ED0007FCF3 /* Cocoa.framework in Frameworks */,
953596692422A77E00DE2D5B /* QuartzCore.framework in Frameworks */,
955F7C6D238DCD170019D088 /* DiskArbitration.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -411,6 +485,8 @@
954C3DEF237DF0250059C698 /* Frameworks */ = {
isa = PBXGroup;
children = (
9559D523243540ED0007FCF3 /* Cocoa.framework */,
953596682422A77D00DE2D5B /* QuartzCore.framework */,
955F7C6C238DCD160019D088 /* DiskArbitration.framework */,
954C3DF0237DF0250059C698 /* ServiceManagement.framework */,
);
@ -441,10 +517,59 @@
name = Products;
sourceTree = "<group>";
};
955500DD241ACBD800618F57 /* Plist Editor */ = {
isa = PBXGroup;
children = (
955500F0241AD75B00618F57 /* Views */,
9567B29A242D95A200DAD128 /* PE_Conversions.swift */,
9571A22E241CF088000D6645 /* PE_Localized.swift */,
955005EC242531E300AB3979 /* PE_Undo.swift */,
955500E0241ACCCA00618F57 /* Document.swift */,
955500EE241AD48100618F57 /* TagData.swift */,
955500EC241AD38500618F57 /* PENode.swift */,
955500E8241AD1E700618F57 /* PlistParser.swift */,
955500E6241AD12800618F57 /* PEFormatters.swift */,
955500EA241AD31900618F57 /* PEValueTransformer.swift */,
9555010F241BEEF200618F57 /* PE_Notifications.swift */,
95550111241BEF4F00618F57 /* PE_Serialize.swift */,
95557D6C2436808D00C95FDE /* PE_Utils.swift */,
952B4D1F2426A3FF00BFB8AB /* PETypes.swift */,
);
path = "Plist Editor";
sourceTree = "<group>";
};
955500F0241AD75B00618F57 /* Views */ = {
isa = PBXGroup;
children = (
9571A229241C2E7F000D6645 /* PlistEditor.storyboard */,
9571A225241C01DB000D6645 /* PlistEditorWC.swift */,
9571A223241BFF44000D6645 /* PlistEditorVC.swift */,
9559D52524355A800007FCF3 /* PEConstraints.swift */,
955500F9241AD7C200618F57 /* PEFindButton.swift */,
955500FB241AD7C200618F57 /* ReplaceButton.swift */,
955500FA241AD7C200618F57 /* PEMenuItem.swift */,
955500F8241AD7C200618F57 /* PEPopUpButton.swift */,
955500FC241AD7C200618F57 /* PEReplaceField.swift */,
955500F5241AD7C200618F57 /* PESearchField.swift */,
955500FD241AD7C200618F57 /* PEOutlineView.swift */,
955500F7241AD7C200618F57 /* PETableCellView.swift */,
955500F4241AD7C200618F57 /* PETableCellViewPop.swift */,
955500FE241AD7C200618F57 /* PETableRowView.swift */,
955500FF241AD7C200618F57 /* PETextField.swift */,
9571A227241C2B0C000D6645 /* PEAlert.swift */,
);
path = Views;
sourceTree = "<group>";
};
9558518C23BA3472002CB3D8 /* images */ = {
isa = PBXGroup;
children = (
9558518D23BA3485002CB3D8 /* Speaker.png */,
9534B79E242CDC9B003F323E /* add.png */,
9534B79D242CDC9B003F323E /* remove.png */,
9534B79A242CDC52003F323E /* Find.png */,
9534B799242CDC52003F323E /* Replace.png */,
955BEE1A23C7171000425AB0 /* check.png */,
);
path = images;
sourceTree = "<group>";
@ -452,7 +577,9 @@
955BEE1123C6B43C00425AB0 /* ThemeManager */ = {
isa = PBXGroup;
children = (
95803FFB2413F58B00F09F2C /* libimagequant */,
955BEE1323C6B49C00425AB0 /* ThemeManager.swift */,
957CDBEF242E4845006FD266 /* HTTPErrors.swift */,
955BEE1423C6B49C00425AB0 /* ThemeManagerVC.swift */,
955BEE1223C6B49C00425AB0 /* ThemeView.swift */,
955BEE1823C6B4B300425AB0 /* ThemeManager.xib */,
@ -518,7 +645,7 @@
95E68AC8235B862F002B37A5 /* Clover */ = {
isa = PBXGroup;
children = (
95803FFB2413F58B00F09F2C /* libimagequant */,
955500DD241ACBD800618F57 /* Plist Editor */,
952EB6A824018B2400BEB0F8 /* GfxUtil */,
955BEE1123C6B43C00425AB0 /* ThemeManager */,
95E68AC9235B862F002B37A5 /* AppDelegate.swift */,
@ -537,7 +664,6 @@
95E50075238ABA56002F3869 /* Tasks.swift */,
953BC20223720C0A0039755D /* FixedWidthViews.swift */,
9533718223709517003F1AF4 /* bootsectors-install */,
955BEE1A23C7171000425AB0 /* check.png */,
9569EC44238DD772003AD72C /* Settings.xib */,
95609068238C61E200ACD7F7 /* MainMenu.xib */,
95E68AD9235B8666002B37A5 /* Installer */,
@ -549,8 +675,8 @@
954FCFD023917A8A00C9273C /* Clover-Bridging-Header.h */,
954FCFD72391818300C9273C /* NSWindowFix.h */,
954FCFD82391818300C9273C /* NSWindowFix.m */,
9580403E2414070F00F09F2C /* PNG8Image.h */,
9580403F2414070F00F09F2C /* PNG8Image.m */,
9580403E2414070F00F09F2C /* ThemeImage.h */,
9580403F2414070F00F09F2C /* ThemeImage.m */,
);
path = Clover;
sourceTree = "<group>";
@ -575,7 +701,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 95E68AD6235B8632002B37A5 /* Build configuration list for PBXNativeTarget "Clover" */;
buildPhases = (
9542ABAA23735EE800DC03E6 /* Zip CloverV2 */,
9542ABAA23735EE800DC03E6 /* zip CloverV2 */,
95E68AC2235B862F002B37A5 /* Sources */,
95E68AC4235B862F002B37A5 /* Resources */,
95E68AE9235C70BE002B37A5 /* Copy CloverV2 */,
@ -735,7 +861,10 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9534B79B242CDC53003F323E /* Replace.png in Resources */,
9571A22A241C2E7F000D6645 /* PlistEditor.storyboard in Resources */,
9556CAF5238DF75600082671 /* InstallerOutline.xib in Resources */,
9534B7A0242CDC9B003F323E /* add.png in Resources */,
9558518E23BA3486002CB3D8 /* Speaker.png in Resources */,
956090B7238C890600ACD7F7 /* Installer.xib in Resources */,
954DECD523899F5F006A9876 /* Bootmanager.png in Resources */,
@ -744,6 +873,8 @@
955BEE1B23C7171100425AB0 /* check.png in Resources */,
9560906A238C61E200ACD7F7 /* MainMenu.xib in Resources */,
9533718323709517003F1AF4 /* bootsectors-install in Resources */,
9534B79F242CDC9B003F323E /* remove.png in Resources */,
9534B79C242CDC53003F323E /* Find.png in Resources */,
955BEE1923C6B4B300425AB0 /* ThemeManager.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -751,7 +882,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
9542ABAA23735EE800DC03E6 /* Zip CloverV2 */ = {
9542ABAA23735EE800DC03E6 /* zip CloverV2 */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -760,7 +891,7 @@
);
inputPaths = (
);
name = "Zip CloverV2";
name = "zip CloverV2";
outputFileListPaths = (
);
outputPaths = (
@ -785,7 +916,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "rm -rf \"${TARGET_BUILD_DIR}\"/Clover.app/Contents/SharedSupport/CloverV2/rcScripts\nrm -rf \"${TARGET_BUILD_DIR}\"/Clover.app/Contents/SharedSupport/CloverV2/ThirdParty\n";
shellScript = "cd ..\n\nif [[ ! -d \"${PWD}\"/CloverPackage ]]; then\n echo \"Error. Cannot found CloverPackage directory.\"\n exit 1\nfi\n\nwithV2=\"${PWD}/CloverPackage/sym/.withV2\"\n\nif [[ -f \"${withV2}\" ]]; then\nrm -rf \"${TARGET_BUILD_DIR}\"/Clover.app/Contents/SharedSupport/CloverV2/rcScripts\nrm -rf \"${TARGET_BUILD_DIR}\"/Clover.app/Contents/SharedSupport/CloverV2/ThirdParty\n\nthemeDir=\"${TARGET_BUILD_DIR}\"/Clover.app/Contents/SharedSupport/CloverV2/themespkg\nif [[ -d \"${themeDir}\"/Clovy ]]; then\ncd \"${themeDir}\"\nrm -f *.plist\nfind . -type d -not -name 'Clovy' -not -name \".*\" -print0 | xargs -0 -I {} rm -rf {}\nfi\nelse\nrm -rf \"${TARGET_BUILD_DIR}\"/Clover.app/Contents/SharedSupport/CloverV2/*\nfi\n\nrm -f \"${withV2}\"\n";
};
/* End PBXShellScriptBuildPhase section */
@ -794,38 +925,67 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
955500EF241AD48200618F57 /* TagData.swift in Sources */,
955500ED241AD38500618F57 /* PENode.swift in Sources */,
95E68AE5235B89D9002B37A5 /* Installer.swift in Sources */,
952EB6B124018B4500BEB0F8 /* efidevp.c in Sources */,
9555010A241AD7C300618F57 /* ReplaceButton.swift in Sources */,
958040312413F63700F09F2C /* libimagequant.c in Sources */,
9571A226241C01DB000D6645 /* PlistEditorWC.swift in Sources */,
9571A224241BFF44000D6645 /* PlistEditorVC.swift in Sources */,
955500E9241AD1E700618F57 /* PlistParser.swift in Sources */,
957CDBF0242E4845006FD266 /* HTTPErrors.swift in Sources */,
95550107241AD7C300618F57 /* PEPopUpButton.swift in Sources */,
95E68AE1235B86A1002B37A5 /* Shared.swift in Sources */,
952B4D202426A3FF00BFB8AB /* PETypes.swift in Sources */,
9571A228241C2B0C000D6645 /* PEAlert.swift in Sources */,
95550106241AD7C300618F57 /* PETableCellView.swift in Sources */,
95E68ACC235B862F002B37A5 /* ViewController.swift in Sources */,
95E68AE2235B86A1002B37A5 /* Disks.swift in Sources */,
954FCFD92391818300C9273C /* NSWindowFix.m in Sources */,
9580403D2413F8CA00F09F2C /* lodepng.c in Sources */,
958040302413F63700F09F2C /* nearest.c in Sources */,
958040372413F63700F09F2C /* mediancut.c in Sources */,
9555010B241AD7C300618F57 /* PEReplaceField.swift in Sources */,
95550112241BEF4F00618F57 /* PE_Serialize.swift in Sources */,
95557D6D2436808D00C95FDE /* PE_Utils.swift in Sources */,
95550109241AD7C300618F57 /* PEMenuItem.swift in Sources */,
95E68ADF235B86A1002B37A5 /* Releases.swift in Sources */,
95550103241AD7C300618F57 /* PETableCellViewPop.swift in Sources */,
9559D52624355A810007FCF3 /* PEConstraints.swift in Sources */,
954BBE99238196EE0032425F /* Locale.swift in Sources */,
9555010C241AD7C300618F57 /* PEOutlineView.swift in Sources */,
95E68ACA235B862F002B37A5 /* AppDelegate.swift in Sources */,
955BEE1523C6B49C00425AB0 /* ThemeView.swift in Sources */,
958861DA235F75FB00B64173 /* Driver.swift in Sources */,
958040402414070F00F09F2C /* PNG8Image.m in Sources */,
958040402414070F00F09F2C /* ThemeImage.m in Sources */,
95E68AE3235B86A1002B37A5 /* bdmesg.swift in Sources */,
9580402E2413F63700F09F2C /* blur.c in Sources */,
95C5152F236A0A7400E4A3A8 /* SettingsView.swift in Sources */,
9555AF28238EFDAD00108C33 /* DriversCollection.swift in Sources */,
952EB6AF24018B4500BEB0F8 /* gfxutil.c in Sources */,
9580402D2413F63700F09F2C /* kmeans.c in Sources */,
958040392413F63700F09F2C /* mempool.c in Sources */,
958040332413F63700F09F2C /* pam.c in Sources */,
953BC20323720C0A0039755D /* FixedWidthViews.swift in Sources */,
95696B7B2401829800AFAD37 /* GengConfig.swift in Sources */,
95E50076238ABA56002F3869 /* Tasks.swift in Sources */,
955005ED242531E300AB3979 /* PE_Undo.swift in Sources */,
958040392413F63700F09F2C /* mempool.c in Sources */,
955500EB241AD31900618F57 /* PEValueTransformer.swift in Sources */,
958040332413F63700F09F2C /* pam.c in Sources */,
95550104241AD7C300618F57 /* PESearchField.swift in Sources */,
9571A22F241CF089000D6645 /* PE_Localized.swift in Sources */,
953BC20323720C0A0039755D /* FixedWidthViews.swift in Sources */,
95550110241BEEF300618F57 /* PE_Notifications.swift in Sources */,
95696B7B2401829800AFAD37 /* GengConfig.swift in Sources */,
9555010E241AD7C300618F57 /* PETextField.swift in Sources */,
95C515222369BAF500E4A3A8 /* NVRAM.swift in Sources */,
95C51536236B1F7700E4A3A8 /* RunAtLogin.swift in Sources */,
95550108241AD7C300618F57 /* PEFindButton.swift in Sources */,
9555010D241AD7C300618F57 /* PETableRowView.swift in Sources */,
95E68AE0235B86A1002B37A5 /* Extensions.swift in Sources */,
952EB6B024018B4500BEB0F8 /* utils.c in Sources */,
9567B29B242D95A200DAD128 /* PE_Conversions.swift in Sources */,
955500E7241AD12800618F57 /* PEFormatters.swift in Sources */,
955689DB23A2728000AD323C /* IO.swift in Sources */,
955500E1241ACCCA00618F57 /* Document.swift in Sources */,
9555AF26238EE53300108C33 /* InstallerOutline.swift in Sources */,
955BEE1723C6B49C00425AB0 /* ThemeManagerVC.swift in Sources */,
955BEE1623C6B49C00425AB0 /* ThemeManager.swift in Sources */,
@ -1141,7 +1301,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1.16;
CURRENT_PROJECT_VERSION = 1.17;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Clover/Frameworks",
@ -1166,11 +1326,13 @@
"$(inherited)",
"$(PROJECT_DIR)/Clover/Frameworks",
"$(PROJECT_DIR)/Clover/Library",
"$(PROJECT_DIR)",
);
MARKETING_VERSION = 1.16;
MARKETING_VERSION = 1.17;
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = org.slice.Clover;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_INCLUDE_PATHS = "";
SWIFT_OBJC_BRIDGING_HEADER = "Clover/Clover-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -1187,7 +1349,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1.16;
CURRENT_PROJECT_VERSION = 1.17;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Clover/Frameworks",
@ -1211,11 +1373,13 @@
"$(inherited)",
"$(PROJECT_DIR)/Clover/Frameworks",
"$(PROJECT_DIR)/Clover/Library",
"$(PROJECT_DIR)",
);
MARKETING_VERSION = 1.16;
MARKETING_VERSION = 1.17;
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = org.slice.Clover;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_INCLUDE_PATHS = "";
SWIFT_OBJC_BRIDGING_HEADER = "Clover/Clover-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};

View File

@ -14,6 +14,9 @@ let localeBundle = Bundle(path: Bundle.main.sharedSupportPath! + "/Lang.bundle")
@NSApplicationMain
final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
var canDrawConcurrently : Bool = false
var documentsPaths : [String] = [String]()
var havefinishLaunching : Bool = false
let CloverRevision : Int = Int(findCloverRevision() ?? "0") ?? 0
var isInstalling : Bool = false
var isInstallerOpen : Bool = false
@ -43,6 +46,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
self.daContext.deallocate()
}
func applicationDidBecomeActive(_ notification: Notification) {
// print("applicationDidBecomeActive")
}
func applicationWillFinishLaunching(_ notification: Notification) {
/*
@ -72,7 +79,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
let appImage = NSImage(named: "NSApplicationIcon")
self.havefinishLaunching = true
let appImage : NSImage? = NSImage(named: "NSApplicationIcon")?.copy() as? NSImage
if #available(OSX 10.10, *) {
let size = self.statusItem.button!.frame.height - 3
appImage?.size = NSMakeSize(size, size)
@ -136,6 +144,17 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
CFRunLoopRun()
}
func applicationShouldOpenUntitledFile(_ sender: NSApplication) -> Bool {
if !self.havefinishLaunching {
if let files : NSArray = UDs.value(forKey: "Docs") as? NSArray {
for f in files {
loadPlist(at: f as! String)
}
}
}
return false
}
@objc func showPopover(_ sender: Any?) {
if (self.popover == nil) {
self.popover = NSPopover()
@ -159,7 +178,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
self.popover?.show(relativeTo: v.bounds, of: v, preferredEdge: NSRectEdge.maxY)
}
}
NSApp.activate(ignoringOtherApps: true)
//NSApp.activate(ignoringOtherApps: true)
}
}
@ -168,6 +187,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
}
@objc func reFreshDisksList() {
(self.settingsWC?.contentViewController as? SettingsViewController)?.searchDisks()
(self.settingsWC?.contentViewController as? SettingsViewController)?.searchESPDisks()
(self.installerWC?.contentViewController as? InstallerViewController)?.populateTargets()
(self.installerOutWC?.contentViewController as? InstallerOutViewController)?.populateTargets()
@ -181,7 +201,5 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
func applicationWillTerminate(_ aNotification: Notification) {
}
}

View File

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15702" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15504"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15702"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@ -343,268 +342,6 @@
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
@ -677,19 +414,5 @@
</items>
<point key="canvasLocation" x="107" y="-456"/>
</menu>
<window title="Clover" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
<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="335" y="390" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1600" height="877"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="Zl5-uA-O9b"/>
</connections>
<point key="canvasLocation" x="140" y="-120"/>
</window>
</objects>
</document>

View File

@ -25,7 +25,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Check now" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="UyZ-5I-L9X">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="checkNow:" target="tSJ-3G-dnL" id="UY0-EP-xVG"/>
@ -35,7 +35,7 @@
<rect key="frame" x="14" y="206" width="311" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Update" id="58l-pb-zEc">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -45,7 +45,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Run at login" bezelStyle="regularSquare" imagePosition="left" inset="2" id="J8R-ej-fRl">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="runAtLogin:" target="tSJ-3G-dnL" id="Kfi-uU-9Tq"/>
@ -64,7 +64,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="lkK-DQ-QK6">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<menu key="menu" id="tIP-gO-fKT"/>
</popUpButtonCell>
<userDefinedRuntimeAttributes>
@ -95,7 +95,7 @@
</textFieldCell>
</textField>
<box verticalHuggingPriority="750" fixedFrame="YES" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="bsQ-GS-duU">
<rect key="frame" x="123" y="497" width="133" height="5"/>
<rect key="frame" x="150" y="497" width="106" height="5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zDK-XE-o2Z">
@ -103,7 +103,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="E23-ao-JFs">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="installClover:" target="tSJ-3G-dnL" id="9VG-7Q-ccY"/>
@ -114,7 +114,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Button" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="EJg-qm-L18">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="updateClover:" target="tSJ-3G-dnL" id="kpg-zk-tVv"/>
@ -138,7 +138,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Uninstall" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="w6z-A1-CZt">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="unInstallDaemon:" target="tSJ-3G-dnL" id="9K2-E3-t1N"/>
@ -153,7 +153,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Install" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="omz-9K-nb4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="installDaemon:" target="tSJ-3G-dnL" id="lQr-TV-nz0"/>
@ -164,7 +164,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="*Disable Sleep Proxy Client" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="kZm-NL-hOA">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="disableSleepProxy:" target="tSJ-3G-dnL" id="jia-ue-L5p"/>
@ -184,7 +184,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="*Make filesystem read-write" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="v3W-6S-CK6">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="makeRootRW:" target="tSJ-3G-dnL" id="KQZ-a6-GKv"/>
@ -226,7 +226,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="NSInfo" imagePosition="only" alignment="center" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="8hx-sN-OfR">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="goToTopic:" target="tSJ-3G-dnL" id="azU-qH-5SJ"/>
@ -237,7 +237,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="NSStopProgressFreestandingTemplate" imagePosition="only" alignment="center" focusRingType="none" imageScaling="proportionallyDown" inset="2" id="2fO-JJ-w1L">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="closeApp:" target="tSJ-3G-dnL" id="Hjx-e6-JXH"/>
@ -246,7 +246,7 @@
<tabView fixedFrame="YES" type="noTabsNoBorder" translatesAutoresizingMaskIntoConstraints="NO" id="PGs-4a-XRc" customClass="LITabView" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="0.0" y="522" width="342" height="120"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem label="Tab" identifier="" id="BAk-3x-mLp">
<view key="view" ambiguous="YES" id="ENG-5U-eLE">
@ -257,7 +257,7 @@
<rect key="frame" x="18" y="101" width="306" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Current revision" id="Re2-vf-XQ6">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -266,7 +266,7 @@
<rect key="frame" x="18" y="78" width="306" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Boot Device:" id="omZ-xs-Wjn">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -275,7 +275,7 @@
<rect key="frame" x="18" y="39" width="306" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="config path:" id="iii-3P-wo8">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -448,7 +448,7 @@
<rect key="frame" x="18" y="64" width="306" height="16"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Boot type:" id="pWw-6m-fZo">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -457,7 +457,7 @@
<rect key="frame" x="18" y="27" width="306" height="16"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="firmware:" id="kyW-hT-U5H">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -487,7 +487,7 @@
<tabView fixedFrame="YES" type="noTabsNoBorder" translatesAutoresizingMaskIntoConstraints="NO" id="vwp-hc-6yE" customClass="LITabView" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="0.0" y="358" width="342" height="113"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem identifier="Mount" id="F4n-I1-4Gq">
<view key="view" id="E05-QZ-1bf">
@ -499,7 +499,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="Hzi-CJ-xVH">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<menu key="menu" id="Vb3-8W-CMc"/>
</popUpButtonCell>
<userDefinedRuntimeAttributes>
@ -515,7 +515,7 @@
<rect key="frame" x="21" y="74" width="122" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Mount" id="s5K-bS-gZa">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -534,7 +534,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="*auto mount" bezelStyle="regularSquare" imagePosition="left" inset="2" id="tdl-nw-YFd">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="autoMount:" target="tSJ-3G-dnL" id="FNN-jm-HUm"/>
@ -545,7 +545,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="umount" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bhl-e0-nVD">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="umount:" target="tSJ-3G-dnL" id="KqE-Db-Uf1"/>
@ -563,7 +563,7 @@
<rect key="frame" x="21" y="96" width="303" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Theme:" id="7yo-8b-ZHc">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -571,8 +571,8 @@
<comboBox verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="MMH-mb-fPM">
<rect key="frame" x="23" y="65" width="302" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="NVRAM" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="kdy-IM-YSv">
<font key="font" metaFont="label" size="13"/>
<comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" placeholderString="NVRAM" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="kdy-IM-YSv">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
<objectValues>
@ -582,7 +582,7 @@
</objectValues>
</comboBoxCell>
<connections>
<action selector="themeBoxSelected:" target="tSJ-3G-dnL" id="76K-Tu-HPV"/>
<outlet property="delegate" destination="tSJ-3G-dnL" id="kY4-3y-8Vx"/>
</connections>
</comboBox>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AzG-cL-pFW">
@ -590,7 +590,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Manager" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="I4v-1L-brN">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="openThemeManager:" target="tSJ-3G-dnL" id="PE4-2e-cP2"/>
@ -651,7 +651,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="hlk-UE-ZPw" id="7ZL-JE-fT7">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<menu key="menu" id="zSa-aR-ghV">
<items>
<menuItem title="Item 1" state="on" id="hlk-UE-ZPw"/>
@ -732,16 +732,94 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5qs-Bm-r0y">
<rect key="frame" x="18" y="53" width="143" height="32"/>
<rect key="frame" x="171" y="84" width="157" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="clover-genconfig" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="135-sE-mfh">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="generateConfig:" target="tSJ-3G-dnL" id="5ip-Lq-aa3"/>
</connections>
</button>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ooY-tQ-8dD">
<rect key="frame" x="22" y="5" width="303" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="KS1-1T-fcC" id="lzk-kj-5l5">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<menu key="menu" id="m7p-bq-UM3">
<items>
<menuItem title="Item 1" state="on" id="KS1-1T-fcC"/>
<menuItem title="Item 2" id="RWc-UL-7IK"/>
<menuItem title="Item 3" id="iXP-Hh-Bj7"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="openCloverPlist:" target="tSJ-3G-dnL" id="4Y7-lE-cSg"/>
</connections>
</popUpButton>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2zc-8N-Nsb">
<rect key="frame" x="22" y="30" width="303" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="B7A-dh-kcd" id="XFT-Gr-fJX">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<menu key="menu" id="Jkx-eL-cSD">
<items>
<menuItem title="Item 1" state="on" id="B7A-dh-kcd"/>
<menuItem title="Item 2" id="L2p-wI-PSk"/>
<menuItem title="Item 3" id="SAz-SB-gPB"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="volumePopUpPressed:" target="tSJ-3G-dnL" id="2pa-Wc-SqV"/>
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tzT-4o-NKo">
<rect key="frame" x="24" y="94" width="133" height="16"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Property List (plist)" id="SPn-Ok-F68">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VMb-q0-gGi">
<rect key="frame" x="203" y="54" width="125" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="New" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="7pm-6a-6q8">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="createNewPlist:" target="tSJ-3G-dnL" id="3Pe-8z-jrp"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="08m-oF-xfG">
<rect key="frame" x="171" y="54" width="40" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="No7-07-kb3">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="searchPanelForPlist:" target="tSJ-3G-dnL" id="vOr-fo-bKY"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6kD-FR-cDL">
<rect key="frame" x="24" y="63" width="133" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="auto save" bezelStyle="regularSquare" imagePosition="left" lineBreakMode="truncatingTail" inset="2" id="zbE-mt-ZUk">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="autosaveDocuments:" target="tSJ-3G-dnL" id="yWo-19-eVf"/>
</connections>
</button>
</subviews>
</view>
</tabViewItem>
@ -751,7 +829,7 @@
<rect key="frame" x="14" y="477" width="137" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="dvq-Ib-bsz">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<segments>
<segment width="32"/>
<segment width="32" selected="YES" tag="1"/>
@ -783,6 +861,7 @@
<connections>
<outlet property="appVersionField" destination="V27-tg-6O6" id="pxR-3a-E0x"/>
<outlet property="autoMountButton" destination="l6m-qm-d70" id="0qW-yE-Mqu"/>
<outlet property="autoSaveButton" destination="6kD-FR-cDL" id="mv9-Gm-Kj8"/>
<outlet property="boardIdField" destination="MXF-sZ-EBb" id="vbJ-VJ-QcR"/>
<outlet property="bootDeviceField" destination="tPD-gh-zbP" id="vYU-oa-H3q"/>
<outlet property="bootTypeField" destination="00r-9S-unY" id="ukc-fU-Jdl"/>
@ -791,6 +870,7 @@
<outlet property="configPathField" destination="K34-kh-yUP" id="zkK-f3-0uj"/>
<outlet property="currentRevField" destination="f5t-Ia-85K" id="D7C-Ny-Ehi"/>
<outlet property="disbaleSleepProxyButton" destination="wDl-KZ-OP0" id="neq-3a-Zq9"/>
<outlet property="disksPEPopUp" destination="2zc-8N-Nsb" id="BPe-W1-rI5"/>
<outlet property="disksPopUp" destination="mcY-kx-lap" id="Ie2-4t-td5"/>
<outlet property="firmwareVendorfield" destination="88K-Sj-rjo" id="aQy-Dd-8G5"/>
<outlet property="infoButton" destination="f2Z-bB-hZf" id="pBL-T9-zoS"/>
@ -800,10 +880,13 @@
<outlet property="makeRootRWButton" destination="y9x-U8-ayg" id="MWS-t0-efx"/>
<outlet property="modelField" destination="SQN-cD-fjL" id="HaD-SS-j8b"/>
<outlet property="nativeNVRAMField" destination="SjP-er-6Hp" id="7W6-DR-kpm"/>
<outlet property="newPlistButton" destination="VMb-q0-gGi" id="3rg-AE-yd6"/>
<outlet property="oemBoardIdField" destination="GcV-id-YTZ" id="yvt-rO-i17"/>
<outlet property="oemProductField" destination="Aad-0P-4R6" id="O85-Y0-ZXY"/>
<outlet property="oemVendorField" destination="84n-SC-fET" id="UbL-NZ-faa"/>
<outlet property="openDocumentButton" destination="08m-oF-xfG" id="mLu-Fp-IAA"/>
<outlet property="openThemeButton" destination="AzG-cL-pFW" id="U9N-0u-T2f"/>
<outlet property="plistsPopUp" destination="ooY-tQ-8dD" id="PrN-EH-dkp"/>
<outlet property="progressBar" destination="DtR-XE-DfU" id="7sf-c8-y5u"/>
<outlet property="runAtLoginButton" destination="g5h-bF-3le" id="tXL-6T-4Mj"/>
<outlet property="snField" destination="0u3-c1-rfb" id="TUb-GS-mTb"/>

View File

@ -18,8 +18,9 @@
#define DEBUG DEBUG_BACKUP // restore original
#endif
*/
#import "NSWindowFix.h"
#import "PNG8Image.h"
#import "ThemeImage.h"
#import "gfxutil.h"
#import "efidevp.h"
@ -288,9 +289,7 @@ struct CUSTOM_TOOL_ENTRY {
UINT8 VolumeType;
};
/**
Set of Search & replace bytes for VideoBiosPatchBytes().
**/
// Set of Search & replace bytes for VideoBiosPatchBytes().
typedef struct _VBIOS_PATCH_BYTES {
VOID *Find;
VOID *Replace;
@ -644,3 +643,4 @@ typedef struct {
DEV_PROPERTY *ArbProperties;
} SETTINGS_DATA;

View File

@ -285,7 +285,9 @@ func getVolumes() -> [String] {
for b in all {
let bsd : String = b as! String
if let mp = getMountPoint(from: bsd) {
mounted.append(mp)
if mp != "/private/var/vm" {
mounted.append(mp)
}
}
}
return mounted

View File

@ -9,6 +9,68 @@
import Cocoa
extension String {
/// Create `Data` from hexadecimal string representation
///
/// This takes a hexadecimal representation and creates a `Data` object. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed.
///
/// - returns: Data represented by this hexadecimal string.
func hexadecimal() -> Data? {
var data = Data(capacity: self.count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, range: NSMakeRange(0, utf16.count)) { match, flags, stop in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)!
data.append(&num, count: 1)
}
guard data.count > 0 else { return nil }
return data
}
/// - returns: Bool value indicating that our string start with the same prefix ignoring casing.
func hasPrefixIgnoringCase(_ str: String) -> Bool {
return self.lowercased().hasPrefix(str.lowercased())
}
/// - returns: only digits contained from the given string
var keepNumericsOnly: String {
return self.components(separatedBy: CharacterSet(charactersIn: "0123456789").inverted).joined(separator: "")
}
//: ### Base64 encoding a string
func base64Encoded() -> String? {
if let data = self.data(using: .utf8) {
return data.base64EncodedString()
}
return nil
}
//: ### Base64 decoding a string
func base64Decoded() -> String? {
if let data = Data(base64Encoded: self) {
return String(data: data, encoding: .utf8)
}
return nil
}
//: ### Base64 encoding data
func base64EncodedHex() -> String? {
if let data = self.hexadecimal() {
return data.base64EncodedString()
}
return nil
}
//: ### Base64 decoding data
func base64DecodedHex() -> String? {
if let data = Data(base64Encoded: self, options: Data.Base64DecodingOptions.ignoreUnknownCharacters) {
return data.hexadecimal()
}
return nil
}
public func noSpaces() -> String {
return self.trimmingCharacters(in: CharacterSet.whitespaces)
@ -17,27 +79,60 @@ extension String {
var lastPath: String {
return (self as NSString).lastPathComponent
}
var fileExtension: String {
return (self as NSString).pathExtension
}
var deletingLastPath: String {
return (self as NSString).deletingLastPathComponent
}
var deletingFileExtension: String {
return (self as NSString).deletingPathExtension
}
var componentsPath: [String] {
return (self as NSString).pathComponents
}
func addPath(_ path: String) -> String {
let nsSt = self as NSString
return nsSt.appendingPathComponent(path)
}
func appendingFileExtension(ext: String) -> String? {
let nsSt = self as NSString
return nsSt.appendingPathExtension(ext)
}
/// Replace any occurences of a string (word) with a new one (newWord)
///
/// - returns: String where word search is case insensitive while newWord replacing is case sensitive..
func replacingOccurrencesOf(inSensitive word: String, withSensitive newWord: String) -> String {
//print("word = \(word)")
//print("newWord = \(newWord)")
var newText = self
if let range = newText.lowercased().range(of: word.lowercased()) {
newText = newText.replacingOccurrences(of: word,
with: newWord,
options: String.CompareOptions.caseInsensitive,
range: range)
}
// check again
let r = newText.lowercased().range(of: word.lowercased())
if (r != nil) {
newText = newText.replacingOccurrencesOf(inSensitive: word, withSensitive: newWord)
}
return newText
}
// NSUserInterfaceItemIdentifier
func interfaceId() -> NSUserInterfaceItemIdentifier {
return NSUserInterfaceItemIdentifier(self)
}
// https://stackoverflow.com/questions/25554986/how-to-convert-string-to-unsafepointeruint8-and-length#
func toPointer() -> UnsafePointer<UInt8>? {
guard let data = self.data(using: String.Encoding.utf8) else { return nil }
@ -59,6 +154,50 @@ extension String {
return UnsafePointer<UInt8>(buffer)
}
/// Escape XML special characthers such:
/// & as &amp, \ as &quot, ' as &apos, < as &lt, and > as &gt
var escapingXMLCharacters: String {
get {
/*
" &quot;
' &apos;
< &lt;
> &gt;
& &amp;
*/
var s = self
s = s.replacingOccurrences(of: "&", with: "&amp;")
s = s.replacingOccurrences(of: "\"", with: "&quot;")
s = s.replacingOccurrences(of: "'", with: "&apos;")
s = s.replacingOccurrences(of: "<", with: "&lt;")
s = s.replacingOccurrences(of: ">", with: "&gt;")
return s
}
}
/// Convert XML characters such:
/// &amp to &, &quot to \ , ' to &apos, &lt to <, and &gt to >
var convertingXMLCharacters: String {
get {
/*
" &quot;
' &apos;
< &lt;
> &gt;
& &amp;
*/
var s = self
s = s.replacingOccurrences(of: "&amp;", with: "&")
s = s.replacingOccurrences(of: "&quot;", with: "\"")
s = s.replacingOccurrences(of: "&apos;", with: "'")
s = s.replacingOccurrences(of: "&lt;", with: "<")
s = s.replacingOccurrences(of: "&gt;", with: ">")
return s
}
}
}
extension NSNumber {
@ -99,6 +238,23 @@ extension UInt32 {
}
extension Data {
/// Create hexadecimal string representation of `Data` object.
///
/// - returns: `String` representation of this `Data` object.
func hexadecimal() -> String {
return map { String(format: "%02x", $0) }
.joined(separator: "")
}
func castToCPointer<T>() -> T {
let mem = UnsafeMutablePointer<T>.allocate(capacity: 1)
_ = self.copyBytes(to: UnsafeMutableBufferPointer(start: mem, count: 1))
let val = mem.move()
mem.deallocate()
return val
}
func toPointer() -> UnsafePointer<UInt8>? {
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: self.count)
@ -225,3 +381,23 @@ extension io_object_t {
extension NSBitmapImageRep {
var png: Data? { representation(using: .png, properties: [:]) }
}
extension NSView {
func removeAllConstraints() {
self.removeConstraints(self.constraints)
for view in self.subviews {
view.removeAllConstraints()
}
}
@IBInspectable var cornerRadius: CGFloat {
get {
return self.layer?.cornerRadius ?? 0
} set {
self.wantsLayer = true
self.layer?.masksToBounds = true
self.layer?.cornerRadius = CGFloat(Int(newValue * 100)) / 100
}
}
}

View File

@ -4,10 +4,67 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>plist</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>GenericDocumentIcon.icns</string>
<key>CFBundleTypeName</key>
<string>XML PropertyList v1</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>com.apple.property-list</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).Document</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>plist</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>GenericDocumentIcon.icns</string>
<key>CFBundleTypeName</key>
<string>Binary PropertyList v1</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>com.apple.property-list</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).Document</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>xml</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>GenericDocumentIcon.icns</string>
<key>CFBundleTypeName</key>
<string>XML</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>public.xml</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).Document</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
@ -20,6 +77,8 @@
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key>

View File

@ -16,7 +16,10 @@ extension String {
if preferred.count > 0 {
table = preferred[0]
}
if localeBundle == nil {
print("localeBundle is nil")
return self
}
var result = localeBundle?.localizedString(forKey: self, value: nil, table: table)
if result == self {

View File

@ -19,6 +19,7 @@
}
return [self canBecomeKeyWindow];
*/
return YES;
}
#pragma clang diagnostic pop

View File

@ -1,107 +0,0 @@
//
// PNG8Image.m
// Clover
//
// Created by vector sigma on 07/03/2020.
// Copyright © 2020 CloverHackyColor. All rights reserved.
//
#import "PNG8Image.h"
@implementation PNG8Image
- (nullable NSData *)png8ImageDataAtPath:(NSString *_Nonnull)imagePath
error:(NSError *_Nullable*_Nullable)errorPtr {
NSString *domain = @"org.slice.Clover.PNG8Image.Error";
// Load PNG file and decode it as raw RGBA pixels
// This uses lodepng library for PNG reading (not part of libimagequant)
const char *input_png_file_path = [imagePath UTF8String];
unsigned int width, height;
unsigned char *raw_rgba_pixels;
unsigned int status = lodepng_decode32_file(&raw_rgba_pixels, &width, &height, input_png_file_path);
if (status) {
NSString *desc = [NSString stringWithFormat:@"Can't load %s: %s\n",
input_png_file_path,
lodepng_error_text(status)];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:1
userInfo:userInfo];
return nil;
}
// Use libimagequant to make a palette for the RGBA pixels
liq_attr *handle = liq_attr_create();
liq_image *input_image = liq_image_create_rgba(handle, raw_rgba_pixels, width, height, 0);
// You could set more options here, like liq_set_quality
liq_result *quantization_result;
if (liq_image_quantize(input_image, handle, &quantization_result) != LIQ_OK) {
NSString *desc = @"Quantization failed\n";
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:2
userInfo:userInfo];
return nil;
}
// Use libimagequant to make new image pixels from the palette
size_t pixels_size = width * height;
unsigned char *raw_8bit_pixels = malloc(pixels_size);
liq_set_dithering_level(quantization_result, 1.0);
liq_write_remapped_image(quantization_result, input_image, raw_8bit_pixels, pixels_size);
const liq_palette *palette = liq_get_palette(quantization_result);
// Save converted pixels as a PNG file
// This uses lodepng library for PNG writing (not part of libimagequant)
LodePNGState state;
lodepng_state_init(&state);
state.info_raw.colortype = LCT_PALETTE;
state.info_raw.bitdepth = 8;
state.info_png.color.colortype = LCT_PALETTE;
state.info_png.color.bitdepth = 8;
for(int i=0; i < palette->count; i++) {
lodepng_palette_add(&state.info_png.color, palette->entries[i].r, palette->entries[i].g, palette->entries[i].b, palette->entries[i].a);
lodepng_palette_add(&state.info_raw, palette->entries[i].r, palette->entries[i].g, palette->entries[i].b, palette->entries[i].a);
}
unsigned char *output_file_data;
size_t output_file_size;
unsigned int out_status = lodepng_encode(&output_file_data, &output_file_size, raw_8bit_pixels, width, height, &state);
if (out_status) {
NSString *desc = [NSString stringWithFormat:@"Can't encode image: %s\n",
lodepng_error_text(out_status)];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:3
userInfo:userInfo];
return nil;
}
NSData *data = [NSData dataWithBytes: output_file_data length: output_file_size];
NSImage *convertedImage = [[NSImage alloc] initWithData:data];
if (convertedImage == nil) {
NSString *desc = @"Can't convert data to NSImage\n";
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:4
userInfo:userInfo];
return nil;
}
liq_result_destroy(quantization_result); // Must be freed only after you're done using the palette
liq_image_destroy(input_image);
liq_attr_destroy(handle);
free(raw_8bit_pixels);
lodepng_state_cleanup(&state);
return data;
}
@end

View File

@ -0,0 +1,148 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
@available(OSX 10.11, *)
final class Document: NSDocument {
var data : Data?
var windowController : PlistEditorWC?
var contentViewController: PlistEditorVC!
override init() {
super.init()
self.undoManager?.disableUndoRegistration()
}
override class var autosavesInPlace: Bool {
return UDs.bool(forKey: kAutoSavePlistsKey)
}
override func canAsynchronouslyWrite(to url: URL,
ofType typeName: String,
for saveOperation: NSDocument.SaveOperationType) -> Bool {
return true
}
override class func canConcurrentlyReadDocuments(ofType: String) -> Bool {
return (ofType == "XML PropertyList v1" ||
ofType == "Binary PropertyList v1" ||
ofType == "XML")
}
override func removeWindowController(_ windowController: NSWindowController) {
super.removeWindowController(windowController)
let documents = NSDocumentController.shared.documents
if documents.count == 1 {
NSApp.setActivationPolicy(.accessory)
}
}
override func makeWindowControllers() {
var parser : PlistParser?
if self.data != nil && self.fileType != nil { // loading a file
parser = PlistParser(fromData: self.data!, fileType: PEFileType(rawValue: self.fileType!)!)
if (parser?.root == nil) {
Swift.print("Document " + ((self.fileURL?.absoluteString) ?? "Untitled".locale) + " was ureadable")
parser = nil // will beep
}
} else if self.data == nil && self.fileType != nil { // loading an empty document
// Empty document??
parser = PlistParser(fromPath: "")
}
if parser != nil {
if parser!.isBinary {
self.fileType = PEFileType.binaryPlistv1.rawValue
}
self.windowController = PlistEditorWC.loadFromNib(parser: parser!)
addWindowController(self.windowController!)
if (self.fileURL != nil) {
self.windowController?.window?.representedFilename = (self.fileURL?.path)!
self.windowController?.window?.setFrameAutosaveName(NSWindow.FrameAutosaveName((self.fileURL?.path)!))
}
if !NSDocumentController.shared.documents.contains(self) {
NSDocumentController.shared.addDocument(self)
}
if let vc = self.windowController?.contentViewController as? PlistEditorVC {
contentViewController = vc
vc.rootNode = parser?.root
}
} else {
NSSound.beep()
}
}
override func write(to url: URL, ofType typeName: String) throws {
if let vc = self.windowController?.contentViewController as? PlistEditorVC {
switch typeName {
case PEFileType.xmlPlistv1.rawValue:
do {
try vc.save().write(to: url, options: [])
} catch {
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
break
case PEFileType.xml.rawValue:
do {
try vc.save().write(to: url, options: [])
} catch {
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
break
case PEFileType.binaryPlistv1.rawValue:
let data = vc.convertToBinaryPlist()
if (data == nil) {
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
do {
try data?.write(to: url, options: [])
} catch {
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
break
default:
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
}
}
override func read(from data: Data, ofType typeName: String) throws {
if typeName == PEFileType.xmlPlistv1.rawValue ||
typeName == PEFileType.binaryPlistv1.rawValue ||
typeName == PEFileType.xml.rawValue {
self.data = data
} else {
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
}
}

View File

@ -0,0 +1,248 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
//MARK: UTC Date formatters
func utcDateFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.timeZone = TimeZone(abbreviation: "UTC")
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
return formatter
}
func utcStringToDate(_ str: String) -> Date {
return utcDateFormatter().date(from: str)!
}
func utcDateToString(_ str: Date) -> String {
return utcDateFormatter().string(from: str)
}
//MARK: localized Date formatters to return current timezone and custom date style
func localizedDateFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.locale = Locale.current
formatter.dateStyle = .medium
formatter.timeStyle = .medium
return formatter
}
func localizedStringToDate(_ str: String) -> Date {
return localizedDateFormatter().date(from: str)!
}
func localizedStringToDateS(_ str: String) -> Date? { /* safe version that can safely be nil */
return localizedDateFormatter().date(from: str)
}
func localizedDateToString(_ date: Date) -> String {
return localizedDateFormatter().string(from: date)
}
func funcyDateFromUser(_ str: String) -> Date? {
let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
let result: NSTextCheckingResult? = detector?.firstMatch(in: str, options: [], range: NSRange(location: 0, length: str.count))
if result?.resultType == .date {
let date: Date? = result?.date
if date != nil {
return date!
}
}
return nil
}
//MARK: Number formatters
func localizedNumberFormatter() -> NumberFormatter {
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
//formatter.groupingSeparator = formatter.locale.groupingSeparator
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 8
return formatter
}
func localizedStringFrom(number: NSNumber) -> String {
return localizedNumberFormatter().string(from: number)!
}
/*
what will goes in the Outlineview cell is a localized string that use custom decimal/thousand separator
*/
func numberFromLocalizedString(string: String) -> NSNumber {
// check if is an hex string
if string.hasPrefix("0x") || string.hasPrefix("0X") {
//get string after 0x
let comp = string.lowercased().components(separatedBy: "0x")
if comp.count == 2 && stringContainsOnlyHexChars(string: comp[1]) {
let scanner = Scanner(string: string)
var value: UInt64 = 0
if scanner.scanHexInt64(&value) {
return NSNumber(value: value)
}
}
}
let nf = localizedNumberFormatter()
let separator = nf.decimalSeparator
var isNegative : Bool = false
var str : String = string
if str.count == 0 {
return NSNumber(value: 0) // length is 0 .. number is 0
}
// check if is a negative number
if str.hasPrefix("-") {
isNegative = true
}
// if the first char is the decimal separator insert a 0 (e.g. ",2" to "0,2")
if str.hasPrefix(separator!) {
str = "0" + str
}
// clean the string from non numerical part (but keep the decimal separator)
let setToKeep = CharacterSet.init(charactersIn: "0123456789" + separator!).inverted
str = str.components(separatedBy: setToKeep).joined()
// check the lenght again..
if str.count == 0 {
return NSNumber(value: 0)
}
// remove the separator if is the last char
if str.hasSuffix(separator!) {
str = String(str.dropLast())
}
// check the lenght again..
if str.count == 0 {
return NSNumber(value: 0)
}
/*
check if we have 0 or at least one separator
(if more than one truncate before the second!)
*/
if (str.range(of: separator!) != nil) {
let arr = str.components(separatedBy: separator!)
if arr.count > 1 {
str = arr[0] + "." + arr[1]
}
}
// readd the subtraction operator if was there
if str != "0" && isNegative {
str = "-" + str
}
//determine if is integer or double
var num : NSNumber? = nil
if (str.range(of: ".") != nil) {
num = NSNumber(value: (str as NSString).doubleValue)
} else {
num = NSNumber(value: (str as NSString).integerValue)
}
if (num != nil) {
return num!
}
return NSNumber(value: 0)
}
//MARK: Data to string/string to Data
/*
We expect the data enclosed in "<>", must have valid hex characters,
must be multiple of 2
*/
func isHexStringValid(string: String) -> String {
var hex = string.lowercased()
if hex.count == 0 {
return DataEmptyString
}
let hexSet : CharacterSet = CharacterSet(charactersIn: "0123456789abcdef")
// remove blank spaces
hex = hex.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
hex = hex.replacingOccurrences(of: " ", with: "")
if hex.count == 0 {
return DataEmptyString
}
// first char must be '<'
if !hex.hasPrefix("<") {
return DataMissingOpenTag
}
// well, is still >=2? (like "<>")
if hex.count < 2 {
return DataMissingEndTag
}
// last char must be '>'
if !hex.hasSuffix(">") {
return DataMissingEndTag
}
// contains only valid hex characters? testw/o "<>"
var copy : String = String(hex.dropFirst(1))
copy = String(copy.dropLast(1))
if copy.trimmingCharacters(in: hexSet).count > 0 {
return DataIllegalHex
}
if copy.count % 2 != 0 { // ----> w/o considering "<>"
return DataOddBytes
}
return "HexSuccess"
}
func stringContainsOnlyHexChars(string: String) -> Bool {
var hex = string.lowercased()
if hex.count == 0 {
return false
}
let hexSet : CharacterSet = CharacterSet(charactersIn: "0123456789abcdef")
// remove blanck spaces
hex = hex.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
if hex.count == 0 {
return false
}
// contains only valid hex characters?
if (hex.rangeOfCharacter(from: hexSet) == nil) {
return false
}
return true
}

View File

@ -0,0 +1,108 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
final class PENode: NSTreeNode, NSCopying, NSCoding {
var pattern : String? = nil // -> this isn't copied with .copy()
internal var ro: Any?
var isRootNode : Bool? = nil
var highLightPattern: String? {
get {
return self.pattern
}
set {
self.pattern = newValue
}
}
var count: Int {
get {
return (self.children == nil) ? 0 : self.children!.count
}
}
override var representedObject: Any? {
get {
return self.ro
} set {
self.ro = newValue
}
}
var isExpandable: Bool {
get {
return (self.tagdata?.type == .Dictionary || self.tagdata?.type == .Array)
}
}
weak var peparent: PENode? {
get {
return self.parent as? PENode
}
}
var tagdata: TagData? {
get {
return (self.representedObject as! TagData)
} set {
self.representedObject = newValue
}
}
required convenience init(coder decoder: NSCoder) {
self.init(representedObject:decoder.decodeObject(forKey: "TagData"))
self.mutableChildren.addObjects(from: decoder.decodeObject(forKey: "childrens") as! [Any])
}
func encode(with coder: NSCoder) {
coder.encode(self.representedObject, forKey: "TagData")
coder.encode(self.mutableChildren, forKey: "childrens")
}
required override init(representedObject modelObject: Any?) {
self.ro = modelObject
super.init(representedObject: self.ro)
}
func copy(with zone: NSZone? = nil) -> Any {
let nodeCopy = PENode(representedObject: self.tagdata!.copy())
self.reAddChildToParent(newNode: nodeCopy, origNode: self)
return nodeCopy
}
private func reAddChildToParent(newNode: PENode, origNode: PENode) {
for item in origNode.mutableChildren {
let origChild = item as! PENode
let nodeCopy = PENode(representedObject: origChild.tagdata!.copy())
newNode.mutableChildren.add(nodeCopy)
self.reAddChildToParent(newNode: nodeCopy, origNode: origChild)
}
}
}

View File

@ -0,0 +1,77 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Foundation
//MARK: Plist Editor custom types
typealias PEArray = [Any]
typealias PEDictionary = [String : Any]
typealias PEInt = Int
typealias PEReal = Double
//MARK: Plist Editor file types
enum PEFileType : String {
case xmlPlistv1 = "XML PropertyList v1"
case binaryPlistv1 = "Binary PropertyList v1"
case xml = "XML"
}
//MARK: supported plist object type
enum PlistTag : Int {
case Dictionary = 0
case Array = 1
case String = 2
case Number = 3
case Bool = 4
case Date = 5
case Data = 6
}
//MARK: PETableCellView behaviour
enum PETableCellViewType: Int {
case key
case value
case tags
case bool
}
//MARK: Autosave
let kAutoSavePlistsKey : String = "autoSavePlists"
//MARK: Paste board types
let kMyPBoardTypeXml = NSPasteboard.PasteboardType(rawValue: "public.xml")
let kMyPBoardTypeData = NSPasteboard.PasteboardType(rawValue: "PBoardType.data")
//MARK: Header and footer
let xml1Header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">"
let xml1Footer = "</plist>"
let binaryPlistHeader = "bplist00"

View File

@ -0,0 +1,299 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
@available(OSX 10.11, *)
extension PlistEditorVC {
@objc func boolPopUpPressed(sender: PEPopUpButton) {
let originalValue : Bool = sender.node!.tagdata!.value as! Bool
let actualValue = (sender.title == localizedYes) ? true : false
if originalValue != actualValue {
self.outline.undo_SetBoolValue(node: sender.node!,
sender: sender,
originalValue: originalValue,
actualValue: actualValue)
}
}
@objc func typePopUpPressed(sender: PEPopUpButton) {
let originalType = sender.node!.tagdata!.type
let actualType = sender.selectedItem?.representedObject as! PlistTag
if originalType != actualType {
let newTree : TagData? = sender.node?.tagdata?.copy() as? TagData
newTree?.type = actualType
/*
switching between contenitors types (Array/Dictionary) can save some memory
since we need don't need to backup the childrens.
Reloading the view it's enough to show children correctly
*/
switch originalType {
case .Dictionary:
if actualType == .Array {
var oldKeys : PEArray = PEArray()
var newKeys : PEArray = PEArray()
var index : Int = 0
for n in sender.node!.mutableChildren {
let ex : PENode = n as! PENode
oldKeys.append(ex.tagdata!.key)
newKeys.append("\(localizedItem) \(index)")
index+=1
}
self.outline.undo_ChangeContenitor(node: sender.node!,
sender: sender,
originalType: gPlistTagStr(tag: originalType),
actualType: gPlistTagStr(tag: actualType),
oldKeys: oldKeys,
newKeys: newKeys)
} else {
if actualType == .String {
newTree?.value = ""
} else if actualType == .Number {
newTree?.value = 0
} else if actualType == .Bool {
newTree?.value = false
} else if actualType == .Data {
newTree?.value = Data()
} else if actualType == .Date {
newTree?.value = Date()
}
self.outline.undo_ChangeType(node: sender.node!,
newData: newTree!,
oldData: sender.node!.tagdata!,
newChilds: nil,
oldChilds: sender.node!.children,
sender: sender)
}
break
case .Array:
if actualType == .Dictionary {
var oldKeys : PEArray = PEArray()
var newKeys : PEArray = PEArray()
var index : Int = 0
for n in sender.node!.mutableChildren {
let _ : PENode = n as! PENode
oldKeys.append("\(localizedItem) \(index)")
if index == 0 {
newKeys.append(localizedNewItem)
} else {
newKeys.append("\(localizedNewItem) - \(index)")
}
index+=1
}
self.outline.undo_ChangeContenitor(node: sender.node!,
sender: sender,
originalType: gPlistTagStr(tag: originalType),
actualType: gPlistTagStr(tag: actualType),
oldKeys: oldKeys,
newKeys: newKeys)
} else {
if actualType == .String {
newTree?.value = ""
} else if actualType == .Number {
newTree?.value = 0
} else if actualType == .Bool {
newTree?.value = false
} else if actualType == .Data {
newTree?.value = Data()
} else if actualType == .Date {
newTree?.value = Date()
}
self.outline.undo_ChangeType(node: sender.node!,
newData: newTree!,
oldData: sender.node!.tagdata!,
newChilds: nil,
oldChilds: sender.node!.children,
sender: sender)
}
break
case .String:
let oldString = sender.node?.tagdata?.value as! String
if actualType == .Number {
let nv = localizedNumberFormatter().number(from: oldString) ?? 0
newTree?.value = nv
} else if actualType == .Bool {
// check if old string is compatible with a bool value
var bv : Bool = false
let localized = localizedYes
let firstChar = localized[localized.startIndex]
if oldString.hasPrefix(String(firstChar).lowercased())
|| oldString.hasPrefix(String(firstChar).uppercased()) {
bv = true
}
newTree?.value = bv
} else if actualType == .Data {
// da migliorare (se la stringa era un hex valido usare quella!!)
// check if old string is compatible with bytes
var dv : Data? = nil
if isHexStringValid(string: oldString) == "HexSuccess" {
dv = oldString.hexadecimal()
}
if (dv == nil) {
dv = oldString.data(using: String.Encoding.utf8)
}
if (dv == nil) {
dv = Data()
}
newTree?.value = dv!
} else if actualType == .Date {
// check if old string is compatible with date
var dv : Date? = nil
dv = localizedStringToDateS(oldString) // first try with "S" version that can be nil
if (dv == nil) {
dv = funcyDateFromUser(oldString) // secondly try to detect it (NSDataDetector + NSTextCheckingResult)
}
if (dv == nil) {
dv = Date() // no way, init a new one..
}
newTree?.value = dv!
} else if actualType == .Dictionary {
newTree?.value = nil
} else if actualType == .Array {
newTree?.value = nil
}
self.outline.undo_ChangeType(node: sender.node!,
newData: newTree!,
oldData: sender.node!.tagdata!,
newChilds: nil,
oldChilds: sender.node!.children,
sender: sender)
break
case .Bool:
if actualType == .String {
var ns : String = "0"
if (sender.node?.tagdata?.value as! NSNumber).boolValue {
ns = "1"
}
newTree?.value = ns
} else if actualType == .Number {
var nn : Int = 0
if (sender.node?.tagdata?.value as! NSNumber).boolValue {
nn = 1
}
newTree?.value = nn
} else if actualType == .Data {
newTree?.value = Data()
} else if actualType == .Date {
newTree?.value = Date()
} else if actualType == .Dictionary {
newTree?.value = nil
} else if actualType == .Array {
newTree?.value = nil
}
self.outline.undo_ChangeType(node: sender.node!,
newData: newTree!,
oldData: sender.node!.tagdata!,
newChilds: nil,
oldChilds: sender.node!.children,
sender: sender)
break
case .Number:
if actualType == .String {
let str = localizedNumberFormatter().string(from: sender.node!.tagdata!.value as! NSNumber)
newTree?.value = str ?? ""
} else if actualType == .Bool {
var nb : Bool = false
if (sender.node?.tagdata?.value as! NSNumber).boolValue {
nb = true
}
newTree?.value = nb
} else if actualType == .Data {
newTree?.value = Data()
} else if actualType == .Date {
newTree?.value = Date()
} else if actualType == .Dictionary {
newTree?.value = nil
} else if actualType == .Array {
newTree?.value = nil
}
self.outline.undo_ChangeType(node: sender.node!,
newData: newTree!,
oldData: sender.node!.tagdata!,
newChilds: nil,
oldChilds: sender.node!.children,
sender: sender)
break
case .Data:
if actualType == .String {
newTree?.value = "\(sender.node!.tagdata!.value!)"
} else if actualType == .Bool {
newTree?.value = false
} else if actualType == .Number {
newTree?.value = 0
} else if actualType == .Date {
newTree?.value = Date()
} else if actualType == .Dictionary {
newTree?.value = nil
} else if actualType == .Array {
newTree?.value = nil
}
self.outline.undo_ChangeType(node: sender.node!,
newData: newTree!,
oldData: sender.node!.tagdata!,
newChilds: nil,
oldChilds: sender.node!.children,
sender: sender)
break
case .Date:
if actualType == .String {
let dateStr = localizedDateToString(sender.node!.tagdata!.value as! Date)
newTree?.value = dateStr
} else if actualType == .Bool {
newTree?.value = false
} else if actualType == .Number {
newTree?.value = 0
} else if actualType == .Data {
newTree?.value = Data()
} else if actualType == .Dictionary {
newTree?.value = nil
} else if actualType == .Array {
newTree?.value = nil
}
self.outline.undo_ChangeType(node: sender.node!,
newData: newTree!,
oldData: sender.node!.tagdata!,
newChilds: nil,
oldChilds: sender.node!.children,
sender: sender)
break
}
}
}
}

View File

@ -0,0 +1,210 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Foundation
//MARK: PENode to Plist
///Conver the given PENode to a XML Property List v1
func gConvertPENodeToPlist(node: PENode?) -> String {
var plist : String = ""
if (node == nil) {
return plist
}
let ro = node!.tagdata!
let type = node!.tagdata!.type
if type == .Dictionary {
if node!.count == 0 {
plist = xml1Header + "\n" + "<dict/>" + "\n" + xml1Footer
} else {
plist = xml1Header + "\n" + gSerializeNodeToPlist(node: node!, file: "", indentation: 0) + xml1Footer
}
} else if type == .Array {
if node!.count == 0 {
plist = xml1Header + "\n" + "<array/>" + "\n" + xml1Footer
} else {
plist = xml1Header + "\n" + gSerializeNodeToPlist(node: node!, file: "", indentation: 0) + xml1Footer
}
} else if type == .String {
plist = xml1Header + "\n" + "<string>" + (ro.value as! String) + "</string>" + "\n" + xml1Footer
} else if type == .Data {
let data = ro.value as! NSData
let strBase64 : String = (data as Data).base64EncodedString(options: .endLineWithLineFeed)
plist = xml1Header + "\n" + "<data>" + strBase64 + "</data>" + "\n" + xml1Footer
} else if type == .Date {
plist = xml1Header + "\n" + "<date>" + utcDateToString(ro.value as! Date) + "</date>" + "\n" + xml1Footer
} else if type == .Number {
let strNum = "\(node!.tagdata!.value!)"
if (strNum as NSString).range(of: ".").location != NSNotFound {
plist = xml1Header + "\n" + "<real>" + strNum + "</real>" + "\n" + xml1Footer
} else {
plist = xml1Header + "\n" + "<integer>" + strNum + "</integer>" + "\n" + xml1Footer
}
} else if type == .Bool {
let n = node!.tagdata!.value as! NSNumber
plist = xml1Header + "\n" + (n.boolValue ? "<true/>" : "<false/>") + "\n" + xml1Footer
}
return plist
}
///This func is meant to be called only by the gConvertPENodeToPlist(node: ) function
func gSerializeNodeToPlist(node: PENode, file: String, indentation: Int) -> String {
var str : String = ""
var indent : String = ""
// isRoot means that is still empty and we need to add the open/close tag for the Dictionary (or array)
let isRoot : Bool = (node.isRootNode == nil) ? false : node.isRootNode!
var i : Int = indentation <= 0 ? 1 : indentation
let indentChild : Int = isRoot ? i : i + 1
while i != 0 {
indent = indent + "\t"
i -= 1
}
let type = node.tagdata!.type
if type == .Dictionary {
if !isRoot {
if node.peparent != nil && node.peparent!.tagdata!.type != .Array {
str += indent + "<key>" + node.tagdata!.key + "</key>" + "\n"
}
} else {
str = "<dict>\n"
}
if node.mutableChildren.count > 0 {
/*
<key>New item</key>
<dict>
<key>test</key>
<string>hi</string>
</dict>
*/
if !isRoot {
str = str + indent + "<dict>" + "\n"
}
for child in node.mutableChildren {
str = str + gSerializeNodeToPlist(node: child as! PENode, file: str, indentation: indentChild)
}
if !isRoot {
str = str + indent + "</dict>" + "\n"
}
} else {
/*
<key>New item</key>
<dict/>
*/
str = str + indent + "<dict/>" + "\n"
}
} else if type == .Array {
if !isRoot {
str += indent + "<key>" + node.tagdata!.key + "</key>" + "\n"
} else {
str = "<array>\n"
}
if node.mutableChildren.count > 0 {
/*
<key>New item</key>
<array>
<string>hi</string>
</array>
*/
if !isRoot {
str = str + indent + "<array>" + "\n"
}
for child in node.mutableChildren {
str = str + gSerializeNodeToPlist(node: child as! PENode, file: str, indentation: indentChild)
}
if !isRoot {
str = str + indent + "</array>" + "\n"
}
} else {
/*
<key>New item</key>
<array/>
*/
str = str + indent + "<array/>" + "\n"
}
} else if type == .String {
/*
<key>test</key>
<string>hi</string>
*/
if node.peparent!.tagdata!.type != .Array {
str = str + indent + "<key>" + node.tagdata!.key + "</key>" + "\n"
} else if isRoot {
str = str + indent + "<key>" + localizedNewItem + "</key>" + "\n"
}
str = str + indent + "<string>" + (node.tagdata!.value as! String) + "</string>" + "\n"
} else if type == .Data {
if node.peparent!.tagdata!.type != .Array {
str = str + indent + "<key>" + node.tagdata!.key + "</key>" + "\n"
} else if isRoot {
str = str + indent + "<key>" + localizedNewItem + "</key>" + "\n"
}
let data = node.tagdata!.value as! NSData
let strBase64 : String = (data as Data).base64EncodedString(options: .endLineWithLineFeed)
str = str + indent + "<data>" + strBase64 + "</data>" + "\n"
} else if type == .Date {
if node.peparent!.tagdata!.type != .Array {
str = str + indent + "<key>" + node.tagdata!.key + "</key>" + "\n"
} else if isRoot {
str = str + indent + "<key>" + localizedNewItem + "</key>" + "\n"
}
str = str + indent + "<date>" + utcDateToString(node.tagdata!.value as! Date) + "</date>" + "\n"
} else if type == .Number {
if node.peparent!.tagdata!.type != .Array {
str = str + indent + "<key>" + node.tagdata!.key + "</key>" + "\n"
} else if isRoot {
str = str + indent + "<key>" + localizedNewItem + "</key>" + "\n"
}
let strNum = "\(node.tagdata!.value!)"
if (strNum as NSString).range(of: ".").location != NSNotFound {
// <real>1.2</real>
str = str + indent + "<real>" + strNum + "</real>" + "\n"
} else {
// <integer>1</integer>
str = str + indent + "<integer>" + strNum + "</integer>" + "\n"
}
} else if type == .Bool {
if node.peparent!.tagdata!.type != .Array {
str = str + indent + "<key>" + node.tagdata!.key + "</key>" + "\n"
} else if isRoot {
str = str + indent + "<key>" + localizedNewItem + "</key>" + "\n"
}
let n = node.tagdata!.value as! NSNumber
str += indent + (n.boolValue ? "<true/>" : "<false/>") + "\n"
}
if isRoot {
if node.tagdata!.type == .Array {
str = str + "</array>\n"
} else {
str = str + "</dict>\n"
}
}
return str
}

View File

@ -0,0 +1,87 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
//MARK: Undo and Redo
let localizedUndo = "Undo".locale
let localizedRedo = "Redo".locale
let localizedUndoRedoTyping = "typing".locale
let localizedUndoRedoChangeType = "change type".locale
let localizedUndoRedoChangeBoolValue = "change bool value".locale
let localizedUndoRedoReplaceDuplicateKey = "replace duplicate key".locale
let localizedUndoRedoMoveItem = "move item".locale
let localizedUndoRedoPasteItem = "paste Item".locale
let localizedUndoRedoRemoveItem = "remove Item".locale
let localizedUndoRedoCutItem = "cut Item".locale
let localizedUndoRedoAddNewItem = "add new Item".locale
//MARK: localized words for the outline
let localizedHeaderKey = "Key".locale
let localizedHeaderType = "Type".locale
let localizedHeaderValue = "Value".locale
//MARK: Plist tags
let localizedUnsupported = "Unsupported".locale
let localizedDictionary = "Dictionary".locale
let localizedArray = "Array".locale
let localizedString = "String".locale
let localizedNumber = "Number".locale
let localizedBool = "Bool".locale
let localizedDate = "Date".locale
let localizedData = "Data".locale
let localizedYes = "YES".locale
let localizedNo = "NO".locale
//MARK: Placheholders
let localizedItem = "Item".locale
let localizedItems = "Items".locale
let localizedUntiteled = "Untiteled".locale
let localizedNewItem = "New Item".locale
let localizedBytes = "bytes".locale
//MARK: Wrong editing
let DataEmptyString = "No data".locale
let DataMissingOpenTag = "missing '<' at the beginning".locale
let DataMissingEndTag = "missing '>' at the end".locale
let DataIllegalHex = "Your data contains illegal characters".locale
let DataOddBytes = "bytes count is odd, must be even".locale
let localizedKeepEditing = "Keep editing".locale
let localizedInvalidValueMsgText = "Invalid value detected!".locale
let localizedInvalidValueInfoText = "Your edit is not valid. Do you want to restore last valid value or keep editing?".locale
//MARK: Search & Replace
let localizedSearch = "Search".locale
let localizedReplace = "Replace".locale
// the 3 string below is for an alert that indicate you that you are attempting to have a duplicate key in the same Dictionary that is not allowed
let localizedDuplicateKeyMsgText = "Duplicate key in the Dictionary!".locale
let localizedDuplicateKeyInfoText = "'%@' is already present in the Dictionary. Do you want to undo the editing or replace the existing key?".locale
// text for the buttons of this alert
let localizedDuplicateKeyReplaceButton = "Replace existing key".locale

View File

@ -0,0 +1,35 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Foundation
//MARK: custom notifications
let PEOutLineViewEndScrolling = Notification.Name("PEOutLineViewEndScrolling")
let PESearchFieldTextDidChange = Notification.Name("PESearchFieldTextDidChange")
let ChoiceButtonDidChangeState = Notification.Name("ChoiceButtonDidChangeState")

View File

@ -0,0 +1,50 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Foundation
/**
A `PropertyListSerialization` for the given `Data` object.
- Parameter data: a `Data` object.
`Date` or `Data`.
- Returns: a `PropertyListFormat.xml` object.
- Discussion: The given data must be a Fundation object.
*/
func serialize(data: Data) -> Any? {
var any : Any?
var format = PropertyListSerialization.PropertyListFormat.xml
do {
try any = PropertyListSerialization.propertyList(from: data,
options: .mutableContainersAndLeaves,
format: &format) as Any
} catch {
any = PEDictionary()
print(error.localizedDescription)
}
return any
}

View File

@ -0,0 +1,603 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
// MARK: Undo and redo support
@available(OSX 10.11, *)
extension PEOutlineView {
// MARK: Undo Manager object
/// Prepares PEOutlineView for the undo operation
func prepareUndoObj() -> AnyObject {
// nil is not expecte here. Be happy if throw an exception
return self.undoManager?.prepare(withInvocationTarget: self) as AnyObject
}
// MARK: Undo drag Item
/// Undo operation when an Item is dragged here an there in PEOutlineView
@objc func undo_DragAndDrop(item: PENode,
fromParent: PENode,
fromIndex: Int,
toParent: PENode,
toIndex: Int) {
self.prepareUndoObj().undo_DragAndDrop(item: item,
fromParent: toParent,
fromIndex: toIndex,
toParent: fromParent,
toIndex: fromIndex)
self.undoManager?.setActionName(localizedUndoRedoMoveItem)
let toParentCount = toParent.mutableChildren.count
let new = item.copy() as! PENode
if fromParent == toParent {
var mutatingToIndex = toIndex
if fromIndex < toIndex {
mutatingToIndex -= 1
}
fromParent.mutableChildren.removeObject(at: fromIndex)
fromParent.mutableChildren.insert(new, at: mutatingToIndex)
DispatchQueue.main.async {
self.reloadItem(fromParent, reloadChildren: true)
}
} else {
self.editorVC?.asyncExpand(item: toParent, expandParentOnly: false)
// drag and past from to another parent
if toParentCount == 0 {
toParent.mutableChildren.add(new)
DispatchQueue.main.async {
self.animator().moveItem(at: fromIndex,
inParent: fromParent,
to: toIndex,
inParent: toParent)
}
} else {
DispatchQueue.main.async {
gDeduplicateKeyInParent(parent:toParent, newNode: new)
toParent.mutableChildren.insert(new, at: toIndex)
fromParent.mutableChildren.removeObject(at: fromIndex)
self.animator().moveItem(at: fromIndex,
inParent: fromParent,
to: toIndex,
inParent: toParent)
self.reloadItem(toParent, reloadChildren: true)
let row = self.row(forItem: new)
if row >= 0 {
self.scrollRowToVisible(row)
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
}
// select the new dragged item
DispatchQueue.main.async {
let toBeSelected = self.row(forItem: new)
if toBeSelected >= 0 {
self.scrollRowToVisible(toBeSelected)
self.selectRowIndexes(IndexSet(integer: toBeSelected), byExtendingSelection: false)
}
}
}
// MARK: Undo replace
/// Undo operation for Search & Replace in PEOutlineView
@objc func undo_FindAndReplace(nodes: NSArray, oldTreeData: NSArray, newTreeData: NSArray) {
self.prepareUndoObj().undo_FindAndReplace(nodes: nodes,
oldTreeData: newTreeData,
newTreeData: oldTreeData)
// select and expand, one by one, rows in oldNodes
self.undoManager?.setActionName(localizedReplace.lowercased())
var index : Int = 0
for n in nodes {
let node = n as! PENode
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
let newData = newTreeData.object(at: index) as? TagData
node.tagdata?.key = (newData?.key)!
node.tagdata?.value = newData?.value
DispatchQueue.main.async {
let row = self.row(forItem: node)
if row >= 0 {
self.scrollRowToVisible(row)
self.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: 0))
self.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: 2))
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
index+=1
}
self.editorVC?.performDelayedSearch()
}
// MARK: Undo set key string
/// Undo operation for a key editing (Key column) in PEOutlineView
@objc func undo_SetKey(node: PENode, newKey: String, oldKey: String) {
// we are setting the key for a specific row and ther's no need to reload the entire group
self.prepareUndoObj().undo_SetKey(node: node,
newKey: oldKey,
oldKey: newKey)
self.undoManager?.setActionName(localizedUndoRedoTyping)
node.tagdata?.key = newKey
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
//selecting the involved row, but we need to reload its view? isn't that already happened?
DispatchQueue.main.async {
self.reloadItem(node.parent, reloadChildren: false)
let row = self.row(forItem: node)
if row >= 0 {
self.scrollRowToVisible(row)
self.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: 0))
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
// MARK: Undo Replace Existing key
/// Undo operation for replacing a duplicate key-value pairs in PEOutlineView
@objc func undo_ReplaceExisting(item: PENode,
inParent: PENode,
indexChild: Int,
editedNode: PENode,
oldKey: String,
newKey: String) {
self.prepareUndoObj().redo_ReplaceExisting(item: item,
inParent: inParent,
indexChild: indexChild,
editedNode: editedNode,
oldKey: newKey,
newKey: oldKey)
self.undoManager?.setActionName(localizedUndoRedoReplaceDuplicateKey)
// expand the node if isn't. (an item can be added to a not expanded group??)
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
inParent.mutableChildren.removeObject(at: indexChild)
DispatchQueue.main.async {
self.removeItems(at: IndexSet(integer: indexChild),
inParent: inParent,
withAnimation: [])
editedNode.tagdata?.key = newKey
// keep trak of the item because must be selected after the parent gets reloaded
let row = self.row(forItem: editedNode)
//reload the parent. In this case the parent it's the same
//self.outline.reloadItem(inParent, reloadChildren: true)
if row >= 0 {
self.scrollRowToVisible(row)
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
// MARK: Redo Replace Existing key
/// Reversed undo operation (Redo) for replacing a duplicate key-value pairs in PEOutlineView
@objc func redo_ReplaceExisting(item: PENode,
inParent: PENode,
indexChild: Int,
editedNode: PENode,
oldKey: String,
newKey: String) {
self.prepareUndoObj().undo_ReplaceExisting(item: item,
inParent: inParent,
indexChild: indexChild,
editedNode: editedNode,
oldKey: newKey,
newKey: oldKey)
self.undoManager?.setActionName(localizedUndoRedoReplaceDuplicateKey)
// expand the node if isn't. (an item can be added to a not expanded group??)
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
inParent.mutableChildren.insert(item, at: indexChild)
editedNode.tagdata?.key = newKey
DispatchQueue.main.async {
self.insertItems(at: IndexSet(integer: indexChild),
inParent: inParent,
withAnimation: [])
// keep trak of the item because must be selected after the parent gets reloaded
let row = self.row(forItem: editedNode)
//reload the parent. In this case the parent it's the same
self.reloadItem(inParent, reloadChildren: true)
if row >= 0 {
self.scrollRowToVisible(row)
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
// MARK: Undo set new value
/// Undo operation (Redo) editing a value (Value Column) in PEOutlineView.
/// See also undoableSetBoolValue(node: , sender:, originalValue:, actualValue:)
@objc func undo_SetValue(node: PENode, newValue: Any, oldValue: Any) {
let row = self.row(forItem: node)
self.prepareUndoObj().undo_SetValue(node: node,
newValue: oldValue,
oldValue: newValue)
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
self.undoManager?.setActionName(localizedUndoRedoTyping)
node.tagdata?.value = newValue
DispatchQueue.main.async {
self.reloadItem(node.parent, reloadChildren: false)
if row >= 0 {
self.scrollRowToVisible(row)
self.reloadData(forRowIndexes: IndexSet(integer: row), columnIndexes: IndexSet(integer: 2))
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
// MARK: Undo change to Bool tag
/// Undo operation (Redo) editing a Bool value (Value Column) in PEOutlineView
@objc func undo_SetBoolValue(node: PENode,
sender: PEPopUpButton,
originalValue: Bool,
actualValue: Bool) {
self.prepareUndoObj().undo_SetBoolValue(node: node,
sender: sender,
originalValue: actualValue,
actualValue: originalValue)
self.undoManager?.setActionName(localizedUndoRedoChangeBoolValue)
node.tagdata?.value = actualValue
// root node cannot have parent and is always visible
if (node.parent != nil) {
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
}
DispatchQueue.main.async {
let row = self.row(forItem: node)
if row >= 0 {
self.scrollRowToVisible(row)
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
sender.animator().selectItem(withTitle:
actualValue ? localizedYes : localizedNo)
}
}
// MARK: Undo change tag
/// Undo operation changing a tag (Tag Column) in PEOutlineView.
@objc func undo_ChangeType(node: PENode,
newData: TagData,
oldData: TagData,
newChilds: [Any]?,
oldChilds: [Any]?,
sender: PEPopUpButton) {
self.prepareUndoObj().undo_ChangeType(node: node,
newData: oldData,
oldData: newData,
newChilds: oldChilds,
oldChilds: newChilds,
sender: sender)
self.undoManager?.setActionName(localizedUndoRedoChangeType)
node.representedObject = newData
node.mutableChildren.removeAllObjects()
if let childrens = newChilds {
node.mutableChildren.addObjects(from: childrens)
}
// root node cannot have parent and is always visible
if (node.parent != nil) {
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
}
DispatchQueue.main.async {
self.reloadItem(node, reloadChildren: true)
let row = self.row(forItem: node)
if row >= 0 {
self.scrollRowToVisible(row)
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
// MARK: Undo change contenitor (Dictionary to Array and viceversa)
/// Undo operation changing a Dictionary to Array and viceversa (Tag Column) in PEOutlineView.
@objc func undo_ChangeContenitor(node: PENode,
sender: PEPopUpButton,
originalType: String,
actualType: String,
oldKeys: PEArray,
newKeys: PEArray) {
/*
We cannot leave old keys, but instead we should:
- Dictionary, must have localized "New Item -x" for each children
- Array, must be localized "Item x" for each children
*/
self.prepareUndoObj().undo_ChangeContenitor(node: node,
sender: sender,
originalType: actualType,
actualType: originalType,
oldKeys: newKeys,
newKeys: oldKeys)
self.undoManager?.setActionName(localizedUndoRedoChangeType)
// root node cannot have parent and is always visible
if (node.parent != nil) {
self.editorVC?.asyncExpand(item: node, expandParentOnly: false)
}
let actualTag = gPlistTag(from: actualType)
if actualTag == .Dictionary {
node.tagdata?.value = nil
node.tagdata?.type = .Dictionary
} else if actualTag == .Array {
node.tagdata?.value = nil
node.tagdata?.type = .Array
}
var index : Int = 0
for n in node.mutableChildren {
let modded : PENode = n as! PENode
modded.tagdata?.key = newKeys[index] as! String
index+=1
}
DispatchQueue.main.async {
sender.animator().selectItem(withTitle: gPlistTagStr(tag: actualTag).locale)
}
self.editorVC?.asyncExpand(item: node, expandParentOnly: true)
DispatchQueue.main.async {
let row = self.row(forItem: node)
if self.isItemExpanded(node) {
self.reloadItem(node, reloadChildren: true)
} else {
self.reloadItem(node, reloadChildren: false)
}
if row >= 0 {
self.scrollRowToVisible(row)
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
// MARK: Undo Paste
/// Undo operation for "Paste" in PEOutlineView.
/// See redo_PasteItem(item: , inParent: , indexChild: ) for the reversed operation.
@objc func undo_Paste(item: PENode, inParent: PENode, indexChild: Int) {
self.prepareUndoObj().redo_Paste(item: item,
inParent: inParent,
indexChild: indexChild)
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
self.undoManager?.setActionName(localizedUndoRedoPasteItem)
inParent.mutableChildren.insert(item, at: indexChild)
DispatchQueue.main.async {
self.insertItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
// basically we are adding a new Item. Reload the parent and the cildrens..and select new item
self.reloadItem(inParent, reloadChildren: true)
// find new item that should be there now..
let row = self.row(forItem: item)
if row >= 0 {
self.scrollRowToVisible(row)
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
// MARK: Undo Paste (reverse)
/// Redo operation for "Paste" in PEOutlineView.
/// See undo_PasteItem(item: , inParent: , indexChild: ) for the reversed operation.
@objc func redo_Paste(item: PENode, inParent: PENode, indexChild: Int) {
self.prepareUndoObj().undo_Paste(item: item,
inParent: inParent,
indexChild: indexChild)
self.undoManager?.setActionName(localizedUndoRedoPasteItem)
// expand the node if isn't
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
// find the row. After deleting it, it will be the row to select again (if exist, othewise select last row in the outline)
DispatchQueue.main.async {
let row = self.row(forItem: item)
inParent.mutableChildren.removeObject(at: indexChild)
self.removeItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
// basically we are removing a new Item just added. Reload the parent and the cildrens..
self.reloadItem(inParent, reloadChildren: true)
//..and select the next row
if row >= 0 {
if (self.item(atRow: row) != nil) {
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
} else {
// row it's out of bounds.. select last in the outline
let lastRow = self.numberOfRows - 1
if lastRow >= 0 {
self.scrollRowToVisible(lastRow)
self.selectRowIndexes(IndexSet(integer: lastRow), byExtendingSelection: false)
}
}
}
}
}
// MARK: Undo Cut
/// Undo operation for "Cut" in PEOutlineView.
/// See redo_Cut(item: , inParent: , indexChild: ) for the reversed operation.
@objc func undo_Cut(item: PENode, inParent: PENode, indexChild: Int) {
// find the row. After deleting it, it will be the row to select again (if exist, othewise select last row in the outline)
self.prepareUndoObj().redo_Cut(item: item,
inParent: inParent,
indexChild: indexChild)
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
self.undoManager?.setActionName(localizedUndoRedoCutItem)
inParent.mutableChildren.removeObject(at: indexChild)
self.removeItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
// basically we are removing a new Item just added. Reload the parent and the cildrens..
DispatchQueue.main.async {
self.reloadItem(inParent, reloadChildren: true)
let row = self.row(forItem: item)
//..and select the next row
if row >= 0 {
if (self.item(atRow: row) != nil) {
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
} else {
// row it's out of bounds.. select last in the outline
let lastRow = self.numberOfRows - 1
if lastRow >= 0 {
self.scrollRowToVisible(lastRow)
self.selectRowIndexes(IndexSet(integer: lastRow), byExtendingSelection: false)
}
}
}
}
}
// MARK: Undo Cut (reverse)
/// Redo operation for "Cut" in PEOutlineView.
/// See undo_Cut(item: , inParent: , indexChild: ) for the reversed operation.
@objc func redo_Cut(item: PENode, inParent: PENode, indexChild: Int) {
self.prepareUndoObj().undo_Cut(item: item,
inParent: inParent,
indexChild: indexChild)
self.undoManager?.setActionName(localizedUndoRedoCutItem)
// expand the node if isn't
self.editorVC?.asyncExpand(item: item, expandParentOnly: true)
// we are adding an item..
inParent.mutableChildren.insert(item, at: indexChild)
DispatchQueue.main.async {
self.insertItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
// select the new one after reloading the group
self.reloadItem(inParent, reloadChildren: true)
let row = self.row(forItem: item)
if row >= 0 {
self.scrollRowToVisible(row)
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
// MARK: Undo add item
/// Undo operation adding New items in PEOutlineView.
/// See undo_Remove(item: , inParent: , indexChild: ) for the reverse operation.
@objc func undo_Add(item: PENode,
inParent: PENode,
indexChild: Int,
target outlineView: PEOutlineView) {
outlineView.prepareUndoObj().undo_Remove(item: item,
inParent: inParent,
indexChild: indexChild,
target: outlineView)
outlineView.undoManager?.setActionName(localizedUndoRedoAddNewItem)
outlineView.editorVC?.asyncExpand(item: item, expandParentOnly: true)
inParent.mutableChildren.insert(item, at: indexChild)
DispatchQueue.main.async {
outlineView.insertItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
// select the new one after reloading the group
outlineView.reloadItem(inParent, reloadChildren: true)
let row = outlineView.row(forItem: item)
if row >= 0 {
outlineView.scrollRowToVisible(row)
outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
}
}
}
// MARK: Redo remove Item
/// Redo operation adding New items in PEOutlineView.
/// See undo_Add(item: , inParent: , indexChild: ) for the reverse operation.
@objc func undo_Remove(item: PENode,
inParent: PENode,
indexChild: Int,
target outlineView: PEOutlineView) {
// find the row. After deleting it, it will be the row to select again (if exist, othewise select last row in the outline)
let row = outlineView.row(forItem: item)
outlineView.prepareUndoObj().undo_Add(item: item,
inParent: inParent,
indexChild: indexChild,
target: outlineView)
outlineView.undoManager?.setActionName(localizedUndoRedoRemoveItem)
// expand the node if isn't
outlineView.editorVC?.asyncExpand(item: item, expandParentOnly: true)
inParent.mutableChildren.removeObject(at: indexChild)
DispatchQueue.main.async {
outlineView.removeItems(at: IndexSet(integer: indexChild), inParent: inParent, withAnimation: [])
// Reload the parent and the cildrens..
outlineView.reloadItem(inParent, reloadChildren: true)
//..and select the next row
if row >= 0 {
outlineView.scrollRowToVisible(row)
if (outlineView.item(atRow: row) != nil) {
outlineView.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
} else {
// row it's out of bounds.. select last in the outline
let lastRow = outlineView.numberOfRows - 1
if lastRow >= 0 {
outlineView.selectRowIndexes(IndexSet(integer: lastRow), byExtendingSelection: false)
}
}
}
}
}
}

View File

@ -0,0 +1,162 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Foundation
/**
Assign a unique name for newNode if parent already contains it.
- Parameter parent: the parent node in which newNode will be inserted.
- Parameter newNode: the new node for which it's key must be unique.
- Discussion: Call this method before insert new nodes in the parent node.
*/
func gDeduplicateKeyInParent(parent: PENode, newNode: PENode) {
/* making an array of existing keys */
var actualKeys = [String]()
for item in parent.mutableChildren {
actualKeys.append(((item as! PENode).tagdata?.key)!)
}
/* new way */
let newKey : String = newNode.tagdata!.key
newNode.tagdata!.key = gProposedNewItem(with: newKey, in: actualKeys)
}
/**
Propose a new key name for a node Dictionary's keys.
- Parameter key: the actual key of the node or the desired one.
- Parameter array: an array of String containing all the keys for the target node's childrens.
- Returns: a `String` with a unique name for the given array. Will return the same string if the given array doesn't contains it.
- Discussion: A dictioanry cannot have duplicate keys. This method ensure that a given key will be unique by adding a numeric suffix (key - n).
*/
func gProposedNewItem(with key: String, in array: [String]) -> String {
var i : Int = 0
var proposed : String = key
repeat {
if !array.contains(proposed) {
break
}
proposed = "\(key) - \(i)"
i+=1
} while true
return proposed
}
/**
A string representation for a given `PlistTag`.
- Parameter tag: a `PlistTag`.
- Returns: a `String` object.
- Discussion: the returned string can be localized which is the main purpose.
*/
func gPlistTagStr(tag: PlistTag) -> String {
switch tag {
case .Dictionary:
return "Dictionary"
case .Array:
return "Array"
case .String:
return "String"
case .Number:
return "Number"
case .Bool:
return "Bool"
case .Date:
return "Date"
case .Data:
return "Data"
}
}
/**
A `PlistTag` representation for a given `PlistTag`.
- Parameter str: a `String` which must be no other than `Dictionary`, `Array`, `String`, `Number`, `Bool`,
`Date` or `Data`.
- Returns: a `String` object.
- Discussion: A `fatalError` occours if the given str is not avalid `PlistTag`.
*/
func gPlistTag(from str: String) -> PlistTag {
switch str {
case "Dictionary":
return .Dictionary
case "Array":
return .Array
case "String":
return .String
case "Number":
return .Number
case "Bool":
return .Bool
case "Date":
return .Date
case "Data":
return .Data
default:
fatalError("plistTag(from str: String): unsupported str parameter \(str)")
}
}
/**
Loading a plist file.
- Parameter path: a `String` representing the path to a loadable plist file.
- Discussion: The insternal Plist Editor is used for loading plist files while in 10.9/10.10, in order, PlistEdit Pro or Xcode are used.
In 10.9/10.10 if mentioned programs aren't present the directory containing the desired file will be opened by the Finder.
*/
func loadPlist(at path: String) {
if #available(OSX 10.11, *) {
let dc = NSDocumentController.shared
dc.openDocument(withContentsOf: URL(fileURLWithPath: path), display: true) {
(document, documentWasAlreadyOpen, error) in
if error != nil {
print(error!.localizedDescription)
NSSound.beep()
} else {
if (document != nil) {
dc.addDocument(document!)
}
}
}
} else {
// check if a document is opened some where
if NSDocumentController.shared.documents.count == 0 {
NSApp.setActivationPolicy(.accessory)
}
// Use reccomended programs (hope) to avoid Text Edit
var success = NSWorkspace.shared.openFile(path, withApplication: "PlistEdit Pro")
if !success {
success = NSWorkspace.shared.openFile(path, withApplication: "Xcode")
}
/* will it be Text Edit???
if !success {
success = NSWorkspace.shared.openFile(path)
}*/
if !success { // open the directory path
success = NSWorkspace.shared.openFile(path.deletingLastPath)
}
}
}

View File

@ -0,0 +1,555 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
/*
This is the minimum plist file (binary format) as a boolean true as root (the shorter value that can hold), and is 42 bytes:
62 70 6C 69 73 74 30 30 09 08 00 00 00 00 00 00 01 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09
Instead, the xml version have a length in bytes (xml1Header + "<true/>" + xml1Footer) of 179.
Line feeds are present only in the header: this means that at least the header should be well formatted
*/
let minBinary : Int = 42
let minXml : Int = 179
final class PlistParser: NSObject, XMLParserDelegate {
var root : PENode?
var plistPath : String? = nil
var error : String? = nil
var isBinary : Bool = false
var rootType : PlistTag = .String
private var currentValue : String? = nil
private var lastElement : String? = nil
private var currentNode: PENode?
//MARK: initialization
override init() {
super.init()
self.root = nil
}
/// You already know the root node for the document.
init(withNode node: PENode) {
super.init()
self.root = node
}
/// PlistParser initialization from Data (either string or binary data).
/// Usually fileType and data comes from NSDocument
convenience init(fromData data: Data, fileType: PEFileType) {
var node : PENode? = nil
var any : Any?
var plistType : PEFileType = fileType
// don't trust NSDocument: check our self if this is a binary plist
if data.count >= minBinary {
let binaryHeaderData: Data = binaryPlistHeader.data(using: String.Encoding.utf8)!
let fileheader = data.subdata(in: 0..<binaryHeaderData.count)
if fileheader == binaryHeaderData {
plistType = .binaryPlistv1 // override!
}
}
if plistType == .binaryPlistv1 {
any = serialize(data: data)
if (any != nil) {
node = PlistParser.populateTreeWith(plist: any!)
}
} else {
any = serialize(data: data)
// don't fail!
if any == nil {
any = PEDictionary()
}
}
/*
determine if this is a stupid plist (root is not a Dictionary nor Array),
otherwise init with node.
Why? "node" is used only with binary plist where its Dictionaries aren't sorted as happen in a xml plist..
..but no need to use the parser if a plist is stupid
*/
if (any != nil) {
if any is NSString {
node = PENode(representedObject: TagData(key: "", type: .String, value: any as! String))
} else if any is NSNumber {
let numberType = CFNumberGetType(any as! NSNumber)
if numberType == .charType {
// Bool
node = PENode(representedObject: TagData(key: "", type: .Bool, value: any as! Bool))
} else {
// Number of any kind, but int or double..
if any is PEInt {
node = PENode(representedObject: TagData(key: "Root", type: .Number, value: any as! PEInt))
} else {
node = PENode(representedObject: TagData(key: "Root", type: .Number, value: any as! PEReal))
}
}
} else if any is NSData {
node = PENode(representedObject: TagData(key: "", type: .Data, value: any as! Data))
} else if any is NSDate {
node = PENode(representedObject: TagData(key: "", type: .Date, value: any as! Date))
}
}
if (node != nil) {
self.init(withNode: node!)
self.isBinary = true
} else {
self.init()
let parser : XMLParser = XMLParser(data: data)
self.currentValue = ""
parser.delegate = self
parser.parse()
}
}
/// PlistParser initialization from path. NSDocument uses data, so this is for custom initializations.
convenience init(fromPath path: String) {
var realOrNilPath : String? = path // this will be nil if the loaded file is not a plist (will avoid to overwrite it)
var data : Data? = nil
var isPlist : Bool = false
var node : PENode? = nil
var PropertyListSerializationError : String? = nil
var any : Any?
data = FileManager.default.contents(atPath: path)
// test if is a binary plist
if (data != nil) {
if (data?.count)! >= minBinary {
let binaryHeaderData: Data = binaryPlistHeader.data(using: String.Encoding.utf8)!
let presumedHeader = data?.subdata(in: 0 ..< binaryHeaderData.count)
if presumedHeader == binaryHeaderData {
isPlist = true
any = serialize(data: data!)
// don't fail!
if any == nil {
any = PEDictionary()
PropertyListSerializationError = "PlistParser: Unknown error loading binary plist file"
}
node = PlistParser.populateTreeWith(plist: any!)
}
}
// test if is a xml plist
if !isPlist {
if (data?.count)! >= minXml {
let xmlHeaderData: Data = xml1Header.data(using: String.Encoding.utf8)!
let presumedHeader = data?.subdata(in: 0 ..< xmlHeaderData.count)
if presumedHeader == xmlHeaderData {
isPlist = true
any = serialize(data: data!)
}
}
}
}
/*
determine if this is a stupid plist (root is not a Dictionary nor Array),
otherwise init with node.
Why? "node" is used only with binary plist where its Dictionaries aren't sorted as happen in a xml plist..
..but no need to use the parser if a plist is stupid
*/
if (any != nil) {
if any is NSString {
node = PENode(representedObject: TagData(key: "", type: .String, value: any as! String))
} else if any is NSNumber {
let numberType = CFNumberGetType(any as! NSNumber)
if numberType == .charType {
node = PENode(representedObject: TagData(key: "Root", type: .Bool, value: any as! Bool))
} else {
// Number of any kind, but int or double..
if any is PEInt {
node = PENode(representedObject: TagData(key: "Root", type: .Number, value: any as! PEInt))
} else {
node = PENode(representedObject: TagData(key: "Root", type: .Number, value: any as! PEReal))
}
}
} else if any is NSData {
node = PENode(representedObject: TagData(key: "", type: .Data, value: any as! Data))
} else if any is NSDate {
node = PENode(representedObject: TagData(key: "", type: .Date, value: any as! Date))
}
}
if (node != nil) {
self.init(withNode: node!)
self.error = PropertyListSerializationError
self.isBinary = true
} else {
self.init(fromData: (data ?? (xml1Header + "\n<dict/>\n" + xml1Footer).data(using: String.Encoding.utf8))!,
fileType: PEFileType.xmlPlistv1)
}
if isPlist {
realOrNilPath = path
}
self.plistPath = realOrNilPath
}
func parser(_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String : String] = [:]) {
switch elementName {
case "dict":
if (self.root == nil) {
self.rootType = .Dictionary
self.root = self.getNewDictNode()
self.currentNode = self.root
} else {
if lastElement == "array" || self.currentNode!.tagdata!.type == .Array {
let new = self.getNewDictNode()
self.currentNode?.mutableChildren.add(new)
self.currentNode = new
break
} else if self.lastElement == "key" {
self.currentNode?.tagdata?.type = .Dictionary
self.currentNode?.tagdata?.value = nil
} else {
let new = self.getNewDictNode()
self.currentNode?.parent?.mutableChildren.add(new)
self.currentNode = new
}
}
break
case "array":
if (self.root == nil) {
self.rootType = .Array
self.root = self.getNewArrayNode()
self.currentNode = self.root
} else {
if self.lastElement == "key" {
self.currentNode?.tagdata?.type = .Array
self.currentNode?.tagdata?.value = nil
} else {
let new = self.getNewArrayNode()
self.currentNode?.parent?.mutableChildren.add(new)
self.currentNode = new
}
}
break
case "key":
let new = self.getNewStringNode()
self.currentNode?.mutableChildren.add(new)
self.currentNode = new
break
case "string":
if self.lastElement != "key" {
let new = self.getNewStringNode()
self.addNewChildren(newNode: new)
}
break
case "date":
if self.lastElement != "key" {
let new = self.getNewDateNode()
self.addNewChildren(newNode: new)
}
break
case "data":
if self.lastElement != "key" {
let new = self.getNewDataNode()
self.addNewChildren(newNode: new)
}
break
case "real":
if self.lastElement != "key" {
let new = self.getNewNumberNode()
self.addNewChildren(newNode: new)
}
break
case "integer":
if self.lastElement != "key" {
let new = self.getNewNumberNode()
self.addNewChildren(newNode: new)
}
break
case "true":
if self.lastElement != "key" {
let new = self.getNewBoolNode()
self.addNewChildren(newNode: new)
}
break
case "false":
if self.lastElement != "key" {
let new = self.getNewBoolNode()
self.addNewChildren(newNode: new)
}
break
default:
break
}
self.lastElement = elementName
}
//MARK: XMLParser functions
func parser(_ parser: XMLParser,
didEndElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?) {
if (self.root != nil) {
let value : String? = self.currentValue?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
switch elementName {
case "dict":
self.goback()
break
case "array":
self.goback()
break
case "key":
self.currentNode?.tagdata?.key = value?.escapingXMLCharacters ?? ""
break
case "string":
self.currentNode?.tagdata?.value = value!.escapingXMLCharacters
self.currentNode?.tagdata?.type = .String
self.goback()
break
case "date":
self.currentNode?.tagdata?.value = utcStringToDate(value!)
self.currentNode?.tagdata?.type = .Date
self.goback()
break
case "data":
let data : Data = Data(base64Encoded: value!, options: Data.Base64DecodingOptions.ignoreUnknownCharacters)!
self.currentNode?.tagdata?.value = data
self.currentNode?.tagdata?.type = .Data
self.goback()
break
case "real":
self.currentNode?.tagdata?.value = PEReal(value!)!
self.currentNode?.tagdata?.type = .Number
self.goback()
break
case "integer":
self.currentNode?.tagdata?.value = PEInt(value!)!
self.currentNode?.tagdata?.type = .Number
self.goback()
break
case "true":
self.currentNode?.tagdata?.value = true
self.currentNode?.tagdata?.type = .Bool
self.goback()
break
case "false":
self.currentNode?.tagdata?.value = false
self.currentNode?.tagdata?.type = .Bool
self.goback()
break
default:
break
}
}
self.lastElement = elementName
self.currentValue = ""
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
self.currentValue = self.currentValue?.appending(string)
}
func parserDidStartDocument(_ parser: XMLParser) {
}
func parserDidEndDocument(_ parser: XMLParser) {
if self.rootType == .Dictionary || self.rootType == .Array {
self.root?.tagdata?.key = "Root"
}
}
func parser(_ parser: XMLParser, foundIgnorableWhitespace whitespaceString: String) {
}
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
self.root = nil
self.error = "\(parseError)".replacingOccurrences(of: "NSXML", with: "Plist")
}
//MARK: Helper functions for XMLParser
private func addNewChildren(newNode: PENode) {
if (self.root == nil) {
self.rootType = (newNode.tagdata?.type)!
self.root = newNode
self.currentNode = self.root
} else {
self.currentNode?.mutableChildren.add(newNode)
self.currentNode = newNode
}
}
func isChildTag(tag: String) -> Bool {
return tag == "string" ||
tag == "date" ||
tag == "data" ||
tag == "real" ||
tag == "integer" ||
tag == "true" ||
tag == "false"
}
private func goback() {
if ((self.currentNode?.peparent) == nil) {
self.currentNode = self.root
} else {
self.currentNode = self.currentNode?.peparent
}
}
//MARK: Get empty nodes
private func getNewDictNode() -> PENode {
return PENode(representedObject: TagData(key: localizedNewItem,
type: .Dictionary,
value: nil))
}
private func getNewArrayNode() -> PENode {
return PENode(representedObject: TagData(key: localizedNewItem,
type: .Array,
value: nil))
}
private func getNewStringNode() -> PENode {
return PENode(representedObject: TagData(key: localizedNewItem,
type: .String,
value: ""))
}
private func getNewNumberNode() -> PENode {
return PENode(representedObject: TagData(key: localizedNewItem,
type: .Number,
value: 0))
}
private func getNewDataNode() -> PENode {
return PENode(representedObject: TagData(key: localizedNewItem,
type: .Data,
value: Data()))
}
private func getNewDateNode() -> PENode {
return PENode(representedObject: TagData(key: localizedNewItem,
type: .Date,
value: Date()))
}
private func getNewBoolNode() -> PENode {
return PENode(representedObject: TagData(key: localizedNewItem,
type: .Bool,
value: false))
}
//MARK: class Helper functions for binary plist
/// Populate a PENode with the contents of a plist
class func populateTreeWith(plist: Any) -> PENode {
let data: TagData = TagData(key: "Root", type: .Dictionary, value: nil) // initialization
let node: PENode = PENode(representedObject: data)
add(child: plist, parent: node)
return node
}
/// Adds a new PENode with the contents of a child value.
/// This function recursively add nodes to the initial parent node
class func add(child: Any, parent: PENode) {
if child is PEDictionary {
let dict = child as! PEDictionary
for key in dict.keys {
let curVal = dict[key]
var type : PlistTag
if curVal is PEDictionary {
type = .Dictionary
} else if curVal is PEArray {
type = .Array
} else if (curVal is PEInt || curVal is PEReal) {
type = .Number
} else if curVal is Bool {
type = .Bool
} else if curVal is String {
type = .String
} else if curVal is Data {
type = .Data
} else if curVal is Date {
type = .Date
} else {
break
//fatalError("add(child: Any, parent: PENode) unsupported tag for \(curVal ?? "Unknown")")
}
let d = TagData(key: key,
type: type,
value: (type == .Array || type == .Dictionary) ? nil : curVal)
let node = PENode(representedObject: d)
parent.mutableChildren.add(node)
add(child: curVal!, parent: node)
}
} else if child is PEArray {
let arr = child as! PEArray
for i in 0..<arr.count {
let curVal = arr[i]
var type : PlistTag
if curVal is PEDictionary {
type = .Dictionary
} else if curVal is PEArray {
type = .Array
} else if (curVal is PEInt || curVal is PEReal) {
type = .Number
} else if curVal is Bool {
type = .Bool
} else if curVal is String {
type = .String
} else if curVal is Data {
type = .Data
} else if curVal is Date {
type = .Date
} else {
break
//fatalError("add(child: Any, parent: PENode) unsupported tag for \(curVal)")
}
let d = TagData(key: "\(localizedItem) \(i)",
type: type,
value: (type == .Array || type == .Dictionary) ? nil : curVal)
let node = PENode(representedObject: d)
parent.mutableChildren.add(node)
add(child: curVal, parent: node)
}
}
}
}

View File

@ -0,0 +1,98 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
//MARK: TagData
final class TagData: NSObject, NSCoding, NSCopying {
var key : String
var type : PlistTag
var value : Any?
var placeHolder : String
required init(key: String, type: PlistTag, value: Any?) {
self.key = key
self.type = type
self.value = value
self.placeHolder = ""
super.init()
self.type = type
// don't hold the value of contenitors, ....save the memory!!!!
if self.type == .Array {
self.value = nil
} else if self.type == .Dictionary {
self.value = nil
}
}
// Welocome to NSCoding!
required init(coder decoder: NSCoder) {
self.key = decoder.decodeObject(forKey: "key") as? String ?? ""
self.type = PlistTag(rawValue: (decoder.decodeObject(forKey: "type") as! NSNumber).intValue) ?? .String
let val = decoder.decodeObject(forKey: "value")
switch self.type {
case .String:
self.value = val as! String
case .Dictionary:
self.value = nil
case .Array:
self.value = nil
case .Number:
if val is PEInt {
self.value = val as! PEInt
} else {
self.value = val as! PEReal
}
case .Bool:
self.value = val as! Bool
case .Date:
self.value = val as! Date
case .Data:
self.value = val as! Data
}
self.placeHolder = ""
}
func encode(with coder: NSCoder) {
coder.encode(self.key, forKey: "key")
coder.encode(NSNumber(value: self.type.rawValue), forKey: "type")
coder.encode(self.value, forKey: "value")
}
func copy(with zone: NSZone? = nil) -> Any {
let keyCopy = "\(self.key)"
let typeCopy = PlistTag.init(rawValue: self.type.rawValue)
let valCopy = (self.value as AnyObject).copy()
return TagData(key: keyCopy, type: typeCopy!, value: valCopy)
}
}

View File

@ -0,0 +1,77 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
@available(OSX 10.11, *)
final class PEAlert : NSAlert {
var anyString : String? = nil
var outline: PEOutlineView? = nil
init(in window: NSWindow?) {
super.init()
self.customizeAlert()
}
func customizeAlert() {
self.alertStyle = NSAlert.Style.warning
let allview = self.window.contentView?.subviews
for v in allview! {
if v is NSImageView {
let imgv = v as! NSImageView
imgv.image = NSApp.applicationIconImage
}
}
}
func keepEditing() -> Bool {
/* usato in Data/Date value (tutti gli altri saranno validi in qualche modo)
Your entry is not valid. Do you want to keep editing and fix the error or cancel editing?
Cancel
Keep Editing
*/
var result = NSApplication.ModalResponse.alertFirstButtonReturn
self.messageText = "Invalid value detected!".locale
self.informativeText = "Your edit is not valid,\nDo you want to restore last valid value or keep editing?".locale
self.addButton(withTitle: localizedKeepEditing)
self.addButton(withTitle: "Undo".locale)
self.beginSheetModal(for: self.window, completionHandler: { (modalResponse) -> Void in
result = modalResponse
if result == NSApplication.ModalResponse.alertFirstButtonReturn {
print("key deleted")
}
})
return result == NSApplication.ModalResponse.alertFirstButtonReturn
}
}

View File

@ -0,0 +1,71 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
//MARK: add constraints to fit superview
/**
Add constraints to the new added subview to fit its superview.
- Parameter superView: The super view.
- Parameter subView: the subview that needs constraints.
*/
func gAddConstraintsToFit(superView: NSView, subView: NSView) {
subView.translatesAutoresizingMaskIntoConstraints = false
superView.addConstraint(NSLayoutConstraint(item: subView,
attribute: .top,
relatedBy: .equal,
toItem: superView,
attribute: .top,
multiplier: 1.0,
constant: 0.0))
superView.addConstraint(NSLayoutConstraint(item: subView,
attribute: .leading,
relatedBy: .equal,
toItem: superView,
attribute: .leading,
multiplier: 1.0,
constant: 0.0))
superView.addConstraint(NSLayoutConstraint(item: superView,
attribute: .bottom,
relatedBy: .equal,
toItem: subView,
attribute: .bottom,
multiplier: 1.0,
constant: 0.0))
superView.addConstraint(NSLayoutConstraint(item: superView,
attribute: .trailing,
relatedBy: .equal,
toItem: subView,
attribute: .trailing,
multiplier: 1.0,
constant: 0.0))
}

View File

@ -0,0 +1,39 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
final class FindButton: NSButton {
required init?(coder: NSCoder) {
super.init(coder: coder)
self.canDrawConcurrently = AppSD.canDrawConcurrently
self.wantsLayer = true
let image = NSImage(named: "Find")
image?.isTemplate = true
self.image = image
}
}

View File

@ -0,0 +1,78 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
final class PEMenuItem: NSMenuItem {
internal var sep : Bool = false
internal var d : String?
public var isKey : Bool = false
public var keyPath : String = ""
public var node : PENode? = nil
public var useEditor : Bool = false
override var isSeparatorItem: Bool {
get {
return self.sep
} set {
self.sep = newValue
}
}
var explanation: String? {
get {
return self.d
} set {
self.d = newValue
}
}
override func copy(with zone: NSZone? = nil) -> Any {
let copy = PEMenuItem()
copy.menu = nil // nil allow to reuse this Item somewhere else..
copy.title = self.title
copy.keyEquivalent = self.keyEquivalent
copy.image = self.image?.copy() as? NSImage
copy.onStateImage = self.onStateImage.copy() as? NSImage
copy.offStateImage = self.offStateImage?.copy() as? NSImage
copy.mixedStateImage = self.mixedStateImage.copy() as? NSImage
copy.representedObject = self.representedObject
copy.submenu = self.submenu?.copy() as? NSMenu;
copy.toolTip = self.toolTip;
copy.target = self.target;
copy.explanation = self.explanation
copy.isKey = self.isKey
copy.keyPath = self.keyPath
copy.node = self.node
copy.useEditor = self.useEditor
copy.action = self.action
return copy
}
}

View File

@ -0,0 +1,336 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
//MARK: PEOutlineView (NSOutlineView)
@available(OSX 10.11, *)
final class PEOutlineView: NSOutlineView, NSMenuDelegate {
var wrongValue : Bool = false
var scrollTimer : Timer? = nil
var editorVC : PlistEditorVC?
override func drawGrid(inClipRect clipRect: NSRect) {
let nRow = self.numberOfRows
if nRow > 0 {
let lastRowRect = self.rect(ofRow: nRow - 1)
let myClipRect = NSMakeRect(0, 0, lastRowRect.size.width, lastRowRect.size.height)
let finalClipRect : NSRect = NSIntersectionRect(clipRect, myClipRect)
super.drawGrid(inClipRect: finalClipRect)
}
}
override func scrollWheel(with event: NSEvent) {
super.scrollWheel(with: event)
NotificationCenter.default.post(name: PEOutLineViewEndScrolling, object: nil)
if (self.scrollTimer != nil) {
if self.scrollTimer!.isValid {
self.scrollTimer?.invalidate()
}
self.scrollTimer = nil
}
self.scrollTimer = Timer.scheduledTimer(timeInterval: 0.2,
target: self,
selector: #selector(endScrolling),
userInfo: nil,
repeats: false)
}
@objc func endScrolling() {
self.enumerateAvailableRowViews { _, row in
if let cv = self.view(atColumn: 0,
row: row,
makeIfNecessary: false) as? PETableCellView {
cv.hideButtons()
if let rv = cv.superview as? PETableRowView {
rv.setBorderType()
}
}
}
}
override func menu(for event: NSEvent) -> NSMenu? {
let point = self.convert(event.locationInWindow, from: nil)
let row = self.row(at: point)
let item = self.item(atRow: row)
if (item == nil) {
return nil
}
self.selectRowIndexes(IndexSet(integer: row), byExtendingSelection: false)
return self.getBaseMenu(outlineView: self, for: item!)
}
func getBaseMenu(outlineView: PEOutlineView, for item: Any) -> NSMenu {
let node: PENode = item as! PENode
// get a key path for the node! (like Boot->Arguments-> etc)
// to do that create an array..
let keyPaths : NSMutableArray = NSMutableArray()
var parent: PENode? = node
while (parent != nil) && (parent != outlineView.item(atRow: 0) as? PENode) {
var key : String = (parent?.ro as! TagData).key
let type = ((parent?.parent as! PENode).ro as! TagData).type
if type == .Array {
// we need the index in this case!
let index : Int = (parent?.parent as! PENode).mutableChildren.index(of: parent!)
//key = arrayPrefixId + String(index) + arraySuffixId
key = localizedItem + " " + String(index)
}
if keyPaths.count == 0 {
keyPaths.add(key)
} else {
keyPaths.insert(key, at: 0)
}
parent = parent?.parent as? PENode
}
let menu = NSMenu(title: "Contextual Menu")
let row = outlineView.row(forItem: item)
if row > 0 && (outlineView.editorVC?.isEditable)! {
menu.addItem(withTitle: "Cut".locale,
action: #selector(outlineView.cut(_:)),
keyEquivalent: "X")
}
menu.addItem(withTitle: "Copy".locale,
action: #selector(outlineView.copy(_:)),
keyEquivalent: "C")
if (outlineView.editorVC?.isEditable)! {
menu.addItem(withTitle: "Paste".locale,
action: #selector(outlineView.paste(_:)),
keyEquivalent: "V")
} else {
return menu
}
return menu
}
func menu(_ menu: NSMenu, willHighlight item: NSMenuItem?) {
print("menu(_ menu: NSMenu, willHighlight item: NSMenuItem?)")
}
/*
Override the validation for the textfield:
Don't accept first responder on textfield if the event is called with a menu (left mouse down)
if the row is not selected don't become first responder!
if already selected 1 click its enough to begin editing!
*/
override func validateProposedFirstResponder(_ responder: NSResponder, for event: NSEvent?) -> Bool {
if responder is PETextField {
//let field = responder as! PETextField
if (event != nil) {
if event?.type == NSEvent.EventType.rightMouseDown {
return false
} else if event?.type == NSEvent.EventType.leftMouseDown {
let selected = self.selectedRow
if selected > 0 {
let point = self.convert((event?.locationInWindow)!, from: nil)
let row = self.row(at: point)
if self.selectedRow == row {
return true
}
self.selectRowIndexes(IndexSet(integer: selected), byExtendingSelection: false)
}
return false
}
}
}
return super.validateProposedFirstResponder(responder, for: event)
}
@objc func cut(_: Any) {
if !(self.editorVC?.isEditable)! {
NSSound.beep()
return
}
let selected = self.selectedRow
if selected >= 0 {
// don't cut Root!
if selected == 0 {
NSSound.beep()
return
}
if let item = self.item(atRow: selected) {
let root = PENode(representedObject: TagData(key: "Root",
type: .Dictionary,
value: nil))
let parent = (item as! PENode).parent
if parent == nil {
NSSound.beep()
return
}
root.mutableChildren.add((item as! PENode).copy())
let plist = gConvertPENodeToPlist(node: root)
let pasteboard = NSPasteboard.general
pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil)
pasteboard.setString(plist, forType: NSPasteboard.PasteboardType.string)
// cut
let indexChild = parent?.children?.firstIndex(of: item as! PENode)
self.undo_Cut(item: item as! PENode,
inParent:parent as! PENode,
indexChild: indexChild!)
} else {
NSSound.beep()
}
}
else
{
NSSound.beep()
}
}
@objc func copy(_: Any) {
let selected = self.selectedRow
if selected > 0 {
let item = self.item(atRow: selected)
let root = PENode(representedObject: TagData(key: "Root",
type: .Dictionary,
value: nil))
root.mutableChildren.add((item! as! PENode).copy())
let plist = gConvertPENodeToPlist(node: root)
let pasteboard = NSPasteboard.general
pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil)
pasteboard.setString(plist, forType: NSPasteboard.PasteboardType.string)
} else {
NSSound.beep()
}
}
@objc func paste(_: Any) {
// paste the NSPasteboardTypeString content, if is a plist and if a row is selected.
//FIXME: check paste op on the root
if !(self.editorVC?.isEditable)! {
NSSound.beep()
return
}
let selected = self.selectedRow
if selected >= 0 {
let plistType = (self.item(atRow: 0) as! PENode).tagdata!.type
if plistType == .Dictionary || plistType == .Array {
let node = self.item(atRow: selected) as! PENode
let pasteboard = NSPasteboard.general
var newNode : PENode
let data = pasteboard.string(forType: NSPasteboard.PasteboardType.string)!.data(using: .utf8)!
let parser = PlistParser(fromData: data, fileType: .xmlPlistv1)
if (parser.root != nil) {
newNode = parser.root!.mutableChildren[0] as! PENode
} else {
let data = TagData(key: localizedNewItem,
type: .String,
value: pasteboard.string(forType: NSPasteboard.PasteboardType.string)! as NSString)
newNode = PENode(representedObject: data)
}
// Are we pasting on Root and is a contenitor?
if (node == self.item(atRow: 0) as! PENode) {
if !self.isItemExpanded(node) {
self.expandItem(node)
}
gDeduplicateKeyInParent(parent: node, newNode: newNode)
self.undo_Paste(item: newNode, inParent: node, indexChild: 0)
} else {
let parent = node.parent
var indexChild = (parent?.children?.firstIndex(of: node))! as Int
indexChild += 1
// isLeaf strictly means "no children", but Dictionaries and Array are contenitors anyway!
if node.tagdata!.type == .Array || node.tagdata!.type == .Dictionary {
let isExpanded = self.isItemExpanded(node)
if isExpanded {
gDeduplicateKeyInParent(parent: node, newNode: newNode)
self.undo_Paste(item: newNode, inParent: node, indexChild: 0)
} else {
gDeduplicateKeyInParent(parent: parent as! PENode, newNode: newNode)
self.undo_Paste(item: newNode, inParent: parent! as! PENode, indexChild: indexChild)
}
} else {
gDeduplicateKeyInParent(parent: parent as! PENode, newNode: newNode)
self.undo_Paste(item: newNode, inParent: parent! as! PENode, indexChild: indexChild)
}
}
} else {
// plist is not a dictionary or array
NSSound.beep()
}
} else {
// no selected row
NSSound.beep()
}
}
//MARK: add/remove functions (called from PETableCellViewKey)
@objc func addNewItemFromCell(node: PENode, parent: PENode) {
self.editorVC?.isAddingNewItem = true
let newItemName = localizedNewItem
let newNode = PENode(representedObject: TagData(key: newItemName,
type: .String,
value: "" as NSString))
var indexChild = (parent.children?.firstIndex(of: node))! as Int
indexChild += 1
// isLeaf strictly means "no children", but Dictionaries and Array are contenitors anyway!
if node.tagdata!.type == .Array || node.tagdata!.type == .Dictionary {
if self.isItemExpanded(node) {
gDeduplicateKeyInParent(parent: node, newNode: newNode)
self.undo_Add(item: newNode, inParent: node, indexChild: 0, target: self)
} else {
gDeduplicateKeyInParent(parent: parent, newNode: newNode)
self.undo_Add(item: newNode, inParent: parent, indexChild: indexChild, target: self)
}
} else {
gDeduplicateKeyInParent(parent: parent, newNode: newNode)
self.undo_Add(item: newNode, inParent: parent, indexChild: indexChild, target: self)
}
self.editorVC?.isAddingNewItem = false
}
func removeItemFromCell(node: PENode, parent: PENode) {
let indexChild = parent.mutableChildren.index(of: node)
self.undo_Remove(item: node, inParent: parent, indexChild: indexChild, target: self)
}
}

View File

@ -0,0 +1,133 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
@available(OSX 10.11, *)
final class PEPopUpButton: NSPopUpButton {
internal var n: PENode?
internal var o: PEOutlineView?
var node: PENode? {
get {
return self.n
} set {
self.n = newValue
}
}
override public var alignmentRectInsets: NSEdgeInsets {
return NSEdgeInsetsMake(0, 0, 0, 0)
}
override func willOpenMenu(_ menu: NSMenu, with event: NSEvent) {
if (self.outline != nil) {
/*
the following is needed when user click on the type column:
He can have ongoing edits on some rows, so that changing first responder
We can aesily inform the outline that We're playing with something else
and have to force ask the user in case edits are wrong.
This also prevent a crash.
*/
if (self.outline?.wrongValue)! {
menu.cancelTrackingWithoutAnimation() // this grant the popup menu to not showup
}
self.window?.makeFirstResponder(self)
self.window?.makeFirstResponder(self.outline)
/*
Also consider that PlistEditor() will resign first responder the PETextField involved,
just to ensure that user will not be able to leave fields with invalid datas
*/
}
}
var outline: PEOutlineView? {
get {
return self.o
} set {
self.o = newValue
}
}
func setAsBool() {
self.removeAllItems()
self.addItems(withTitles: [localizedNo, localizedYes])
}
func setAsRoot() {
self.removeAllItems()
self.addItem(withTitle: gPlistTagStr(tag: .Dictionary).locale)
self.lastItem?.representedObject = PlistTag.Dictionary
self.addItem(withTitle: gPlistTagStr(tag: .Dictionary).locale)
self.lastItem?.representedObject = PlistTag.Dictionary
}
func setAsAllType() {
self.removeAllItems()
self.addItem(withTitle: gPlistTagStr(tag: .Dictionary).locale)
self.lastItem?.representedObject = PlistTag.Dictionary
self.addItem(withTitle: gPlistTagStr(tag: .Array).locale)
self.lastItem?.representedObject = PlistTag.Array
self.addItem(withTitle: gPlistTagStr(tag: .String).locale)
self.lastItem?.representedObject = PlistTag.String
self.addItem(withTitle: gPlistTagStr(tag: .Number).locale)
self.lastItem?.representedObject = PlistTag.Number
self.addItem(withTitle: gPlistTagStr(tag: .Bool).locale)
self.lastItem?.representedObject = PlistTag.Bool
self.addItem(withTitle: gPlistTagStr(tag: .Data).locale)
self.lastItem?.representedObject = PlistTag.Data
self.addItem(withTitle: gPlistTagStr(tag: .Date).locale)
self.lastItem?.representedObject = PlistTag.Date
}
}
class PEPopUpButtonCell: NSPopUpButtonCell {
override func drawTitle(_ title: NSAttributedString,
withFrame frame: NSRect,
in controlView: NSView) -> NSRect {
/*
overriding this method give to the title color the right white color
instead of gray under dark mode. Can change in future so, again, the following method
will help adjusting the title color. For now just returning super!
*/
return super.drawTitle(title, withFrame:frame, in:controlView)
/*
var attributedTitle = title
if let popUpButton = self.controlView as? PEPopUpButton {
if let object = popUpButton.selectedItem?.representedObject as? Dictionary<String, String> {
if let shortTitle = object["shortTitle"] {
attributedTitle = NSAttributedString(string:shortTitle, attributes:title.attributes(at:0, effectiveRange:nil))
}
}
}
return super.drawTitle(attributedTitle, withFrame:frame, in:controlView)
*/
}
}

View File

@ -0,0 +1,60 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
//MARK: PETextField (NSTextField)
@available(OSX 10.11, *)
final class PEReplaceField: NSSearchField {
var editorVC : PlistEditorVC?
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.canDrawConcurrently = AppSD.canDrawConcurrently
self.wantsLayer = true
let c = self.cell as? NSSearchFieldCell
let image = NSImage(named: "Replace")?.copy() as! NSImage
image.isTemplate = true
image.size = NSMakeSize(13, 13)
c?.searchButtonCell?.image = image
c?.searchButtonCell?.imageScaling = .scaleNone
self.placeholderString = localizedReplace
}
override func textDidChange(_ aNotification: Notification) {
NotificationCenter.default.post(name: PESearchFieldTextDidChange,
object: self,
userInfo: aNotification.userInfo)
}
}

View File

@ -0,0 +1,38 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
final class PESearchField: NSSearchField {
@IBOutlet var countLabel : NSTextField? = nil
override func textDidChange(_ aNotification: Notification) {
NotificationCenter.default.post(name: PESearchFieldTextDidChange,
object: self,
userInfo: aNotification.userInfo)
}
}

View File

@ -0,0 +1,157 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
import CoreData
//MARK: PETableCellView (NSTableCellView)
@available(OSX 10.11, *)
final class PETableCellView: NSTableCellView {
@IBOutlet var addButton : NSButton!
@IBOutlet var removeButton : NSButton!
@IBOutlet var buttonsView: NSView!
@IBOutlet var buttonsViewWidthConstraint: NSLayoutConstraint!
var trackingArea: NSTrackingArea?
var scroller : NSScrollView? = nil
var outline : PEOutlineView?
var node : PENode?
var type : PETableCellViewType = .key
var field: PETextField? {
get {
return self.textField as? PETextField
}
set {
self.textField = newValue
}
}
func setup(outline: PEOutlineView, column: Int, type: PETableCellViewType) {
// actions
if type == .key {
self.addButton.ignoresMultiClick = true
self.addButton.target = self
self.addButton.action = #selector(self.addNewItem)
self.removeButton.ignoresMultiClick = true
self.removeButton.target = self
self.removeButton.action = #selector(self.removeItem)
}
self.type = type
self.outline = outline
self.field?.column = column
self.field?.delegate = self.outline?.editorVC
self.field?.outline = self.outline
self.scroller = outline.enclosingScrollView
}
@objc func reloadItemInParent(_ aNotification: Notification) {
let parent = self.node?.peparent
let ro : TagData? = parent?.tagdata
if (parent != nil) && ((ro != nil)) && (ro!.type == .Array) {
let childIndex : Int = parent!.mutableChildren.index(of: self.node!)
DispatchQueue.main.async {
self.textField?.animator().stringValue = "Item \(childIndex)"
}
}
}
override var backgroundStyle: NSView.BackgroundStyle {
didSet {
if #available(OSX 10.13, *) { /* do nothing */ } else {
if self.backgroundStyle == .light {
self.textField?.textColor = NSColor.controlTextColor
} else if self.backgroundStyle == .dark {
self.textField?.textColor = NSColor.alternateSelectedControlTextColor
}
}
}
}
@objc func addNewItem() {
if (self.node != nil) && (self.node?.parent != nil) && !(self.outline?.wrongValue)! {
self.outline?.addNewItemFromCell(node: self.node!, parent: self.node?.parent as! PENode)
self.hideButtons()
} else {
//
NSSound.beep()
self.window?.makeFirstResponder(self.outline)
}
}
@objc func removeItem() {
let parent = self.node?.parent
if (self.node != nil) && (parent != nil) && !(self.outline?.wrongValue)! {
self.outline?.removeItemFromCell(node: self.node!, parent: parent as! PENode)
} else {
NSSound.beep()
self.window?.makeFirstResponder(self.outline)
}
}
@objc func hideButtons() {
self.buttonsViewWidthConstraint.constant = 0
}
override func mouseDown(with theEvent: NSEvent) { }
override func mouseUp(with theEvent: NSEvent) { }
override func mouseEntered(with theEvent: NSEvent) {
if self.type == .key &&
self.outline?.editorVC != nil &&
self.outline!.editorVC!.isEditable &&
self.window!.isKeyWindow {
self.buttonsViewWidthConstraint.constant = 46
}
}
override func mouseExited(with event: NSEvent) {
self.hideButtons()
}
override func rightMouseDown(with event: NSEvent) { }
override func updateTrackingAreas() {
if self.type == .key {
if self.trackingArea != nil {
self.removeTrackingArea(self.trackingArea!)
}
self.trackingArea = NSTrackingArea(rect: self.bounds,
options: [.activeAlways,/*.activeInKeyWindow, */.mouseEnteredAndExited] ,
owner: self, userInfo: nil)
self.addTrackingArea(self.trackingArea!)
}
}
}

View File

@ -0,0 +1,66 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
//MARK: PETableCellViewPop (NSTableCellView)
@available(OSX 10.11, *)
final class PETableCellViewPop: NSTableCellView {
var type : PETableCellViewType = .tags
@IBOutlet var popup : PEPopUpButton!
override init(frame frameRect: NSRect) {
super.init(frame:frameRect)
self.wantsLayer = true
self.canDrawSubviewsIntoLayer = true
self.canDrawConcurrently = AppSD.canDrawConcurrently
}
required init(coder: NSCoder) {
super.init(coder: coder)!
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
}
func setup(with type: PETableCellViewType, outline: PEOutlineView) {
self.type = type
self.popup.outline = outline
self.popup.target = self.popup?.outline?.editorVC
switch self.type {
case .bool:
self.popup.action = #selector(self.popup.outline!.editorVC!.boolPopUpPressed)
case .tags:
self.popup.action = #selector(self.popup.outline!.editorVC!.typePopUpPressed)
case .value: fallthrough
case .key:
break // not here
}
}
}

View File

@ -0,0 +1,190 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
enum RowViewBorderType : Int {
case none = 0
case verticalOnly = 2
case verticalAndBottom = 3
}
@available(OSX 10.11, *)
final class PETableRowView: NSTableRowView {
internal var n: PENode?
internal var o: PEOutlineView? = nil
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.wantsLayer = true
self.canDrawSubviewsIntoLayer = true
self.canDrawConcurrently = AppSD.canDrawConcurrently
self.isEmphasized = true
//self.postsBoundsChangedNotifications = true
}
required init?(coder decoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var outline: PEOutlineView? {
get {
return self.o
} set {
self.o = newValue
}
}
var node: PENode? {
get {
return self.n
} set {
self.n = newValue
}
}
func setBorderType() {
super.setNeedsDisplay(self.bounds)
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
var bt : RowViewBorderType = .none
if self.isSelected {
self.highLightAllBorders(in: dirtyRect)
} else {
let row : Int = self.outline?.selectedRow ?? -1
if row >= 0 {
if let selNode = self.outline?.item(atRow: row) as? PENode {
// determine if self.node is a child (or sub child of selfNode)
let ipsc = "\(self.node!.indexPath)".trimmingCharacters(in: CharacterSet(arrayLiteral: "]"))
let ips = "\(selNode.indexPath)".trimmingCharacters(in: CharacterSet(arrayLiteral: "]")) + ","
if ipsc.hasPrefix(ips) {
bt = .verticalOnly
let childs = selNode.mutableChildren
if childs.count > 0 && (childs.lastObject as! PENode) == self.node {
bt = .verticalAndBottom
}
}
}
}
}
switch bt {
case .none: break // taken into account previously
case.verticalAndBottom:
self.highLightLeftBorder(in: dirtyRect)
self.highLightRightBorder(in: dirtyRect)
self.highLightBottomBorder(in: dirtyRect)
case .verticalOnly:
self.highLightLeftBorder(in: dirtyRect)
self.highLightRightBorder(in: dirtyRect)
}
}
private func borderColor() -> NSColor {
return NSColor.alternateSelectedControlColor
}
private func lineWidth() -> CGFloat {
return 2.5
}
private func highLightAllBorders(in dirtyRect: NSRect) {
let origin = NSMakePoint(dirtyRect.origin.x, dirtyRect.origin.y)
var rect = NSRect.zero
rect.origin = origin
rect.size.width = dirtyRect.size.width
rect.size.height = dirtyRect.size.height
let path: NSBezierPath = NSBezierPath(rect: rect)
path.lineWidth = self.lineWidth()
self.borderColor().set()
path.stroke()
}
private func highLightRightBorder(in dirtyRect: NSRect) {
let origin = NSMakePoint(dirtyRect.origin.x, dirtyRect.origin.y)
var rect = NSRect.zero
rect.origin = origin
rect.size.width = dirtyRect.size.width
rect.size.height = dirtyRect.size.height
let path: NSBezierPath = NSBezierPath()
path.lineWidth = self.lineWidth()
path.move(to: NSMakePoint(rect.size.width - 1, 0))
// draw right vertical side
path.line(to: NSMakePoint(rect.size.width - 1, rect.size.height))
self.borderColor().set()
path.stroke()
}
private func highLightLeftBorder(in dirtyRect: NSRect) {
let origin = NSMakePoint(dirtyRect.origin.x, dirtyRect.origin.y)
var rect = NSRect.zero
rect.origin = origin
rect.size.width = dirtyRect.size.width
rect.size.height = dirtyRect.size.height
let path: NSBezierPath = NSBezierPath()
path.lineWidth = self.lineWidth()
path.move(to: NSMakePoint(1, 0))
// draw right vertical side
path.line(to: NSMakePoint(1, rect.size.width))
self.borderColor().set()
path.stroke()
}
private func highLightBottomBorder(in dirtyRect: NSRect) {
let origin = NSMakePoint(dirtyRect.origin.x, dirtyRect.origin.y)
var rect = NSRect.zero
rect.origin = origin
rect.size.width = dirtyRect.size.width
rect.size.height = dirtyRect.size.height
let path: NSBezierPath = NSBezierPath()
path.lineWidth = self.lineWidth()
path.move(to: NSMakePoint(0, rect.size.height))
// draw orizzontal-bottom side
path.line(to: NSMakePoint(rect.size.width, rect.size.height))
self.borderColor().set()
path.stroke()
}
private func isParent() -> Bool {
let type : PlistTag = self.node!.tagdata!.type
return type == .Array || type == .Dictionary
}
private func getParent(of node: PENode?) -> PENode? {
if let parent : PENode = node?.parent as? PENode {
let root : PENode? = self.outline?.item(atRow: 0) as? PENode
if parent != root {
return parent
}
}
return nil
}
}

View File

@ -0,0 +1,102 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
//MARK: PETextField (NSTextField)
@available(OSX 10.11, *)
final class PETextField: NSTextField, NSTextViewDelegate {
var outline : PEOutlineView? = nil
var node : PENode? = nil
var column : Int = -1
override var intrinsicContentSize: NSSize {
get {
return NSMakeSize(self.frame.size.width, super.intrinsicContentSize.height)
}
}
override var lineBreakMode: NSLineBreakMode {
get {
return .byTruncatingTail
}
set {
super.lineBreakMode = .byTruncatingTail
}
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.wantsLayer = true
self.canDrawConcurrently = AppSD.canDrawConcurrently
self.cell = PETextFieldCell()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func mouseDown(with event: NSEvent) {
// doing nothing
}
override func textShouldBeginEditing(_ textObject: NSText) -> Bool {
return (self.outline?.editorVC?.isEditable)!
}
override func textShouldEndEditing(_ textObject: NSText) -> Bool {
return true
}
func textViewDidChangeSelection(_ notification: Notification) {
if let cur = self.currentEditor() {
if cur.selectedRange.length > 0 {
self.textColor = NSColor.controlTextColor
}
}
}
override func updateLayer() {
super.updateLayer()
//if Thread.isMainThread {
// self.invalidateIntrinsicContentSize()
//}
}
}
@available(OSX 10.11, *)
final class PETextFieldCell : NSTextFieldCell {
override func drawingRect(forBounds rect: NSRect) -> NSRect {
let height : CGFloat = 14//rect.size.height
let y = ((rect.size.height - height) / 2)
let newRect = NSRect(x: 0, y: y, width: rect.size.width, height: height)
return super.drawingRect(forBounds: newRect)
}
}

View File

@ -0,0 +1,402 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment version="101100" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15702"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Window Controller-->
<scene sceneID="R2V-B0-nI4">
<objects>
<windowController storyboardIdentifier="Document Window Controller" id="jGA-0Y-lOj" customClass="PlistEditorWC" customModule="Clover" customModuleProvider="target" sceneMemberID="viewController">
<window key="window" title="Property List Editor" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="Ckk-yw-fiv">
<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="427" y="498" width="570" height="424"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<value key="minSize" type="size" width="570" height="63"/>
<connections>
<outlet property="delegate" destination="jGA-0Y-lOj" id="98r-iN-zZc"/>
</connections>
</window>
<connections>
<segue destination="5gI-5U-AMq" kind="relationship" relationship="window.shadowedContentViewController" id="nsd-lR-9xd"/>
</connections>
</windowController>
<customObject id="6f7-a7-6o1" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="82" y="344"/>
</scene>
<!--Plist EditorVC-->
<scene sceneID="hIz-AP-VOD">
<objects>
<viewController id="5gI-5U-AMq" customClass="PlistEditorVC" customModule="Clover" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" wantsLayer="YES" id="rwV-zf-hpT">
<rect key="frame" x="0.0" y="0.0" width="568" height="423"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<splitView arrangesAllSubviews="NO" dividerStyle="thin" translatesAutoresizingMaskIntoConstraints="NO" id="AXh-Wi-ja8">
<rect key="frame" x="0.0" y="0.0" width="568" height="423"/>
<subviews>
<customView id="pl5-54-raW">
<rect key="frame" x="0.0" y="0.0" width="568" height="58"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="f5f-vb-JDD" customClass="ReplaceButton" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="0.0" y="24" width="32" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="1ls-PK-4Lv"/>
</constraints>
<buttonCell key="cell" type="push" bezelStyle="rounded" imagePosition="overlaps" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="jFu-Mz-fHK">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="showReplaceViewWithSender:" target="5gI-5U-AMq" id="atr-1K-cE2"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CjQ-1i-oKD" customClass="FindButton" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
<buttonCell key="cell" type="push" bezelStyle="rounded" imagePosition="overlaps" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="rsa-xW-mCe">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="showFindViewWithSender:" target="5gI-5U-AMq" id="tUA-X4-eWw"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ilk-Ta-1NW">
<rect key="frame" x="536" y="23" width="32" height="33"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="2BP-mm-vHy"/>
</constraints>
<buttonCell key="cell" type="push" bezelStyle="rounded" image="NSStopProgressTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="enu-el-QQ6">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<searchField wantsLayer="YES" verticalHuggingPriority="750" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7Wv-ZY-JzG" customClass="PESearchField" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="30" y="31" width="343" height="21"/>
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" id="oQI-NZ-Akk">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</searchFieldCell>
<connections>
<outlet property="countLabel" destination="jd2-u3-VLB" id="lgt-7a-34s"/>
</connections>
</searchField>
<searchField wantsLayer="YES" verticalHuggingPriority="750" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bgd-lF-t3r" customClass="PEReplaceField" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="30" y="7" width="343" height="21"/>
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" id="Icu-Xk-a2R">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</searchFieldCell>
</searchField>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="32r-Nm-cON">
<rect key="frame" x="379" y="5" width="182" height="24"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="momentary" id="hrz-X2-ewv">
<font key="font" metaFont="system"/>
<segments>
<segment label="All" width="73"/>
<segment label="Replace" width="105" tag="1"/>
</segments>
</segmentedCell>
</segmentedControl>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jd2-u3-VLB">
<rect key="frame" x="311" y="34" width="37" height="14"/>
<constraints>
<constraint firstAttribute="width" constant="33" id="dsC-nE-aXf"/>
</constraints>
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="right" title="Label" id="Hao-mW-Dwn">
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="HDq-VH-JlL">
<rect key="frame" x="379" y="29" width="163" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="159" id="Mhz-ZO-mK0"/>
</constraints>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="momentary" id="I4H-p8-bsp">
<font key="font" metaFont="system"/>
<segments>
<segment image="NSLeftFacingTriangleTemplate" width="78"/>
<segment image="NSRightFacingTriangleTemplate" width="78" tag="1"/>
</segments>
</segmentedCell>
</segmentedControl>
</subviews>
<constraints>
<constraint firstItem="bgd-lF-t3r" firstAttribute="baseline" secondItem="CjQ-1i-oKD" secondAttribute="firstBaseline" id="19i-v0-bLC"/>
<constraint firstItem="32r-Nm-cON" firstAttribute="leading" secondItem="bgd-lF-t3r" secondAttribute="trailing" constant="8" symbolic="YES" id="7Cs-e5-3mY"/>
<constraint firstItem="HDq-VH-JlL" firstAttribute="leading" secondItem="7Wv-ZY-JzG" secondAttribute="trailing" constant="8" symbolic="YES" id="7G5-bS-GBT"/>
<constraint firstItem="Ilk-Ta-1NW" firstAttribute="leading" secondItem="HDq-VH-JlL" secondAttribute="trailing" constant="2" id="7Jt-3k-pUB"/>
<constraint firstItem="f5f-vb-JDD" firstAttribute="trailing" secondItem="CjQ-1i-oKD" secondAttribute="trailing" id="8Sy-kh-PjV"/>
<constraint firstItem="bgd-lF-t3r" firstAttribute="baseline" secondItem="32r-Nm-cON" secondAttribute="baseline" id="8mL-f1-sfK"/>
<constraint firstItem="f5f-vb-JDD" firstAttribute="leading" secondItem="pl5-54-raW" secondAttribute="leading" constant="6" id="92x-jk-bMW"/>
<constraint firstItem="jd2-u3-VLB" firstAttribute="firstBaseline" secondItem="HDq-VH-JlL" secondAttribute="baseline" id="DsB-IM-eLK"/>
<constraint firstItem="f5f-vb-JDD" firstAttribute="top" secondItem="7Wv-ZY-JzG" secondAttribute="top" id="EUO-ki-9yh"/>
<constraint firstItem="7Wv-ZY-JzG" firstAttribute="leading" secondItem="f5f-vb-JDD" secondAttribute="trailing" constant="4" id="GGZ-Bi-b5f"/>
<constraint firstItem="7Wv-ZY-JzG" firstAttribute="baseline" secondItem="f5f-vb-JDD" secondAttribute="firstBaseline" id="Jef-9b-99Y"/>
<constraint firstItem="CjQ-1i-oKD" firstAttribute="top" secondItem="bgd-lF-t3r" secondAttribute="top" id="P68-zx-2Ps"/>
<constraint firstItem="7Wv-ZY-JzG" firstAttribute="baseline" secondItem="HDq-VH-JlL" secondAttribute="baseline" id="VRR-ns-fTy"/>
<constraint firstItem="jd2-u3-VLB" firstAttribute="baseline" secondItem="HDq-VH-JlL" secondAttribute="firstBaseline" id="Vo7-0d-7vJ"/>
<constraint firstItem="CjQ-1i-oKD" firstAttribute="top" secondItem="f5f-vb-JDD" secondAttribute="bottom" constant="3" id="W4d-UM-Itx"/>
<constraint firstItem="7Wv-ZY-JzG" firstAttribute="leading" secondItem="bgd-lF-t3r" secondAttribute="leading" id="bOE-a9-1xQ"/>
<constraint firstAttribute="height" constant="58" id="cSS-9y-hU6"/>
<constraint firstAttribute="trailing" secondItem="32r-Nm-cON" secondAttribute="trailing" constant="9" id="e3U-pX-dS1"/>
<constraint firstItem="HDq-VH-JlL" firstAttribute="top" secondItem="Ilk-Ta-1NW" secondAttribute="top" id="ibe-bf-QYG"/>
<constraint firstItem="f5f-vb-JDD" firstAttribute="top" secondItem="pl5-54-raW" secondAttribute="top" constant="6" id="l8K-20-Ma8"/>
<constraint firstItem="HDq-VH-JlL" firstAttribute="leading" secondItem="jd2-u3-VLB" secondAttribute="trailing" constant="35" id="m1y-bt-kbV"/>
<constraint firstItem="jd2-u3-VLB" firstAttribute="centerY" secondItem="Ilk-Ta-1NW" secondAttribute="centerY" id="pLJ-Vu-Qou"/>
<constraint firstItem="f5f-vb-JDD" firstAttribute="leading" secondItem="CjQ-1i-oKD" secondAttribute="leading" id="v7b-0r-gnh"/>
<constraint firstItem="HDq-VH-JlL" firstAttribute="leading" secondItem="32r-Nm-cON" secondAttribute="leading" id="vLw-ST-pEh"/>
<constraint firstAttribute="trailing" secondItem="Ilk-Ta-1NW" secondAttribute="trailing" constant="6" id="ySS-D2-EAo"/>
</constraints>
</customView>
<customView fixedFrame="YES" id="Ejg-Hv-w6Y">
<rect key="frame" x="0.0" y="59" width="568" height="364"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="18" horizontalPageScroll="10" verticalLineScroll="18" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bIm-n2-Z3h">
<rect key="frame" x="0.0" y="0.0" width="568" height="366"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" ambiguous="YES" id="qrn-6L-y8h">
<rect key="frame" x="1" y="0.0" width="566" height="365"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="18" headerView="7Nk-Jw-tEm" viewBased="YES" floatsGroupRows="NO" indentationPerLevel="16" outlineTableColumn="X9J-xy-hX5" id="RNB-nt-8rR" customClass="PEOutlineView" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="566" height="340"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="keyColumn" width="250" minWidth="130" maxWidth="10000" id="X9J-xy-hX5">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Key">
<font key="font" metaFont="message" size="11"/>
<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="LVi-ta-OMQ">
<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="keyColumn" id="22d-da-f6t" customClass="PETableCellView" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="250" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="0hm-Vs-Zmm">
<rect key="frame" x="2" y="0.0" width="247" height="18"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="5TZ-Az-hyl" customClass="PETextField" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="-2" y="0.0" width="251" height="18"/>
<constraints>
<constraint firstAttribute="height" constant="18" id="7tz-s6-Lrv"/>
</constraints>
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" title="Table View Cell" id="TeM-gI-jED" customClass="PETextFieldCell" customModule="Clover" customModuleProvider="target">
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="5TZ-Az-hyl" secondAttribute="trailing" id="RTM-O5-X7J"/>
<constraint firstAttribute="bottom" secondItem="5TZ-Az-hyl" secondAttribute="bottom" id="Sw6-XX-cCD"/>
<constraint firstItem="5TZ-Az-hyl" firstAttribute="top" secondItem="0hm-Vs-Zmm" secondAttribute="top" id="gqb-2d-jCL"/>
<constraint firstItem="5TZ-Az-hyl" firstAttribute="leading" secondItem="0hm-Vs-Zmm" secondAttribute="leading" id="wsD-dw-QMl"/>
</constraints>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="mZc-eX-BQC">
<rect key="frame" x="250" y="0.0" width="0.0" height="18"/>
<subviews>
<button verticalHuggingPriority="750" id="axH-cK-qTb">
<rect key="frame" x="0.0" y="0.0" width="18" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="NSAddTemplate" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="1Rt-Pk-fry">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button verticalHuggingPriority="750" id="nTZ-Fc-JgW">
<rect key="frame" x="28" y="0.0" width="18" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="NSRemoveTemplate" imagePosition="overlaps" alignment="center" imageScaling="proportionallyDown" inset="2" id="eNR-b9-Q89">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</subviews>
<constraints>
<constraint firstAttribute="width" id="M9v-d8-uoy"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstItem="0hm-Vs-Zmm" firstAttribute="centerY" secondItem="22d-da-f6t" secondAttribute="centerY" id="9M8-a7-Mqy"/>
<constraint firstItem="0hm-Vs-Zmm" firstAttribute="top" secondItem="mZc-eX-BQC" secondAttribute="top" id="K8n-fy-scd"/>
<constraint firstAttribute="trailing" secondItem="mZc-eX-BQC" secondAttribute="trailing" id="goB-Oz-t67"/>
<constraint firstItem="0hm-Vs-Zmm" firstAttribute="bottom" secondItem="mZc-eX-BQC" secondAttribute="bottom" id="mPh-h6-8wP"/>
<constraint firstItem="0hm-Vs-Zmm" firstAttribute="leading" secondItem="22d-da-f6t" secondAttribute="leading" constant="2" id="qMI-uM-dUi"/>
<constraint firstItem="mZc-eX-BQC" firstAttribute="leading" secondItem="0hm-Vs-Zmm" secondAttribute="trailing" constant="1" id="y2K-l6-Sky"/>
</constraints>
<connections>
<outlet property="addButton" destination="axH-cK-qTb" id="msK-09-e63"/>
<outlet property="buttonsView" destination="mZc-eX-BQC" id="Js0-jz-OfL"/>
<outlet property="buttonsViewWidthConstraint" destination="M9v-d8-uoy" id="cmv-8N-K0l"/>
<outlet property="removeButton" destination="nTZ-Fc-JgW" id="rve-XC-3MT"/>
<outlet property="textField" destination="5TZ-Az-hyl" id="lnR-GR-tcS"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="typeColumn" width="88" minWidth="88" maxWidth="88" id="bGL-SR-Wh3">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Tag">
<font key="font" metaFont="message" size="11"/>
<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="XBx-pA-Iw5">
<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="typeColumn" id="gHc-lQ-Qof" customClass="PETableCellViewPop" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="250" y="0.0" width="88" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<popUpButton translatesAutoresizingMaskIntoConstraints="NO" id="k2B-pt-n9Q" customClass="PEPopUpButton" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="3" y="-1" width="82" height="20"/>
<popUpButtonCell key="cell" type="bevel" bezelStyle="rounded" alignment="left" controlSize="small" lineBreakMode="truncatingTail" imageScaling="proportionallyDown" inset="2" id="4Az-is-Owj" customClass="PEPopUpButtonCell" customModule="Clover" customModuleProvider="target">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="message" size="11"/>
<menu key="menu" id="79b-Hx-UYz"/>
</popUpButtonCell>
</popUpButton>
</subviews>
<constraints>
<constraint firstItem="k2B-pt-n9Q" firstAttribute="centerY" secondItem="gHc-lQ-Qof" secondAttribute="centerY" id="NQ1-Of-GiK"/>
<constraint firstAttribute="trailing" secondItem="k2B-pt-n9Q" secondAttribute="trailing" constant="3" id="W6y-3k-U9t"/>
<constraint firstItem="k2B-pt-n9Q" firstAttribute="top" secondItem="gHc-lQ-Qof" secondAttribute="top" constant="-1" id="YiU-2w-Tl6"/>
<constraint firstItem="k2B-pt-n9Q" firstAttribute="leading" secondItem="gHc-lQ-Qof" secondAttribute="leading" constant="3" id="ift-jN-Nxm"/>
</constraints>
<connections>
<outlet property="popup" destination="k2B-pt-n9Q" id="mwx-yY-5NI"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="valueColumn" width="228" minWidth="30" maxWidth="3.4028234663852886e+38" id="0dr-UX-s5d">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Value">
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="Chl-KY-FFQ">
<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 id="evN-Wc-0mr" customClass="PETableCellView" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="338" y="0.0" width="228" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="dJN-3b-nDD" customClass="PETextField" customModule="Clover" customModuleProvider="target">
<rect key="frame" x="0.0" y="1" width="228" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="17" id="2fe-wh-Byc"/>
</constraints>
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" title="Table View Cell" id="iif-L9-91I" customClass="PETextFieldCell" customModule="Clover" customModuleProvider="target">
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="dJN-3b-nDD" firstAttribute="centerX" secondItem="evN-Wc-0mr" secondAttribute="centerX" id="Oxi-u7-QXH"/>
<constraint firstItem="dJN-3b-nDD" firstAttribute="leading" secondItem="evN-Wc-0mr" secondAttribute="leading" constant="2" id="kub-J2-9Xh"/>
<constraint firstItem="dJN-3b-nDD" firstAttribute="centerY" secondItem="evN-Wc-0mr" secondAttribute="centerY" id="wqd-wO-Q9p"/>
</constraints>
<connections>
<outlet property="textField" destination="dJN-3b-nDD" id="DHW-Y8-dLJ"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn>
</tableColumns>
<connections>
<outlet property="dataSource" destination="5gI-5U-AMq" id="ZUW-ih-SLm"/>
<outlet property="delegate" destination="5gI-5U-AMq" id="jbP-Bu-M4O"/>
</connections>
</outlineView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="jjX-ox-rJd">
<rect key="frame" x="1" y="92" width="362" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="RDZ-64-PQC">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="7Nk-Jw-tEm">
<rect key="frame" x="0.0" y="0.0" width="566" height="25"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
</subviews>
</customView>
</subviews>
<holdingPriorities>
<real value="58"/>
<real value="250"/>
</holdingPriorities>
</splitView>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="AXh-Wi-ja8" secondAttribute="bottom" id="2dM-ER-ind"/>
<constraint firstAttribute="trailing" secondItem="AXh-Wi-ja8" secondAttribute="trailing" id="Cjs-Nq-cEH"/>
<constraint firstItem="AXh-Wi-ja8" firstAttribute="leading" secondItem="rwV-zf-hpT" secondAttribute="leading" id="P0p-Uf-G8a"/>
<constraint firstItem="AXh-Wi-ja8" firstAttribute="top" secondItem="rwV-zf-hpT" secondAttribute="top" id="nH3-EE-bKv"/>
</constraints>
</view>
<connections>
<outlet property="doneBtn" destination="Ilk-Ta-1NW" id="MPX-vf-UZi"/>
<outlet property="editorView" destination="Ejg-Hv-w6Y" id="VPU-p2-yXS"/>
<outlet property="findAndReplaceViewHeightConstraint" destination="cSS-9y-hU6" id="XwZ-ek-SRP"/>
<outlet property="findView" destination="pl5-54-raW" id="3Bq-qj-6Nt"/>
<outlet property="nextOrPreviousSegment" destination="HDq-VH-JlL" id="mne-oN-Gv7"/>
<outlet property="outline" destination="RNB-nt-8rR" id="d7w-L8-eki"/>
<outlet property="replaceField" destination="bgd-lF-t3r" id="Jcl-1E-d3y"/>
<outlet property="replaceOneOrAllSegment" destination="32r-Nm-cON" id="VbA-Xa-A5S"/>
<outlet property="scrollView" destination="bIm-n2-Z3h" id="E8b-BH-UyK"/>
<outlet property="searchField" destination="7Wv-ZY-JzG" id="37Q-2C-M6c"/>
<outlet property="showFindViewBtn" destination="CjQ-1i-oKD" id="44h-CB-tqw"/>
<outlet property="showReplaceBtn" destination="f5f-vb-JDD" id="wUY-mZ-0zp"/>
</connections>
</viewController>
<customObject id="2Tp-Fl-jBw" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="83" y="989"/>
</scene>
</scenes>
<resources>
<image name="NSAddTemplate" width="11" height="11"/>
<image name="NSLeftFacingTriangleTemplate" width="9" height="12"/>
<image name="NSRemoveTemplate" width="11" height="11"/>
<image name="NSRightFacingTriangleTemplate" width="9" height="12"/>
<image name="NSStopProgressTemplate" width="11" height="11"/>
</resources>
</document>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,89 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
@available(OSX 10.11, *)
class PlistEditorWC: NSWindowController, NSWindowDelegate {
var subview: PlistEditorWC?
var fixed: Bool = false
override func windowDidLoad() {
super.windowDidLoad()
self.window?.titlebarAppearsTransparent = false
self.window?.isMovableByWindowBackground = true
self.window?.styleMask = [.titled, .miniaturizable, .closable, .resizable]
self.window?.collectionBehavior = [.fullScreenAuxiliary, .fullScreenPrimary]
if #available(OSX 10.12, *) {
self.window?.tabbingMode = NSWindow.TabbingMode.preferred
} else {
self.shouldCascadeWindows = true
}
}
func windowWillClose(_ notification: Notification) { }
func windowDidBecomeMain(_ notification: Notification) { }
func windowDidBecomeKey(_ notification: Notification) { }
func windowDidResignKey(_ notification: Notification) { }
class func loadFromNib(parser: PlistParser) -> PlistEditorWC {
let id = NSStoryboard.SceneIdentifier("Document Window Controller")
let wc = NSStoryboard(name: NSStoryboard.Name("PlistEditor"),
bundle: nil).instantiateController(withIdentifier: id) as! PlistEditorWC
(wc.contentViewController as? PlistEditorVC)?.parser = parser
return wc
}
override func newWindowForTab(_ sender: Any?) {
let dc = NSDocumentController.shared
do {
let doc = try dc.makeUntitledDocument(ofType: "XML PropertyList v1") as? Document
doc?.makeWindowControllers()
dc.addDocument(doc!) // <--- very important! w/o this call the document is not connected to main menu
if #available(OSX 10.12, *) {
self.window!.addTabbedWindow((doc?.windowController?.window)!, ordered: .above)
}
doc?.windowController?.window?.makeKeyAndOrderFront(self)
} catch let error as NSError {
let alert = NSAlert()
alert.messageText = error.localizedFailureReason ?? error.localizedDescription
alert.informativeText = error.localizedRecoverySuggestion ?? ""
alert.runModal()
}
}
}

View File

@ -0,0 +1,39 @@
/*
* vector sigma (https://github.com/vectorsigma72)
* Copyright 2020 vector sigma All Rights Reserved.
*
* The source code contained or described herein and all documents related
* to the source code ("Material") are owned by vector sigma.
* Title to the Material remains with vector sigma or its suppliers and licensors.
* The Material is proprietary of vector sigma and is protected by worldwide copyright.
* No part of the Material may be used, copied, reproduced, modified, published,
* uploaded, posted, transmitted, distributed, or disclosed in any way without
* vector sigma's prior express written permission.
*
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by vector sigma in writing.
*
* Unless otherwise agreed by vector sigma in writing, you may not remove or alter
* this notice or any other notice embedded in Materials by vector sigma in any way.
*
* The license is granted for the CloverBootloader project (i.e. https://github.com/CloverHackyColor/CloverBootloader)
* and all the users as long as the Material is used only within the
* source code and for the exclusive use of CloverBootloader, which must
* be free from any type of payment or commercial service for the license to be valid.
*/
import Cocoa
final class ReplaceButton: NSButton {
required init?(coder: NSCoder) {
super.init(coder: coder)
self.canDrawConcurrently = AppSD.canDrawConcurrently
self.wantsLayer = true
let image = NSImage(named: "Replace")
image?.isTemplate = true
self.image = image
}
}

View File

@ -54,11 +54,20 @@ func getLatestReleases(reply: @escaping (String?, String?, String?, String?) ->
//applink = "/CloverHackyColor/CloverBootloader/releases/download/5099/Clover.app_v1.17_r5104.pkg.zip\" rel=\"nofollow\" class=\"d-flex flex-items-center min-width-0\">"
applink = "https://github.com\(applink!.components(separatedBy: "\"")[0])"
if applink!.lastPath.hasPrefix("Clover.app_v") && applink!.hasSuffix(".zip") {
// Clover.app_v1.17_r5104.pkg.zip
if applink!.lastPath.hasPrefix("Clover.app_v") && applink!.hasSuffix(".pkg") {
// Clover.app_v1.17.pkg
appvers = applink!.components(separatedBy: "Clover.app_v")[1]
//print(appvers)
appvers = appvers!.components(separatedBy: "_r")[0]
appvers = appvers!.components(separatedBy: ".pkg")[0]
if let pkgName = applink?.deletingFileExtension.lastPath {
let donwPath = NSHomeDirectory().addPath("Desktop/Clover_app_download").addPath(pkgName)
if fm.fileExists(atPath: donwPath) {
applink = nil
appvers = nil
break
}
}
break
}
}

View File

@ -12,13 +12,13 @@ import ServiceManagement
extension AppDelegate {
func setLaunchAtStartup() {
let success : Bool = SMLoginItemSetEnabled(gHelperID, true)
UserDefaults.standard.set(success, forKey: kRunAtLogin)
UserDefaults.standard.synchronize()
UDs.set(success, forKey: kRunAtLogin)
UDs.synchronize()
}
func removeLaunchAtStartup() {
let success : Bool = SMLoginItemSetEnabled(gHelperID, false)
UserDefaults.standard.set(success ? false : true, forKey: kRunAtLogin)
UserDefaults.standard.synchronize()
UDs.set(success ? false : true, forKey: kRunAtLogin)
UDs.synchronize()
}
}

View File

@ -36,7 +36,8 @@ final class SoundSlider : NSSlider {
var field : NSTextField?
}
final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSessionDownloadDelegate {
final class SettingsViewController:
NSViewController, NSTextFieldDelegate, NSComboBoxDelegate, URLSessionDownloadDelegate {
// MARK: Variables
@IBOutlet var tabViewInfo : LITabView!
// tab 0
@ -71,6 +72,12 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
@IBOutlet var soundDevicePopUp : NSPopUpButton!
@IBOutlet var soundVolumeSlider : SoundSlider!
@IBOutlet var soundVolumeField : NSTextField!
// tab 3
@IBOutlet var autoSaveButton : NSButton!
@IBOutlet var newPlistButton : NSButton!
@IBOutlet var openDocumentButton : NSButton!
@IBOutlet var disksPEPopUp : NSPopUpButton!
@IBOutlet var plistsPopUp : NSPopUpButton!
@IBOutlet var disbaleSleepProxyButton : NSButton!
@IBOutlet var makeRootRWButton : NSButton!
@ -134,7 +141,7 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
// sync
self.tabViewFuncSelector.selectSegment(withTag: 0)
self.tabViewFunc.selectTabViewItem(at: 0)
AppSD.themeUser = UDs.string(forKey: kThemeUserKey) ?? kDefaultThemeUser
AppSD.themeRepo = UDs.string(forKey: kThemeRepoKey) ?? kDefaultThemeRepo
@ -198,9 +205,23 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
self.oemVendorField.stringValue = getOEMVendor() ?? kNotAvailable.locale
self.oemProductField.stringValue = getOEMProduct() ?? kNotAvailable.locale
self.oemBoardIdField.stringValue = getOEMBoard() ?? kNotAvailable.locale
// tab 3
self.plistsPopUp.removeAllItems()
if #available(OSX 10.11, *) {
self.autoSaveButton.state = UDs.bool(forKey: kAutoSavePlistsKey) ? .on : .off
} else {
self.autoSaveButton.isEnabled = false
self.autoSaveButton.isHidden = true
self.newPlistButton.isEnabled = false
self.newPlistButton.isHidden = true
}
self.setUpInfo()
self.setUpdateButton()
if !fm.fileExists(atPath: Bundle.main.sharedSupportPath!.addPath("CloverV2/EFI")) {
self.searchUpdate()
}
}
func setUpInfo() {
@ -280,8 +301,21 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
let daemonExist = fm.fileExists(atPath: kDaemonPath) && fm.fileExists(atPath: kLaunchPlistPath)
self.unInstallDaemonButton.isEnabled = daemonExist
self.searchDisks()
self.searchESPDisks()
if self.disksPEPopUp.indexOfSelectedItem == 0 {
for item in self.disksPEPopUp.itemArray {
if let disk = item.representedObject as? String {
if self.bootDevice != nil && disk == self.bootDevice {
self.disksPEPopUp.select(item)
self.volumePopUpPressed(self.disksPEPopUp)
break
}
}
}
}
let itervals = ["never", "daily", "weekly", "monthly"]
self.timeIntervalPopUp.removeAllItems()
for i in itervals {
@ -324,8 +358,13 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
}
func setUpdateInformations() {
let rev = findCloverRevision(at: Bundle.main.sharedSupportPath!.addPath("CloverV2/EFI")) ?? "0000"
var title = "\("Install Clover".locale) \(rev)"
var cloverAvailable = false
var title = "Download".locale
if fm.fileExists(atPath: Bundle.main.sharedSupportPath!.addPath("CloverV2/EFI")) {
cloverAvailable = true
let rev = findCloverRevision(at: Bundle.main.sharedSupportPath!.addPath("CloverV2/EFI")) ?? "0000"
title = "\("Install Clover".locale) \(rev)"
}
self.installCloverButton.title = title
self.bootDevice = findBootPartitionDevice()
@ -348,7 +387,7 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
let last : String = (self.lastReleaseRev == nil) ? kNotAvailable.locale : "r\(self.lastReleaseRev!)"
title = "\("Update Clover available".locale): \(last)"
self.installCloverButton.isEnabled = true
self.installCloverButton.isEnabled = cloverAvailable
}
override var representedObject: Any? {
@ -369,6 +408,50 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
}
// MARK: Disks
func searchDisks() {
let selected = self.disksPEPopUp.selectedItem?.representedObject as? String
self.disksPEPopUp.removeAllItems()
self.disksPEPopUp.addItem(withTitle: "Select a disk..".locale)
for v in getVolumes() {
if !isWritable(diskOrMtp: v) {
continue
}
if !fm.fileExists(atPath: v.addPath("EFI/CLOVER")) {
continue
}
if let disk = getBSDName(of: v) {
if kBannedMedia.contains(getVolumeName(from: disk) ?? "") {
continue
}
let parentDiskName : String = getMediaName(from: getBSDParent(of: disk) ?? "") ?? kNotAvailable.locale
let fs = getFS(from: v) ?? kNotAvailable.locale
let title : String = "\(disk), \(fs), \("mount point".locale): \(v), \(parentDiskName)"
self.disksPEPopUp.addItem(withTitle: title)
self.disksPEPopUp.lastItem?.representedObject = disk
if disk == self.bootDevice {
let image : NSImage = NSImage(named: "NSApplicationIcon")!.copy() as! NSImage
image.size = NSMakeSize(16, 16)
self.disksPEPopUp.lastItem?.image = image
} else if let image : NSImage = getIconFor(volume: disk) {
image.size = NSMakeSize(16, 16)
self.disksPEPopUp.lastItem?.image = image
}
}
}
if (selected != nil) {
for item in self.disksPEPopUp.itemArray {
if let d = item.representedObject as? String {
if d == selected {
self.disksPEPopUp.select(item)
break
}
}
}
}
}
func searchESPDisks() {
self.unmountButton.isEnabled = false
let selected = self.disksPopUp.selectedItem?.representedObject as? String
@ -408,6 +491,7 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
}
}
}
// MARK: Mount ESPs
@IBAction func mountESP(_ sender: NSPopUpButton!) {
self.unmountButton.isEnabled = false
@ -522,6 +606,7 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
NSSound.beep()
}
DispatchQueue.main.async {
self.searchDisks()
self.searchESPDisks()
}
}
@ -551,7 +636,17 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
}
}
func comboBoxSelectionDidChange(_ notification: Notification) {
if (notification.object as? NSComboBox) == self.themeBox {
self.themeBoxSelected(self.themeBox)
}
}
func controlTextDidEndEditing(_ obj: Notification) {
if (obj.object as? NSComboBox) == self.themeBox {
self.themeBoxSelected(self.themeBox)
return
}
var updateThemeRepo = false
if let field = obj.object as? NSTextField {
@ -600,7 +695,7 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
}
if NSDictionary(dictionary: conf).write(toFile: fullPath,
atomically: false) {
NSWorkspace.shared.openFile(dir)
loadPlist(at: fullPath)
} else {
NSSound.beep()
}
@ -610,6 +705,85 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
}
}
@IBAction func createNewPlist(_ sender: NSButton!) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
NSDocumentController.shared.newDocument(self)
}
}
@IBAction func autosaveDocuments(_ sender: NSButton!) {
UDs.set(sender.state == .on, forKey: kAutoSavePlistsKey)
UDs.synchronize()
}
@IBAction func searchPanelForPlist(_ sender: NSButton!) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
let op = NSOpenPanel()
op.allowsMultipleSelection = false
op.canChooseDirectories = false
op.canCreateDirectories = false
op.canChooseFiles = true
op.allowedFileTypes = ["plist"]
// make the app regular
NSApp.setActivationPolicy(.regular)
op.begin { (result) in
if result == .OK {
if let path = op.url?.path {
loadPlist(at: path) // this will make the app regular again in 10.11+
}
} else {
// check if a document is opened some where
if NSDocumentController.shared.documents.count == 0 {
NSApp.setActivationPolicy(.accessory)
}
}
}
}
}
@IBAction func volumePopUpPressed(_ sender: NSPopUpButton!) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
self.plistsPopUp.removeAllItems()
self.plistsPopUp.addItem(withTitle: "...")
if let disk = sender.selectedItem?.representedObject as? String {
if let mp = getMountPoint(from: disk) {
let clover = mp.addPath("EFI/CLOVER")
if fm.fileExists(atPath: clover) {
if fm.fileExists(atPath: clover.addPath("config.plist")) {
self.plistsPopUp.addItem(withTitle: "config.plist")
self.plistsPopUp.lastItem?.representedObject = clover.addPath("config.plist")
}
let enumerator = fm.enumerator(atPath: clover)
while let file = enumerator?.nextObject() as? String {
if file.fileExtension == "plist" &&
(file.range(of: "kexts/") == nil) &&
!file.hasPrefix("themes/") &&
file != "pref.plist" &&
file != "config.plist"{
self.plistsPopUp.addItem(withTitle: file)
self.plistsPopUp.lastItem?.representedObject = clover.addPath(file)
}
}
}
} else {
NSSound.beep()
}
}
}
}
@IBAction func openCloverPlist(_ sender: NSPopUpButton!) {
if let path = sender.selectedItem?.representedObject as? String {
if !fm.fileExists(atPath: path) {
NSSound.beep()
return
}
loadPlist(at: path)
}
}
@IBAction func installClover(_ sender: NSButton!) {
let myPath = Bundle.main.bundlePath.lowercased()
@ -658,7 +832,7 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
AppSD.installerOutWC?.showWindow(self)
}
NSApp.activate(ignoringOtherApps: true)
//NSApp.activate(ignoringOtherApps: true)
}
}
@ -758,20 +932,26 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
}
// MARK: NVRAM editing
@IBAction func themeBoxSelected(_ sender: NSComboBox!) {
func themeBoxSelected(_ sender: NSComboBox!) {
//sender.window?.makeFirstResponder(nil)
let key = "Clover.Theme"
if let old = sender.cell?.representedObject as? String {
let theme = sender.stringValue
if old != theme {
if theme.count == 0 {
deleteNVRAM(key: key)
} else {
setNVRAM(key: key, stringValue: theme)
}
}
let theme = sender.stringValue
var nvramValue : String = ""
if let data = getNVRAM()?.object(forKey: key) as? Data {
nvramValue = String(decoding: data, as: UTF8.self)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
if nvramValue != theme {
if theme.count == 0 {
deleteNVRAM(key: key)
} else {
setNVRAM(key: key, stringValue: theme)
}
} else {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
var value = ""
if let nvram = getNVRAM() {
let nvdata = nvram.object(forKey: key) as? Data
@ -971,11 +1151,21 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
let installerRev = findCloverRevision(at: Bundle.main.sharedSupportPath!.addPath("CloverV2/EFI"))
let installerRevNum = Int(installerRev ?? "0") ?? 0
if (self.lastReleaseLink != nil && self.lastReleaseRev != nil) {
if !fm.fileExists(atPath: Bundle.main.sharedSupportPath!.addPath("CloverV2/EFI")) {
DispatchQueue.main.async {
self.updateClover(self.updateCloverButton)
}
}
}
if (self.lastReleaseLink != nil && self.lastReleaseRev != nil)
&& lastRevNum > 0
&& (lastRevNum > currRevNum) {
UDs.set(self.lastReleaseLink!, forKey: kLastUpdateLink)
UDs.set(self.lastReleaseRev!, forKey: kLastUpdateRevision)
DispatchQueue.main.async {
if #available(OSX 10.10, *) {
AppSD.statusItem.button?.title = "\(lastRevNum)"
@ -1049,7 +1239,7 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
alert.addButton(withTitle: "Download".locale)
alert.addButton(withTitle: "Close".locale)
if alert.runModal() == .alertFirstButtonReturn {
NSWorkspace.shared.open(url)
self.updateCloverApp(at: url)
}
}
}
@ -1075,8 +1265,6 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
let b = URLSessionConfiguration.default
let session = Foundation.URLSession(configuration: b, delegate: self, delegateQueue: nil)
//let session = URLSession(configuration: .default)
if (url != nil) {
self.installCloverButton.isEnabled = false
self.downloadTask = session.downloadTask(with: url!)
@ -1084,6 +1272,25 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
}
}
func updateCloverApp(at url: URL) {
if AppSD.isInstalling ||
AppSD.isInstallerOpen ||
self.lastReleaseLink == nil ||
self.lastReleaseRev == nil {
NSSound.beep()
return
}
self.progressBar.isHidden = false
self.progressBar.doubleValue = 0.0
let b = URLSessionConfiguration.default
let session = Foundation.URLSession(configuration: b, delegate: self, delegateQueue: nil)
self.installCloverButton.isEnabled = false
self.downloadTask = session.downloadTask(with: url)
self.downloadTask?.resume()
}
func urlSession(_ session: URLSession,
downloadTask: URLSessionDownloadTask,
didFinishDownloadingTo location: URL) {
@ -1098,8 +1305,9 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
let lastPath = downloadTask.originalRequest!.url!.lastPathComponent
let data = try Data(contentsOf: location)
if lastPath.fileExtension == "zip" && lastPath.hasPrefix("CloverV2") {
// ok, We have the download completed: replace CloverV2 inside SharedSupport directory!
if lastPath.fileExtension == "zip" &&
(lastPath.hasPrefix("CloverV2") || lastPath.hasPrefix("Clover.app")) {
let isApp = lastPath.hasPrefix("Clover.app")
// Decompress the zip archive
// NSUserName() ensure the user have read write permissions
@ -1115,27 +1323,52 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
let file = tempDir.addPath(lastPath)
try data.write(to: URL(fileURLWithPath: file))
DispatchQueue.main.async {
let task : Process = Process()
task.environment = ProcessInfo().environment
let bash = "/bin/bash"
// unzip -d output_dir/ zipfiles.zip
let cmd = "/usr/bin/unzip -qq -d \(tempDir) \(file)"
if #available(OSX 10.13, *) {
task.executableURL = URL(fileURLWithPath: bash)
} else {
task.launchPath = bash
}
task.arguments = ["-c", cmd]
task.terminationHandler = { t in
if t.terminationStatus == 0 {
self.replaceCloverV2(with: tempDir.addPath("CloverV2"))
if isApp {
DispatchQueue.main.async {
let task : Process = Process()
task.environment = ProcessInfo().environment
let bash = "/bin/bash"
// unzip -d output_dir/ zipfiles.zip
let cmd = "/usr/bin/unzip -qq -d \(tempDir) \(file)"
if #available(OSX 10.13, *) {
task.executableURL = URL(fileURLWithPath: bash)
} else {
task.launchPath = bash
}
task.arguments = ["-c", cmd]
task.terminationHandler = { t in
if t.terminationStatus == 0 {
self.moveCloverApp(at: file.deletingFileExtension)
}
}
task.launch()
}
} else {
DispatchQueue.main.async {
let task : Process = Process()
task.environment = ProcessInfo().environment
let bash = "/bin/bash"
// unzip -d output_dir/ zipfiles.zip
let cmd = "/usr/bin/unzip -qq -d \(tempDir) \(file)"
if #available(OSX 10.13, *) {
task.executableURL = URL(fileURLWithPath: bash)
} else {
task.launchPath = bash
}
task.arguments = ["-c", cmd]
task.terminationHandler = { t in
if t.terminationStatus == 0 {
self.replaceCloverV2(with: tempDir.addPath("CloverV2"))
}
}
task.launch()
}
task.launch()
}
}
} catch {
@ -1180,8 +1413,28 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
var isDir : ObjCBool = false
if fm.fileExists(atPath: newOne, isDirectory: &isDir) {
if isDir.boolValue {
// clean some unused stuff
if fm.fileExists(atPath: newOne.addPath("rcScripts")) {
try? fm.removeItem(atPath: newOne.addPath("rcScripts"))
}
if fm.fileExists(atPath: newOne.addPath("ThirdParty")) {
try? fm.removeItem(atPath: newOne.addPath("ThirdParty"))
}
// let only one theme (Clovy) as we have a themes manager
if fm.fileExists(atPath: newOne.addPath("themespkg/Clovy")) {
if let themes = try? fm.contentsOfDirectory(atPath: newOne.addPath("themespkg")) {
for file in themes {
if file != "Clovy" {
try? fm.removeItem(atPath: newOne.addPath("themespkg").addPath(file))
}
}
}
}
do {
try fm.removeItem(atPath: Cloverv2Path)
if fm.fileExists(atPath: Cloverv2Path) {
try fm.removeItem(atPath: Cloverv2Path)
}
try fm.copyItem(atPath: newOne, toPath: Cloverv2Path)
DispatchQueue.main.async {
self.lastReleaseRev = nil
@ -1191,11 +1444,60 @@ final class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSe
}
} catch {
print(error)
NSSound.beep()
}
}
}
}
private func moveCloverApp(at path: String) {
// move to ~/Desktop/Clover_app_new
let new = NSHomeDirectory().addPath("Desktop/Clover_app_download")
if fm.fileExists(atPath: new) {
try? fm.removeItem(atPath: new)
}
do {
if fm.fileExists(atPath: new) {
try fm.removeItem(atPath: new)
}
try fm.createDirectory(atPath: new, withIntermediateDirectories: false, attributes: nil)
try fm.copyItem(atPath: path, toPath: new.addPath(path.lastPath))
DispatchQueue.main.async {
self.lastReleaseRev = nil
self.lastReleaseLink = nil
self.setUpdateInformations()
self.setUpdateButton()
}
self.removeAttributes(at: new.addPath(path.lastPath))
NSWorkspace.shared.openFile(new)
} catch {
print(error)
NSSound.beep()
}
}
private func removeAttributes(at path: String) {
let task : Process = Process()
task.environment = ProcessInfo().environment
let bash = "/bin/bash"
let cmd = "/usr/bin/xattr -rc \(path)"
if #available(OSX 10.13, *) {
task.executableURL = URL(fileURLWithPath: bash)
} else {
task.launchPath = bash
}
task.arguments = ["-c", cmd]
task.terminationHandler = { t in
if t.terminationStatus != 0 {
NSSound.beep()
}
}
task.launch()
}
// MARK: Close
@IBAction func closeApp(_ sender: NSButton?) {
NSApp.terminate(nil)

View File

@ -92,7 +92,7 @@ func findCloverHashCommit(at EFIdir: String) -> String? {
if (rev != nil), let revision = String(cString: (rev?.utf8String)!,
encoding: String.Encoding.utf8) {
if revision.count == 8 {
if revision.count >= 4 && revision.count <= 40 {
return revision
}
}
@ -114,3 +114,4 @@ func getCoreTypeImage(named: String, isTemplate: Bool) -> NSImage? {
image?.isTemplate = isTemplate
return image
}

View File

@ -1,5 +1,5 @@
//
// PNG8Image.h
// ThemeImage.h
// Clover
//
// Created by vector sigma on 07/03/2020.
@ -12,10 +12,9 @@
//NS_ASSUME_NONNULL_BEGIN
@interface PNG8Image : NSImage
- (nullable NSData *)png8ImageDataAtPath:(NSString *_Nonnull)imagePath
error:(NSError *_Nullable*_Nullable)errorPtr;
@interface ThemeImage : NSImage
@property (nonatomic, strong) NSData * _Nonnull pngData;
- (id _Nullable)initWithThemeImageAtPath:(nonnull NSString *)path error:(NSError *_Nullable*_Nullable)errorPtr;
@end
//NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,163 @@
//
// PNG8Image.m
// Clover
//
// Created by vector sigma on 07/03/2020.
// Copyright © 2020 CloverHackyColor. All rights reserved.
//
#import "ThemeImage.h"
@implementation ThemeImage
- (id _Nullable)initWithThemeImageAtPath:(nonnull NSString *)path
error:(NSError *_Nullable*_Nullable)errorPtr {
if (!(self = [super init])) {
return nil;
}
NSData *mainData = [NSData dataWithContentsOfFile: path];
NSString *domain = @"org.slice.Clover.PNG8Image.Error";
if (!mainData || [mainData length] < 4) {
NSString *desc = [NSString stringWithFormat:@"Size of %@ is too small to be an image\n", path];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:1
userInfo:userInfo];
return nil;
}
UInt8 * bytes = (UInt8 *)[mainData bytes];
if (bytes[0] != 0x89 || bytes[0] != 0x50 || bytes[0] != 0x4E || bytes[0] != 0x47) {
NSBitmapImageRep *bir = [[NSBitmapImageRep alloc] initWithData:mainData];
if (bir) {
mainData = [bir representationUsingType:NSPNGFileType properties:[NSDictionary new]];
if (mainData == nil) {
NSString *desc = [NSString stringWithFormat:@"Can't convert %@ to png\n", path];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:2
userInfo:userInfo];
return nil;
} else {
[self addRepresentation: [[NSBitmapImageRep alloc] initWithData:mainData]];
}
}
}
unsigned int width, height;
unsigned char *raw_rgba_pixels;
unsigned int status = lodepng_decode32(&raw_rgba_pixels,
&width,
&height,
[mainData bytes],
[mainData length]);
if (status) {
NSString *desc = [NSString stringWithFormat:@"%@, %s\n",
path,
lodepng_error_text(status)];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:3
userInfo:userInfo];
return nil;
}
// Use libimagequant to make a palette for the RGBA pixels
liq_attr *handle = liq_attr_create();
liq_image *input_image = liq_image_create_rgba(handle,
raw_rgba_pixels,
width,
height,
0);
// You could set more options here, like liq_set_quality
liq_result *quantization_result;
if (liq_image_quantize(input_image, handle, &quantization_result) != LIQ_OK) {
NSString *desc = [NSString stringWithFormat:@"Quantization failed for %@", path];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:4
userInfo:userInfo];
return nil;
}
// Use libimagequant to make new image pixels from the palette
size_t pixels_size = width * height;
unsigned char *raw_8bit_pixels = malloc(pixels_size);
liq_set_dithering_level(quantization_result, 1.0);
liq_write_remapped_image(quantization_result,
input_image,
raw_8bit_pixels,
pixels_size);
const liq_palette *palette = liq_get_palette(quantization_result);
// Save converted pixels as a PNG file
// This uses lodepng library for PNG writing (not part of libimagequant)
LodePNGState state;
lodepng_state_init(&state);
state.info_raw.colortype = LCT_PALETTE;
state.info_raw.bitdepth = 8;
state.info_png.color.colortype = LCT_PALETTE;
state.info_png.color.bitdepth = 8;
for(int i = 0; i < palette->count; i++) {
lodepng_palette_add(&state.info_png.color,
palette->entries[i].r,
palette->entries[i].g,
palette->entries[i].b,
palette->entries[i].a);
lodepng_palette_add(&state.info_raw,
palette->entries[i].r,
palette->entries[i].g,
palette->entries[i].b,
palette->entries[i].a);
}
unsigned char *output_file_data;
size_t output_file_size;
unsigned int out_status = lodepng_encode(&output_file_data,
&output_file_size,
raw_8bit_pixels,
width,
height,
&state);
if (out_status) {
NSString *desc = [NSString stringWithFormat:@"Can't encode %@: %s\n",
path,
lodepng_error_text(out_status)];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:5
userInfo:userInfo];
return nil;
}
// Prove the conversion
self.pngData = [NSData dataWithBytes: output_file_data length: output_file_size];
NSImage *convertedImage = [[NSImage alloc] initWithData:self.pngData];
if (convertedImage == nil) {
NSString *desc = [NSString stringWithFormat:@"Can't convert data to NSImage (%@)", path];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
*errorPtr = [NSError errorWithDomain:domain
code:6
userInfo:userInfo];
return nil;
}
liq_result_destroy(quantization_result); // Must be freed only after you're done using the palette
liq_image_destroy(input_image);
liq_attr_destroy(handle);
free(raw_8bit_pixels);
lodepng_state_cleanup(&state);
return self;
}
@end

View File

@ -0,0 +1,82 @@
//
// HTTPErrors.swift
// Clover
//
// Created by vector sigma on 27/03/2020.
// Copyright © 2020 CloverHackyColor. All rights reserved.
//
import Foundation
func gHTTPInfo(for statusCode: Int) -> String {
var info = "Unknown"
switch statusCode {
case 100: info = "Continue"
case 101: info = "Switching Protocols"
case 102: info = "Processing"
case 200: info = "OK"
case 201: info = "Created"
case 202: info = "Accepted"
case 203: info = "Non-authoritative Information"
case 204: info = "No Content"
case 205: info = "Reset Content"
case 206: info = "Partial Content"
case 207: info = "Multi-Status"
case 208: info = "Already Reported"
case 226: info = "IM Used"
case 300: info = "Multiple Choices"
case 301: info = "Moved Permanently"
case 302: info = "Found"
case 303: info = "See Other"
case 304: info = "Not Modified"
case 305: info = "Use Proxy"
case 307: info = "Temporary Redirect"
case 308: info = "Permanent Redirect"
case 400: info = "Bad Request"
case 401: info = "Unauthorized"
case 402: info = "Payment Required"
case 403: info = "Forbidden"
case 404: info = "Not Found"
case 405: info = "Method Not Allowed"
case 406: info = "Not Acceptable"
case 407: info = "Proxy Authentication Required"
case 408: info = "Request Timeout"
case 409: info = "Conflict"
case 410: info = "Gone"
case 411: info = "Length Required"
case 412: info = "Precondition Failed"
case 413: info = "Payload Too Large"
case 414: info = "Request-URI Too Long"
case 415: info = "Unsupported Media Type"
case 416: info = "Requested Range Not Satisfiable"
case 417: info = "Expectation Failed"
case 418: info = "I'm a teapot"
case 421: info = "Misdirected Request"
case 422: info = "Unprocessable Entity"
case 423: info = "Locked"
case 424: info = "Failed Dependency"
case 426: info = "Upgrade Required"
case 428: info = "Precondition Required"
case 429: info = "Too Many Requests"
case 431: info = "Request Header Fields Too Large"
case 444: info = "Connection Closed Without Response"
case 451: info = "Unavailable For Legal Reasons"
case 499: info = "Client Closed Request"
case 500: info = "Internal Server Error"
case 501: info = "Not Implemented"
case 502: info = "Bad Gateway"
case 503: info = "Service Unavailable"
case 504: info = "Gateway Timeout"
case 505: info = "HTTP Version Not Supported"
case 506: info = "Variant Also Negotiates"
case 507: info = "Insufficient Storage"
case 508: info = "Loop Detected"
case 510: info = "Not Extended"
case 511: info = "Network Authentication Required"
case 599: info = "Network Connect Timeout Error"
default:
break
}
return info
}

View File

@ -20,15 +20,24 @@ enum ThemeDownload {
case complete
}
enum GitProtocol : String {
case https = "https"
case git = "git"
}
let kCloverThemeAttributeKey = "org.cloverTheme.sha"
final class ThemeManager: NSObject, URLSessionDataDelegate {
private let errorDomain : String = "org.slice.Clover.ThemeManager.Error"
var statusError : Error? = nil
var delegate : ThemeManagerVC?
private var user : String
private var repo : String
var basePath : String
private var urlBaseStr : String
private var themeManagerIndexDir : String
private var gitInitCount : Int32 = 0
let userAgent = "Clover"
@ -39,7 +48,7 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
self.user = user
self.repo = repo
self.basePath = basePath
self.urlBaseStr = "https://api.github.com/repos/\(user)/\(repo)/git/trees/master?recursive=1"
self.urlBaseStr = "\(GitProtocol.https.rawValue)://api.github.com/repos/\(user)/\(repo)/git/trees/master?recursive=1"
self.themeManagerIndexDir = indexDir
self.delegate = delegate
if !fm.fileExists(atPath: self.themeManagerIndexDir) {
@ -48,8 +57,9 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
attributes: nil)
}
}
public func getIndexedThemes() -> [String] {
self.statusError = nil
var themes = [String]()
if self.getSha() != nil {
let themesIndexPath = self.themeManagerIndexDir.addPath("Themes")
@ -67,10 +77,11 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
}
public func getThemes(completion: @escaping ([String]) -> ()) {
self.statusError = nil
var themes : [String] = [String]()
let themesIndexPath : String = self.themeManagerIndexDir.addPath("Themes")
self.getInfo(urlString: urlBaseStr) { (success) in
self.getInfo(urlString: self.urlBaseStr) { (success) in
do {
let files : [String] = try fm.contentsOfDirectory(atPath: themesIndexPath)
@ -114,8 +125,9 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
return url
}
private func downloadloadFile(at url: String, dst: String,
completion: @escaping (Bool) -> ()) {
private func downloadFile(at url: String, dst: String,
completion: @escaping (Bool) -> ()) {
if let validURL : URL = URL(string: self.normalize(url)) {
let upperDir : String = dst.deletingLastPath
if !fm.fileExists(atPath: upperDir) {
@ -124,26 +136,62 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
withIntermediateDirectories: true,
attributes: nil)
} catch {
print("DF1, \(error)")
let errStr = "DF0, \(error.localizedDescription)."
let se : Error = NSError(domain: self.errorDomain,
code: 1000,
userInfo: [NSLocalizedDescriptionKey: errStr])
statusError = se
completion(false)
return
}
}
var request : URLRequest = URLRequest(url: validURL)
request.httpMethod = "GET"
request.setValue(userAgent, forHTTPHeaderField: "User-Agent")
request.setValue(self.userAgent, forHTTPHeaderField: "User-Agent")
let config = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: config)
let task = session.dataTask(with: request, completionHandler: {(d, r, e) in
if let response = r as? HTTPURLResponse {
switch response.statusCode {
case 200:
break
default:
let errStr = "DF1, Error: request for '\(validURL)' response with status code \(response.statusCode) (\(gHTTPInfo(for: response.statusCode)))."
let se : Error = NSError(domain: self.errorDomain,
code: 1001,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
} else {
let errStr = "DF2, Error: empty response for '\(validURL)'."
let se : Error = NSError(domain: self.errorDomain,
code: 1002,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
if (e != nil) {
print("DF2, \(e!)")
let errStr = "DF3, \(e!.localizedDescription)."
let se : Error = NSError(domain: self.errorDomain,
code: 1003,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
guard let data = d else {
print("DF3, Error: no datas.")
let errStr = "DF4, Error: no datas."
let se : Error = NSError(domain: self.errorDomain,
code: 1004,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
@ -151,15 +199,23 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
try data.write(to: URL(fileURLWithPath: dst))
completion(true)
} catch {
print("DF4, \(error)")
let errStr = "DF5, \(error.localizedDescription)"
let se : Error = NSError(domain: self.errorDomain,
code: 1005,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
}
})
task.resume()
} else {
print("DF5, Error: invalid url '\(url)'.")
let errStr = "DF6, Error: invalid url '\(url)'."
let se : Error = NSError(domain: self.errorDomain,
code: 1006,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
}
}
@ -169,25 +225,52 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
var request : URLRequest = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.setValue(userAgent, forHTTPHeaderField: "User-Agent")
request.setValue(self.userAgent, forHTTPHeaderField: "User-Agent")
let config = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: config)
let task = session.dataTask(with: request, completionHandler: {(d, r, e) in
if let reponse = r as? HTTPURLResponse {
if reponse.statusCode != 200 {
let errStr = "GI0, Error: request for '\(url)' reponse with status code \(reponse.statusCode) (\(gHTTPInfo(for: reponse.statusCode)))."
let se : Error = NSError(domain: self.errorDomain,
code: 2000,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
}
if (e != nil) {
print("GI1, \(e!)")
let errStr = "GI1, \(e!.localizedDescription)"
let se : Error = NSError(domain: self.errorDomain,
code: 2001,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
guard let data = d else {
print("GI2, no data.")
let errStr = "GI2, no data"
let se : Error = NSError(domain: self.errorDomain,
code: 2002,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
guard let utf8 = String(decoding: data, as: UTF8.self).data(using: .utf8) else {
print("GI3, data is not utf8.")
let errStr = "GI3, data is not utf8."
let se : Error = NSError(domain: self.errorDomain,
code: 2003,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
@ -197,19 +280,34 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
if let jdict = json as? [String: Any] {
guard let truncated = jdict["truncated"] as? Bool else {
print("GI4, Error: 'truncated' key not found")
let errStr = "GI4, Error: 'truncated' key not found"
let se : Error = NSError(domain: self.errorDomain,
code: 2004,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
if truncated == true {
print("GI4, Error: json has truncated list.")
let errStr = "GI5, Error: json has truncated list."
let se : Error = NSError(domain: self.errorDomain,
code: 2005,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
guard let sha = jdict["sha"] as? String else {
print("GI4, Error: 'sha' key not found")
let errStr = "GI6, Error: 'sha' key not found"
let se : Error = NSError(domain: self.errorDomain,
code: 2006,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
@ -221,7 +319,12 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
}
guard let tree = jdict["tree"] as? [[String: Any]] else {
print("GI4, Error: 'tree' key not found, or not an array.")
let errStr = "GI7, Error: 'tree' key not found, or not an array."
let se : Error = NSError(domain: self.errorDomain,
code: 2007,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
@ -234,7 +337,12 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
}
} catch {
print("GI5, Error: can't write sha commit.")
let errStr = "GI8, Error: can't write sha commit."
let se : Error = NSError(domain: self.errorDomain,
code: 2008,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
@ -250,7 +358,12 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
*/
guard let type = obj["type"] as? String else {
print("GI6, Error: 'type' key not found")
let errStr = "GI9, Error: 'type' key not found."
let se : Error = NSError(domain: self.errorDomain,
code: 2009,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
break
}
@ -267,13 +380,23 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
if !theme.write(toFile: plistPath, atomically: false) {
print("GI7, Error: 'path' key not found.")
let errStr = "GI10, Error: can't write \(plistPath)"
let se : Error = NSError(domain: self.errorDomain,
code: 2010,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
break
}
}
} else {
print("GI8, Error: 'path' key not found.")
let errStr = "GI11, Error: 'path' key not found."
let se : Error = NSError(domain: self.errorDomain,
code: 2011,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
break
}
@ -289,26 +412,46 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
self.removeOld(new: sha)
completion(true)
} catch {
print("GI9, \(error)")
let errStr = "GI12, \(error.localizedDescription)"
let se : Error = NSError(domain: self.errorDomain,
code: 2012,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
return
}
//remove old themes (old sha)
} else {
print("GI10, json is not a dictionary (API change?).")
let errStr = "GI13, json is not a dictionary (API change?)."
let se : Error = NSError(domain: self.errorDomain,
code: 2013,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
}
} catch {
print("GI11, \(error)")
let errStr = "GI14, \(error.localizedDescription)"
let se : Error = NSError(domain: self.errorDomain,
code: 2014,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
}
})
task.resume()
} else {
print("GI12, \(urlString) is invalid.")
let errStr = "GI15, \(urlString) is invalid."
let se : Error = NSError(domain: self.errorDomain,
code: 2015,
userInfo: [NSLocalizedDescriptionKey: errStr])
self.statusError = se
completion(false)
}
}
@ -338,6 +481,7 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
}
/// Return the path for a given theme, if the download succeded
public func download(theme: String, down: ThemeDownload, completion: @escaping (String?) -> ()) {
self.statusError = nil
if let sha = self.getSha() {
let shaPath : String = self.basePath.addPath(sha)
let themeDest : String = (down == .complete)
@ -357,52 +501,47 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
let plistPath : String = "\(themeManagerIndexDir)/Themes/\(theme).plist"
if let files : [String] = NSArray(contentsOfFile: plistPath) as? [String] {
// ---------------------------------------
let fc : Int = files.count
if fc > 0 {
var broken : Bool = false
let dg = DispatchGroup()
var succeded : Bool = true
let dispatchGroup = DispatchGroup()
for i in 0..<fc {
dg.enter()
if broken {
dg.leave()
break
} else {
let file : String = files[i]
// build the url
let furl : String = "https://github.com/\(self.user)/\(self.repo)/raw/master/\(file)"
if down == .thumbnail {
if file != theme.addPath("screenshot.png")
&& file != theme.addPath("theme.svg")
&& file != theme.addPath("theme.plist") {
dg.leave()
continue
}
let file : String = files[i]
// build the url
let furl : String = "\(GitProtocol.https.rawValue)://raw.githubusercontent.com/CloverHackyColor/CloverThemes/master/\(file)"
if down == .thumbnail {
if file != theme.addPath("screenshot.png")
&& file != theme.addPath("theme.svg")
&& file != theme.addPath("theme.plist") {
continue
}
let filedest : String = (down == .complete)
? themeDest.deletingLastPath.addPath(file)
: shaPath.addPath(file)
if !fm.fileExists(atPath: filedest) {
self.downloadloadFile(at: self.normalize(furl), dst: filedest) { (success) in
broken = (success == false)
dg.leave()
}
} else {
dg.leave()
}
let filedest : String = (down == .complete)
? themeDest.deletingLastPath.addPath(file)
: shaPath.addPath(file)
if !fm.fileExists(atPath: filedest) {
dispatchGroup.enter()
self.downloadFile(at: self.normalize(furl), dst: filedest) { (success) in
succeded = success
dispatchGroup.leave()
}
if !succeded {
break
}
}
}
dg.notify(queue: .main) {
if broken {
completion(nil)
} else {
dispatchGroup.notify(queue: DispatchQueue.main, execute: {
if succeded {
completion(themeDest)
} else {
completion(nil)
}
}
})
} else {
completion(nil)
}
@ -413,7 +552,6 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
} else {
completion(nil)
}
}
public func getImageUrl(for theme: String, completion: @escaping (String?) -> ()) {
@ -434,8 +572,7 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
return
}
}
print("downloading thumbnail for \(theme)")
// theme not found?? Downloading...
self.download(theme: theme, down: .thumbnail) { (path) in
if let localTheme : String = path {
@ -484,33 +621,82 @@ final class ThemeManager: NSObject, URLSessionDataDelegate {
return fm.fileExists(atPath: "\(self.themeManagerIndexDir)/Themes/\(theme).plist")
}
public func optimizeTheme(at path: String, err: inout Error?){
let enumerator = fm.enumerator(atPath: path)
while let file = enumerator?.nextObject() as? String {
let fullPath = path.addPath(file)
if file.fileExtension == "png" || file.fileExtension == "icns" {
if let data = try? Data(contentsOf: URL(fileURLWithPath: fullPath)) {
if data[0] != 0x89 || data[0] != 0x50 || data[0] != 0x4E || data[0] != 0x47 {
// convert it to png
if let png = NSBitmapImageRep(data: data)?.png {
do {
try png.write(to: URL(fileURLWithPath: fullPath))
} catch {
err = error
return
public func optimizeTheme(at path: String, completion: @escaping (Error?) -> ()) {
DispatchQueue.global(priority: .background).async(execute: { () -> Void in
let plist = NSDictionary(contentsOfFile: path.addPath("theme.plist")) as? [String : Any]
let theme = plist?["Theme"] as? [String : Any]
let Selection = plist?["Selection"] as? [String : Any]
//logo 128x128 pixels Theme->Banner
let logo : String = (theme?["Banner"] as? String) ?? "logo.png"
// selection_big 64x64 pixels Theme->Selection->Big
let Selection_big : String = (Selection?["Big"] as? String) ?? "Selection_big.png"
// selection_small 144x144 pixels Theme->Selection->Small
let Selection_small : String = (Selection?["Small"] as? String) ?? "Selection_small.png"
var images : [String] = [String]()
let enumerator = fm.enumerator(atPath: path)
while let file = enumerator?.nextObject() as? String {
if file.fileExtension == "png" || file.fileExtension == "icns" {
images.append(file)
}
}
for file in images {
let fullPath = path.addPath(file)
do {
let image = try ThemeImage(themeImageAtPath: fullPath)
let size = image.size
let fileName = fullPath.lastPath
if file.hasPrefix("icons/") || file.hasPrefix("alternative_icons/") {
if (fileName.hasPrefix("os_") || fileName.hasPrefix("vol_")) { // 128x128 pixels
if size.width != 128 || size.height != 128 {
image.size = NSMakeSize(128, 128)
}
} else if (fileName.hasPrefix("func_") ||
fileName.hasPrefix("tool_") ||
fileName == "pointer.png") { // 32x32 pixels
if size.width != 32 || size.height != 32 {
image.size = NSMakeSize(32, 32)
}
}
} else {
if file == logo { // logo 128x128 pixels
if size.width != 128 || size.height != 128 {
image.size = NSMakeSize(128, 128)
}
} else if file == Selection_big { // selection_big 144x144 pixels
if size.width != 144 || size.height != 144 {
image.size = NSMakeSize(144, 144)
}
} else if file == Selection_small { // selection_small 64x64 pixels
if size.width != 64 || size.height != 64 {
image.size = NSMakeSize(64, 64)
}
} else if (file == "radio_button" ||
file == "radio_button_selected" ||
file == "checkbox" ||
file == "checkbox_checked") { // 15x15 pixels
if size.width != 15 || size.height != 15 {
image.size = NSMakeSize(15, 15)
}
}
}
}
do {
let data = try PNG8Image().png8ImageData(atPath: fullPath)
try data.write(to: URL(fileURLWithPath: fullPath))
try image.pngData.write(to: URL(fileURLWithPath: fullPath))
} catch {
err = error
completion(error)
break
}
if file == images.last {
completion(nil)
}
}
}
})
}
}

View File

@ -43,6 +43,8 @@ NSTableViewDelegate, NSTableViewDataSource, WebFrameLoadDelegate, WebUIDelegate
var loaded : Bool = false
var showInstalled : Bool = false
var isBusy : Bool = false
override func awakeFromNib() {
super.awakeFromNib()
if !self.loaded {
@ -213,8 +215,8 @@ NSTableViewDelegate, NSTableViewDataSource, WebFrameLoadDelegate, WebUIDelegate
}
@IBAction func optimizeThemePressed(_ sender: NSButton!) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
var success : Bool = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
var beep : Bool = true
let sr = self.sidebar.selectedRow
if sr >= 0 {
if let v = self.sidebar.view(atColumn: 0, row: sr, makeIfNecessary: false) as? ThemeView {
@ -224,30 +226,54 @@ NSTableViewDelegate, NSTableViewDataSource, WebFrameLoadDelegate, WebUIDelegate
|| fm.fileExists(atPath: self.targetVolume!) {
let themePath = self.targetVolume!.addPath("EFI/CLOVER/themes").addPath(v.name)
if fm.fileExists(atPath: themePath) {
beep = false
self.isBusy = true
self.spinner.startAnimation(nil)
var error : Error? = nil
self.manager?.optimizeTheme(at: themePath, err: &error)
if (error != nil) {
NSSound(named: "Basso")?.play()
let alert = NSAlert()
alert.messageText = "😱"
alert.informativeText = error!.localizedDescription
alert.alertStyle = .critical
alert.addButton(withTitle: "Ok".locale)
alert.beginSheetModal(for: self.view.window!) { (reponse) in
self.optimizeButton.isEnabled = false
self.installButton.isEnabled = false
self.unistallButton.isEnabled = false
self.targetPop.isEnabled = false
self.nameBox.isEnabled = false
self.installedThemesCheckBox.isEnabled = false
//return
self.manager?.optimizeTheme(at: themePath, completion: { (error) in
DispatchQueue.main.async {
self.isBusy = false
self.spinner.stopAnimation(nil)
self.optimizeButton.isEnabled = true
self.installButton.isEnabled = true
self.unistallButton.isEnabled = true
self.targetPop.isEnabled = true
self.nameBox.isEnabled = true
self.installedThemesCheckBox.isEnabled = true
}
return
}
self.spinner.stopAnimation(nil)
success = true
if (error != nil) {
DispatchQueue.main.async {
NSSound(named: "Basso")?.play()
let alert = NSAlert()
alert.messageText = "😱"
alert.informativeText = error!.localizedDescription
alert.alertStyle = .critical
alert.addButton(withTitle: "Ok".locale)
alert.beginSheetModal(for: self.view.window!) { (reponse) in
}
}
} else {
DispatchQueue.main.async {
NSSound(named: "Glass")?.play()
}
}
})
}
}
}
}
}
// this sound is only when succeded and or it fail when theme is not found
NSSound(named: success ? "Glass" : "Basso")?.play()
if beep {
NSSound.beep()
}
}
}
@ -340,7 +366,7 @@ NSTableViewDelegate, NSTableViewDataSource, WebFrameLoadDelegate, WebUIDelegate
}
@IBAction func unInstallPressed(_ sender: NSButton!) {
if AppSD.isInstalling {
if AppSD.isInstalling || self.isBusy {
NSSound.beep()
return
}
@ -380,7 +406,7 @@ NSTableViewDelegate, NSTableViewDataSource, WebFrameLoadDelegate, WebUIDelegate
}
@IBAction func InstallPressed(_ sender: NSButton!) {
if AppSD.isInstalling {
if AppSD.isInstalling || self.isBusy {
NSSound.beep()
return
}
@ -396,42 +422,68 @@ NSTableViewDelegate, NSTableViewDataSource, WebFrameLoadDelegate, WebUIDelegate
}
let theme = self.nameBox.stringValue
let themeDest = self.targetVolume!.addPath("EFI/CLOVER/themes").addPath(theme)
self.spinner.startAnimation(nil)
let sr = self.sidebar.selectedRow
if sr >= 0 && sr < self.dataSource().count {
// ---------
self.isBusy = true
self.spinner.startAnimation(nil)
self.optimizeButton.isEnabled = false
self.installButton.isEnabled = false
self.unistallButton.isEnabled = false
self.targetPop.isEnabled = false
self.nameBox.isEnabled = false
self.installedThemesCheckBox.isEnabled = false
// ---------
self.manager?.download(theme: theme,
down: .complete,
completion: { (path) in
if let themePath = path {
try? fm.removeItem(atPath: themeDest)
do {
try fm.moveItem(atPath: themePath, toPath: themeDest)
NSSound(named: "Glass")?.play()
} catch {
NSSound(named: "Basso")?.play()
DispatchQueue.main.async {
let alert = NSAlert()
alert.messageText = "Installation failed".locale
alert.informativeText = "\(error.localizedDescription)"
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
} else {
if self.manager?.statusError != nil {
NSSound(named: "Basso")?.play()
DispatchQueue.main.async {
let alert = NSAlert()
alert.messageText = "Installation failed".locale
alert.informativeText = "Theme \"\(theme)\" not found."
alert.informativeText = self.manager!.statusError!.localizedDescription
alert.addButton(withTitle: "OK")
alert.runModal()
}
} else {
if let themePath = path {
try? fm.removeItem(atPath: themeDest)
do {
try fm.moveItem(atPath: themePath, toPath: themeDest)
NSSound(named: "Glass")?.play()
} catch {
NSSound(named: "Basso")?.play()
DispatchQueue.main.async {
let alert = NSAlert()
alert.messageText = "Installation failed".locale
alert.informativeText = "\(error.localizedDescription)"
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
} else {
NSSound(named: "Basso")?.play()
DispatchQueue.main.async {
let alert = NSAlert()
alert.messageText = "Installation failed".locale
alert.informativeText = "Theme \"\(theme)\" not found."
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
}
DispatchQueue.main.async {
self.isBusy = false
AppSD.isInstalling = false
self.spinner.stopAnimation(nil)
self.targetPop.isEnabled = true
self.nameBox.isEnabled = true
self.installedThemesCheckBox.isEnabled = true
self.onSelection()
}
})
}
@ -498,6 +550,14 @@ NSTableViewDelegate, NSTableViewDataSource, WebFrameLoadDelegate, WebUIDelegate
}
func tableViewSelectionDidChange(_ notification: Notification) {
if self.isBusy {
NSSound.beep()
return
}
self.onSelection()
}
func onSelection() {
self.isPngTheme = false
self.optimizeButton.isEnabled = false
self.optimizeButton.state = .off
@ -517,7 +577,6 @@ NSTableViewDelegate, NSTableViewDataSource, WebFrameLoadDelegate, WebUIDelegate
} else {
self.installButton.isEnabled = false
}
if let path = v.imagePath {
self.nameBox.stringValue = v.name

View File

@ -126,33 +126,50 @@ typedef enum LodePNGColorType {
bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types.
Return value: LodePNG error code (0 means no error).
*/
unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h,
const unsigned char* in, size_t insize,
LodePNGColorType colortype, unsigned bitdepth);
unsigned lodepng_decode_memory(unsigned char** out,
unsigned* w,
unsigned* h,
const unsigned char* in,
size_t insize,
LodePNGColorType colortype,
unsigned bitdepth);
/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/
unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h,
const unsigned char* in, size_t insize);
unsigned lodepng_decode32(unsigned char** out,
unsigned* w,
unsigned* h,
const unsigned char* in,
size_t insize);
/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/
unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h,
const unsigned char* in, size_t insize);
unsigned lodepng_decode24(unsigned char** out,
unsigned* w,
unsigned* h,
const unsigned char* in,
size_t insize);
#ifdef LODEPNG_COMPILE_DISK
/*
Load PNG from disk, from file with given name.
Same as the other decode functions, but instead takes a filename as input.
*/
unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h,
unsigned lodepng_decode_file(unsigned char** out,
unsigned* w,
unsigned* h,
const char* filename,
LodePNGColorType colortype, unsigned bitdepth);
LodePNGColorType colortype,
unsigned bitdepth);
/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/
unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h,
unsigned lodepng_decode32_file(unsigned char** out,
unsigned* w,
unsigned* h,
const char* filename);
/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/
unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
unsigned lodepng_decode24_file(unsigned char** out,
unsigned* w,
unsigned* h,
const char* filename);
#endif /*LODEPNG_COMPILE_DISK*/
#endif /*LODEPNG_COMPILE_DECODER*/
@ -175,17 +192,27 @@ unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
bitdepth: the bit depth of the raw input image. See explanation on PNG color types.
Return value: LodePNG error code (0 means no error).
*/
unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize,
const unsigned char* image, unsigned w, unsigned h,
LodePNGColorType colortype, unsigned bitdepth);
unsigned lodepng_encode_memory(unsigned char** out,
size_t* outsize,
const unsigned char* image,
unsigned w,
unsigned h,
LodePNGColorType colortype,
unsigned bitdepth);
/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/
unsigned lodepng_encode32(unsigned char** out, size_t* outsize,
const unsigned char* image, unsigned w, unsigned h);
unsigned lodepng_encode32(unsigned char** out,
size_t* outsize,
const unsigned char* image,
unsigned w,
unsigned h);
/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/
unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
const unsigned char* image, unsigned w, unsigned h);
unsigned lodepng_encode24(unsigned char** out,
size_t* outsize,
const unsigned char* image,
unsigned w,
unsigned h);
#ifdef LODEPNG_COMPILE_DISK
/*
@ -194,16 +221,23 @@ unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
NOTE: This overwrites existing files without warning!
*/
unsigned lodepng_encode_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h,
LodePNGColorType colortype, unsigned bitdepth);
const unsigned char* image,
unsigned w,
unsigned h,
LodePNGColorType colortype,
unsigned bitdepth);
/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/
unsigned lodepng_encode32_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h);
const unsigned char* image,
unsigned w,
unsigned h);
/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/
unsigned lodepng_encode24_file(const char* filename,
const unsigned char* image, unsigned w, unsigned h);
const unsigned char* image,
unsigned w,
unsigned h);
#endif /*LODEPNG_COMPILE_DISK*/
#endif /*LODEPNG_COMPILE_ENCODER*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,342 @@
/*
Clover.app
language code: ar
Copyright © 2019-2020 CloverHackyColor. All rights reserved.
*/
// Globals
"Unsupported" = "Unsupported";
"N/A" = "N/A"; // not available (please be short)
// Mount / unmount
"Clover wants to mount %@" = "Clover wants to mount %@";
"Clover wants to umount %@" = "Clover wants to umount %@";
"Mount" = "Mount";
"umount" = "umount";
"mount point" = "mount point";
"*auto mount" = "*auto mount";
// Info
"System Serial Number:" = "System Serial Number:";
"Model:" = "Model:";
"board-id:" = "board-id:";
"OEM Vendor:" = "OEM Vendor:";
"OEM Product:" = "OEM Product:";
"OEM Board:" = "OEM Board:";
"NVRAM is native:" = "NVRAM is native:";
"unknown" = "unknown";
"Yes" = "Yes"; // first char upper case
"No" = "No"; // first char upper case
// Sound
"Startup Sound" = "Startup Sound";
"Device:" = "Device:";
"Volume level:" = "Volume level:";
"LineOut" = "Line Out";
"Speaker" = "Speaker";
"Headphones" = "Headphones";
"Garniture" = "Garniture";
"Other" = "Other";
"true" = "true";
"false" = "false";
// Theme
"Theme:" = "Theme:";
"Themes" = "Themes"; // window title
"No themes found" = "No themes found";
"Manager" = "Manager";
"Can't remove the theme" = "Can't remove the theme";
"Show installed" = "Show installed";
"Optimize" = "Optimize";
"Sound:" = "Sound:";
// Main view (pop over)
"*Make filesystem read-write" = "*Make filesystem read-write";
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
"*Require CloverDaemon" = "*Require CloverDaemon";
"Read daemon log" = "Read daemon log";
"Read bdmesg" = "Read boot log";
"Install CloverDaemonNew" = "Install CloverDaemonNew";
"Uninstall CloverDaemonNew" = "Uninstall CloverDaemonNew";
"Update" = "Update";
"Download" = "Download";
"Update to r%d" = "Update to r%d"; // Update to r5101
"Check update:" = "Check update:";
"Check now" = "Check now";
"never" = "never";
"daily" = "daily";
"weekly" = "weekly";
"monthly" = "monthly";
"last checked:" = "last checked:"; // last date update was checked
"Run at login" = "Run at login";
"Close" = "إغلاق";
// Installer
"Install Clover" = "Install Clover";
"Current Clover revision" = "Current Clover revision";
"Boot Device:" = "Boot Device:";
"config path:" = "config path:";
"Installation succeded" = "Installation succeded";
"Installation failed" = "Installation failed";
"Clover Installer" = "Clover Installer";
"Select a disk.." = "Select a disk..";
"Install" = "Install";
"Uninstall" = "Uninstall";
"AltBoot" = "Alternative boot";
// Clover Bootloader and drivers
"UEFI only" = "Install Clover to be used with UEFI motherboards only.";
"Install alternative booting PBR" = "Install alternative booting PBR with choice of boot with a key pressed.
File to boot = boot{keypressed}";
"Don't install any bootloader (boot0X, boot1X)" = "Don't install any bootloader (boot0X, boot1X).
Usefull for UEFI motherboards that don't need bootloader files.
Can also be use if you don't want to upgrade MBR or PBR sectors.";
"Clover legacy BIOS boot sectors" = "Clover EFI requires three essential files. (in simple terms)
boot0 (On the drive's MBR) responsible for loading boot1.
boot1 (On the partition's boot-sector) to finding boot2.
boot2 (On the partition's root directory) for loading CLOVERX64.efi, and kernel etc.";
"boot0af" = "Used for BIOS booting on BIOS motherboards.
boot0af (boot0 Active First) bootloader try to boot the active partition defined in MBR. If there is no active partition, it will try to boot the first EFI/FAT32/HFS partition (defined in the MBR and then the GPT) with a valid PBR signature.
This choice will setup selected HFS/Fat32 partition to be active.";
"boot0ss" = "Used for BIOS booting on BIOS motherboards.
boot0ss (boot0 Signature Scanning) bootloader try to boot the first EFI/FAT32/HFS partition (defined in the MBR and then the GPT) with a valid PBR signature. If no partition is found it will try to boot the active partition defined in MBR.
This bootloader is a good choice when you have Windows installed on the same disk because Windows wants to have its partition active.
This choice will not activate any partition in MBR.";
"boot6" = "Clover EFI 64-bits using SATA to access drives.";
"boot7" = "Clover EFI 64-bits BiosBlockIO";
"UEFI mandatory" = "UEFI mandatory";
"BIOS mandatory" = "BIOS mandatory";
"UEFI/Other" = "UEFI/Other";
"BIOS/Other" = "BIOS/Other";
"UEFI, but not from this installer" = "UEFI, but not from this installer";
"BIOS, but not from this installer" = "BIOS, but not from this installer";
"UEFI/FileSystem" = "UEFI, filesystem drivers";
"BIOS/FileSystem" = "BIOS, filesystem drivers";
"UEFI/HID" = "UEFI, Human Interface Devices";
"BIOS/HID" = "BIOS, Human Interface Devices";
"UEFI/FileVault2" = "UEFI, FileVault 2 drivers";
"BIOS/FileVault2" = "BIOS, FileVault 2 drivers";
"UEFI/MemoryFix" = "UEFI, memory fix drivers";
"ApfsDriverLoader.efi" = "Supports APFS filesystem driver from container for macOS 10.13 and newers";
"AppleImageCodec.efi" = "Decode PNG and BMP for FileVault2.";
"AppleImageLoader.efi" = "Secure AppleEfiFat binary driver with implementation of AppleLoadImage protocol with EfiBinary signature verification.";
"AppleKeyAggregator.efi" = "Support for boot UI dialog for FileVault2.";
"AppleKeyFeeder.efi" = "Support for PS/2 keyboard to use with FileVault 2.";
"AppleUISupport.efi" = "Set of protocols for support EfiLoginUi for FileVault.";
"AppleUITheme.efi" = "Create boot UI Themes support for FileVault2.";
"AptioInputFix.efi" = "Driver to fix input problems on UEFI firmware such as AMI Aptio.";
"AptioMemoryFix.efi" = "Preferred driver to fix Memory problems on UEFI firmware such as AMI Aptio. Do not use with other AptioFix together.";
"AudioDxe.efi" = "HDA driver to play Startup Sound during Clover boot.
Configure it via Startup sound output from Option menu, including:
Volume and Audio device (for supported IOAudio devices only).";
"FirmwareVolume.efi" = "Create FirmwareVolume with cursor images for FileVault2.";
"FSInject.efi" = "Provide injection of kernel extensions from Clover folder and allow to force load them from both /System/Library/Extensions and /Library/Extensions.";
"GrubEXFAT.efi" = "ExFAT filesystem driver from GRUB.";
"GrubISO9660.efi" = "ISO 9600 filesystem driver from GRUB.";
"GrubNTFS.efi" = "NTFS filesystem driver from GRUB.";
"GrubUDF.efi" = "UDF filesystem driver from GRUB.";
"SMCHelper.efi" = "Restore SMC keys left in NVRAM by FakeSMC.";
"XhciDxe.efi" = "USB 3.0 driver";
"AppleEvent.uefi" = "Create AppleEvent protocol for FileVault2.";
"AppleGraphicsConfig.uefi" = "Create optional AppleGraphicsConfig protocol.";
"CsmVideoDxe.efi" = "Video Driver for Clover GUI allowing to choose more resolutions. It is based on CSM module in UEFI BIOS and required CSM will be enabled.
Clover may not started with it and you may have wake problem in system. Use with precautions.";
"DataHubDxe.efi" = "This is DataHub protocol support mandatory for macOS.
Usually it is already present but sometime it may be missed. In this case you should see warning on screen.";
"EmuVariableUefi.efi" = "Workaround for store NVRAM variables for systems without UEFI hardware.
Mostly UEFI boot uses hardware NVRAM but in some rare cases this driver is needed. Use it only if you have a problem without it";
"EnglishDxe.efi" = "Support for UnicodeCollation protocol used by EFI Shell if it missed in UEFI.";
"Fat.efi" = "FAT filesystem driver.";
"HashServiceFix.efi" = "Fix Hash support if absent in native UEFI BIOS.";
"HFSPlus.efi" = "Alternate HFS+ filesystem driver.";
"NvmExpressDxe.efi" = "Driver for support NVM Express devices.";
"OsxAptioFix3Drv.efi" = "Alternate driver (v3) to fix Memory problems on UEFI firmware. Do not use with other AptioFix together.";
"OsxAptioFixDrv.efi" = "Old Driver to fix Memory problems on UEFI firmware such as AMI Aptio. Do not use with other AptioFix together.";
"OsxFatBinaryDrv.efi" = "Driver for support FAT Binary executables for OS X 10.9 and older.";
"OsxLowMemFixDrv.efi" = "Simplified variant of OsxAptioFixDrv. Do not use with other AptioFix together.";
"PartitionDxe.efi" = "Driver to support non-usual partition maps such as: hybrid GPT/MBR or Apple Partition Map.";
"Ps2MouseDxe.efi" = "PS/2 mouse driver";
"UsbKbDxe.efi" = "Keyboard driver for boot UI support.";
"UsbMouseDxe.efi" = "USB mouse driver";
"VBoxExt2.efi" = "EXT2/3 filesystem driver from VirtualBox.";
"VBoxExt4.efi" = "EXT4 filesystem driver from VirtualBox.";
"VBoxHfs.efi" = "HFS+ filesystem driver.";
"VBoxIso9600.efi" = "ISO 9600 filesystem driver.";
// Plist Editor Menu
// Clover.app Menu
"About Clover" = "حول Clover";
"Preferences…" = "تفضيلات…";
"Services" = "خدمات";
"Hide Clover" = "إخفاء Clover";
"Hide Others" = "إخفاء الأخرى";
"Show All" = "إظهار الكل";
"Quit Clover" = "إنهاء Clover";
// File
"New" = "New";
"Open…" = "فتح…";
"Open Recent" = "Open Recent";
"Close" = "إغلاق";
"Page Setup…" = "Page Setup…";
"Print…" = "Print…";
// Edit
"Undo" = "تراجع";
"Redo" = "إعادة";
"Cut" = "قص";
"Copy" = "نسخ";
"Paste" = "لصق";
"Paste and Match Style" = "لصق ومطابقة النمط";
"Delete" = "حذف";
"Select All" = "تحديد الكل";
// Edit->Find
"Edit" = "تحرير";
"File" = "ملف";
"Find" = "بحث";
"Find…" = "بحث…";
"Find and Replace…" = "بحث واستبدال...";
"Find Next" = "بحث عن التالي";
"Find Previous" = "بحث عن السابق";
"Use Selection for Find" = "استخدام التحديد للبحث";
"Use Selection for Replace" = "استخدام التحديد للاستبدال";
"Jump to Selection" = "انتقال سريع إلى التحديد";
// Edit->Spelling and Grammar
"Spelling" = "تدقيق إملائي";
"Spelling and Grammar" = "التدقيق الإملائي والتدقيق النحوي";
"Show Spelling and Grammar" = "إظهار التدقيق الإملائي والتدقيق النحوي";
"Check Document Now" = "تدقيق المستند الآن";
"Check Spelling While Typing" = "تحقق من التدقيق الإملائي أثناء الكتابة";
"Check Grammar With Spelling" = "تحقق من التدقيق النحوي مع التدقيق الإملائي";
"Correct Spelling Automatically" = "تصحيح التدقيق الإملائي تلقائيًا";
// Edit->Substitutions
"Substitutions" = "البدائل";
"Show Substitutions" = "إظهار البدائل";
"Smart Copy/Paste" = "النسخ/اللصق الذكي";
"Smart Quotes" = "علامات الاقتباس الذكية";
"Smart Dashes" = "شرط ذكية";
"Smart Links" = "الروابط الذكية";
"Text Replacement" = "استبدال النص";
// Edit->Transformations
"Transformations" = "التحويلات";
"Make Upper Case" = "استخدام الأحرف الكبيرة";
"Make Lower Case" = "تحويل إلى أحرف صغيرة";
"Capitalize" = "البدء بأحرف كبيرة";
// Edit->Speech
"Speech" = "محادثة";
"Start Speaking" = "بدء التكلم";
"Stop Speaking" = "إيقاف التكلم";
// View
"View" = "View";
"Show Toolbar" = "Show Toolbar";
"Customize Toolbar…" = "Customize Toolbar…";
"Show Sidebar" = "Show Sidebar";
// Window
"Window" = "نافذة";
"Minimize" = "تصغير";
"Zoom" = "تكبير/تصغير";
"Bring Clover Window to Front" = "<null>";
"Bring All to Front" = "إحضار الكل إلى الأمام";
// Help
"Clover Help" = "Clover Help";
// Plist Editor document
"Search" = "Search";
"Replace" = "Replace";
"All" = "All";
"Item" = "Item";
"Items" = "Items";
"Untiteled" = "Untiteled";
"New Item" = "New Item";
"bytes" = "bytes";
"typing" = "typing";
"change type" = "change type";
"change bool value" = "change bool value";
"replace duplicate key" = "replace duplicate key";
"move item" = "move item";
"paste Item" = "paste Item";
"remove Item" = "remove Item";
"cut Item" = "cut Item";
"add new Item" = "add new Item";
"No data" = "No data";
"missing '<' at the beginning" = "missing '<' at the beginning";
"missing '>' at the end" = "missing '>' at the end";
"Your data contains illegal characters" = "Your data contains illegal characters";
"bytes count is odd, must be even" = "bytes count is odd, must be even";
"Keep editing" = "Keep editing";
"Duplicate key in the Dictionary!" = "Duplicate key in the Dictionary!";
"'%@' is already present in the Dictionary. Do you want to undo the editing or replace the existing key?" = "'%@' is already present in the Dictionary. Do you want to undo the editing or replace the existing key?";
"Invalid value detected!" = "Invalid value detected!";
"Your edit is not valid. Do you want to restore last valid value or keep editing?" = "Your edit is not valid. Do you want to restore last valid value or keep editing?";
// Plist Editor header
"Key" = "Key";
"Type" = "Type";
"Value" = "Value";
// Plist Editor tags
"Dictionary" = "Dictionary";
"Array" = "Array";
"String" = "String";
"Number" = "Number";
"Bool" = "Bool";
"Date" = "Date";
"Data" = "Data";
// Plist Editor Boolean values
"YES" = "YES";
"NO" = "NO";

Some files were not shown because too many files have changed in this diff Show More