import AppKit // MARK: - Menu Management class MenuManager { weak var delegate: MenuManagerDelegate? private var statusItem: NSStatusItem? init(delegate: MenuManagerDelegate) { self.delegate = delegate // Add observer for shortcut changes to update menu NotificationCenter.default.addObserver( self, selector: #selector(updateShortcuts), name: .shortcutSettingChanged, object: nil ) // Add observer for desktop icons setting changes NotificationCenter.default.addObserver( self, selector: #selector(handleDesktopIconsSettingChanged), name: .hideDesktopIconsSettingChanged, object: nil ) // Add observer for desktop widgets setting changes NotificationCenter.default.addObserver( self, selector: #selector(handleDesktopWidgetsSettingChanged), name: .hideDesktopWidgetsSettingChanged, object: nil ) } func setupMenu() { setupMainMenu() setupStatusBarMenu() // Set initial shortcuts updateShortcuts() } private func setupMainMenu() { // Create main menu let mainMenu = NSMenu() // Application menu (first menu) let appMenu = NSMenu() let appName = "ShotScreen" // About item with info icon let aboutItem = NSMenuItem(title: "About \(appName)", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: "") if let infoIcon = NSImage(systemSymbolName: "info.circle", accessibilityDescription: "About") { infoIcon.size = NSSize(width: 16, height: 16) aboutItem.image = infoIcon } appMenu.addItem(aboutItem) appMenu.addItem(NSMenuItem.separator()) // Settings item with gear icon let settingsItem = NSMenuItem(title: "Settings", action: #selector(MenuManager.openSettings), keyEquivalent: ",") settingsItem.target = self if let gearIcon = NSImage(systemSymbolName: "gearshape", accessibilityDescription: "Settings") { gearIcon.size = NSSize(width: 16, height: 16) settingsItem.image = gearIcon } appMenu.addItem(settingsItem) // Show Stash item with folder icon let showStashItem = NSMenuItem(title: "Show Stash", action: #selector(MenuManager.showStash), keyEquivalent: "s") showStashItem.target = self if let folderIcon = NSImage(systemSymbolName: "folder", accessibilityDescription: "Folder") { folderIcon.size = NSSize(width: 16, height: 16) showStashItem.image = folderIcon } appMenu.addItem(showStashItem) // NIEUW: Reset First Launch Wizard (for testing) appMenu.addItem(NSMenuItem.separator()) let resetWizardItem = NSMenuItem(title: "Reset First Launch Wizard", action: #selector(MenuManager.resetFirstLaunchWizard), keyEquivalent: "") resetWizardItem.target = self if let resetIcon = NSImage(systemSymbolName: "arrow.clockwise", accessibilityDescription: "Reset") { resetIcon.size = NSSize(width: 16, height: 16) resetWizardItem.image = resetIcon } appMenu.addItem(resetWizardItem) appMenu.addItem(NSMenuItem.separator()) // Quit item with power icon let quitItem = NSMenuItem(title: "Quit \(appName)", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q") if let powerIcon = NSImage(systemSymbolName: "power", accessibilityDescription: "Quit") { powerIcon.size = NSSize(width: 16, height: 16) quitItem.image = powerIcon } appMenu.addItem(quitItem) // Add app menu to main menu let appMenuItem = NSMenuItem(title: appName, action: nil, keyEquivalent: "") appMenuItem.submenu = appMenu mainMenu.addItem(appMenuItem) // Set the menu NSApplication.shared.mainMenu = mainMenu } private func setupStatusBarMenu() { // Create status item in menu bar statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) if let button = statusItem?.button { // Use MenuIcon for status bar instead of AppIcon if let bundle = Bundle.main.url(forResource: "MenuIcon", withExtension: "png", subdirectory: "images"), let menuIcon = NSImage(contentsOf: bundle) { menuIcon.size = NSSize(width: 16, height: 16) button.image = menuIcon } else if let menuIcon = NSImage(named: "MenuIcon") { menuIcon.size = NSSize(width: 16, height: 16) button.image = menuIcon } else if let screenshotIcon = NSImage(systemSymbolName: "camera.aperture", accessibilityDescription: "ShotScreen") { // Fallback to aperture icon if menu icon not available screenshotIcon.size = NSSize(width: 16, height: 16) button.image = screenshotIcon } else { // Final fallback to green dot button.image = NSImage(named: "NSStatusAvailable") } // Debug: Print all status bar items debugPrintStatusBarItems() // Create a menu for the status item let statusMenu = NSMenu() // Take Screenshot menu item with camera icon let screenshotItem = NSMenuItem(title: "Take Screenshot", action: #selector(MenuManager.triggerScreenshot), keyEquivalent: "") screenshotItem.target = self if let cameraIcon = NSImage(systemSymbolName: "camera", accessibilityDescription: "Camera") { cameraIcon.size = NSSize(width: 16, height: 16) screenshotItem.image = cameraIcon } updateScreenshotShortcut(screenshotItem) // Set initial shortcut statusMenu.addItem(screenshotItem) // NIEUW: Capture Whole Screen (Double Hotkey) menu item with monitor icon let wholeScreenItem = NSMenuItem(title: "Capture This Screen", action: #selector(MenuManager.triggerWholeScreenCapture), keyEquivalent: "") wholeScreenItem.target = self if let monitorIcon = NSImage(systemSymbolName: "display", accessibilityDescription: "Monitor") { monitorIcon.size = NSSize(width: 16, height: 16) wholeScreenItem.image = monitorIcon } updateWholeScreenCaptureShortcut(wholeScreenItem) // Set initial shortcut statusMenu.addItem(wholeScreenItem) // NIEUW: Capture All Screens menu item let allScreensItem = NSMenuItem(title: "Capture All Screens", action: #selector(MenuManager.triggerAllScreensCapture), keyEquivalent: "") allScreensItem.target = self if let screensIcon = NSImage(systemSymbolName: "rectangle.3.group", accessibilityDescription: "All Screens") { screensIcon.size = NSSize(width: 16, height: 16) allScreensItem.image = screensIcon } updateAllScreensCaptureShortcut(allScreensItem) // Set initial shortcut statusMenu.addItem(allScreensItem) // NIEUW: Simple Window Capture menu item (native macOS) let windowCaptureItem = NSMenuItem(title: "Capture Window", action: #selector(MenuManager.triggerNativeWindowCapture), keyEquivalent: "") windowCaptureItem.target = self if let windowIcon = NSImage(systemSymbolName: "macwindow", accessibilityDescription: "Window") { windowIcon.size = NSSize(width: 16, height: 16) windowCaptureItem.image = windowIcon } updateWindowCaptureShortcut(windowCaptureItem) // Set initial shortcut display statusMenu.addItem(windowCaptureItem) // Add separator between screenshot actions and other options statusMenu.addItem(NSMenuItem.separator()) // NIEUW: Toggle Desktop Icons menu item let hideIconsItem = NSMenuItem(title: "", action: #selector(MenuManager.toggleDesktopIcons), keyEquivalent: "") hideIconsItem.target = self updateDesktopIconsMenuItem(hideIconsItem) // Set initial title and icon statusMenu.addItem(hideIconsItem) print("โž• Desktop Icons menu item added with initial state") // NIEUW: Toggle Desktop Widgets menu item let hideWidgetsItem = NSMenuItem(title: "", action: #selector(MenuManager.toggleDesktopWidgets), keyEquivalent: "") hideWidgetsItem.target = self updateDesktopWidgetsMenuItem(hideWidgetsItem) // Set initial title and icon statusMenu.addItem(hideWidgetsItem) print("โž• Desktop Widgets menu item added with initial state") // Add separator between screenshot actions and other options statusMenu.addItem(NSMenuItem.separator()) // Show Stash menu item with folder icon let stashItem = NSMenuItem(title: "Show Stash", action: #selector(MenuManager.showStash), keyEquivalent: "") stashItem.target = self if let folderIcon = NSImage(systemSymbolName: "folder", accessibilityDescription: "Folder") { folderIcon.size = NSSize(width: 16, height: 16) stashItem.image = folderIcon } statusMenu.addItem(stashItem) // ๐Ÿ”„ UPDATE: Check for Updates menu item with arrow icon let updatesItem = NSMenuItem(title: "Check for Updates", action: #selector(MenuManager.checkForUpdates), keyEquivalent: "") updatesItem.target = self updatesItem.isEnabled = UpdateManager.shared.isUpdaterAvailable if let updateIcon = NSImage(systemSymbolName: "arrow.clockwise.circle", accessibilityDescription: "Check for Updates") { updateIcon.size = NSSize(width: 16, height: 16) updatesItem.image = updateIcon } statusMenu.addItem(NSMenuItem.separator()) // NIEUW: Reset First Launch Wizard (for testing) - also in status bar menu let resetWizardItem = NSMenuItem(title: "Reset First Launch Wizard", action: #selector(MenuManager.resetFirstLaunchWizard), keyEquivalent: "") resetWizardItem.target = self if let resetIcon = NSImage(systemSymbolName: "arrow.clockwise", accessibilityDescription: "Reset") { resetIcon.size = NSSize(width: 16, height: 16) resetWizardItem.image = resetIcon } statusMenu.addItem(resetWizardItem) statusMenu.addItem(NSMenuItem.separator()) // Settings menu item with gear icon let settingsItem = NSMenuItem(title: "Settings", action: #selector(MenuManager.openSettings), keyEquivalent: "") settingsItem.target = self if let gearIcon = NSImage(systemSymbolName: "gearshape", accessibilityDescription: "Settings") { gearIcon.size = NSSize(width: 16, height: 16) settingsItem.image = gearIcon } statusMenu.addItem(settingsItem) // Quit menu item with power icon let quitItem = NSMenuItem(title: "Quit ShotScreen", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "") if let powerIcon = NSImage(systemSymbolName: "power", accessibilityDescription: "Quit") { powerIcon.size = NSSize(width: 16, height: 16) quitItem.image = powerIcon } statusMenu.addItem(quitItem) statusItem?.menu = statusMenu } } // MARK: - Menu Actions @objc func triggerScreenshot() { delegate?.triggerScreenshot() } @objc func triggerWholeScreenCapture() { delegate?.triggerWholeScreenCapture() } @objc func triggerNativeWindowCapture() { delegate?.triggerNativeWindowCapture() } @objc func triggerAllScreensCapture() { delegate?.triggerAllScreensCapture() } @objc func openSettings() { delegate?.openSettings() } @objc func showStash() { delegate?.showStash() } @objc func resetFirstLaunchWizard() { delegate?.resetFirstLaunchWizard() } @objc func toggleDesktopIcons() { delegate?.toggleDesktopIcons() } @objc func toggleDesktopWidgets() { delegate?.toggleDesktopWidgets() } @objc func checkForUpdates() { delegate?.checkForUpdates() } // MARK: - Dynamic Version Helper private func getDynamicAboutTitle() -> String { let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown" return "ShotScreen v\(version)" } @objc func showAbout() { delegate?.showAbout() } @objc func exitApp() { delegate?.shouldTerminate = true NSApplication.shared.terminate(self) } // MARK: - Menu Item Updates private func updateDesktopIconsMenuItem(_ menuItem: NSMenuItem) { let isHidden = SettingsManager.shared.hideDesktopIconsDuringScreenshot if isHidden { menuItem.title = "Show Desktop Icons" // Try specific icon first, fallback to generic folder icon if let icon = NSImage(systemSymbolName: "folder.badge.minus", accessibilityDescription: "Desktop Icons Hidden") { icon.size = NSSize(width: 16, height: 16) menuItem.image = icon } else if let fallbackIcon = NSImage(systemSymbolName: "folder", accessibilityDescription: "Folder") { fallbackIcon.size = NSSize(width: 16, height: 16) menuItem.image = fallbackIcon } else { menuItem.image = nil } } else { menuItem.title = "Hide Desktop Icons" // Try specific icon first, fallback to generic folder icon if let icon = NSImage(systemSymbolName: "folder.badge.plus", accessibilityDescription: "Desktop Icons Visible") { icon.size = NSSize(width: 16, height: 16) menuItem.image = icon } else if let fallbackIcon = NSImage(systemSymbolName: "folder", accessibilityDescription: "Folder") { fallbackIcon.size = NSSize(width: 16, height: 16) menuItem.image = fallbackIcon } else { menuItem.image = nil } } print("๐Ÿ–ฅ๏ธ Desktop Icons menu item updated: '\(menuItem.title)' (Hidden: \(isHidden))") } private func updateDesktopWidgetsMenuItem(_ menuItem: NSMenuItem) { let isHidden = SettingsManager.shared.hideDesktopWidgetsDuringScreenshot if isHidden { menuItem.title = "Show Desktop Widgets" // Try specific widget icons first, fallback to simpler rectangle icon if let icon = NSImage(systemSymbolName: "rectangle.3.group.bubble", accessibilityDescription: "Desktop Widgets Hidden") { icon.size = NSSize(width: 16, height: 16) menuItem.image = icon } else if let fallbackIcon = NSImage(systemSymbolName: "rectangle.3.group", accessibilityDescription: "Widgets") { fallbackIcon.size = NSSize(width: 16, height: 16) menuItem.image = fallbackIcon } else if let simpleIcon = NSImage(systemSymbolName: "rectangle.grid.2x2", accessibilityDescription: "Grid") { simpleIcon.size = NSSize(width: 16, height: 16) menuItem.image = simpleIcon } else { menuItem.image = nil } } else { menuItem.title = "Hide Desktop Widgets" // Try specific widget icons first, fallback to simpler rectangle icon if let icon = NSImage(systemSymbolName: "rectangle.3.group.bubble.fill", accessibilityDescription: "Desktop Widgets Visible") { icon.size = NSSize(width: 16, height: 16) menuItem.image = icon } else if let fallbackIcon = NSImage(systemSymbolName: "rectangle.3.group.fill", accessibilityDescription: "Widgets") { fallbackIcon.size = NSSize(width: 16, height: 16) menuItem.image = fallbackIcon } else if let simpleIcon = NSImage(systemSymbolName: "rectangle.grid.2x2.fill", accessibilityDescription: "Grid") { simpleIcon.size = NSSize(width: 16, height: 16) menuItem.image = simpleIcon } else { menuItem.image = nil } } print("๐Ÿ“ฑ Desktop Widgets menu item updated: '\(menuItem.title)' (Hidden: \(isHidden))") } // Public method to refresh menu when settings change func refreshDesktopIconsMenuItem() { print("๐Ÿ”„ Refreshing desktop icons menu item...") if let menu = statusItem?.menu { // Find the desktop icons menu item (it should be the one with our action) var foundItem = false for item in menu.items { if item.action == #selector(MenuManager.toggleDesktopIcons) { updateDesktopIconsMenuItem(item) foundItem = true break } } if !foundItem { print("โš ๏ธ Desktop icons menu item not found!") } } else { print("โš ๏ธ Status item menu not available!") } } // Public method to refresh widgets menu when settings change func refreshDesktopWidgetsMenuItem() { print("๐Ÿ”„ Refreshing desktop widgets menu item...") if let menu = statusItem?.menu { // Find the desktop widgets menu item (it should be the one with our action) var foundItem = false for item in menu.items { if item.action == #selector(MenuManager.toggleDesktopWidgets) { updateDesktopWidgetsMenuItem(item) foundItem = true break } } if !foundItem { print("โš ๏ธ Desktop widgets menu item not found!") } } else { print("โš ๏ธ Status item menu not available!") } } // MARK: - Settings Change Handlers @objc private func handleDesktopIconsSettingChanged() { print("๐Ÿ“ข Notification received: Desktop Icons setting changed") DispatchQueue.main.async { [weak self] in self?.refreshDesktopIconsMenuItem() } } @objc private func handleDesktopWidgetsSettingChanged() { print("๐Ÿ“ข Notification received: Desktop Widgets setting changed") DispatchQueue.main.async { [weak self] in self?.refreshDesktopWidgetsMenuItem() } } // MARK: - Shortcut Management @objc func updateShortcuts() { // Update shortcuts in both main menu and status bar menu updateMenuShortcuts() } private func updateMenuShortcuts() { // Update status bar menu shortcuts if let menu = statusItem?.menu { for item in menu.items { if item.action == #selector(MenuManager.triggerScreenshot) { updateScreenshotShortcut(item) } else if item.action == #selector(MenuManager.triggerWholeScreenCapture) { updateWholeScreenCaptureShortcut(item) } else if item.action == #selector(MenuManager.triggerNativeWindowCapture) { updateWindowCaptureShortcut(item) } else if item.action == #selector(MenuManager.triggerAllScreensCapture) { updateAllScreensCaptureShortcut(item) } } } // Update main menu shortcuts if needed updateMainMenuShortcuts() } private func updateMainMenuShortcuts() { // Update shortcuts in the main application menu if let mainMenu = NSApplication.shared.mainMenu { // Find the application menu (first submenu) if let appMenuItem = mainMenu.items.first, let appSubmenu = appMenuItem.submenu { for item in appSubmenu.items { if item.action == #selector(MenuManager.openSettings) { // Settings already has "," shortcut, keep it break } } } } } private func updateScreenshotShortcut(_ menuItem: NSMenuItem) { let settings = SettingsManager.shared if settings.useCustomShortcut && settings.customShortcutModifiers != 0 && settings.customShortcutKey != 0 { // Use custom shortcut - display only, same as others let shortcutText = getShortcutDisplay(modifiers: settings.customShortcutModifiers, keyCode: settings.customShortcutKey) setMenuItemWithRightAlignedShortcut(menuItem, title: "Take Area Screenshot", shortcut: shortcutText) } else { // Use default Cmd+Shift+4 - display only, positioned slightly to the left setMenuItemWithRightAlignedShortcut(menuItem, title: "Take Area Screenshot", shortcut: "โŒ˜โ‡ง4") } // Clear actual key equivalents since these are display-only menuItem.keyEquivalent = "" menuItem.keyEquivalentModifierMask = [] } private func updateWholeScreenCaptureShortcut(_ menuItem: NSMenuItem) { let settings = SettingsManager.shared let baseShortcut: String if settings.useCustomShortcut && settings.customShortcutModifiers != 0 && settings.customShortcutKey != 0 { // Use custom shortcut baseShortcut = getShortcutDisplay(modifiers: settings.customShortcutModifiers, keyCode: settings.customShortcutKey) } else { // Use default shortcut baseShortcut = "โŒ˜โ‡ง4" } // Display shortcut as: 2 x [shortcut] (double hotkey for whole screen) setMenuItemWithRightAlignedShortcut(menuItem, title: "Capture Screen", shortcut: "2 x \(baseShortcut)") // Clear actual key equivalents since these are display-only menuItem.keyEquivalent = "" menuItem.keyEquivalentModifierMask = [] } private func updateWindowCaptureShortcut(_ menuItem: NSMenuItem) { let settings = SettingsManager.shared let baseShortcut: String if settings.useCustomShortcut && settings.customShortcutModifiers != 0 && settings.customShortcutKey != 0 { // Use custom shortcut baseShortcut = getShortcutDisplay(modifiers: settings.customShortcutModifiers, keyCode: settings.customShortcutKey) } else { // Use default shortcut baseShortcut = "โŒ˜โ‡ง4" } // Display shortcut as: [shortcut] โ†’ โฃ (indicating spacebar during selection) setMenuItemWithRightAlignedShortcut(menuItem, title: "Capture Window", shortcut: "\(baseShortcut) โ†’ โฃ") // Clear actual key equivalents since these are display-only menuItem.keyEquivalent = "" menuItem.keyEquivalentModifierMask = [] } private func updateAllScreensCaptureShortcut(_ menuItem: NSMenuItem) { let settings = SettingsManager.shared let baseShortcut: String if settings.useCustomShortcut && settings.customShortcutModifiers != 0 && settings.customShortcutKey != 0 { // Use custom shortcut baseShortcut = getShortcutDisplay(modifiers: settings.customShortcutModifiers, keyCode: settings.customShortcutKey) } else { // Use default shortcut baseShortcut = "โŒ˜โ‡ง4" } // Triple hotkey for all screens capture setMenuItemWithRightAlignedShortcut(menuItem, title: "Capture All Screens", shortcut: "3 x \(baseShortcut)") // Clear actual key equivalents since these are display-only menuItem.keyEquivalent = "" menuItem.keyEquivalentModifierMask = [] } private func setMenuItemWithRightAlignedShortcut(_ menuItem: NSMenuItem, title: String, shortcut: String) { // Create attributed string with proper tab stop for right alignment let attributedTitle = NSMutableAttributedString() // Create paragraph style with right-aligned tab stop let paragraphStyle = NSMutableParagraphStyle() // Calculate the right tab stop position (approximately 250 points from left margin) // This gives us consistent right alignment for all shortcuts let tabStopPosition: CGFloat = 250.0 // Create a right-aligned tab stop let tabStop = NSTextTab(textAlignment: .right, location: tabStopPosition, options: [:]) paragraphStyle.tabStops = [tabStop] // Add the main title with normal styling let titleString = NSAttributedString(string: title, attributes: [ .font: NSFont.menuFont(ofSize: 0), .paragraphStyle: paragraphStyle ]) attributedTitle.append(titleString) // Add tab character to move to the tab stop let tabString = NSAttributedString(string: "\t", attributes: [ .paragraphStyle: paragraphStyle ]) attributedTitle.append(tabString) // Add the shortcut with different styling at the tab stop (right-aligned) let shortcutString = NSAttributedString(string: shortcut, attributes: [ .foregroundColor: NSColor.secondaryLabelColor, .font: NSFont.menuFont(ofSize: 0), .paragraphStyle: paragraphStyle ]) attributedTitle.append(shortcutString) menuItem.attributedTitle = attributedTitle } private func getShortcutDisplay(modifiers: UInt, keyCode: UInt16) -> String { var result = "" // Add modifier symbols in correct order: Control, Option, Shift, Command if modifiers & (1 << 3) != 0 { result += "โŒƒ" } // Control if modifiers & (1 << 2) != 0 { result += "โŒฅ" } // Option if modifiers & (1 << 1) != 0 { result += "โ‡ง" } // Shift if modifiers & (1 << 0) != 0 { result += "โŒ˜" } // Command let keyString = keyCodeToDisplayString(keyCode) result += keyString return result } private func keyCodeToDisplayString(_ keyCode: UInt16) -> String { // Convert keyCode to display string (uppercase for better readability) switch keyCode { case 0: return "A" case 1: return "S" case 2: return "D" case 3: return "F" case 4: return "H" case 5: return "G" case 6: return "Z" case 7: return "X" case 8: return "C" case 9: return "V" case 11: return "B" case 12: return "Q" case 13: return "W" case 14: return "E" case 15: return "R" case 16: return "Y" case 17: return "T" case 18: return "1" case 19: return "2" case 20: return "3" case 21: return "4" case 22: return "6" case 23: return "5" case 24: return "=" case 25: return "9" case 26: return "7" case 27: return "-" case 28: return "8" case 29: return "0" case 30: return "]" case 31: return "O" case 32: return "U" case 33: return "[" case 34: return "I" case 35: return "P" case 37: return "L" case 38: return "J" case 39: return "'" case 40: return "K" case 41: return ";" case 42: return "\\" case 43: return "," case 44: return "/" case 45: return "N" case 46: return "M" case 47: return "." case 50: return "`" default: return "" } } private func getModifierSymbols(_ modifier: UInt) -> String { var symbols: [String] = [] if modifier & (1 << 3) != 0 { symbols.append("โŒƒ") } // Control if modifier & (1 << 2) != 0 { symbols.append("โฃ") } // Option (now Space symbol) if modifier & (1 << 1) != 0 { symbols.append("โ‡ง") } // Shift if modifier & (1 << 0) != 0 { symbols.append("โŒ˜") } // Command return symbols.joined() } // MARK: - License Management func setAppEnabled(_ enabled: Bool) { print("๐Ÿ” LICENSE: Setting app menu enabled state to: \(enabled)") guard let menu = statusItem?.menu else { print("โš ๏ธ LICENSE: Status item menu not available!") return } // Disable/enable screenshot-related menu items for item in menu.items { if item.action == #selector(MenuManager.triggerScreenshot) || item.action == #selector(MenuManager.triggerWholeScreenCapture) || item.action == #selector(MenuManager.triggerNativeWindowCapture) || item.action == #selector(MenuManager.triggerAllScreensCapture) || item.action == #selector(MenuManager.showStash) || item.action == #selector(MenuManager.toggleDesktopIcons) || item.action == #selector(MenuManager.toggleDesktopWidgets) { item.isEnabled = enabled } // Keep Settings, Updates, About, and Quit always enabled } // Update menu icon to indicate license status if !enabled { // Show different icon for trial/expired - use lock icon if let lockIcon = NSImage(systemSymbolName: "lock.fill", accessibilityDescription: "License Required") { lockIcon.size = NSSize(width: 16, height: 16) statusItem?.button?.image = lockIcon } statusItem?.button?.toolTip = "ShotScreen - License Required" } else { // Restore normal menu icon for licensed if let bundle = Bundle.main.url(forResource: "MenuIcon", withExtension: "png", subdirectory: "images"), let menuIcon = NSImage(contentsOf: bundle) { menuIcon.size = NSSize(width: 16, height: 16) statusItem?.button?.image = menuIcon } else if let menuIcon = NSImage(named: "MenuIcon") { menuIcon.size = NSSize(width: 16, height: 16) statusItem?.button?.image = menuIcon } else if let screenshotIcon = NSImage(systemSymbolName: "camera.aperture", accessibilityDescription: "ShotScreen") { screenshotIcon.size = NSSize(width: 16, height: 16) statusItem?.button?.image = screenshotIcon } statusItem?.button?.toolTip = "ShotScreen" } // Clear any title to prevent duplicate icons statusItem?.button?.title = "" } // MARK: - Cleanup func cleanup() { print("๐Ÿงน MenuManager: Cleaning up...") NotificationCenter.default.removeObserver(self) if let item = statusItem { NSStatusBar.system.removeStatusItem(item) print("๐Ÿงน STATUS: Removed status bar item successfully") } statusItem = nil } deinit { print("๐Ÿ—‘๏ธ MenuManager: Deinitializing and cleaning up") cleanup() } private func debugPrintStatusBarItems() { print("๐Ÿ” DEBUG: Checking all status bar items...") // Try to get information about other status bar items let statusBar = NSStatusBar.system print("๐Ÿ” DEBUG: System status bar available: \(statusBar)") // Check if there might be multiple ShotScreen processes running let runningApps = NSWorkspace.shared.runningApplications let shotScreenApps = runningApps.filter { $0.bundleIdentifier?.contains("ShotScreen") == true || $0.localizedName?.contains("ShotScreen") == true } print("๐Ÿ” DEBUG: Found \(shotScreenApps.count) ShotScreen-related processes:") for app in shotScreenApps { print(" - \(app.localizedName ?? "Unknown") (PID: \(app.processIdentifier))") } // Check for screenshot-related apps let screenshotApps = runningApps.filter { let name = $0.localizedName?.lowercased() ?? "" let bundle = $0.bundleIdentifier?.lowercased() ?? "" return name.contains("screenshot") || name.contains("capture") || name.contains("screen") || bundle.contains("screenshot") || bundle.contains("capture") || bundle.contains("screen") } print("๐Ÿ” DEBUG: Found \(screenshotApps.count) screenshot/capture-related apps:") for app in screenshotApps { print(" - \(app.localizedName ?? "Unknown"): \(app.bundleIdentifier ?? "No Bundle ID")") } } } // MARK: - MenuManager Delegate Protocol protocol MenuManagerDelegate: AnyObject { func triggerScreenshot() func triggerWholeScreenCapture() func triggerNativeWindowCapture() func triggerAllScreensCapture() func openSettings() func showStash() func resetFirstLaunchWizard() func toggleDesktopIcons() func toggleDesktopWidgets() func checkForUpdates() func showAbout() var shouldTerminate: Bool { get set } }