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:
vectorsigma72 2020-02-11 15:46:53 +01:00
parent 27de9a9c9a
commit 99e9fc5d89
37 changed files with 2238 additions and 107 deletions

View File

@ -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)";

View File

@ -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,

View File

@ -25,7 +25,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Check now" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="UyZ-5I-L9X">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="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"/>

View File

@ -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
}
}

View File

@ -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. Youre 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
}

View File

@ -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 \""

View File

@ -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)

View 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")
}
}

View 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>

View 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()
}
}
}

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -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

View File

@ -8,7 +8,7 @@
import Foundation
let daemonVersion = "1.1.1"
let daemonVersion = "1.1.2"
let fm = FileManager.default

View File

@ -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)
}
}

View 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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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" = "*잠자기 프록시 클라이언트 비활성화";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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";