├── LICENSE ├── README.md ├── build └── moreMenu.bundle.zip ├── moreMenu.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── w0lf.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── w0lf.xcuserdatad │ └── xcschemes │ ├── moreMenu.xcscheme │ └── xcschememanagement.plist ├── moreMenu ├── Info.plist ├── ZKSwizzle.h ├── ZKSwizzle.m ├── main.h └── main.m └── preview.gif /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Wolfgang Baird 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # moreMenu 2 | 3 | ![preview](preview.gif) 4 | 5 | # Information: 6 | 7 | - Tested on 10.14 8 | - MacForge plugin to shrink the menubar of the running application 9 | - Author: 10 | + [w0lfschild](https://github.com/w0lfschild) 11 | 12 | # Installation: 13 | 14 | 1. Download and open [MacForge](https://github.com/w0lfschild/app_updates/raw/master/MacForge/MacForge.zip) 15 | 2. Install [cleanMenu](https://www.macenhance.com/mflink?macforge://github.com/w0lfschild/myRepo/raw/master/myPaidRepo/org.w0lf.moreMenu) 16 | 3. Close and re-open all applications to load the plugin 17 | -------------------------------------------------------------------------------- /build/moreMenu.bundle.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w0lfschild/moreMenu/767447100eb1a7299d120e7b3d8fc6e228084c17/build/moreMenu.bundle.zip -------------------------------------------------------------------------------- /moreMenu.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FB3A9DAC20D6E5250023E421 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FB3A9DAB20D6E5250023E421 /* main.m */; }; 11 | FB3A9DB720D6E53C0023E421 /* ZKSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = FB3A9DB520D6E53C0023E421 /* ZKSwizzle.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | FB3A9DA820D6E5250023E421 /* moreMenu.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = moreMenu.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 16 | FB3A9DAB20D6E5250023E421 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 17 | FB3A9DAF20D6E5250023E421 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 18 | FB3A9DB520D6E53C0023E421 /* ZKSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZKSwizzle.m; sourceTree = ""; }; 19 | FB3A9DB620D6E53C0023E421 /* ZKSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZKSwizzle.h; sourceTree = ""; }; 20 | FB3A9DB820D71EC70023E421 /* main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = ""; }; 21 | /* End PBXFileReference section */ 22 | 23 | /* Begin PBXFrameworksBuildPhase section */ 24 | FB3A9DA520D6E5250023E421 /* Frameworks */ = { 25 | isa = PBXFrameworksBuildPhase; 26 | buildActionMask = 2147483647; 27 | files = ( 28 | ); 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXFrameworksBuildPhase section */ 32 | 33 | /* Begin PBXGroup section */ 34 | FB3A9D9F20D6E5250023E421 = { 35 | isa = PBXGroup; 36 | children = ( 37 | FB3A9DAA20D6E5250023E421 /* moreMenu */, 38 | FB3A9DA920D6E5250023E421 /* Products */, 39 | ); 40 | sourceTree = ""; 41 | }; 42 | FB3A9DA920D6E5250023E421 /* Products */ = { 43 | isa = PBXGroup; 44 | children = ( 45 | FB3A9DA820D6E5250023E421 /* moreMenu.bundle */, 46 | ); 47 | name = Products; 48 | sourceTree = ""; 49 | }; 50 | FB3A9DAA20D6E5250023E421 /* moreMenu */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | FB3A9DB920D725780023E421 /* ZKSwizzle */, 54 | FB3A9DB820D71EC70023E421 /* main.h */, 55 | FB3A9DAB20D6E5250023E421 /* main.m */, 56 | FB3A9DAF20D6E5250023E421 /* Info.plist */, 57 | ); 58 | path = moreMenu; 59 | sourceTree = ""; 60 | }; 61 | FB3A9DB920D725780023E421 /* ZKSwizzle */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | FB3A9DB620D6E53C0023E421 /* ZKSwizzle.h */, 65 | FB3A9DB520D6E53C0023E421 /* ZKSwizzle.m */, 66 | ); 67 | name = ZKSwizzle; 68 | sourceTree = ""; 69 | }; 70 | /* End PBXGroup section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | FB3A9DA720D6E5250023E421 /* moreMenu */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = FB3A9DB220D6E5250023E421 /* Build configuration list for PBXNativeTarget "moreMenu" */; 76 | buildPhases = ( 77 | FB3A9DA420D6E5250023E421 /* Sources */, 78 | FB3A9DA520D6E5250023E421 /* Frameworks */, 79 | FB3A9DA620D6E5250023E421 /* Resources */, 80 | ); 81 | buildRules = ( 82 | ); 83 | dependencies = ( 84 | ); 85 | name = moreMenu; 86 | productName = zinger; 87 | productReference = FB3A9DA820D6E5250023E421 /* moreMenu.bundle */; 88 | productType = "com.apple.product-type.bundle"; 89 | }; 90 | /* End PBXNativeTarget section */ 91 | 92 | /* Begin PBXProject section */ 93 | FB3A9DA020D6E5250023E421 /* Project object */ = { 94 | isa = PBXProject; 95 | attributes = { 96 | LastUpgradeCheck = 1000; 97 | ORGANIZATIONNAME = "Wolfgang Baird"; 98 | TargetAttributes = { 99 | FB3A9DA720D6E5250023E421 = { 100 | CreatedOnToolsVersion = 10.0; 101 | }; 102 | }; 103 | }; 104 | buildConfigurationList = FB3A9DA320D6E5250023E421 /* Build configuration list for PBXProject "moreMenu" */; 105 | compatibilityVersion = "Xcode 9.3"; 106 | developmentRegion = en; 107 | hasScannedForEncodings = 0; 108 | knownRegions = ( 109 | en, 110 | ); 111 | mainGroup = FB3A9D9F20D6E5250023E421; 112 | productRefGroup = FB3A9DA920D6E5250023E421 /* Products */; 113 | projectDirPath = ""; 114 | projectRoot = ""; 115 | targets = ( 116 | FB3A9DA720D6E5250023E421 /* moreMenu */, 117 | ); 118 | }; 119 | /* End PBXProject section */ 120 | 121 | /* Begin PBXResourcesBuildPhase section */ 122 | FB3A9DA620D6E5250023E421 /* Resources */ = { 123 | isa = PBXResourcesBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | ); 127 | runOnlyForDeploymentPostprocessing = 0; 128 | }; 129 | /* End PBXResourcesBuildPhase section */ 130 | 131 | /* Begin PBXSourcesBuildPhase section */ 132 | FB3A9DA420D6E5250023E421 /* Sources */ = { 133 | isa = PBXSourcesBuildPhase; 134 | buildActionMask = 2147483647; 135 | files = ( 136 | FB3A9DAC20D6E5250023E421 /* main.m in Sources */, 137 | FB3A9DB720D6E53C0023E421 /* ZKSwizzle.m in Sources */, 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | /* End PBXSourcesBuildPhase section */ 142 | 143 | /* Begin XCBuildConfiguration section */ 144 | FB3A9DB020D6E5250023E421 /* Debug */ = { 145 | isa = XCBuildConfiguration; 146 | buildSettings = { 147 | ALWAYS_SEARCH_USER_PATHS = NO; 148 | CLANG_ANALYZER_NONNULL = YES; 149 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 150 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 151 | CLANG_CXX_LIBRARY = "libc++"; 152 | CLANG_ENABLE_MODULES = YES; 153 | CLANG_ENABLE_OBJC_ARC = YES; 154 | CLANG_ENABLE_OBJC_WEAK = YES; 155 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 156 | CLANG_WARN_BOOL_CONVERSION = YES; 157 | CLANG_WARN_COMMA = YES; 158 | CLANG_WARN_CONSTANT_CONVERSION = YES; 159 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 160 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 161 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 162 | CLANG_WARN_EMPTY_BODY = YES; 163 | CLANG_WARN_ENUM_CONVERSION = YES; 164 | CLANG_WARN_INFINITE_RECURSION = YES; 165 | CLANG_WARN_INT_CONVERSION = YES; 166 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 167 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 168 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 169 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 170 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 171 | CLANG_WARN_STRICT_PROTOTYPES = YES; 172 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 173 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 174 | CLANG_WARN_UNREACHABLE_CODE = YES; 175 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 176 | CODE_SIGN_IDENTITY = "Mac Developer"; 177 | COPY_PHASE_STRIP = NO; 178 | DEBUG_INFORMATION_FORMAT = dwarf; 179 | ENABLE_STRICT_OBJC_MSGSEND = YES; 180 | ENABLE_TESTABILITY = YES; 181 | GCC_C_LANGUAGE_STANDARD = gnu11; 182 | GCC_DYNAMIC_NO_PIC = NO; 183 | GCC_NO_COMMON_BLOCKS = YES; 184 | GCC_OPTIMIZATION_LEVEL = 0; 185 | GCC_PREPROCESSOR_DEFINITIONS = ( 186 | "DEBUG=1", 187 | "$(inherited)", 188 | ); 189 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 190 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 191 | GCC_WARN_UNDECLARED_SELECTOR = YES; 192 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 193 | GCC_WARN_UNUSED_FUNCTION = YES; 194 | GCC_WARN_UNUSED_VARIABLE = YES; 195 | MACOSX_DEPLOYMENT_TARGET = 10.14; 196 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 197 | ONLY_ACTIVE_ARCH = YES; 198 | SDKROOT = macosx; 199 | }; 200 | name = Debug; 201 | }; 202 | FB3A9DB120D6E5250023E421 /* Release */ = { 203 | isa = XCBuildConfiguration; 204 | buildSettings = { 205 | ALWAYS_SEARCH_USER_PATHS = NO; 206 | CLANG_ANALYZER_NONNULL = YES; 207 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 208 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 209 | CLANG_CXX_LIBRARY = "libc++"; 210 | CLANG_ENABLE_MODULES = YES; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_ENABLE_OBJC_WEAK = YES; 213 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 214 | CLANG_WARN_BOOL_CONVERSION = YES; 215 | CLANG_WARN_COMMA = YES; 216 | CLANG_WARN_CONSTANT_CONVERSION = YES; 217 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 218 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 219 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 220 | CLANG_WARN_EMPTY_BODY = YES; 221 | CLANG_WARN_ENUM_CONVERSION = YES; 222 | CLANG_WARN_INFINITE_RECURSION = YES; 223 | CLANG_WARN_INT_CONVERSION = YES; 224 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 225 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 226 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 227 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 228 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 229 | CLANG_WARN_STRICT_PROTOTYPES = YES; 230 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 231 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 232 | CLANG_WARN_UNREACHABLE_CODE = YES; 233 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 234 | CODE_SIGN_IDENTITY = "Mac Developer"; 235 | COPY_PHASE_STRIP = NO; 236 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 237 | ENABLE_NS_ASSERTIONS = NO; 238 | ENABLE_STRICT_OBJC_MSGSEND = YES; 239 | GCC_C_LANGUAGE_STANDARD = gnu11; 240 | GCC_NO_COMMON_BLOCKS = YES; 241 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 242 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 243 | GCC_WARN_UNDECLARED_SELECTOR = YES; 244 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 245 | GCC_WARN_UNUSED_FUNCTION = YES; 246 | GCC_WARN_UNUSED_VARIABLE = YES; 247 | MACOSX_DEPLOYMENT_TARGET = 10.14; 248 | MTL_ENABLE_DEBUG_INFO = NO; 249 | SDKROOT = macosx; 250 | }; 251 | name = Release; 252 | }; 253 | FB3A9DB320D6E5250023E421 /* Debug */ = { 254 | isa = XCBuildConfiguration; 255 | buildSettings = { 256 | CODE_SIGN_STYLE = Automatic; 257 | COMBINE_HIDPI_IMAGES = YES; 258 | DEVELOPMENT_TEAM = EX596BNL45; 259 | INFOPLIST_FILE = moreMenu/Info.plist; 260 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Application Support/SIMBL/Plugins"; 261 | PRODUCT_BUNDLE_IDENTIFIER = org.w0lf.zinger; 262 | PRODUCT_NAME = "$(TARGET_NAME)"; 263 | SKIP_INSTALL = YES; 264 | WRAPPER_EXTENSION = bundle; 265 | }; 266 | name = Debug; 267 | }; 268 | FB3A9DB420D6E5250023E421 /* Release */ = { 269 | isa = XCBuildConfiguration; 270 | buildSettings = { 271 | CODE_SIGN_STYLE = Automatic; 272 | COMBINE_HIDPI_IMAGES = YES; 273 | DEVELOPMENT_TEAM = EX596BNL45; 274 | INFOPLIST_FILE = moreMenu/Info.plist; 275 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Application Support/SIMBL/Plugins"; 276 | PRODUCT_BUNDLE_IDENTIFIER = org.w0lf.zinger; 277 | PRODUCT_NAME = "$(TARGET_NAME)"; 278 | SKIP_INSTALL = YES; 279 | WRAPPER_EXTENSION = bundle; 280 | }; 281 | name = Release; 282 | }; 283 | /* End XCBuildConfiguration section */ 284 | 285 | /* Begin XCConfigurationList section */ 286 | FB3A9DA320D6E5250023E421 /* Build configuration list for PBXProject "moreMenu" */ = { 287 | isa = XCConfigurationList; 288 | buildConfigurations = ( 289 | FB3A9DB020D6E5250023E421 /* Debug */, 290 | FB3A9DB120D6E5250023E421 /* Release */, 291 | ); 292 | defaultConfigurationIsVisible = 0; 293 | defaultConfigurationName = Release; 294 | }; 295 | FB3A9DB220D6E5250023E421 /* Build configuration list for PBXNativeTarget "moreMenu" */ = { 296 | isa = XCConfigurationList; 297 | buildConfigurations = ( 298 | FB3A9DB320D6E5250023E421 /* Debug */, 299 | FB3A9DB420D6E5250023E421 /* Release */, 300 | ); 301 | defaultConfigurationIsVisible = 0; 302 | defaultConfigurationName = Release; 303 | }; 304 | /* End XCConfigurationList section */ 305 | }; 306 | rootObject = FB3A9DA020D6E5250023E421 /* Project object */; 307 | } 308 | -------------------------------------------------------------------------------- /moreMenu.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /moreMenu.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /moreMenu.xcodeproj/project.xcworkspace/xcuserdata/w0lf.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w0lfschild/moreMenu/767447100eb1a7299d120e7b3d8fc6e228084c17/moreMenu.xcodeproj/project.xcworkspace/xcuserdata/w0lf.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /moreMenu.xcodeproj/xcuserdata/w0lf.xcuserdatad/xcschemes/moreMenu.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /moreMenu.xcodeproj/xcuserdata/w0lf.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | moreMenu.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | FB3A9DA720D6E5250023E421 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /moreMenu/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 0.3.1 19 | CFBundleVersion 20 | 3 21 | NSHumanReadableCopyright 22 | Copyright © 2018 Wolfgang Baird. All rights reserved. 23 | NSPrincipalClass 24 | 25 | SIMBLTargetApplications 26 | 27 | 28 | BundleIdentifier 29 | * 30 | 31 | 32 | macSubstratePlugin 33 | 34 | AuthorEmail 35 | aguywithlonghair@yahoo.com 36 | AuthorName 37 | w0lf 38 | Description 39 | More menu space 40 | TargetAppBundleID 41 | * 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /moreMenu/ZKSwizzle.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZKSwizzle.h 3 | // ZKSwizzle 4 | // 5 | // Created by Alexander S Zielenski on 7/24/14. 6 | // Copyright (c) 2014 Alexander S Zielenski. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | // This is a class for streamlining swizzling. Simply create a new class of any name you want and 14 | // Example: 15 | /* 16 | @interface ZKHookClass : NSObject 17 | - (NSString *)description; // hooks -description on NSObject 18 | - (void)addedMethod; // all subclasses of NSObject now respond to -addedMethod 19 | @end 20 | 21 | @implementation ZKHookClass 22 | ... 23 | @end 24 | 25 | [ZKSwizzle swizzleClass:ZKClass(ZKHookClass) forClass:ZKClass(destination)]; 26 | */ 27 | 28 | #ifndef ZKSWIZZLE_DEFS 29 | #define ZKSWIZZLE_DEFS 30 | 31 | // CRAZY MACROS FOR DYNAMIC PROTOTYPE CREATION 32 | #define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5 ,4 ,3 ,2, 1, 0) 33 | #define VA_NUM_ARGS_IMPL(_0, _1,_2,_3,_4,_5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 ,N,...) N 34 | 35 | #define WRAP0() 36 | #define WRAP1(VARIABLE) , typeof ( VARIABLE ) 37 | #define WRAP2(VARIABLE, ...) WRAP1(VARIABLE) WRAP1(__VA_ARGS__) 38 | #define WRAP3(VARIABLE, ...) WRAP1(VARIABLE) WRAP2(__VA_ARGS__) 39 | #define WRAP4(VARIABLE, ...) WRAP1(VARIABLE) WRAP3(__VA_ARGS__) 40 | #define WRAP5(VARIABLE, ...) WRAP1(VARIABLE) WRAP4(__VA_ARGS__) 41 | #define WRAP6(VARIABLE, ...) WRAP1(VARIABLE) WRAP5(__VA_ARGS__) 42 | #define WRAP7(VARIABLE, ...) WRAP1(VARIABLE) WRAP6(__VA_ARGS__) 43 | #define WRAP8(VARIABLE, ...) WRAP1(VARIABLE) WRAP7(__VA_ARGS__) 44 | #define WRAP9(VARIABLE, ...) WRAP1(VARIABLE) WRAP8(__VA_ARGS__) 45 | #define WRAP10(VARIABLE, ...) WRAP1(VARIABLE) WRAP9(__VA_ARGS__) 46 | #define WRAP11(VARIABLE, ...) WRAP1(VARIABLE) WRAP10(__VA_ARGS__) 47 | #define WRAP12(VARIABLE, ...) WRAP1(VARIABLE) WRAP11(__VA_ARGS__) 48 | #define WRAP13(VARIABLE, ...) WRAP1(VARIABLE) WRAP12(__VA_ARGS__) 49 | #define WRAP14(VARIABLE, ...) WRAP1(VARIABLE) WRAP13(__VA_ARGS__) 50 | #define WRAP15(VARIABLE, ...) WRAP1(VARIABLE) WRAP14(__VA_ARGS__) 51 | #define WRAP16(VARIABLE, ...) WRAP1(VARIABLE) WRAP15(__VA_ARGS__) 52 | #define WRAP17(VARIABLE, ...) WRAP1(VARIABLE) WRAP16(__VA_ARGS__) 53 | #define WRAP18(VARIABLE, ...) WRAP1(VARIABLE) WRAP17(__VA_ARGS__) 54 | #define WRAP19(VARIABLE, ...) WRAP1(VARIABLE) WRAP18(__VA_ARGS__) 55 | #define WRAP20(VARIABLE, ...) WRAP1(VARIABLE) WRAP19(__VA_ARGS__) 56 | 57 | #define CAT(A, B) A ## B 58 | #define INVOKE(MACRO, NUMBER, ...) CAT(MACRO, NUMBER)(__VA_ARGS__) 59 | #define WRAP_LIST(...) INVOKE(WRAP, VA_NUM_ARGS(__VA_ARGS__), __VA_ARGS__) 60 | 61 | // Gets the a class with the name CLASS 62 | #define ZKClass(CLASS) objc_getClass(#CLASS) 63 | 64 | // returns the value of an instance variable. 65 | #if !__has_feature(objc_arc) 66 | #define ZKHookIvar(OBJECT, TYPE, NAME) (*(TYPE *)ZKIvarPointer(OBJECT, NAME)) 67 | #else 68 | #define ZKHookIvar(OBJECT, TYPE, NAME) \ 69 | _Pragma("clang diagnostic push") \ 70 | _Pragma("clang diagnostic ignored \"-Wignored-attributes\"") \ 71 | (*(__unsafe_unretained TYPE *)ZKIvarPointer(OBJECT, NAME)) \ 72 | _Pragma("clang diagnostic pop") 73 | #endif 74 | // returns the original implementation of the swizzled function or null or not found 75 | #define ZKOrig(TYPE, ...) ((TYPE (*)(id, SEL WRAP_LIST(__VA_ARGS__)))(ZKOriginalImplementation(self, _cmd, __PRETTY_FUNCTION__)))(self, _cmd, ##__VA_ARGS__) 76 | 77 | // returns the original implementation of the superclass of the object swizzled 78 | #define ZKSuper(TYPE, ...) ((TYPE (*)(id, SEL WRAP_LIST(__VA_ARGS__)))(ZKSuperImplementation(self, _cmd, __PRETTY_FUNCTION__)))(self, _cmd, ##__VA_ARGS__) 79 | 80 | #define _ZKSwizzleInterfaceConditionally(CLASS_NAME, TARGET_CLASS, SUPERCLASS, GROUP, IMMEDIATELY) \ 81 | @interface _$ ## CLASS_NAME : SUPERCLASS @end \ 82 | @implementation _$ ## CLASS_NAME \ 83 | + (void)initialize {} \ 84 | @end \ 85 | @interface CLASS_NAME : _$ ## CLASS_NAME @end \ 86 | @implementation CLASS_NAME (ZKSWIZZLE) \ 87 | + (void)load { \ 88 | _$ZKRegisterInterface(self, #GROUP);\ 89 | if (IMMEDIATELY) { \ 90 | [self _ZK_unconditionallySwizzle]; \ 91 | } \ 92 | } \ 93 | + (void)_ZK_unconditionallySwizzle { \ 94 | ZKSwizzle(CLASS_NAME, TARGET_CLASS); \ 95 | } \ 96 | @end 97 | 98 | // Bootstraps your swizzling class so that it requires no setup 99 | // outside of this macro call 100 | // If you override +load you must call ZKSwizzle(CLASS_NAME, TARGET_CLASS) 101 | // yourself, otherwise the swizzling would not take place 102 | #define ZKSwizzleInterface(CLASS_NAME, TARGET_CLASS, SUPERCLASS) \ 103 | _ZKSwizzleInterfaceConditionally(CLASS_NAME, TARGET_CLASS, SUPERCLASS, ZK_UNGROUPED, YES) 104 | 105 | // Same as ZKSwizzleInterface, except 106 | #define ZKSwizzleInterfaceGroup(CLASS_NAME, TARGET_CLASS, SUPER_CLASS, GROUP) \ 107 | _ZKSwizzleInterfaceConditionally(CLASS_NAME, TARGET_CLASS, SUPER_CLASS, GROUP, NO) 108 | 109 | __BEGIN_DECLS 110 | 111 | // Make sure to cast this before you use it 112 | typedef id (*ZKIMP)(id, SEL, ...); 113 | 114 | // returns a pointer to the instance variable "name" on the object 115 | void *ZKIvarPointer(id self, const char *name); 116 | // returns the original implementation of a method with selector "sel" of an object hooked by the methods below 117 | ZKIMP ZKOriginalImplementation(id self, SEL sel, const char *info); 118 | // returns the implementation of a method with selector "sel" of the superclass of object 119 | ZKIMP ZKSuperImplementation(id object, SEL sel, const char *info); 120 | 121 | // hooks all the implemented methods of source with destination 122 | // adds any methods that arent implemented on destination to destination that are implemented in source 123 | #define ZKSwizzle(src, dst) _ZKSwizzle(ZKClass(src), ZKClass(dst)) 124 | BOOL _ZKSwizzle(Class src, Class dest); 125 | 126 | #define ZKSwizzleGroup(NAME) _ZKSwizzleGroup(#NAME) 127 | void _$ZKRegisterInterface(Class cls, const char *groupName); 128 | BOOL _ZKSwizzleGroup(const char *groupName); 129 | 130 | // Calls above method with the superclass of source for desination 131 | #define ZKSwizzleClass(src) _ZKSwizzleClass(ZKClass(src)) 132 | BOOL _ZKSwizzleClass(Class cls); 133 | 134 | __END_DECLS 135 | #endif 136 | 137 | -------------------------------------------------------------------------------- /moreMenu/ZKSwizzle.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZKSwizzle.m 3 | // ZKSwizzle 4 | // 5 | // Created by Alexander S Zielenski on 7/24/14. 6 | // Copyright (c) 2014 Alexander S Zielenski. All rights reserved. 7 | // 8 | 9 | #import "ZKSwizzle.h" 10 | static NSMutableDictionary *classTable; 11 | 12 | @interface NSObject (ZKSwizzle) 13 | + (void)_ZK_unconditionallySwizzle; 14 | @end 15 | 16 | void *ZKIvarPointer(id self, const char *name) { 17 | Ivar ivar = class_getInstanceVariable(object_getClass(self), name); 18 | return ivar == NULL ? NULL : (__bridge void *)self + ivar_getOffset(ivar); 19 | } 20 | 21 | static SEL destinationSelectorForSelector(SEL cmd, Class dst) { 22 | return NSSelectorFromString([@"_ZK_old_" stringByAppendingFormat:@"%s_%@", class_getName(dst), NSStringFromSelector(cmd)]); 23 | } 24 | 25 | static Class classFromInfo(const char *info) { 26 | NSUInteger bracket_index = -1; 27 | for (NSUInteger i = 0; i < strlen(info); i++) { 28 | if (info[i] == '[') { 29 | bracket_index = i; 30 | } 31 | } 32 | bracket_index++; 33 | 34 | if (bracket_index == -1) { 35 | [NSException raise:@"Failed to parse info" format:@"Couldn't find swizzle class for info: %s", info]; 36 | return NULL; 37 | } 38 | 39 | char after_bracket[255]; 40 | memcpy(after_bracket, &info[bracket_index], strlen(info) - bracket_index - 1); 41 | 42 | for (NSUInteger i = 0; i < strlen(info); i++) { 43 | if (after_bracket[i] == ' ') { 44 | after_bracket[i] = '\0'; 45 | } 46 | } 47 | 48 | return objc_getClass(after_bracket); 49 | } 50 | 51 | // takes __PRETTY_FUNCTION__ for info which gives the name of the swizzle source class 52 | /* 53 | 54 | We add the original implementation onto the swizzle class 55 | On ZKOrig, we use __PRETTY_FUNCTION__ to get the name of the swizzle class 56 | Then we get the implementation of that selector on the swizzle class 57 | Then we call it directly, passing in the correct selector and self 58 | 59 | */ 60 | ZKIMP ZKOriginalImplementation(id self, SEL sel, const char *info) { 61 | if (sel == NULL || self == NULL || info == NULL) { 62 | [NSException raise:@"Invalid Arguments" format:@"One of self: %@, self: %@, or info: %s is NULL", self, NSStringFromSelector(sel), info]; 63 | return NULL; 64 | } 65 | 66 | Class cls = classFromInfo(info); 67 | Class dest = object_getClass(self); 68 | 69 | if (cls == NULL || dest == NULL) { 70 | [NSException raise:@"Failed obtain class pair" format:@"src: %@ | dst: %@ | sel: %@", NSStringFromClass(cls), NSStringFromClass(dest), NSStringFromSelector(sel)]; 71 | return NULL; 72 | } 73 | 74 | SEL destSel = destinationSelectorForSelector(sel, cls); 75 | 76 | Method method = class_getInstanceMethod(dest, destSel); 77 | 78 | if (method == NULL) { 79 | [NSException raise:@"Failed to retrieve method" format:@"Got null for the source class %@ with selector %@ (%@)", NSStringFromClass(cls), NSStringFromSelector(sel), NSStringFromSelector(destSel)]; 80 | return NULL; 81 | } 82 | 83 | ZKIMP implementation = (ZKIMP)method_getImplementation(method); 84 | if (implementation == NULL) { 85 | [NSException raise:@"Failed to get implementation" format:@"The objective-c runtime could not get the implementation for %@ on the class %@. There is no fix for this", NSStringFromClass(cls), NSStringFromSelector(sel)]; 86 | } 87 | 88 | return implementation; 89 | } 90 | 91 | ZKIMP ZKSuperImplementation(id object, SEL sel, const char *info) { 92 | if (sel == NULL || object == NULL) { 93 | [NSException raise:@"Invalid Arguments" format:@"One of self: %@, self: %@ is NULL", object, NSStringFromSelector(sel)]; 94 | return NULL; 95 | } 96 | 97 | Class cls = object_getClass(object); 98 | if (cls == NULL) { 99 | [NSException raise:@"Invalid Argument" format:@"Could not obtain class for the passed object"]; 100 | return NULL; 101 | } 102 | 103 | // Two scenarios: 104 | // 1.) The superclass was not swizzled, no problem 105 | // 2.) The superclass was swizzled, problem 106 | 107 | // We want to return the swizzled class's superclass implementation 108 | // If this is a subclass of such a class, we want two behaviors: 109 | // a.) If this imp was also swizzled, no problem, return the superclass's swizzled imp 110 | // b.) This imp was not swizzled, return the class that was originally swizzled's superclass's imp 111 | Class sourceClass = classFromInfo(info); 112 | if (sourceClass != NULL) { 113 | BOOL isClassMethod = class_isMetaClass(cls); 114 | // This was called from a swizzled method, get the class it was swizzled with 115 | NSString *className = classTable[NSStringFromClass(sourceClass)]; 116 | if (className != NULL) { 117 | cls = NSClassFromString(className); 118 | // make sure we get a class method if we asked for one 119 | if (isClassMethod) { 120 | cls = object_getClass(cls); 121 | } 122 | } 123 | } 124 | 125 | cls = class_getSuperclass(cls); 126 | 127 | // This is a root class, it has no super class 128 | if (cls == NULL) { 129 | [NSException raise:@"Invalid Argument" format:@"Could not obtain superclass for the passed object"]; 130 | return NULL; 131 | } 132 | 133 | Method method = class_getInstanceMethod(cls, sel); 134 | if (method == NULL) { 135 | [NSException raise:@"Failed to retrieve method" format:@"We could not find the super implementation for the class %@ and selector %@, are you sure it exists?", NSStringFromClass(cls), NSStringFromSelector(sel)]; 136 | return NULL; 137 | } 138 | 139 | ZKIMP implementation = (ZKIMP)method_getImplementation(method); 140 | if (implementation == NULL) { 141 | [NSException raise:@"Failed to get implementation" format:@"The objective-c runtime could not get the implementation for %@ on the class %@. There is no fix for this", NSStringFromClass(cls), NSStringFromSelector(sel)]; 142 | } 143 | 144 | return implementation; 145 | } 146 | 147 | static BOOL enumerateMethods(Class, Class); 148 | BOOL _ZKSwizzle(Class src, Class dest) { 149 | if (dest == NULL) 150 | return NO; 151 | 152 | NSString *destName = NSStringFromClass(dest); 153 | if (!destName) { 154 | return NO; 155 | } 156 | 157 | if (!classTable) { 158 | classTable = [[NSMutableDictionary alloc] init]; 159 | } 160 | 161 | if ([classTable objectForKey:NSStringFromClass(src)]) { 162 | [NSException raise:@"Invalid Argument" 163 | format:@"This source class (%@) was already swizzled with another, (%@)", NSStringFromClass(src), classTable[NSStringFromClass(src)]]; 164 | return NO; 165 | } 166 | 167 | BOOL success = enumerateMethods(dest, src); 168 | // The above method only gets instance methods. Do the same method for the metaclass of the class 169 | success &= enumerateMethods(object_getClass(dest), object_getClass(src)); 170 | 171 | [classTable setObject:destName forKey:NSStringFromClass(src)]; 172 | return success; 173 | } 174 | 175 | BOOL _ZKSwizzleClass(Class cls) { 176 | return _ZKSwizzle(cls, [cls superclass]); 177 | } 178 | 179 | static BOOL enumerateMethods(Class destination, Class source) { 180 | #if OBJC_API_VERSION < 2 181 | [NSException raise:@"Unsupported feature" format:@"ZKSwizzle is only available in objc 2.0"]; 182 | return NO; 183 | 184 | #else 185 | 186 | unsigned int methodCount; 187 | Method *methodList = class_copyMethodList(source, &methodCount); 188 | BOOL success = YES; 189 | for (int i = 0; i < methodCount; i++) { 190 | Method method = methodList[i]; 191 | SEL selector = method_getName(method); 192 | NSString *methodName = NSStringFromSelector(selector); 193 | 194 | // Don't do anything with the unconditional swizzle 195 | if (sel_isEqual(selector, @selector(_ZK_unconditionallySwizzle))) { 196 | continue; 197 | } 198 | 199 | // We only swizzle methods that are implemented 200 | if (class_respondsToSelector(destination, selector)) { 201 | Method originalMethod = class_getInstanceMethod(destination, selector); 202 | 203 | const char *originalType = method_getTypeEncoding(originalMethod); 204 | const char *newType = method_getTypeEncoding(method); 205 | if (strcmp(originalType, newType) != 0) { 206 | NSLog(@"ZKSwizzle: incompatible type encoding for %@. (expected %s, got %s)", methodName, originalType, newType); 207 | // Incompatible type encoding 208 | success = NO; 209 | continue; 210 | } 211 | 212 | // We are re-adding the destination selector because it could be on a superclass and not on the class itself. This method could fail 213 | class_addMethod(destination, selector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); 214 | 215 | SEL destSel = destinationSelectorForSelector(selector, source); 216 | if (!class_addMethod(destination, destSel, method_getImplementation(method), method_getTypeEncoding(originalMethod))) { 217 | NSLog(@"ZKSwizzle: failed to add method %@ onto class %@ with selector %@", NSStringFromSelector(selector), NSStringFromClass(source), NSStringFromSelector(destSel)); 218 | success = NO; 219 | continue; 220 | } 221 | 222 | method_exchangeImplementations(class_getInstanceMethod(destination, selector), class_getInstanceMethod(destination, destSel)); 223 | } else { 224 | // Add any extra methods to the class but don't swizzle them 225 | success &= class_addMethod(destination, selector, method_getImplementation(method), method_getTypeEncoding(method)); 226 | } 227 | } 228 | 229 | unsigned int propertyCount; 230 | objc_property_t *propertyList = class_copyPropertyList(source, &propertyCount); 231 | for (int i = 0; i < propertyCount; i++) { 232 | objc_property_t property = propertyList[i]; 233 | const char *name = property_getName(property); 234 | unsigned int attributeCount; 235 | objc_property_attribute_t *attributes = property_copyAttributeList(property, &attributeCount); 236 | 237 | if (class_getProperty(destination, name) == NULL) { 238 | class_addProperty(destination, name, attributes, attributeCount); 239 | } else { 240 | class_replaceProperty(destination, name, attributes, attributeCount); 241 | } 242 | 243 | free(attributes); 244 | } 245 | 246 | free(propertyList); 247 | free(methodList); 248 | return success; 249 | #endif 250 | } 251 | 252 | // Options were to use a group class and traverse its subclasses 253 | // or to create a groups dictionary 254 | // This works because +load on NSObject is called before attribute((constructor)) 255 | static NSMutableDictionary *groups = nil; 256 | void _$ZKRegisterInterface(Class cls, const char *groupName) { 257 | if (!groups) 258 | groups = [NSMutableDictionary dictionary]; 259 | 260 | NSString *groupString = @(groupName); 261 | NSMutableArray *groupList = groups[groupString]; 262 | if (!groupList) { 263 | groupList = [NSMutableArray array]; 264 | groups[groupString] = groupList; 265 | } 266 | 267 | [groupList addObject:NSStringFromClass(cls)]; 268 | } 269 | 270 | BOOL _ZKSwizzleGroup(const char *groupName) { 271 | NSArray *groupList = groups[@(groupName)]; 272 | if (!groupList) { 273 | [NSException raise:@"Invalid Argument" format:@"ZKSwizzle: There is no group by the name of %s", groupName]; 274 | return NO; 275 | } 276 | 277 | BOOL success = YES; 278 | for (NSString *className in groupList) { 279 | Class cls = NSClassFromString(className); 280 | if (cls == NULL) 281 | continue; 282 | 283 | if (class_respondsToSelector(object_getClass(cls), @selector(_ZK_unconditionallySwizzle))) { 284 | [cls _ZK_unconditionallySwizzle]; 285 | } else { 286 | success = NO; 287 | } 288 | } 289 | 290 | return success; 291 | } 292 | -------------------------------------------------------------------------------- /moreMenu/main.h: -------------------------------------------------------------------------------- 1 | // 2 | // main.h 3 | // moreMenu 4 | // 5 | // Created by Wolfgang Baird on 6/17/18. 6 | // Copyright © 2018 Wolfgang Baird. All rights reserved. 7 | // 8 | 9 | #ifndef main_h 10 | #define main_h 11 | #endif /* main_h */ 12 | 13 | @import AppKit; 14 | #import "ZKSwizzle.h" 15 | 16 | @interface moreMenu : NSObject 17 | + (moreMenu*) sharedInstance; 18 | - (void)toggleMenu:(id)sender; 19 | - (void)addMenu:(id)sender; 20 | - (void)hideMenu:(id)sender; 21 | - (void)establishMenu; 22 | - (void)setupOurButton:(BOOL)pointLeft; 23 | @end 24 | 25 | @interface wb_NSMenuHook : NSMenu 26 | @end 27 | 28 | @interface wb_NSMenuItemHook : NSMenuItem 29 | @end 30 | -------------------------------------------------------------------------------- /moreMenu/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // moreMenu 4 | // 5 | // Created by Wolfgang Baird on 6/17/18. 6 | // Copyright © 2018 Wolfgang Baird. All rights reserved. 7 | // 8 | 9 | #import "main.h" 10 | #import 11 | 12 | BOOL hideMenu = false; 13 | NSArray *customEmoji; 14 | NSArray *originalMenuArray; 15 | 16 | NSMenu *expanderMenu; 17 | NSMenu *mainMenu; 18 | 19 | NSMenuItem *expanderItem; 20 | NSMenuItem *mainItem; 21 | 22 | moreMenu *controlla; 23 | NSUserDefaults *defaults; 24 | 25 | // Initial setup of the plugin, do this only once 26 | // SIMBL & mach_inject 27 | __attribute__((constructor)) void moreMenuEntry() { 28 | static dispatch_once_t onceToken; 29 | dispatch_once(&onceToken, ^{ 30 | controlla = [moreMenu sharedInstance]; 31 | ZKSwizzle(wb_NSMenuHook, NSMenu); 32 | ZKSwizzle(wb_NSMenuItemHook, NSMenuItem); 33 | 34 | defaults = [NSUserDefaults standardUserDefaults]; 35 | hideMenu = [defaults boolForKey:@"wb_moreMenu_hide"]; 36 | 37 | // Support for custom emoji / strings 38 | NSArray *defEmoji = [[NSArray alloc] initWithObjects:@"moreMenu", @"👈", @"👉", nil]; 39 | NSArray *newEmoji = [defaults arrayForKey:@"wb_moreMenu_emoji"]; 40 | NSMutableArray *emojiBuild = [[NSMutableArray alloc] init]; 41 | if (newEmoji.count > 0) { 42 | NSUInteger count = newEmoji.count; 43 | if (count > 2) count = 2; 44 | for (int i = 0; i < count; i++) { 45 | NSString *emoji = newEmoji[i]; 46 | if (emoji.length > 0) 47 | [emojiBuild addObject:emoji]; 48 | else 49 | [emojiBuild addObject:defEmoji[i]]; 50 | } 51 | } else { 52 | emojiBuild = defEmoji.mutableCopy; 53 | } 54 | customEmoji = emojiBuild.copy; 55 | 56 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 57 | [controlla establishMenu]; 58 | if (hideMenu) { 59 | [controlla hideMenu:nil]; 60 | } else { 61 | Boolean addItem = true; 62 | for (NSMenuItem *i in NSApp.mainMenu.itemArray) 63 | if (i.tag == 3015) 64 | addItem = false; 65 | if (addItem) 66 | [controlla addMenu:nil]; 67 | } 68 | }); 69 | 70 | NSLog(@"moreMenu loaded..."); 71 | }); 72 | } 73 | 74 | @implementation moreMenu 75 | 76 | + (void)load { 77 | moreMenuEntry(); 78 | } 79 | 80 | // Get a shared instance of moreMenu 81 | + (moreMenu*) sharedInstance { 82 | static moreMenu* plugin = nil; 83 | if (plugin == nil) 84 | plugin = [[moreMenu alloc] init]; 85 | return plugin; 86 | } 87 | 88 | // Setup the hide/show menu/button 89 | - (void)setupOurButton:(BOOL)pointLeft { 90 | expanderItem = [[NSMenuItem alloc] initWithTitle:customEmoji[0] action:@selector(toggleMenu:) keyEquivalent:@""]; 91 | [expanderItem setTag:3015]; 92 | [expanderItem setTarget:controlla]; 93 | if (pointLeft) { 94 | expanderMenu = [[NSMenu alloc] initWithTitle:customEmoji[1]]; 95 | [expanderItem setSubmenu:expanderMenu]; 96 | } else { 97 | expanderMenu = [[NSMenu alloc] initWithTitle:customEmoji[2]]; 98 | [expanderItem setTitle:customEmoji[2]]; 99 | } 100 | // [[expanderMenu addItemWithTitle:@"" action:@selector(toggleMenu:) keyEquivalent:@""] setTarget:controlla]; 101 | // [expanderItem setSubmenu:expanderMenu]; 102 | } 103 | 104 | - (void)setupOurMenu { 105 | mainMenu = [[NSMenu alloc] initWithTitle:customEmoji[0]]; 106 | mainItem = [[NSMenuItem alloc] initWithTitle:customEmoji[0] action:nil keyEquivalent:@""]; 107 | [mainItem setTag:3016]; 108 | [mainItem setTarget:controlla]; 109 | for (NSMenuItem *i in originalMenuArray) { 110 | // Fix for first menuitem often not being named 111 | if (i == originalMenuArray.firstObject) 112 | if ([i.title isEqualToString:@""] || [i.title isEqualToString:@"Apple"]) 113 | if (i.submenu.title.length > 0) 114 | [i setTitle:i.submenu.title]; 115 | // [i setTitle:[NSBundle mainBundle].bundlePath.stringByDeletingPathExtension.lastPathComponent]; 116 | 117 | // Fix for a crash probably could do this without the try catch 118 | @try { 119 | [mainMenu addItem:i]; 120 | } @catch (NSException *exception) { 121 | [i.menu removeItem:i]; 122 | [mainMenu addItem:i]; 123 | } @finally { 124 | } 125 | } 126 | [self setupOurButton:false]; 127 | [mainMenu addItem:expanderItem]; 128 | [mainItem setSubmenu:mainMenu]; 129 | } 130 | 131 | /* 132 | Hide the menu by removing everything from the mainmenu and our epanderMenu then resetup our expanderMenu and add it to the mainmenu 133 | Interesting enough regardless of what the item is named the first menuitem in an applications mainMenu seems to always display 134 | The applications name so that makes things easy on us 135 | */ 136 | - (void)hide { 137 | [NSApp.mainMenu removeAllItems]; 138 | [expanderMenu removeAllItems]; 139 | [mainMenu removeAllItems]; 140 | [self setupOurMenu]; 141 | [NSApp.mainMenu setItemArray:[NSArray arrayWithObject:mainItem]]; 142 | } 143 | 144 | // Prevent adding duplicates of our expander item to the mainMenu 145 | - (void)checkAndAdd { 146 | if (![NSApp.mainMenu.itemArray containsObject:expanderItem]) 147 | [NSApp.mainMenu addItem:expanderItem]; 148 | } 149 | 150 | // SHow the original menu by removing everything then setup the original menu array and add our hiding button 151 | - (void)show { 152 | [NSApp.mainMenu removeAllItems]; 153 | [expanderMenu removeAllItems]; 154 | [mainMenu removeAllItems]; 155 | [self setupOurButton:true]; 156 | [NSApp.mainMenu setItemArray:originalMenuArray]; 157 | [self checkAndAdd]; 158 | } 159 | 160 | // Write out our current state of hideMenu to the application preferences 161 | - (void)writeDefaults { 162 | defaults = [NSUserDefaults standardUserDefaults]; 163 | [defaults setBool:hideMenu forKey:@"wb_moreMenu_hide"]; 164 | [defaults synchronize]; 165 | } 166 | 167 | // Specifically hide the mainMenu, this is called on load if the preferences tell us to 168 | - (void)hideMenu:(id)sender { 169 | hideMenu = true; 170 | [self writeDefaults]; 171 | [self hide]; 172 | } 173 | 174 | // Add menu 175 | - (void)addMenu:(id)sender { 176 | [self setupOurButton:!hideMenu]; 177 | [self checkAndAdd]; 178 | } 179 | 180 | // Toggle the mainMenu state 181 | - (void)toggleMenu:(id)sender { 182 | hideMenu = !hideMenu; 183 | [self writeDefaults]; 184 | if (hideMenu == true) { 185 | [self hide]; 186 | } else { 187 | [self show]; 188 | } 189 | } 190 | 191 | // Update the `originalMenu` but only do this if out menu is not currently hidden 192 | - (void)establishMenu { 193 | if (NSApp.mainMenu.itemArray.count > 0) { 194 | NSMutableArray *newMenuArray = [[NSMutableArray alloc] initWithArray:NSApp.mainMenu.itemArray]; 195 | if (![newMenuArray[0] isEqualTo:mainItem]) { 196 | if ([[(NSMenuItem*)[newMenuArray lastObject] title] isEqualToString:customEmoji[0]]) 197 | [newMenuArray removeLastObject]; 198 | originalMenuArray = newMenuArray.copy; 199 | // NSLog(@"zzz establishMenu %@", originalMenuArray); 200 | } 201 | } 202 | } 203 | 204 | @end 205 | 206 | extern MenuRef _NSGetCarbonMenu(NSMenu *); 207 | 208 | @implementation wb_NSMenuHook 209 | 210 | - (MenuRef) menuReference { 211 | MenuRef theMenuReference = _NSGetCarbonMenu(self); 212 | if (theMenuReference==0) { 213 | // this is necessary to make cocoa actually create the underlying carbon menu 214 | NSMenu *theMainMenu = [NSApp mainMenu]; 215 | NSMenuItem *theDummyMenuItem = [theMainMenu addItemWithTitle: @"sub" 216 | action: NULL keyEquivalent: @""]; 217 | [theDummyMenuItem setSubmenu: self]; 218 | [theDummyMenuItem setSubmenu: nil]; 219 | [theMainMenu removeItem: theDummyMenuItem]; 220 | return _NSGetCarbonMenu(self); 221 | } 222 | return theMenuReference; 223 | } 224 | 225 | // Check if we clicked out mainmenu item 226 | - (void)_updateForTracking { 227 | ZKOrig(void); 228 | if ([self isEqualTo:expanderMenu]) { 229 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.001 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 230 | MenuTrackingData theMenuTrackingData; 231 | if (GetMenuTrackingData([self menuReference], &theMenuTrackingData)==noErr) { 232 | // Clicker the mainMenu menu 233 | [controlla toggleMenu:nil]; 234 | } 235 | }); 236 | } 237 | } 238 | 239 | // Prevent crashing if you try to add an item beyond index range 240 | - (void)insertItem:(NSMenuItem *)newItem atIndex:(NSInteger)index { 241 | // Fix for crash when menu is hidden and an application tries to insert an item at an expected index that no longer exists 242 | if (index <= self.itemArray.count) 243 | ZKOrig(void, newItem, index); 244 | else { 245 | if (hideMenu) { 246 | // Fix for items not being added when menu is hidden 247 | NSMenu *menu = NSApp.mainMenu.itemArray.firstObject.submenu; 248 | [menu insertItem:newItem atIndex:menu.itemArray.count - 1]; 249 | NSMutableArray *muta = [NSMutableArray arrayWithArray:originalMenuArray]; 250 | [muta insertObject:newItem atIndex:muta.count - 1]; 251 | originalMenuArray = muta.copy; 252 | } else { 253 | if (self.itemArray.count > 0) 254 | ZKOrig(void, newItem, self.itemArray.count - 1); 255 | } 256 | } 257 | } 258 | 259 | // Hook this to make sure we stay hidden 260 | - (void)addItem:(NSMenuItem *)newItem { 261 | // use Spotify+ (https://github.com/w0lfschild/spotifyPlus) to fix empty items in dock menu 262 | ZKOrig(void, newItem); 263 | 264 | // Fix to avoid Safari crash 265 | Boolean update = true; 266 | if ([self.className isEqualToString:@"WebsiteIconMenu"]) update = false; 267 | 268 | if (update == true) { 269 | if (hideMenu) { 270 | if (NSApp.mainMenu.itemArray.count > 1) { 271 | // NSLog(@"zzz, we need our mainItem"); 272 | [controlla establishMenu]; 273 | [controlla hide]; 274 | } 275 | } else { 276 | if (![NSApp.mainMenu.itemArray containsObject:expanderItem]) { 277 | // NSLog(@"zzz, we need our expanderItem"); 278 | // [controlla establishMenu]; 279 | [controlla addMenu:nil]; 280 | } 281 | } 282 | } 283 | } 284 | 285 | @end 286 | 287 | @implementation wb_NSMenuItemHook 288 | 289 | // Fix for Spotify crash with menu hidden 290 | - (id)initWithTitle:(NSString *)string action:(SEL)selector keyEquivalent:(NSString *)charCode { 291 | // Avoid crash with nil string value 292 | if (string == nil) string = @""; 293 | return ZKOrig(id, string, selector, charCode); 294 | } 295 | 296 | @end 297 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/w0lfschild/moreMenu/767447100eb1a7299d120e7b3d8fc6e228084c17/preview.gif --------------------------------------------------------------------------------