mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2025-01-21 20:51:31 +01:00
Clover.app v1.15 with themes manager
Themes can be downloaded without git. The parser allows users to chose the Github user and the repository name, and so load themes from forks or any repo that has compatible directories structure (the repository must contains only themes, at first level).
This commit is contained in:
parent
27de9a9c9a
commit
99e9fc5d89
@ -23,6 +23,11 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
955BEE1923C6B4B300425AB0 /* ThemeManager.xib in Resources */ = {isa = PBXBuildFile; fileRef = 955BEE1823C6B4B300425AB0 /* ThemeManager.xib */; };
|
||||
955BEE1B23C7171100425AB0 /* check.png in Resources */ = {isa = PBXBuildFile; fileRef = 955BEE1A23C7171000425AB0 /* check.png */; };
|
||||
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 */; };
|
||||
@ -188,6 +193,11 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
955BEE1823C6B4B300425AB0 /* ThemeManager.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ThemeManager.xib; sourceTree = "<group>"; };
|
||||
955BEE1A23C7171000425AB0 /* check.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = check.png; sourceTree = "<group>"; };
|
||||
955F7C6C238DCD160019D088 /* DiskArbitration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DiskArbitration.framework; path = System/Library/Frameworks/DiskArbitration.framework; sourceTree = SDKROOT; };
|
||||
95609069238C61E200ACD7F7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
9560906C238C624200ACD7F7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
@ -388,6 +398,17 @@
|
||||
path = images;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
955BEE1123C6B43C00425AB0 /* ThemeManager */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
955BEE1323C6B49C00425AB0 /* ThemeManager.swift */,
|
||||
955BEE1423C6B49C00425AB0 /* ThemeManagerVC.swift */,
|
||||
955BEE1223C6B49C00425AB0 /* ThemeView.swift */,
|
||||
955BEE1823C6B4B300425AB0 /* ThemeManager.xib */,
|
||||
);
|
||||
path = ThemeManager;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
95C51578236B21AE00E4A3A8 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -423,6 +444,7 @@
|
||||
95E68AC8235B862F002B37A5 /* Clover */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
955BEE1123C6B43C00425AB0 /* ThemeManager */,
|
||||
95E68AC9235B862F002B37A5 /* AppDelegate.swift */,
|
||||
95C5152E236A0A7400E4A3A8 /* SettingsView.swift */,
|
||||
95C51535236B1F7700E4A3A8 /* RunAtLogin.swift */,
|
||||
@ -438,6 +460,7 @@
|
||||
95E50075238ABA56002F3869 /* Tasks.swift */,
|
||||
953BC20223720C0A0039755D /* FixedWidthViews.swift */,
|
||||
9533718223709517003F1AF4 /* bootsectors-install */,
|
||||
955BEE1A23C7171000425AB0 /* check.png */,
|
||||
9569EC44238DD772003AD72C /* Settings.xib */,
|
||||
95609068238C61E200ACD7F7 /* MainMenu.xib */,
|
||||
95E68AD9235B8666002B37A5 /* Installer */,
|
||||
@ -639,8 +662,10 @@
|
||||
954DECD523899F5F006A9876 /* Bootmanager.png in Resources */,
|
||||
95E68ACE235B8632002B37A5 /* Assets.xcassets in Resources */,
|
||||
9569EC42238DD772003AD72C /* Settings.xib in Resources */,
|
||||
955BEE1B23C7171100425AB0 /* check.png in Resources */,
|
||||
9560906A238C61E200ACD7F7 /* MainMenu.xib in Resources */,
|
||||
9533718323709517003F1AF4 /* bootsectors-install in Resources */,
|
||||
955BEE1923C6B4B300425AB0 /* ThemeManager.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -698,6 +723,7 @@
|
||||
95E68ADF235B86A1002B37A5 /* Releases.swift in Sources */,
|
||||
954BBE99238196EE0032425F /* Locale.swift in Sources */,
|
||||
95E68ACA235B862F002B37A5 /* AppDelegate.swift in Sources */,
|
||||
955BEE1523C6B49C00425AB0 /* ThemeView.swift in Sources */,
|
||||
958861DA235F75FB00B64173 /* Driver.swift in Sources */,
|
||||
95E68AE3235B86A1002B37A5 /* bdmesg.swift in Sources */,
|
||||
95C5152F236A0A7400E4A3A8 /* SettingsView.swift in Sources */,
|
||||
@ -709,6 +735,8 @@
|
||||
95E68AE0235B86A1002B37A5 /* Extensions.swift in Sources */,
|
||||
955689DB23A2728000AD323C /* IO.swift in Sources */,
|
||||
9555AF26238EE53300108C33 /* InstallerOutline.swift in Sources */,
|
||||
955BEE1723C6B49C00425AB0 /* ThemeManagerVC.swift in Sources */,
|
||||
955BEE1623C6B49C00425AB0 /* ThemeManager.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1021,7 +1049,7 @@
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1.14;
|
||||
CURRENT_PROJECT_VERSION = 1.15;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Clover/Frameworks",
|
||||
@ -1037,7 +1065,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Clover/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.14;
|
||||
MARKETING_VERSION = 1.15;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.slice.Clover;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@ -1057,7 +1085,7 @@
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1.14;
|
||||
CURRENT_PROJECT_VERSION = 1.15;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Clover/Frameworks",
|
||||
@ -1072,7 +1100,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Clover/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.14;
|
||||
MARKETING_VERSION = 1.15;
|
||||
OTHER_LDFLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.slice.Clover;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -16,6 +16,11 @@ let localeBundle = Bundle(path: Bundle.main.sharedSupportPath! + "/Lang.bundle")
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
||||
var isInstalling : Bool = false
|
||||
var isInstallerOpen : Bool = false
|
||||
var themeUser = UDs.string(forKey: kThemeUserKey) ?? kDefaultThemeUser
|
||||
var themeRepo = UDs.string(forKey: kThemeRepoKey) ?? kDefaultThemeRepo
|
||||
|
||||
var themes : [String] = [String]()
|
||||
var installedThemes : [String] = [String]()
|
||||
var popover : NSPopover?
|
||||
|
||||
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||
@ -23,6 +28,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
||||
var settingsWC: SettingsWindowController? = nil
|
||||
var installerWC : InstallerWindowController? = nil
|
||||
var installerOutWC : InstallerOutWindowController? = nil
|
||||
var themeManagerWC : ThemeManagerWC?
|
||||
|
||||
var daSession : DASession? = nil
|
||||
var daContext : UnsafeMutablePointer<Int> = UnsafeMutablePointer<Int>.allocate(capacity: 1)
|
||||
@ -87,8 +93,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
self.settingsWC = SettingsWindowController.loadFromNib()
|
||||
|
||||
NotificationCenter.default.addObserver(self,
|
||||
|
@ -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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<menu key="menu" id="tIP-gO-fKT"/>
|
||||
</popUpButtonCell>
|
||||
<userDefinedRuntimeAttributes>
|
||||
@ -80,7 +80,7 @@
|
||||
<rect key="frame" x="14" y="186" width="311" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" title="Check update:" id="fMZ-r2-dlm">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -89,7 +89,7 @@
|
||||
<rect key="frame" x="14" y="91" width="311" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" title="Label" id="v3d-HG-AeW">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="disableSleepProxy:" target="tSJ-3G-dnL" id="jia-ue-L5p"/>
|
||||
@ -174,7 +174,7 @@
|
||||
<rect key="frame" x="21" y="265" width="304" height="11"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="clipping" title="*Require CloverDaemon" id="yQ9-cU-wtc">
|
||||
<font key="font" metaFont="label" size="9"/>
|
||||
<font key="font" metaFont="menu" size="9"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="makeRootRW:" target="tSJ-3G-dnL" id="KQZ-a6-GKv"/>
|
||||
@ -195,7 +195,7 @@
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Read daemon log" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="UZY-ie-enb">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="readDaemonLog:" target="tSJ-3G-dnL" id="Mo0-RL-dK0"/>
|
||||
@ -206,7 +206,7 @@
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Read bdmesg" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bhg-iF-USh">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="readbdmesg:" target="tSJ-3G-dnL" id="eFP-wf-VyP"/>
|
||||
@ -216,7 +216,7 @@
|
||||
<rect key="frame" x="61" y="9" width="224" height="15"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="Label" id="goU-fD-Ng6">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -284,7 +284,7 @@
|
||||
<rect key="frame" x="30" y="20" width="294" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingHead" selectable="YES" allowsUndo="NO" focusRingType="none" title="N/A" id="EX6-6q-G8U">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -298,7 +298,7 @@
|
||||
<rect key="frame" x="30" y="56" width="294" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" selectable="YES" allowsUndo="NO" title="N/A" id="EFW-lW-i6c">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -324,7 +324,7 @@
|
||||
<rect key="frame" x="30" y="79" width="294" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" selectable="YES" focusRingType="none" title="N/A" id="PZZ-nm-ERe">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -342,7 +342,7 @@
|
||||
<rect key="frame" x="30" y="39" width="294" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" selectable="YES" focusRingType="none" title="N/A" id="oPZ-3Y-89o">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -360,7 +360,7 @@
|
||||
<rect key="frame" x="30" y="0.0" width="294" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" selectable="YES" focusRingType="none" title="N/A" id="okN-IH-2al">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -386,7 +386,7 @@
|
||||
<rect key="frame" x="30" y="79" width="294" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" selectable="YES" focusRingType="none" title="N/A" id="HZg-rD-gZv">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -413,7 +413,7 @@
|
||||
<rect key="frame" x="30" y="39" width="294" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" selectable="YES" focusRingType="none" title="Label" id="J7N-7a-sdN">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -422,7 +422,7 @@
|
||||
<rect key="frame" x="30" y="0.0" width="294" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" selectable="YES" focusRingType="none" title="Label" id="0dT-GZ-Ytt">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -474,7 +474,7 @@
|
||||
<rect key="frame" x="256" y="497" width="69" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<segmentedCell key="cell" controlSize="small" borderStyle="border" alignment="left" style="rounded" trackingMode="momentary" id="3O5-bI-89P">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<segments>
|
||||
<segment image="NSLeftFacingTriangleTemplate" width="31"/>
|
||||
<segment image="NSRightFacingTriangleTemplate" width="31" tag="1"/>
|
||||
@ -487,10 +487,10 @@
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<tabViewItems>
|
||||
<tabViewItem identifier="Mount" id="F4n-I1-4Gq">
|
||||
<view key="view" ambiguous="YES" id="E05-QZ-1bf">
|
||||
<view key="view" id="E05-QZ-1bf">
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="113"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
@ -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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -524,7 +524,7 @@
|
||||
<rect key="frame" x="21" y="58" width="52" height="11"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="clipping" title="(ESP)" id="TTH-hz-cX2">
|
||||
<font key="font" metaFont="label" size="9"/>
|
||||
<font key="font" metaFont="menu" size="9"/>
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="umount:" target="tSJ-3G-dnL" id="KqE-Db-Uf1"/>
|
||||
@ -555,32 +555,88 @@
|
||||
</view>
|
||||
</tabViewItem>
|
||||
<tabViewItem identifier="Theme" id="Th2-UC-6Hh">
|
||||
<view key="view" id="hLu-g7-21T">
|
||||
<view key="view" ambiguous="YES" id="hLu-g7-21T">
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="113"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3iW-Mp-ZEr">
|
||||
<rect key="frame" x="21" y="74" width="303" height="16"/>
|
||||
<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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0Zs-Un-dNa" customClass="FWTextField" customModule="Clover" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="46" width="199" height="21"/>
|
||||
<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"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="DVf-rV-Hrc">
|
||||
<font key="font" metaFont="system"/>
|
||||
<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"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<objectValues>
|
||||
<string>Item 1</string>
|
||||
<string>Item 2</string>
|
||||
<string>Item 3</string>
|
||||
</objectValues>
|
||||
</comboBoxCell>
|
||||
<connections>
|
||||
<action selector="themeBoxSelected:" target="tSJ-3G-dnL" id="76K-Tu-HPV"/>
|
||||
</connections>
|
||||
</comboBox>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AzG-cL-pFW">
|
||||
<rect key="frame" x="17" y="33" width="311" height="32"/>
|
||||
<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"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="openThemeManager:" target="tSJ-3G-dnL" id="PE4-2e-cP2"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6VD-VJ-phW">
|
||||
<rect key="frame" x="21" y="20" width="75" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" title="GitHub user" id="Bi4-5Q-YMR">
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QbU-PE-PmE">
|
||||
<rect key="frame" x="21" y="2" width="75" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" title="GitHub repo" id="dE6-xG-swG">
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2Va-F7-vYB">
|
||||
<rect key="frame" x="102" y="1" width="220" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="mini" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" id="dmw-9Z-gN5">
|
||||
<font key="font" metaFont="menu" size="9"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="fixedWidth">
|
||||
<real key="value" value="199"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
</userDefinedRuntimeAttributes>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="tSJ-3G-dnL" id="tWZ-Mw-jdD"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rfY-Ds-j3c">
|
||||
<rect key="frame" x="102" y="19" width="220" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="mini" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" id="glm-s6-df6">
|
||||
<font key="font" metaFont="menu" size="9"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="tSJ-3G-dnL" id="IJL-eP-tCC"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
</view>
|
||||
@ -595,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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<menu key="menu" id="zSa-aR-ghV">
|
||||
<items>
|
||||
<menuItem title="Item 1" state="on" id="hlk-UE-ZPw"/>
|
||||
@ -620,7 +676,7 @@
|
||||
<rect key="frame" x="18" y="94" width="306" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="right" title="Startup Sound" id="Vif-FP-OaO">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -634,7 +690,7 @@
|
||||
<rect key="frame" x="42" y="4" width="37" height="11"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="clipping" title="0%" id="vRT-SW-j4L">
|
||||
<font key="font" metaFont="label" size="9"/>
|
||||
<font key="font" metaFont="menu" size="9"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -643,7 +699,7 @@
|
||||
<rect key="frame" x="18" y="25" width="306" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" title="Volume level:" id="ebr-lB-pTD">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -652,7 +708,7 @@
|
||||
<rect key="frame" x="18" y="79" width="241" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" title="Device:" id="sIi-by-bNC">
|
||||
<font key="font" metaFont="message" size="11"/>
|
||||
<font key="font" metaFont="label" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -661,7 +717,7 @@
|
||||
<rect key="frame" x="263" y="80" width="61" height="11"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<textFieldCell key="cell" controlSize="mini" lineBreakMode="clipping" alignment="right" title="r5102 +" id="JZg-ju-g1k">
|
||||
<font key="font" metaFont="label" size="9"/>
|
||||
<font key="font" metaFont="menu" size="9"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -675,7 +731,7 @@
|
||||
<rect key="frame" x="18" y="477" width="104" 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="system"/>
|
||||
<font key="font" metaFont="label" size="13"/>
|
||||
<segments>
|
||||
<segment width="32"/>
|
||||
<segment width="32" selected="YES" tag="1"/>
|
||||
@ -726,6 +782,7 @@
|
||||
<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="openThemeButton" destination="AzG-cL-pFW" id="U9N-0u-T2f"/>
|
||||
<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"/>
|
||||
@ -735,7 +792,9 @@
|
||||
<outlet property="tabViewFunc" destination="vwp-hc-6yE" id="gQE-Kf-hSJ"/>
|
||||
<outlet property="tabViewFuncSelector" destination="4ox-0p-Q9L" id="Tes-Eu-6Tt"/>
|
||||
<outlet property="tabViewInfo" destination="PGs-4a-XRc" id="ghG-r0-tYY"/>
|
||||
<outlet property="themeField" destination="0Zs-Un-dNa" id="gHl-ys-oZv"/>
|
||||
<outlet property="themeBox" destination="MMH-mb-fPM" id="2nx-c0-WqX"/>
|
||||
<outlet property="themeRepoField" destination="2Va-F7-vYB" id="o15-c9-jRd"/>
|
||||
<outlet property="themeUserField" destination="rfY-Ds-j3c" id="RNj-Rn-fsb"/>
|
||||
<outlet property="timeIntervalPopUp" destination="1qf-uO-CvA" id="774-SY-GdB"/>
|
||||
<outlet property="unInstallDaemonButton" destination="MWn-i4-CYn" id="r2Y-WW-wd9"/>
|
||||
<outlet property="unmountButton" destination="Zc5-Dl-jIh" id="5wb-ye-REX"/>
|
||||
|
@ -60,3 +60,106 @@ extension String {
|
||||
return UnsafePointer<UInt8>(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension URL {
|
||||
// https://stackoverflow.com/questions/38343186/write-extend-file-attributes-swift-example?answertab=active#tab-top
|
||||
/// Get extended attribute.
|
||||
func extendedAttribute(forName name: String) throws -> Data {
|
||||
|
||||
let data = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> Data in
|
||||
|
||||
// Determine attribute size:
|
||||
let length = getxattr(fileSystemPath, name, nil, 0, 0, 0)
|
||||
guard length >= 0 else { throw URL.posixError(errno) }
|
||||
|
||||
// Create buffer with required size:
|
||||
var data = Data(count: length)
|
||||
|
||||
// Retrieve attribute:
|
||||
let result = data.withUnsafeMutableBytes { [count = data.count] in
|
||||
getxattr(fileSystemPath, name, $0.baseAddress, count, 0, 0)
|
||||
}
|
||||
guard result >= 0 else { throw URL.posixError(errno) }
|
||||
return data
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
/// Set extended attribute.
|
||||
func setExtendedAttribute(data: Data, forName name: String) throws {
|
||||
|
||||
try self.withUnsafeFileSystemRepresentation { fileSystemPath in
|
||||
let result = data.withUnsafeBytes {
|
||||
setxattr(fileSystemPath, name, $0.baseAddress, data.count, 0, 0)
|
||||
}
|
||||
guard result >= 0 else { throw URL.posixError(errno) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove extended attribute.
|
||||
func removeExtendedAttribute(forName name: String) throws {
|
||||
|
||||
try self.withUnsafeFileSystemRepresentation { fileSystemPath in
|
||||
let result = removexattr(fileSystemPath, name, 0)
|
||||
guard result >= 0 else { throw URL.posixError(errno) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Get list of all extended attributes.
|
||||
func listExtendedAttributes() throws -> [String] {
|
||||
|
||||
let list = try self.withUnsafeFileSystemRepresentation { fileSystemPath -> [String] in
|
||||
let length = listxattr(fileSystemPath, nil, 0, 0)
|
||||
guard length >= 0 else { throw URL.posixError(errno) }
|
||||
|
||||
// Create buffer with required size:
|
||||
var namebuf = Array<CChar>(repeating: 0, count: length)
|
||||
|
||||
// Retrieve attribute list:
|
||||
let result = listxattr(fileSystemPath, &namebuf, namebuf.count, 0)
|
||||
guard result >= 0 else { throw URL.posixError(errno) }
|
||||
|
||||
// Extract attribute names:
|
||||
let list = namebuf.split(separator: 0).compactMap {
|
||||
$0.withUnsafeBufferPointer {
|
||||
$0.withMemoryRebound(to: UInt8.self) {
|
||||
String(bytes: $0, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
/// Helper function to create an NSError from a Unix errno.
|
||||
private static func posixError(_ err: Int32) -> NSError {
|
||||
return NSError(domain: NSPOSIXErrorDomain, code: Int(err),
|
||||
userInfo: [NSLocalizedDescriptionKey: String(cString: strerror(err))])
|
||||
}
|
||||
}
|
||||
|
||||
extension io_object_t {
|
||||
/// - Returns: The device's name.
|
||||
func name() -> String? {
|
||||
let buf = UnsafeMutablePointer<io_name_t>.allocate(capacity: 1)
|
||||
defer { buf.deallocate() }
|
||||
return buf.withMemoryRebound(to: CChar.self, capacity: MemoryLayout<io_name_t>.size) {
|
||||
if IORegistryEntryGetName(self, $0) == KERN_SUCCESS {
|
||||
return String(cString: $0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func info() -> NSDictionary? {
|
||||
var serviceDictionary : Unmanaged<CFMutableDictionary>?
|
||||
if IORegistryEntryCreateCFProperties(self, &serviceDictionary, kCFAllocatorDefault, 0) == KERN_SUCCESS {
|
||||
if let info : NSDictionary = serviceDictionary?.takeRetainedValue() {
|
||||
return info
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -43,9 +43,9 @@ func getFirmawareVendor() -> String? {
|
||||
cleanedData.append(data[i])
|
||||
}
|
||||
}
|
||||
cleanedData.append(0x00)
|
||||
return String(bytes: cleanedData, encoding: .utf8)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -80,7 +80,9 @@ func getOEMVendor() -> String? {
|
||||
if let data = getEFIPlatform()?.object(forKey: "OEMVendor") as? Data {
|
||||
return String(bytes: data, encoding: .utf8)
|
||||
}
|
||||
return nil
|
||||
|
||||
let ockey = "4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:oem-vendor"
|
||||
return getNVRAM(variable: ockey)
|
||||
}
|
||||
|
||||
/// Get OEMProduct string.
|
||||
@ -88,7 +90,9 @@ func getOEMProduct() -> String? {
|
||||
if let data = getEFIPlatform()?.object(forKey: "OEMProduct") as? Data {
|
||||
return String(bytes: data, encoding: .utf8)
|
||||
}
|
||||
return nil
|
||||
|
||||
let ockey = "4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:oem-product"
|
||||
return getNVRAM(variable: ockey)
|
||||
}
|
||||
|
||||
/// Get OEMBoard string.
|
||||
@ -96,6 +100,63 @@ func getOEMBoard() -> String? {
|
||||
if let data = getEFIPlatform()?.object(forKey: "OEMBoard") as? Data {
|
||||
return String(bytes: data, encoding: .utf8)
|
||||
}
|
||||
|
||||
let ockey = "4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:oem-board"
|
||||
return getNVRAM(variable: ockey)
|
||||
}
|
||||
|
||||
/// Get OEMVendor Short string.
|
||||
func getOEMVendorShort() -> String? {
|
||||
if let vendor = getOEMVendor() {
|
||||
switch vendor {
|
||||
case "ASRock": fallthrough
|
||||
case "Alienware": fallthrough
|
||||
case "ECS": fallthrough
|
||||
case "EVGA": fallthrough
|
||||
case "FUJITSU": fallthrough
|
||||
case "IBM": fallthrough
|
||||
case "Intel": fallthrough
|
||||
case "Shuttle": fallthrough
|
||||
case "TOSHIBA": fallthrough
|
||||
case "XFX":
|
||||
return vendor
|
||||
case "Apple Inc.":
|
||||
return "Apple"
|
||||
case "ASUSTeK Computer INC.": fallthrough
|
||||
case "ASUSTeK COMPUTER INC.":
|
||||
return "ASUS"
|
||||
case "Dell Inc.":
|
||||
return "Dell"
|
||||
case "DFI": fallthrough
|
||||
case "DFI Inc.":
|
||||
return "DFI"
|
||||
case "EPoX COMPUTER CO., LTD":
|
||||
return "EPoX"
|
||||
case "First International Computer, Inc.":
|
||||
return "FIC"
|
||||
case "FUJITSU SIEMENS":
|
||||
return "FUJITSU"
|
||||
case "Gigabyte Technology Co., Ltd.":
|
||||
return "Gigabyte"
|
||||
case "Hewlett-Packard":
|
||||
return "HP"
|
||||
case "Intel Corp.": fallthrough
|
||||
case "Intel Corporation": fallthrough
|
||||
case "INTEL Corporation":
|
||||
return "Intel"
|
||||
case "Lenovo": fallthrough
|
||||
case "LENOVO":
|
||||
return "Lenovo"
|
||||
case "Micro-Star International": fallthrough
|
||||
case "MICRO-STAR INTERNATIONAL CO., LTD": fallthrough
|
||||
case "MICRO-STAR INTERNATIONAL CO.,LTD": fallthrough
|
||||
case "MSI":
|
||||
return "MSI"
|
||||
case "To be filled by O.E.M.": break
|
||||
default:
|
||||
return vendor
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -108,7 +169,6 @@ func getSystemSerialNumber() -> String? {
|
||||
cleanedData.append(data[i])
|
||||
}
|
||||
}
|
||||
cleanedData.append(0x00)
|
||||
return String(bytes: cleanedData, encoding: .utf8)
|
||||
}
|
||||
return nil
|
||||
@ -123,7 +183,6 @@ func getEFIModel() -> String? {
|
||||
cleanedData.append(data[i])
|
||||
}
|
||||
}
|
||||
cleanedData.append(0x00)
|
||||
return String(bytes: cleanedData, encoding: .utf8)
|
||||
}
|
||||
return nil
|
||||
@ -138,7 +197,6 @@ func getEFIBoardID() -> String? {
|
||||
cleanedData.append(data[i])
|
||||
}
|
||||
}
|
||||
cleanedData.append(0x00)
|
||||
return String(bytes: cleanedData, encoding: .utf8)
|
||||
}
|
||||
return nil
|
||||
@ -159,3 +217,49 @@ func isLegacyFirmware() -> Bool {
|
||||
|
||||
return !isUEFI
|
||||
}
|
||||
|
||||
/// Get the property value from the given IOService dictionary. You’re responsible for releasing the result (if not transferred to Swift objects).
|
||||
func IOServiceGetPropertyFrom(matching name: String, property: String) -> Any? {
|
||||
var obj : Any? = nil
|
||||
var iter : io_iterator_t = 0
|
||||
var rl : uint32 = 0
|
||||
|
||||
var result : kern_return_t = IORegistryCreateIterator(kIOMasterPortDefault,
|
||||
kIOServicePlane,
|
||||
0,
|
||||
&iter)
|
||||
|
||||
if result == KERN_SUCCESS && iter != 0 {
|
||||
var entry : io_object_t
|
||||
repeat {
|
||||
|
||||
entry = IOIteratorNext(iter)
|
||||
if entry != IO_OBJECT_NULL {
|
||||
if entry.name() == name {
|
||||
let ref = IORegistryEntryCreateCFProperty(entry,
|
||||
property as CFString,
|
||||
kCFAllocatorDefault,
|
||||
0)
|
||||
if ref != nil {
|
||||
obj = ref!.takeRetainedValue()
|
||||
IOObjectRelease(entry)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
rl += 1
|
||||
result = IORegistryIteratorEnterEntry(iter)
|
||||
} else {
|
||||
if rl == 0 {
|
||||
IOObjectRelease(entry)
|
||||
break
|
||||
}
|
||||
result = IORegistryIteratorExitEntry(iter)
|
||||
rl -= 1
|
||||
}
|
||||
} while (true)
|
||||
IOObjectRelease(iter)
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
@ -37,6 +37,43 @@ func getNVRAM() -> NSMutableDictionary? {
|
||||
return dict?.takeRetainedValue()
|
||||
}
|
||||
|
||||
/// Get a single nvram variable
|
||||
func getNVRAM(variable name: String) -> String? {
|
||||
var value : String? = nil
|
||||
var ref: io_registry_entry_t
|
||||
var masterPort = mach_port_t()
|
||||
var oResult: kern_return_t
|
||||
oResult = IOMasterPort(bootstrap_port, &masterPort)
|
||||
|
||||
if oResult != KERN_SUCCESS {
|
||||
return nil
|
||||
}
|
||||
|
||||
ref = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options")
|
||||
if ref == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
let vref = IORegistryEntryCreateCFProperty(ref,
|
||||
name as CFString,
|
||||
kCFAllocatorDefault, 0)
|
||||
if (vref != nil) {
|
||||
let data = vref?.takeRetainedValue() as! Data
|
||||
var cleanedData = Data()
|
||||
for i in 0..<data.count {
|
||||
if data[i] != 0x00 {
|
||||
cleanedData.append(data[i])
|
||||
}
|
||||
}
|
||||
|
||||
value = String(bytes: cleanedData, encoding: .utf8)
|
||||
}
|
||||
|
||||
IOObjectRelease(ref)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// MARK: set NVRAM key
|
||||
func setNVRAM(key: String, stringValue: String) {
|
||||
var cmd : String = "do shell script \""
|
||||
|
@ -19,7 +19,6 @@ class LITabView: NSTabView {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SoundSlider : NSSlider {
|
||||
var field : NSTextField?
|
||||
}
|
||||
@ -51,7 +50,10 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSessionD
|
||||
@IBOutlet var autoMountButton : NSButton!
|
||||
@IBOutlet var unmountButton : NSButton!
|
||||
// tab 1
|
||||
@IBOutlet var themeField : FWTextField!
|
||||
@IBOutlet var themeBox : NSComboBox!
|
||||
@IBOutlet var openThemeButton : NSButton!
|
||||
@IBOutlet var themeUserField : NSTextField!
|
||||
@IBOutlet var themeRepoField : NSTextField!
|
||||
// tab 2
|
||||
@IBOutlet var soundDevicePopUp : NSPopUpButton!
|
||||
@IBOutlet var soundVolumeSlider : SoundSlider!
|
||||
@ -89,8 +91,6 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSessionD
|
||||
|
||||
var loaded : Bool = false
|
||||
|
||||
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
if !self.loaded {
|
||||
@ -121,7 +121,38 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSessionD
|
||||
self.tabViewFuncSelector.selectSegment(withTag: 0)
|
||||
self.tabViewFunc.selectTabViewItem(at: 0)
|
||||
|
||||
self.themeField.delegate = self
|
||||
AppSD.themeUser = UDs.string(forKey: kThemeUserKey) ?? kDefaultThemeUser
|
||||
AppSD.themeRepo = UDs.string(forKey: kThemeRepoKey) ?? kDefaultThemeRepo
|
||||
|
||||
self.themeUserField.stringValue = AppSD.themeUser
|
||||
self.themeRepoField.stringValue = AppSD.themeRepo
|
||||
|
||||
if #available(OSX 10.10, *) {
|
||||
self.themeUserField.placeholderString = kDefaultThemeUser
|
||||
self.themeRepoField.placeholderString = kDefaultThemeRepo
|
||||
}
|
||||
|
||||
self.themeBox.removeAllItems()
|
||||
let themeManagerIndexDir = NSHomeDirectory().addPath("Library/Application Support/CloverApp/Themeindex/\(AppSD.themeUser)_\(AppSD.themeRepo)")
|
||||
|
||||
let tm = ThemeManager(user: AppSD.themeUser,
|
||||
repo: AppSD.themeRepo,
|
||||
basePath: themeManagerIndexDir,
|
||||
indexDir: themeManagerIndexDir,
|
||||
delegate: nil)
|
||||
|
||||
let indexedThemes = tm.getIndexedThemes()
|
||||
self.themeBox.addItems(withObjectValues: indexedThemes)
|
||||
|
||||
if indexedThemes.contains("embedded") {
|
||||
self.themeBox.addItem(withObjectValue: "embedded")
|
||||
}
|
||||
|
||||
if indexedThemes.contains("random") {
|
||||
self.themeBox.addItem(withObjectValue: "random")
|
||||
}
|
||||
|
||||
self.themeBox.completes = true
|
||||
|
||||
self.soundVolumeSlider.field = self.soundVolumeField
|
||||
self.soundVolumeField.stringValue = kNotAvailable.locale
|
||||
@ -170,10 +201,10 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSessionD
|
||||
var nvdata = nvram?.object(forKey: "Clover.Theme") as? Data
|
||||
|
||||
if #available(OSX 10.10, *) {
|
||||
self.themeField.placeholderString = kNotAvailable.locale
|
||||
self.themeBox.placeholderString = "NVRAM"
|
||||
}
|
||||
self.themeField.stringValue = (nvdata != nil) ? String(decoding: nvdata!, as: UTF8.self) : ""
|
||||
self.themeField.cell?.representedObject = self.themeField.stringValue
|
||||
self.themeBox.stringValue = (nvdata != nil) ? String(decoding: nvdata!, as: UTF8.self) : ""
|
||||
self.themeBox.cell?.representedObject = self.themeBox.stringValue
|
||||
|
||||
nvdata = nvram?.object(forKey: "Clover.RootRW") as? Data
|
||||
var value : String = String(decoding: nvdata ?? Data(), as: UTF8.self)
|
||||
@ -487,6 +518,52 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSessionD
|
||||
}
|
||||
|
||||
// MARK: Controls actions
|
||||
@IBAction func openThemeManager(_ sender: NSButton!) {
|
||||
DispatchQueue.main.async {
|
||||
if #available(OSX 10.11, *) {
|
||||
if (AppSD.themeManagerWC == nil) {
|
||||
AppSD.themeManagerWC = ThemeManagerWC.loadFromNib()
|
||||
}
|
||||
|
||||
AppSD.themeManagerWC?.showWindow(self)
|
||||
} else {
|
||||
if (AppSD.themeManagerWC == nil) {
|
||||
AppSD.themeManagerWC = ThemeManagerWC.loadFromNib()
|
||||
}
|
||||
|
||||
AppSD.themeManagerWC?.showWindow(self)
|
||||
}
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
}
|
||||
|
||||
func controlTextDidEndEditing(_ obj: Notification) {
|
||||
var updateThemeRepo = false
|
||||
if let field = obj.object as? NSTextField {
|
||||
|
||||
if field == self.themeUserField {
|
||||
if field.stringValue.count > 0 {
|
||||
UDs.set(field.stringValue, forKey: kThemeUserKey)
|
||||
} else {
|
||||
UDs.set(kDefaultThemeUser, forKey: kThemeUserKey)
|
||||
}
|
||||
updateThemeRepo = true
|
||||
} else if field == self.themeRepoField {
|
||||
if field.stringValue.count > 0 {
|
||||
UDs.set(field.stringValue, forKey: kThemeRepoKey)
|
||||
} else {
|
||||
UDs.set(kDefaultThemeRepo, forKey: kThemeRepoKey)
|
||||
}
|
||||
updateThemeRepo = true
|
||||
}
|
||||
}
|
||||
|
||||
if updateThemeRepo {
|
||||
AppSD.themeUser = UDs.string(forKey: kThemeUserKey) ?? kDefaultThemeUser
|
||||
AppSD.themeRepo = UDs.string(forKey: kThemeRepoKey) ?? kDefaultThemeRepo
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func installClover(_ sender: NSButton!) {
|
||||
|
||||
let myPath = Bundle.main.bundlePath.lowercased()
|
||||
@ -635,28 +712,30 @@ class SettingsViewController: NSViewController, NSTextFieldDelegate, URLSessionD
|
||||
}
|
||||
|
||||
// MARK: NVRAM editing
|
||||
func controlTextDidEndEditing(_ obj: Notification) {
|
||||
if let field = obj.object as? NSTextField {
|
||||
let delete : Bool = field.stringValue.count == 0
|
||||
if field == self.themeField {
|
||||
if let old = field.cell?.representedObject as? String {
|
||||
let key = "Clover.Theme"
|
||||
if old != field.stringValue {
|
||||
if delete {
|
||||
deleteNVRAM(key: key)
|
||||
} else {
|
||||
setNVRAM(key: key, stringValue: field.stringValue/*, error: &error*/)
|
||||
}
|
||||
|
||||
let nvdata = getNVRAM()?.object(forKey: key) as? Data
|
||||
field.stringValue = (nvdata != nil) ? String(decoding: nvdata!, as: UTF8.self) : ""
|
||||
field.cell?.representedObject = field.stringValue
|
||||
}
|
||||
@IBAction func themeBoxSelected(_ sender: NSComboBox!) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
|
||||
var value = ""
|
||||
if let nvram = getNVRAM() {
|
||||
let nvdata = nvram.object(forKey: key) as? Data
|
||||
value = String(decoding: nvdata ?? Data(), as: UTF8.self)
|
||||
}
|
||||
sender.stringValue = value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IBAction func soundSliderDidMove(_ sender: SoundSlider!) {
|
||||
sender.field?.stringValue = "\(Int(sender.doubleValue))%"
|
||||
|
||||
@ -1139,13 +1218,7 @@ class SettingsWindowController: NSWindowController, NSWindowDelegate {
|
||||
self.viewController = newValue
|
||||
}
|
||||
}
|
||||
/*
|
||||
class func loadFromStoryBoard() -> SettingsWindowController {
|
||||
let wc = NSStoryboard(name: "Settings",
|
||||
bundle: nil).instantiateController(withIdentifier: "SettingsWindowController") as! SettingsWindowController
|
||||
return wc
|
||||
}
|
||||
*/
|
||||
|
||||
class func loadFromNib() -> SettingsWindowController? {
|
||||
var topLevel: NSArray? = nil
|
||||
Bundle.main.loadNibNamed("Settings", owner: self, topLevelObjects: &topLevel)
|
||||
|
477
CloverApp/Clover/ThemeManager/ThemeManager.swift
Normal file
477
CloverApp/Clover/ThemeManager/ThemeManager.swift
Normal file
@ -0,0 +1,477 @@
|
||||
//
|
||||
// ThemeManager.swift
|
||||
// ThemeManager
|
||||
//
|
||||
// Created by vector sigma on 04/01/2020.
|
||||
// Copyright © 2020 vectorsigma. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
let kThemeUserKey = "themeUser"
|
||||
let kThemeRepoKey = "themeRepo"
|
||||
|
||||
let kDefaultThemeUser = "CloverHackyColor" // CloverHackyColor
|
||||
let kDefaultThemeRepo = "CloverThemes"
|
||||
|
||||
enum ThemeDownload {
|
||||
case indexOnly
|
||||
case thumbnail
|
||||
case complete
|
||||
}
|
||||
|
||||
let kCloverThemeAttributeKey = "org.cloverTheme.sha"
|
||||
|
||||
class ThemeManager: NSObject, URLSessionDataDelegate {
|
||||
var delegate : ThemeManagerVC?
|
||||
private var user : String
|
||||
private var repo : String
|
||||
var basePath : String
|
||||
private var urlBaseStr : String
|
||||
private var themeManagerIndexDir : String
|
||||
|
||||
let userAgent = "Clover"
|
||||
|
||||
required init(user: String, repo: String,
|
||||
basePath: String,
|
||||
indexDir : String,
|
||||
delegate: ThemeManagerVC?) {
|
||||
self.user = user
|
||||
self.repo = repo
|
||||
self.basePath = basePath
|
||||
self.urlBaseStr = "https://api.github.com/repos/\(user)/\(repo)/git/trees/master?recursive=1"
|
||||
self.themeManagerIndexDir = indexDir
|
||||
self.delegate = delegate
|
||||
if !fm.fileExists(atPath: self.themeManagerIndexDir) {
|
||||
try? fm.createDirectory(atPath: self.themeManagerIndexDir,
|
||||
withIntermediateDirectories: true,
|
||||
attributes: nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func getIndexedThemes() -> [String] {
|
||||
var themes = [String]()
|
||||
if self.getSha() != nil {
|
||||
let themesIndexPath = self.themeManagerIndexDir.addPath("Themes")
|
||||
if let files = try? fm.contentsOfDirectory(atPath: themesIndexPath) {
|
||||
for f in files {
|
||||
let theme = f.deletingFileExtension
|
||||
let ext = f.fileExtension
|
||||
if ext == "plist" {
|
||||
themes.append(theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return themes.sorted()
|
||||
}
|
||||
|
||||
public func getThemes(completion: @escaping ([String]) -> ()) {
|
||||
var themes = [String]()
|
||||
let themesIndexPath = self.themeManagerIndexDir.addPath("Themes")
|
||||
|
||||
self.getInfo(urlString: urlBaseStr) { (success) in
|
||||
|
||||
do {
|
||||
let files = try fm.contentsOfDirectory(atPath: themesIndexPath)
|
||||
for f in files {
|
||||
let theme = f.deletingFileExtension
|
||||
let ext = f.fileExtension
|
||||
if ext == "plist" {
|
||||
themes.append(theme)
|
||||
}
|
||||
}
|
||||
completion(themes)
|
||||
} catch {
|
||||
completion(themes)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the sha string only if the sha exist, and only if Themes directory exists.
|
||||
func getSha() -> String? {
|
||||
var sha : String? = nil
|
||||
let themesPath = "\(self.themeManagerIndexDir)/Themes"
|
||||
let shaPath = "\(self.themeManagerIndexDir)/sha"
|
||||
|
||||
if fm.fileExists(atPath: themesPath) && fm.fileExists(atPath: shaPath) {
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: shaPath)) {
|
||||
sha = String(data: data, encoding: .utf8)
|
||||
if ((sha != nil) && (sha!.count < 40)) {
|
||||
sha = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sha
|
||||
}
|
||||
|
||||
private func normalize(_ url: String) -> String {
|
||||
if (url.range(of: " ") != nil) {
|
||||
return url.replacingOccurrences(of: " ", with: "%20")
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
private func downloadloadFile(at url: String, dst: String,
|
||||
completion: @escaping (Bool) -> ()) {
|
||||
if let validURL = URL(string: self.normalize(url)) {
|
||||
let upperDir = dst.deletingLastPath
|
||||
if !fm.fileExists(atPath: upperDir) {
|
||||
do {
|
||||
try fm.createDirectory(atPath: upperDir,
|
||||
withIntermediateDirectories: true,
|
||||
attributes: nil)
|
||||
} catch {
|
||||
print("DF1, \(error)")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
var request = URLRequest(url: validURL)
|
||||
request.httpMethod = "GET"
|
||||
request.setValue(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 (e != nil) {
|
||||
print("DF2, \(e!)")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
guard let data = d else {
|
||||
print("DF3, Error: no datas.")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
do {
|
||||
try data.write(to: URL(fileURLWithPath: dst))
|
||||
completion(true)
|
||||
} catch {
|
||||
print("DF4, \(error)")
|
||||
completion(false)
|
||||
}
|
||||
})
|
||||
|
||||
task.resume()
|
||||
|
||||
} else {
|
||||
print("DF5, Error: invalid url '\(url)'.")
|
||||
completion(false)
|
||||
}
|
||||
}
|
||||
|
||||
private func getInfo(urlString: String, completion: @escaping (Bool) -> ()) {
|
||||
if let url = URL(string: self.normalize(urlString)) {
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue(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 (e != nil) {
|
||||
print("GI1, \(e!)")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
guard let data = d else {
|
||||
print("GI2, no data.")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
guard let utf8 = String(decoding: data, as: UTF8.self).data(using: .utf8) else {
|
||||
print("GI3, data is not utf8.")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let json = try JSONSerialization.jsonObject(with: utf8, options: .mutableContainers)
|
||||
|
||||
if let jdict = json as? [String: Any] {
|
||||
guard let truncated = jdict["truncated"] as? Bool else {
|
||||
print("GI4, Error: 'truncated' key not found")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
if truncated == true {
|
||||
print("GI4, Error: json has truncated list.")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
guard let sha = jdict["sha"] as? String else {
|
||||
print("GI4, Error: 'sha' key not found")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
if sha == self.getSha() {
|
||||
// we're done because we already have all the files needed
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
guard let tree = jdict["tree"] as? [[String: Any]] else {
|
||||
print("GI4, Error: 'tree' key not found, or not an array.")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
let shaPath = "\(self.themeManagerIndexDir)/\(sha)"
|
||||
|
||||
do {
|
||||
if !fm.fileExists(atPath: shaPath) {
|
||||
try fm.createDirectory(atPath: shaPath, withIntermediateDirectories: true, attributes: nil)
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("GI5, Error: can't write sha commit.")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
for obj in tree {
|
||||
/*
|
||||
"path": ".gitignore",
|
||||
"mode": "100644",
|
||||
"type": "blob",
|
||||
"sha": "e43b0f988953ae3a84b00331d0ccf5f7d51cb3cf",
|
||||
"size": 10,
|
||||
"url": "https://api.github.com/repos/vectorsigma72/CloverThemes/git/blobs/e43b0f988953ae3a84b00331d0ccf5f7d51cb3cf"
|
||||
*/
|
||||
|
||||
guard let type = obj["type"] as? String else {
|
||||
print("GI6, Error: 'type' key not found")
|
||||
completion(false)
|
||||
break
|
||||
}
|
||||
|
||||
if let path = obj["path"] as? String {
|
||||
|
||||
if !path.hasPrefix(".") && type == "blob" { // .gitignore, .DS_Store
|
||||
let themeName = path.components(separatedBy: "/")[0]
|
||||
let plistPath = "\(self.themeManagerIndexDir)/\(sha)/\(themeName).plist"
|
||||
let theme = NSMutableArray(contentsOfFile: plistPath) ?? NSMutableArray()
|
||||
if !theme.contains(path) {
|
||||
theme.add(path)
|
||||
}
|
||||
|
||||
|
||||
if !theme.write(toFile: plistPath, atomically: false) {
|
||||
print("GI7, Error: 'path' key not found.")
|
||||
completion(false)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("GI8, Error: 'path' key not found.")
|
||||
completion(false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// move temp files in standard position
|
||||
do {
|
||||
try sha.write(toFile: "\(self.themeManagerIndexDir)/sha", atomically: true, encoding: .utf8)
|
||||
if fm.fileExists(atPath: "\(self.themeManagerIndexDir)/Themes") {
|
||||
try fm.removeItem(atPath: "\(self.themeManagerIndexDir)/Themes")
|
||||
}
|
||||
try fm.moveItem(atPath: shaPath, toPath: "\(self.themeManagerIndexDir)/Themes")
|
||||
self.removeOld(new: sha)
|
||||
completion(true)
|
||||
} catch {
|
||||
print("GI9, \(error)")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
//remove old themes (old sha)
|
||||
} else {
|
||||
print("GI10, json is not a dictionary (API change?).")
|
||||
completion(false)
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("GI11, \(error)")
|
||||
completion(false)
|
||||
}
|
||||
})
|
||||
|
||||
task.resume()
|
||||
} else {
|
||||
print("GI12, \(urlString) is invalid.")
|
||||
completion(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove old thumbnails from old sha commits
|
||||
private func removeOld(new sha: String) {
|
||||
do {
|
||||
let contents : [String] = try fm.contentsOfDirectory(atPath: self.themeManagerIndexDir)
|
||||
for c in contents {
|
||||
if c != "sha" && c != "Themes" && c != sha {
|
||||
try fm.removeItem(atPath: "\(self.themeManagerIndexDir)/\(c)")
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
|
||||
/// Return the path for a given theme, if the download succeded
|
||||
public func download(theme: String, down: ThemeDownload, completion: @escaping (String?) -> ()) {
|
||||
if let sha = getSha() {
|
||||
let shaPath = self.basePath.addPath(sha)
|
||||
let themeDest = (down == .complete)
|
||||
? self.themeManagerIndexDir.addPath("Downloads").addPath(theme)
|
||||
: shaPath.addPath(theme)
|
||||
|
||||
if (down != .complete) && fm.fileExists(atPath: themeDest) {
|
||||
completion(themeDest)
|
||||
} else {
|
||||
if !fm.fileExists(atPath: themeDest) {
|
||||
do {
|
||||
try fm.createDirectory(atPath: themeDest,
|
||||
withIntermediateDirectories: true,
|
||||
attributes: nil)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
let plistPath = "\(themeManagerIndexDir)/Themes/\(theme).plist"
|
||||
|
||||
if let files = NSArray(contentsOfFile: plistPath) as? [String] {
|
||||
let fc = files.count
|
||||
if fc > 0 {
|
||||
var broken = false
|
||||
let dg = DispatchGroup()
|
||||
for i in 0..<fc {
|
||||
dg.enter()
|
||||
if broken {
|
||||
dg.leave()
|
||||
break
|
||||
} else {
|
||||
let file = files[i]
|
||||
// build the url
|
||||
let furl = "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 filedest = (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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dg.notify(queue: .main) {
|
||||
if broken {
|
||||
completion(nil)
|
||||
} else {
|
||||
completion(themeDest)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public func getImageUrl(for theme: String, completion: @escaping (String?) -> ()) {
|
||||
/*
|
||||
We are going to load an image in a web view,
|
||||
so no matter if the url will be a file on the local
|
||||
filesystem (downloaded theme) or just on the online repository.
|
||||
*/
|
||||
if let sha = self.getSha() {
|
||||
let localTheme = "\(basePath)/\(sha)/\(theme)"
|
||||
let png = "\(localTheme)/screenshot.png"
|
||||
let svg = "\(localTheme)/theme.svg"
|
||||
if fm.fileExists(atPath: png) {
|
||||
completion(png)
|
||||
return
|
||||
} else if fm.fileExists(atPath: svg) {
|
||||
completion(svg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// theme not found?? Downloading...
|
||||
self.download(theme: theme, down: .thumbnail) { (path) in
|
||||
if let localTheme = path {
|
||||
let png = "\(localTheme)/screenshot.png"
|
||||
let svg = "\(localTheme)/theme.svg"
|
||||
if fm.fileExists(atPath: png) {
|
||||
completion(png)
|
||||
} else if fm.fileExists(atPath: svg) {
|
||||
completion(svg)
|
||||
}
|
||||
} else {
|
||||
completion(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func signTheme(at path: String) {
|
||||
if let sha = getSha() {
|
||||
let fileURL = URL(fileURLWithPath: path)
|
||||
let data = sha.data(using: .utf8)
|
||||
|
||||
// remove all attributes
|
||||
do {
|
||||
let list = try fileURL.listExtendedAttributes()
|
||||
for attr in list {
|
||||
try fileURL.removeExtendedAttribute(forName: attr)
|
||||
}
|
||||
} catch {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
|
||||
// set attribute
|
||||
do {
|
||||
try fileURL.setExtendedAttribute(data: data!, forName: kCloverThemeAttributeKey)
|
||||
/* nine test
|
||||
let adata = try fileURL.extendedAttribute(forName: attr)
|
||||
print(String(data: adata, encoding: .utf8)!)
|
||||
*/
|
||||
} catch let error {
|
||||
print(error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func exist(theme: String) -> Bool {
|
||||
return fm.fileExists(atPath: "\(self.themeManagerIndexDir)/Themes/\(theme).plist")
|
||||
}
|
||||
|
||||
}
|
217
CloverApp/Clover/ThemeManager/ThemeManager.xib
Normal file
217
CloverApp/Clover/ThemeManager/ThemeManager.xib
Normal file
@ -0,0 +1,217 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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="15702"/>
|
||||
<plugIn identifier="com.apple.WebKitIBPlugin" version="15702"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner"/>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Themes" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="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="196" y="240" width="751" height="486"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1600" height="877"/>
|
||||
<value key="minSize" type="size" width="751" height="486"/>
|
||||
<value key="maxSize" type="size" width="751" height="486"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ" customClass="GradientView" customModule="Clover" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="751" height="486"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="76" horizontalPageScroll="10" verticalLineScroll="76" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="r0K-Ss-e4a">
|
||||
<rect key="frame" x="5" y="20" width="151" height="461"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" copiesOnScroll="NO" id="YEz-5q-kQJ">
|
||||
<rect key="frame" x="1" y="1" width="149" height="459"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="73" viewBased="YES" id="ydq-wg-aj2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="149" height="459"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="2" height="3"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="column1" width="147" minWidth="40" maxWidth="1000" id="SqG-sb-peF">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<font key="font" metaFont="controlContent" 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="XIw-Cc-NAw">
|
||||
<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="IDM-eI-bga">
|
||||
<rect key="frame" x="1" y="1" width="147" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vOd-E8-08W">
|
||||
<rect key="frame" x="0.0" y="0.0" width="147" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="zkl-n1-vr4">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<connections>
|
||||
<outlet property="textField" destination="vOd-E8-08W" id="AAe-gI-75c"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<nil key="backgroundColor"/>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="YES" id="Hkt-fv-NXX">
|
||||
<rect key="frame" x="1" y="119" width="223" height="11"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="NO" id="3dn-D5-Whz">
|
||||
<rect key="frame" x="224" y="17" width="11" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Q5A-Np-Gc4">
|
||||
<rect key="frame" x="162" y="74" width="579" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" title="Description:" id="VVK-qM-R76">
|
||||
<font key="font" metaFont="controlContent" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="ClZ-60-myJ" id="zuI-fa-6ch"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="BMd-hN-0ay">
|
||||
<rect key="frame" x="162" y="51" width="579" height="15"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" title="Author:" id="nVY-gj-ELR">
|
||||
<font key="font" metaFont="controlContent" size="11"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="ClZ-60-myJ" id="Jzj-85-Zvh"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<comboBox focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Be-Yl-lqT">
|
||||
<rect key="frame" x="164" y="97" width="201" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" focusRingType="none" placeholderString="Theme" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="ZdF-kp-eIO">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<objectValues>
|
||||
<string>Item 1</string>
|
||||
<string>Item 2</string>
|
||||
<string>Item 3</string>
|
||||
</objectValues>
|
||||
</comboBoxCell>
|
||||
<connections>
|
||||
<action selector="nameBoxSelected:" target="ClZ-60-myJ" id="rPH-oQ-4RO"/>
|
||||
<outlet property="delegate" destination="ClZ-60-myJ" id="nX1-Jt-4WY"/>
|
||||
</connections>
|
||||
</comboBox>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="11W-vh-mqt">
|
||||
<rect key="frame" x="576" y="13" width="169" height="32"/>
|
||||
<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="hSu-fW-idj">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="InstallPressed:" target="ClZ-60-myJ" id="Phd-pp-aYa"/>
|
||||
</connections>
|
||||
</button>
|
||||
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bLz-we-3A0" customClass="FWPopUpButton" customModule="Clover" customModuleProvider="target">
|
||||
<rect key="frame" x="162" y="123" width="580" 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="cP4-wD-4Ph" id="MEO-zC-Iak">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<menu key="menu" id="fEx-Zt-ngB">
|
||||
<items>
|
||||
<menuItem title="Item 1" state="on" id="cP4-wD-4Ph"/>
|
||||
<menuItem title="Item 2" id="4tT-M5-qZS"/>
|
||||
<menuItem title="Item 3" id="5hd-qb-ZN5"/>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
<action selector="targetPopPressed:" target="ClZ-60-myJ" id="cA6-3I-hDI"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<progressIndicator wantsLayer="YES" fixedFrame="YES" maxValue="100" displayedWhenStopped="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="tiZ-sK-mNx">
|
||||
<rect key="frame" x="558" y="23" width="16" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</progressIndicator>
|
||||
<webView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7R3-UE-Q3H">
|
||||
<rect key="frame" x="164" y="155" width="575" height="326"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<webPreferences key="preferences" defaultFontSize="16" defaultFixedFontSize="13" minimumFontSize="0" javaEnabled="NO" javaScriptCanOpenWindowsAutomatically="NO">
|
||||
<nil key="identifier"/>
|
||||
</webPreferences>
|
||||
</webView>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qNn-1b-hdX">
|
||||
<rect key="frame" x="396" y="102" width="345" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="check" title="Show installed" bezelStyle="regularSquare" imagePosition="left" inset="2" id="PPl-2s-7pe">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="showInstalledThemes:" target="ClZ-60-myJ" id="mYF-2e-FAu"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KDK-ig-T4V">
|
||||
<rect key="frame" x="387" y="13" width="169" height="32"/>
|
||||
<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="Nxb-Kj-kDi">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="unInstallPressed:" target="ClZ-60-myJ" id="tVl-6l-eul"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Rhe-Oy-I6J" id="Zlh-Ne-N7V"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="171.5" y="236"/>
|
||||
</window>
|
||||
<customObject id="Rhe-Oy-I6J" customClass="ThemeManagerWC" customModule="Clover" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="window" destination="QvC-M9-y7g" id="lZL-g6-Xhg"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<viewController id="ClZ-60-myJ" customClass="ThemeManagerVC" customModule="Clover" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="authorField" destination="BMd-hN-0ay" id="hW8-ad-MeQ"/>
|
||||
<outlet property="infoField" destination="Q5A-Np-Gc4" id="A6G-u3-jDk"/>
|
||||
<outlet property="installButton" destination="11W-vh-mqt" id="uVA-cy-LtI"/>
|
||||
<outlet property="installedThemesCheckBox" destination="qNn-1b-hdX" id="7cy-kq-gen"/>
|
||||
<outlet property="nameBox" destination="1Be-Yl-lqT" id="STE-Aj-xum"/>
|
||||
<outlet property="sidebar" destination="ydq-wg-aj2" id="dMd-2Y-AFt"/>
|
||||
<outlet property="spinner" destination="tiZ-sK-mNx" id="jY2-rJ-ECs"/>
|
||||
<outlet property="targetPop" destination="bLz-we-3A0" id="EcP-hC-Wbm"/>
|
||||
<outlet property="unistallButton" destination="KDK-ig-T4V" id="XVD-ko-iT8"/>
|
||||
<outlet property="view" destination="EiT-Mj-1SZ" id="rmo-5n-mHh"/>
|
||||
<outlet property="webView" destination="7R3-UE-Q3H" id="nTk-DN-ARl"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
</objects>
|
||||
</document>
|
738
CloverApp/Clover/ThemeManager/ThemeManagerVC.swift
Normal file
738
CloverApp/Clover/ThemeManager/ThemeManagerVC.swift
Normal file
@ -0,0 +1,738 @@
|
||||
//
|
||||
// ThemeManagerVC.swift
|
||||
// ThemeManager
|
||||
//
|
||||
// Created by vector sigma on 07/01/2020.
|
||||
// Copyright © 2020 vectorsigma. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import WebKit
|
||||
|
||||
class GradientView : NSView {
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
/*
|
||||
let up = NSColor(red: 48 / 255, green: 35 / 255, blue: 255 / 255, alpha: 0.5)
|
||||
let down = NSColor(red: 150 / 255, green: 158 / 255, blue: 215 / 255, alpha: 0.5)
|
||||
let grad = NSGradient(colors: [down, up])
|
||||
grad?.draw(in: dirtyRect, angle: 45)*/
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeManagerVC: NSViewController,
|
||||
NSTableViewDelegate, NSTableViewDataSource, WebFrameLoadDelegate, WebUIDelegate {
|
||||
var targetVolume : String? = nil
|
||||
|
||||
var manager : ThemeManager?
|
||||
@IBOutlet var installedThemesCheckBox : NSButton!
|
||||
|
||||
@IBOutlet var webView : WebView!
|
||||
@IBOutlet var sidebar : NSTableView!
|
||||
@IBOutlet var nameBox : NSComboBox!
|
||||
@IBOutlet var authorField : NSTextField!
|
||||
@IBOutlet var infoField : NSTextField!
|
||||
|
||||
@IBOutlet var targetPop : FWPopUpButton!
|
||||
@IBOutlet var spinner : NSProgressIndicator!
|
||||
@IBOutlet var installButton : NSButton!
|
||||
@IBOutlet var unistallButton : NSButton!
|
||||
|
||||
var loaded : Bool = false
|
||||
var showInstalled : Bool = false
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
if !self.loaded {
|
||||
if #available(OSX 10.10, *) {} else {
|
||||
self.viewDidLoad()
|
||||
}
|
||||
self.loaded = true
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
if #available(OSX 10.10, *) {
|
||||
super.viewDidLoad()
|
||||
}
|
||||
self.loaded = true
|
||||
|
||||
/*
|
||||
let winImagePath = "/Library/Desktop Pictures/Ink Cloud.jpg"
|
||||
|
||||
if fm.fileExists(atPath: winImagePath) {
|
||||
let winImage = NSImage(byReferencingFile: winImagePath)
|
||||
let winImageSize = NSMakeSize(self.view.frame.width, self.view.frame.height)
|
||||
winImage?.size = winImageSize
|
||||
self.view.window?.backgroundColor = NSColor.init(patternImage: winImage!)
|
||||
}*/
|
||||
|
||||
self.view.window?.title = self.view.window!.title.locale
|
||||
let settingVC = AppSD.settingsWC?.contentViewController as? SettingsViewController
|
||||
settingVC?.themeUserField.isEnabled = false
|
||||
settingVC?.themeRepoField.isEnabled = false
|
||||
self.webView.drawsBackground = false
|
||||
self.webView.uiDelegate = self
|
||||
|
||||
self.webView.frameLoadDelegate = self
|
||||
self.webView.mainFrame.frameView.allowsScrolling = false
|
||||
|
||||
|
||||
localize(view: self.view)
|
||||
AppSD.isInstallerOpen = true
|
||||
settingVC?.disksPopUp.isEnabled = false
|
||||
settingVC?.updateCloverButton.isEnabled = false
|
||||
settingVC?.unmountButton.isEnabled = false
|
||||
|
||||
AppSD.themes.removeAll()
|
||||
self.nameBox.completes = true
|
||||
|
||||
self.nameBox.removeAllItems()
|
||||
self.sidebar.delegate = self
|
||||
self.sidebar.dataSource = self
|
||||
self.sidebar.backgroundColor = NSColor.clear
|
||||
self.sidebar.enclosingScrollView?.contentView.drawsBackground = false
|
||||
|
||||
if #available(OSX 10.10, *) {
|
||||
self.sidebar.usesStaticContents = true
|
||||
}
|
||||
|
||||
self.targetPop.removeAllItems()
|
||||
self.populateTargets()
|
||||
|
||||
if let bootDevice = findBootPartitionDevice() {
|
||||
if let mountPoint = getMountPoint(from: bootDevice) {
|
||||
for i in self.targetPop.itemArray {
|
||||
if let target = i.representedObject as? String {
|
||||
if bootDevice == target {
|
||||
self.targetPop.select(i)
|
||||
self.targetVolume = mountPoint
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.showIndexing()
|
||||
//return
|
||||
let themeManagerIndexDir = NSHomeDirectory().addPath("Library/Application Support/CloverApp/Themeindex/\(AppSD.themeUser)_\(AppSD.themeRepo)")
|
||||
|
||||
self.manager = ThemeManager(user: AppSD.themeUser,
|
||||
repo: AppSD.themeRepo,
|
||||
basePath: themeManagerIndexDir,
|
||||
indexDir: themeManagerIndexDir,
|
||||
delegate: self)
|
||||
|
||||
/*
|
||||
1) immediately load indexed themes (if any).
|
||||
Fast if thumbnails already exists.
|
||||
*/
|
||||
|
||||
for t in self.manager!.getIndexedThemes() {
|
||||
self.add(theme: t)
|
||||
}
|
||||
|
||||
/*
|
||||
2) download thumbnails.
|
||||
Slower if thumbnails didn't exist or themes never indexed.
|
||||
*/
|
||||
|
||||
self.manager?.getThemes { (t) in
|
||||
let sorted = t.sorted()
|
||||
for t in sorted {
|
||||
DispatchQueue.main.async {
|
||||
self.add(theme: t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dataSource() -> [String] {
|
||||
if showInstalled {
|
||||
return AppSD.installedThemes
|
||||
}
|
||||
return AppSD.themes
|
||||
}
|
||||
|
||||
@IBAction func showInstalledThemes(_ sender: NSButton!) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
let copy = self.manager!.getIndexedThemes().sorted() // in cases of low downloads
|
||||
self.nameBox.removeAllItems()
|
||||
self.sidebar.noteNumberOfRowsChanged()
|
||||
AppSD.installedThemes.removeAll()
|
||||
AppSD.themes.removeAll()
|
||||
|
||||
if sender.state == .on {
|
||||
if (self.targetVolume != nil && fm.fileExists(atPath: self.targetVolume!)) {
|
||||
let themeDir = self.targetVolume!.addPath("EFI/CLOVER/themes")
|
||||
var installed = [String]()
|
||||
for theme in copy {
|
||||
if fm.fileExists(atPath: themeDir.addPath(theme)) {
|
||||
installed.append(theme)
|
||||
}
|
||||
}
|
||||
|
||||
if fm.fileExists(atPath: themeDir) {
|
||||
do {
|
||||
let userTheme = try fm.contentsOfDirectory(atPath: themeDir)
|
||||
for theme in userTheme {
|
||||
if (fm.fileExists(atPath: themeDir.addPath(theme).addPath("theme.plist"))
|
||||
&& fm.fileExists(atPath: themeDir.addPath(theme).addPath("screenshot.png"))) {
|
||||
if !installed.contains(theme) {
|
||||
installed.append(theme)
|
||||
}
|
||||
} else if fm.fileExists(atPath: themeDir.addPath(theme).addPath("theme.svg")) {
|
||||
if !installed.contains(theme) {
|
||||
installed.append(theme)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
AppSD.installedThemes = installed.sorted()
|
||||
self.nameBox.addItems(withObjectValues: installed.sorted())
|
||||
if installed.count == 0 {
|
||||
self.showNoThemes()
|
||||
}
|
||||
}
|
||||
self.showInstalled = true
|
||||
} else {
|
||||
AppSD.themes = copy
|
||||
self.nameBox.addItems(withObjectValues: copy.sorted())
|
||||
self.showInstalled = false
|
||||
if copy.count == 0 {
|
||||
self.showNoThemes()
|
||||
}
|
||||
}
|
||||
self.sidebar.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
func showNoThemes() {
|
||||
self.webView.mainFrame.loadHTMLString(
|
||||
"""
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.center {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
border: 3px solid gray;
|
||||
}
|
||||
|
||||
.center p {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-ms-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="center">
|
||||
<p>\("No themes found".locale)</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""",
|
||||
baseURL: Bundle.main.bundleURL)
|
||||
}
|
||||
|
||||
func showIndexing() {
|
||||
// https://www.w3schools.com/howto/howto_css_loader.asp
|
||||
self.webView.mainFrame.loadHTMLString("""
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial- scale=1">
|
||||
<style>
|
||||
.loader {
|
||||
position: absolute;
|
||||
left: 30%;
|
||||
top: 25%;
|
||||
border: 20px solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
border-top: 16px solid white;
|
||||
border-right: 16px solid gray;
|
||||
border-bottom: 16px solid white;
|
||||
border-left: 16px solid gray;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
-webkit-animation: spin 2s linear infinite;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
.blinking{
|
||||
animation:blinkingText 1.2s infinite;
|
||||
}
|
||||
|
||||
@keyframes blinkingText{
|
||||
0%{ color: gray; }
|
||||
49%{ color: gray; }
|
||||
60%{ color: transparent; }
|
||||
99%{ color:transparent; }
|
||||
100%{ color: gray; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2><span class="blinking">Indexing..</span></h2>
|
||||
|
||||
<div class="loader"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
""", baseURL: Bundle.main.bundleURL)
|
||||
}
|
||||
|
||||
@IBAction func unInstallPressed(_ sender: NSButton!) {
|
||||
if AppSD.isInstalling {
|
||||
NSSound.beep()
|
||||
return
|
||||
}
|
||||
if self.targetPop.indexOfSelectedItem == 0
|
||||
|| self.targetVolume == nil
|
||||
|| (self.targetVolume != nil && !fm.fileExists(atPath: self.targetVolume!)) {
|
||||
NSSound.beep()
|
||||
return // this should not happen
|
||||
}
|
||||
|
||||
let theme = self.nameBox.stringValue
|
||||
let themePath = self.targetVolume!.addPath("EFI/CLOVER/themes").addPath(theme)
|
||||
|
||||
let sr = self.sidebar.selectedRow
|
||||
if sr >= 0 && sr < self.dataSource().count {
|
||||
self.spinner.startAnimation(nil)
|
||||
do {
|
||||
if fm.fileExists(atPath: themePath) {
|
||||
try fm.removeItem(atPath: themePath)
|
||||
self.unistallButton.isEnabled = false
|
||||
self.unistallButton.animator().isHidden = true
|
||||
NSSound(contentsOfFile: "/System/Library/Components/CoreAudio.component/Contents/SharedSupport/SystemSounds/finder/empty trash.aif", byReference: false)?.play()
|
||||
}
|
||||
self.spinner.stopAnimation(nil)
|
||||
self.showInstalledThemes(self.installedThemesCheckBox)
|
||||
} catch {
|
||||
NSSound(named: "Basso")?.play()
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Can't remove the theme".locale
|
||||
alert.informativeText = "\(error.localizedDescription)"
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.runModal()
|
||||
self.spinner.stopAnimation(nil)
|
||||
self.showInstalledThemes(self.installedThemesCheckBox)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func InstallPressed(_ sender: NSButton!) {
|
||||
if AppSD.isInstalling {
|
||||
NSSound.beep()
|
||||
return
|
||||
}
|
||||
|
||||
if self.targetPop.indexOfSelectedItem == 0
|
||||
|| self.targetVolume == nil
|
||||
|| (self.targetVolume != nil && !fm.fileExists(atPath: self.targetVolume!)) {
|
||||
NSSound.beep()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||
self.targetPop.performClick(self.targetPop)
|
||||
}
|
||||
return
|
||||
}
|
||||
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.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 {
|
||||
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 {
|
||||
AppSD.isInstalling = false
|
||||
self.spinner.stopAnimation(nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func addRow(for theme: String) {
|
||||
let index = AppSD.themes.count
|
||||
AppSD.themes.append(theme)
|
||||
//if self.installedThemesCheckBox.state == .off {
|
||||
self.sidebar.beginUpdates()
|
||||
self.sidebar.insertRows(at: IndexSet(integer: index), withAnimation: .slideUp)
|
||||
self.sidebar.endUpdates()
|
||||
//}
|
||||
self.nameBox.addItem(withObjectValue: theme)
|
||||
}
|
||||
|
||||
func add(theme: String) {
|
||||
if AppSD.themes.contains(theme) {
|
||||
return
|
||||
}
|
||||
if let sha = self.manager?.getSha() {
|
||||
let themePath = self.manager!.basePath.addPath(sha).addPath(theme)
|
||||
if fm.fileExists(atPath: themePath.addPath("theme.svg")) {
|
||||
self.addRow(for: theme)
|
||||
} else if (fm.fileExists(atPath: themePath.addPath("screenshot.png"))
|
||||
&& fm.fileExists(atPath: themePath.addPath("theme.plist"))) {
|
||||
self.addRow(for: theme)
|
||||
} else {
|
||||
self.manager?.download(theme: theme, down: .thumbnail, completion: { (path) in
|
||||
if path != nil {
|
||||
DispatchQueue.main.async {
|
||||
self.addRow(for: theme)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func nameBoxSelected(_ sender: NSComboBox!) {
|
||||
let theme = sender.stringValue
|
||||
if theme.count > 0 && self.dataSource().contains(theme) {
|
||||
let index = self.dataSource().firstIndex(of: theme)
|
||||
self.sidebar.selectRowIndexes(IndexSet(integer: index!), byExtendingSelection: false)
|
||||
self.sidebar.scrollRowToVisible(index!)
|
||||
} else {
|
||||
NSSound.beep()
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int) {
|
||||
if self.sidebar.selectedRow < 0 {
|
||||
self.sidebar.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false)
|
||||
}
|
||||
}
|
||||
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
return self.dataSource().count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
|
||||
return 82
|
||||
}
|
||||
|
||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
let sr = self.sidebar.selectedRow
|
||||
if sr >= 0 {
|
||||
if let v = self.sidebar.view(atColumn: 0, row: sr, makeIfNecessary: false) as? ThemeView {
|
||||
if v.isInstalled {
|
||||
self.unistallButton.animator().isHidden = false
|
||||
self.unistallButton.isEnabled = true
|
||||
} else {
|
||||
self.unistallButton.animator().isHidden = true
|
||||
self.unistallButton.isEnabled = false
|
||||
}
|
||||
|
||||
if (self.manager?.exist(theme: v.name))! {
|
||||
self.installButton.isEnabled = true
|
||||
} else {
|
||||
self.installButton.isEnabled = false
|
||||
}
|
||||
|
||||
guard let path = v.imagePath else {
|
||||
return
|
||||
}
|
||||
|
||||
self.nameBox.stringValue = v.name
|
||||
self.authorField.stringValue = v.author ?? ""
|
||||
self.infoField.stringValue = v.info ?? ""
|
||||
|
||||
self.webView.mainFrame.loadHTMLString("""
|
||||
<html>
|
||||
<body>
|
||||
<div align="center" style="position:relative; height: 100%; width: 100%; top:0;left 0;">
|
||||
<img src="\(URL(fileURLWithPath: path))" style="width: 100% ; max-width: 575px;top:0;"/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""", baseURL: Bundle.main.bundleURL)
|
||||
|
||||
if path.hasSuffix(".svg") {
|
||||
let svgStr = try? String(contentsOfFile: path)
|
||||
var author : String? = nil
|
||||
var version : String? = nil
|
||||
var description : String? = nil
|
||||
if let lines = svgStr?.components(separatedBy: .newlines) {
|
||||
/*
|
||||
Version="0.87"
|
||||
Year="2018-2019"
|
||||
Author="Blackosx"
|
||||
Description="Vector version of BGM (Based on Clovy theme file structure)"
|
||||
*/
|
||||
|
||||
for line in lines {
|
||||
if (line.range(of: "Version=\"") != nil) {
|
||||
version = line.components(separatedBy: "Version=\"")[1].components(separatedBy: "\"")[0]
|
||||
}
|
||||
}
|
||||
for line in lines {
|
||||
if (line.range(of: "Author=\"") != nil) {
|
||||
author = line.components(separatedBy: "Author=\"")[1].components(separatedBy: "\"")[0]
|
||||
}
|
||||
}
|
||||
for line in lines {
|
||||
if (line.range(of: "Description=\"") != nil) {
|
||||
description = line.components(separatedBy: "Description=\"")[1].components(separatedBy: "\"")[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.authorField.stringValue = author ?? ""
|
||||
self.infoField.stringValue = "\(description ?? "?"), v\(version ?? "?")"
|
||||
} else {
|
||||
// get theme.plist
|
||||
let plistPath = "\((path as NSString).deletingLastPathComponent)/theme.plist"
|
||||
let plist = NSDictionary(contentsOfFile: plistPath)
|
||||
self.authorField.stringValue = (plist?.object(forKey: "Author") as? String) ?? ""
|
||||
self.infoField.stringValue = (plist?.object(forKey: "Description") as? String) ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
if let identifier = tableColumn?.identifier {
|
||||
if identifier.rawValue == "column1" {
|
||||
let tv = ThemeView(manager: self.manager!,
|
||||
name: self.dataSource()[row],
|
||||
row: row)
|
||||
tv.manager.delegate = self
|
||||
return tv
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
|
||||
return ThemeTableRowView()
|
||||
}
|
||||
|
||||
// MARK: find suitable disks
|
||||
func populateTargets() {
|
||||
if AppSD.isInstalling {
|
||||
return
|
||||
}
|
||||
|
||||
let selected : String? = self.targetPop.selectedItem?.representedObject as? String
|
||||
self.targetPop.removeAllItems()
|
||||
self.targetPop.addItem(withTitle: "Select a disk..".locale)
|
||||
let disks : NSDictionary = getAlldisks()
|
||||
let bootPartition = findBootPartitionDevice()
|
||||
let diskSorted : [String] = (disks.allKeys as! [String]).sorted()
|
||||
for d in diskSorted {
|
||||
let disk : String = d
|
||||
if isWritable(diskOrMtp: disk) && !kBannedMedia.contains(getVolumeName(from: disk) ?? "") {
|
||||
let fs : String = getFS(from: disk)?.lowercased() ?? kNotAvailable.locale
|
||||
let psm : String = getPartitionSchemeMap(from: disk) ?? kNotAvailable.locale
|
||||
let name : String = getVolumeName(from: disk) ?? kNotAvailable.locale
|
||||
let mp : String = getMountPoint(from: disk) ?? kNotAvailable.locale
|
||||
let parentDiskName : String = getMediaName(from: getBSDParent(of: disk) ?? "") ?? kNotAvailable.locale
|
||||
|
||||
let supportedFS = ["msdos", "fat32", "exfat", "hfs"]
|
||||
|
||||
if supportedFS.contains(fs) {
|
||||
self.targetPop.addItem(withTitle: "\(disk)\t\(name), \("mount point".locale): \(mp), \(fs.uppercased()), \(psm): (\(parentDiskName))")
|
||||
self.targetPop.invalidateIntrinsicContentSize()
|
||||
// get the image
|
||||
if disk == bootPartition {
|
||||
let image : NSImage = NSImage(named: "NSApplicationIcon")!.copy() as! NSImage
|
||||
image.size = NSMakeSize(16, 16)
|
||||
self.targetPop.lastItem?.image = image
|
||||
} else if let image : NSImage = getIconFor(volume: disk) {
|
||||
image.size = NSMakeSize(16, 16)
|
||||
self.targetPop.lastItem?.image = image
|
||||
}
|
||||
self.targetPop.lastItem?.representedObject = disk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selected != nil) {
|
||||
for item in self.targetPop.itemArray {
|
||||
if let d = item.representedObject as? String {
|
||||
if selected == d {
|
||||
self.targetPop.select(item)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func targetPopPressed(_ sender: FWPopUpButton!) {
|
||||
if let disk = sender?.selectedItem?.representedObject as? String {
|
||||
if !isMountPoint(path: disk) {
|
||||
//DispatchQueue.global(qos: .background).async {
|
||||
let cmd = "diskutil mount \(disk)"
|
||||
let msg = String(format: "Clover wants to mount %@", disk)
|
||||
let script = "do shell script \"\(cmd)\" with prompt \"\(msg)\" with administrator privileges"
|
||||
|
||||
let task = Process()
|
||||
|
||||
if #available(OSX 10.12, *) {
|
||||
task.launchPath = "/usr/bin/osascript"
|
||||
task.arguments = ["-e", script]
|
||||
} else {
|
||||
task.launchPath = "/usr/sbin/diskutil"
|
||||
task.arguments = ["mount", disk]
|
||||
}
|
||||
|
||||
task.terminationHandler = { t in
|
||||
if t.terminationStatus == 0 {
|
||||
if isMountPoint(path: disk) {
|
||||
self.targetVolume = getMountPoint(from: disk)
|
||||
DispatchQueue.main.async {
|
||||
self.populateTargets()
|
||||
self.showInstalledThemes(self.installedThemesCheckBox)
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.async { self.view.window?.makeKeyAndOrderFront(nil) }
|
||||
} else {
|
||||
NSSound.beep()
|
||||
self.showInstalledThemes(self.installedThemesCheckBox)
|
||||
}
|
||||
}
|
||||
task.launch()
|
||||
//}
|
||||
} else {
|
||||
self.targetVolume = getMountPoint(from: disk)
|
||||
self.populateTargets()
|
||||
self.showInstalledThemes(self.installedThemesCheckBox)
|
||||
}
|
||||
} else {
|
||||
self.targetVolume = nil
|
||||
self.unistallButton.isEnabled = false
|
||||
self.unistallButton.isHidden = true
|
||||
self.showInstalledThemes(self.installedThemesCheckBox)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: ThemeManager Window controller
|
||||
class ThemeManagerWC: NSWindowController, NSWindowDelegate {
|
||||
var viewController : NSViewController? = nil
|
||||
override var contentViewController: NSViewController? {
|
||||
get {
|
||||
self.viewController
|
||||
}
|
||||
set {
|
||||
self.viewController = newValue
|
||||
}
|
||||
}
|
||||
|
||||
class func loadFromNib() -> ThemeManagerWC? {
|
||||
var topLevel: NSArray? = nil
|
||||
Bundle.main.loadNibNamed("ThemeManager", owner: self, topLevelObjects: &topLevel)
|
||||
if (topLevel != nil) {
|
||||
var wc : ThemeManagerWC? = nil
|
||||
for o in topLevel! {
|
||||
if o is ThemeManagerWC {
|
||||
wc = o as? ThemeManagerWC
|
||||
}
|
||||
}
|
||||
|
||||
for o in topLevel! {
|
||||
if o is ThemeManagerVC {
|
||||
wc?.contentViewController = o as! ThemeManagerVC
|
||||
}
|
||||
}
|
||||
return wc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func windowShouldClose(_ sender: NSWindow) -> Bool {
|
||||
if AppSD.isInstalling {
|
||||
return false
|
||||
}
|
||||
let settingVC = AppSD.settingsWC?.contentViewController as? SettingsViewController
|
||||
settingVC?.disksPopUp.isEnabled = true
|
||||
settingVC?.updateCloverButton.isEnabled = true
|
||||
settingVC?.searchESPDisks()
|
||||
AppSD.isInstallerOpen = false
|
||||
settingVC?.themeUserField.isEnabled = true
|
||||
settingVC?.themeRepoField.isEnabled = true
|
||||
self.window = nil
|
||||
self.close()
|
||||
AppSD.themeManagerWC = nil // remove a strong reference
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class ThemeTableRowView: NSTableRowView {
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
self.wantsLayer = true
|
||||
self.layer?.backgroundColor = NSColor.clear.cgColor
|
||||
}
|
||||
|
||||
override var isEmphasized: Bool {
|
||||
set {}
|
||||
get {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override var selectionHighlightStyle: NSTableView.SelectionHighlightStyle {
|
||||
set {}
|
||||
get {
|
||||
return .regular
|
||||
}
|
||||
}
|
||||
|
||||
override func drawSelection(in dirtyRect: NSRect) {
|
||||
if self.selectionHighlightStyle != .none {
|
||||
let selectionRect = NSInsetRect(self.bounds, 2.5, 2.5)
|
||||
NSColor.green.setFill()
|
||||
//NSColor(calibratedWhite: 0.85, alpha: 0.6).setFill()
|
||||
let selectionPath = NSBezierPath.init(rect: selectionRect)
|
||||
selectionPath.fill()
|
||||
}
|
||||
}
|
||||
}
|
150
CloverApp/Clover/ThemeManager/ThemeView.swift
Normal file
150
CloverApp/Clover/ThemeManager/ThemeView.swift
Normal file
@ -0,0 +1,150 @@
|
||||
//
|
||||
// ThemeView.swift
|
||||
// ThemeManager
|
||||
//
|
||||
// Created by vector sigma on 07/01/2020.
|
||||
// Copyright © 2020 vectorsigma. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import WebKit
|
||||
|
||||
class ThemeView: NSView, WebFrameLoadDelegate, WebUIDelegate {
|
||||
var manager : ThemeManager
|
||||
var imagePath : String?
|
||||
var name : String
|
||||
var author : String?
|
||||
var info : String?
|
||||
var row : Int
|
||||
var isInstalled = false
|
||||
|
||||
public let webView: WebView = {
|
||||
let wv = WebView(frame: NSMakeRect(0, 0, 147, 82))
|
||||
wv.mainFrame.frameView.allowsScrolling = false
|
||||
wv.drawsBackground = false
|
||||
return wv
|
||||
} ()
|
||||
|
||||
deinit {
|
||||
self.webView.stopLoading(nil)
|
||||
}
|
||||
|
||||
func webView(_ sender: WebView!, makeFirstResponder responder: NSResponder!) {
|
||||
if self.row >= 0 {
|
||||
self.manager.delegate?.sidebar.selectRowIndexes(IndexSet(integer: self.row),
|
||||
byExtendingSelection: false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - designated initializer
|
||||
init(manager: ThemeManager, name: String, row: Int) {
|
||||
self.manager = manager
|
||||
self.name = name
|
||||
self.row = row
|
||||
super.init(frame: NSMakeRect(0, 0, 147, 82))
|
||||
self.setup()
|
||||
self.load()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func setup() {
|
||||
self.addSubview(self.webView)
|
||||
|
||||
self.webView.translatesAutoresizingMaskIntoConstraints = false
|
||||
let left = NSLayoutConstraint(item: self.webView,
|
||||
attribute: .left,
|
||||
relatedBy: .equal,
|
||||
toItem: self,
|
||||
attribute: .left,
|
||||
multiplier: 1.0,
|
||||
constant: 0.0)
|
||||
let bottom = NSLayoutConstraint(item: self.webView,
|
||||
attribute: .bottom,
|
||||
relatedBy: .equal,
|
||||
toItem: self,
|
||||
attribute: .bottom,
|
||||
multiplier: 1.0,
|
||||
constant: 0.0)
|
||||
let width = NSLayoutConstraint(item: self.webView,
|
||||
attribute: .width,
|
||||
relatedBy: .equal,
|
||||
toItem: self,
|
||||
attribute: .width,
|
||||
multiplier: 1.0,
|
||||
constant: 0.0)
|
||||
let height = NSLayoutConstraint(item: self.webView,
|
||||
attribute: .height,
|
||||
relatedBy: .equal,
|
||||
toItem: self,
|
||||
attribute: .height,
|
||||
multiplier: 1.0,
|
||||
constant: 0.0)
|
||||
self.addConstraints([left, bottom, height, width])
|
||||
|
||||
|
||||
self.webView.uiDelegate = self
|
||||
self.webView.frameLoadDelegate = self
|
||||
}
|
||||
|
||||
private func load() {
|
||||
if self.manager.exist(theme: self.name) {
|
||||
self.manager.getImageUrl(for: self.name, completion: { (ipath) in
|
||||
if let path = ipath {
|
||||
self.imagePath = path
|
||||
}
|
||||
self.setInstalledStatus()
|
||||
})
|
||||
} else {
|
||||
self.setInstalledStatus()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func targetVolumeDidChange() {
|
||||
self.setInstalledStatus()
|
||||
}
|
||||
|
||||
func setInstalledStatus() {
|
||||
if let vol = self.manager.delegate?.targetVolume {
|
||||
self.isInstalled = fm.fileExists(atPath: vol.addPath("EFI/CLOVER/themes").addPath(self.name))
|
||||
if (self.imagePath == nil) {
|
||||
if fm.fileExists(atPath: vol.addPath("EFI/CLOVER/themes").addPath(self.name).addPath("theme.svg")) {
|
||||
self.imagePath = vol.addPath("EFI/CLOVER/themes").addPath(self.name).addPath("theme.svg")
|
||||
self.setInstalledStatus()
|
||||
} else if fm.fileExists(atPath: vol.addPath("EFI/CLOVER/themes").addPath(self.name).addPath("screenshot.png")) {
|
||||
self.imagePath = vol.addPath("EFI/CLOVER/themes").addPath(self.name).addPath("screenshot.png")
|
||||
self.setInstalledStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let path = self.imagePath {
|
||||
if self.isInstalled {
|
||||
let icon = Bundle.main.path(forResource: "check", ofType: "png")
|
||||
self.webView.mainFrame.loadHTMLString("""
|
||||
<html>
|
||||
<body>
|
||||
<div align="center" style="position:relative; height: 100%; width: 100%; top:0;left 0;">
|
||||
<img src="\(URL(fileURLWithPath: path))" style="width: 100% ; max-width: 128px;"/>
|
||||
<img src="\(URL(fileURLWithPath: icon!))" style="position:absolute; top:8; left:8;"/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""", baseURL: Bundle.main.bundleURL)
|
||||
} else {
|
||||
self.webView.mainFrame.loadHTMLString("""
|
||||
<html>
|
||||
<body>
|
||||
<div align="center" style="position:relative; height: 100%; width: 100%; top:0;left 0;">
|
||||
<img src="\(URL(fileURLWithPath: path))" style="width: 100% ; max-width: 128px;top:0;"/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""", baseURL: Bundle.main.bundleURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
CloverApp/Clover/check.png
Normal file
BIN
CloverApp/Clover/check.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
@ -77,6 +77,33 @@ class Installer: NSObject {
|
||||
Darwin.exit(EXIT_FAILURE)
|
||||
}
|
||||
|
||||
func disableInsexing(for volume: String) {
|
||||
if fm.fileExists(atPath: volume) {
|
||||
var file = volume.addPath(".metadata_never_index")
|
||||
if !fm.fileExists(atPath: file) {
|
||||
try? "".write(toFile: file, atomically: false, encoding: .utf8)
|
||||
}
|
||||
|
||||
file = volume.addPath(".Spotlight-V100")
|
||||
if fm.fileExists(atPath: file) {
|
||||
try? fm.removeItem(atPath: file)
|
||||
}
|
||||
|
||||
/*
|
||||
Clean everythings that starts with "._"
|
||||
This of course only for ESPs
|
||||
*/
|
||||
|
||||
let enumerator = fm.enumerator(atPath: volume)
|
||||
while let file = enumerator?.nextObject() as? String {
|
||||
let fullPath = volume.addPath(file)
|
||||
if file.hasPrefix("._") {
|
||||
try? fm.removeItem(atPath: fullPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func copyReplace(src: String, dst: String, attr: [FileAttributeKey: Any]?, log: Bool) -> Bool {
|
||||
var attributes : [FileAttributeKey: Any]? = attr
|
||||
let upperDir = dst.deletingLastPath
|
||||
@ -265,6 +292,10 @@ class Installer: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
if isESP {
|
||||
self.disableInsexing(for: targetVol)
|
||||
}
|
||||
|
||||
// MARK: Check paths
|
||||
var boot0Path: String? = nil
|
||||
var boot1Path: String? = nil
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
let daemonVersion = "1.1.1"
|
||||
let daemonVersion = "1.1.2"
|
||||
|
||||
let fm = FileManager.default
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
let cmdVersion = "1.0.4"
|
||||
let cmdVersion = "1.0.5"
|
||||
let savedNVRAMPath = "/tmp/NVRAM_saved"
|
||||
let NVRAMSavedToRoot = "/tmp/NVRAM_savedToRoot"
|
||||
|
||||
@ -82,7 +82,7 @@ func disableInsexing(for volume: String) {
|
||||
}
|
||||
|
||||
file = volume.addPath(".Spotlight-V100")
|
||||
if fm.fileExists(atPath: volume.addPath("")) {
|
||||
if fm.fileExists(atPath: file) {
|
||||
try? fm.removeItem(atPath: file)
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "Falsch";
|
||||
|
||||
"Theme:" = "Thema:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Filesystem schreib-/lesbar machen";
|
||||
"*Disable Sleep Proxy Client" = "*Sleep Proxy Client deaktivieren";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "faux";
|
||||
|
||||
"Theme:" = "Thème:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Son:";
|
||||
"*Make filesystem read-write" = "*Changer système fichiers en lecture-écriture";
|
||||
"*Disable Sleep Proxy Client" = "*Désactiver Mise en veille du Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Jadikan FileSystem Read-Write";
|
||||
"*Disable Sleep Proxy Client" = "*Tiadakan Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "falso";
|
||||
|
||||
"Theme:" = "Tema:";
|
||||
"Themes" = "Temi"; // window title
|
||||
"No themes found" = "Nessun tema trovato";
|
||||
"Manager" = "Gestore"; // Theme manager
|
||||
"Can't remove the theme" = "Non posso rimuovere il tema";
|
||||
"Show installed" = "Mostra installati";
|
||||
"Sound:" = "Suono:";
|
||||
"*Make filesystem read-write" = "*Rendi il filesystem scrivibile";
|
||||
"*Disable Sleep Proxy Client" = "*Disabilita Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "아니오";
|
||||
|
||||
"Theme:" = "테마:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "사운드:";
|
||||
"*Make filesystem read-write" = "*파일시스템을 읽기-쓰기로 변경";
|
||||
"*Disable Sleep Proxy Client" = "*잠자기 프록시 클라이언트 비활성화";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "falso";
|
||||
|
||||
"Theme:" = "Tema:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Som:";
|
||||
"*Make filesystem read-write" = "*Fazer filesystem leitura-escrita";
|
||||
"*Disable Sleep Proxy Client" = "*Disabilitar Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "ложь";
|
||||
|
||||
"Theme:" = "Тема:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Звук:";
|
||||
"*Make filesystem read-write" = "*Открыть файловую систему на запись";
|
||||
"*Disable Sleep Proxy Client" = "*Отключение Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "Lažno";
|
||||
|
||||
"Theme:" = "Tema:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Zvuk:";
|
||||
"*Make filesystem read-write" = "*Filesystem čine čitanje-Pisanje";
|
||||
"*Disable Sleep Proxy Client" = "*Sleep Proxy Client deaktiviran";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "否";
|
||||
|
||||
"Theme:" = "主题:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "声音:";
|
||||
"*Make filesystem read-write" = "*让文件系统可读写";
|
||||
"*Disable Sleep Proxy Client" = "*禁用 Sleep Proxy Client";
|
||||
|
@ -50,6 +50,11 @@
|
||||
"false" = "false";
|
||||
|
||||
"Theme:" = "Theme:";
|
||||
"Themes" = "Themes"; // window title
|
||||
"No themes found" = "No themes found";
|
||||
"Manager" = "Manager"; // Theme manager
|
||||
"Can't remove the theme" = "Can't remove the theme";
|
||||
"Show installed" = "Show installed";
|
||||
"Sound:" = "Sound:";
|
||||
"*Make filesystem read-write" = "*Make filesystem read-write";
|
||||
"*Disable Sleep Proxy Client" = "*Disable Sleep Proxy Client";
|
||||
|
Loading…
Reference in New Issue
Block a user