Showing preview only (8,030K chars total). Download the full file or copy to clipboard to get everything.
Repository: Codeux/Textual
Branch: master
Commit: 243a6e2c06ad
Files: 910
Total size: 7.4 MB
Directory structure:
gitextract_u26s7vme/
├── .gitignore
├── .gitmodules
├── Common/
│ ├── Documents/
│ │ └── Acknowledgements.docx
│ └── Release DMG.dmgCanvas/
│ └── Disk Image
├── Configurations/
│ ├── Build/
│ │ ├── Code Signing Identity.xcconfig
│ │ ├── Common/
│ │ │ ├── Foundation Debug.xcconfig
│ │ │ ├── Foundation.xcconfig
│ │ │ ├── Preserve Symbols.xcconfig
│ │ │ ├── Textual App.xcconfig
│ │ │ ├── Textual Extensions.xcconfig
│ │ │ ├── Textual.xcconfig
│ │ │ ├── XPC Service - ICL Extensions.xcconfig
│ │ │ ├── XPC Service - ICL.xcconfig
│ │ │ └── XPC Services.xcconfig
│ │ ├── Debug/
│ │ │ ├── Enabled Features.xcconfig
│ │ │ ├── Textual App.xcconfig
│ │ │ ├── Textual Extensions.xcconfig
│ │ │ ├── Textual.xcconfig
│ │ │ ├── XPC Service - ICL Extensions.xcconfig
│ │ │ ├── XPC Service - ICL.xcconfig
│ │ │ └── XPC Services.xcconfig
│ │ ├── README.md
│ │ └── Standard Release/
│ │ ├── Enabled Features.xcconfig
│ │ ├── Textual App.xcconfig
│ │ ├── Textual Extensions.xcconfig
│ │ ├── Textual.xcconfig
│ │ ├── XPC Service - ICL Extensions.xcconfig
│ │ ├── XPC Service - ICL.xcconfig
│ │ └── XPC Services.xcconfig
│ ├── ExportArchiveConfiguration.plist
│ └── Sandbox/
│ └── Inherited.entitlements
├── README.md
├── Sources/
│ ├── App/
│ │ ├── Build Scripts/
│ │ │ ├── BuildExtensions.sh
│ │ │ ├── BuildFrameworks.sh
│ │ │ ├── BuildServices.sh
│ │ │ ├── ExportArchive.sh
│ │ │ ├── MergeSwift.sh
│ │ │ ├── PostprocessSparkle.sh
│ │ │ ├── UpdateFeatureFlags.sh
│ │ │ └── UpdateVersionInfo.sh
│ │ ├── Classes/
│ │ │ ├── Controllers/
│ │ │ │ ├── TXAppearance.m
│ │ │ │ ├── TXApplication.m
│ │ │ │ ├── TXGlobalModels.m
│ │ │ │ ├── TXMasterController.m
│ │ │ │ ├── TXMenuController.m
│ │ │ │ ├── TXSharedApplication.m
│ │ │ │ └── TXWindowController.m
│ │ │ ├── Dialogs/
│ │ │ │ ├── Channel Spotlight/
│ │ │ │ │ ├── TDCChannelSpotlightAppearance.m
│ │ │ │ │ ├── TDCChannelSpotlightController.m
│ │ │ │ │ ├── TDCChannelSpotlightControls.m
│ │ │ │ │ ├── TDCChannelSpotlightSearchResult.m
│ │ │ │ │ └── TDCChannelSpotlightSearchResultsTable.m
│ │ │ │ ├── File Transfers/
│ │ │ │ │ ├── TDCFileTransferDialog.m
│ │ │ │ │ ├── TDCFileTransferDialogTableCell.m
│ │ │ │ │ └── TDCFileTransferDialogTransferController.m
│ │ │ │ ├── License Manager/
│ │ │ │ │ └── Standalone/
│ │ │ │ │ ├── TDCLicenseManagerDialog.m
│ │ │ │ │ ├── TDCLicenseManagerMigrateAppStoreSheet.m
│ │ │ │ │ ├── TDCLicenseManagerRecoverLostLicenseSheet.m
│ │ │ │ │ ├── TDCLicenseUpgradeActivateSheet.m
│ │ │ │ │ ├── TDCLicenseUpgradeCommonActions.m
│ │ │ │ │ ├── TDCLicenseUpgradeDialog.m
│ │ │ │ │ └── TDCLicenseUpgradeEligibilitySheet.m
│ │ │ │ ├── Preferences/
│ │ │ │ │ ├── TDCPreferencesController.m
│ │ │ │ │ ├── TDCPreferencesNotificationConfiguration.m
│ │ │ │ │ └── TDCPreferencesUserStyleSheet.m
│ │ │ │ ├── Server Endpoint/
│ │ │ │ │ ├── TDCServerEndpointListSheet.m
│ │ │ │ │ └── TDCServerEndpointListSheetTable.m
│ │ │ │ ├── TDCAboutDialog.m
│ │ │ │ ├── TDCAddressBookSheet.m
│ │ │ │ ├── TDCAlert.m
│ │ │ │ ├── TDCChannelBanListSheet.m
│ │ │ │ ├── TDCChannelInviteSheet.m
│ │ │ │ ├── TDCChannelModifyModesSheet.m
│ │ │ │ ├── TDCChannelModifyTopicSheet.m
│ │ │ │ ├── TDCChannelPropertiesNotificationConfiguration.m
│ │ │ │ ├── TDCChannelPropertiesSheet.m
│ │ │ │ ├── TDCHighlightEntrySheet.m
│ │ │ │ ├── TDCInputPrompt.m
│ │ │ │ ├── TDCNicknameColorSheet.m
│ │ │ │ ├── TDCProgressIndicatorSheet.m
│ │ │ │ ├── TDCServerChangeNicknameSheet.m
│ │ │ │ ├── TDCServerChannelListDialog.m
│ │ │ │ ├── TDCServerHighlightListSheet.m
│ │ │ │ ├── TDCServerPropertiesSheet.m
│ │ │ │ ├── TDCSheetBase.m
│ │ │ │ ├── TDCWelcomeSheet.m
│ │ │ │ └── TDCWindowBase.m
│ │ │ ├── Headers/
│ │ │ │ ├── External Libraries/
│ │ │ │ │ ├── GTMEncodeHTML.h
│ │ │ │ │ └── OELReachability.h
│ │ │ │ ├── IRC.h
│ │ │ │ ├── IRCAddressBook.h
│ │ │ │ ├── IRCAddressBookUserTracking.h
│ │ │ │ ├── IRCChannel.h
│ │ │ │ ├── IRCChannelConfig.h
│ │ │ │ ├── IRCChannelMemberList.h
│ │ │ │ ├── IRCChannelMode.h
│ │ │ │ ├── IRCChannelUser.h
│ │ │ │ ├── IRCClient.h
│ │ │ │ ├── IRCClientConfig.h
│ │ │ │ ├── IRCColorFormat.h
│ │ │ │ ├── IRCCommandIndex.h
│ │ │ │ ├── IRCConnection.h
│ │ │ │ ├── IRCHighlightLogEntry.h
│ │ │ │ ├── IRCHighlightMatchCondition.h
│ │ │ │ ├── IRCISupportInfo.h
│ │ │ │ ├── IRCMessage.h
│ │ │ │ ├── IRCModeInfo.h
│ │ │ │ ├── IRCNetworkList.h
│ │ │ │ ├── IRCNumerics.h
│ │ │ │ ├── IRCPrefix.h
│ │ │ │ ├── IRCSendingMessage.h
│ │ │ │ ├── IRCServer.h
│ │ │ │ ├── IRCTreeItem.h
│ │ │ │ ├── IRCUser.h
│ │ │ │ ├── IRCUserRelations.h
│ │ │ │ ├── IRCWorld.h
│ │ │ │ ├── Internal/
│ │ │ │ │ ├── IRCAddressBookInternal.h
│ │ │ │ │ ├── IRCChannelConfigInternal.h
│ │ │ │ │ ├── IRCChannelUserInternal.h
│ │ │ │ │ ├── IRCClientConfigInternal.h
│ │ │ │ │ ├── IRCHighlightLogEntryInternal.h
│ │ │ │ │ ├── IRCHighlightMatchConditionInternal.h
│ │ │ │ │ ├── IRCMessageInternal.h
│ │ │ │ │ ├── IRCModeInfoInternal.h
│ │ │ │ │ ├── IRCPrefixInternal.h
│ │ │ │ │ ├── IRCServerInternal.h
│ │ │ │ │ ├── IRCUserInternal.h
│ │ │ │ │ ├── TDCChannelPropertiesSheetInternal.h
│ │ │ │ │ ├── TDCChannelSpotlightAppearanceInternal.h
│ │ │ │ │ ├── TDCChannelSpotlightControllerInternal.h
│ │ │ │ │ ├── TDCFileTransferDialogInternal.h
│ │ │ │ │ └── TVCLogLineInternal.h
│ │ │ │ ├── NSColorHelper.h
│ │ │ │ ├── NSStringHelper.h
│ │ │ │ ├── NSViewHelper.h
│ │ │ │ ├── Private/
│ │ │ │ │ ├── ICLPayloadLocalPrivate.h
│ │ │ │ │ ├── IRCAddressBookMatchCachePrivate.h
│ │ │ │ │ ├── IRCAddressBookUserTrackingPrivate.h
│ │ │ │ │ ├── IRCChannelConfigPrivate.h
│ │ │ │ │ ├── IRCChannelMemberListControllerPrivate.h
│ │ │ │ │ ├── IRCChannelMemberListPrivate.h
│ │ │ │ │ ├── IRCChannelModePrivate.h
│ │ │ │ │ ├── IRCChannelPrivate.h
│ │ │ │ │ ├── IRCChannelUserPrivate.h
│ │ │ │ │ ├── IRCClientConfigPrivate.h
│ │ │ │ │ ├── IRCClientPrivate.h
│ │ │ │ │ ├── IRCClientRequestedCommandsPrivate.h
│ │ │ │ │ ├── IRCColorFormatPrivate.h
│ │ │ │ │ ├── IRCCommandIndexPrivate.h
│ │ │ │ │ ├── IRCConnectionPrivate.h
│ │ │ │ │ ├── IRCExtrasPrivate.h
│ │ │ │ │ ├── IRCHighlightLogEntryPrivate.h
│ │ │ │ │ ├── IRCISupportInfoPrivate.h
│ │ │ │ │ ├── IRCMessageBatchPrivate.h
│ │ │ │ │ ├── IRCMessagePrivate.h
│ │ │ │ │ ├── IRCServerPrivate.h
│ │ │ │ │ ├── IRCTimerCommandPrivate.h
│ │ │ │ │ ├── IRCTreeItemPrivate.h
│ │ │ │ │ ├── IRCUserNicknameColorStyleGeneratorPrivate.h
│ │ │ │ │ ├── IRCUserPersistentStorePrivate.h
│ │ │ │ │ ├── IRCUserPrivate.h
│ │ │ │ │ ├── IRCUserRelationsPrivate.h
│ │ │ │ │ ├── IRCWorldPrivate.h
│ │ │ │ │ ├── NSTableVIewHelperPrivate.h
│ │ │ │ │ ├── NSViewHelperPrivate.h
│ │ │ │ │ ├── SwiftBridgingHeaderPrivate.h
│ │ │ │ │ ├── TDCAboutDialogPrivate.h
│ │ │ │ │ ├── TDCAddressBookSheetPrivate.h
│ │ │ │ │ ├── TDCChannelBanListSheetPrivate.h
│ │ │ │ │ ├── TDCChannelInviteSheetPrivate.h
│ │ │ │ │ ├── TDCChannelModifyModesSheetPrivate.h
│ │ │ │ │ ├── TDCChannelModifyTopicSheetPrivate.h
│ │ │ │ │ ├── TDCChannelPropertiesNotificationConfigurationPrivate.h
│ │ │ │ │ ├── TDCChannelPropertiesSheetPrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightAppearancePrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightControllerPrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightControlsPrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightSearchResultPrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightSearchResultsTablePrivate.h
│ │ │ │ │ ├── TDCFileTransferDialogPrivate.h
│ │ │ │ │ ├── TDCFileTransferDialogTableCellPrivate.h
│ │ │ │ │ ├── TDCFileTransferDialogTransferControllerPrivate.h
│ │ │ │ │ ├── TDCHighlightEntrySheetPrivate.h
│ │ │ │ │ ├── TDCLicenseManagerDialogPrivate.h
│ │ │ │ │ ├── TDCLicenseManagerMigrateAppStoreSheetPrivate.h
│ │ │ │ │ ├── TDCLicenseManagerRecoverLostLicenseSheetPrivate.h
│ │ │ │ │ ├── TDCLicenseUpgradeActivateSheetPrivate.h
│ │ │ │ │ ├── TDCLicenseUpgradeCommonActionsPrivate.h
│ │ │ │ │ ├── TDCLicenseUpgradeDialogPrivate.h
│ │ │ │ │ ├── TDCLicenseUpgradeEligibilitySheetPrivate.h
│ │ │ │ │ ├── TDCNicknameColorSheetPrivate.h
│ │ │ │ │ ├── TDCPreferencesControllerPrivate.h
│ │ │ │ │ ├── TDCPreferencesNotificationConfigurationPrivate.h
│ │ │ │ │ ├── TDCPreferencesUserStyleSheetPrivate.h
│ │ │ │ │ ├── TDCProgressIndicatorSheetPrivate.h
│ │ │ │ │ ├── TDCServerChangeNicknameSheetPrivate.h
│ │ │ │ │ ├── TDCServerChannelListDialogPrivate.h
│ │ │ │ │ ├── TDCServerEndpointListSheetPrivate.h
│ │ │ │ │ ├── TDCServerEndpointListSheetTablePrivate.h
│ │ │ │ │ ├── TDCServerHighlightListSheetPrivate.h
│ │ │ │ │ ├── TDCServerPropertiesSheetPrivate.h
│ │ │ │ │ ├── TDCSharedProtocolDefinitionsPrivate.h
│ │ │ │ │ ├── TDCWelcomeSheetPrivate.h
│ │ │ │ │ ├── THOPluginDispatcherPrivate.h
│ │ │ │ │ ├── THOPluginItemPrivate.h
│ │ │ │ │ ├── THOPluginManagerPrivate.h
│ │ │ │ │ ├── THOPluginProtocolPrivate.h
│ │ │ │ │ ├── TLOEncryptionManagerPrivate.h
│ │ │ │ │ ├── TLOFileLoggerPrivate.h
│ │ │ │ │ ├── TLOInputHistoryPrivate.h
│ │ │ │ │ ├── TLOLicenseManagerDownloaderPrivate.h
│ │ │ │ │ ├── TLOLicenseManagerLastGenPrivate.h
│ │ │ │ │ ├── TLOLicenseManagerPrivate.h
│ │ │ │ │ ├── TLONicknameCompletionStatusPrivate.h
│ │ │ │ │ ├── TLONotificationConfigurationPrivate.h
│ │ │ │ │ ├── TLONotificationControllerPrivate.h
│ │ │ │ │ ├── TLOSpeechSynthesizerPrivate.h
│ │ │ │ │ ├── TLOSpokenNotificationPrivate.h
│ │ │ │ │ ├── TPCApplicationInfoPrivate.h
│ │ │ │ │ ├── TPCPathInfoPrivate.h
│ │ │ │ │ ├── TPCPreferencesImportExportPrivate.h
│ │ │ │ │ ├── TPCPreferencesLocalPrivate.h
│ │ │ │ │ ├── TPCResourceManagerPrivate.h
│ │ │ │ │ ├── TPCSandboxMigrationPrivate.h
│ │ │ │ │ ├── TPCThemeControllerPrivate.h
│ │ │ │ │ ├── TPCThemePrivate.h
│ │ │ │ │ ├── TVCAppearancePrivate.h
│ │ │ │ │ ├── TVCChannelSelectionOutlineViewCellPrivate.h
│ │ │ │ │ ├── TVCChannelSelectionViewControllerPrivate.h
│ │ │ │ │ ├── TVCContentNavigationOutlineViewPrivate.h
│ │ │ │ │ ├── TVCDockIconPrivate.h
│ │ │ │ │ ├── TVCErrorMessagePopoverControllerPrivate.h
│ │ │ │ │ ├── TVCErrorMessagePopoverPrivate.h
│ │ │ │ │ ├── TVCLogControllerHistoricLogFilePrivate.h
│ │ │ │ │ ├── TVCLogControllerInlineMediaServicePrivate.h
│ │ │ │ │ ├── TVCLogControllerOperationQueuePrivate.h
│ │ │ │ │ ├── TVCLogControllerPrivate.h
│ │ │ │ │ ├── TVCLogLinePrivate.h
│ │ │ │ │ ├── TVCLogPolicyPrivate.h
│ │ │ │ │ ├── TVCLogScriptEventSinkPrivate.h
│ │ │ │ │ ├── TVCLogViewInternalWK1.h
│ │ │ │ │ ├── TVCLogViewInternalWK2.h
│ │ │ │ │ ├── TVCLogViewPrivate.h
│ │ │ │ │ ├── TVCMainWindowAppearancePrivate.h
│ │ │ │ │ ├── TVCMainWindowChannelViewPrivate.h
│ │ │ │ │ ├── TVCMainWindowLoadingScreenPrivate.h
│ │ │ │ │ ├── TVCMainWindowPrivate.h
│ │ │ │ │ ├── TVCMainWindowSegmentedControlPrivate.h
│ │ │ │ │ ├── TVCMainWindowSplitViewPrivate.h
│ │ │ │ │ ├── TVCMainWindowTextViewAppearancePrivate.h
│ │ │ │ │ ├── TVCMainWindowTextViewPrivate.h
│ │ │ │ │ ├── TVCMainWindowTitlebarAccessoryViewPrivate.h
│ │ │ │ │ ├── TVCMemberListAppearancePrivate.h
│ │ │ │ │ ├── TVCMemberListCellPrivate.h
│ │ │ │ │ ├── TVCMemberListPrivate.h
│ │ │ │ │ ├── TVCMemberListUserInfoPopoverPrivate.h
│ │ │ │ │ ├── TVCNotificationConfigurationViewControllerPrivate.h
│ │ │ │ │ ├── TVCServerListAppearancePrivate.h
│ │ │ │ │ ├── TVCServerListCellPrivate.h
│ │ │ │ │ ├── TVCServerListPrivate.h
│ │ │ │ │ ├── TVCTextFormatterMenuPrivate.h
│ │ │ │ │ ├── TVCTextViewWithIRCFormatterPrivate.h
│ │ │ │ │ ├── TVCWK1AutoScrollerPrivate.h
│ │ │ │ │ ├── TXAppearancePrivate.h
│ │ │ │ │ ├── TXApplicationPrivate.h
│ │ │ │ │ ├── TXGlobalModelsPrivate.h
│ │ │ │ │ ├── TXMasterControllerPrivate.h
│ │ │ │ │ ├── TXMenuControllerPrivate.h
│ │ │ │ │ ├── TXSharedApplicationPrivate.h
│ │ │ │ │ ├── TXWindowControllerPrivate.h
│ │ │ │ │ ├── TextualPrivate.h
│ │ │ │ │ ├── WKWebViewPrivate.h
│ │ │ │ │ └── WebScriptObjectHelperPrivate.h
│ │ │ │ ├── TDCAlert.h
│ │ │ │ ├── TDCInputPrompt.h
│ │ │ │ ├── TDCSheetBase.h
│ │ │ │ ├── TDCWindowBase.h
│ │ │ │ ├── THOPluginManager.h
│ │ │ │ ├── THOPluginProtocol.h
│ │ │ │ ├── THOUnicodeHelper.h
│ │ │ │ ├── TLOEncryptionManager.h
│ │ │ │ ├── TLOInternetAddressLookup.h
│ │ │ │ ├── TLOKeyEventHandler.h
│ │ │ │ ├── TLOLinkParser.h
│ │ │ │ ├── TLONotificationController.h
│ │ │ │ ├── TLOSoundPlayer.h
│ │ │ │ ├── TLOpenLink.h
│ │ │ │ ├── TPCApplicationInfo.h
│ │ │ │ ├── TPCPathInfo.h
│ │ │ │ ├── TPCPreferencesImportExport.h
│ │ │ │ ├── TPCPreferencesLocal.h
│ │ │ │ ├── TPCPreferencesReload.h
│ │ │ │ ├── TPCPreferencesUserDefaultsLocal.h
│ │ │ │ ├── TPCResourceManager.h
│ │ │ │ ├── TPCTheme.h
│ │ │ │ ├── TPCThemeController.h
│ │ │ │ ├── TVCAlert.h
│ │ │ │ ├── TVCAppearance.h
│ │ │ │ ├── TVCAutoExpandingTextField.h
│ │ │ │ ├── TVCAutoExpandingTokenField.h
│ │ │ │ ├── TVCBasicTableView.h
│ │ │ │ ├── TVCChannelSelectionViewController.h
│ │ │ │ ├── TVCLogController.h
│ │ │ │ ├── TVCLogLine.h
│ │ │ │ ├── TVCLogRenderer.h
│ │ │ │ ├── TVCLogView.h
│ │ │ │ ├── TVCMainWindow.h
│ │ │ │ ├── TVCMainWindowAppearance.h
│ │ │ │ ├── TVCMainWindowLoadingScreen.h
│ │ │ │ ├── TVCMainWindowSplitView.h
│ │ │ │ ├── TVCMainWindowTextView.h
│ │ │ │ ├── TVCMainWindowTextViewAppearance.h
│ │ │ │ ├── TVCMemberList.h
│ │ │ │ ├── TVCMemberListAppearance.h
│ │ │ │ ├── TVCServerList.h
│ │ │ │ ├── TVCServerListAppearance.h
│ │ │ │ ├── TVCTextViewWithIRCFormatter.h
│ │ │ │ ├── TVCValidatedComboBox.h
│ │ │ │ ├── TVCValidatedTextField.h
│ │ │ │ ├── TXAppearance.h
│ │ │ │ ├── TXAppearanceHelper.h
│ │ │ │ ├── TXGlobalModels.h
│ │ │ │ ├── TXMasterController.h
│ │ │ │ ├── TXMenuController.h
│ │ │ │ ├── TXSharedApplication.h
│ │ │ │ ├── Textual.h
│ │ │ │ └── TextualApplication.h
│ │ │ ├── Helpers/
│ │ │ │ ├── Cocoa (Objective-C)/
│ │ │ │ │ ├── NSColorHelper.m
│ │ │ │ │ ├── NSStringHelper.m
│ │ │ │ │ ├── NSTableViewHelper.m
│ │ │ │ │ ├── NSViewHelper.m
│ │ │ │ │ └── TXAppearanceHelper.m
│ │ │ │ ├── External Libraries/
│ │ │ │ │ ├── Google/
│ │ │ │ │ │ └── GTMEncodeHTML.m
│ │ │ │ │ └── WebKit/
│ │ │ │ │ └── WebScriptObjectHelper.m
│ │ │ │ ├── Plugin Architecture/
│ │ │ │ │ ├── THOPluginDispatcher.m
│ │ │ │ │ ├── THOPluginItem.m
│ │ │ │ │ ├── THOPluginItemLogging.m
│ │ │ │ │ └── THOPluginManager.m
│ │ │ │ └── THOUnicodeHelper.m
│ │ │ ├── IRC/
│ │ │ │ ├── IRCAddressBook.m
│ │ │ │ ├── IRCAddressBookMatchCache.m
│ │ │ │ ├── IRCAddressBookUserTracking.m
│ │ │ │ ├── IRCChannel.m
│ │ │ │ ├── IRCChannelConfig.m
│ │ │ │ ├── IRCChannelMemberList.m
│ │ │ │ ├── IRCChannelMemberListController.m
│ │ │ │ ├── IRCChannelMode.m
│ │ │ │ ├── IRCClient.m
│ │ │ │ ├── IRCClientConfig.m
│ │ │ │ ├── IRCClientRequestedCommands.m
│ │ │ │ ├── IRCCommandIndex.m
│ │ │ │ ├── IRCConnection.m
│ │ │ │ ├── IRCExtras.m
│ │ │ │ ├── IRCHighlightLogEntry.m
│ │ │ │ ├── IRCHighlightMatchCondition.m
│ │ │ │ ├── IRCISupportInfo.m
│ │ │ │ ├── IRCMessage.m
│ │ │ │ ├── IRCMessageBatch.m
│ │ │ │ ├── IRCModeInfo.m
│ │ │ │ ├── IRCNetworkList.m
│ │ │ │ ├── IRCPrefix.m
│ │ │ │ ├── IRCSendingMessage.m
│ │ │ │ ├── IRCServer.m
│ │ │ │ ├── IRCTimerCommand.m
│ │ │ │ ├── IRCTreeItem.m
│ │ │ │ ├── IRCWorld.m
│ │ │ │ └── Users/
│ │ │ │ ├── IRCChannelUser.m
│ │ │ │ ├── IRCUser.m
│ │ │ │ ├── IRCUserNicknameColorStyleGenerator.m
│ │ │ │ ├── IRCUserPersistentStore.m
│ │ │ │ └── IRCUserRelations.m
│ │ │ ├── Library/
│ │ │ │ ├── Color Formatting/
│ │ │ │ │ └── IRCColorFormat.m
│ │ │ │ ├── External Libraries/
│ │ │ │ │ └── Sockets/
│ │ │ │ │ └── OELReachability.m
│ │ │ │ ├── License Manager/
│ │ │ │ │ └── Standalone/
│ │ │ │ │ ├── TLOLicenseManager.m
│ │ │ │ │ ├── TLOLicenseManagerDownloader.m
│ │ │ │ │ └── TLOLicenseManagerLastGen.m
│ │ │ │ ├── TLOEncryptionManager.m
│ │ │ │ ├── TLOFileLogger.m
│ │ │ │ ├── TLOInputHistory.m
│ │ │ │ ├── TLOInternetAddressLookup.m
│ │ │ │ ├── TLOKeyEventHandler.m
│ │ │ │ ├── TLOLinkParser.swift
│ │ │ │ ├── TLONicknameCompletionStatus.m
│ │ │ │ ├── TLONotificationConfiguration.m
│ │ │ │ ├── TLONotificationController.m
│ │ │ │ ├── TLOSoundPlayer.m
│ │ │ │ ├── TLOSpeechSynthesizer.m
│ │ │ │ ├── TLOSpokenNotification.m
│ │ │ │ └── TLOpenLink.swift
│ │ │ ├── Others/
│ │ │ │ └── main.m
│ │ │ ├── Preferences/
│ │ │ │ ├── TPCApplicationInfo.m
│ │ │ │ ├── TPCPathInfo.m
│ │ │ │ ├── TPCPreferencesImportExport.m
│ │ │ │ ├── TPCPreferencesLocal.m
│ │ │ │ ├── TPCPreferencesReload.m
│ │ │ │ ├── TPCPreferencesUserDefaultsLocal.m
│ │ │ │ ├── TPCResourceManager.m
│ │ │ │ ├── TPCSandboxMigration.m
│ │ │ │ └── Themes/
│ │ │ │ ├── TPCTheme.m
│ │ │ │ └── TPCThemeController.m
│ │ │ ├── Services/
│ │ │ │ └── ICLPayloadLocal.m
│ │ │ └── Views/
│ │ │ ├── Channel Selection Table/
│ │ │ │ ├── TVCChannelSelectionOutlineCellView.m
│ │ │ │ └── TVCChannelSelectionViewController.m
│ │ │ ├── Channel View/
│ │ │ │ ├── Extras/
│ │ │ │ │ ├── TVCLogControllerHistoricLogFile.m
│ │ │ │ │ ├── TVCLogControllerInlineMediaService.m
│ │ │ │ │ └── TVCLogControllerOperationQueue.m
│ │ │ │ ├── TVCLogController.m
│ │ │ │ ├── TVCLogLine.m
│ │ │ │ ├── TVCLogPolicy.m
│ │ │ │ ├── TVCLogRenderer.m
│ │ │ │ ├── TVCLogScriptEventSink.m
│ │ │ │ ├── TVCLogView.m
│ │ │ │ ├── TVCLogViewInternalWK1.m
│ │ │ │ ├── TVCLogViewInternalWK2.m
│ │ │ │ └── TVCWK1AutoScroller.m
│ │ │ ├── Errors/
│ │ │ │ ├── TVCAlert.m
│ │ │ │ ├── TVCErrorMessagePopover.m
│ │ │ │ └── TVCErrorMessagePopoverController.m
│ │ │ ├── Input Text Field/
│ │ │ │ ├── TVCAutoExpandingTextField.m
│ │ │ │ ├── TVCAutoExpandingTokenField.m
│ │ │ │ ├── TVCTextFormatterMenu.m
│ │ │ │ ├── TVCTextViewWithIRCFormatter.m
│ │ │ │ ├── TVCValidatedComboBox.m
│ │ │ │ └── TVCValidatedTextField.m
│ │ │ ├── Main Window/
│ │ │ │ ├── TVCMainWindow.m
│ │ │ │ ├── TVCMainWindowAppearance.m
│ │ │ │ ├── TVCMainWindowChannelView.m
│ │ │ │ ├── TVCMainWindowLoadingScreen.m
│ │ │ │ ├── TVCMainWindowSegmentedControl.m
│ │ │ │ ├── TVCMainWindowSplitView.m
│ │ │ │ ├── TVCMainWindowTextView.m
│ │ │ │ ├── TVCMainWindowTextViewAppearance.m
│ │ │ │ └── TVCMainWindowTitlebarAccessoryView.m
│ │ │ ├── Preferences/
│ │ │ │ └── TVCNotificationConfigurationViewController.m
│ │ │ ├── Server List/
│ │ │ │ ├── TVCServerList.m
│ │ │ │ ├── TVCServerListAppearance.m
│ │ │ │ └── TVCServerListCell.m
│ │ │ ├── TVCAppearance.m
│ │ │ ├── TVCBasicTableView.m
│ │ │ ├── TVCContentNavigationOutlineView.m
│ │ │ ├── TVCDockIcon.m
│ │ │ └── User List/
│ │ │ ├── TVCMemberList.m
│ │ │ ├── TVCMemberListAppearance.m
│ │ │ ├── TVCMemberListCell.m
│ │ │ └── TVCMemberListUserInfoPopover.m
│ │ ├── Configurations/
│ │ │ └── Sandbox/
│ │ │ ├── Debug.entitlements
│ │ │ ├── Sandbox Disabled.entitlements
│ │ │ └── Standard Release.entitlements
│ │ ├── Resources/
│ │ │ ├── Images/
│ │ │ │ ├── Application/
│ │ │ │ │ ├── applicationIcon.icns
│ │ │ │ │ ├── applicationIcon.psd
│ │ │ │ │ └── applicationIconBirthday.icns
│ │ │ │ ├── Copyright Information for Images.txt
│ │ │ │ ├── Dock Icon Badges/
│ │ │ │ │ └── Photoshop/
│ │ │ │ │ ├── Mavericks/
│ │ │ │ │ │ ├── blueDockBadge.psd
│ │ │ │ │ │ ├── blueDockBadgeWithShadow.psd
│ │ │ │ │ │ ├── redDockBadge.psd
│ │ │ │ │ │ └── redDockBadgeWithShadow.psd
│ │ │ │ │ └── Yosemite/
│ │ │ │ │ └── redDockBadge.psd
│ │ │ │ ├── Encryption Badges/
│ │ │ │ │ ├── encryptionLockIconDark.tif
│ │ │ │ │ ├── encryptionLockIconDark@2x.tif
│ │ │ │ │ ├── encryptionLockIconLight.tif
│ │ │ │ │ └── encryptionLockIconLight@2x.tif
│ │ │ │ ├── IRC Formatting Colors/
│ │ │ │ │ ├── FormattingColor_Rainbow.tif
│ │ │ │ │ └── FormattingColor_Rainbow@2x.tif
│ │ │ │ ├── Status Badges/
│ │ │ │ │ ├── channelRoomStatusIconDarkActive.tif
│ │ │ │ │ ├── channelRoomStatusIconDarkActive@2x.tif
│ │ │ │ │ ├── channelRoomStatusIconDarkInactive.tif
│ │ │ │ │ ├── channelRoomStatusIconDarkInactive@2x.tif
│ │ │ │ │ ├── channelRoomStatusIconLightActive.tif
│ │ │ │ │ ├── channelRoomStatusIconLightActive@2x.tif
│ │ │ │ │ ├── channelRoomStatusIconLightInactive.tif
│ │ │ │ │ ├── channelRoomStatusIconLightInactive@2x.tif
│ │ │ │ │ ├── channelRoomStatusIconMavericksDark.psd
│ │ │ │ │ └── channelRoomStatusIconMavericksLight.psd
│ │ │ │ └── User Interface/
│ │ │ │ ├── Miscellaneous/
│ │ │ │ │ └── ErroneousTextFieldValueIndicator.tif
│ │ │ │ └── Server List/
│ │ │ │ └── Glass/
│ │ │ │ ├── VibrantDarkServerListViewPrivateMessageUserIconActive.tif
│ │ │ │ ├── VibrantDarkServerListViewPrivateMessageUserIconActive@2x.tif
│ │ │ │ ├── VibrantDarkServerListViewPrivateMessageUserIconInactive.tif
│ │ │ │ ├── VibrantDarkServerListViewPrivateMessageUserIconInactive@2x.tif
│ │ │ │ ├── VibrantLightServerListViewPrivateMessageUserIconActive.tif
│ │ │ │ ├── VibrantLightServerListViewPrivateMessageUserIconActive@2x.tif
│ │ │ │ ├── VibrantLightServerListViewPrivateMessageUserIconInactive.tif
│ │ │ │ └── VibrantLightServerListViewPrivateMessageUserIconInactive@2x.tif
│ │ │ ├── Language Files/
│ │ │ │ ├── _randomToken
│ │ │ │ └── en.lproj/
│ │ │ │ ├── Accessibility.strings
│ │ │ │ ├── BasicLanguage.strings
│ │ │ │ ├── CommonErrors.strings
│ │ │ │ ├── IRC.strings
│ │ │ │ ├── Notifications.strings
│ │ │ │ ├── OffTheRecord.strings
│ │ │ │ ├── Prompts.strings
│ │ │ │ ├── TDCAboutDialog.strings
│ │ │ │ ├── TDCAddressBookSheet.strings
│ │ │ │ ├── TDCChannelBanListSheet.strings
│ │ │ │ ├── TDCChannelInviteSheet.strings
│ │ │ │ ├── TDCChannelModifyModesSheet.strings
│ │ │ │ ├── TDCChannelModifyTopicSheet.strings
│ │ │ │ ├── TDCChannelPropertiesSheet.strings
│ │ │ │ ├── TDCChannelSpotlightController.strings
│ │ │ │ ├── TDCFileTransferDialog.strings
│ │ │ │ ├── TDCLicenseUpgradeEligibilitySheet.strings
│ │ │ │ ├── TDCPreferencesController.strings
│ │ │ │ ├── TDCPreferencesUserStyleSheet.strings
│ │ │ │ ├── TDCServerChannelListDialog.strings
│ │ │ │ ├── TDCServerEndpointListSheet.strings
│ │ │ │ ├── TDCServerPropertiesSheet.strings
│ │ │ │ ├── TLOLicenseManager.strings
│ │ │ │ ├── TVCMainWindow.strings
│ │ │ │ └── TVCNotificationConfigurationView.strings
│ │ │ ├── License Manager/
│ │ │ │ └── RemoteLicenseSystemPublicKey.pub
│ │ │ ├── Property Lists/
│ │ │ │ ├── Application Properties/
│ │ │ │ │ └── Info.plist
│ │ │ │ ├── IRCCommandIndexLocalData.plist
│ │ │ │ ├── IRCCommandIndexRemoteData.plist
│ │ │ │ ├── IRCNetworks.plist
│ │ │ │ ├── Preferences/
│ │ │ │ │ ├── KeysExcludedFromContainer.plist
│ │ │ │ │ ├── KeysExcludedFromExport.plist
│ │ │ │ │ ├── KeysExcludedFromMigrate.plist
│ │ │ │ │ ├── PreferenceKeyMasterList.plist
│ │ │ │ │ ├── RegisteredUserDefaults.plist
│ │ │ │ │ └── RegisteredUserDefaultsInContainer.plist
│ │ │ │ ├── StaticStore.plist
│ │ │ │ └── TemplateLineTypes.plist
│ │ │ ├── Scripting/
│ │ │ │ └── Script Files/
│ │ │ │ ├── Bundled Scripts/
│ │ │ │ │ ├── Installing Custom Scripts.txt
│ │ │ │ │ ├── date.scpt
│ │ │ │ │ └── moti.scpt
│ │ │ │ └── Textual Extras Installer/
│ │ │ │ ├── Create-Installers
│ │ │ │ ├── Installation Files/
│ │ │ │ │ └── Scripts/
│ │ │ │ │ ├── apps.scpt
│ │ │ │ │ ├── banhammer.scpt
│ │ │ │ │ ├── ffuu.scpt
│ │ │ │ │ ├── flip.scpt
│ │ │ │ │ ├── hermes.scpt
│ │ │ │ │ ├── music.scpt
│ │ │ │ │ ├── np.scpt
│ │ │ │ │ ├── o_o.scpt
│ │ │ │ │ ├── page.scpt
│ │ │ │ │ ├── qt.scpt
│ │ │ │ │ ├── radium.scpt
│ │ │ │ │ ├── reverse.scpt
│ │ │ │ │ ├── shell.scpt
│ │ │ │ │ ├── slap.scpt
│ │ │ │ │ ├── spotify.scpt
│ │ │ │ │ └── vlc.scpt
│ │ │ │ ├── Installer Helpers/
│ │ │ │ │ ├── Build-Extension-Package
│ │ │ │ │ ├── Build-Final-Package
│ │ │ │ │ └── Build-Scripts-Package
│ │ │ │ ├── Installer Resources/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ ├── Localizable.strings
│ │ │ │ │ └── Welcome.rtf
│ │ │ │ ├── Packages/
│ │ │ │ │ └── Textual-Extras.pkg
│ │ │ │ └── distribution.plist
│ │ │ ├── Styling/
│ │ │ │ ├── Bundled Styles/
│ │ │ │ │ ├── Astria/
│ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ ├── copyright.txt
│ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Equinox/
│ │ │ │ │ │ ├── LICENSE.txt
│ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Sapientia/
│ │ │ │ │ │ ├── Resources/
│ │ │ │ │ │ │ └── Documentation/
│ │ │ │ │ │ │ └── changelog.txt
│ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ ├── copyright.txt
│ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Simplified/
│ │ │ │ │ │ ├── Varieties/
│ │ │ │ │ │ │ ├── Dark/
│ │ │ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ │ │ └── settings.plist
│ │ │ │ │ │ │ └── Light/
│ │ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ │ └── settings.plist
│ │ │ │ │ │ ├── copyright.txt
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Sulaco/
│ │ │ │ │ │ ├── LICENSE.txt
│ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ ├── newMessagePostedWithSender.mustache
│ │ │ │ │ │ │ └── newMessagePostedWithoutSender.mustache
│ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Tomorrow/
│ │ │ │ │ │ ├── Varieties/
│ │ │ │ │ │ │ ├── Dark/
│ │ │ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ │ │ └── settings.plist
│ │ │ │ │ │ │ └── Light/
│ │ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ │ └── settings.plist
│ │ │ │ │ │ ├── copyright.txt
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ └── Total Sublime/
│ │ │ │ │ ├── Templates/
│ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ ├── copyright.txt
│ │ │ │ │ ├── design.css
│ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ ├── scripts.js
│ │ │ │ │ └── settings.plist
│ │ │ │ ├── JavaScript/
│ │ │ │ │ └── API/
│ │ │ │ │ ├── core.js
│ │ │ │ │ ├── corePrivate.js
│ │ │ │ │ └── private/
│ │ │ │ │ ├── conversationTracking.js
│ │ │ │ │ ├── core/
│ │ │ │ │ │ ├── clickMenuSelection.js
│ │ │ │ │ │ ├── documentBody.js
│ │ │ │ │ │ ├── events.js
│ │ │ │ │ │ ├── inlineMedia.js
│ │ │ │ │ │ ├── messageBuffer.js
│ │ │ │ │ │ └── scrollTo.js
│ │ │ │ │ ├── scriptSink.js
│ │ │ │ │ └── scroller/
│ │ │ │ │ ├── automatic.js
│ │ │ │ │ ├── automaticEmpty.js
│ │ │ │ │ └── state.js
│ │ │ │ └── Style Default Templates/
│ │ │ │ └── Version 4/
│ │ │ │ ├── baseLayout.css
│ │ │ │ ├── baseLayout.mustache
│ │ │ │ ├── dateIndicator.mustache
│ │ │ │ ├── encryptedMessageLock.mustache
│ │ │ │ ├── formattedMessageFragment.mustache
│ │ │ │ ├── historyIndicator.mustache
│ │ │ │ ├── messageBufferLoadingIndicator.mustache
│ │ │ │ ├── messageBufferSessionIndicator.mustache
│ │ │ │ ├── newMessagePostedWithSender.mustache
│ │ │ │ ├── newMessagePostedWithoutSender.mustache
│ │ │ │ ├── renderedChannelNameLinkResource.mustache
│ │ │ │ ├── renderedStandardAnchorLinkResource.mustache
│ │ │ │ └── sessionIndicator.mustache
│ │ │ └── User Interface/
│ │ │ ├── Appearance/
│ │ │ │ ├── TDCChannelSpotlightAppearance.plist
│ │ │ │ ├── TDCChannelSpotlightAppearanceTemplate.plist
│ │ │ │ ├── TVCMainWindowAppearance.plist
│ │ │ │ ├── TVCMainWindowAppearanceTemplate.plist
│ │ │ │ ├── TVCMainWindowTextViewAppearance.plist
│ │ │ │ ├── TVCMainWindowTextViewAppearanceTemplate.plist
│ │ │ │ ├── TVCMemberListAppearance.plist
│ │ │ │ ├── TVCMemberListAppearanceTemplate.plist
│ │ │ │ ├── TVCServerListAppearance.plist
│ │ │ │ └── TVCServerListAppearanceTemplate.plist
│ │ │ └── en.lproj/
│ │ │ ├── TDCAboutDialog.xib
│ │ │ ├── TDCAddressBookSheet.xib
│ │ │ ├── TDCChannelBanListSheet.xib
│ │ │ ├── TDCChannelInviteSheet.xib
│ │ │ ├── TDCChannelModifyModesSheet.xib
│ │ │ ├── TDCChannelModifyTopicSheet.xib
│ │ │ ├── TDCChannelPropertiesSheet.xib
│ │ │ ├── TDCChannelSpotlightController.xib
│ │ │ ├── TDCFileTransferDialog.xib
│ │ │ ├── TDCHighlightEntrySheet.xib
│ │ │ ├── TDCLicenseManagerDialog.xib
│ │ │ ├── TDCLicenseManagerMigrateAppStoreSheet.xib
│ │ │ ├── TDCLicenseManagerRecoverLostLicenseSheet.xib
│ │ │ ├── TDCLicenseUpgradeActivateSheet.xib
│ │ │ ├── TDCLicenseUpgradeDialog.xib
│ │ │ ├── TDCLicenseUpgradeEligibilitySheet.xib
│ │ │ ├── TDCNicknameColorSheet.xib
│ │ │ ├── TDCPreferences.xib
│ │ │ ├── TDCPreferencesUserStyleSheet.xib
│ │ │ ├── TDCProgressIndicatorSheet.xib
│ │ │ ├── TDCServerChangeNicknameSheet.xib
│ │ │ ├── TDCServerChannelListDialog.xib
│ │ │ ├── TDCServerEndpointListSheet.xib
│ │ │ ├── TDCServerHighlightListSheet.xib
│ │ │ ├── TDCServerPropertiesSheet.xib
│ │ │ ├── TDCWelcomeSheet.xib
│ │ │ ├── TVCAlert.xib
│ │ │ ├── TVCChannelSelectionView.xib
│ │ │ ├── TVCMainWindow.xib
│ │ │ ├── TVCNotificationConfigurationView.xib
│ │ │ └── TXCMainMenu.xib
│ │ └── Textual App.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ ├── Textual (App Store).xcscheme
│ │ ├── Textual (Debug).xcscheme
│ │ ├── Textual (Standard Release Sandboxed).xcscheme
│ │ └── Textual (Standard Release).xcscheme
│ ├── Plugins/
│ │ ├── Blowfish Encryption/
│ │ │ ├── ACKNOWLEDGEMENT.txt
│ │ │ ├── Blowfish Encryption Extension.xcodeproj/
│ │ │ │ ├── project.pbxproj
│ │ │ │ └── xcshareddata/
│ │ │ │ └── xcschemes/
│ │ │ │ └── Blowfish Encryption Extension.xcscheme
│ │ │ ├── Classes/
│ │ │ │ ├── BlowfishEncryption.h
│ │ │ │ ├── BlowfishEncryption.m
│ │ │ │ ├── BlowfishEncryptionBase.h
│ │ │ │ ├── BlowfishEncryptionBase.m
│ │ │ │ ├── BlowfishEncryptionKeyExchange.h
│ │ │ │ ├── BlowfishEncryptionKeyExchange.mm
│ │ │ │ ├── BlowfishEncryptionKeyExchangeBase.h
│ │ │ │ ├── BlowfishEncryptionKeyExchangeBase.mm
│ │ │ │ ├── NSDataHelper.h
│ │ │ │ ├── NSDataHelper.m
│ │ │ │ ├── TPIBlowfishEncryption.h
│ │ │ │ ├── TPIBlowfishEncryption.m
│ │ │ │ ├── TPIBlowfishEncryptionSwizzledClasses.h
│ │ │ │ └── TPIBlowfishEncryptionSwizzledClasses.m
│ │ │ ├── LICENSE-GPLv2.txt
│ │ │ ├── LICENSE.txt
│ │ │ └── Resources/
│ │ │ ├── Language Files/
│ │ │ │ └── en.lproj/
│ │ │ │ └── BasicLanguage.strings
│ │ │ ├── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Interface/
│ │ │ └── en.lproj/
│ │ │ └── TPIBlowfishEncryption.xib
│ │ ├── Caffeine/
│ │ │ ├── Caffeine Extension.xcodeproj/
│ │ │ │ ├── project.pbxproj
│ │ │ │ └── xcshareddata/
│ │ │ │ └── xcschemes/
│ │ │ │ └── Caffeine Extension.xcscheme
│ │ │ ├── Classes/
│ │ │ │ ├── TPI_Caffeine.h
│ │ │ │ └── TPI_Caffeine.m
│ │ │ └── Resources/
│ │ │ ├── Language Files/
│ │ │ │ └── en.lproj/
│ │ │ │ └── BasicLanguage.strings
│ │ │ ├── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Interface/
│ │ │ └── en.lproj/
│ │ │ └── TPI_Caffeine.xib
│ │ ├── Chat Filter/
│ │ │ ├── Chat Filter Extension.xcodeproj/
│ │ │ │ └── project.pbxproj
│ │ │ ├── Classes/
│ │ │ │ ├── TPI_ChatFilter.h
│ │ │ │ ├── TPI_ChatFilter.m
│ │ │ │ ├── TPI_ChatFilterEditFilterSheet.h
│ │ │ │ ├── TPI_ChatFilterEditFilterSheet.m
│ │ │ │ ├── TPI_ChatFilterExtension.h
│ │ │ │ ├── TPI_ChatFilterExtension.m
│ │ │ │ ├── TPI_ChatFilterInternal.h
│ │ │ │ ├── TPI_ChatFilterLogic.h
│ │ │ │ ├── TPI_ChatFilterLogic.m
│ │ │ │ ├── TPI_NumberOnlyTextFieldFormatter.h
│ │ │ │ └── TPI_NumberOnlyTextFieldFormatter.m
│ │ │ └── Resources/
│ │ │ ├── Language Files/
│ │ │ │ └── en.lproj/
│ │ │ │ ├── TPI_ChatFilterEditFilterSheet.strings
│ │ │ │ ├── TPI_ChatFilterExtension.strings
│ │ │ │ └── TPI_ChatFilterLogic.strings
│ │ │ ├── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Interface/
│ │ │ └── en.lproj/
│ │ │ ├── TPI_ChatFilterEditFilterSheet.xib
│ │ │ └── TPI_ChatFilterExtension.xib
│ │ ├── Sample Code/
│ │ │ └── Preference Pane/
│ │ │ ├── Classes/
│ │ │ │ ├── TPI_PreferencePaneExample.h
│ │ │ │ └── TPI_PreferencePaneExample.m
│ │ │ ├── PreferencePaneExample.xcodeproj/
│ │ │ │ └── project.pbxproj
│ │ │ └── Resources/
│ │ │ ├── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Interface/
│ │ │ └── PreferencePane.xib
│ │ ├── Smiley Converter/
│ │ │ ├── Classes/
│ │ │ │ ├── TPISmileyConverter.h
│ │ │ │ └── TPISmileyConverter.m
│ │ │ ├── Resources/
│ │ │ │ ├── Language Files/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ └── BasicLanguage.strings
│ │ │ │ ├── Property Lists/
│ │ │ │ │ ├── Info.plist
│ │ │ │ │ ├── conversionTable.plist
│ │ │ │ │ └── conversionTable2.plist
│ │ │ │ └── User Interface/
│ │ │ │ └── en.lproj/
│ │ │ │ └── TPISmileyConverter.xib
│ │ │ └── Smiley Converter Extension.xcodeproj/
│ │ │ └── project.pbxproj
│ │ ├── System Profiler/
│ │ │ ├── Classes/
│ │ │ │ ├── SystemProfiler.h
│ │ │ │ ├── TPISystemProfiler.h
│ │ │ │ ├── TPISystemProfiler.m
│ │ │ │ ├── TPI_SP_SysInfo.h
│ │ │ │ └── TPI_SP_SysInfo.m
│ │ │ ├── Resources/
│ │ │ │ ├── Language Files/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ └── BasicLanguage.strings
│ │ │ │ ├── Property Lists/
│ │ │ │ │ ├── Info.plist
│ │ │ │ │ └── MacintoshModels.plist
│ │ │ │ └── User Interface/
│ │ │ │ └── en.lproj/
│ │ │ │ └── TPISystemProfiler.xib
│ │ │ └── System Profiler Extension.xcodeproj/
│ │ │ └── project.pbxproj
│ │ ├── User Insights/
│ │ │ ├── Classes/
│ │ │ │ ├── TPIUserInsights.h
│ │ │ │ └── TPIUserInsights.m
│ │ │ ├── Resources/
│ │ │ │ ├── Language Files/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ └── BasicLanguage.strings
│ │ │ │ └── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Insights Extension.xcodeproj/
│ │ │ └── project.pbxproj
│ │ ├── Wiki Link Parser/
│ │ │ ├── Classes/
│ │ │ │ ├── TPIWikiStyleLinkParser.h
│ │ │ │ └── TPIWikiStyleLinkParser.m
│ │ │ ├── Resources/
│ │ │ │ ├── Language Files/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ └── BasicLanguage.strings
│ │ │ │ ├── Property Lists/
│ │ │ │ │ └── Info.plist
│ │ │ │ └── User Interface/
│ │ │ │ └── en.lproj/
│ │ │ │ └── TPIWikiStyleLinkParser.xib
│ │ │ └── Wiki-style Link Parser Extension.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ └── xcshareddata/
│ │ │ └── xcschemes/
│ │ │ └── Wiki-style Link Parser Extension.xcscheme
│ │ └── ZNC Additions/
│ │ ├── Classes/
│ │ │ ├── TPI_ZNCAdditions.h
│ │ │ └── TPI_ZNCAdditions.m
│ │ ├── Resources/
│ │ │ ├── Language Files/
│ │ │ │ └── en.lproj/
│ │ │ │ └── BasicLanguage.strings
│ │ │ └── Property Lists/
│ │ │ └── Info.plist
│ │ └── ZNC Additions Extension.xcodeproj/
│ │ └── project.pbxproj
│ └── Shared/
│ ├── Headers/
│ │ ├── External Libraries/
│ │ │ ├── GCDAsyncSocket.h
│ │ │ └── GCDAsyncSocketExtensions.h
│ │ ├── IRCConnectionConfig.h
│ │ ├── IRCConnectionErrors.h
│ │ ├── Internal/
│ │ │ └── IRCConnectionConfigInternal.h
│ │ ├── Private/
│ │ │ ├── NSObjectHelperPrivate.h
│ │ │ ├── TPCPreferencesPrivate.h
│ │ │ ├── TPCPreferencesUserDefaultsPrivate.h
│ │ │ └── TVCLogLineXPCPrivate.h
│ │ ├── StaticDefinitions.h
│ │ ├── TLOLocalization.h
│ │ ├── TLOTimer.h
│ │ ├── TPCPreferences.h
│ │ └── TPCPreferencesUserDefaults.h
│ ├── Helpers/
│ │ └── Cocoa (Objective-C)/
│ │ └── NSObjectHelper.m
│ ├── IRC/
│ │ ├── IRCConnectionConfig.m
│ │ └── IRCConnectionErrors.m
│ ├── Library/
│ │ ├── External Libraries/
│ │ │ └── Sockets/
│ │ │ ├── GCDAsyncSocket-patch.txt
│ │ │ ├── GCDAsyncSocket.m
│ │ │ └── GCDAsyncSocketExtensions.m
│ │ ├── TLOLocalization.m
│ │ ├── TLOLocalization.swift
│ │ └── TLOTimer.m
│ ├── Preferences/
│ │ ├── TPCPreferences.m
│ │ └── TPCPreferencesUserDefaults.m
│ └── Views/
│ └── Channel View/
│ └── TVCLogLineXPC.m
└── XPC Services/
├── Historic Log File Manager/
│ ├── Classes/
│ │ ├── HLSHistoricLogLineEntityMigration.m
│ │ ├── HLSHistoricLogProcessMain.m
│ │ ├── HLSHistoricLogViewContext.m
│ │ ├── HSLHistoricLogProcessDelegate.m
│ │ ├── Headers/
│ │ │ └── Private/
│ │ │ ├── HLSHistoricLogLineEntityMigrationPrivate.h
│ │ │ ├── HLSHistoricLogProcessMainPrivate.h
│ │ │ ├── HLSHistoricLogProtocol.h
│ │ │ ├── HLSHistoricLogViewContextPrivate.h
│ │ │ ├── HSLHistoricLogPCHPrivate.h
│ │ │ └── HSLHistoricLogProcessDelegatePrivate.h
│ │ └── main.m
│ ├── Configurations/
│ │ └── Sandbox/
│ │ └── Release.entitlements
│ ├── Historic Log File Manager.xcodeproj/
│ │ └── project.pbxproj
│ └── Resources/
│ ├── Data Models/
│ │ ├── HistoricLogFileStorageModel.xcdatamodeld/
│ │ │ ├── .xccurrentversion
│ │ │ ├── LogControllerStorageModel (model 2).xcdatamodel/
│ │ │ │ └── contents
│ │ │ └── LogControllerStorageModel (model 3).xcdatamodel/
│ │ │ └── contents
│ │ └── HistoricLogFileStorageModel.xcmappingmodel/
│ │ └── xcmapping.xml
│ └── Property Lists/
│ └── Info.plist
├── IRC Remote Connection Manager/
│ ├── Classes/
│ │ ├── Headers/
│ │ │ └── Private/
│ │ │ ├── IRCConnectionPrivate.h
│ │ │ ├── RCMConnectionManagerProtocol.h
│ │ │ ├── RCMProcessDelegatePrivate.h
│ │ │ ├── RCMProcessMainPrivate.h
│ │ │ ├── RCMProcessPCHPrivate.h
│ │ │ └── SwiftBridgingHeaderPrivate.h
│ │ ├── IRC/
│ │ │ ├── IRCConnection.swift
│ │ │ ├── IRCConnectionSocket.swift
│ │ │ ├── IRCConnectionSocketClassic.swift
│ │ │ └── IRCConnectionSocketNWF.swift
│ │ └── Service/
│ │ ├── RCMProcessDelegate.m
│ │ ├── RCMProcessMain.m
│ │ └── main.m
│ ├── Configurations/
│ │ └── Sandbox/
│ │ └── Release.entitlements
│ ├── IRC Remote Connection Manager.xcodeproj/
│ │ └── project.pbxproj
│ └── Resources/
│ ├── Language Files/
│ │ └── en.lproj/
│ │ └── ConnectionErrors.strings
│ └── Property Lists/
│ └── Info.plist
└── Inline Content Loader/
├── Build Scripts/
│ └── BuildExtensions.sh
├── Classes/
│ ├── Headers/
│ │ ├── ICLHelpers.h
│ │ ├── ICLInlineContentModule.h
│ │ ├── ICLMediaAssessment.h
│ │ ├── ICLMediaAssessor.h
│ │ ├── ICLMediaType.h
│ │ ├── ICLPayload.h
│ │ ├── ICLPluginProtocol.h
│ │ ├── Internal/
│ │ │ ├── ICLInlineContentModuleInternal.h
│ │ │ ├── ICLMediaAssessmentInternal.h
│ │ │ └── ICLPayloadInternal.h
│ │ └── Private/
│ │ ├── CoreModulesImportsPrivate.h
│ │ ├── ICLInlineContentModulePrivate.h
│ │ ├── ICLInlineContentProtocol.h
│ │ ├── ICLPayloadPrivate.h
│ │ ├── ICLPluginManagerPrivate.h
│ │ ├── ICLProcessDelegatePrivate.h
│ │ ├── ICLProcessMainPrivate.h
│ │ └── ICLProcessPCHPrivate.h
│ ├── Modules/
│ │ ├── Assessed Media/
│ │ │ ├── ICMAssessedMedia.h
│ │ │ └── ICMAssessedMedia.m
│ │ └── Root Classes/
│ │ ├── ICMInlineHTML.h
│ │ ├── ICMInlineHTML.m
│ │ ├── ICMInlineImage.h
│ │ ├── ICMInlineImage.m
│ │ ├── ICMInlineVideo.h
│ │ └── ICMInlineVideo.m
│ └── Service/
│ ├── ICLHelpers.m
│ ├── ICLInlineContentModule.m
│ ├── ICLMediaAssessment.m
│ ├── ICLMediaAssessor.m
│ ├── ICLPayloadLocal.m
│ ├── ICLPayloadShared.m
│ ├── ICLPluginManager.m
│ ├── ICLProcessDelegate.m
│ ├── ICLProcessMain.m
│ └── main.m
├── Configurations/
│ └── Sandbox/
│ └── Release.entitlements
├── Extensions/
│ └── Core Media/
│ ├── Classes/
│ │ ├── Headers/
│ │ │ └── Private/
│ │ │ ├── ICPCoreMediaPCHPrivate.h
│ │ │ └── ICPCoreMediaPrivate.h
│ │ ├── ICPCoreMedia.m
│ │ └── Modules/
│ │ ├── Common Images/
│ │ │ ├── ICMCommonInlineImages.h
│ │ │ └── ICMCommonInlineImages.m
│ │ ├── Common Videos/
│ │ │ ├── ICMCommonInlineVideos.h
│ │ │ └── ICMCommonInlineVideos.m
│ │ ├── Dailymotion/
│ │ │ ├── ICMDailymotion.h
│ │ │ └── ICMDailymotion.m
│ │ ├── Gyazo/
│ │ │ ├── ICMGyazo.h
│ │ │ └── ICMGyazo.m
│ │ ├── Imgur .gifv/
│ │ │ ├── ICMImgurGifv.h
│ │ │ └── ICMImgurGifv.m
│ │ ├── Pornhub/
│ │ │ ├── ICMPornhub.h
│ │ │ └── ICMPornhub.m
│ │ ├── Streamable/
│ │ │ ├── ICMStreamable.h
│ │ │ └── ICMStreamable.m
│ │ ├── Twitch Clips/
│ │ │ ├── ICMTwitchClips.h
│ │ │ └── ICMTwitchClips.m
│ │ ├── Twitch Live/
│ │ │ ├── ICMTwitchLive.h
│ │ │ └── ICMTwitchLive.m
│ │ ├── Twitter/
│ │ │ ├── ICMTweet.h
│ │ │ └── ICMTweet.m
│ │ ├── Vimeo/
│ │ │ ├── ICMVimeo.h
│ │ │ └── ICMVimeo.m
│ │ ├── YouTube/
│ │ │ ├── ICMYouTube.h
│ │ │ └── ICMYouTube.m
│ │ └── xkcd/
│ │ ├── ICMXkcd.h
│ │ └── ICMXkcd.m
│ ├── Inline Content Loader Core Media.xcodeproj/
│ │ └── project.pbxproj
│ └── Resources/
│ ├── Modules/
│ │ ├── Dailymotion/
│ │ │ └── ICMDailymotion.mustache
│ │ ├── Pornhub/
│ │ │ └── ICMPornhub.mustache
│ │ ├── Twitch Clips/
│ │ │ └── ICMTwitchClips.mustache
│ │ ├── Twitch Live/
│ │ │ └── ICMTwitchLive.mustache
│ │ ├── Twitter/
│ │ │ └── ICMTweet.js
│ │ ├── Vimeo/
│ │ │ └── ICMVimeo.mustache
│ │ └── YouTube/
│ │ └── ICMYouTube.mustache
│ └── Property Lists/
│ └── Info.plist
├── Inline Content Loader.xcodeproj/
│ └── project.pbxproj
└── Resources/
├── Modules/
│ ├── ICMInlineHTML.css
│ ├── ICMInlineHTML.js
│ ├── ICMInlineHTML.mustache
│ ├── ICMInlineImage.css
│ ├── ICMInlineImage.js
│ ├── ICMInlineImage.mustache
│ ├── ICMInlineVideo.css
│ ├── ICMInlineVideo.js
│ ├── ICMInlineVideo.mustache
│ └── InlineImageLiveResize.js
└── Property Lists/
└── Info.plist
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*~
*~.*
.DS_Store
.LSOverride
*.mode1v3
*.mode2v3
*.pbxuser
*.perspectivev3
*.pyc
.tmp
build
Build Results
xcuserdata
*.xcworkspace
Sources/App/Resources/Scripting/Script Files/Textual Extras Installer/Installation Files/Extensions/*
Sources/App/Resources/Scripting/Script Files/Textual Extras Installer/Packages/MACAPPSTORE/*
Sources/App/Resources/Scripting/Script Files/Textual Extras Installer/Packages/STANDALONE/*
!/Configurations/Build
================================================
FILE: .gitmodules
================================================
[submodule "Frameworks/Auto Hyperlinks"]
path = Frameworks/Auto Hyperlinks
url = https://github.com/Codeux-Software/Auto-Hyperlinks.git
[submodule "Frameworks/Cocoa Extensions"]
path = Frameworks/Cocoa Extensions
url = https://github.com/Codeux-Software/Cocoa-Extensions.git
[submodule "Frameworks/Encryption Kit"]
path = Frameworks/Encryption Kit
url = https://github.com/Codeux-Software/Encryption-Kit.git
[submodule "Frameworks/Static Libraries"]
path = Frameworks/Static Libraries
url = https://github.com/Codeux-Software/Static-Libraries.git
[submodule "Sources/Plugins/Sample Code/Swift Bot"]
path = Sources/Plugins/Sample Code/Swift Bot
url = https://github.com/Codeux/Textual-Swift-Plugin.git
================================================
FILE: Configurations/Build/Code Signing Identity.xcconfig
================================================
// This is the code signing identity used by all projects.
CODE_SIGN_IDENTITY = Mac Developer
// Set these values to empty
DEVELOPMENT_TEAM = 8482Q6EPL6
PROVISIONING_PROFILE_SPECIFIER = 8482Q6EPL6/
================================================
FILE: Configurations/Build/Common/Foundation Debug.xcconfig
================================================
// Foundation Debug.xcconfig acts as a base for ALL projects.
// This configuration should NEVER contain any settings
// which may be unique to a specific project.
#include "Foundation.xcconfig"
DEPLOYMENT_POSTPROCESSING = NO
ENABLE_NS_ASSERTIONS = YES
ENABLE_TESTABILITY = YES
GCC_OPTIMIZATION_LEVEL = 0
GCC_PREPROCESSOR_DEFINITIONS = DEBUG
ONLY_ACTIVE_ARCH = YES
RUN_CLANG_STATIC_ANALYZER = NO
SWIFT_COMPILATION_MODE = singlefile
SWIFT_OPTIMIZATION_LEVEL = -Onone
VERSION_INFO_BUILDER = ${USER}
//
// On Xcode 16, Textual failed to run in debug mode because
// a "Textual.debug.dylib" file was missing in the @rpath
// of extensions and services. Going to disable this feature
// of Xcode until a later release of Xcode.
//
// From Xcode 16 beta 1 release notes:
//
// Some large or complex projects may fail to build and
// run if they are scanning for specific Mach-O sections
// in their binaries. (123416939)
//
// Workaround: Try setting the build setting ENABLE_DEBUG_DYLIB=NO.
// This will disable the debug dylib that enables the new preview
// execution mode. Setting this to NO will still allow you to
// preview in Xcode 16 Seed 1 using the legacy execution mode,
// but support for this mode will be removed in a future build.
//
ENABLE_DEBUG_DYLIB = NO
================================================
FILE: Configurations/Build/Common/Foundation.xcconfig
================================================
// Foundation.xcconfig acts as a base for ALL projects.
// This configuration should NEVER contain any settings
// which may be unique to a specific project.
ALWAYS_SEARCH_USER_PATHS = NO
APPLY_RULES_IN_COPY_FILES = YES
ARCHS = $(ARCHS_STANDARD)
CLANG_ANALYZER_GCD_PERFORMANCE = YES
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES
CLANG_CXX_LANGUAGE_STANDARD = gnu++0x
CLANG_ENABLE_MODULES = YES
CLANG_ENABLE_OBJC_ARC = YES
CLANG_USE_OPTIMIZATION_PROFILE = NO
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
CLANG_WARN_BOOL_CONVERSION = YES
CLANG_WARN_COMMA = YES
CLANG_WARN_CONSTANT_CONVERSION = YES
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
CLANG_WARN_EMPTY_BODY = YES
CLANG_WARN_ENUM_CONVERSION = YES
CLANG_WARN_INFINITE_RECURSION = YES
CLANG_WARN_INT_CONVERSION = YES
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES
CLANG_WARN_STRICT_PROTOTYPES = YES
CLANG_WARN_SUSPICIOUS_MOVE = YES
CLANG_WARN_UNREACHABLE_CODE = YES
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
CODE_SIGN_ENTITLEMENTS =
COMBINE_HIDPI_IMAGES = YES
CONFIGURATION_BUILD_DIR = $(PROJECT_DIR)/Build Results/$(CONFIGURATION)
COPY_PHASE_STRIP = NO
DEAD_CODE_STRIPPING = YES
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
DEFINES_MODULE = YES
DEPLOYMENT_POSTPROCESSING = YES
ENABLE_HARDENED_RUNTIME = YES
ENABLE_NS_ASSERTIONS = YES
ENABLE_STRICT_OBJC_MSGSEND = NO
FRAMEWORK_SEARCH_PATHS =
GCC_C_LANGUAGE_STANDARD = gnu99
GCC_DYNAMIC_NO_PIC = YES
GCC_ENABLE_OBJC_EXCEPTIONS = YES
GCC_GENERATE_DEBUGGING_SYMBOLS = YES
GCC_NO_COMMON_BLOCKS = YES
GCC_OPTIMIZATION_LEVEL = fast
GCC_PREFIX_HEADER =
GCC_UNROLL_LOOPS = YES
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES
GCC_WARN_ABOUT_MISSING_NEWLINE = YES
GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = NO
GCC_WARN_UNDECLARED_SELECTOR = NO
GCC_WARN_UNINITIALIZED_AUTOS = YES
GCC_WARN_UNUSED_FUNCTION = YES
GCC_WARN_UNUSED_LABEL = YES
GCC_WARN_UNUSED_VARIABLE = YES
HEADER_SEARCH_PATHS =
INFOPLIST_OUTPUT_FORMAT = binary
LD_NO_PIE = YES
LD_NO_PIE[arch=arm64] = NO
LD_RUNPATH_SEARCH_PATHS = @loader_path/../Frameworks @executable_path/../Frameworks
LIBRARY_SEARCH_PATHS =
MACOSX_DEPLOYMENT_TARGET = 11.0
ONLY_ACTIVE_ARCH = NO
OTHER_CFLAGS = -Xclang -fobjc-runtime-has-weak
PLIST_FILE_OUTPUT_FORMAT = binary
PRODUCT_BUNDLE_IDENTIFIER =
REMOVE_HEADERS_FROM_EMBEDDED_BUNDLES = YES
RUN_CLANG_STATIC_ANALYZER = NO
STRINGS_FILE_OUTPUT_ENCODING = binary
SWIFT_COMPILATION_MODE = wholemodule
SWIFT_INSTALL_OBJC_HEADER = NO
SWIFT_OPTIMIZATION_LEVEL = -O
SWIFT_VERSION = 5.0
VERSION_INFO_BUILDER = Codeux Software, LLC
WARNING_CFLAGS = -Wobjc-circular-container -Winfinite-recursion
#include "../Code Signing Identity.xcconfig"
================================================
FILE: Configurations/Build/Common/Preserve Symbols.xcconfig
================================================
// It is important to preserve symbols and code that is
// not directly used so that extensions can use them.
DEAD_CODE_STRIPPING = NO
GCC_SYMBOLS_PRIVATE_EXTERN = NO
STRIP_STYLE = non-global
================================================
FILE: Configurations/Build/Common/Textual App.xcconfig
================================================
// Main application
TEXTUAL_WORKSPACE_DIR = ${PROJECT_DIR}/../..
HEADER_SEARCH_PATHS = "${TEXTUAL_WORKSPACE_TEMP_DIR}/Build Headers/" "${TEXTUAL_WORKSPACE_DIR}/Sources/Shared/Headers/**"
FRAMEWORK_SEARCH_PATHS = "${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Frameworks/**" "${TEXTUAL_WORKSPACE_DIR}/Frameworks/**"
LIBRARY_SEARCH_PATHS = "${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Frameworks/**" "${TEXTUAL_WORKSPACE_DIR}/Frameworks/**"
PRODUCT_BUNDLE_IDENTIFIER = ${TEXTUAL_BUNDLE_IDENTIFIER}
PRODUCT_NAME = ${TEXTUAL_PRODUCT_NAME}
WRAPPER_EXTENSION = app
PRODUCT_MODULE_NAME = Textual
CONFIGURATION_BUILD_DIR = $(TEXTUAL_WORKSPACE_DIR)/Build Results/$(CONFIGURATION)
GCC_PREFIX_HEADER = ${PROJECT_DIR}/Classes/Headers/Private/TextualPrivate.h
SWIFT_OBJC_BRIDGING_HEADER = ${PROJECT_DIR}/Classes/Headers/Private/SwiftBridgingHeaderPrivate.h
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
REMOVE_HEADERS_FROM_EMBEDDED_BUNDLES = NO
INFOPLIST_FILE = ${TEXTUAL_WORKSPACE_TEMP_DIR}/Info.plist
#include "Preserve Symbols.xcconfig"
================================================
FILE: Configurations/Build/Common/Textual Extensions.xcconfig
================================================
// Textual Extensions *.xcconfig file contains settings that
// are specific to extensions for Textual (main application).
// TEXTUAL_WORKSPACE_DIR is replaced by the build script
TEXTUAL_WORKSPACE_DIR = ${PROJECT_DIR}/../../..
CONFIGURATION_BUILD_DIR = ${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Extensions
CODE_SIGN_ENTITLEMENTS = ${TEXTUAL_WORKSPACE_DIR}/Configurations/Sandbox/Inherited.entitlements
HEADER_SEARCH_PATHS = ${inherited} "${TEXTUAL_WORKSPACE_TEMP_DIR}/Build Headers/" "${TEXTUAL_WORKSPACE_DIR}/Sources/App/Classes/Headers/**" "${TEXTUAL_WORKSPACE_DIR}/Sources/Shared/Headers/**"
LIBRARY_SEARCH_PATHS = ${inherited} "${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Frameworks/**" "${TEXTUAL_WORKSPACE_DIR}/Frameworks/**"
FRAMEWORK_SEARCH_PATHS = ${inherited} "${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Frameworks/**" "${TEXTUAL_WORKSPACE_DIR}/Frameworks/**"
WRAPPER_EXTENSION = bundle
PROVISIONING_PROFILE =
PROVISIONING_PROFILE_SPECIFIER =
BUNDLE_LOADER = ${TEXTUAL_PRODUCT_BINARY}
================================================
FILE: Configurations/Build/Common/Textual.xcconfig
================================================
// Main application, extensions, and XPC services
TEXTUAL_PRODUCT_NAME = Textual
// TEXTUAL_PRODUCT_LOCATION is replaced by the build script
TEXTUAL_PRODUCT_LOCATION = /Applications/${TEXTUAL_PRODUCT_NAME}.app
// TEXTUAL_PRODUCT_BINARY is replaced by the build script
TEXTUAL_PRODUCT_BINARY = ${TEXTUAL_PRODUCT_LOCATION}/Contents/MacOS/${TEXTUAL_PRODUCT_NAME}
// TEXTUAL_WORKSPACE_TEMP_DIR should be same for every
// project so only override TEXTUAL_WORKSPACE_DIR, not this.
TEXTUAL_WORKSPACE_TEMP_DIR = ${TEXTUAL_WORKSPACE_DIR}/.tmp
SHARED_PRECOMPS_DIR = ${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedPCH
DWARF_DSYM_FOLDER_PATH = ${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedSymbols
// TEXTUAL_BUILT_AS_UNIVERSAL_BINARY acts an easy way to
// modify code based on the type of binary being built.
// As with other feature flags, changing this value only
// adds or excludes certain chunks of code. It doesn't
// change the underlying build process.
TEXTUAL_BUILT_AS_UNIVERSAL_BINARY = 1
================================================
FILE: Configurations/Build/Common/XPC Service - ICL Extensions.xcconfig
================================================
// Inline Content Loader XPC service extensions
// TEXTUAL_WORKSPACE_DIR is replaced by the build script
TEXTUAL_WORKSPACE_DIR = ${PROJECT_DIR}/../../../..
// ICL_WORKSPACE_DIR is NOT replaced by the build script
ICL_WORKSPACE_DIR = ${TEXTUAL_WORKSPACE_DIR}/XPC Services/Inline Content Loader
// ICL_PRODUCT_LOCATION is replaced by the build script
ICL_PRODUCT_LOCATION = ${TEXTUAL_PRODUCT_LOCATION}/Contents/XPCServices/InlineContentLoader.xpc
// ICL_PRODUCT_BINARY is replaced by the build script
ICL_PRODUCT_BINARY = ${ICL_PRODUCT_LOCATION}/Contents/MacOS/InlineContentLoader
CONFIGURATION_BUILD_DIR = ${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-ICLExtension
HEADER_SEARCH_PATHS = ${inherited} "${ICL_WORKSPACE_DIR}/Classes/Headers/**" "${ICL_WORKSPACE_DIR}/Classes/Modules/**" "${TEXTUAL_WORKSPACE_DIR}/Sources/Shared/Headers/**"
LIBRARY_SEARCH_PATHS = ${inherited} "${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Frameworks/**" "${TEXTUAL_WORKSPACE_DIR}/Frameworks/**"
FRAMEWORK_SEARCH_PATHS = ${inherited} "${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Frameworks/**" "${TEXTUAL_WORKSPACE_DIR}/Frameworks/**"
WRAPPER_EXTENSION = bundle
PROVISIONING_PROFILE =
PROVISIONING_PROFILE_SPECIFIER =
BUNDLE_LOADER = ${ICL_PRODUCT_BINARY}
================================================
FILE: Configurations/Build/Common/XPC Service - ICL.xcconfig
================================================
// Inline content loader XPC service
#include "Preserve Symbols.xcconfig"
================================================
FILE: Configurations/Build/Common/XPC Services.xcconfig
================================================
// XPC services
// TEXTUAL_WORKSPACE_DIR is replaced by the build script
TEXTUAL_WORKSPACE_DIR = ${PROJECT_DIR}/../..
CONFIGURATION_BUILD_DIR = ${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-XPCServices
CODE_SIGN_ENTITLEMENTS = ${TEXTUAL_WORKSPACE_DIR}/Configurations/Sandbox/Inherited.entitlements
HEADER_SEARCH_PATHS = $(inherited) "${TEXTUAL_WORKSPACE_TEMP_DIR}/Build Headers/" "${TEXTUAL_WORKSPACE_DIR}/Sources/Shared/Headers/**"
LIBRARY_SEARCH_PATHS = $(inherited) "${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Frameworks/**" "${TEXTUAL_WORKSPACE_DIR}/Frameworks/**"
FRAMEWORK_SEARCH_PATHS = $(inherited) "${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Frameworks/**" "${TEXTUAL_WORKSPACE_DIR}/Frameworks/**"
WRAPPER_EXTENSION = xpc
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) TEXTUAL_BUILDING_XPC_SERVICE=1
// Link against frameworks in app itself rather than creating copies
// ./(self) ../(Contents) ../(.xpc bundle) ../(XPCServices) ../(Contents)
LD_RUNPATH_SEARCH_PATHS = $(inherited) @loader_path/../../../../Frameworks
================================================
FILE: Configurations/Build/Debug/Enabled Features.xcconfig
================================================
// The main project of Textual, its extensions, and its XPC services
// all inherit one of these configuration files to allow them to
// selectively disable code based on which features are enabled.
// These macros that are used to disable and enable the inclusion
// of certain segments of code. Toggling one of these flags does not
// guarantee that the toggled feature will work. These only toggle the code.
// Not add additional resource files that the code may be dependent on.
// To add new feature flags, modify UpdateFeatureFlags.sh
TEXTUAL_BUILT_INSIDE_SANDBOX=1
TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION=1
TEXTUAL_BUILT_WITH_LICENSE_MANAGER=1
================================================
FILE: Configurations/Build/Debug/Textual App.xcconfig
================================================
#include "../Common/Foundation Debug.xcconfig"
#include "Textual.xcconfig"
#include "../Common/Textual App.xcconfig"
CODE_SIGN_ENTITLEMENTS = ${PROJECT_DIR}/Configurations/Sandbox/Debug.entitlements
================================================
FILE: Configurations/Build/Debug/Textual Extensions.xcconfig
================================================
#include "../Common/Foundation Debug.xcconfig"
#include "Textual.xcconfig"
#include "../Common/Textual Extensions.xcconfig"
================================================
FILE: Configurations/Build/Debug/Textual.xcconfig
================================================
#include "../Common/Textual.xcconfig"
#include "Enabled Features.xcconfig"
TEXTUAL_BUNDLE_IDENTIFIER = com.codeux.apps.textual
TEXTUAL_GROUP_CONTAINER_IDENTIFIER = 8482Q6EPL6.com.codeux.apps.textual
TEXTUAL_BUILD_SCHEME_TOKEN = debug
TEXTUAL_EXTENSION_BUILD_SCHEME = Debug
TEXTUAL_FRAMEWORK_BUILD_SCHEME = Release // Frameworks don't have a Debug scheme
TEXTUAL_XPC_SERVICE_BUILD_SCHEME = Debug
================================================
FILE: Configurations/Build/Debug/XPC Service - ICL Extensions.xcconfig
================================================
#include "../Common/Foundation Debug.xcconfig"
#include "Textual.xcconfig"
#include "../Common/XPC Service - ICL Extensions.xcconfig"
================================================
FILE: Configurations/Build/Debug/XPC Service - ICL.xcconfig
================================================
#include "XPC Services.xcconfig"
#include "../Common/XPC Service - ICL.xcconfig"
ICL_EXTENSION_BUILD_SCHEME = Debug
================================================
FILE: Configurations/Build/Debug/XPC Services.xcconfig
================================================
#include "../Common/Foundation Debug.xcconfig"
#include "Textual.xcconfig"
#include "../Common/XPC Services.xcconfig"
================================================
FILE: Configurations/Build/README.md
================================================
TODO: Write a README
================================================
FILE: Configurations/Build/Standard Release/Enabled Features.xcconfig
================================================
// The main project of Textual, its extensions, and its XPC services
// all inherit one of these configuration files to allow them to
// selectively disable code based on which features are enabled.
// These macros that are used to disable and enable the inclusion
// of certain segments of code. Toggling one of these flags does not
// guarantee that the toggled feature will work. These only toggle the code.
// Not add additional resource files that the code may be dependent on.
// To add new feature flags, modify UpdateFeatureFlags.sh
TEXTUAL_BUILT_INSIDE_SANDBOX=1
TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION=1
TEXTUAL_BUILT_WITH_LICENSE_MANAGER=1
TEXTUAL_BUILT_WITH_SPARKLE_ENABLED=1
================================================
FILE: Configurations/Build/Standard Release/Textual App.xcconfig
================================================
#include "../Common/Foundation.xcconfig"
#include "Textual.xcconfig"
#include "../Common/Textual App.xcconfig"
CODE_SIGN_ENTITLEMENTS = ${PROJECT_DIR}/Configurations/Sandbox/Standard Release.entitlements
================================================
FILE: Configurations/Build/Standard Release/Textual Extensions.xcconfig
================================================
#include "../Common/Foundation.xcconfig"
#include "Textual.xcconfig"
#include "../Common/Textual Extensions.xcconfig"
================================================
FILE: Configurations/Build/Standard Release/Textual.xcconfig
================================================
#include "../Common/Textual.xcconfig"
#include "Enabled Features.xcconfig"
TEXTUAL_BUNDLE_IDENTIFIER = com.codeux.apps.textual
TEXTUAL_GROUP_CONTAINER_IDENTIFIER = 8482Q6EPL6.com.codeux.apps.textual
TEXTUAL_BUILD_SCHEME_TOKEN = devid
TEXTUAL_EXTENSION_BUILD_SCHEME = Release
TEXTUAL_FRAMEWORK_BUILD_SCHEME = Release
TEXTUAL_XPC_SERVICE_BUILD_SCHEME = Release
================================================
FILE: Configurations/Build/Standard Release/XPC Service - ICL Extensions.xcconfig
================================================
#include "../Common/Foundation.xcconfig"
#include "Textual.xcconfig"
#include "../Common/XPC Service - ICL Extensions.xcconfig"
================================================
FILE: Configurations/Build/Standard Release/XPC Service - ICL.xcconfig
================================================
#include "XPC Services.xcconfig"
#include "../Common/XPC Service - ICL.xcconfig"
ICL_EXTENSION_BUILD_SCHEME = Release
================================================
FILE: Configurations/Build/Standard Release/XPC Services.xcconfig
================================================
#include "../Common/Foundation.xcconfig"
#include "Textual.xcconfig"
#include "../Common/XPC Services.xcconfig"
================================================
FILE: Configurations/ExportArchiveConfiguration.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>teamID</key>
<string>8482Q6EPL6</string>
<key>method</key>
<string>developer-id</string>
</dict>
</plist>
================================================
FILE: Configurations/Sandbox/Inherited.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>
================================================
FILE: README.md
================================================
> [!IMPORTANT]
> Textual is no longer being actively maintained. For the life of the project, it only ever had one full time maintainer whom has now moved on to different ventures in their life. To all that have contributed to Textual in some form in the past; be it a suggestion, bug report, pull request, financial support, or some other form of contribvution, you will forever be loved. Thank you so much. Words cannot properly express the gratitude we have for every single user.
# Textual [](https://github.com/Codeux-Software/Textual/blob/master) [](http://www.textualapp.com/mac-app-store)
Textual is a highly customizable app for interacting with Internet Relay Chat (IRC) chatrooms on macOS.
Textual can be customized with styles written in CSS, HTML, and JavaScript; [plugins](https://help.codeux.com/textual/Writing-Plugins.kb) written in Objective-C & Swift, and [scripts](https://help.codeux.com/textual/Writing-Scripts.kb) written in AppleScript (plus many other languages)
Precompiled versions of Textual can be purchased in the [directly from codeux.com](https://www.textualapp.com/).
## Screenshots
[](https://www.codeux.com/textual/private/images/v600media/YosemiteLightFullscreen.png)
[](https://www.codeux.com/textual/private/images/v600media/YosemiteDarkFullscreen.png)
## Resources
- [Homepage](https://codeux.com/textual)
- [Frequently Asked Questions](https://help.codeux.com/textual/Frequently-Asked-Questions.kb)
- [Support](https://help.codeux.com/textual/Support.kb)
- \#textual on irc.libera.chat
- Guides: [Writing Plugins](https://help.codeux.com/textual/Writing-Plugins.kb), [Writing Scripts](https://help.codeux.com/textual/Writing-Scripts.kb)
## Note Regarding Downloading Source Code
Textual is dependent on several other projects to build. This repository is automatically linked against these other projects using what are known as "submodules" — Clicking the "Download ZIP" button to build a copy of Textual will not download a copy of these projects. The source code must be cloned using [Github for Mac](https://mac.github.com/) or by using the following commands in Terminal:
```
git clone https://github.com/Codeux-Software/Textual.git Textual
cd Textual
git submodule update --init --recursive
```
## Note Regarding Code Signing
**DO NOT change the Code Signing Identity setting through Xcode.** Textual uses a configuration file to specify the code signing identity. This allows it to be used across all projects associated with Textual without having to modify each.
**DO** edit the file located at _[Configurations ➜ Build ➜ Code Signing Identity.xcconfig](https://github.com/Codeux-Software/Textual/blob/master/Configurations/Build/Code%20Signing%20Identity.xcconfig)_
**It is HIGHLY DISCOURAGED to turn off code signing.** Certain features rely on the fact that Textual is properly signed and is within a sandboxed environment.
**TEXTUAL DOES NOT REQUIRE A CERTIFICATE ISSUED BY APPLE TO BUILD** which means there is absolutely no reason to turn code signing off.
## Note Regarding Trial Mode
The code which is responsible for licensing paid copies of Textual is in the source code that you download from here.
If you do not have a license key, then set the ``TEXTUAL_BUILT_WITH_LICENSE_MANAGER`` flag to `0` in the `Standard Release` configuration file to disable the inclusion of this code at build time.
## Building Textual
The latest version of Textual requires two things to be built. One is a valid (does not need to be issued by Apple) code signing certificate. The second is an installation of Xcode 10.0 or newer on macOS High Sierra. **Building on anything earlier is not supported because of Swift 4.2 code.**
**DO NOT change the Code Signing Identity setting through Xcode.** Modify the file located at _[Configurations ➜ Build ➜ Code Signing Identity.xcconfig](https://github.com/Codeux-Software/Textual/blob/master/Configurations/Build/Code%20Signing%20Identity.xcconfig)_ instead.
Build Textual using the "Standard Release" build scheme.
## Original Limechat License
Textual began as a fork of [LimeChat](https://github.com/psychs/limechat) in 2010
LimeChat's original license is presented below.
<pre>
The New BSD License
Copyright (c) 2008 - 2010 Satoshi Nakagawa < psychs AT limechat DOT net >
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
</pre>
## License for content originating from Textual
Unless stated otherwise by Textual's [Acknowledgements.pdf](Acknowledgements.pdf) document, the license presented below shall govern the distribution of and modifications to; the work hosted by this repository.
<pre>
Copyright (c) 2010 - 2020 Codeux Software, LLC & respective contributors.
Please see Acknowledgements.pdf for additional information.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Textual, "Codeux Software, LLC", nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
</pre>
================================================
FILE: Sources/App/Build Scripts/BuildExtensions.sh
================================================
#!/bin/bash
set -e
echo "Building using architecture: ${ARCHS}"
TEXTUAL_PRODUCT_LOCATION="${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}"
TEXTUAL_PRODUCT_BINARY="${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}"
plugins=(
'Caffeine'
'Chat Filter'
'Smiley Converter'
'System Profiler'
'User Insights'
'ZNC Additions'
)
for plugin in "${plugins[@]}"; do
cd "${TEXTUAL_WORKSPACE_DIR}/Sources/Plugins/${plugin}"
xcodebuild -target "$plugin Extension" \
-configuration "${TEXTUAL_EXTENSION_BUILD_SCHEME}" \
ARCHS="${ARCHS}" \
CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY}" \
DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" \
PROVISIONING_PROFILE_SPECIFIER="" \
TEXTUAL_WORKSPACE_DIR="${TEXTUAL_WORKSPACE_DIR}" \
TEXTUAL_PRODUCT_LOCATION="${TEXTUAL_PRODUCT_LOCATION}" \
TEXTUAL_PRODUCT_BINARY="${TEXTUAL_PRODUCT_BINARY}"
done
exit 0
================================================
FILE: Sources/App/Build Scripts/BuildFrameworks.sh
================================================
#!/bin/bash
set -e
echo "Building using architecture: ${ARCHS}"
CONFIGURATION_BUILD_DIR="${TEXTUAL_WORKSPACE_TEMP_DIR}/SharedBuildProducts-Frameworks"
xcb() {
target=$1
xcodebuild -target "$target" \
-configuration "${TEXTUAL_FRAMEWORK_BUILD_SCHEME}" \
ARCHS="${ARCHS}" \
CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY}" \
CONFIGURATION_BUILD_DIR="${CONFIGURATION_BUILD_DIR}" \
DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" \
PROVISIONING_PROFILE_SPECIFIER="" \
DWARF_DSYM_FOLDER_PATH="${DWARF_DSYM_FOLDER_PATH}"
}
# Assumes the name and filename of the framework is the same just without spaces.
frameworks=(
'Auto Hyperlinks'
'Encryption Kit'
'Cocoa Extensions'
)
for framework in "${frameworks[@]}"; do
cd "${TEXTUAL_WORKSPACE_DIR}/Frameworks/${framework}/"
xcb "${framework// /}.framework"
done
exit 0
================================================
FILE: Sources/App/Build Scripts/BuildServices.sh
================================================
#!/bin/bash
set -e
echo "Building using architecture: ${ARCHS}"
TEXTUAL_PRODUCT_LOCATION="${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}"
TEXTUAL_PRODUCT_BINARY="${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}"
services=(
'Historic Log File Manager'
'Inline Content Loader'
'IRC Remote Connection Manager'
)
for service in "${services[@]}"; do
cd "${TEXTUAL_WORKSPACE_DIR}/XPC Services/${service}/"
xcodebuild -target "$service" \
-configuration "${TEXTUAL_XPC_SERVICE_BUILD_SCHEME}" \
ARCHS="${ARCHS}" \
CODE_SIGN_IDENTITY="${CODE_SIGN_IDENTITY}" \
DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM}" \
PROVISIONING_PROFILE_SPECIFIER="" \
TEXTUAL_WORKSPACE_DIR="${TEXTUAL_WORKSPACE_DIR}" \
TEXTUAL_PRODUCT_LOCATION="${TEXTUAL_PRODUCT_LOCATION}" \
TEXTUAL_PRODUCT_BINARY="${TEXTUAL_PRODUCT_BINARY}"
done
================================================
FILE: Sources/App/Build Scripts/ExportArchive.sh
================================================
#!/bin/sh
set -e
WORKING_PATH="${TEXTUAL_WORKSPACE_TEMP_DIR}/ArchiveTan"
mkdir -p "${WORKING_PATH}"
cd "${WORKING_PATH}"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
xcodebuild -exportArchive \
-exportOptionsPlist "${TEXTUAL_WORKSPACE_DIR}/Configurations/ExportArchiveConfiguration.plist" \
-archivePath "${ARCHIVE_PATH}" \
-exportPath "${WORKING_PATH}"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Zip product and send to notary
#
# Format to add notary to keychain:
#
# xcrun notarytool store-credentials "Textual Notary"
# --apple-id "<e-mail address>"
# --team-id <team id>
# --password "<password>"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
WORKING_ZIP_PATH="./${FULL_PRODUCT_NAME}.zip"
zip -y -r -X "${WORKING_ZIP_PATH}" "./${FULL_PRODUCT_NAME}/"
xcrun notarytool submit "${WORKING_ZIP_PATH}" \
--keychain-profile "Textual Notary" \
--wait \
--verbose \
--progress
# Remove uploaded product
rm "${WORKING_ZIP_PATH}"
# Stable app
xcrun stapler staple --verbose "./${FULL_PRODUCT_NAME}/"
# Create new zip with stapled app
zip -y -r -X "${WORKING_ZIP_PATH}" "./${FULL_PRODUCT_NAME}/"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
# Call `git` after `cd` into working path to make
# sure we are in a directory of a git repository.
GIT_COMMIT_HASH=`git rev-parse --short HEAD`
EXPORT_PATH="${HOME}/Desktop/Textual-${GIT_COMMIT_HASH}"
mkdir -p "${EXPORT_PATH}"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
if [ "${TEXTUAL_BUILT_AS_UNIVERSAL_BINARY}" == "1" ]; then
ARCHSPEC_PATH="${EXPORT_PATH}/universal"
else
ARCHSPEC_PATH="${EXPORT_PATH}/intel"
fi
mkdir -p "${ARCHSPEC_PATH}"
ZIP_EXPORT_PATH="${ARCHSPEC_PATH}/Textual.zip"
mv "${WORKING_ZIP_PATH}" "${ZIP_EXPORT_PATH}"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
BUNDLE_VERSION_LONG=$(/usr/libexec/PlistBuddy -c "Print \"CFBundleVersion\"" "./${FULL_PRODUCT_NAME}/Contents/Info.plist")
BUNDLE_VERSION_SHORT=$(/usr/libexec/PlistBuddy -c "Print \"CFBundleShortVersionString\"" "./${FULL_PRODUCT_NAME}/Contents/Info.plist")
BUNDLE_MINIMUM_TARGET=$(/usr/libexec/PlistBuddy -c "Print \"LSMinimumSystemVersion\"" "./${FULL_PRODUCT_NAME}/Contents/Info.plist")
echo "<?php
/* Generated on: $(date) */
" > ./buildInfo.php
echo " \$current_release_version_short = \"${BUNDLE_VERSION_SHORT}\";" >> ./buildInfo.php
echo " \$current_release_version_long = \"${BUNDLE_VERSION_LONG}\";" >> ./buildInfo.php
echo " \$current_release_version_signature = \"${GIT_COMMIT_HASH}\";" >> ./buildInfo.php
echo " \$current_release_minimum_system_version = \"${BUNDLE_MINIMUM_TARGET}\";" >> buildInfo.php
mv "./buildInfo.php" "${EXPORT_PATH}"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
echo "<section class=\"main\">
<section class=\"header\">Release Notes for ${BUNDLE_VERSION_LONG}</section>
<section class=\"body\">
<ul>" > ./buildLog.txt
git log --since='48 hours ago' --pretty=format:' <li>%s</li>' >> ./buildLog.txt
echo "
</ul>
</section>
</section>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->" >> ./buildLog.txt
mv "./buildLog.txt" "${EXPORT_PATH}"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
cd "${DWARF_DSYM_FOLDER_PATH}"
DYSM_EXPORT_PATH="${ARCHSPEC_PATH}/Debug symbols.zip"
zip -y -r -X "${DYSM_EXPORT_PATH}" *
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
rm -rf "${WORKING_PATH}"
exit 0;
================================================
FILE: Sources/App/Build Scripts/MergeSwift.sh
================================================
#!/bin/sh
set -e
#
# Textual and Remote Connection Manager service both use Swift.
# Textual embeds the library and the service will inherit that.
# For some reason, during development, I found that the service
# would not launch because "libswiftSwiftOnoneSupport.dylib"
# was missing in Textual's copy of the library.
#
# Textual and the service both declare -Onone optimization level
# at the same time, so I don't know why one will have it and
# the other does not.
#
# To workaround this issue and any in the future, this script exists.
# This script will find all libswift*.dylb files that aren't in the
# main copy of the library and bring those files in.
#
# TODO: Revisit this when I've had sleep (June 28, 2018)
#
cd "${TARGET_BUILD_DIR}/${CONTENTS_FOLDER_PATH}"
find . -type f -name "libswift*" \
-not -path "./Frameworks/*" \
-not -path "./Resources/*" \
-not -path "*/Resources/libswiftRemoteMirror.dylib" \
-exec cp '{}' ./Frameworks/ \; \
-exec rm '{}' \;
exit 0;
================================================
FILE: Sources/App/Build Scripts/PostprocessSparkle.sh
================================================
#!/bin/bash
set -e
echo "Performing postprocessing on Sparkle framework"
cd "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
rm -rf Sparkle.framework/Versions/B/XPCServices/Downloader.xpc
codesign -f -s "$CODE_SIGN_IDENTITY" -o runtime Sparkle.framework/Versions/B/XPCServices/Installer.xpc
codesign -f -s "$CODE_SIGN_IDENTITY" -o runtime Sparkle.framework/Versions/B/Autoupdate
codesign -f -s "$CODE_SIGN_IDENTITY" -o runtime Sparkle.framework/Versions/B/Updater.app
codesign -f -s "$CODE_SIGN_IDENTITY" -o runtime Sparkle.framework
exit 0
================================================
FILE: Sources/App/Build Scripts/UpdateFeatureFlags.sh
================================================
#!/bin/bash
set -e
cd "${TEXTUAL_WORKSPACE_TEMP_DIR}/Build Headers/"
echo "
/* ANY CHANGES TO THIS FILE WILL NOT BE SAVED AND WILL NOT BE COMMITTED */
" > _FeatureFlags.h
featureNames=("TEXTUAL_BUILT_INSIDE_SANDBOX"
"TEXTUAL_BUILT_WITH_SPARKLE_ENABLED"
"TEXTUAL_BUILT_WITH_LICENSE_MANAGER"
"TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION"
"TEXTUAL_BUILT_FOR_APP_STORE_DISTRIBUTION"
"TEXTUAL_BUILT_AS_UNIVERSAL_BINARY")
for feature in "${featureNames[@]}"; do
featureValue="${!feature}"
if [ -n "${featureValue}" ]; then
echo "#define ${feature} ${featureValue}" >> _FeatureFlags.h
else
echo "#define ${feature} 0" >> _FeatureFlags.h
fi
done
if cmp -s "FeatureFlags.h" "_FeatureFlags.h"; then
echo "The feature flags file hasn't changed. Not deploying."
rm "_FeatureFlags.h"
else
# Force flag is used on rm to avoid error for missing file
rm -f "FeatureFlags.h"
mv "_FeatureFlags.h" "FeatureFlags.h"
fi
# ------ #
# Exit with success
exit 0;
================================================
FILE: Sources/App/Build Scripts/UpdateVersionInfo.sh
================================================
#!/bin/sh
set -e
cd "${TEXTUAL_WORKSPACE_TEMP_DIR}/"
# Make a copy of the Info.plist file in the .tmp folder
# This will be the Info.plist file manipulated with the version
# information that is generated below.
infoPlistSource="${PROJECT_DIR}/Resources/Property Lists/Application Properties/Info.plist"
infoPlistTarget="${TEXTUAL_WORKSPACE_TEMP_DIR}/Info.plist"
if [ ! -f "${infoPlistTarget}" ] || [ "${infoPlistTarget}" -ot "${infoPlistSource}" ]; then
echo "Step 1: Info.plist file doesn't exist and/or is oudated. Performing copy."
# Copy with -p flag to preserve modification time
cp -p "${infoPlistSource}" "${infoPlistTarget}"
else
echo "Step 1: Info.plist file hasn't changed."
fi
# Write the version information to the Info.plist file.
# The build version is the date of the last commit in git.
gitBundle=`which git`
if [ -z "${gitBundle}" ]; then
bundleVersionNew="000000.00"
else
gitDateOfLastCommit=`"${gitBundle}" log -n1 --format="%at"`
bundleVersionNew=`/bin/date -u -r "${gitDateOfLastCommit}" "+%y%m%d.%H"`
fi;
bundleVersionOld=$(/usr/libexec/PlistBuddy -c "Print \"CFBundleVersion\"" Info.plist)
if [ "${bundleVersionOld}" != "${bundleVersionNew}" ]; then
echo "Step 2: Writing version: New ('${bundleVersionNew}'), Old ('${bundleVersionOld}')"
/usr/libexec/PlistBuddy -c "Set \"CFBundleVersion\" \"${bundleVersionNew}\"" Info.plist
else
echo "Step 2: The version hasn't changed."
fi
# ------ #
# Gather the information necessary for building Textual's BuildConfig.h
# header. This header file gives various section of the code base version
# information so it does not need to constantly access the Info.plist file.
bundleVersionShort=$(/usr/libexec/PlistBuddy -c "Print \"CFBundleShortVersionString\"" Info.plist)
mkdir -p "./Build Headers"
cd "./Build Headers"
echo "
/* ANY CHANGES TO THIS FILE WILL NOT BE SAVED AND WILL NOT BE COMMITTED */
#define TXBundleBuildProductName @\"${PRODUCT_NAME}\"
#define TXBundleBuildProductIdentifier @\"${PRODUCT_BUNDLE_IDENTIFIER}\"
#define TXBundleBuildProductIdentifierCString \"${PRODUCT_BUNDLE_IDENTIFIER}\"
#define TXBundleBuildGroupContainerIdentifier @\"${TEXTUAL_GROUP_CONTAINER_IDENTIFIER}\"
#define TXBundleBuildVersion @\"${bundleVersionNew}\"
#define TXBundleBuildVersionShort @\"${bundleVersionShort}\"
#define TXBundleBuildScheme @\"${TEXTUAL_BUILD_SCHEME_TOKEN}\"
" > _BuildConfig.h
if [ -z "$CODE_SIGN_IDENTITY" ]; then
echo "#define TXBundleBuiltWithoutCodeSigning 1" >> _BuildConfig.h
fi
if cmp -s "BuildConfig.h" "_BuildConfig.h"; then
echo "Step 3: The build configuration file hasn't changed. Not deploying."
rm "_BuildConfig.h"
else
# Force flag is used on rm to avoid error for missing file
rm -f "BuildConfig.h"
mv "_BuildConfig.h" "BuildConfig.h"
fi
# ------ #
# Compile list of enabled features
exec "${PROJECT_DIR}/Build Scripts/UpdateFeatureFlags.sh" > "${TEXTUAL_WORKSPACE_TEMP_DIR}/Script-Logs/UpdateFeatureFlags.txt"
# ------ #
# Exit with success
exit 0;
================================================
FILE: Sources/App/Classes/Controllers/TXAppearance.m
================================================
/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2018 - 2020 Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual and/or Codeux Software, nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import "NSObjectHelperPrivate.h"
#import "TPCPreferencesLocal.h"
#import "TXAppearance.h"
NS_ASSUME_NONNULL_BEGIN
NSString * const TXApplicationAppearanceChangedNotification = @"TXApplicationAppearanceChangedNotification";
NSString * const TXSystemAppearanceChangedNotification = @"TXSystemAppearanceChangedNotification";
@interface TXAppearancePropertyCollection ()
@property (nonatomic, copy, readwrite) NSString *appearanceName;
@property (nonatomic, assign, readwrite) TXAppearanceType appearanceType;
@property (nonatomic, assign, readwrite) BOOL isDarkAppearance;
@property (nonatomic, assign, readwrite) TXAppKitAppearanceTarget appKitAppearanceTarget;
@end
@interface TXAppearance ()
@property (nonatomic, strong, readwrite) TXAppearancePropertyCollection *properties;
@end
@implementation TXAppearance
#pragma mark -
#pragma mark Initialization
- (instancetype)init
{
if ((self = [super init])) {
[self prepareInitialState];
return self;
}
return nil;
}
- (void)prepareInitialState
{
[self updateAppearance];
[RZWorkspaceNotificationCenter() addObserver:self
selector:@selector(accessibilityDisplayOptionsDidChange:)
name:NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification
object:nil];
[NSApp addObserver:self forKeyPath:@"effectiveAppearance" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void)prepareForApplicationTermination
{
LogToConsoleTerminationProgress("Removing appearance change observers");
[RZNotificationCenter() removeObserver:self];
[NSApp removeObserver:self forKeyPath:@"effectiveAppearance"];
}
#pragma mark -
#pragma mark Properties
+ (nullable NSString *)appearanceNameForType:(TXAppearanceType)type
{
switch (type) {
case TXAppearanceTypeBigSurLight:
{
return @"BigSurLight";
}
case TXAppearanceTypeBigSurDark:
{
return @"BigSurDark";
}
default:
{
return nil;
}
}
}
#pragma mark -
#pragma mark Notifications
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString *, id> *)change context:(nullable void *)context
{
if ([keyPath isEqualToString:@"effectiveAppearance"]) {
[self applicationAppearanceChanged];
}
}
- (void)applicationAppearanceChanged
{
/* Wait until next pass of the run loop to perform
update because the effective appearance may not
be propagated to all subviews when this is called. */
XRPerformBlockAsynchronouslyOnMainQueue(^{
[self updateAppearanceBySystemChange];
});
}
- (void)systemColorsDidChange:(NSNotification *)aNote
{
[self updateAppearanceBySystemChange];
}
- (void)accessibilityDisplayOptionsDidChange:(NSNotification *)aNote
{
[self updateAppearanceBySystemChange];
}
- (void)updateAppearance
{
[self updateAppearanceBySystemChange:NO];
}
- (void)updateAppearanceBySystemChange
{
[self updateAppearanceBySystemChange:YES];
}
- (void)updateAppearanceBySystemChange:(BOOL)systemChanged
{
TXAppearanceType appearanceType = TXAppearanceTypeBigSurLight;
TXPreferredAppearance preferredAppearance = [TPCPreferences appearance];
/* Determine user's preference */
switch (preferredAppearance) {
case TXPreferredAppearanceInherited:
{
if ([TXAppearancePropertyCollection systemWideDarkModeEnabled]) {
appearanceType = TXAppearanceTypeBigSurDark;
}
break;
}
case TXPreferredAppearanceDark:
{
appearanceType = TXAppearanceTypeBigSurDark;
break;
}
default:
{
break;
}
}
BOOL isAppearanceDark = (appearanceType == TXAppearanceTypeBigSurDark);
/* Determine best appearance inheritance approach */
/* Before Mojave, appearance needs to be applied to every view.
After Mojave, views properly inherit the appearance of the parent
window or the system. If the user selects a specific appearance,
then we apply that to the parent window on Mojave and later to
allow views to inherit that. If the user selects the system
appearance, then we apply nothing and allow it to be inherited
from the system. */
TXAppKitAppearanceTarget appKitAppearanceTarget = TXAppKitAppearanceTargetNone;
if (preferredAppearance != TXPreferredAppearanceInherited) {
appKitAppearanceTarget = TXAppKitAppearanceTargetWindow;
}
/* Test for changes */
TXAppearancePropertyCollection *oldProperties = self.properties;
BOOL changeAppearance = (oldProperties == nil ||
(oldProperties.appearanceType != appearanceType) ||
(oldProperties.appKitAppearanceTarget != appKitAppearanceTarget));
if (changeAppearance == NO)
{
/* Even if the desired appearance hasn't changed, we still
signal views to perform selection update so that vibrant
views can draw correctly when the system changes. */
if (systemChanged == NO) {
return;
}
}
else
{
/* When appearance changes as a result of a system change, then we
treat it as an application change as that's more specialized. */
systemChanged = NO;
}
/* Assign new properties */
TXAppearancePropertyCollection *newProperties = [TXAppearancePropertyCollection new];
newProperties.appearanceName = [self.class appearanceNameForType:appearanceType];
newProperties.appearanceType = appearanceType;
newProperties.isDarkAppearance = isAppearanceDark;
newProperties.appKitAppearanceTarget = appKitAppearanceTarget;
self.properties = newProperties;
/* Notify observers */
if (systemChanged == NO) {
[self notifyApplicationAppearanceChanged];
} else {
[self notifySystemAppearanceChanged];
}
}
- (void)notifyApplicationAppearanceChanged
{
[RZNotificationCenter() postNotificationName:TXApplicationAppearanceChangedNotification object:self];
}
- (void)notifySystemAppearanceChanged
{
[RZNotificationCenter() postNotificationName:TXSystemAppearanceChangedNotification object:self];
}
@end
#pragma mark -
#pragma mark Property Collection
@implementation TXAppearancePropertyCollection
- (nullable NSAppearance *)appKitAppearance
{
if (self.appKitAppearanceTarget == TXAppKitAppearanceTargetNone) {
return nil;
}
if (self.isDarkAppearance) {
return [self.class appKitDarkAppearance];
} else {
return [self.class appKitLightAppearance];
}
}
- (NSString *)shortAppearanceDescription
{
if (self.isDarkAppearance == NO) {
return @"light";
} else {
return @"dark";
}
}
+ (BOOL)systemWideDarkModeEnabled
{
return ([[NSApp effectiveAppearance] bestMatchFromAppearancesWithNames:@[NSAppearanceNameDarkAqua]] != nil);
}
+ (nullable NSAppearance *)appKitDarkAppearance
{
return [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
}
+ (nullable NSAppearance *)appKitLightAppearance
{
return [NSAppearance appearanceNamed:NSAppearanceNameAqua];
}
@end
NS_ASSUME_NONNULL_END
================================================
FILE: Sources/App/Classes/Controllers/TXApplication.m
================================================
/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2010 - 2020 Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual, "Codeux Software, LLC", nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import "TDCAlert.h"
#import "TLOLocalization.h"
#import "TPCApplicationInfo.h"
#import "TXApplicationPrivate.h"
NS_ASSUME_NONNULL_BEGIN
@implementation TXApplication
+ (BOOL)checkForOtherCopiesOfTextualRunning
{
pid_t ourProcessIdentifier = [[NSProcessInfo processInfo] processIdentifier];
for (NSRunningApplication *application in RZWorkspace().runningApplications) {
if ([application.bundleIdentifier isEqualToString:@"com.codeux.apps.textual"] ||
[application.bundleIdentifier isEqualToString:@"com.codeux.apps.textual-mas"] ||
[application.bundleIdentifier isEqualToString:@"com.codeux.irc.textual5"])
{
if (application.processIdentifier == ourProcessIdentifier) {
continue;
}
BOOL continueLaunch = [TDCAlert modalAlertWithMessage:TXTLS(@"Prompts[kx4-q8]")
title:TXTLS(@"Prompts[hcb-3i]")
defaultButton:TXTLS(@"Prompts[mvh-ms]")
alternateButton:TXTLS(@"Prompts[99q-gg]")];
return continueLaunch;
}
}
return YES;
}
- (void)sendEvent:(NSEvent *)event
{
BOOL performedCustomEvent = [self performedCustomKeyboardEvent:event];
if (performedCustomEvent) {
return;
}
[super sendEvent:event];
}
- (BOOL)performedCustomKeyboardEvent:(NSEvent *)event
{
if (event.type != NSEventTypeKeyDown) {
return NO;
}
NSWindow *keyWindow = self.keyWindow;
if ([self sendCustomKeyboardEvent:event toObject:keyWindow]) {
return YES;
}
NSResponder *firstResponder = keyWindow.firstResponder;
if ([self sendCustomKeyboardEvent:event toObject:firstResponder]) {
return YES;
}
return NO;
}
- (BOOL)sendCustomKeyboardEvent:(NSEvent *)event toObject:(nullable id)object
{
if (object == nil) {
return NO;
}
if ([object respondsToSelector:@selector(performedCustomKeyboardEvent:)]) {
if ([(id)object performedCustomKeyboardEvent:event]) {
return YES;
}
}
return NO;
}
@end
NS_ASSUME_NONNULL_END
================================================
FILE: Sources/App/Classes/Controllers/TXGlobalModels.m
================================================
/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2010 - 2018 Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual, "Codeux Software, LLC", nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import <time.h>
#import "TLOLocalization.h"
#import "TXGlobalModels.h"
NS_ASSUME_NONNULL_BEGIN
#pragma mark -
#pragma mark Time
NSString * _Nullable TXFormattedTimestamp(NSDate *date, NSString *format)
{
NSCParameterAssert(date != nil);
NSCParameterAssert(format != nil);
time_t global = (time_t)date.timeIntervalSince1970;
const size_t outputBufferSize = 256;
char outputBuffer[(outputBufferSize + 1)];
struct tm *localTime = localtime(&global);
if (strftime(outputBuffer, outputBufferSize, format.UTF8String, localTime) == 0) {
return nil;
}
return @(outputBuffer);
}
NSString * _Nullable TXHumanReadableTimeInterval(NSTimeInterval dateInterval, BOOL shortValue, NSCalendarUnit orderMatrix)
{
/* Default what we will return */
if (orderMatrix == 0) {
orderMatrix = (NSCalendarUnitYear |
NSCalendarUnitMonth |
NSCalendarUnitDay |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond);
}
/* Convert calendar units to a text rep */
NSMutableArray<NSNumber *> *units = [NSMutableArray arrayWithCapacity:6];
if (orderMatrix & NSCalendarUnitYear) {
[units addObject:@(NSCalendarUnitYear)];
}
if (orderMatrix & NSCalendarUnitMonth) {
[units addObject:@(NSCalendarUnitMonth)];
}
if (orderMatrix & NSCalendarUnitDay) {
[units addObject:@(NSCalendarUnitDay)];
}
if (orderMatrix & NSCalendarUnitHour) {
[units addObject:@(NSCalendarUnitHour)];
}
if (orderMatrix & NSCalendarUnitMinute) {
[units addObject:@(NSCalendarUnitMinute)];
}
if (orderMatrix & NSCalendarUnitSecond) {
[units addObject:@(NSCalendarUnitSecond)];
}
/* Build compare information */
NSCalendar *systemCalendar = [NSCalendar currentCalendar];
NSDate *date1 = [NSDate date];
NSDate *date2 = [NSDate dateWithTimeIntervalSinceNow:dateInterval];
/* Perform comparison */
NSDateComponents *breakdownInfo = [systemCalendar components:orderMatrix fromDate:date1 toDate:date2 options:0];
if (breakdownInfo == nil) {
return nil;
}
NSMutableString *returnResult = nil;
for (NSNumber *unit in units) {
NSInteger unitValue = [breakdownInfo valueForComponent:unit.unsignedIntegerValue];
/* If results isn't zero, we show it */
if (unitValue == 0) {
continue;
}
if (unitValue < 0) {
unitValue *= (-1);
}
NSString *languageKey = nil;
if (unitValue == 1) { // plurals
languageKey = [NSString stringWithFormat:@"fko-64-%@", unit];
} else {
languageKey = [NSString stringWithFormat:@"eoq-pr-%@", unit];
}
/* shortValue returns only the first time component */
if (shortValue) {
return [NSString stringWithFormat:@"%ld %@", unitValue, TXTLS(languageKey)];
}
if (returnResult == nil) {
returnResult = [NSMutableString string];
}
if (unit == units.lastObject) {
[returnResult appendFormat:@"%ld %@", unitValue, TXTLS(languageKey)];
} else {
[returnResult appendFormat:@"%ld %@, ", unitValue, TXTLS(languageKey)];
}
}
if (returnResult.length > 0) {
return [returnResult copy];
}
/* Return "0 seconds" when there are no results. */
return [NSString stringWithFormat:@"0 %@", TXTLS(@"BasicLanguage[eoq-pr-128]")];
}
NSString * _Nullable TXFormatDateLongStyle(id dateObject, BOOL relativeOutput)
{
return TXFormatDate(dateObject, NSDateFormatterLongStyle, NSDateFormatterLongStyle, relativeOutput);
}
NSString * _Nullable TXFormatDate(id dateObject, NSDateFormatterStyle dateStyle, NSDateFormatterStyle timeStyle, BOOL relativeOutput)
{
NSCParameterAssert(dateObject != nil);
NSDateFormatter *dateFormatter = [NSDateFormatter new];
dateFormatter.doesRelativeDateFormatting = relativeOutput;
dateFormatter.lenient = YES;
dateFormatter.dateStyle = dateStyle;
dateFormatter.timeStyle = timeStyle;
NSString *resultString = nil;
if ([dateObject isKindOfClass:[NSString class]]) {
resultString = [dateFormatter stringForObjectValue:dateObject];
} else if ([dateObject isKindOfClass:[NSDate class]]) {
resultString = [dateFormatter stringFromDate:dateObject];
}
return resultString;
}
NSDateFormatter *TXSharedISOStandardDateFormatter(void)
{
static NSDateFormatter *_isoStandardDateFormatter = nil;
if (_isoStandardDateFormatter == nil) {
NSDateFormatter *dateFormatter = [NSDateFormatter new];
dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; //2011-10-19T16:40:51.620Z
_isoStandardDateFormatter = dateFormatter;
}
return _isoStandardDateFormatter;
}
#pragma mark -
#pragma mark Misc
NSUInteger TXRandomNumber(u_int32_t maximum)
{
return arc4random_uniform(maximum);
}
NSString *TXFormattedNumber(NSInteger number)
{
return [NSNumberFormatter localizedStringFromNumber:@(number) numberStyle:NSNumberFormatterDecimalStyle];
}
NSComparator NSDefaultComparator = ^(id object1, id object2)
{
return [object1 compare:object2];
};
NS_ASSUME_NONNULL_END
================================================
FILE: Sources/App/Classes/Controllers/TXMasterController.m
================================================
/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2008 - 2010 Satoshi Nakagawa <psychs AT limechat DOT net>
* Copyright (c) 2010 - 2020 Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual, "Codeux Software, LLC", nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import "BuildConfig.h"
#import "NSObjectHelperPrivate.h"
#import "OELReachability.h"
#import "TDCAlert.h"
#import "TLOEncryptionManagerPrivate.h"
#import "TLOLicenseManagerPrivate.h"
#import "TLOLocalization.h"
#import "TLOSpeechSynthesizerPrivate.h"
#import "THOPluginManagerPrivate.h"
#import "TDCLicenseManagerDialogPrivate.h"
#import "TVCLogControllerHistoricLogFilePrivate.h"
#import "TVCLogControllerInlineMediaServicePrivate.h"
#import "TVCLogControllerOperationQueuePrivate.h"
#import "TVCMainWindowPrivate.h"
#import "IRCChannelPrivate.h"
#import "IRCChannelMemberListPrivate.h"
#import "IRCCommandIndexPrivate.h"
#import "IRCExtrasPrivate.h"
#import "IRCWorldPrivate.h"
#import "TPCApplicationInfoPrivate.h"
#import "TPCPreferencesLocalPrivate.h"
#import "TPCPreferencesUserDefaults.h"
#import "TPCResourceManagerPrivate.h"
#import "TPCSandboxMigrationPrivate.h"
#import "TPCThemeControllerPrivate.h"
#import "TXMenuControllerPrivate.h"
#import "TXWindowControllerPrivate.h"
#import "TXMasterControllerPrivate.h"
#import "IRCClient.h"
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
#import <Sparkle/Sparkle.h>
#endif
NS_ASSUME_NONNULL_BEGIN
@interface TXMasterController ()
@property (nonatomic, strong, readwrite) IRCWorld *world;
@property (nonatomic, assign, readwrite) BOOL debugModeIsOn;
@property (nonatomic, assign, readwrite) BOOL ghostModeIsOn;
@property (nonatomic, assign, readwrite) BOOL applicationIsActive;
@property (nonatomic, assign, readwrite) BOOL applicationIsLaunched;
@property (nonatomic, assign, readwrite) BOOL applicationIsTerminating;
@property (nonatomic, assign, readwrite) BOOL applicationIsChangingActiveState;
@property (readonly) BOOL isSafeToPerformApplicationTermination;
@property (nonatomic, assign, readwrite) BOOL terminateHistoricLogSaveFinished;
@property (nonatomic, strong, readwrite) IBOutlet TVCMainWindow *mainWindow;
@property (nonatomic, weak, readwrite) IBOutlet TXMenuController *menuController;
@property (nonatomic, assign) NSUInteger applicationLaunchRemainder;
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
@property (nonatomic, strong, readwrite) SPUStandardUpdaterController *updateController;
#endif
@end
@implementation TXMasterController
#pragma mark -
#pragma mark Initialization
- (instancetype)init
{
if ((self = [super init])) {
[NSObject setGlobalMasterControllerClassReference:self];
[self prepareInitialState];
return self;
}
return nil;
}
- (void)prepareInitialState
{
LogToConsoleSetDefaultSubsystemToMainBundle(@"General");
NSUInteger keyboardKeys = ([NSEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask);
if ((keyboardKeys & NSEventModifierFlagControl) == NSEventModifierFlagControl) {
self.debugModeIsOn = YES;
LogToConsoleInfo("Launching in debug mode");
}
#if defined(DEBUG)
self.ghostModeIsOn = YES; // Do not use auto connect during debug
#else
if ((keyboardKeys & NSEventModifierFlagShift) == NSEventModifierFlagShift) {
self.ghostModeIsOn = YES;
LogToConsoleInfo("Launching without auto connecting to the configured servers");
}
#endif
}
- (void)awakeFromNib
{
static BOOL _awakeFromNibCalled = NO;
if (_awakeFromNibCalled == NO) {
_awakeFromNibCalled = YES;
[self _awakeFromNib];
}
}
- (void)_awakeFromNib
{
/* Migrate files and preferences */
[TPCSandboxMigration migrateResources];
/* Initialize preferences */
[TPCPreferences initPreferences];
/* Call shared instance to warm it */
[TXSharedApplication sharedAppearance];
/* We wait until -awakeFromNib to wake the window so that the menu
controller created by the main nib has time to load. */
[RZMainBundle() loadNibNamed:@"TVCMainWindow" owner:self topLevelObjects:nil];
}
- (void)applicationWakeStepOne
{
self.world = [IRCWorld new];
}
- (void)applicationWakeStepTwo
{
[IRCCommandIndex populateCommandIndex];
[self prepareNetworkReachabilityNotifier];
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerDidWakeUp:) name:NSWorkspaceDidWakeNotification object:nil];
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerWillSleep:) name:NSWorkspaceWillSleepNotification object:nil];
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerWillPowerOff:) name:NSWorkspaceWillPowerOffNotification object:nil];
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerScreenDidWake:) name:NSWorkspaceScreensDidWakeNotification object:nil];
[RZWorkspaceNotificationCenter() addObserver:self selector:@selector(computerScreenWillSleep:) name:NSWorkspaceScreensDidSleepNotification object:nil];
[RZNotificationCenter() addObserver:self selector:@selector(pluginsFinishedLoading:) name:THOPluginManagerFinishedLoadingPluginsNotification object:nil];
[RZAppleEventManager() setEventHandler:self andSelector:@selector(handleURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
[NSColorPanel setPickerMask:(NSColorPanelRGBModeMask | NSColorPanelGrayModeMask | NSColorPanelColorListModeMask | NSColorPanelWheelModeMask | NSColorPanelCrayonModeMask)];
[[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
XRPerformBlockAsynchronouslyOnGlobalQueueWithPriority(^{
[TPCResourceManager copyResourcesToApplicationSupportFolder];
}, DISPATCH_QUEUE_PRIORITY_BACKGROUND);
/* We want to guarantee some specific things happen before the
app is considered "launched" and ready to use. This property
counts down once each task completes and once it reaches 0,
then the app is considered launched. */
/* 1 is default value because we want plugins to be loaded
before we are finished launching. */
[self addObserver:self forKeyPath:@"applicationLaunchRemainder" options:NSKeyValueObservingOptionNew context:NULL];
self.applicationLaunchRemainder = 1;
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
[self prepareLicenseManager];
#endif
[self prepareThirdPartyServices];
/* Load plugins last so that -applicationDidFinishLaunching is posted
only once they have loaded and everything else has been setup. */
[sharedPluginManager() loadPlugins];
}
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString *, id> *)change context:(nullable void *)context
{
if ([keyPath isEqualToString:@"applicationLaunchRemainder"]) {
if (self.applicationLaunchRemainder == 0) {
[self applicationDidFinishLaunching];
}
}
}
- (void)pluginsFinishedLoading:(NSNotification *)notification
{
self.applicationLaunchRemainder -= 1;
}
#pragma mark -
#pragma mark Services
- (void)prepareThirdPartyServiceSparkleFramework
{
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
SPUStandardUpdaterController *controller =
[[SPUStandardUpdaterController alloc] initWithStartingUpdater:NO
updaterDelegate:(id <SPUUpdaterDelegate>)self
userDriverDelegate:nil];
self.updateController = controller;
SPUUpdater *updater = controller.updater;
if ([updater respondsToSelector:@selector(clearFeedURLFromUserDefaults)]) {
[updater performSelector:@selector(clearFeedURLFromUserDefaults)];
} else {
[RZUserDefaults() removeObjectForKey:@"SUFeedURL"];
}
NSError *error;
(void)[updater startUpdater:&error];
if (error) {
LogToConsoleError("Sparkle failed to start updater: %{public}@", error.description);
}
#endif
}
- (void)prepareThirdPartyServices
{
[self prepareThirdPartyServiceSparkleFramework];
}
- (void)prepareNetworkReachabilityNotifier
{
OELReachability *notifier = [TXSharedApplication sharedNetworkReachabilityNotifier];
notifier.reachableBlock = ^(OELReachability *reachability) {
[self.world noteReachabilityChanged:YES];
};
notifier.unreachableBlock = ^(OELReachability *reachability) {
[self.world noteReachabilityChanged:NO];
};
[notifier startNotifier];
}
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
- (void)prepareLicenseManager
{
TLOLicenseManagerSetup();
[[TXSharedApplication sharedLicenseManagerDialog] applicationDidFinishLaunching];
}
#endif
#pragma mark -
#pragma mark NSApplication Delegate
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
/* UserNotifications.framework wants delegation set before app has
finished launching. A simple access to the singleton will set this
for us which we can just do here. */
LogToConsoleDebug("Preparing notification controller singeton: %@",
sharedNotificationController().description);
}
- (void)applicationDidFinishLaunching
{
[self removeObserver:self forKeyPath:@"applicationLaunchRemainder"];
self.applicationIsLaunched = YES;
if ([self.mainWindow reloadLoadingScreen]) {
[self.world autoConnectAfterWakeup:NO];
}
[self.mainWindow maybeToggleFullscreenAfterLaunch];
}
- (void)applicationWillResignActive:(NSNotification *)notification
{
self.applicationIsChangingActiveState = YES;
}
- (void)applicationWillBecomeActive:(NSNotification *)notification
{
self.applicationIsChangingActiveState = YES;
}
- (void)applicationDidResignActive:(NSNotification *)notification
{
self.applicationIsActive = NO;
self.applicationIsChangingActiveState = NO;
}
- (void)applicationDidBecomeActive:(NSNotification *)notification
{
self.applicationIsActive = YES;
self.applicationIsChangingActiveState = NO;
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
{
if (self.applicationIsTerminating) {
return NO;
}
[self.mainWindow makeKeyAndOrderFront:nil];
return YES;
}
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
{
if (self.applicationIsTerminating) {
return NO;
}
[self.mainWindow makeKeyAndOrderFront:nil];
return YES;
}
- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)sender
{
/* This will have no effect on our app. Implement to suppress warning in console. */
return YES;
}
#pragma mark -
#pragma mark NSApplication Terminate Procedure
- (NSMenu *)applicationDockMenu:(NSApplication *)sender
{
return self.menuController.dockMenu;
}
- (BOOL)queryTerminate
{
if (self.applicationIsTerminating) {
LogToConsoleTerminationProgress("Termination is already in progress");
return YES;
}
if ([TPCPreferences confirmQuit] == NO) {
return YES;
}
BOOL stillConnected = NO;
for (IRCClient *u in worldController().clientList) {
if (u.isConnecting || u.isConnected) {
stillConnected = YES;
}
}
if (stillConnected) {
BOOL result = [TDCAlert modalAlertWithMessage:TXTLS(@"Prompts[77u-vp]")
title:TXTLS(@"Prompts[6vj-2p]")
defaultButton:TXTLS(@"Prompts[1bf-k0]")
alternateButton:TXTLS(@"Prompts[qso-2g]")];
LogToConsoleTerminationProgress("Perform termination: %{BOOL}d", result);
return result;
}
return YES;
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
if ([self queryTerminate] == NO) {
return NSTerminateCancel;
}
XRPerformBlockAsynchronouslyOnMainQueue(^{
[self performApplicationTerminationStepOne];
});
return NSTerminateLater;
}
- (BOOL)isSafeToPerformApplicationTermination
{
/* Clients are still disconnecting */
BOOL condition1 = (self.terminatingClientCount == 0);
/* Core Data is saving */
BOOL condition2 = (TVCLogControllerHistoricLogSharedInstance().isSaving == NO &&
self.terminateHistoricLogSaveFinished);
LogToConsoleTerminationProgress("Conditions: %{BOOL}d %{BOOL}d", condition1, condition2);
return (condition1 && condition2);
}
- (void)performApplicationTerminationStepOne
{
LogToConsoleTerminationProgress("Step one entry");
self.applicationIsTerminating = YES;
[[TXSharedApplication sharedAppearance] prepareForApplicationTermination];
[self.mainWindow prepareForApplicationTermination];
LogToConsoleTerminationProgress("Giving up shared application delegation");
[[NSApplication sharedApplication] setDelegate:nil];
LogToConsoleTerminationProgress("Removing workspace notification center observer");
[RZWorkspaceNotificationCenter() removeObserver:self];
LogToConsoleTerminationProgress("Removing shared notification center observer");
[RZNotificationCenter() removeObserver:self];
LogToConsoleTerminationProgress("Removing AppleScript event observer");
[RZAppleEventManager() removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
LogToConsoleTerminationProgress("Stopping reachability notifier");
[[TXSharedApplication sharedNetworkReachabilityNotifier] stopNotifier];
LogToConsoleTerminationProgress("Stopping speech synthesizer");
[[TXSharedApplication sharedSpeechSynthesizer] setIsStopped:YES];
[TVCLogControllerInlineMediaSharedInstance() prepareForApplicationTermination];
#if TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION == 1
[sharedEncryptionManager() prepareForApplicationTermination];
#endif
[self.menuController prepareForApplicationTermination];
[self performApplicationTerminationStepTwo];
}
- (void)performApplicationTerminationStepTwo
{
if (self.applicationIsTerminating == NO) {
return;
}
LogToConsoleTerminationProgress("Step two entry");
self.terminatingClientCount = worldController().clientCount;
[self.world prepareForApplicationTermination];
if (self.isSafeToPerformApplicationTermination) {
[self performApplicationTerminationStepThree];
return;
}
/* We want certain things to 100% happen before the app completely closes.
This block that is performed below loops until all these actions are completed.
Notable actions: gracefully leaving IRC, saving historic logs, etc. */
XRPerformBlockAsynchronouslyOnGlobalQueueWithPriority(^{
do {
/* We wait until this value reaches zero so that
view controllers had the chance to perform any
changes they want to historic log. */
if (self.terminatingClientCount == 0) {
[TVCLogControllerHistoricLogSharedInstance() prepareForApplicationTermination];
self.terminateHistoricLogSaveFinished = YES;
}
/* Sleep a little bit so we aren't looping a lot. */
[NSThread sleepForTimeInterval:0.5];
} while (self.isSafeToPerformApplicationTermination == NO);
XRPerformBlockAsynchronouslyOnMainQueue(^{
[self performApplicationTerminationStepThree];
});
}, DISPATCH_QUEUE_PRIORITY_HIGH);
}
- (void)performApplicationTerminationStepThree
{
if (self.applicationIsTerminating == NO) {
return;
}
LogToConsoleTerminationProgress("Step three entry");
if (self.skipTerminateSave == NO) {
LogToConsoleTerminationProgress("Saving IRC world");
[self.world save];
}
LogToConsoleTerminationProgress("Suspending member list dispatch queue");
[IRCChannelMemberList suspendMemberListSerialQueues];
LogToConsoleTerminationProgress("Unloading plugins");
[sharedPluginManager() unloadPlugins];
[windowController() prepareForApplicationTermination];
[themeController() prepareForApplicationTermination];
LogToConsoleTerminationProgress("Saving running internal");
[TPCApplicationInfo saveTimeIntervalSinceApplicationInstall];
LogToConsoleTerminationProgress("Terminate");
[NSApp replyToApplicationShouldTerminate:YES];
}
- (void)terminateGracefully
{
self.applicationIsTerminating = YES;
[RZSharedApplication() terminate:nil];
}
#pragma mark -
#pragma mark NSWorkspace Notifications
- (void)handleURLEvent:(NSAppleEventDescriptor *)event
withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
NSAppleEventDescriptor *description = [event descriptorAtIndex:1];
NSString *stringValue = description.stringValue;
[IRCExtras parseIRCProtocolURI:stringValue withDescriptor:event];
}
- (void)computerScreenWillSleep:(NSNotification *)note
{
LogToConsole("Preparing for screen sleep");
[self.world prepareForScreenSleep];
}
- (void)computerScreenDidWake:(NSNotification *)note
{
LogToConsole("Waking from screen sleep");
[self.world wakeFromScreenSleep];
}
- (void)computerWillSleep:(NSNotification *)note
{
LogToConsole("Preparing for sleep");
[self.world prepareForSleep];
[[TXSharedApplication sharedSpeechSynthesizer] setIsStopped:YES];
[[TXSharedApplication sharedSpeechSynthesizer] clearQueue];
[[TXSharedApplication sharedNetworkReachabilityNotifier] stopNotifier];
}
- (void)computerDidWakeUp:(NSNotification *)note
{
LogToConsole("Waking from sleep");
[[TXSharedApplication sharedSpeechSynthesizer] setIsStopped:NO];
[[TXSharedApplication sharedNetworkReachabilityNotifier] startNotifier];
[self.world autoConnectAfterWakeup:YES];
}
- (void)computerWillPowerOff:(NSNotification *)note
{
[self terminateGracefully];
}
#pragma mark -
#pragma mark Sparkle Delegate
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
- (void)updaterWillRelaunchApplication:(SPUUpdater *)updater
{
self.applicationIsTerminating = YES;
}
- (NSSet<NSString *> *)allowedChannelsForUpdater:(SPUUpdater *)updater
{
BOOL receiveBetaUpdates = [TPCPreferences receiveBetaUpdates];
if (receiveBetaUpdates) {
return [NSSet setWithObject:@"beta"];
}
return [NSSet set];
}
#endif
@end
NS_ASSUME_NONNULL_END
================================================
FILE: Sources/App/Classes/Controllers/TXMenuController.m
================================================
/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2008 - 2010 Satoshi Nakagawa <psychs AT limechat DOT net>
* Copyright (c) 2010 - 2020 Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual, "Codeux Software, LLC", nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import "NSObjectHelperPrivate.h"
#import "IRCClientConfig.h"
#import "IRCClientPrivate.h"
#import "IRCChannelPrivate.h"
#import "IRCChannelMode.h"
#import "IRCChannelUser.h"
#import "IRCExtrasPrivate.h"
#import "IRCISupportInfo.h"
#import "IRCUser.h"
#import "IRCWorldPrivate.h"
#import "TVCBasicTableView.h"
#import "TVCLogController.h"
#import "TVCLogViewPrivate.h"
#import "TVCLogViewInternalWK2.h"
#import "TVCMemberList.h"
#import "TVCMainWindowPrivate.h"
#import "TVCMainWindowSplitView.h"
#import "TVCMainWindowTextView.h"
#import "TLOEncryptionManagerPrivate.h"
#import "TLOLicenseManagerPrivate.h"
#import "TLOLocalization.h"
#import "TLOpenLink.h"
#import "TDCAboutDialogPrivate.h"
#import "TDCAlert.h"
#import "TDCChannelInviteSheetPrivate.h"
#import "TDCChannelModifyModesSheetPrivate.h"
#import "TDCChannelModifyTopicSheetPrivate.h"
#import "TDCChannelPropertiesSheetPrivate.h"
#import "TDCChannelSpotlightControllerPrivate.h"
#import "TDCFileTransferDialogPrivate.h"
#import "TDCInputPrompt.h"
#import "TDCLicenseManagerDialogPrivate.h"
#import "TDCNicknameColorSheetPrivate.h"
#import "TDCPreferencesControllerPrivate.h"
#import "TDCServerChangeNicknameSheetPrivate.h"
#import "TDCServerHighlightListSheetPrivate.h"
#import "TDCServerPropertiesSheetPrivate.h"
#import "TDCWelcomeSheetPrivate.h"
#import "TPCPathInfoPrivate.h"
#import "TPCPreferencesImportExport.h"
#import "TPCPreferencesLocalPrivate.h"
#import "TPCPreferencesReload.h"
#import "TPCPreferencesUserDefaults.h"
#import "TXMasterControllerPrivate.h"
#import "TXWindowControllerPrivate.h"
#import "TXMenuControllerPrivate.h"
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
#import <Sparkle/Sparkle.h>
#endif
NS_ASSUME_NONNULL_BEGIN
#define _popWindowViewIfExists(c) if ([windowController() maybeBringWindowForward:(c)]) { \
return; \
}
@interface TXMenuController () <NSMenuItemValidation>
@property (nonatomic, assign) BOOL menuIsOpen;
@property (nonatomic, assign) BOOL menuPerformedActionLastOpen;
@property (nonatomic, weak) IRCClient *pointedClient;
@property (nonatomic, weak) IRCChannel *pointedChannel;
@property (nonatomic, copy) NSString *currentSearchPhrase;
@property (readonly, nullable) TVCLogController *selectedViewController;
@property (readonly, nullable) TVCLogView *selectedViewControllerBackingView;
@property (readonly) TDCFileTransferDialog *fileTransferController;
@property (nonatomic, strong, readwrite) IBOutlet NSMenu *channelViewChannelNameMenu;
@property (nonatomic, strong, readwrite) IBOutlet NSMenu *channelViewGeneralMenu;
@property (nonatomic, strong, readwrite) IBOutlet NSMenu *channelViewURLMenu;
@property (nonatomic, strong, readwrite) IBOutlet NSMenu *dockMenu;
#if TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION == 1
@property (nonatomic, strong, readwrite) IBOutlet NSMenu *encryptionManagerStatusMenu;
#endif
@property (nonatomic, weak, readwrite) IBOutlet NSMenu *mainMenuNavigationChannelListMenu;
@property (nonatomic, weak, readwrite) IBOutlet NSMenu *mainMenuChannelMenu;
@property (nonatomic, weak, readwrite) IBOutlet NSMenu *mainMenuQueryMenu;
@property (nonatomic, weak, readwrite) IBOutlet NSMenuItem *mainMenuChannelMenuItem;
@property (nonatomic, weak, readwrite) IBOutlet NSMenuItem *mainMenuQueryMenuItem;
@property (nonatomic, weak, readwrite) IBOutlet NSMenuItem *mainMenuServerMenuItem;
@property (nonatomic, weak, readwrite) IBOutlet NSMenuItem *mainMenuWindowMenuItem;
@property (nonatomic, strong, readwrite) IBOutlet NSMenu *mainWindowSegmentedControllerCellMenu;
@property (nonatomic, strong, readwrite) IBOutlet NSMenu *serverListNoSelectionMenu;
@property (nonatomic, strong, readwrite) IBOutlet NSMenu *userControlMenu;
@property (nonatomic, weak, readwrite) IBOutlet NSMenuItem *muteNotificationsDockMenuItem;
@property (nonatomic, weak, readwrite) IBOutlet NSMenuItem *muteNotificationsFileMenuItem;
@property (nonatomic, weak, readwrite) IBOutlet NSMenuItem *muteNotificationsSoundsDockMenuItem;
@property (nonatomic, weak, readwrite) IBOutlet NSMenuItem *muteNotificationsSoundsFileMenuItem;
@end
@implementation TXMenuController
- (void)prepareInitialState
{
self.currentSearchPhrase = @"";
if ([TPCPreferences soundIsMuted]) {
self.muteNotificationsSoundsDockMenuItem.state = NSControlStateValueOn;
self.muteNotificationsSoundsFileMenuItem.state = NSControlStateValueOn;
}
[self.channelViewGeneralMenu itemWithTag:MTWKGeneralChannelMenu].submenu = [self.mainMenuChannelMenu copy];
[self setupOtherServices];
[RZNotificationCenter() addObserver:self selector:@selector(menuItemWillPerformedAction:) name:NSMenuWillSendActionNotification object:nil];
[RZNotificationCenter() addObserver:self selector:@selector(menuItemPerformedAction:) name:NSMenuDidSendActionNotification object:nil];
[RZNotificationCenter() addObserver:self selector:@selector(mainWindowSelectionChanged:) name:TVCMainWindowSelectionChangedNotification object:nil];
}
- (void)setupOtherServices
{
[self.fileTransferController startUsingDownloadDestinationURL];
}
- (void)prepareForApplicationTermination
{
LogToConsoleTerminationProgress("Preparing menu controller");
[self.fileTransferController prepareForApplicationTermination];
}
- (void)preferencesChanged
{
[self.fileTransferController clearIPAddress];
}
- (void)mainWindowSelectionChanged:(NSNotification *)notification
{
if (self.menuIsOpen == NO) {
[self resetSelectedItems];
}
/* When the selection changes, menus that may be dynamic are force
revalidated so that Command I (or other shortcuts) work with channel
selected, but not for the server console. */
NSMenuItem *channelMenu = self.mainMenuChannelMenuItem;
[self _forceMenuItemValidation:channelMenu];
NSMenuItem *queryMenu = self.mainMenuQueryMenuItem;
[self _forceMenuItemValidation:queryMenu];
}
- (void)_forceMenuValidation:(NSMenu *)menu
{
NSParameterAssert(menu != nil);
for (NSMenuItem *menuItem in menu.itemArray) {
[self _forceMenuItemValidation:menuItem];
}
}
- (void)_forceMenuItemValidation:(NSMenuItem *)menuItem
{
NSParameterAssert(menuItem != nil);
id target = menuItem.target;
if (target == nil) {
return;
}
if ([target respondsToSelector:@selector(validateMenuItem:)]) {
[target performSelector:@selector(validateMenuItem:) withObject:menuItem];
}
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
NSParameterAssert(menuItem != nil);
if (masterController().applicationIsTerminating) {
return NO;
}
/* Menu validation works in two passes:
1. First -_validateMenuItem: is called which performs validation for
the individual menu item including hiding it and related items.
2. The result is then passed to the logic below which performs more
specialized work such as disabling large group of menu items when
the trial has expired.
*/
BOOL validationResult = [self _validateMenuItem:menuItem];
if (validationResult == NO) {
return NO;
}
NSUInteger tag = menuItem.tag;
/* The submenus of the main menu are all targets of the menu
controller so that we can chose to hide or show some depending
on context. For the top most submenus, we have nothing further
to do after performing initial validation. */
switch (tag) {
case MTMainMenuApp:
case MTMainMenuFile:
case MTMainMenuEdit:
case MTMainMenuView:
case MTMainMenuServer:
case MTMainMenuChannel:
case MTMainMenuQuery:
case MTMainMenuNavigate:
case MTMainMenuWindow:
case MTMainMenuHelp:
{
return YES;
}
} // switch
/* When the main window is not the focused window or when we are
in a sheet, most items can be disabled which means at this point
we will default to disabled and allow the bottom logic to enable
only the bare essentials. */
BOOL defaultToNoForSheet = ( mainWindow().attachedSheet != nil ||
(mainWindow().mainWindow == NO &&
mainWindow().isBeneathMouse == NO));
if (defaultToNoForSheet) {
validationResult = NO;
}
/* If the app has not finished launching,
then default everything to disabled. */
if (masterController().applicationIsLaunched == NO) {
validationResult = NO;
}
/* If trial is expired, then default everything to disabled. */
BOOL isTrialExpired = NO;
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
if (TLOLicenseManagerTextualIsRegistered() == NO && TLOLicenseManagerIsTrialExpired()) {
/* Set flag letting logic know trial expired */
isTrialExpired = YES;
/* Disable everything by default except "Manage license…" */
validationResult = (tag == MTMMAppManageLicense);
} // if
#endif
/* If certain items are hidden because of sheet but not because
of the trial being expired, then enable additional items. */
if (validationResult == NO && defaultToNoForSheet && isTrialExpired == NO) {
switch (tag) {
case MTMMAppAboutApp: // "About Textual"
case MTMMAppPreferences: // "Preferences…"
case MTMMAppManageLicense: // "Manage license…"
case MTMMAppCheckForUpdates: // "Check for updates…"
case MTMMHelpAdvancedMenuEnableDeveloperMode: // "Enable Developer Mode"
case MTMMHelpAdvancedMenuHiddenPreferences: // "Hidden Preferences…"
case MTMMFileDisableAllNotifications: // "Disable All Notifications"
case MTMMFileDisableAllNotificationSounds: // "Disable All Notification Sounds"
case MTDockMenuDisableAllNotifications: // "Disable All Notifications"
case MTDockMenuDisableAllNotificationSounds: // "Disable All Notification Sounds"
{
validationResult = YES;
break;
}
} // switch
} // if
/* These are the bare minimum of menu items that must be enabled
at all times because they are essential to the entire application. */
/* This list may look incomplete but it isn't. Many menu items,
such as Undo, Cut, Copy, Quit, etc. are not a target of the menu
controller which means they never pass through this logic. */
if (validationResult == NO) {
switch (tag) {
case MTMMAppAboutApp: // "About Textual"
case MTMMAppQuitApp: // "Quit Textual & IRC"
case MTMMFilePrint: // "Print"
case MTMMFileCloseWindow: // "Close Window"
case MTMMEditPaste: // "Paste"
case MTMMViewToggleFullscreen: // "Toggle Fullscreen"
case MTMMWindowMainWindow: // "Main Window"
case MTMMHelpAcknowledgements: // "Acknowledgements"
case MTMMHelpLicenseAgreement: // "License Agreement"
case MTMMHelpPrivacyPolicy: // "Privacy Policy"
case MTMMHelpFrequentlyAskedQuestions: // "Frequently Asked Questions"
case MTMMHelpKnowledgeBaseMenu: // "Knowledge Base"
case MTMMHelpAdvancedMenu: // "Advanced"
case MTMMHelpAdvancedMenuExportPreferences: // "Export Preferences"
{
validationResult = YES;
break;
}
default:
{
if (menuItem.parentItem.tag == MTMMHelpKnowledgeBaseMenu) {
validationResult = YES;
}
break;
}
} // switch
} // if
return validationResult;
}
- (BOOL)_validateMenuItem:(NSMenuItem *)menuItem
{
NSParameterAssert(menuItem != nil);
NSUInteger tag = menuItem.tag;
IRCClient *u = mainWindow().selectedClient;
IRCChannel *c = mainWindow().selectedChannel;
switch (tag) {
case MTMainMenuChannel: // "Channel"
{
BOOL isChannel = c.isChannel;
menuItem.hidden = (isChannel == NO);
if (isChannel) {
menuItem.submenu = self.mainMenuChannelMenu;
} else {
menuItem.submenu = nil;
}
return YES;
}
case MTMainMenuQuery: // "Query"
{
BOOL isQuery = (c.isPrivateMessage || c.isUtility);
menuItem.hidden = (isQuery == NO);
if (isQuery) {
menuItem.submenu = self.mainMenuQueryMenu;
} else {
menuItem.submenu = nil;
}
return YES;
}
case MTMMAppManageLicense: // "Manage license…"
{
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 0
menuItem.hidden = YES;
#endif
return YES;
}
case MTMMAppCheckForUpdates: // "Check for Updates"
{
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 0
menuItem.hidden = YES;
#endif
return YES;
}
case MTMMFileCloseWindow: // "Close Window"
{
TXCommandWKeyAction keyAction = [TPCPreferences commandWKeyAction];
if (keyAction == TXCommandWKeyActionCloseWindow || mainWindow().keyWindow == NO) {
menuItem.title = TXTLS(@"BasicLanguage[1f6-bg]");
return YES;
}
if (u == nil) {
return NO;
}
switch (keyAction) {
case TXCommandWKeyActionPartChannel:
{
if (c == nil) {
menuItem.title = TXTLS(@"BasicLanguage[1f6-bg]");
return NO;
}
if (c.isChannel) {
menuItem.title = TXTLS(@"BasicLanguage[5td-3f]");
if (c.isActive == NO) {
return NO;
}
} else if (c.isPrivateMessage) {
menuItem.title = TXTLS(@"BasicLanguage[hri-l0]");
} else if (c.isUtility) {
menuItem.title = TXTLS(@"BasicLanguage[hri-l0]");
}
break;
}
case TXCommandWKeyActionDisconnect:
{
menuItem.title = TXTLS(@"BasicLanguage[w3a-je]", u.networkNameAlt);
if (u.isConnecting == NO && u.isConnected == NO) {
return NO;
}
break;
}
case TXCommandWKeyActionTerminate:
{
menuItem.title = TXTLS(@"BasicLanguage[x97-ro]");
break;
}
default:
{
break;
}
}
return YES;
}
case MTMMEditPaste: // "Paste"
case MTWKGeneralPaste: // "Paste" (WebView)
{
NSString *currentPasteboard = RZPasteboard().stringContent;
if (currentPasteboard.length == 0) {
return NO;
}
if (mainWindow().keyWindow) {
return mainWindowTextField().editable;
}
id firstResponder = [NSApp keyWindow].firstResponder;
if ([firstResponder respondsToSelector:@selector(isEditable)]) {
return [firstResponder isEditable];
}
return NO;
}
case MTMMViewMarkScrollback: // "Mark Scrollback"
case MTMMViewScrollbackMarker: // "Scrollback Marker"
case MTMMViewMarkAllAsRead: // "Mark All as Read"
case MTMMViewClearScrollback: // "Clear Scrollback"
case MTMMViewIncreaseFontSize: // "Increase Font Size"
case MTMMViewDecreaseFontSize: // "Decrease Font Size"
case MTMMNavigationJumpToCurrentSession: // "Jump to Current Session"
case MTMMNavigationJumpToPresent: // "Jump to Present"
{
return (self.selectedViewController != nil);
}
case MTMMViewToggleFullscreen:
{
NSWindowCollectionBehavior collectionBehavior = [NSApp keyWindow].collectionBehavior;
return ((collectionBehavior & NSWindowCollectionBehaviorFullScreenAuxiliary) == NSWindowCollectionBehaviorFullScreenAuxiliary ||
(collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary) == NSWindowCollectionBehaviorFullScreenPrimary);
}
case MTMMServerConnect: // "Connect"
{
if (u == nil) {
menuItem.hidden = NO;
return NO;
}
BOOL connected = (u.isConnected || u.isConnecting);
menuItem.hidden = connected;
return (connected == NO && u.isQuitting == NO);
}
case MTMMServerConnectWithoutProxy: // "Connect Without Proxy"
{
NSUInteger flags = ([NSEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask);
if (flags != NSEventModifierFlagShift) {
menuItem.hidden = YES;
return NO;
}
if (u == nil) {
menuItem.hidden = YES;
return NO;
}
BOOL condition = (u.isConnected || u.isConnecting ||
u.config.proxyType == IRCConnectionProxyTypeNone);
menuItem.hidden = condition;
return (condition == NO && u.isQuitting == NO);
}
case MTMMServerDisconnect: // "Disconnect"
{
BOOL connected = (u.isConnected || u.isConnecting);
menuItem.hidden = (connected == NO);
return connected;
}
case MTMMServerCancelReconnect: // "Cancel Reconnect"
{
BOOL reconnecting = u.isReconnecting;
menuItem.hidden = (reconnecting == NO);
return reconnecting;
}
case MTMMServerChannelList: // "Channel List…"
{
return u.isLoggedIn;
}
case MTMMServerChangeNickname: // "Change Nickname…"
case MTWKGeneralChangeNickname: // "Change Nickname…"
{
return u.isConnected;
}
case MTMMServerDuplicateServer: // "Duplicate Server"
case MTMMServerAddChannel: // "Add Channel…"
case MTMMServerServerProperties: // "Server Properties…"
{
return (u != nil);
}
case MTMMServerDeleteServer: // "Delete Server…"
{
return (u && u.isConnecting == NO && u.isConnected == NO);
}
case MTMMNavigationNextHighlight: // "Next Highlight"
case MTMMNavigationPreviousHighlight: // "Previous Highlight"
{
TVCLogController *viewController = self.selectedViewController;
if (viewController == nil) {
return NO;
}
return [viewController highlightAvailable:(tag == MTMMNavigationPreviousHighlight)];
}
case MTMMChannelJoinChannel: // "Join Channel"
{
menuItem.hidden = (u.isLoggedIn == NO || c.isActive);
return YES;
}
case MTMMChannelLeaveChannel: // "Leave Channel"
{
menuItem.hidden = (u.isLoggedIn == NO || c.isActive == NO);
NSMenuItem *joinChannel = [menuItem.menu itemWithTag:MTMMChannelJoinChannel];
[menuItem.menu itemWithTag:MTMMChannelLeaveChannelSeparator].hidden = (menuItem.hidden && joinChannel.hidden);
return YES;
}
case MTMMChannelAddChannel: // "Add Channel…"
{
return (u != nil);
}
case MTMMChannelViewLogs: // "View Logs"
{
return [TPCPreferences logToDiskIsEnabled];
}
case MTMMChannelModifyTopic: // "Modify Topic"
case MTMMChannelModesMenu: // "Modes"
case MTMMChannelListOfBans: // "List of Bans"
{
return (u.isLoggedIn && c.isActive);
}
case MTMMChannelListOfBanExceptions: // "List of Ban Exceptions"
{
menuItem.hidden = ([u.supportInfo isListSupported:IRCISupportInfoListTypeBanException] == NO);
return (u.isLoggedIn && c.isActive);
}
case MTMMChannelListOfInviteExceptions: // "List of Invite Exceptions"
{
menuItem.hidden = ([u.supportInfo isListSupported:IRCISupportInfoListTypeInviteException] == NO);
return (u.isLoggedIn && c.isActive);
}
case MTMMChannelListOfQuiets: // "List of Quiets"
{
menuItem.hidden = ([u.supportInfo isListSupported:IRCISupportInfoListTypeQuiet] == NO);
return (u.isLoggedIn && c.isActive);
}
case MTMMQueryQueryLogs: // "Query Logs"
{
/* Query menu is used for utility windows too so we
hide "Query Logs" for anything except private messages. */
BOOL isQuery = c.isPrivateMessage;
menuItem.hidden = (isQuery == NO);
[menuItem.menu itemWithTag:MTMMQueryCloseQuerySeparator].hidden = (isQuery == NO);
return [TPCPreferences logToDiskIsEnabled];
}
case MTMMWindowToggleVisibilityOfServerList: // "Toggle Visibility of Server List"
case MTMMWindowSortChannelList: // "Sort Channel List"
case MTMMWindowCenterWindow: // "Center Window"
case MTMMWindowResetWindowToDefaultSize: // "Reset Window to Default Size"
{
BOOL isMainWindowMain = mainWindow().mainWindow;
menuItem.hidden = (isMainWindowMain == NO);
if (tag == MTMMWindowSortChannelList) {
[menuItem.menu itemWithTag:MTMMWindowSortChannelListSeparator].hidden = (isMainWindowMain == NO);
} else if (tag == MTMMWindowResetWindowToDefaultSize) {
[menuItem.menu itemWithTag:MTMMWindowResetWindowToDefaultSizeSeparator].hidden = (isMainWindowMain == NO);
}
return YES;
}
case MTMMWindowMainWindow: // "Main Window"
{
BOOL isMainWindowMain = mainWindow().mainWindow;
BOOL isMainWindowDisabled = mainWindow().disabled;
menuItem.hidden = isMainWindowMain;
return (isMainWindowDisabled == NO);
}
case MTMMWindowToggleVisibilityOfMemberList: // "Toggle Visibility of Member List"
{
BOOL isMainWindowMain = mainWindow().mainWindow;
menuItem.hidden = (isMainWindowMain == NO);
return c.isChannel;
}
case MTMMWindowToggleWindowAppearance: // "Toggle Window Appearance"
{
BOOL isMainWindowMain = mainWindow().mainWindow;
menuItem.hidden = (isMainWindowMain == NO);
[menuItem.menu itemWithTag:MTMMWindowToggleWindowAppearanceSeparator].hidden = (isMainWindowMain == NO);
return YES;
}
case MTMMWindowAddressBook: // "Address Book"
case MTMMWindowIgnoreList: // "Ignore List"
{
BOOL isMainWindowMain = mainWindow().mainWindow;
menuItem.hidden = (isMainWindowMain == NO);
return (u != nil);
}
case MTMMWindowViewLogs: // "View Logs"
{
return [TPCPreferences logToDiskIsEnabled];
}
case MTMMWindowHighlightList: // "Highlight List"
{
BOOL isMainWindowMain = mainWindow().mainWindow;
menuItem.hidden = (isMainWindowMain == NO);
if (u == nil) {
return NO;
}
return [TPCPreferences logHighlights];
}
case MTUserControlsAddIgnore: // "Add Ignore"
{
/* To make it as efficient as possible, we only check for ignore
for the "Add Ignore" menu item. When that menu item is validated,
we validate "Modify Ignore" and "Remove Ignore" at the same time. */
NSMenuItem *modifyIgnoreMenuItem = [menuItem.menu itemWithTag:MTUserControlsModifyIgnore];
NSMenuItem *removeIgnoreMenuItem = [menuItem.menu itemWithTag:MTUserControlsRemoveIgnore];
if (c.isUtility) {
modifyIgnoreMenuItem.hidden = YES;
removeIgnoreMenuItem.hidden = YES;
menuItem.hidden = NO;
return NO;
}
/* If less than or more than one user is selected, then hide all
menu items except "Add Ignore" and disable the "Add Ignore" item. */
NSArray<IRCChannelUser *> *nicknames = [self selectedMembers:menuItem];
NSString *hostmask = nicknames.firstObject.user.hostmask;
if (nicknames.count != 1 || hostmask == nil) {
modifyIgnoreMenuItem.hidden = YES;
removeIgnoreMenuItem.hidden = YES;
menuItem.hidden = NO;
return NO;
}
/* Update visibility depending on whether ignore is available */
/* When this logic was first introduced, we kept a reference to
the ignores in the represented object of the menu item.
This was stopped because information about the ignore can
change while the menu item is still open, making the object
we will reference when action is performed garbage. */
NSArray *userIgnores = [u findIgnoresForHostmask:hostmask];
BOOL condition = (userIgnores.count == 0);
modifyIgnoreMenuItem.hidden = condition;
removeIgnoreMenuItem.hidden = condition;
menuItem.hidden = (condition == NO);
return YES;
}
case MTUserControlsModifyIgnore: // "Modify Ignore"
case MTUserControlsRemoveIgnore: // "Remove Ignore"
{
return YES;
}
case MTUserControlsInviteTo: // "Invite To…"
{
if (u.isLoggedIn == NO || c.isUtility) {
return NO;
}
NSUInteger channelCount = 0;
for (IRCChannel *e in u.channelList) {
if (c != e && e.isChannel) {
channelCount++;
}
}
return (channelCount > 0);
}
case MTUserControlsGetInfo: // "Get Info (Whois)"
case MTUserControlsClientToClientMenu: // "Client-to-Client"
{
return (u.isLoggedIn && c.isUtility == NO);
}
case MTUserControlsPrivateMessage: // "Private Message (Query)"
{
menuItem.hidden = (c.isChannel == NO);
return (u.isLoggedIn && c.isUtility == NO);
}
case MTUserControlsGiveOp: // "Give Op (+o)"
case MTUserControlsGiveHalfop: // "Give Halfop (+h)"
case MTUserControlsGiveVoice: // "Give Voice (+v)"
case MTUserControlsTakeOp: // "Take Op (-o)"
case MTUserControlsTakeHalfop: // "Take Halfop (-h)"
case MTUserControlsTakeVoice: // "Take Voice (-v)"
{
return (u.isLoggedIn && c.isActive);
}
case MTUserControlsAllModesGiven: // "All Modes Given"
{
return NO;
}
case MTUserControlsAllModesTaken: // "All Modes Taken"
{
#define _setHidden(tag, value) [menuItem.menu itemWithTag:(tag)].hidden = (value)
if (c.isChannel == NO) {
_setHidden(MTUserControlsGiveOp, YES);
_setHidden(MTUserControlsGiveHalfop, YES);
_setHidden(MTUserControlsGiveVoice, YES);
_setHidden(MTUserControlsTakeOp, YES);
_setHidden(MTUserControlsTakeHalfop, YES);
_setHidden(MTUserControlsTakeVoice, YES);
_setHidden(MTUserControlsAllModesGiven, YES);
_setHidden(MTUserControlsAllModesGivenSeparator, YES);
_setHidden(MTUserControlsAllModesTaken, YES);
_setHidden(MTUserControlsAllModesTakenSeparator, YES);
return NO;
}
_setHidden(MTUserControlsAllModesGivenSeparator, NO);
_setHidden(MTUserControlsAllModesTakenSeparator, NO);
NSArray *nicknames = [self selectedMembers:menuItem];
if (nicknames.count == 1)
{
IRCChannelUser *user = nicknames[0];
IRCUserRank userRanks = user.ranks;
BOOL UserHasModeO = ((userRanks & IRCUserRankNormalOperator) == IRCUserRankNormalOperator);
BOOL UserHasModeH = NO;
BOOL UserHasModeV = ((userRanks & IRCUserRankVoiced) == IRCUserRankVoiced);
_setHidden(MTUserControlsGiveOp, UserHasModeO);
_setHidden(MTUserControlsGiveVoice, UserHasModeV);
_setHidden(MTUserControlsTakeOp, (UserHasModeO == NO));
_setHidden(MTUserControlsTakeVoice, (UserHasModeV == NO));
BOOL halfOpModeSupported = [u.supportInfo modeSymbolIsUserPrefix:@"h"];
if (halfOpModeSupported == NO) {
_setHidden(MTUserControlsGiveHalfop, YES);
_setHidden(MTUserControlsTakeHalfop, YES);
} else {
UserHasModeH = ((userRanks & IRCUserRankHalfOperator) == IRCUserRankHalfOperator);
_setHidden(MTUserControlsGiveHalfop, UserHasModeH);
_setHidden(MTUserControlsTakeHalfop, (UserHasModeH == NO));
}
BOOL hideGiveSepItem = ((UserHasModeO == NO || UserHasModeV == NO) || (UserHasModeH == NO && halfOpModeSupported));
_setHidden(MTUserControlsAllModesGiven, hideGiveSepItem);
BOOL hideTakenSepItem = (UserHasModeO || UserHasModeH || UserHasModeV);
_setHidden(MTUserControlsAllModesTaken, hideTakenSepItem);
}
else
{
_setHidden(MTUserControlsGiveOp, NO);
_setHidden(MTUserControlsGiveHalfop, NO);
_setHidden(MTUserControlsGiveVoice, NO);
_setHidden(MTUserControlsTakeOp, NO);
_setHidden(MTUserControlsTakeHalfop, NO);
_setHidden(MTUserControlsTakeVoice, NO);
_setHidden(MTUserControlsAllModesGiven, YES);
_setHidden(MTUserControlsAllModesTaken, YES);
}
return NO;
#undef _setHidden
}
case MTUserControlsBan: // "Ban"
case MTUserControlsKick: // "Kick"
case MTUserControlsBanAndKick: // "Ban and Kick"
{
BOOL isChannel = c.isChannel;
menuItem.hidden = (isChannel == NO);
[menuItem.menu itemWithTag:MTUserControlsBanAndKickSeparator].hidden = (isChannel == NO);
return (u.isLoggedIn && isChannel && c.isActive);
}
case MTUserControlsIRCOperatorMenu: // "IRC Operator"
{
menuItem.hidden = (u.userIsIRCop == NO);
return (u.isLoggedIn && c.isUtility == NO);
}
#if TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION == 1
case MTOTRStatusButtonStartPrivateConversation:
case MTOTRStatusButtonRefreshPrivateConversation:
case MTOTRStatusButtonEndPrivateConversation:
case MTOTRStatusButtonAuthenticateChatPartner:
case MTOTRStatusButtonViewListOfFingerprints:
{
/* Even if we are not logged in, we still ask the encryption manager
to validate the menu item first so that it can hide specific menu items.
After it has done that, then we can disable if not logged in. */
if ([TPCPreferences textEncryptionIsEnabled] == NO) {
return NO;
}
if (u.isLoggedIn == NO) {
return NO;
}
BOOL valid = [sharedEncryptionManager()
validateMenuItem:menuItem
withStateOf:[u encryptionAccountNameForUser:c.name]
from:[u encryptionAccountNameForLocalUser]];
return valid;
}
#endif
case MTWKGeneralSearchWithGoogle: // "Search With Google"
{
TVCLogView *webView = self.selectedViewControllerBackingView;
if (webView == nil) {
return NO;
}
NSString *searchProviderName = [self searchProviderName];
menuItem.title = TXTLS(@"BasicLanguage[1ll-h9]", searchProviderName);
return webView.hasSelection;
}
case MTWKGeneralLookUpInDictionary: // "Look Up in Dictionary"
{
TVCLogView *webView = self.selectedViewControllerBackingView;
if (webView == nil) {
return NO;
}
NSString *selection = webView.selection;
NSUInteger selectionLength = selection.length;
if (selectionLength == 0 || selectionLength > 40) {
menuItem.title = TXTLS(@"BasicLanguage[o5l-4s]");
return NO;
}
if (selectionLength > 25) {
selection = [selection substringToIndex:24];
selection = [NSString stringWithFormat:@"%@…", selection.trim];
}
menuItem.title = TXTLS(@"BasicLanguage[zxs-yy]", selection);
return (selectionLength > 0);
}
case MTWKGeneralCopy: // "Copy" (WebView)
{
TVCLogView *webView = self.selectedViewControllerBackingView;
if (webView == nil) {
return NO;
}
return webView.hasSelection;
}
case MTWKGeneralQueryLogs: // "Query Logs" (WebKit)
{
menuItem.hidden = (c.isPrivateMessage == NO);
return [TPCPreferences logToDiskIsEnabled];
}
case MTWKGeneralChannelMenu: // "Channel" (WebKit)
{
menuItem.hidden = (c.isChannel == NO);
/* "Query Logs" will appear above this menu item,
but if this is neither channel or query, then we
have to hide the separator above that so it's not
just sitting there with nothing beneath it. */
NSMenuItem *queryLogs = [menuItem.menu itemWithTag:MTWKGeneralQueryLogs];
[menuItem.menu itemWithTag:MTWKGeneralPasteSeparator].hidden = (menuItem.hidden && queryLogs.hidden);
return YES;
}
case MTMMHelpAdvancedMenuEnableDeveloperMode: // Developer Mode
{
if ([TPCPreferences developerModeEnabled]) {
menuItem.state = NSControlStateValueOn;
} else {
menuItem.state = NSControlStateValueOff;
}
return YES;
}
case MTMainWindowSegmentedControllerAddChannel: // "Add Channel…"
{
return (u != nil);
}
default:
{
break;
}
}
return YES;
}
- (void)menuWillOpen:(NSMenu *)menu
{
self.menuIsOpen = YES;
self.pointedClient = mainWindow().selectedClient;
self.pointedChannel = mainWindow().selectedChannel;
self.menuPerformedActionLastOpen = NO;
}
- (void)menuDidClose:(NSMenu *)menu
{
self.menuIsOpen = NO;
/* This delegate callback is received before -menuItemPerformedAction:
is called. So that our selected items can be reset if the user did
not perform an action, we call -menuClosedTimer the next time the
main queue comes around. The action is performed on the current pass
which means this prevents a race. */
XRPerformBlockAsynchronouslyOnMainQueue(^{
[self _menuClosedTimer];
});
}
- (void)_menuClosedTimer
{
if (self.menuPerformedActionLastOpen) {
return;
}
[self resetSelectedItems];
}
- (void)menuItemWillPerformedAction:(NSNotification *)aNote
{
NSMenuItem *menuItem = aNote.userInfo[@"MenuItem"];
if (menuItem.target != self) {
return;
}
self.menuPerformedActionLastOpen = YES;
}
- (void)menuItemPerformedAction:(NSNotification *)aNote
{
NSMenuItem *menuItem = aNote.userInfo[@"MenuItem"];
if (menuItem.target != self) {
return;
}
[self resetSelectedItems];
}
#pragma mark -
#pragma mark Selection
- (void)resetSelectedItems
{
self.pointedClient = nil;
self.pointedChannel = nil;
}
- (nullable IRCClient *)selectedClient
{
IRCClient *pointedClient = self.pointedClient;
if (pointedClient) {
return pointedClient;
}
return mainWindow().selectedClient;
}
- (nullable IRCChannel *)selectedChannel
{
IRCChannel *pointedChannel = self.pointedChannel;
if (pointedChannel) {
return pointedChannel;
}
return mainWindow().selectedChannel;
}
- (nullable TVCLogController *)selectedViewController
{
IRCChannel *selectedChannel = self.selectedChannel;
if (selectedChannel) {
return selectedChannel.viewController;
}
return self.selectedClient.viewController;
}
- (nullable TVCLogView *)selectedViewControllerBackingView
{
TVCLogController *viewController = self.selectedViewController;
return viewController.backingView;
}
#pragma mark -
#pragma mark Selected User(s)
- (BOOL)checkSelectedMembers:(id)sender
{
return ([self selectedMembers:sender].count > 0);
}
- (NSArray<IRCChannelUser *> *)selectedMembers:(id)sender
{
return [self selectedMembers:sender returnStrings:NO];
}
- (NSArray<NSString *> *)selectedMembersNicknames:(id)sender
{
return [self selectedMembers:sender returnStrings:YES];
}
- (NSArray *)selectedMembers:(id)sender returnStrings:(BOOL)returnStrings
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isActive == NO) {
return @[];
}
/* Return a specific nickname for WebView events */
NSString *pointedNickname = nil;
if ([sender isKindOfClass:[NSMenuItem class]]) {
pointedNickname = ((NSMenuItem *)sender).userInfo;
} else {
pointedNickname = self.pointedNickname;
}
if (pointedNickname) {
if (returnStrings) {
return @[pointedNickname];
}
IRCChannelUser *user = [c findMember:pointedNickname];
if (user) {
return @[user];
}
return @[];
}
/* If we did not have a specific nickname, then query
the user list for selected rows. */
NSMutableArray *userArray = [NSMutableArray array];
NSIndexSet *selectedRows = mainWindowMemberList().selectedRowIndexes;
[selectedRows enumerateIndexesUsingBlock:^(NSUInteger index, BOOL *stop) {
IRCChannelUser *user = [mainWindowMemberList() itemAtRow:index];
if (returnStrings) {
[userArray addObject:user.user.nickname];
} else {
[userArray addObject:user];
}
}];
return [userArray copy];
}
- (void)deselectMembers:(id)sender
{
if ([sender isKindOfClass:[NSMenuItem class]]) {
if (((NSMenuItem *)sender).userInfo.length > 0) {
return; // Nothing to deselect when our sender used userInfo
}
}
if (self.pointedNickname) {
self.pointedNickname = nil;
return;
}
[mainWindowMemberList() deselectAll:sender];
}
#pragma mark -
#pragma mark Find Panel
- (void)_showFindPromptOpenDialog:(id)sender
{
void (^promptCompletionBlock)(NSString *) = ^(NSString *resultString)
{
if ([self.currentSearchPhrase isEqualToString:resultString]) {
return;
}
self.currentSearchPhrase = resultString;
TVCLogView *webView = self.selectedViewControllerBackingView;
[webView findString:resultString movingForward:YES];
};
NSString *resultString = nil;
TVCAlertResponseButton response =
[TDCInputPrompt promptWithMessage:TXTLS(@"Prompts[d2w-4o]")
title:TXTLS(@"Prompts[akr-eh]")
defaultButton:TXTLS(@"Prompts[q5h-xx]")
alternateButton:TXTLS(@"Prompts[qso-2g]")
prefillString:self.currentSearchPhrase
resultString:&resultString];
if (response == TVCAlertResponseButtonFirst) {
promptCompletionBlock(resultString);
}
}
- (void)showFindPrompt:(id)sender
{
NSParameterAssert(sender != nil);
if (mainWindow().keyWindow == NO) {
return;
}
if ([sender tag] == MTMMEditFindMenuFind || self.currentSearchPhrase.length == 0) {
[self _showFindPromptOpenDialog:sender];
return;
}
TVCLogView *webView = self.selectedViewControllerBackingView;
if ([sender tag] == MTMMEditFindMenuFindNext) {
[webView findString:self.currentSearchPhrase movingForward:YES];
} else {
[webView findString:self.currentSearchPhrase movingForward:NO];
}
}
#pragma mark -
#pragma mark Edit
- (void)copy:(id)sender
{
id firstResponder = [NSApp keyWindow].firstResponder;
if ([firstResponder respondsToSelector:@selector(copy:)]) {
[firstResponder performSelector:@selector(copy:) withObject:sender];
}
}
- (void)paste:(id)sender
{
if (mainWindow().keyWindow) {
[mainWindowTextField() focus];
[mainWindowTextField() paste:sender];
return;
}
id firstResponder = [NSApp keyWindow].firstResponder;
if ([firstResponder respondsToSelector:@selector(paste:)]) {
[firstResponder performSelector:@selector(paste:) withObject:sender];
}
}
- (void)print:(id)sender
{
if (mainWindow().keyWindow) {
TVCLogView *webView = self.selectedViewControllerBackingView;
if (webView == nil) {
return;
}
[webView print];
return;
}
id firstResponder = [NSApp keyWindow].firstResponder;
if ([firstResponder respondsToSelector:@selector(print:)]) {
[firstResponder performSelector:@selector(print:) withObject:sender];
}
}
#pragma mark -
#pragma mark Backing View
- (void)copyLogAsHtml:(id)sender
{
TVCLogView *webView = self.selectedViewControllerBackingView;
if (webView == nil) {
return;
}
[webView copyContentString];
}
- (void)openWebInspector:(id)sender
{
/*
When WebKit2 first announced, there was no way for an
app to take an NSMenu, modify it, and return the result
when a context menu is presented.
WebKit1 had a delegate method for that named:
-webView:contextMenuItemsForElement:defaultMenuItems:
WebKit2 added this delegate method, in private, but it
was not available until the next update of macOS.
'Til then, we had to fake "Inspect Element" because we
did not have access to the default implementation that
is available through the delegate method.
Apple now flags the function names that were used to do that.
This will break "Inspect Element" on one version of macOS.
Which one I don't even recall.
But it is worth it to make the review process smoother.
Sorry.
*/
(void)
[TDCAlert alertWithMessage:TXTLS(@"Prompts[kig-m1]")
title:TXTLS(@"Prompts[ujw-64]")
defaultButton:TXTLS(@"Prompts[c7s-dq]")
alternateButton:nil];
}
- (void)markScrollback:(id)sender
{
TVCLogController *viewController = self.selectedViewController;
if (viewController == nil) {
return;
}
[viewController mark];
}
- (void)gotoScrollbackMarker:(id)sender
{
TVCLogController *viewController = self.selectedViewController;
if (viewController == nil) {
return;
}
[viewController goToMark];
}
- (void)clearScrollback:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil) {
return;
}
if (c) {
[mainWindow() clearContentsOfChannel:c];
} else {
[mainWindow() clearContentsOfClient:u];
}
}
- (void)increaseLogFontSize:(id)sender
{
[mainWindow() changeTextSize:YES];
}
- (void)decreaseLogFontSize:(id)sender
{
[mainWindow() changeTextSize:NO];
}
- (NSString *)searchProviderName
{
NSDictionary *preferredWebServices =
[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"NSPreferredWebServices"];
NSDictionary *defaultSearchProvider = [preferredWebServices dictionaryForKey:@"NSWebServicesProviderWebSearch"];
NSString *searchProviderName = [defaultSearchProvider stringForKey:@"NSDefaultDisplayName"];
if (searchProviderName == nil) {
return @"Google";
}
return searchProviderName;
}
- (void)searchGoogle:(id)sender
{
TVCLogView *webView = self.selectedViewControllerBackingView;
if (webView == nil) {
return;
}
NSString *selection = webView.selection;
if (selection.length == 0) {
return;
}
NSPasteboard *searchPasteboard = [NSPasteboard pasteboardWithUniqueName];
searchPasteboard.stringContent = selection;
NSPerformService(@"Search With %WebSearchProvider@", searchPasteboard);
}
- (void)lookUpInDictionary:(id)sender
{
TVCLogView *webView = self.selectedViewControllerBackingView;
if (webView == nil) {
return;
}
NSString *selection = webView.selection;
if (selection.length == 0) {
return;
}
NSString *urlString = [NSString stringWithFormat:@"dict://%@", selection.percentEncodedString];
[TLOpenLink openWithString:urlString];
}
#pragma mark -
#pragma mark Server
- (void)connect:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil || u.isConnecting || u.isConnected || u.isQuitting) {
return;
}
[u connect];
[mainWindow() expandClient:u];
}
- (void)connectBypassingProxy:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil || u.isConnecting || u.isConnected || u.isQuitting) {
return;
}
[u connect:IRCClientConnectModeNormal bypassProxy:YES];
[mainWindow() expandClient:u];
}
- (void)disconnect:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil || (u.isConnecting == NO && u.isConnected == NO) || u.isQuitting) {
return;
}
[u quit];
}
- (void)cancelReconnection:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil) {
return;
}
[u cancelReconnect];
}
- (void)showServerChannelList:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil || u.isLoggedIn == NO) {
return;
}
[u createChannelListDialog];
[u requestChannelList];
}
- (void)addServer:(id)sender
{
[windowController() popMainWindowSheetIfExists];
TDCServerPropertiesSheet *sheet =
[[TDCServerPropertiesSheet alloc] initWithClient:nil];
sheet.delegate = self;
sheet.window = mainWindow();
[sheet startWithSelection:TDCServerPropertiesSheetSelectionDefault context:nil];
[windowController() addWindowToWindowList:sheet];
}
- (void)duplicateServer:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil) {
return;
}
IRCClientConfigMutable *config = [u.config uniqueCopyMutable];
config.connectionName = [config.connectionName stringByAppendingString:@"_"];
IRCClient *newClient = [worldController() createClientWithConfig:config reload:YES];
if (newClient.config.sidebarItemExpanded) { // Only expand new client if old was expanded already.
[mainWindow() expandClient:newClient];
}
[worldController() save];
}
- (void)deleteServer:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil || u.isConnecting || u.isConnected) {
return;
}
BOOL result = [TDCAlert modalAlertWithMessage:TXTLS(@"Prompts[etl-ss]")
title:TXTLS(@"Prompts[0kz-wd]")
defaultButton:TXTLS(@"Prompts[mvh-ms]")
alternateButton:TXTLS(@"Prompts[99q-gg]")];
if (result == NO) {
return;
}
[worldController() destroyClient:u];
[worldController() save];
}
#pragma mark -
#pragma mark Channel
- (void)joinChannel:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO || c.isActive) {
return;
}
[u joinChannel:c];
[mainWindow() select:c];
}
- (void)leaveChannel:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
/* Second boxed in condition is because this is used for queries too */
if (u == nil || c == nil || (c.isChannel && (u.isLoggedIn == NO || c.isActive == NO))) {
return;
}
if (c.isChannel) {
[u partChannel:c];
return;
}
[worldController() destroyChannel:c];
}
- (void)addChannel:(id)sender
{
[windowController() popMainWindowSheetIfExists];
IRCClient *u = self.selectedClient;
if (u == nil) {
return;
}
TDCChannelPropertiesSheet *sheet =
[[TDCChannelPropertiesSheet alloc] initWithClient:u];
sheet.delegate = self;
sheet.window = mainWindow();
[sheet start];
[windowController() addWindowToWindowList:sheet];
}
- (void)deleteChannel:(id)sender
{
IRCChannel *c = self.selectedChannel;
if (c == nil) {
return;
}
if (c.isChannel) {
BOOL result = [TDCAlert modalAlertWithMessage:TXTLS(@"Prompts[516-ms]")
title:TXTLS(@"Prompts[i8o-7z]")
defaultButton:TXTLS(@"Prompts[mvh-ms]")
alternateButton:TXTLS(@"Prompts[99q-gg]")
suppressionKey:@"delete_channel"
suppressionText:nil];
if (result == NO) {
return;
}
}
[worldController() destroyChannel:c];
[worldController() save];
}
- (void)copyUniqueIdentifier:(id)sender
{
IRCChannel *c = self.selectedChannel;
if (c == nil) {
return;
}
[RZPasteboard() setStringContent:c.uniqueIdentifier];
}
#pragma mark -
#pragma mark Other Actions
- (void)copyUrl:(id)sender
{
NSString *pointedUrl = ((NSMenuItem *)sender).userInfo;
if (pointedUrl.length == 0) {
return;
}
RZPasteboard().stringContent = pointedUrl;
}
- (void)joinChannelClicked:(id)sender
{
NSParameterAssert(sender != nil);
IRCClient *u = self.selectedClient;
if (u == nil || u.isLoggedIn == NO) {
return;
}
NSString *pointedChannelName = nil;
if ([sender isKindOfClass:[NSMenuItem class]]) {
pointedChannelName = ((NSMenuItem *)sender).userInfo;
} else if ([sender isKindOfClass:[NSString class]]) {
pointedChannelName = sender;
} else {
return;
}
if ([u stringIsChannelName:pointedChannelName] == NO) {
return;
}
IRCChannel *c = [u findChannelOrCreate:pointedChannelName];
[u joinChannel:c];
[mainWindow() select:c];
}
- (void)emptyAction:(id)sender
{
/* Empty action used to validate submenus */
}
#pragma mark -
#pragma mark Ignores
- (void)memberAddIgnore:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
NSArray *nicknames = [self selectedMembersNicknames:sender];
if (nicknames.count == 0) {
return;
}
[self deselectMembers:sender];
NSString *command = [NSString stringWithFormat:@"ignore %@", nicknames[0]];
[u sendCommand:command completeTarget:YES target:c.name];
}
- (void)memberRemoveIgnore:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
NSArray *nicknames = [self selectedMembersNicknames:sender];
if (nicknames.count == 0) {
return;
}
[self deselectMembers:sender];
NSString *command = [NSString stringWithFormat:@"unignore %@", nicknames[0]];
[u sendCommand:command completeTarget:YES target:c.name];
}
- (void)memberModifyIgnore:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil) {
return;
}
NSArray<IRCChannelUser *> *nicknames = [self selectedMembers:sender];
[self deselectMembers:sender];
/* User's hostmask and other information can change between the point
the menu item is opened and the point the action is performed.
We therefore perform a new query for ignores when performing action. */
NSString *hostmask = nicknames.firstObject.user.hostmask;
if (nicknames.count != 1 || hostmask == nil) {
return;
}
NSArray *userIgnores = [u findIgnoresForHostmask:hostmask];
/* If we have more than one user ignore, then open
the address book instead of a specific ignore. */
if (userIgnores.count == 1) {
[self showServerPropertiesSheetForClient:u
withSelection:TDCServerPropertiesSheetSelectionNewIgnoreEntry
context:userIgnores[0]];
} else {
[self showServerPropertiesSheetForClient:u
withSelection:TDCServerPropertiesSheetSelectionAddressBook
context:nil];
}
}
#pragma mark -
#pragma mark Members
- (void)memberInMemberListDoubleClicked:(id)sender
{
NSInteger rowBeneathMouse = mainWindowMemberList().rowBeneathMouse;
if (rowBeneathMouse < 0) {
return;
}
TXUserDoubleClickAction action = [TPCPreferences userDoubleClickOption];
if (action == TXUserDoubleClickActionWhois) {
[self whoisSelectedMembers:sender];
} else if (action == TXUserDoubleClickActionPrivateMessage) {
[self memberStartPrivateMessage:sender];
} else if (action == TXUserDoubleClickActionInsertTextField) {
[self memberInsertNameIntoTextField:sender];
}
}
- (void)memberInChannelViewDoubleClicked:(id)sender
{
TXUserDoubleClickAction action = [TPCPreferences userDoubleClickOption];
if (action == TXUserDoubleClickActionWhois) {
[self whoisSelectedMembers:sender];
} else if (action == TXUserDoubleClickActionPrivateMessage) {
[self memberStartPrivateMessage:sender];
} else if (action == TXUserDoubleClickActionInsertTextField) {
[self memberInsertNameIntoTextField:sender];
}
}
- (void)memberInsertNameIntoTextField:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
NSArray *nicknames = [self selectedMembersNicknames:sender];
if (nicknames.count == 0) {
return;
}
[self deselectMembers:sender];
TVCMainWindowTextView *textView = mainWindowTextField();
NSRange selectedRange = textView.selectedRange;
NSMutableString *stringToInsert = [NSMutableString string];
if (selectedRange.location > 0) {
UniChar previousCharacter = [textView.stringValue characterAtIndex:(selectedRange.location - 1)];
if ([[NSCharacterSet whitespaceCharacterSet] characterIsMember:previousCharacter] == NO) {
[stringToInsert appendString:@" "];
}
}
NSString *nicknamesString = [nicknames componentsJoinedByString:@", "];
[stringToInsert appendString:nicknamesString];
NSString *completionSuffix = [TPCPreferences tabCompletionSuffix];
if (completionSuffix != nil) {
[stringToInsert appendString:completionSuffix];
}
[textView replaceCharactersInRange:selectedRange withString:stringToInsert];
[textView resetFontColorInRange:selectedRange];
[textView focus];
}
- (void)memberSendWhois:(id)sender
{
[self whoisSelectedMembers:sender];
}
- (void)whoisSelectedMembers:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
[u sendWhois:nickname];
}
[self deselectMembers:sender];
}
- (void)memberStartPrivateMessage:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
IRCChannel *query = [u findChannelOrCreate:nickname isPrivateMessage:YES];
[mainWindow() select:query];
}
[self deselectMembers:sender];
}
- (void)memberSendCTCPPing:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
[u sendCTCPPing:nickname];
}
[self deselectMembers:sender];
}
- (void)memberSendCTCPFinger:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
[u sendCTCPQuery:nickname command:@"FINGER" text:nil];
}
[self deselectMembers:sender];
}
- (void)memberSendCTCPTime:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
[u sendCTCPQuery:nickname command:@"TIME" text:nil];
}
[self deselectMembers:sender];
}
- (void)memberSendCTCPVersion:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
[u sendCTCPQuery:nickname command:@"VERSION" text:nil];
}
[self deselectMembers:sender];
}
- (void)memberSendCTCPUserinfo:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
[u sendCTCPQuery:nickname command:@"USERINFO" text:nil];
}
[self deselectMembers:sender];
}
- (void)memberSendCTCPClientInfo:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
[u sendCTCPQuery:nickname command:@"CLIENTINFO" text:nil];
}
[self deselectMembers:sender];
}
- (void)_processModeChange:(id)sender usingCommand:(NSString *)modeCommand
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
NSArray *nicknames = [self selectedMembersNicknames:sender];
[self deselectMembers:sender];
NSString *nicknamesString = [nicknames componentsJoinedByString:@" "];
NSString *command = [NSString stringWithFormat:@"%@ %@", modeCommand, nicknamesString];
[u sendCommand:command completeTarget:YES target:c.name];
}
- (void)memberModeGiveOp:(id)sender
{
[self _processModeChange:sender usingCommand:@"OP"];
}
- (void)memberModeTakeOp:(id)sender
{
[self _processModeChange:sender usingCommand:@"DEOP"];
}
- (void)memberModeGiveHalfop:(id)sender
{
[self _processModeChange:sender usingCommand:@"HALFOP"];
}
- (void)memberModeTakeHalfop:(id)sender
{
[self _processModeChange:sender usingCommand:@"DEHALFOP"];
}
- (void)memberModeGiveVoice:(id)sender
{
[self _processModeChange:sender usingCommand:@"VOICE"];
}
- (void)memberModeTakeVoice:(id)sender
{
[self _processModeChange:sender usingCommand:@"DEVOICE"];
}
- (void)memberKickFromChannel:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
[u kick:nickname inChannel:c];
}
[self deselectMembers:sender];
}
- (void)memberBanFromChannel:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
NSString *command = [NSString stringWithFormat:@"BAN %@", nickname];
[u sendCommand:command completeTarget:YES target:c.name];
}
[self deselectMembers:sender];
}
- (void)memberKickbanFromChannel:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
NSString *command = [NSString stringWithFormat:@"KICKBAN %@ %@", nickname, [TPCPreferences defaultKickMessage]];
[u sendCommand:command completeTarget:YES target:c.name];
}
[self deselectMembers:sender];
}
- (void)memberKillFromServer:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
NSString *command = [NSString stringWithFormat:@"KILL %@ %@", nickname, [TPCPreferences IRCopDefaultKillMessage]];
[u sendCommand:command];
}
[self deselectMembers:sender];
}
- (void)memberBanFromServer:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
if ([u nicknameIsMyself:nickname]) {
[u printDebugInformation:TXTLS(@"IRC[0r1-5l]", u.serverAddress) inChannel:c];
continue;
}
NSString *command = [NSString stringWithFormat:@"GLINE %@ %@", nickname, [TPCPreferences IRCopDefaultGlineMessage]];
[u sendCommand:command];
}
[self deselectMembers:sender];
}
- (void)memberShunOnServer:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO) {
return;
}
for (NSString *nickname in [self selectedMembersNicknames:sender]) {
NSString *command = [NSString stringWithFormat:@"SHUN %@ %@", nickname, [TPCPreferences IRCopDefaultShunMessage]];
[u sendCommand:command];
}
[self deselectMembers:sender];
}
- (void)_showSetVhostPromptOpenDialog:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO) {
return;
}
NSArray *nicknames = [self selectedMembersNicknames:sender];
if (nicknames.count == 0) {
return;
}
[self deselectMembers:sender];
void (^promptCompletionBlock)(NSString *) = ^(NSString *resultString)
{
NSString *vhost = resultString.trimAndGetFirstToken;
if (vhost.length == 0) {
return;
}
for (NSString *nickname in nicknames) {
NSString *command = [NSString stringWithFormat:@"hs setall %@ %@", nickname, vhost];
[u sendCommand:command completeTarget:NO target:nil];
}
};
NSString *vhost = nil;
TVCAlertResponseButton response =
[TDCInputPrompt promptWithMessage:TXTLS(@"Prompts[2mx-jf]")
title:TXTLS(@"Prompts[7gr-e4]")
defaultButton:TXTLS(@"Prompts[c7s-dq]")
alternateButton:TXTLS(@"Prompts[qso-2g]")
prefillString:nil
resultString:&vhost];
if (response == TVCAlertResponseButtonFirst) {
promptCompletionBlock(vhost);
}
}
- (void)showSetVhostPrompt:(id)sender
{
[self _showSetVhostPromptOpenDialog:sender];
}
#pragma mark -
#pragma mark File Transfers
- (TDCFileTransferDialog *)fileTransferController
{
return [TXSharedApplication sharedFileTransferDialog];
}
- (void)showFileTransfersWindow:(id)sender
{
[self.fileTransferController show:YES restorePosition:YES];
}
- (void)memberSendFileRequest:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO) {
return;
}
NSArray *nicknames = [self selectedMembersNicknames:sender];
if (nicknames.count == 0) {
return;
}
[self deselectMembers:sender];
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
openPanel.allowsMultipleSelection = YES;
openPanel.canChooseDirectories = NO;
openPanel.canChooseFiles = YES;
openPanel.canCreateDirectories = NO;
openPanel.resolvesAliases = YES;
[openPanel beginSheetModalForWindow:mainWindow() completionHandler:^(NSInteger returnCode) {
if (returnCode != NSModalResponseOK) {
return;
}
[self.fileTransferController.fileTransferTable beginUpdates];
for (NSString *nickname in nicknames) {
for (NSURL *path in openPanel.URLs) {
[self.fileTransferController addSenderForClient:u nickname:nickname path:path.path autoOpen:YES];
}
}
[self.fileTransferController.fileTransferTable endUpdates];
}];
}
- (void)memberSendDroppedFilesToSelectedChannel:(NSArray<NSString *> *)files
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isPrivateMessage == NO) {
return;
}
[self memberSendDroppedFiles:files to:c.name];
}
- (void)memberSendDroppedFiles:(NSArray<NSString *> *)files row:(NSUInteger)row
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isPrivateMessage == NO) {
return;
}
IRCChannelUser *member = [mainWindowMemberList() itemAtRow:row];
[self memberSendDroppedFiles:files to:member.user.nickname];
}
- (void)memberSendDroppedFiles:(NSArray<NSString *> *)files to:(NSString *)nickname
{
NSParameterAssert(files != nil);
NSParameterAssert(nickname != nil);
IRCClient *u = self.selectedClient;
if (u == nil || u.isLoggedIn == NO) {
return;
}
[self.fileTransferController.fileTransferTable beginUpdates];
for (NSString *file in files) {
BOOL isDirectory = NO;
if ([RZFileManager() fileExistsAtPath:file isDirectory:&isDirectory] == NO) {
continue;
} else if (isDirectory) {
continue;
}
[self.fileTransferController addSenderForClient:u nickname:nickname path:file autoOpen:YES];
}
[self.fileTransferController.fileTransferTable endUpdates];
}
#pragma mark -
#pragma mark Logging
- (void)openLogLocation:(id)sender
{
NSURL *path = [TPCPathInfo transcriptFolderURL];
if (path == nil) {
return;
}
if ([RZFileManager() fileExistsAtURL:path]) {
[RZWorkspace() openURL:path];
return;
}
[TDCAlert modalAlertWithMessage:TXTLS(@"Prompts[f05-hu]")
title:TXTLS(@"Prompts[k55-19]")
defaultButton:TXTLS(@"Prompts[c7s-dq]")
alternateButton:nil];
}
- (void)openChannelLogs:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil) {
return;
}
NSURL *path = c.logFilePath;
if (path == nil) {
return;
}
if ([RZFileManager() fileExistsAtURL:path]) {
[RZWorkspace() openURL:path];
return;
}
[TDCAlert modalAlertWithMessage:TXTLS(@"Prompts[f05-hu]")
title:TXTLS(@"Prompts[k55-19]")
defaultButton:TXTLS(@"Prompts[c7s-dq]")
alternateButton:nil];
}
#pragma mark -
#pragma mark Help
- (void)openAcknowledgements:(id)sender
{
NSURL *Acknowledgements = [RZMainBundle() URLForResource:@"Acknowledgements" withExtension:@"pdf" subdirectory:@"Documentation"];
[RZWorkspace() openURL:Acknowledgements];
}
- (void)openHelpMenuItem:(id)sender
{
NSParameterAssert(sender != nil);
NSDictionary *_helpMenuLinks = @{
@(MTMMHelpLicenseAgreement) : @"https://help.codeux.com/textual/End-User-License-Agreement.kb",
@(MTMMHelpPrivacyPolicy) : @"https://help.codeux.com/textual/Privacy-Policy.kb",
@(MTMMHelpFrequentlyAskedQuestions) : @"https://help.codeux.com/textual/Frequently-Asked-Questions.kb",
@(MTMMHelpKBMenuKnowledgeBaseHome) : @"https://help.codeux.com/textual/home.kb",
@(MTMMHelpKBMenuChatEncryption) : @"https://help.codeux.com/textual/Off-the-Record-Messaging.kb",
@(MTMMHelpKBMenuCommandReference) : @"https://help.codeux.com/textual/Command-Reference.kb",
@(MTMMHelpKBMenuFeatureRequests) : @"https://help.codeux.com/textual/Support.kb",
@(MTMMHelpKBMenuKeyboardShortcuts) : @"https://help.codeux.com/textual/Keyboard-Shortcuts.kb",
@(MTMMHelpKBMenuMemoryManagement) : @"https://help.codeux.com/textual/Memory-Management.kb",
@(MTMMHelpKBMenuNetworkTimeouts) : @"https://help.codeux.com/textual/Network-Timeouts.kb",
@(MTMMHelpKBMenuTextFormatting) : @"https://help.codeux.com/textual/Text-Formatting.kb",
@(MTMMHelpKBMenuStylingInformation) : @"https://help.codeux.com/textual/Styles.kb",
@(MTMMHelpKBMenuConnectingWithCertificate) : @"https://help.codeux.com/textual/Using-CertFP.kb",
@(MTMMHelpKBMenuConnectingToBouncer) : @"https://help.codeux.com/textual/Connecting-to-ZNC-Bouncer.kb",
@(MTMMHelpKBMenuDCCFileTransferInformation) : @"https://help.codeux.com/textual/DCC-File-Transfer-Information.kb"
};
NSString *link = _helpMenuLinks[@([sender tag])];
[TLOpenLink openWithString:link inBackground:NO];
}
- (void)openStandaloneStoreWebpage:(id)sender
{
[TLOpenLink openWithString:@"https://www.textualapp.com/standalone-store" inBackground:NO];
}
- (void)contactSupport:(id)sender
{
[TLOpenLink openWithString:@"https://contact.codeux.com/" inBackground:NO];
}
- (void)connectToTextualHelpChannel:(id)sender
{
[IRCExtras createConnectionToServer:@"irc.libera.chat +6697" channelList:@"#textual" connectWhenCreated:YES mergeConnectionIfPossible:YES selectFirstChannelAdded:YES];
}
- (void)connectToTextualTestingChannel:(id)sender
{
[IRCExtras createConnectionToServer:@"irc.libera.chat +6697" channelList:@"#textual-testing" connectWhenCreated:YES mergeConnectionIfPossible:YES selectFirstChannelAdded:YES];
}
#pragma mark -
#pragma mark IRC
- (void)showChannelBanList:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
[u createChannelBanListSheet];
[u sendModes:@"+b" withParameters:nil inChannel:c];
}
- (void)showChannelBanExceptionList:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
[u createChannelBanExceptionListSheet];
[u sendModes:@"+e" withParameters:nil inChannel:c];
}
- (void)showChannelInviteExceptionList:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
[u createChannelInviteExceptionListSheet];
[u sendModes:@"+I" withParameters:nil inChannel:c];
}
- (void)showChannelQuietList:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
[u createChannelQuietListSheet];
[u sendModes:@"+q" withParameters:nil inChannel:c];
}
- (void)toggleChannelModerationMode:(id)sender
{
NSParameterAssert(sender != nil);
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
NSString *modeSymbol = nil;
if ([sender tag] == MTMMChannelModesMenuRemoveModerated) {
modeSymbol = @"-m";
} else {
modeSymbol = @"+m";
}
[u sendModes:modeSymbol withParameters:nil inChannel:c];
}
- (void)toggleChannelInviteMode:(id)sender
{
NSParameterAssert(sender != nil);
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
NSString *modeSymbol = nil;
if ([sender tag] == MTMMChannelModesMenuRemoveInviteOnly) {
modeSymbol = @"-i";
} else {
modeSymbol = @"+i";
}
[u sendModes:modeSymbol withParameters:nil inChannel:c];
}
#pragma mark -
#pragma mark Window
- (void)closeWindow:(id)sender
{
TXCommandWKeyAction keyAction = [TPCPreferences commandWKeyAction];
if (keyAction == TXCommandWKeyActionCloseWindow || mainWindow().keyWindow == NO) {
NSWindow *windowToClose = [NSApp keyWindow];
if (windowToClose == nil) {
windowToClose = [NSApp mainWindow];
}
if (windowToClose) {
[windowToClose performClose:sender];
}
return;
}
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil) {
return;
}
switch (keyAction) {
case TXCommandWKeyActionPartChannel:
{
if (c == nil) {
return;
}
if (c.isChannel) {
if (c.isActive == NO) {
return;
}
[u partChannel:c];
} else {
[worldController() destroyChannel:c];
}
break;
}
case TXCommandWKeyActionDisconnect:
{
if (u.isConnecting == NO && u.isConnected == NO) {
return;
}
[u quit];
break;
}
case TXCommandWKeyActionTerminate:
{
[NSApp terminate:sender];
break;
}
default:
{
break;
}
}
}
- (void)showMainWindow:(id)sender
{
[mainWindow() makeKeyAndOrderFront:sender];
}
- (void)centerMainWindow:(id)sender
{
[mainWindow() exactlyCenterWindow];
}
- (void)toggleFullscreen:(id)sender
{
[[NSApp keyWindow] toggleFullScreen:sender];
}
- (void)resetMainWindowFrame:(id)sender
{
if (mainWindow().inFullscreenMode) {
[mainWindow() toggleFullScreen:sender];
}
[mainWindow() setFrame:[mainWindow() defaultWindowFrame] display:YES animate:YES];
[mainWindow() exactlyCenterWindow];
}
- (void)sortChannelListNames:(id)sender
{
for (IRCClient *u in worldController().clientList) {
NSMutableArray *channelList = [u.channelList mutableCopy];
[channelList sortUsingComparator:^NSComparisonResult(IRCChannel *channel1, IRCChannel *channel2) {
if (channel1.isChannel && channel2.isChannel == NO) {
return NSOrderedAscending;
}
NSString *name1 = channel1.name.lowercaseString;
NSString *name2 = channel2.name.lowercaseString;
return [name1 compare:name2];
}];
if ([channelList isEqualToArray:u.channelList]) {
continue;
}
u.channelList = channelList;
[u reloadServerListItems];
}
[worldController() save];
}
- (void)markAllAsRead:(id)sender
{
[mainWindow() markAllAsRead];
}
#pragma mark -
#pragma mark Preferences
- (void)importPreferences:(id)sender
{
[TPCPreferencesImportExport importInWindow:mainWindow()];
}
- (void)exportPreferences:(id)sender
{
[TPCPreferencesImportExport exportInWindow:mainWindow()];
}
#pragma mark -
#pragma mark Off-the-Record Messaging
#if TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION == 1
#define _encryptionNotEnabled ([TPCPreferences textEncryptionIsEnabled] == NO)
- (void)encryptionStartPrivateConversation:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (_encryptionNotEnabled || u == nil || c == nil || u.isLoggedIn == NO || c.isPrivateMessage == NO) {
return;
}
[sharedEncryptionManager() beginConversationWith:[u encryptionAccountNameForUser:c.name]
from:[u encryptionAccountNameForLocalUser]];
}
- (void)encryptionRefreshPrivateConversation:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (_encryptionNotEnabled || u == nil || c == nil || u.isLoggedIn == NO || c.isPrivateMessage == NO) {
return;
}
[sharedEncryptionManager() refreshConversationWith:[u encryptionAccountNameForUser:c.name]
from:[u encryptionAccountNameForLocalUser]];
}
- (void)encryptionEndPrivateConversation:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (_encryptionNotEnabled || u == nil || c == nil || u.isLoggedIn == NO || c.isPrivateMessage == NO) {
return;
}
[sharedEncryptionManager() endConversationWith:[u encryptionAccountNameForUser:c.name]
from:[u encryptionAccountNameForLocalUser]];
}
- (void)encryptionAuthenticateChatPartner:(id)sender
{
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (_encryptionNotEnabled || u == nil || c == nil || u.isLoggedIn == NO || c.isPrivateMessage == NO) {
return;
}
[sharedEncryptionManager() authenticateUser:[u encryptionAccountNameForUser:c.name]
from:[u encryptionAccountNameForLocalUser]];
}
- (void)encryptionListFingerprints:(id)sender
{
[sharedEncryptionManager() presentListOfFingerprints];
}
- (void)encryptionWhatIsThisInformation:(id)sender
{
[TLOpenLink openWithString:@"https://help.codeux.com/textual/Off-the-Record-Messaging.kb" inBackground:NO];
}
#undef _encryptionNotEnabled
#endif
#pragma mark -
#pragma mark Notifications
- (void)toggleMuteOnNotificationsShortcutOn:(BOOL)toggleOn
{
sharedNotificationController().areNotificationsDisabled = toggleOn;
NSControlStateValue state = ((toggleOn) ? NSControlStateValueOn : NSControlStateValueOff);
self.muteNotificationsFileMenuItem.state = state;
self.muteNotificationsDockMenuItem.state = state;
}
- (void)toggleMuteOnNotificationSoundsShortcutOn:(BOOL)toggleOn
{
[TPCPreferences setSoundIsMuted:toggleOn];
NSControlStateValue state = ((toggleOn) ? NSControlStateValueOn : NSControlStateValueOff);
self.muteNotificationsSoundsDockMenuItem.state = state;
self.muteNotificationsSoundsFileMenuItem.state = state;
}
- (void)toggleMuteOnNotificationSounds:(id)sender
{
if ([TPCPreferences soundIsMuted]) {
[self toggleMuteOnNotificationSoundsShortcutOn:NO];
} else {
[self toggleMuteOnNotificationSoundsShortcutOn:YES];
}
}
- (void)toggleMuteOnNotifications:(id)sender
{
if (sharedNotificationController().areNotificationsDisabled) {
[self toggleMuteOnNotificationsShortcutOn:NO];
} else {
[self toggleMuteOnNotificationsShortcutOn:YES];
}
}
#pragma mark -
#pragma mark Appearance
- (void)resetMainWindowAppearance:(id)sender
{
[TPCPreferences setAppearance:TXPreferredAppearanceInherited];
[TPCPreferences performReloadAction:TPCPreferencesReloadActionAppearance];
}
- (void)toggleMainWindowAppearance:(id)sender
{
TXPreferredAppearance appearance = [TPCPreferences appearance];
switch (appearance) {
case TXPreferredAppearanceInherited:
{
TXAppearance *appAppearance = [TXSharedApplication sharedAppearance];
if (appAppearance.properties.isDarkAppearance == NO) {
appearance = TXPreferredAppearanceDark;
} else {
appearance = TXPreferredAppearanceLight;
}
break;
}
case TXPreferredAppearanceLight:
{
appearance = TXPreferredAppearanceDark;
break;
}
case TXPreferredAppearanceDark:
{
appearance = TXPreferredAppearanceLight;
break;
}
} // switch()
[TPCPreferences setAppearance:appearance];
[TPCPreferences performReloadAction:TPCPreferencesReloadActionAppearance];
}
- (void)toggleServerListVisibility:(id)sender
{
[mainWindow().contentSplitView toggleServerListVisibility];
}
- (void)toggleMemberListVisibility:(id)sender
{
mainWindowMemberList().isHiddenByUser = (mainWindowMemberList().isHiddenByUser == NO);
[mainWindow().contentSplitView toggleMemberListVisibility];
}
- (void)forceReloadTheme:(id)sender
{
[mainWindow() reloadTheme];
}
#pragma mark -
#pragma mark License Manager
- (void)manageLicense:(id)sender
{
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
[self manageLicense:sender activateLicenseKey:nil licenseKeyPassedByArgument:NO];
#endif
}
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
- (void)manageLicense:(id)sender activateLicenseKey:(nullable NSString *)licenseKey
{
[self manageLicense:sender activateLicenseKey:licenseKey licenseKeyPassedByArgument:NO];
}
- (void)manageLicense:(id)sender activateLicenseKeyWithURL:(NSURL *)licenseKeyURL
{
NSParameterAssert(licenseKeyURL != nil);
NSString *path = licenseKeyURL.path;
if (path == nil) {
return;
}
NSCharacterSet *slashCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"/"];
NSString *licenseKey = [path stringByTrimmingCharactersInSet:slashCharacterSet];
if (licenseKey.length == 0) {
return;
}
[self manageLicense:sender activateLicenseKey:licenseKey licenseKeyPassedByArgument:NO];
}
- (void)manageLicense:(id)sender activateLicenseKey:(nullable NSString *)licenseKey licenseKeyPassedByArgument:(BOOL)licenseKeyPassedByArgument
{
TDCLicenseManagerDialog *licenseDialog = [TXSharedApplication sharedLicenseManagerDialog];
[licenseDialog show];
if (licenseKey) {
[licenseDialog activateLicenseKey:licenseKey silently:licenseKeyPassedByArgument];
}
}
#endif
#pragma mark -
#pragma mark Developer
- (void)toggleDeveloperMode:(id)sender
{
[TPCPreferences setDeveloperModeEnabled:([TPCPreferences developerModeEnabled] == NO)];
[TPCPreferences performReloadAction:TPCPreferencesReloadActionIRCCommandCache];
}
- (void)resetDoNotAskMePopupWarnings:(id)sender
{
NSDictionary *settings = [RZUserDefaults() dictionaryRepresentation];
for (NSString *key in settings) {
if ([key hasPrefix:TDCAlertSuppressionPrefix] == NO) {
continue;
}
[RZUserDefaults() setBool:NO forKey:key];
}
}
#pragma mark -
#pragma mark Sparkle Framework
- (void)checkForUpdates:(id)sender
{
#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
SPUStandardUpdaterController *controller = masterController().updateController;
[controller checkForUpdates:sender];
#endif
}
#pragma mark -
#pragma mark Navigation
- (void)navigateToTreeItemAtURL:(NSURL *)url
{
NSParameterAssert(url != nil);
NSString *path = url.path;
if (path == nil) {
return;
}
NSCharacterSet *slashCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"/"];
NSString *identifier = [path stringByTrimmingCharactersInSet:slashCharacterSet];
if (identifier.length == 0) {
return;
}
[self navigateToTreeItemWithIdentifier:identifier];
}
- (void)navigateToTreeItemWithIdentifier:(NSString *)identifier
{
NSParameterAssert(identifier != nil);
/* Do not use assert for this condition so we
don't crash user when we open a malformed URL. */
if (identifier.length != 36) {
return;
}
IRCTreeItem *item = [worldController() findItemWithId:identifier];
if (item == nil) {
return;
}
[self navigateToTreeItem:item];
}
- (void)navigateToTreeItem:(IRCTreeItem *)item
{
NSParameterAssert(item != nil);
[mainWindow() select:item];
}
- (void)populateNavigationChannelList
{
[self.mainMenuNavigationChannelListMenu removeAllItems];
NSUInteger channelCount = 0;
for (IRCClient *u in worldController().clientList) {
NSMenu *channelSubmenu = [NSMenu new];
NSMenuItem *clientMenuItem = [NSMenuItem new];
clientMenuItem.title = u.name;
clientMenuItem.submenu = channelSubmenu;
for (IRCChannel *c in u.channelList) {
NSMenuItem *channelMenuItem = nil;
if (channelCount >= 10) {
channelMenuItem = [NSMenuItem menuItemWithTitle:c.name
target:self
action:@selector(_navigateToChannelInNavigationList:)];
} else {
NSUInteger keyboardIndex = (channelCount + 1);
if (keyboardIndex == 10) {
keyboardIndex = 0; // Have 0 as the last item.
}
channelMenuItem = [NSMenuItem menuItemWithTitle:c.name
target:self
action:@selector(_navigateToChannelInNavigationList:)
keyEquivalent:[NSString stringWithUniChar:('0' + keyboardIndex)]
keyEquivalentMask:NSEventModifierFlagCommand];
}
channelMenuItem.userInfo = [worldController() pasteboardStringForItem:c];
[channelSubmenu addItem:channelMenuItem];
channelCount += 1;
}
[self.mainMenuNavigationChannelListMenu addItem:clientMenuItem];
}
}
- (void)_navigateToChannelInNavigationList:(NSMenuItem *)sender
{
IRCTreeItem *treeItem = [worldController() findItemWithPasteboardString:sender.userInfo];
if (treeItem == nil) {
return;
}
[mainWindow() select:treeItem];
}
- (void)performNavigationAction:(id)sender
{
NSParameterAssert(sender != nil);
IRCClient *u = self.selectedClient;
if (u == nil) {
return;
}
switch ([sender tag]) {
case MTMMNavigationServersMenuNextServer:
{
[mainWindow() selectNextServer:sender];
break;
}
case MTMMNavigationServersMenuPreviousServer:
{
[mainWindow() selectPreviousServer:sender];
break;
}
case MTMMNavigationServersMenuNextActiveServer:
{
[mainWindow() selectNextActiveServer:sender];
break;
}
case MTMMNavigationServersMenuPreviousActiveServer:
{
[mainWindow() selectPreviousActiveServer:sender];
break;
}
case MTMMNavigationChannelsMenuNextChannel:
{
[mainWindow() selectNextChannel:sender];
break;
}
case MTMMNavigationChannelsMenuPreviousChannel:
{
[mainWindow() selectPreviousChannel:sender];
break;
}
case MTMMNavigationChannelsMenuNextActiveChannel:
{
[mainWindow() selectNextActiveChannel:sender];
break;
}
case MTMMNavigationChannelsMenuPreviousActiveChannel:
{
[mainWindow() selectPreviousActiveChannel:sender];
break;
}
case MTMMNavigationChannelsMenuNextUnreadChannel:
{
[mainWindow() selectNextUnreadChannel:sender];
break;
}
case MTMMNavigationChannelsMenuPreviousUnreadChannel:
{
[mainWindow() selectPreviousUnreadChannel:sender];
break;
}
case MTMMNavigationMoveBackward:
{
[mainWindow() selectPreviousWindow:sender];
break;
}
case MTMMNavigationMoveForward:
{
[mainWindow() selectNextWindow:sender];
break;
}
case MTMMNavigationPreviousSelection:
{
[mainWindow() selectPreviousSelection:sender];
break;
}
} // switch()
}
- (void)onNextHighlight:(id)sender
{
TVCLogController *viewController = self.selectedViewController;
if (viewController == nil) {
return;
}
[viewController nextHighlight];
}
- (void)onPreviousHighlight:(id)sender
{
TVCLogController *viewController = self.selectedViewController;
if (viewController == nil) {
return;
}
[viewController previousHighlight];
}
- (void)jumpToCurrentSession:(id)sender
{
TVCLogController *viewController = self.selectedViewController;
if (viewController == nil) {
return;
}
[viewController jumpToCurrentSession];
}
- (void)jumpToPresent:(id)sender
{
TVCLogController *viewController = self.selectedViewController;
if (viewController == nil) {
return;
}
[viewController jumpToPresent];
}
#pragma mark -
#pragma mark Channel Properties Sheet
- (void)showChannelPropertiesSheet:(id)sender
{
[windowController() popMainWindowSheetIfExists];
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || c.isChannel == NO) {
return;
}
TDCChannelPropertiesSheet *sheet =
[[TDCChannelPropertiesSheet alloc] initWithChannel:c];
sheet.delegate = self;
sheet.window = mainWindow();
[sheet start];
[windowController() addWindowToWindowList:sheet];
}
- (void)channelPropertiesSheet:(TDCChannelPropertiesSheet *)sender onOk:(IRCChannelConfig *)config
{
IRCClient *u = sender.client;
if (u == nil) {
return;
}
IRCChannel *c = sender.channel;
if (c == nil) {
[worldController() createChannelWithConfig:config onClient:u];
[mainWindow() expandClient:u];
return;
}
[c updateConfig:config];
[worldController() save];
}
- (void)channelPropertiesSheetWillClose:(TDCChannelPropertiesSheet *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Channel Invite Sheet
- (void)memberSendInvite:(id)sender
{
[windowController() popMainWindowSheetIfExists];
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO || c.isActive == NO) {
return;
}
NSArray *nicknames = [self selectedMembersNicknames:sender];
if (nicknames.count == 0) {
return;
}
[self deselectMembers:sender];
NSMutableArray<NSString *> *channels = [NSMutableArray array];
for (IRCChannel *e in u.channelList) {
if (c != e && e.isChannel) {
[channels addObject:e.name];
}
}
if (channels.count == 0) {
return;
}
TDCChannelInviteSheet *sheet =
[[TDCChannelInviteSheet alloc] initWithNicknames:nicknames onClient:u];
sheet.delegate = (id)self;
sheet.window = mainWindow();
[sheet startWithChannels:channels];
[windowController() addWindowToWindowList:sheet];
}
- (void)channelInviteSheet:(TDCChannelInviteSheet *)sender onSelectChannel:(NSString *)channelName
{
IRCClient *u = sender.client;
if (u == nil || u.isLoggedIn == NO) {
return;
}
for (NSString *nickname in sender.nicknames) {
[u sendInviteTo:nickname toJoinChannelNamed:channelName];
}
}
- (void)channelInviteSheetWillClose:(TDCChannelInviteSheet *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Address Book Sheet
- (void)showAddressBook:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil) {
return;
}
[self showServerPropertiesSheetForClient:u
withSelection:TDCServerPropertiesSheetSelectionAddressBook
context:nil];
}
- (void)showIgnoreList:(id)sender
{
[self showAddressBook:sender];
}
#pragma mark -
#pragma mark Welcome Sheet
- (void)showWelcomeSheet:(id)sender
{
[windowController() popMainWindowSheetIfExists];
TDCWelcomeSheet *sheet =
[[TDCWelcomeSheet alloc] initWithWindow:mainWindow()];
sheet.delegate = (id)self;
[sheet start];
[windowController() addWindowToWindowList:sheet];
}
- (void)welcomeSheet:(TDCWelcomeSheet *)sender onOk:(IRCClientConfig *)config
{
IRCClient *u = [worldController() createClientWithConfig:config reload:YES];
[mainWindow() expandClient:u];
[worldController() save];
[u connect];
[u selectFirstChannelInChannelList];
}
- (void)welcomeSheetWillClose:(TDCWelcomeSheet *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark About Window
- (void)showAboutWindow:(id)sender
{
_popWindowViewIfExists(@"TDCAboutDialog");
TDCAboutDialog *dialog = [TDCAboutDialog new];
dialog.delegate = (id)self;
[dialog show];
[windowController() addWindowToWindowList:dialog];
}
- (void)aboutDialogWillClose:(TDCAboutDialog *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Server Properties Sheet
- (void)showServerPropertiesSheetForClient:(IRCClient *)client withSelection:(TDCServerPropertiesSheetSelection)selection context:(nullable id)context
{
NSParameterAssert(client != nil);
[windowController() popMainWindowSheetIfExists];
TDCServerPropertiesSheet *sheet = [[TDCServerPropertiesSheet alloc] initWithClient:client];
sheet.delegate = self;
sheet.window = mainWindow();
[sheet startWithSelection:selection context:context];
[windowController() addWindowToWindowList:sheet];
}
- (void)showServerPropertiesSheet:(id)sender
{
IRCClient *u = self.selectedClient;
if (u == nil) {
return;
}
[self showServerPropertiesSheetForClient:u
withSelection:TDCServerPropertiesSheetSelectionDefault
context:nil];
}
- (void)serverPropertiesSheet:(TDCServerPropertiesSheet *)sender onOk:(IRCClientConfig *)config
{
IRCClient *u = sender.client;
if (u == nil) {
u = [worldController() createClientWithConfig:config reload:YES];
[mainWindow() expandClient:u];
[worldController() save];
return;
}
BOOL sameEncoding = (config.primaryEncoding == u.config.primaryEncoding);
[u updateConfig:config];
if (sameEncoding == NO) {
[mainWindow() reloadTheme];
}
[mainWindow() reloadTreeGroup:u];
[worldController() save];
}
- (void)serverPropertiesSheetWillClose:(TDCServerPropertiesSheet *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Highlight List Sheet
- (void)showServerHighlightList:(id)sender
{
[windowController() popMainWindowSheetIfExists];
IRCClient *u = self.selectedClient;
if (u == nil) {
return;
}
TDCServerHighlightListSheet *sheet =
[[TDCServerHighlightListSheet alloc] initWithClient:u];
sheet.delegate = (id)self;
sheet.window = mainWindow();
[sheet start];
[windowController() addWindowToWindowList:sheet];
}
- (void)serverHighlightListSheetWillClose:(TDCServerHighlightListSheet *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Nickname Color Sheet
- (void)memberChangeColor:(NSString *)nickname
{
NSParameterAssert(nickname != nil);
[windowController() popMainWindowSheetIfExists];
IRCClient *u = self.selectedClient;
if (u == nil) {
return;
}
TDCNicknameColorSheet *sheet =
[[TDCNicknameColorSheet alloc] initWithNickname:nickname];
sheet.delegate = (id)self;
sheet.window = mainWindow();
[sheet start];
[windowController() addWindowToWindowList:sheet];
}
- (void)nicknameColorSheetOnOk:(TDCNicknameColorSheet *)sender
{
[mainWindow() reloadTheme];
}
- (void)nicknameColorSheetWillClose:(TDCNicknameColorSheet *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Channel Topic Sheet
- (void)showChannelModifyTopicSheet:(id)sender
{
[windowController() popMainWindowSheetIfExists];
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || c.isChannel == NO) {
return;
}
TDCChannelModifyTopicSheet *sheet =
[[TDCChannelModifyTopicSheet alloc] initWithChannel:c];
sheet.delegate = (id)self;
sheet.window = mainWindow();
[sheet start];
[windowController() addWindowToWindowList:sheet];
}
- (void)channelModifyTopicSheet:(TDCChannelModifyTopicSheet *)sender onOk:(NSString *)topic
{
IRCClient *u = sender.client;
IRCChannel *c = sender.channel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
[u sendTopicTo:topic inChannel:c];
}
- (void)channelModifyTopicSheetWillClose:(TDCChannelModifyTopicSheet *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Channel Mode Sheet
- (void)showChannelModifyModesSheet:(id)sender
{
[windowController() popMainWindowSheetIfExists];
IRCClient *u = self.selectedClient;
IRCChannel *c = self.selectedChannel;
if (u == nil || c == nil || c.isChannel == NO) {
return;
}
TDCChannelModifyModesSheet *sheet =
[[TDCChannelModifyModesSheet alloc] initWithChannel:c];
sheet.delegate = (id)self;
sheet.window = mainWindow();
[sheet start];
[windowController() addWindowToWindowList:sheet];
}
- (void)channelModifyModesSheet:(TDCChannelModifyModesSheet *)sender onOk:(IRCChannelModeContainer *)modes
{
IRCClient *u = sender.client;
IRCChannel *c = sender.channel;
if (u == nil || c == nil || u.isLoggedIn == NO || c.isChannel == NO) {
return;
}
NSString *changeString = [c.modeInfo getChangeCommand:modes];
if (changeString.length == 0) {
return;
}
[u sendModes:changeString withParameters:nil inChannel:c];
}
- (void)channelModifyModesSheetWillClose:(TDCChannelModifyModesSheet *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Channel Spotlight Window
- (void)showChannelSpotlightWindow:(id)sender
{
_popWindowViewIfExists(@"TDCChannelSpotlightController");
TDCChannelSpotlightController *dialog = [TDCChannelSpotlightController new];
dialog.delegate = (id)self;
[dialog show];
[windowController() addWindowToWindowList:dialog];
}
- (void)channelSpotlightController:(TDCChannelSpotlightController *)sender selectChannel:(IRCChannel *)channel
{
[mainWindow() select:channel];
}
- (void)channelSpotlightControllerWillClose:(TDCChannelSpotlightController *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Change Nickname Sheet
- (void)showServerChangeNicknameSheet:(id)sender
{
[windowController() popMainWindowSheetIfExists];
IRCClient *u = self.selectedClient;
if (u == nil || u.isLoggedIn == NO) {
return;
}
TDCServerChangeNicknameSheet *sheet =
[[TDCServerChangeNicknameSheet alloc] initWithClient:u];
sheet.delegate = (id)self;
sheet.window = mainWindow();
[sheet start];
[windowController() addWindowToWindowList:sheet];
}
- (void)serverChangeNicknameSheet:(TDCServerChangeNicknameSheet *)sender didInputNickname:(NSString *)nickname
{
IRCClient *u = sender.client;
if (u == nil || u.isConnected == NO) {
return;
}
[u changeNickname:nickname];
}
- (void)serverChangeNicknameSheetWillClose:(TDCServerChangeNicknameSheet *)sender
{
[windowController() removeWindowFromWindowList:sender];
}
#pragma mark -
#pragma mark Preferences Dialog
- (void)showPreferencesWindow:(id)sender
{
[self showPreferencesWindowWithSelection:TDCPreferencesControllerSelectionDefault];
}
- (void)showNotificationPreferences:(id)sender
{
[self showPreferencesWindowWithSelection:TDCPreferencesControllerSelectionNotifications];
}
- (void)showStylePreferences:(id)sender
{
[self showPreferencesWindowWithSelection:TDCPreferencesControllerSelectionStyle];
}
- (void)showHiddenPreferences:(id)sender
{
[self showPreferencesWindowWithSelection:TDCPreferencesControllerSelectionHiddenPreferences];
}
- (void)showPreferencesWindowWithSelection:(TDCPreferencesControllerSelection)selection
{
TDCPreferencesController *openWindow = [windowController() windowFromWindowList:@"TDCPreferencesController"];
if (openWindow) {
[openWindow show:selection];
return;
}
TDCPreferencesController *controller =
[TDCPreferencesController new];
controller.delegate = (id)self;
[controller show:selection];
[windowController() addWindowToWindowList:controller];
}
- (void)preferencesDialogWillClose:(TDCPreferencesController *)sender
{
[TPCPreferences performReloadAction:(TPCPreferencesReloadActionHighlightKeywords |
TPCPreferencesReloadActionPreferencesChanged)];
[windowController() removeWindowFromWindowList:sender];
}
@end
#pragma mark -
#pragma mark Main Window Proxy
@implementation TXMenuControllerMainWindowProxy
- (void)openStandaloneStoreWebpage:(id)sender
{
[menuController() openStandaloneStoreWebpage:sender];
}
- (void)manageLicense:(id)sender
{
[menuController() manageLicense:sender];
}
- (void)showWelcomeSheet:(id)sender
{
[menuController() showWelcomeSheet:sender];
}
@end
NS_ASSUME_NONNULL_END
================================================
FILE: Sources/App/Classes/Controllers/TXSharedApplication.m
================================================
/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2010 - 2020 Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual, "Codeux Software, LLC", nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import "BuildConfig.h"
#import "OELReachability.h"
#import "TXAppearance.h"
#import "TXMasterController.h"
#import "TXMenuController.h"
#import "TXWindowControllerPrivate.h"
#import "TPCThemeController.h"
#import "THOPluginManagerPrivate.h"
#import "IRCWorld.h"
#import "TLOEncryptionManagerPrivate.h"
#import "TLONotificationController.h"
#import "TLOSpeechSynthesizerPrivate.h"
#import "TDCFileTransferDialogPrivate.h"
#import "TDCLicenseManagerDialogPrivate.h"
#import "TVCLogControllerOperationQueuePrivate.h"
#import "TXSharedApplicationPrivate.h"
NS_ASSUME_NONNULL_BEGIN
NSString * const TXErrorDomain = @"TextualErrorDomain";
#define _defineSharedInstance(si_name, si_class, si_init_method) \
+ (si_class *)si_name \
{ \
static id sharedSelf = nil; \
\
static dispatch_once_t onceToken; \
\
dispatch_once(&onceToken, ^{ \
sharedSelf = [si_class si_init_method]; \
}); \
\
return sharedSelf; \
}
@implementation TXSharedApplication
_defineSharedInstance(sharedAppearance, TXAppearance, new)
#if TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION == 1
_defineSharedInstance(sharedEncryptionManager, TLOEncryptionManager, new)
#endif
_defineSharedInstance(sharedNetworkReachabilityNotifier, OELReachability, reachabilityForInternetConnection)
_defineSharedInstance(sharedNotificationController, TLONotificationController, new)
_defineSharedInstance(sharedPluginManager, THOPluginManager, new)
_defineSharedInstance(sharedPrintingQueue, TVCLogControllerPrintingOperationQueue, new)
_defineSharedInstance(sharedSpeechSynthesizer, TLOSpeechSynthesizer, new)
_defineSharedInstance(sharedThemeController, TPCThemeController, new)
_defineSharedInstance(sharedWindowController, TXWindowController, new)
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
_defineSharedInstance(sharedLicenseManagerDialog, TDCLicenseManagerDialog, new)
#endif
_defineSharedInstance(sharedFileTransferDialog, TDCFileTransferDialog, new)
os_log_t ApplicationTerminationLogSubsystem(void)
{
static os_log_t cachedValue = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cachedValue = os_log_create(TXBundleBuildProductIdentifierCString, "Termination");
});
return cachedValue;
}
@end
#pragma mark -
@implementation NSObject (TXSharedApplicationObjectExtension)
__weak static TXMasterController *TXGlobalMasterControllerClassReference;
+ (void)setGlobalMasterControllerClassReference:(id)masterController
{
TXGlobalMasterControllerClassReference = masterController;
}
- (TXMasterController *)masterController
{
return TXGlobalMasterControllerClassReference;
}
+ (TXMasterController *)masterController
{
return TXGlobalMasterControllerClassReference;
}
@end
NS_ASSUME_NONNULL_END
================================================
FILE: Sources/App/Classes/Controllers/TXWindowController.m
================================================
/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2010 - 2020 Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual, "Codeux Software, LLC", nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import "TXMasterController.h"
#import "TVCMainWindow.h"
#import "TXWindowControllerPrivate.h"
NS_ASSUME_NONNULL_BEGIN
@interface TXWindowController ()
@property (nonatomic, strong) NSMutableDictionary *windowObjects;
@end
@implementation TXWindowController
- (instancetype)init
{
if ((self = [super init])) {
[self prepareInitialState];
return self;
}
return nil;
}
- (void)prepareInitialState
{
self.windowObjects = [NSMutableDictionary dictionary];
}
- (void)prepareForApplicationTermination
{
LogToConsoleTerminationProgress("Preparing window controller");
@synchronized(self.windowObjects) {
[self.windowObjects removeAllObjects];
self.windowObjects = nil;
}
}
+ (NSString *)windowDescriptionForWindow:(id)window
{
return [self windowDescriptionForWindow:window inRelationTo:nil];
}
+ (NSString *)windowDescriptionForWindow:(id)window inRelationTo:(nullable id)relatedObject
{
NSParameterAssert(window != nil);
NSString *windowClass = NSStringFromClass([window class]);
if (relatedObject == nil) {
return windowClass;
}
return [NSString stringWithFormat:@"%@ -> %@", windowClass, [relatedObject description]];
}
- (void)addWindowToWindowList:(id)window
{
[self addWindowToWindowList:window inRelationTo:nil];
}
- (void)addWindowToWindowList:(id)window inRelationTo:(nullable id)relatedObject
{
NSString *windowDescription = [self.class windowDescriptionForWindow:window inRelationTo:relatedObject];
[self addWindowToWindowList:window withDescription:windowDescription];
}
- (void)addWindowToWindowList:(id)window withDescription:(NSString *)windowDescription
{
NSParameterAssert(window != nil);
NSAssert([window respondsToSelector:@selector(window)],
@"'window' does not respond to -window");
@synchronized(self.windowObjects) {
self.windowObjects[windowDescription] = window;
}
}
- (void)removeWindowFromWindowList:(id)window
{
[self removeWindowFromWindowList:window inRelationTo:nil];
}
- (void)removeWindowFromWindowList:(id)window inRelationTo:(nullable id)relatedObject
{
NSParameterAssert(window != nil);
if ([window isKindOfClass:[NSArray class]]) {
for (id object in window) {
[self removeWindowFromWindowList:object inRelationTo:relatedObject];
}
return; // Do not continue...
}
BOOL windowWasString = NO;
NSString *windowDescription = nil;
if ([window isKindOfClass:[NSString class]]) {
windowWasString = YES;
windowDescription = window;
} else {
windowDescription = [self.class windowDescriptionForWindow:window inRelationTo:relatedObject];
}
if (windowDescription == nil) {
return; // Cannot continue...
}
@synchronized(self.windowObjects) {
if (self.windowObjects[windowDescription] == nil && windowWasString == NO) {
windowDescription = [self.windowObjects firstKeyForObject:window];
}
if (windowDescription) {
[self.windowObjects removeObjectForKey:windowDescription];
}
}
}
- (nullable id)windowFromWindowList:(NSString *)windowDescription
{
NSParameterAssert(windowDescription != nil);
@synchronized(self.windowObjects) {
return self.windowObjects[windowDescription];
}
}
- (NSArray *)windowsFromWindowList:(NSArray<NSString *> *)windowDescriptions
{
NSParameterAssert(windowDescriptions != nil);
@synchronized(self.windowObjects) {
NSMutableArray *returnedValues = [NSMutableArray array];
for (id windowDescription in windowDescriptions) {
if ([windowDescription isKindOfClass:[NSString class]] == NO) {
continue;
}
id windowObject = self.windowObjects[windowDescription];
if (windowObject) {
[returnedValues addObject:windowObject];
}
}
return [returnedValues copy];
}
}
- (BOOL)maybeBringWindowForward:(NSString *)windowDescription
{
NSParameterAssert(windowDescription != nil);
id windowObject = [self windowFromWindowList:windowDescription];
if (windowObject) {
NSWindow *window = [windowObject window];
[window makeKeyAndOrderFront:nil];
return YES;
}
return NO;
}
- (void)popMainWindowSheetIfExists
{
NSWindow *attachedSheet = mainWindow().attachedSheet;
if (attachedSheet == nil) {
return;
}
[attachedSheet close];
}
@end
NS_ASSUME_NONNULL_END
================================================
FILE: Sources/App/Classes/Dialogs/Channel Spotlight/TDCChannelSpotlightAppearance.m
================================================
/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2018 - 2020Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual, "Codeux Software, LLC", nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import "NSObjectHelperPrivate.h"
#import "TVCAppearancePrivate.h"
#import "TDCChannelSpotlightControlsPrivate.h"
#import "TDCChannelSpotlightAppearanceInternal.h"
NS_ASSUME_NONNULL_BEGIN
@interface TDCChannelSpotlightAppearance ()
#pragma mark -
#pragma mark Search Field
@property (nonatomic, copy, nullable, readwrite) NSColor *searchFieldTextColor;
@property (nonatomic, copy, nullable, readwrite) NSColor *searchFieldCompletionTextColor;
@property (nonatomic, copy, nullable, readwrite) NSColor *searchFieldNoResultsTextColor;
#pragma mark -
#pragma mark Search Result
@property (nonatomic, copy, nullable, readwrite) NSColor *searchResultRowSelectionColorActiveWindow;
@property (nonatomic, copy, nullable, readwrite) NSColor *searchResultRowSelectionColorInactiveWindow;
@property (nonatomic, assign, readwrite) BOOL searchResultRowEmphasized;
@property (nonatomic, copy, nullable, readwrite) NSColor *searchResultChannelNameTextColor;
@property (nonatomic, copy, nullable, readwrite) NSColor *searchResultChannelDescriptionTextColor;
@property (nonatomic, copy, nullable, readwrite) NSColor *searchResultKeyboardShortcutTextColor;
@property (nonatomic, assign, readwrite) CGFloat searchResultKeyboardShortcutDeselectedOffset;
@property (nonatomic, assign, readwrite) CGFloat searchResultKeyboardShortcutSelectedOffset;
@property (nonatomic, copy, nullable, readwrite) NSColor *searchResultSelectedTextColor;
@end
@implementation TDCChannelSpotlightAppearance
#pragma mark -
#pragma mark Initialization
- (nullable instancetype)initWithWindow:(TDCChannelSpotlightPanel *)window
{
NSParameterAssert(window != nil);
NSURL *appearanceLocation = [self.class appearanceLocation];
BOOL forRetinaDisplay = window.runningInHighResolutionMode;
if ((self = [super initWithAppearanceAtURL:appearanceLocation forRetinaDisplay:forRetinaDisplay])) {
[self prepareInitialState];
return self;
}
return nil;
}
+ (NSURL *)appearanceLocation
{
return [RZMainBundle() URLForResource:@"TDCChannelSpotlightAppearance" withExtension:@"plist"];
}
- (void)prepareInitialState
{
NSDictionary *properties = self.appearanceProperties;
NSDictionary *searchField = properties[@"Search Field"];
self.searchFieldTextColor = [self colorInGroup:searchField withKey:@"controlTextColor"];
self.searchFieldCompletionTextColor = [self colorInGroup:searchField withKey:@"completionTextColor"];
self.searchFieldNoResultsTextColor = [self colorInGroup:searchField withKey:@"noResultsTextColor"];
NSDictionary *searchResult = properties[@"Search Result"];
self.searchResultRowSelectionColorActiveWindow = [self colorInGroup:searchResult withKey:@"selectionColor" forActiveWindow:YES];
self.searchResultRowSelectionColorInactiveWindow = [self colorInGroup:searchResult withKey:@"selectionColor" forActiveWindow:NO];
self.searchResultRowEmphasized = [searchResult boolForKey:@"rowEmphasized"];
self.searchResultChannelNameTextColor = [self colorInGroup:searchResult withKey:@"channelNameTextColor"];
self.searchResultChannelDescriptionTextColor = [self colorInGroup:searchResult withKey:@"channelDescriptionTextColor"];
self.searchResultKeyboardShortcutTextColor = [self colorInGroup:searchResult withKey:@"keyboardShortcutTextColor"];
self.searchResultKeyboardShortcutDeselectedOffset = [self measurementInGroup:searchResult withKey:@"keyboardShortcutDeselectedOffset"];
self.searchResultKeyboardShortcutSelectedOffset = [self measurementInGroup:searchResult withKey:@"keyboardShortcutSelectedOffset"];
self.searchResultSelectedTextColor = [self colorInGroup:searchResult withKey:@"selectedTextColor"];
[self flushAppearanceProperties];
}
@end
NS_ASSUME_NONNULL_END
================================================
FILE: Sources/App/Classes/Dialogs/Channel Spotlight/TDCChannelSpotlightController.m
================================================
/* *********************************************************************
* _____ _ _
* |_ _|____ _| |_ _ _ __ _| |
* | |/ _ \ \/ / __| | | |/ _` | |
* | | __/> <| |_| |_| | (_| | |
* |_|\___/_/\_\\__|\__,_|\__,_|_|
*
* Copyright (c) 2010 - 2020 Codeux Software, LLC & respective contributors.
* Please see Acknowledgements.pdf for additional information.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Textual, "Codeux Software, LLC", nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*********************************************************************** */
#import "NSObjectHelperPrivate.h"
#import "TXMasterController.h"
#import "IRCAddressBookUserTracking.h"
#import "IRCClient.h"
#import "IRCWorld.h"
#import "TPCPreferencesLocal.h"
#import "TPCPreferencesUserDefaults.h"
#import "TLOLicenseManagerPrivate.h"
#import "TLOLocalization.h"
#import "TVCMainWindowPrivate.h"
#import "TDCLicenseManagerDialogPrivate.h"
#import "TDCChannelSpotlightAppearanceInternal.h"
#import "TDCChannelSpotlightSearchResultPrivate.h"
#import "TDCChannelSpotlightSearchResultsTablePrivate.h"
#import "TDCChannelSpotlightControlsPrivate.h"
#import "TDCChannelSpotlightControllerInternal.h"
NS_ASSUME_NONNULL_BEGIN
@interface TDCChannelSpotlightController () <NSTableViewDataSource, NSTableViewDelegate>
@property (nonatomic, strong, readwrite) TDCChannelSpotlightAppearance *userInterfaceObjects;
@property (nonatomic, weak) IBOutlet NSVisualEffectView *visualEffectView;
@property (nonatomic, weak) IBOutlet NSTextField *noResultsLabel;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *noResultsLabelLeadingConstraint;
@property (nonatomic, weak) IBOutlet NSView *searchResultsView;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *searchResultsViewHeightConstraint;
@property (nonatomic, weak) IBOutlet NSTextField *searchField;
@property (nonatomic, weak) IBOutlet NSTableView *searchResultsTable;
@property (nonatomic, strong) IBOutlet NSArrayController *searchResultsController;
@property (nonatomic, strong) id mouseEventMonitor;
@end
@implementation TDCChannelSpotlightController
#pragma mark -
#pragma mark Initialization
- (instancetype)init
{
if ((self = [super init])) {
[self prepareInitialState];
return self;
}
return nil;
}
- (void)prepareInitialState
{
[RZMainBundle() loadNibNamed:@"TDCChannelSpotlightController" owner:self topLevelObjects:nil];
self.searchResultsTable.doubleAction = @selector(delegatePostSelectChannelForDoubleClickedRow:);
self.mouseEventMonitor =
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown
handler:^NSEvent *(NSEvent *event) {
return [self respondToKeyDownEvent:event];
}];
self.searchResultsController.sortDescriptors = @[
[NSSortDescriptor sortDescriptorWithKey:@"distance" ascending:NO selector:@selector(compare:)]
];
[RZNotificationCenter() addObserver:self selector:@selector(applicationAppearanceChanged:) name:TXApplicationAppearanceChangedNotification object:nil];
[RZNotificationCenter() addObserver:self selector:@selector(channelListChanged:) name:IRCClientChannelListWasModifiedNotification object:nil];
[RZNotificationCenter() addObserver:self selector:@selector(clientListChanged:) name:IRCWorldClientListWasModifiedNotification object:nil];
[RZNotificationCenter() addObserver:self selector:@selector(mainWindowSelectionChanged:) name:TVCMainWindowSelectionChangedNotification object:nil];
[RZNotificationCenter() addObserver:self selector:@selector(preferencesChanged:) name:TPCPreferencesUserDefaultsDidChangeNotification object:nil];
#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
[RZNotificationCenter() addObserver:self selector:@selector(licenseManagerDeactivatedLicense:) name:TDCLicenseManagerDeactivatedLicenseNotification object:nil];
[RZNotificationCenter() addObserver:self selector:@selector(licenseManagerTrialExpired:) name:TDCLicenseManagerTrialExpiredNotification object:nil];
#endif
[self populateArrayController];
[self applicationAppearanceChanged];
[self.noResultsLabelLeadingConstraint archiveConstant];
[self.searchResultsViewHeightConstraint archiveConstant];
[self updatePredicate];
}
#pragma mark -
#pragma mark Appearance
- (void)applicationAppearanceChanged:(NSNotification *)notification
{
[self applicationAppearanceChanged];
}
- (void)applicationAppearanceChanged
{
TDCChannelSpotlightAppearance *appearance = [[TDCChannelSpotlightAppearance alloc] initWithWindow:(id)self.window];
self.userInterfaceObjects = appearance;
[self updateVibrancyWithAppearance:appearance];
[self updateControlsWithAppearance:appearance];
[self updateSearchResultsSelection];
}
- (void)updateVibrancyWithAppearance:(TDCChannelSpotlightAppearance *)appearance
{
NSParameterAssert(appearance != nil);
NSAppearance *appKitAppearance = appearance.appKitAppearance;
switch (appearance.appKitAppearanceTarget) {
case TXAppKitAppearanceTargetWindow:
{
self.window.appearance = appKitAppearance;
break;
}
default:
{
break;
}
} // switch()
}
- (void)updateControlsWithAppearance:(TDCChannelSpotlightAppearance *)appearance
{
NSParameterAssert(appearance != nil);
self.searchField.textColor = appearance.searchFieldTextColor;
self.noResultsLabel.textColor = appearance.searchFieldNoResultsTextColor;
}
- (void)updateControlsState
{
NSString *searchString = self.searchField.stringValue;
if (searchString.length == 0) {
self.noResultsLabel.stringValue = @"";
[self.noResultsLabelLeadingConstraint zeroOutConstant];
[self.searchResultsViewHeightConstraint zeroOutConstant];
return;
}
if (self.searchResultsCount == 0) {
self.noResultsLabel.stringValue = TXTLS(@"TDCChannelSpotlightController[tyv-p6]");
[self.noResultsLabelLeadingConstraint restoreArchivedConstant];
[self.searchResultsViewHeightConstraint zeroOutConstant];
return;
}
self.noResultsLabel.stringValue = @"";
[self.noResultsLabelLeadingConstraint zeroOutConstant];
[self.searchResultsViewHeightConstraint restoreArchivedConstant];
[self selectFirstSearchResultIfNecessary];
}
- (void)updateSearchResultsSelection
{
NSTableView *table = self.searchResultsTable;
[table invalidateBackgroundForSelection];
}
#pragma mark -
#pragma mark Events
- (nullable NSEvent *)respondToKeyDownEvent:(NSEvent *)event
{
if (self.window.isKeyWindow == NO) {
return event;
}
switch (event.keyCode) {
case 18 ... 23: // 0-9 (top row)
case 25 ... 26:
case 28 ... 29:
case 82 ... 92: // 0-9 (number pad)
{
NSUInteger keyboardKeys = (event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask);
keyboardKeys &= ~NSEventModifierFlagNumericPad;
if (keyboardKeys == NSEventModifierFlagCommand) {
return [self handleCommandNumberEvent:event];
}
return event;
}
case 36: // return
case 76: // enter
{
[self delegatePostSelectChannelForSelectedRow];
return nil;
}
case 53: // escape
{
[self clearSearchStringOrClose];
return nil;
}
case 126: // arrow up
case 125: // arrow down
case 116: // page up
case 121: // page down
{
return [self handlePageUpDownEvent:event];
}
}
return event;
}
- (nullable NSEvent *)handlePageUpDownEvent:(NSEvent *)event
{
NSUInteger searchResultsCount = self.searchResultsCount;
if (searchResultsCount == 0) {
return nil;
}
NSInteger selectedRow = self.searchResultsTable.selectedRow;
// Wrap around table when we reach the top or bottom
if (event.keyCode == 126 || event.keyCode == 116) { // up
if (selectedRow == 0) {
[self.searchResultsTable selectItemAtIndex:(searchResultsCount - 1)];
return nil;
}
} else if (event.keyCode == 125 || event.keyCode == 121) { // down
if (selectedRow == (searchResultsCount - 1)) {
[self.searchResultsTable selectItemAtIndex:0];
return nil;
}
gitextract_u26s7vme/
├── .gitignore
├── .gitmodules
├── Common/
│ ├── Documents/
│ │ └── Acknowledgements.docx
│ └── Release DMG.dmgCanvas/
│ └── Disk Image
├── Configurations/
│ ├── Build/
│ │ ├── Code Signing Identity.xcconfig
│ │ ├── Common/
│ │ │ ├── Foundation Debug.xcconfig
│ │ │ ├── Foundation.xcconfig
│ │ │ ├── Preserve Symbols.xcconfig
│ │ │ ├── Textual App.xcconfig
│ │ │ ├── Textual Extensions.xcconfig
│ │ │ ├── Textual.xcconfig
│ │ │ ├── XPC Service - ICL Extensions.xcconfig
│ │ │ ├── XPC Service - ICL.xcconfig
│ │ │ └── XPC Services.xcconfig
│ │ ├── Debug/
│ │ │ ├── Enabled Features.xcconfig
│ │ │ ├── Textual App.xcconfig
│ │ │ ├── Textual Extensions.xcconfig
│ │ │ ├── Textual.xcconfig
│ │ │ ├── XPC Service - ICL Extensions.xcconfig
│ │ │ ├── XPC Service - ICL.xcconfig
│ │ │ └── XPC Services.xcconfig
│ │ ├── README.md
│ │ └── Standard Release/
│ │ ├── Enabled Features.xcconfig
│ │ ├── Textual App.xcconfig
│ │ ├── Textual Extensions.xcconfig
│ │ ├── Textual.xcconfig
│ │ ├── XPC Service - ICL Extensions.xcconfig
│ │ ├── XPC Service - ICL.xcconfig
│ │ └── XPC Services.xcconfig
│ ├── ExportArchiveConfiguration.plist
│ └── Sandbox/
│ └── Inherited.entitlements
├── README.md
├── Sources/
│ ├── App/
│ │ ├── Build Scripts/
│ │ │ ├── BuildExtensions.sh
│ │ │ ├── BuildFrameworks.sh
│ │ │ ├── BuildServices.sh
│ │ │ ├── ExportArchive.sh
│ │ │ ├── MergeSwift.sh
│ │ │ ├── PostprocessSparkle.sh
│ │ │ ├── UpdateFeatureFlags.sh
│ │ │ └── UpdateVersionInfo.sh
│ │ ├── Classes/
│ │ │ ├── Controllers/
│ │ │ │ ├── TXAppearance.m
│ │ │ │ ├── TXApplication.m
│ │ │ │ ├── TXGlobalModels.m
│ │ │ │ ├── TXMasterController.m
│ │ │ │ ├── TXMenuController.m
│ │ │ │ ├── TXSharedApplication.m
│ │ │ │ └── TXWindowController.m
│ │ │ ├── Dialogs/
│ │ │ │ ├── Channel Spotlight/
│ │ │ │ │ ├── TDCChannelSpotlightAppearance.m
│ │ │ │ │ ├── TDCChannelSpotlightController.m
│ │ │ │ │ ├── TDCChannelSpotlightControls.m
│ │ │ │ │ ├── TDCChannelSpotlightSearchResult.m
│ │ │ │ │ └── TDCChannelSpotlightSearchResultsTable.m
│ │ │ │ ├── File Transfers/
│ │ │ │ │ ├── TDCFileTransferDialog.m
│ │ │ │ │ ├── TDCFileTransferDialogTableCell.m
│ │ │ │ │ └── TDCFileTransferDialogTransferController.m
│ │ │ │ ├── License Manager/
│ │ │ │ │ └── Standalone/
│ │ │ │ │ ├── TDCLicenseManagerDialog.m
│ │ │ │ │ ├── TDCLicenseManagerMigrateAppStoreSheet.m
│ │ │ │ │ ├── TDCLicenseManagerRecoverLostLicenseSheet.m
│ │ │ │ │ ├── TDCLicenseUpgradeActivateSheet.m
│ │ │ │ │ ├── TDCLicenseUpgradeCommonActions.m
│ │ │ │ │ ├── TDCLicenseUpgradeDialog.m
│ │ │ │ │ └── TDCLicenseUpgradeEligibilitySheet.m
│ │ │ │ ├── Preferences/
│ │ │ │ │ ├── TDCPreferencesController.m
│ │ │ │ │ ├── TDCPreferencesNotificationConfiguration.m
│ │ │ │ │ └── TDCPreferencesUserStyleSheet.m
│ │ │ │ ├── Server Endpoint/
│ │ │ │ │ ├── TDCServerEndpointListSheet.m
│ │ │ │ │ └── TDCServerEndpointListSheetTable.m
│ │ │ │ ├── TDCAboutDialog.m
│ │ │ │ ├── TDCAddressBookSheet.m
│ │ │ │ ├── TDCAlert.m
│ │ │ │ ├── TDCChannelBanListSheet.m
│ │ │ │ ├── TDCChannelInviteSheet.m
│ │ │ │ ├── TDCChannelModifyModesSheet.m
│ │ │ │ ├── TDCChannelModifyTopicSheet.m
│ │ │ │ ├── TDCChannelPropertiesNotificationConfiguration.m
│ │ │ │ ├── TDCChannelPropertiesSheet.m
│ │ │ │ ├── TDCHighlightEntrySheet.m
│ │ │ │ ├── TDCInputPrompt.m
│ │ │ │ ├── TDCNicknameColorSheet.m
│ │ │ │ ├── TDCProgressIndicatorSheet.m
│ │ │ │ ├── TDCServerChangeNicknameSheet.m
│ │ │ │ ├── TDCServerChannelListDialog.m
│ │ │ │ ├── TDCServerHighlightListSheet.m
│ │ │ │ ├── TDCServerPropertiesSheet.m
│ │ │ │ ├── TDCSheetBase.m
│ │ │ │ ├── TDCWelcomeSheet.m
│ │ │ │ └── TDCWindowBase.m
│ │ │ ├── Headers/
│ │ │ │ ├── External Libraries/
│ │ │ │ │ ├── GTMEncodeHTML.h
│ │ │ │ │ └── OELReachability.h
│ │ │ │ ├── IRC.h
│ │ │ │ ├── IRCAddressBook.h
│ │ │ │ ├── IRCAddressBookUserTracking.h
│ │ │ │ ├── IRCChannel.h
│ │ │ │ ├── IRCChannelConfig.h
│ │ │ │ ├── IRCChannelMemberList.h
│ │ │ │ ├── IRCChannelMode.h
│ │ │ │ ├── IRCChannelUser.h
│ │ │ │ ├── IRCClient.h
│ │ │ │ ├── IRCClientConfig.h
│ │ │ │ ├── IRCColorFormat.h
│ │ │ │ ├── IRCCommandIndex.h
│ │ │ │ ├── IRCConnection.h
│ │ │ │ ├── IRCHighlightLogEntry.h
│ │ │ │ ├── IRCHighlightMatchCondition.h
│ │ │ │ ├── IRCISupportInfo.h
│ │ │ │ ├── IRCMessage.h
│ │ │ │ ├── IRCModeInfo.h
│ │ │ │ ├── IRCNetworkList.h
│ │ │ │ ├── IRCNumerics.h
│ │ │ │ ├── IRCPrefix.h
│ │ │ │ ├── IRCSendingMessage.h
│ │ │ │ ├── IRCServer.h
│ │ │ │ ├── IRCTreeItem.h
│ │ │ │ ├── IRCUser.h
│ │ │ │ ├── IRCUserRelations.h
│ │ │ │ ├── IRCWorld.h
│ │ │ │ ├── Internal/
│ │ │ │ │ ├── IRCAddressBookInternal.h
│ │ │ │ │ ├── IRCChannelConfigInternal.h
│ │ │ │ │ ├── IRCChannelUserInternal.h
│ │ │ │ │ ├── IRCClientConfigInternal.h
│ │ │ │ │ ├── IRCHighlightLogEntryInternal.h
│ │ │ │ │ ├── IRCHighlightMatchConditionInternal.h
│ │ │ │ │ ├── IRCMessageInternal.h
│ │ │ │ │ ├── IRCModeInfoInternal.h
│ │ │ │ │ ├── IRCPrefixInternal.h
│ │ │ │ │ ├── IRCServerInternal.h
│ │ │ │ │ ├── IRCUserInternal.h
│ │ │ │ │ ├── TDCChannelPropertiesSheetInternal.h
│ │ │ │ │ ├── TDCChannelSpotlightAppearanceInternal.h
│ │ │ │ │ ├── TDCChannelSpotlightControllerInternal.h
│ │ │ │ │ ├── TDCFileTransferDialogInternal.h
│ │ │ │ │ └── TVCLogLineInternal.h
│ │ │ │ ├── NSColorHelper.h
│ │ │ │ ├── NSStringHelper.h
│ │ │ │ ├── NSViewHelper.h
│ │ │ │ ├── Private/
│ │ │ │ │ ├── ICLPayloadLocalPrivate.h
│ │ │ │ │ ├── IRCAddressBookMatchCachePrivate.h
│ │ │ │ │ ├── IRCAddressBookUserTrackingPrivate.h
│ │ │ │ │ ├── IRCChannelConfigPrivate.h
│ │ │ │ │ ├── IRCChannelMemberListControllerPrivate.h
│ │ │ │ │ ├── IRCChannelMemberListPrivate.h
│ │ │ │ │ ├── IRCChannelModePrivate.h
│ │ │ │ │ ├── IRCChannelPrivate.h
│ │ │ │ │ ├── IRCChannelUserPrivate.h
│ │ │ │ │ ├── IRCClientConfigPrivate.h
│ │ │ │ │ ├── IRCClientPrivate.h
│ │ │ │ │ ├── IRCClientRequestedCommandsPrivate.h
│ │ │ │ │ ├── IRCColorFormatPrivate.h
│ │ │ │ │ ├── IRCCommandIndexPrivate.h
│ │ │ │ │ ├── IRCConnectionPrivate.h
│ │ │ │ │ ├── IRCExtrasPrivate.h
│ │ │ │ │ ├── IRCHighlightLogEntryPrivate.h
│ │ │ │ │ ├── IRCISupportInfoPrivate.h
│ │ │ │ │ ├── IRCMessageBatchPrivate.h
│ │ │ │ │ ├── IRCMessagePrivate.h
│ │ │ │ │ ├── IRCServerPrivate.h
│ │ │ │ │ ├── IRCTimerCommandPrivate.h
│ │ │ │ │ ├── IRCTreeItemPrivate.h
│ │ │ │ │ ├── IRCUserNicknameColorStyleGeneratorPrivate.h
│ │ │ │ │ ├── IRCUserPersistentStorePrivate.h
│ │ │ │ │ ├── IRCUserPrivate.h
│ │ │ │ │ ├── IRCUserRelationsPrivate.h
│ │ │ │ │ ├── IRCWorldPrivate.h
│ │ │ │ │ ├── NSTableVIewHelperPrivate.h
│ │ │ │ │ ├── NSViewHelperPrivate.h
│ │ │ │ │ ├── SwiftBridgingHeaderPrivate.h
│ │ │ │ │ ├── TDCAboutDialogPrivate.h
│ │ │ │ │ ├── TDCAddressBookSheetPrivate.h
│ │ │ │ │ ├── TDCChannelBanListSheetPrivate.h
│ │ │ │ │ ├── TDCChannelInviteSheetPrivate.h
│ │ │ │ │ ├── TDCChannelModifyModesSheetPrivate.h
│ │ │ │ │ ├── TDCChannelModifyTopicSheetPrivate.h
│ │ │ │ │ ├── TDCChannelPropertiesNotificationConfigurationPrivate.h
│ │ │ │ │ ├── TDCChannelPropertiesSheetPrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightAppearancePrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightControllerPrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightControlsPrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightSearchResultPrivate.h
│ │ │ │ │ ├── TDCChannelSpotlightSearchResultsTablePrivate.h
│ │ │ │ │ ├── TDCFileTransferDialogPrivate.h
│ │ │ │ │ ├── TDCFileTransferDialogTableCellPrivate.h
│ │ │ │ │ ├── TDCFileTransferDialogTransferControllerPrivate.h
│ │ │ │ │ ├── TDCHighlightEntrySheetPrivate.h
│ │ │ │ │ ├── TDCLicenseManagerDialogPrivate.h
│ │ │ │ │ ├── TDCLicenseManagerMigrateAppStoreSheetPrivate.h
│ │ │ │ │ ├── TDCLicenseManagerRecoverLostLicenseSheetPrivate.h
│ │ │ │ │ ├── TDCLicenseUpgradeActivateSheetPrivate.h
│ │ │ │ │ ├── TDCLicenseUpgradeCommonActionsPrivate.h
│ │ │ │ │ ├── TDCLicenseUpgradeDialogPrivate.h
│ │ │ │ │ ├── TDCLicenseUpgradeEligibilitySheetPrivate.h
│ │ │ │ │ ├── TDCNicknameColorSheetPrivate.h
│ │ │ │ │ ├── TDCPreferencesControllerPrivate.h
│ │ │ │ │ ├── TDCPreferencesNotificationConfigurationPrivate.h
│ │ │ │ │ ├── TDCPreferencesUserStyleSheetPrivate.h
│ │ │ │ │ ├── TDCProgressIndicatorSheetPrivate.h
│ │ │ │ │ ├── TDCServerChangeNicknameSheetPrivate.h
│ │ │ │ │ ├── TDCServerChannelListDialogPrivate.h
│ │ │ │ │ ├── TDCServerEndpointListSheetPrivate.h
│ │ │ │ │ ├── TDCServerEndpointListSheetTablePrivate.h
│ │ │ │ │ ├── TDCServerHighlightListSheetPrivate.h
│ │ │ │ │ ├── TDCServerPropertiesSheetPrivate.h
│ │ │ │ │ ├── TDCSharedProtocolDefinitionsPrivate.h
│ │ │ │ │ ├── TDCWelcomeSheetPrivate.h
│ │ │ │ │ ├── THOPluginDispatcherPrivate.h
│ │ │ │ │ ├── THOPluginItemPrivate.h
│ │ │ │ │ ├── THOPluginManagerPrivate.h
│ │ │ │ │ ├── THOPluginProtocolPrivate.h
│ │ │ │ │ ├── TLOEncryptionManagerPrivate.h
│ │ │ │ │ ├── TLOFileLoggerPrivate.h
│ │ │ │ │ ├── TLOInputHistoryPrivate.h
│ │ │ │ │ ├── TLOLicenseManagerDownloaderPrivate.h
│ │ │ │ │ ├── TLOLicenseManagerLastGenPrivate.h
│ │ │ │ │ ├── TLOLicenseManagerPrivate.h
│ │ │ │ │ ├── TLONicknameCompletionStatusPrivate.h
│ │ │ │ │ ├── TLONotificationConfigurationPrivate.h
│ │ │ │ │ ├── TLONotificationControllerPrivate.h
│ │ │ │ │ ├── TLOSpeechSynthesizerPrivate.h
│ │ │ │ │ ├── TLOSpokenNotificationPrivate.h
│ │ │ │ │ ├── TPCApplicationInfoPrivate.h
│ │ │ │ │ ├── TPCPathInfoPrivate.h
│ │ │ │ │ ├── TPCPreferencesImportExportPrivate.h
│ │ │ │ │ ├── TPCPreferencesLocalPrivate.h
│ │ │ │ │ ├── TPCResourceManagerPrivate.h
│ │ │ │ │ ├── TPCSandboxMigrationPrivate.h
│ │ │ │ │ ├── TPCThemeControllerPrivate.h
│ │ │ │ │ ├── TPCThemePrivate.h
│ │ │ │ │ ├── TVCAppearancePrivate.h
│ │ │ │ │ ├── TVCChannelSelectionOutlineViewCellPrivate.h
│ │ │ │ │ ├── TVCChannelSelectionViewControllerPrivate.h
│ │ │ │ │ ├── TVCContentNavigationOutlineViewPrivate.h
│ │ │ │ │ ├── TVCDockIconPrivate.h
│ │ │ │ │ ├── TVCErrorMessagePopoverControllerPrivate.h
│ │ │ │ │ ├── TVCErrorMessagePopoverPrivate.h
│ │ │ │ │ ├── TVCLogControllerHistoricLogFilePrivate.h
│ │ │ │ │ ├── TVCLogControllerInlineMediaServicePrivate.h
│ │ │ │ │ ├── TVCLogControllerOperationQueuePrivate.h
│ │ │ │ │ ├── TVCLogControllerPrivate.h
│ │ │ │ │ ├── TVCLogLinePrivate.h
│ │ │ │ │ ├── TVCLogPolicyPrivate.h
│ │ │ │ │ ├── TVCLogScriptEventSinkPrivate.h
│ │ │ │ │ ├── TVCLogViewInternalWK1.h
│ │ │ │ │ ├── TVCLogViewInternalWK2.h
│ │ │ │ │ ├── TVCLogViewPrivate.h
│ │ │ │ │ ├── TVCMainWindowAppearancePrivate.h
│ │ │ │ │ ├── TVCMainWindowChannelViewPrivate.h
│ │ │ │ │ ├── TVCMainWindowLoadingScreenPrivate.h
│ │ │ │ │ ├── TVCMainWindowPrivate.h
│ │ │ │ │ ├── TVCMainWindowSegmentedControlPrivate.h
│ │ │ │ │ ├── TVCMainWindowSplitViewPrivate.h
│ │ │ │ │ ├── TVCMainWindowTextViewAppearancePrivate.h
│ │ │ │ │ ├── TVCMainWindowTextViewPrivate.h
│ │ │ │ │ ├── TVCMainWindowTitlebarAccessoryViewPrivate.h
│ │ │ │ │ ├── TVCMemberListAppearancePrivate.h
│ │ │ │ │ ├── TVCMemberListCellPrivate.h
│ │ │ │ │ ├── TVCMemberListPrivate.h
│ │ │ │ │ ├── TVCMemberListUserInfoPopoverPrivate.h
│ │ │ │ │ ├── TVCNotificationConfigurationViewControllerPrivate.h
│ │ │ │ │ ├── TVCServerListAppearancePrivate.h
│ │ │ │ │ ├── TVCServerListCellPrivate.h
│ │ │ │ │ ├── TVCServerListPrivate.h
│ │ │ │ │ ├── TVCTextFormatterMenuPrivate.h
│ │ │ │ │ ├── TVCTextViewWithIRCFormatterPrivate.h
│ │ │ │ │ ├── TVCWK1AutoScrollerPrivate.h
│ │ │ │ │ ├── TXAppearancePrivate.h
│ │ │ │ │ ├── TXApplicationPrivate.h
│ │ │ │ │ ├── TXGlobalModelsPrivate.h
│ │ │ │ │ ├── TXMasterControllerPrivate.h
│ │ │ │ │ ├── TXMenuControllerPrivate.h
│ │ │ │ │ ├── TXSharedApplicationPrivate.h
│ │ │ │ │ ├── TXWindowControllerPrivate.h
│ │ │ │ │ ├── TextualPrivate.h
│ │ │ │ │ ├── WKWebViewPrivate.h
│ │ │ │ │ └── WebScriptObjectHelperPrivate.h
│ │ │ │ ├── TDCAlert.h
│ │ │ │ ├── TDCInputPrompt.h
│ │ │ │ ├── TDCSheetBase.h
│ │ │ │ ├── TDCWindowBase.h
│ │ │ │ ├── THOPluginManager.h
│ │ │ │ ├── THOPluginProtocol.h
│ │ │ │ ├── THOUnicodeHelper.h
│ │ │ │ ├── TLOEncryptionManager.h
│ │ │ │ ├── TLOInternetAddressLookup.h
│ │ │ │ ├── TLOKeyEventHandler.h
│ │ │ │ ├── TLOLinkParser.h
│ │ │ │ ├── TLONotificationController.h
│ │ │ │ ├── TLOSoundPlayer.h
│ │ │ │ ├── TLOpenLink.h
│ │ │ │ ├── TPCApplicationInfo.h
│ │ │ │ ├── TPCPathInfo.h
│ │ │ │ ├── TPCPreferencesImportExport.h
│ │ │ │ ├── TPCPreferencesLocal.h
│ │ │ │ ├── TPCPreferencesReload.h
│ │ │ │ ├── TPCPreferencesUserDefaultsLocal.h
│ │ │ │ ├── TPCResourceManager.h
│ │ │ │ ├── TPCTheme.h
│ │ │ │ ├── TPCThemeController.h
│ │ │ │ ├── TVCAlert.h
│ │ │ │ ├── TVCAppearance.h
│ │ │ │ ├── TVCAutoExpandingTextField.h
│ │ │ │ ├── TVCAutoExpandingTokenField.h
│ │ │ │ ├── TVCBasicTableView.h
│ │ │ │ ├── TVCChannelSelectionViewController.h
│ │ │ │ ├── TVCLogController.h
│ │ │ │ ├── TVCLogLine.h
│ │ │ │ ├── TVCLogRenderer.h
│ │ │ │ ├── TVCLogView.h
│ │ │ │ ├── TVCMainWindow.h
│ │ │ │ ├── TVCMainWindowAppearance.h
│ │ │ │ ├── TVCMainWindowLoadingScreen.h
│ │ │ │ ├── TVCMainWindowSplitView.h
│ │ │ │ ├── TVCMainWindowTextView.h
│ │ │ │ ├── TVCMainWindowTextViewAppearance.h
│ │ │ │ ├── TVCMemberList.h
│ │ │ │ ├── TVCMemberListAppearance.h
│ │ │ │ ├── TVCServerList.h
│ │ │ │ ├── TVCServerListAppearance.h
│ │ │ │ ├── TVCTextViewWithIRCFormatter.h
│ │ │ │ ├── TVCValidatedComboBox.h
│ │ │ │ ├── TVCValidatedTextField.h
│ │ │ │ ├── TXAppearance.h
│ │ │ │ ├── TXAppearanceHelper.h
│ │ │ │ ├── TXGlobalModels.h
│ │ │ │ ├── TXMasterController.h
│ │ │ │ ├── TXMenuController.h
│ │ │ │ ├── TXSharedApplication.h
│ │ │ │ ├── Textual.h
│ │ │ │ └── TextualApplication.h
│ │ │ ├── Helpers/
│ │ │ │ ├── Cocoa (Objective-C)/
│ │ │ │ │ ├── NSColorHelper.m
│ │ │ │ │ ├── NSStringHelper.m
│ │ │ │ │ ├── NSTableViewHelper.m
│ │ │ │ │ ├── NSViewHelper.m
│ │ │ │ │ └── TXAppearanceHelper.m
│ │ │ │ ├── External Libraries/
│ │ │ │ │ ├── Google/
│ │ │ │ │ │ └── GTMEncodeHTML.m
│ │ │ │ │ └── WebKit/
│ │ │ │ │ └── WebScriptObjectHelper.m
│ │ │ │ ├── Plugin Architecture/
│ │ │ │ │ ├── THOPluginDispatcher.m
│ │ │ │ │ ├── THOPluginItem.m
│ │ │ │ │ ├── THOPluginItemLogging.m
│ │ │ │ │ └── THOPluginManager.m
│ │ │ │ └── THOUnicodeHelper.m
│ │ │ ├── IRC/
│ │ │ │ ├── IRCAddressBook.m
│ │ │ │ ├── IRCAddressBookMatchCache.m
│ │ │ │ ├── IRCAddressBookUserTracking.m
│ │ │ │ ├── IRCChannel.m
│ │ │ │ ├── IRCChannelConfig.m
│ │ │ │ ├── IRCChannelMemberList.m
│ │ │ │ ├── IRCChannelMemberListController.m
│ │ │ │ ├── IRCChannelMode.m
│ │ │ │ ├── IRCClient.m
│ │ │ │ ├── IRCClientConfig.m
│ │ │ │ ├── IRCClientRequestedCommands.m
│ │ │ │ ├── IRCCommandIndex.m
│ │ │ │ ├── IRCConnection.m
│ │ │ │ ├── IRCExtras.m
│ │ │ │ ├── IRCHighlightLogEntry.m
│ │ │ │ ├── IRCHighlightMatchCondition.m
│ │ │ │ ├── IRCISupportInfo.m
│ │ │ │ ├── IRCMessage.m
│ │ │ │ ├── IRCMessageBatch.m
│ │ │ │ ├── IRCModeInfo.m
│ │ │ │ ├── IRCNetworkList.m
│ │ │ │ ├── IRCPrefix.m
│ │ │ │ ├── IRCSendingMessage.m
│ │ │ │ ├── IRCServer.m
│ │ │ │ ├── IRCTimerCommand.m
│ │ │ │ ├── IRCTreeItem.m
│ │ │ │ ├── IRCWorld.m
│ │ │ │ └── Users/
│ │ │ │ ├── IRCChannelUser.m
│ │ │ │ ├── IRCUser.m
│ │ │ │ ├── IRCUserNicknameColorStyleGenerator.m
│ │ │ │ ├── IRCUserPersistentStore.m
│ │ │ │ └── IRCUserRelations.m
│ │ │ ├── Library/
│ │ │ │ ├── Color Formatting/
│ │ │ │ │ └── IRCColorFormat.m
│ │ │ │ ├── External Libraries/
│ │ │ │ │ └── Sockets/
│ │ │ │ │ └── OELReachability.m
│ │ │ │ ├── License Manager/
│ │ │ │ │ └── Standalone/
│ │ │ │ │ ├── TLOLicenseManager.m
│ │ │ │ │ ├── TLOLicenseManagerDownloader.m
│ │ │ │ │ └── TLOLicenseManagerLastGen.m
│ │ │ │ ├── TLOEncryptionManager.m
│ │ │ │ ├── TLOFileLogger.m
│ │ │ │ ├── TLOInputHistory.m
│ │ │ │ ├── TLOInternetAddressLookup.m
│ │ │ │ ├── TLOKeyEventHandler.m
│ │ │ │ ├── TLOLinkParser.swift
│ │ │ │ ├── TLONicknameCompletionStatus.m
│ │ │ │ ├── TLONotificationConfiguration.m
│ │ │ │ ├── TLONotificationController.m
│ │ │ │ ├── TLOSoundPlayer.m
│ │ │ │ ├── TLOSpeechSynthesizer.m
│ │ │ │ ├── TLOSpokenNotification.m
│ │ │ │ └── TLOpenLink.swift
│ │ │ ├── Others/
│ │ │ │ └── main.m
│ │ │ ├── Preferences/
│ │ │ │ ├── TPCApplicationInfo.m
│ │ │ │ ├── TPCPathInfo.m
│ │ │ │ ├── TPCPreferencesImportExport.m
│ │ │ │ ├── TPCPreferencesLocal.m
│ │ │ │ ├── TPCPreferencesReload.m
│ │ │ │ ├── TPCPreferencesUserDefaultsLocal.m
│ │ │ │ ├── TPCResourceManager.m
│ │ │ │ ├── TPCSandboxMigration.m
│ │ │ │ └── Themes/
│ │ │ │ ├── TPCTheme.m
│ │ │ │ └── TPCThemeController.m
│ │ │ ├── Services/
│ │ │ │ └── ICLPayloadLocal.m
│ │ │ └── Views/
│ │ │ ├── Channel Selection Table/
│ │ │ │ ├── TVCChannelSelectionOutlineCellView.m
│ │ │ │ └── TVCChannelSelectionViewController.m
│ │ │ ├── Channel View/
│ │ │ │ ├── Extras/
│ │ │ │ │ ├── TVCLogControllerHistoricLogFile.m
│ │ │ │ │ ├── TVCLogControllerInlineMediaService.m
│ │ │ │ │ └── TVCLogControllerOperationQueue.m
│ │ │ │ ├── TVCLogController.m
│ │ │ │ ├── TVCLogLine.m
│ │ │ │ ├── TVCLogPolicy.m
│ │ │ │ ├── TVCLogRenderer.m
│ │ │ │ ├── TVCLogScriptEventSink.m
│ │ │ │ ├── TVCLogView.m
│ │ │ │ ├── TVCLogViewInternalWK1.m
│ │ │ │ ├── TVCLogViewInternalWK2.m
│ │ │ │ └── TVCWK1AutoScroller.m
│ │ │ ├── Errors/
│ │ │ │ ├── TVCAlert.m
│ │ │ │ ├── TVCErrorMessagePopover.m
│ │ │ │ └── TVCErrorMessagePopoverController.m
│ │ │ ├── Input Text Field/
│ │ │ │ ├── TVCAutoExpandingTextField.m
│ │ │ │ ├── TVCAutoExpandingTokenField.m
│ │ │ │ ├── TVCTextFormatterMenu.m
│ │ │ │ ├── TVCTextViewWithIRCFormatter.m
│ │ │ │ ├── TVCValidatedComboBox.m
│ │ │ │ └── TVCValidatedTextField.m
│ │ │ ├── Main Window/
│ │ │ │ ├── TVCMainWindow.m
│ │ │ │ ├── TVCMainWindowAppearance.m
│ │ │ │ ├── TVCMainWindowChannelView.m
│ │ │ │ ├── TVCMainWindowLoadingScreen.m
│ │ │ │ ├── TVCMainWindowSegmentedControl.m
│ │ │ │ ├── TVCMainWindowSplitView.m
│ │ │ │ ├── TVCMainWindowTextView.m
│ │ │ │ ├── TVCMainWindowTextViewAppearance.m
│ │ │ │ └── TVCMainWindowTitlebarAccessoryView.m
│ │ │ ├── Preferences/
│ │ │ │ └── TVCNotificationConfigurationViewController.m
│ │ │ ├── Server List/
│ │ │ │ ├── TVCServerList.m
│ │ │ │ ├── TVCServerListAppearance.m
│ │ │ │ └── TVCServerListCell.m
│ │ │ ├── TVCAppearance.m
│ │ │ ├── TVCBasicTableView.m
│ │ │ ├── TVCContentNavigationOutlineView.m
│ │ │ ├── TVCDockIcon.m
│ │ │ └── User List/
│ │ │ ├── TVCMemberList.m
│ │ │ ├── TVCMemberListAppearance.m
│ │ │ ├── TVCMemberListCell.m
│ │ │ └── TVCMemberListUserInfoPopover.m
│ │ ├── Configurations/
│ │ │ └── Sandbox/
│ │ │ ├── Debug.entitlements
│ │ │ ├── Sandbox Disabled.entitlements
│ │ │ └── Standard Release.entitlements
│ │ ├── Resources/
│ │ │ ├── Images/
│ │ │ │ ├── Application/
│ │ │ │ │ ├── applicationIcon.icns
│ │ │ │ │ ├── applicationIcon.psd
│ │ │ │ │ └── applicationIconBirthday.icns
│ │ │ │ ├── Copyright Information for Images.txt
│ │ │ │ ├── Dock Icon Badges/
│ │ │ │ │ └── Photoshop/
│ │ │ │ │ ├── Mavericks/
│ │ │ │ │ │ ├── blueDockBadge.psd
│ │ │ │ │ │ ├── blueDockBadgeWithShadow.psd
│ │ │ │ │ │ ├── redDockBadge.psd
│ │ │ │ │ │ └── redDockBadgeWithShadow.psd
│ │ │ │ │ └── Yosemite/
│ │ │ │ │ └── redDockBadge.psd
│ │ │ │ ├── Encryption Badges/
│ │ │ │ │ ├── encryptionLockIconDark.tif
│ │ │ │ │ ├── encryptionLockIconDark@2x.tif
│ │ │ │ │ ├── encryptionLockIconLight.tif
│ │ │ │ │ └── encryptionLockIconLight@2x.tif
│ │ │ │ ├── IRC Formatting Colors/
│ │ │ │ │ ├── FormattingColor_Rainbow.tif
│ │ │ │ │ └── FormattingColor_Rainbow@2x.tif
│ │ │ │ ├── Status Badges/
│ │ │ │ │ ├── channelRoomStatusIconDarkActive.tif
│ │ │ │ │ ├── channelRoomStatusIconDarkActive@2x.tif
│ │ │ │ │ ├── channelRoomStatusIconDarkInactive.tif
│ │ │ │ │ ├── channelRoomStatusIconDarkInactive@2x.tif
│ │ │ │ │ ├── channelRoomStatusIconLightActive.tif
│ │ │ │ │ ├── channelRoomStatusIconLightActive@2x.tif
│ │ │ │ │ ├── channelRoomStatusIconLightInactive.tif
│ │ │ │ │ ├── channelRoomStatusIconLightInactive@2x.tif
│ │ │ │ │ ├── channelRoomStatusIconMavericksDark.psd
│ │ │ │ │ └── channelRoomStatusIconMavericksLight.psd
│ │ │ │ └── User Interface/
│ │ │ │ ├── Miscellaneous/
│ │ │ │ │ └── ErroneousTextFieldValueIndicator.tif
│ │ │ │ └── Server List/
│ │ │ │ └── Glass/
│ │ │ │ ├── VibrantDarkServerListViewPrivateMessageUserIconActive.tif
│ │ │ │ ├── VibrantDarkServerListViewPrivateMessageUserIconActive@2x.tif
│ │ │ │ ├── VibrantDarkServerListViewPrivateMessageUserIconInactive.tif
│ │ │ │ ├── VibrantDarkServerListViewPrivateMessageUserIconInactive@2x.tif
│ │ │ │ ├── VibrantLightServerListViewPrivateMessageUserIconActive.tif
│ │ │ │ ├── VibrantLightServerListViewPrivateMessageUserIconActive@2x.tif
│ │ │ │ ├── VibrantLightServerListViewPrivateMessageUserIconInactive.tif
│ │ │ │ └── VibrantLightServerListViewPrivateMessageUserIconInactive@2x.tif
│ │ │ ├── Language Files/
│ │ │ │ ├── _randomToken
│ │ │ │ └── en.lproj/
│ │ │ │ ├── Accessibility.strings
│ │ │ │ ├── BasicLanguage.strings
│ │ │ │ ├── CommonErrors.strings
│ │ │ │ ├── IRC.strings
│ │ │ │ ├── Notifications.strings
│ │ │ │ ├── OffTheRecord.strings
│ │ │ │ ├── Prompts.strings
│ │ │ │ ├── TDCAboutDialog.strings
│ │ │ │ ├── TDCAddressBookSheet.strings
│ │ │ │ ├── TDCChannelBanListSheet.strings
│ │ │ │ ├── TDCChannelInviteSheet.strings
│ │ │ │ ├── TDCChannelModifyModesSheet.strings
│ │ │ │ ├── TDCChannelModifyTopicSheet.strings
│ │ │ │ ├── TDCChannelPropertiesSheet.strings
│ │ │ │ ├── TDCChannelSpotlightController.strings
│ │ │ │ ├── TDCFileTransferDialog.strings
│ │ │ │ ├── TDCLicenseUpgradeEligibilitySheet.strings
│ │ │ │ ├── TDCPreferencesController.strings
│ │ │ │ ├── TDCPreferencesUserStyleSheet.strings
│ │ │ │ ├── TDCServerChannelListDialog.strings
│ │ │ │ ├── TDCServerEndpointListSheet.strings
│ │ │ │ ├── TDCServerPropertiesSheet.strings
│ │ │ │ ├── TLOLicenseManager.strings
│ │ │ │ ├── TVCMainWindow.strings
│ │ │ │ └── TVCNotificationConfigurationView.strings
│ │ │ ├── License Manager/
│ │ │ │ └── RemoteLicenseSystemPublicKey.pub
│ │ │ ├── Property Lists/
│ │ │ │ ├── Application Properties/
│ │ │ │ │ └── Info.plist
│ │ │ │ ├── IRCCommandIndexLocalData.plist
│ │ │ │ ├── IRCCommandIndexRemoteData.plist
│ │ │ │ ├── IRCNetworks.plist
│ │ │ │ ├── Preferences/
│ │ │ │ │ ├── KeysExcludedFromContainer.plist
│ │ │ │ │ ├── KeysExcludedFromExport.plist
│ │ │ │ │ ├── KeysExcludedFromMigrate.plist
│ │ │ │ │ ├── PreferenceKeyMasterList.plist
│ │ │ │ │ ├── RegisteredUserDefaults.plist
│ │ │ │ │ └── RegisteredUserDefaultsInContainer.plist
│ │ │ │ ├── StaticStore.plist
│ │ │ │ └── TemplateLineTypes.plist
│ │ │ ├── Scripting/
│ │ │ │ └── Script Files/
│ │ │ │ ├── Bundled Scripts/
│ │ │ │ │ ├── Installing Custom Scripts.txt
│ │ │ │ │ ├── date.scpt
│ │ │ │ │ └── moti.scpt
│ │ │ │ └── Textual Extras Installer/
│ │ │ │ ├── Create-Installers
│ │ │ │ ├── Installation Files/
│ │ │ │ │ └── Scripts/
│ │ │ │ │ ├── apps.scpt
│ │ │ │ │ ├── banhammer.scpt
│ │ │ │ │ ├── ffuu.scpt
│ │ │ │ │ ├── flip.scpt
│ │ │ │ │ ├── hermes.scpt
│ │ │ │ │ ├── music.scpt
│ │ │ │ │ ├── np.scpt
│ │ │ │ │ ├── o_o.scpt
│ │ │ │ │ ├── page.scpt
│ │ │ │ │ ├── qt.scpt
│ │ │ │ │ ├── radium.scpt
│ │ │ │ │ ├── reverse.scpt
│ │ │ │ │ ├── shell.scpt
│ │ │ │ │ ├── slap.scpt
│ │ │ │ │ ├── spotify.scpt
│ │ │ │ │ └── vlc.scpt
│ │ │ │ ├── Installer Helpers/
│ │ │ │ │ ├── Build-Extension-Package
│ │ │ │ │ ├── Build-Final-Package
│ │ │ │ │ └── Build-Scripts-Package
│ │ │ │ ├── Installer Resources/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ ├── Localizable.strings
│ │ │ │ │ └── Welcome.rtf
│ │ │ │ ├── Packages/
│ │ │ │ │ └── Textual-Extras.pkg
│ │ │ │ └── distribution.plist
│ │ │ ├── Styling/
│ │ │ │ ├── Bundled Styles/
│ │ │ │ │ ├── Astria/
│ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ ├── copyright.txt
│ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Equinox/
│ │ │ │ │ │ ├── LICENSE.txt
│ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Sapientia/
│ │ │ │ │ │ ├── Resources/
│ │ │ │ │ │ │ └── Documentation/
│ │ │ │ │ │ │ └── changelog.txt
│ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ ├── copyright.txt
│ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Simplified/
│ │ │ │ │ │ ├── Varieties/
│ │ │ │ │ │ │ ├── Dark/
│ │ │ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ │ │ └── settings.plist
│ │ │ │ │ │ │ └── Light/
│ │ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ │ └── settings.plist
│ │ │ │ │ │ ├── copyright.txt
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Sulaco/
│ │ │ │ │ │ ├── LICENSE.txt
│ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ ├── newMessagePostedWithSender.mustache
│ │ │ │ │ │ │ └── newMessagePostedWithoutSender.mustache
│ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ ├── Tomorrow/
│ │ │ │ │ │ ├── Varieties/
│ │ │ │ │ │ │ ├── Dark/
│ │ │ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ │ │ └── settings.plist
│ │ │ │ │ │ │ └── Light/
│ │ │ │ │ │ │ ├── Templates/
│ │ │ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ │ │ ├── design.css
│ │ │ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ │ │ └── settings.plist
│ │ │ │ │ │ ├── copyright.txt
│ │ │ │ │ │ ├── scripts.js
│ │ │ │ │ │ └── settings.plist
│ │ │ │ │ └── Total Sublime/
│ │ │ │ │ ├── Templates/
│ │ │ │ │ │ └── encryptedMessageLock.mustache
│ │ │ │ │ ├── copyright.txt
│ │ │ │ │ ├── design.css
│ │ │ │ │ ├── inlineMedia.css
│ │ │ │ │ ├── scripts.js
│ │ │ │ │ └── settings.plist
│ │ │ │ ├── JavaScript/
│ │ │ │ │ └── API/
│ │ │ │ │ ├── core.js
│ │ │ │ │ ├── corePrivate.js
│ │ │ │ │ └── private/
│ │ │ │ │ ├── conversationTracking.js
│ │ │ │ │ ├── core/
│ │ │ │ │ │ ├── clickMenuSelection.js
│ │ │ │ │ │ ├── documentBody.js
│ │ │ │ │ │ ├── events.js
│ │ │ │ │ │ ├── inlineMedia.js
│ │ │ │ │ │ ├── messageBuffer.js
│ │ │ │ │ │ └── scrollTo.js
│ │ │ │ │ ├── scriptSink.js
│ │ │ │ │ └── scroller/
│ │ │ │ │ ├── automatic.js
│ │ │ │ │ ├── automaticEmpty.js
│ │ │ │ │ └── state.js
│ │ │ │ └── Style Default Templates/
│ │ │ │ └── Version 4/
│ │ │ │ ├── baseLayout.css
│ │ │ │ ├── baseLayout.mustache
│ │ │ │ ├── dateIndicator.mustache
│ │ │ │ ├── encryptedMessageLock.mustache
│ │ │ │ ├── formattedMessageFragment.mustache
│ │ │ │ ├── historyIndicator.mustache
│ │ │ │ ├── messageBufferLoadingIndicator.mustache
│ │ │ │ ├── messageBufferSessionIndicator.mustache
│ │ │ │ ├── newMessagePostedWithSender.mustache
│ │ │ │ ├── newMessagePostedWithoutSender.mustache
│ │ │ │ ├── renderedChannelNameLinkResource.mustache
│ │ │ │ ├── renderedStandardAnchorLinkResource.mustache
│ │ │ │ └── sessionIndicator.mustache
│ │ │ └── User Interface/
│ │ │ ├── Appearance/
│ │ │ │ ├── TDCChannelSpotlightAppearance.plist
│ │ │ │ ├── TDCChannelSpotlightAppearanceTemplate.plist
│ │ │ │ ├── TVCMainWindowAppearance.plist
│ │ │ │ ├── TVCMainWindowAppearanceTemplate.plist
│ │ │ │ ├── TVCMainWindowTextViewAppearance.plist
│ │ │ │ ├── TVCMainWindowTextViewAppearanceTemplate.plist
│ │ │ │ ├── TVCMemberListAppearance.plist
│ │ │ │ ├── TVCMemberListAppearanceTemplate.plist
│ │ │ │ ├── TVCServerListAppearance.plist
│ │ │ │ └── TVCServerListAppearanceTemplate.plist
│ │ │ └── en.lproj/
│ │ │ ├── TDCAboutDialog.xib
│ │ │ ├── TDCAddressBookSheet.xib
│ │ │ ├── TDCChannelBanListSheet.xib
│ │ │ ├── TDCChannelInviteSheet.xib
│ │ │ ├── TDCChannelModifyModesSheet.xib
│ │ │ ├── TDCChannelModifyTopicSheet.xib
│ │ │ ├── TDCChannelPropertiesSheet.xib
│ │ │ ├── TDCChannelSpotlightController.xib
│ │ │ ├── TDCFileTransferDialog.xib
│ │ │ ├── TDCHighlightEntrySheet.xib
│ │ │ ├── TDCLicenseManagerDialog.xib
│ │ │ ├── TDCLicenseManagerMigrateAppStoreSheet.xib
│ │ │ ├── TDCLicenseManagerRecoverLostLicenseSheet.xib
│ │ │ ├── TDCLicenseUpgradeActivateSheet.xib
│ │ │ ├── TDCLicenseUpgradeDialog.xib
│ │ │ ├── TDCLicenseUpgradeEligibilitySheet.xib
│ │ │ ├── TDCNicknameColorSheet.xib
│ │ │ ├── TDCPreferences.xib
│ │ │ ├── TDCPreferencesUserStyleSheet.xib
│ │ │ ├── TDCProgressIndicatorSheet.xib
│ │ │ ├── TDCServerChangeNicknameSheet.xib
│ │ │ ├── TDCServerChannelListDialog.xib
│ │ │ ├── TDCServerEndpointListSheet.xib
│ │ │ ├── TDCServerHighlightListSheet.xib
│ │ │ ├── TDCServerPropertiesSheet.xib
│ │ │ ├── TDCWelcomeSheet.xib
│ │ │ ├── TVCAlert.xib
│ │ │ ├── TVCChannelSelectionView.xib
│ │ │ ├── TVCMainWindow.xib
│ │ │ ├── TVCNotificationConfigurationView.xib
│ │ │ └── TXCMainMenu.xib
│ │ └── Textual App.xcodeproj/
│ │ ├── project.pbxproj
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ ├── Textual (App Store).xcscheme
│ │ ├── Textual (Debug).xcscheme
│ │ ├── Textual (Standard Release Sandboxed).xcscheme
│ │ └── Textual (Standard Release).xcscheme
│ ├── Plugins/
│ │ ├── Blowfish Encryption/
│ │ │ ├── ACKNOWLEDGEMENT.txt
│ │ │ ├── Blowfish Encryption Extension.xcodeproj/
│ │ │ │ ├── project.pbxproj
│ │ │ │ └── xcshareddata/
│ │ │ │ └── xcschemes/
│ │ │ │ └── Blowfish Encryption Extension.xcscheme
│ │ │ ├── Classes/
│ │ │ │ ├── BlowfishEncryption.h
│ │ │ │ ├── BlowfishEncryption.m
│ │ │ │ ├── BlowfishEncryptionBase.h
│ │ │ │ ├── BlowfishEncryptionBase.m
│ │ │ │ ├── BlowfishEncryptionKeyExchange.h
│ │ │ │ ├── BlowfishEncryptionKeyExchange.mm
│ │ │ │ ├── BlowfishEncryptionKeyExchangeBase.h
│ │ │ │ ├── BlowfishEncryptionKeyExchangeBase.mm
│ │ │ │ ├── NSDataHelper.h
│ │ │ │ ├── NSDataHelper.m
│ │ │ │ ├── TPIBlowfishEncryption.h
│ │ │ │ ├── TPIBlowfishEncryption.m
│ │ │ │ ├── TPIBlowfishEncryptionSwizzledClasses.h
│ │ │ │ └── TPIBlowfishEncryptionSwizzledClasses.m
│ │ │ ├── LICENSE-GPLv2.txt
│ │ │ ├── LICENSE.txt
│ │ │ └── Resources/
│ │ │ ├── Language Files/
│ │ │ │ └── en.lproj/
│ │ │ │ └── BasicLanguage.strings
│ │ │ ├── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Interface/
│ │ │ └── en.lproj/
│ │ │ └── TPIBlowfishEncryption.xib
│ │ ├── Caffeine/
│ │ │ ├── Caffeine Extension.xcodeproj/
│ │ │ │ ├── project.pbxproj
│ │ │ │ └── xcshareddata/
│ │ │ │ └── xcschemes/
│ │ │ │ └── Caffeine Extension.xcscheme
│ │ │ ├── Classes/
│ │ │ │ ├── TPI_Caffeine.h
│ │ │ │ └── TPI_Caffeine.m
│ │ │ └── Resources/
│ │ │ ├── Language Files/
│ │ │ │ └── en.lproj/
│ │ │ │ └── BasicLanguage.strings
│ │ │ ├── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Interface/
│ │ │ └── en.lproj/
│ │ │ └── TPI_Caffeine.xib
│ │ ├── Chat Filter/
│ │ │ ├── Chat Filter Extension.xcodeproj/
│ │ │ │ └── project.pbxproj
│ │ │ ├── Classes/
│ │ │ │ ├── TPI_ChatFilter.h
│ │ │ │ ├── TPI_ChatFilter.m
│ │ │ │ ├── TPI_ChatFilterEditFilterSheet.h
│ │ │ │ ├── TPI_ChatFilterEditFilterSheet.m
│ │ │ │ ├── TPI_ChatFilterExtension.h
│ │ │ │ ├── TPI_ChatFilterExtension.m
│ │ │ │ ├── TPI_ChatFilterInternal.h
│ │ │ │ ├── TPI_ChatFilterLogic.h
│ │ │ │ ├── TPI_ChatFilterLogic.m
│ │ │ │ ├── TPI_NumberOnlyTextFieldFormatter.h
│ │ │ │ └── TPI_NumberOnlyTextFieldFormatter.m
│ │ │ └── Resources/
│ │ │ ├── Language Files/
│ │ │ │ └── en.lproj/
│ │ │ │ ├── TPI_ChatFilterEditFilterSheet.strings
│ │ │ │ ├── TPI_ChatFilterExtension.strings
│ │ │ │ └── TPI_ChatFilterLogic.strings
│ │ │ ├── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Interface/
│ │ │ └── en.lproj/
│ │ │ ├── TPI_ChatFilterEditFilterSheet.xib
│ │ │ └── TPI_ChatFilterExtension.xib
│ │ ├── Sample Code/
│ │ │ └── Preference Pane/
│ │ │ ├── Classes/
│ │ │ │ ├── TPI_PreferencePaneExample.h
│ │ │ │ └── TPI_PreferencePaneExample.m
│ │ │ ├── PreferencePaneExample.xcodeproj/
│ │ │ │ └── project.pbxproj
│ │ │ └── Resources/
│ │ │ ├── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Interface/
│ │ │ └── PreferencePane.xib
│ │ ├── Smiley Converter/
│ │ │ ├── Classes/
│ │ │ │ ├── TPISmileyConverter.h
│ │ │ │ └── TPISmileyConverter.m
│ │ │ ├── Resources/
│ │ │ │ ├── Language Files/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ └── BasicLanguage.strings
│ │ │ │ ├── Property Lists/
│ │ │ │ │ ├── Info.plist
│ │ │ │ │ ├── conversionTable.plist
│ │ │ │ │ └── conversionTable2.plist
│ │ │ │ └── User Interface/
│ │ │ │ └── en.lproj/
│ │ │ │ └── TPISmileyConverter.xib
│ │ │ └── Smiley Converter Extension.xcodeproj/
│ │ │ └── project.pbxproj
│ │ ├── System Profiler/
│ │ │ ├── Classes/
│ │ │ │ ├── SystemProfiler.h
│ │ │ │ ├── TPISystemProfiler.h
│ │ │ │ ├── TPISystemProfiler.m
│ │ │ │ ├── TPI_SP_SysInfo.h
│ │ │ │ └── TPI_SP_SysInfo.m
│ │ │ ├── Resources/
│ │ │ │ ├── Language Files/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ └── BasicLanguage.strings
│ │ │ │ ├── Property Lists/
│ │ │ │ │ ├── Info.plist
│ │ │ │ │ └── MacintoshModels.plist
│ │ │ │ └── User Interface/
│ │ │ │ └── en.lproj/
│ │ │ │ └── TPISystemProfiler.xib
│ │ │ └── System Profiler Extension.xcodeproj/
│ │ │ └── project.pbxproj
│ │ ├── User Insights/
│ │ │ ├── Classes/
│ │ │ │ ├── TPIUserInsights.h
│ │ │ │ └── TPIUserInsights.m
│ │ │ ├── Resources/
│ │ │ │ ├── Language Files/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ └── BasicLanguage.strings
│ │ │ │ └── Property Lists/
│ │ │ │ └── Info.plist
│ │ │ └── User Insights Extension.xcodeproj/
│ │ │ └── project.pbxproj
│ │ ├── Wiki Link Parser/
│ │ │ ├── Classes/
│ │ │ │ ├── TPIWikiStyleLinkParser.h
│ │ │ │ └── TPIWikiStyleLinkParser.m
│ │ │ ├── Resources/
│ │ │ │ ├── Language Files/
│ │ │ │ │ └── en.lproj/
│ │ │ │ │ └── BasicLanguage.strings
│ │ │ │ ├── Property Lists/
│ │ │ │ │ └── Info.plist
│ │ │ │ └── User Interface/
│ │ │ │ └── en.lproj/
│ │ │ │ └── TPIWikiStyleLinkParser.xib
│ │ │ └── Wiki-style Link Parser Extension.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ └── xcshareddata/
│ │ │ └── xcschemes/
│ │ │ └── Wiki-style Link Parser Extension.xcscheme
│ │ └── ZNC Additions/
│ │ ├── Classes/
│ │ │ ├── TPI_ZNCAdditions.h
│ │ │ └── TPI_ZNCAdditions.m
│ │ ├── Resources/
│ │ │ ├── Language Files/
│ │ │ │ └── en.lproj/
│ │ │ │ └── BasicLanguage.strings
│ │ │ └── Property Lists/
│ │ │ └── Info.plist
│ │ └── ZNC Additions Extension.xcodeproj/
│ │ └── project.pbxproj
│ └── Shared/
│ ├── Headers/
│ │ ├── External Libraries/
│ │ │ ├── GCDAsyncSocket.h
│ │ │ └── GCDAsyncSocketExtensions.h
│ │ ├── IRCConnectionConfig.h
│ │ ├── IRCConnectionErrors.h
│ │ ├── Internal/
│ │ │ └── IRCConnectionConfigInternal.h
│ │ ├── Private/
│ │ │ ├── NSObjectHelperPrivate.h
│ │ │ ├── TPCPreferencesPrivate.h
│ │ │ ├── TPCPreferencesUserDefaultsPrivate.h
│ │ │ └── TVCLogLineXPCPrivate.h
│ │ ├── StaticDefinitions.h
│ │ ├── TLOLocalization.h
│ │ ├── TLOTimer.h
│ │ ├── TPCPreferences.h
│ │ └── TPCPreferencesUserDefaults.h
│ ├── Helpers/
│ │ └── Cocoa (Objective-C)/
│ │ └── NSObjectHelper.m
│ ├── IRC/
│ │ ├── IRCConnectionConfig.m
│ │ └── IRCConnectionErrors.m
│ ├── Library/
│ │ ├── External Libraries/
│ │ │ └── Sockets/
│ │ │ ├── GCDAsyncSocket-patch.txt
│ │ │ ├── GCDAsyncSocket.m
│ │ │ └── GCDAsyncSocketExtensions.m
│ │ ├── TLOLocalization.m
│ │ ├── TLOLocalization.swift
│ │ └── TLOTimer.m
│ ├── Preferences/
│ │ ├── TPCPreferences.m
│ │ └── TPCPreferencesUserDefaults.m
│ └── Views/
│ └── Channel View/
│ └── TVCLogLineXPC.m
└── XPC Services/
├── Historic Log File Manager/
│ ├── Classes/
│ │ ├── HLSHistoricLogLineEntityMigration.m
│ │ ├── HLSHistoricLogProcessMain.m
│ │ ├── HLSHistoricLogViewContext.m
│ │ ├── HSLHistoricLogProcessDelegate.m
│ │ ├── Headers/
│ │ │ └── Private/
│ │ │ ├── HLSHistoricLogLineEntityMigrationPrivate.h
│ │ │ ├── HLSHistoricLogProcessMainPrivate.h
│ │ │ ├── HLSHistoricLogProtocol.h
│ │ │ ├── HLSHistoricLogViewContextPrivate.h
│ │ │ ├── HSLHistoricLogPCHPrivate.h
│ │ │ └── HSLHistoricLogProcessDelegatePrivate.h
│ │ └── main.m
│ ├── Configurations/
│ │ └── Sandbox/
│ │ └── Release.entitlements
│ ├── Historic Log File Manager.xcodeproj/
│ │ └── project.pbxproj
│ └── Resources/
│ ├── Data Models/
│ │ ├── HistoricLogFileStorageModel.xcdatamodeld/
│ │ │ ├── .xccurrentversion
│ │ │ ├── LogControllerStorageModel (model 2).xcdatamodel/
│ │ │ │ └── contents
│ │ │ └── LogControllerStorageModel (model 3).xcdatamodel/
│ │ │ └── contents
│ │ └── HistoricLogFileStorageModel.xcmappingmodel/
│ │ └── xcmapping.xml
│ └── Property Lists/
│ └── Info.plist
├── IRC Remote Connection Manager/
│ ├── Classes/
│ │ ├── Headers/
│ │ │ └── Private/
│ │ │ ├── IRCConnectionPrivate.h
│ │ │ ├── RCMConnectionManagerProtocol.h
│ │ │ ├── RCMProcessDelegatePrivate.h
│ │ │ ├── RCMProcessMainPrivate.h
│ │ │ ├── RCMProcessPCHPrivate.h
│ │ │ └── SwiftBridgingHeaderPrivate.h
│ │ ├── IRC/
│ │ │ ├── IRCConnection.swift
│ │ │ ├── IRCConnectionSocket.swift
│ │ │ ├── IRCConnectionSocketClassic.swift
│ │ │ └── IRCConnectionSocketNWF.swift
│ │ └── Service/
│ │ ├── RCMProcessDelegate.m
│ │ ├── RCMProcessMain.m
│ │ └── main.m
│ ├── Configurations/
│ │ └── Sandbox/
│ │ └── Release.entitlements
│ ├── IRC Remote Connection Manager.xcodeproj/
│ │ └── project.pbxproj
│ └── Resources/
│ ├── Language Files/
│ │ └── en.lproj/
│ │ └── ConnectionErrors.strings
│ └── Property Lists/
│ └── Info.plist
└── Inline Content Loader/
├── Build Scripts/
│ └── BuildExtensions.sh
├── Classes/
│ ├── Headers/
│ │ ├── ICLHelpers.h
│ │ ├── ICLInlineContentModule.h
│ │ ├── ICLMediaAssessment.h
│ │ ├── ICLMediaAssessor.h
│ │ ├── ICLMediaType.h
│ │ ├── ICLPayload.h
│ │ ├── ICLPluginProtocol.h
│ │ ├── Internal/
│ │ │ ├── ICLInlineContentModuleInternal.h
│ │ │ ├── ICLMediaAssessmentInternal.h
│ │ │ └── ICLPayloadInternal.h
│ │ └── Private/
│ │ ├── CoreModulesImportsPrivate.h
│ │ ├── ICLInlineContentModulePrivate.h
│ │ ├── ICLInlineContentProtocol.h
│ │ ├── ICLPayloadPrivate.h
│ │ ├── ICLPluginManagerPrivate.h
│ │ ├── ICLProcessDelegatePrivate.h
│ │ ├── ICLProcessMainPrivate.h
│ │ └── ICLProcessPCHPrivate.h
│ ├── Modules/
│ │ ├── Assessed Media/
│ │ │ ├── ICMAssessedMedia.h
│ │ │ └── ICMAssessedMedia.m
│ │ └── Root Classes/
│ │ ├── ICMInlineHTML.h
│ │ ├── ICMInlineHTML.m
│ │ ├── ICMInlineImage.h
│ │ ├── ICMInlineImage.m
│ │ ├── ICMInlineVideo.h
│ │ └── ICMInlineVideo.m
│ └── Service/
│ ├── ICLHelpers.m
│ ├── ICLInlineContentModule.m
│ ├── ICLMediaAssessment.m
│ ├── ICLMediaAssessor.m
│ ├── ICLPayloadLocal.m
│ ├── ICLPayloadShared.m
│ ├── ICLPluginManager.m
│ ├── ICLProcessDelegate.m
│ ├── ICLProcessMain.m
│ └── main.m
├── Configurations/
│ └── Sandbox/
│ └── Release.entitlements
├── Extensions/
│ └── Core Media/
│ ├── Classes/
│ │ ├── Headers/
│ │ │ └── Private/
│ │ │ ├── ICPCoreMediaPCHPrivate.h
│ │ │ └── ICPCoreMediaPrivate.h
│ │ ├── ICPCoreMedia.m
│ │ └── Modules/
│ │ ├── Common Images/
│ │ │ ├── ICMCommonInlineImages.h
│ │ │ └── ICMCommonInlineImages.m
│ │ ├── Common Videos/
│ │ │ ├── ICMCommonInlineVideos.h
│ │ │ └── ICMCommonInlineVideos.m
│ │ ├── Dailymotion/
│ │ │ ├── ICMDailymotion.h
│ │ │ └── ICMDailymotion.m
│ │ ├── Gyazo/
│ │ │ ├── ICMGyazo.h
│ │ │ └── ICMGyazo.m
│ │ ├── Imgur .gifv/
│ │ │ ├── ICMImgurGifv.h
│ │ │ └── ICMImgurGifv.m
│ │ ├── Pornhub/
│ │ │ ├── ICMPornhub.h
│ │ │ └── ICMPornhub.m
│ │ ├── Streamable/
│ │ │ ├── ICMStreamable.h
│ │ │ └── ICMStreamable.m
│ │ ├── Twitch Clips/
│ │ │ ├── ICMTwitchClips.h
│ │ │ └── ICMTwitchClips.m
│ │ ├── Twitch Live/
│ │ │ ├── ICMTwitchLive.h
│ │ │ └── ICMTwitchLive.m
│ │ ├── Twitter/
│ │ │ ├── ICMTweet.h
│ │ │ └── ICMTweet.m
│ │ ├── Vimeo/
│ │ │ ├── ICMVimeo.h
│ │ │ └── ICMVimeo.m
│ │ ├── YouTube/
│ │ │ ├── ICMYouTube.h
│ │ │ └── ICMYouTube.m
│ │ └── xkcd/
│ │ ├── ICMXkcd.h
│ │ └── ICMXkcd.m
│ ├── Inline Content Loader Core Media.xcodeproj/
│ │ └── project.pbxproj
│ └── Resources/
│ ├── Modules/
│ │ ├── Dailymotion/
│ │ │ └── ICMDailymotion.mustache
│ │ ├── Pornhub/
│ │ │ └── ICMPornhub.mustache
│ │ ├── Twitch Clips/
│ │ │ └── ICMTwitchClips.mustache
│ │ ├── Twitch Live/
│ │ │ └── ICMTwitchLive.mustache
│ │ ├── Twitter/
│ │ │ └── ICMTweet.js
│ │ ├── Vimeo/
│ │ │ └── ICMVimeo.mustache
│ │ └── YouTube/
│ │ └── ICMYouTube.mustache
│ └── Property Lists/
│ └── Info.plist
├── Inline Content Loader.xcodeproj/
│ └── project.pbxproj
└── Resources/
├── Modules/
│ ├── ICMInlineHTML.css
│ ├── ICMInlineHTML.js
│ ├── ICMInlineHTML.mustache
│ ├── ICMInlineImage.css
│ ├── ICMInlineImage.js
│ ├── ICMInlineImage.mustache
│ ├── ICMInlineVideo.css
│ ├── ICMInlineVideo.js
│ ├── ICMInlineVideo.mustache
│ └── InlineImageLiveResize.js
└── Property Lists/
└── Info.plist
SYMBOL INDEX (32 symbols across 28 files)
FILE: Sources/App/Classes/Headers/IRCColorFormat.h
type NSString (line 53) | typedef NSString *IRCTextFormatterAttributeName
FILE: Sources/App/Classes/Headers/IRCISupportInfo.h
type IRCISupportInfoListTypeBan (line 43) | typedef NS_ENUM(NSUInteger, IRCISupportInfoListType)
FILE: Sources/App/Classes/Headers/Internal/IRCAddressBookInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCChannelConfigInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCChannelUserInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCClientConfigInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCHighlightLogEntryInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCHighlightMatchConditionInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCMessageInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCModeInfoInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCPrefixInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCServerInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/IRCUserInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Internal/TVCLogLineInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Classes/Headers/Private/TDCFileTransferDialogPrivate.h
type TDCFileTransferDialogTransferStatusComplete (line 45) | typedef NS_ENUM(NSUInteger, TDCFileTransferDialogTransferStatus) {
FILE: Sources/App/Classes/Headers/Private/TLOLicenseManagerPrivate.h
type NSString (line 52) | typedef NSString *TLOLicenseManagerLicenseDictionaryKey
FILE: Sources/App/Classes/Headers/Private/WKWebViewPrivate.h
type OpaqueWKPage (line 14) | struct OpaqueWKPage
type OpaqueWKInspector (line 16) | struct OpaqueWKInspector
FILE: Sources/App/Classes/Headers/TVCLogRenderer.h
type NSString (line 44) | typedef NSString *TVCLogRendererConfigurationAttribute
type NSString (line 45) | typedef NSString *TVCLogRendererResultsAttribute
FILE: Sources/App/Classes/Headers/TXAppearance.h
function NS_ASSUME_NONNULL_BEGIN (line 38) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/App/Resources/Styling/Bundled Styles/Equinox/scripts.js
function NickColorGenerator (line 60) | function NickColorGenerator(message) {
function isMessageInViewport (line 141) | function isMessageInViewport(elem) {
function toggleHistoryIfScrolled (line 152) | function toggleHistoryIfScrolled() {
FILE: Sources/Plugins/Blowfish Encryption/Classes/BlowfishEncryption.h
type EKBlowfishEncryptionModeOfOperation (line 35) | typedef enum EKBlowfishEncryptionModeOfOperation : NSInteger {
FILE: Sources/Plugins/Chat Filter/Classes/TPI_ChatFilterInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/Shared/Headers/Internal/IRCConnectionConfigInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: Sources/Shared/Headers/TLOLocalization.h
function NSString (line 48) | __attribute__((annotate("returns_localized_nsstring")))
FILE: XPC Services/Inline Content Loader/Classes/Headers/Internal/ICLInlineContentModuleInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: XPC Services/Inline Content Loader/Classes/Headers/Internal/ICLMediaAssessmentInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 38) | NS_ASSUME_NONNULL_BEGIN
FILE: XPC Services/Inline Content Loader/Classes/Headers/Internal/ICLPayloadInternal.h
function NS_ASSUME_NONNULL_BEGIN (line 40) | NS_ASSUME_NONNULL_BEGIN
FILE: XPC Services/Inline Content Loader/Resources/Modules/InlineImageLiveResize.js
function InlineImageLiveResize (line 46) | function InlineImageLiveResize() {
Condensed preview — 910 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (8,390K chars).
[
{
"path": ".gitignore",
"chars": 442,
"preview": "*~\n*~.*\n.DS_Store\n.LSOverride\n*.mode1v3\n*.mode2v3\n*.pbxuser\n*.perspectivev3\n*.pyc\n.tmp\nbuild\nBuild Results\nxcuserdata\n*."
},
{
"path": ".gitmodules",
"chars": 711,
"preview": "[submodule \"Frameworks/Auto Hyperlinks\"]\n\tpath = Frameworks/Auto Hyperlinks\n\turl = https://github.com/Codeux-Software/Au"
},
{
"path": "Configurations/Build/Code Signing Identity.xcconfig",
"chars": 202,
"preview": "\n// This is the code signing identity used by all projects.\n\nCODE_SIGN_IDENTITY = Mac Developer\n\n// Set these values to "
},
{
"path": "Configurations/Build/Common/Foundation Debug.xcconfig",
"chars": 1272,
"preview": "\n// Foundation Debug.xcconfig acts as a base for ALL projects.\n// This configuration should NEVER contain any settings\n/"
},
{
"path": "Configurations/Build/Common/Foundation.xcconfig",
"chars": 3040,
"preview": "\n// Foundation.xcconfig acts as a base for ALL projects.\n// This configuration should NEVER contain any settings\n// whic"
},
{
"path": "Configurations/Build/Common/Preserve Symbols.xcconfig",
"chars": 194,
"preview": "\n// It is important to preserve symbols and code that is\n// not directly used so that extensions can use them.\n\nDEAD_COD"
},
{
"path": "Configurations/Build/Common/Textual App.xcconfig",
"chars": 1049,
"preview": "\n// Main application\n\nTEXTUAL_WORKSPACE_DIR = ${PROJECT_DIR}/../..\n\nHEADER_SEARCH_PATHS = \"${TEXTUAL_WORKSPACE_TEMP_DIR}"
},
{
"path": "Configurations/Build/Common/Textual Extensions.xcconfig",
"chars": 1025,
"preview": "\n// Textual Extensions *.xcconfig file contains settings that\n// are specific to extensions for Textual (main applicatio"
},
{
"path": "Configurations/Build/Common/Textual.xcconfig",
"chars": 977,
"preview": "\n// Main application, extensions, and XPC services\n\nTEXTUAL_PRODUCT_NAME = Textual\n\n// TEXTUAL_PRODUCT_LOCATION is repla"
},
{
"path": "Configurations/Build/Common/XPC Service - ICL Extensions.xcconfig",
"chars": 1259,
"preview": "\n// Inline Content Loader XPC service extensions\n\n// TEXTUAL_WORKSPACE_DIR is replaced by the build script\nTEXTUAL_WORKS"
},
{
"path": "Configurations/Build/Common/XPC Service - ICL.xcconfig",
"chars": 76,
"preview": "\n// Inline content loader XPC service\n\n#include \"Preserve Symbols.xcconfig\"\n"
},
{
"path": "Configurations/Build/Common/XPC Services.xcconfig",
"chars": 1050,
"preview": "\n// XPC services\n\n// TEXTUAL_WORKSPACE_DIR is replaced by the build script\nTEXTUAL_WORKSPACE_DIR = ${PROJECT_DIR}/../..\n"
},
{
"path": "Configurations/Build/Debug/Enabled Features.xcconfig",
"chars": 653,
"preview": "\n// The main project of Textual, its extensions, and its XPC services\n// all inherit one of these configuration files to"
},
{
"path": "Configurations/Build/Debug/Textual App.xcconfig",
"chars": 201,
"preview": "\n#include \"../Common/Foundation Debug.xcconfig\"\n#include \"Textual.xcconfig\"\n#include \"../Common/Textual App.xcconfig\"\n\nC"
},
{
"path": "Configurations/Build/Debug/Textual Extensions.xcconfig",
"chars": 125,
"preview": "\n#include \"../Common/Foundation Debug.xcconfig\"\n#include \"Textual.xcconfig\"\n#include \"../Common/Textual Extensions.xccon"
},
{
"path": "Configurations/Build/Debug/Textual.xcconfig",
"chars": 398,
"preview": "\n#include \"../Common/Textual.xcconfig\"\n#include \"Enabled Features.xcconfig\"\n\nTEXTUAL_BUNDLE_IDENTIFIER = com.codeux.apps"
},
{
"path": "Configurations/Build/Debug/XPC Service - ICL Extensions.xcconfig",
"chars": 135,
"preview": "\n#include \"../Common/Foundation Debug.xcconfig\"\n#include \"Textual.xcconfig\"\n#include \"../Common/XPC Service - ICL Extens"
},
{
"path": "Configurations/Build/Debug/XPC Service - ICL.xcconfig",
"chars": 118,
"preview": "\n#include \"XPC Services.xcconfig\"\n#include \"../Common/XPC Service - ICL.xcconfig\"\n\nICL_EXTENSION_BUILD_SCHEME = Debug\n"
},
{
"path": "Configurations/Build/Debug/XPC Services.xcconfig",
"chars": 119,
"preview": "\n#include \"../Common/Foundation Debug.xcconfig\"\n#include \"Textual.xcconfig\"\n#include \"../Common/XPC Services.xcconfig\"\n"
},
{
"path": "Configurations/Build/README.md",
"chars": 22,
"preview": "\nTODO: Write a README\n"
},
{
"path": "Configurations/Build/Standard Release/Enabled Features.xcconfig",
"chars": 690,
"preview": "\n// The main project of Textual, its extensions, and its XPC services\n// all inherit one of these configuration files to"
},
{
"path": "Configurations/Build/Standard Release/Textual App.xcconfig",
"chars": 206,
"preview": "\n#include \"../Common/Foundation.xcconfig\"\n#include \"Textual.xcconfig\"\n#include \"../Common/Textual App.xcconfig\"\n\nCODE_SI"
},
{
"path": "Configurations/Build/Standard Release/Textual Extensions.xcconfig",
"chars": 119,
"preview": "\n#include \"../Common/Foundation.xcconfig\"\n#include \"Textual.xcconfig\"\n#include \"../Common/Textual Extensions.xcconfig\"\n"
},
{
"path": "Configurations/Build/Standard Release/Textual.xcconfig",
"chars": 362,
"preview": "\n#include \"../Common/Textual.xcconfig\"\n#include \"Enabled Features.xcconfig\"\n\nTEXTUAL_BUNDLE_IDENTIFIER = com.codeux.apps"
},
{
"path": "Configurations/Build/Standard Release/XPC Service - ICL Extensions.xcconfig",
"chars": 129,
"preview": "\n#include \"../Common/Foundation.xcconfig\"\n#include \"Textual.xcconfig\"\n#include \"../Common/XPC Service - ICL Extensions.x"
},
{
"path": "Configurations/Build/Standard Release/XPC Service - ICL.xcconfig",
"chars": 120,
"preview": "\n#include \"XPC Services.xcconfig\"\n#include \"../Common/XPC Service - ICL.xcconfig\"\n\nICL_EXTENSION_BUILD_SCHEME = Release\n"
},
{
"path": "Configurations/Build/Standard Release/XPC Services.xcconfig",
"chars": 113,
"preview": "\n#include \"../Common/Foundation.xcconfig\"\n#include \"Textual.xcconfig\"\n#include \"../Common/XPC Services.xcconfig\"\n"
},
{
"path": "Configurations/ExportArchiveConfiguration.plist",
"chars": 286,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Configurations/Sandbox/Inherited.entitlements",
"chars": 288,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "README.md",
"chars": 7765,
"preview": "> [!IMPORTANT]\n> Textual is no longer being actively maintained. For the life of the project, it only ever had one full "
},
{
"path": "Sources/App/Build Scripts/BuildExtensions.sh",
"chars": 901,
"preview": "#!/bin/bash\n\nset -e\n\necho \"Building using architecture: ${ARCHS}\"\n\nTEXTUAL_PRODUCT_LOCATION=\"${TARGET_BUILD_DIR}/${FULL_"
},
{
"path": "Sources/App/Build Scripts/BuildFrameworks.sh",
"chars": 884,
"preview": "#!/bin/bash\n\nset -e\n\necho \"Building using architecture: ${ARCHS}\"\n\nCONFIGURATION_BUILD_DIR=\"${TEXTUAL_WORKSPACE_TEMP_DIR"
},
{
"path": "Sources/App/Build Scripts/BuildServices.sh",
"chars": 866,
"preview": "#!/bin/bash\n\nset -e\n\necho \"Building using architecture: ${ARCHS}\"\n\nTEXTUAL_PRODUCT_LOCATION=\"${TARGET_BUILD_DIR}/${FULL_"
},
{
"path": "Sources/App/Build Scripts/ExportArchive.sh",
"chars": 3397,
"preview": "#!/bin/sh\n\nset -e\n\nWORKING_PATH=\"${TEXTUAL_WORKSPACE_TEMP_DIR}/ArchiveTan\"\n\nmkdir -p \"${WORKING_PATH}\"\n\ncd \"${WORKING_PA"
},
{
"path": "Sources/App/Build Scripts/MergeSwift.sh",
"chars": 989,
"preview": "#!/bin/sh\n\nset -e\n\n#\n# Textual and Remote Connection Manager service both use Swift.\n# Textual embeds the library and th"
},
{
"path": "Sources/App/Build Scripts/PostprocessSparkle.sh",
"chars": 548,
"preview": "#!/bin/bash\n\nset -e\n\necho \"Performing postprocessing on Sparkle framework\"\n\ncd \"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_"
},
{
"path": "Sources/App/Build Scripts/UpdateFeatureFlags.sh",
"chars": 972,
"preview": "#!/bin/bash\n\nset -e\n\ncd \"${TEXTUAL_WORKSPACE_TEMP_DIR}/Build Headers/\"\n\necho \"\n/* ANY CHANGES TO THIS FILE WILL NOT BE S"
},
{
"path": "Sources/App/Build Scripts/UpdateVersionInfo.sh",
"chars": 3014,
"preview": "#!/bin/sh\n\nset -e\n\ncd \"${TEXTUAL_WORKSPACE_TEMP_DIR}/\"\n\n# Make a copy of the Info.plist file in the .tmp folder\n# This w"
},
{
"path": "Sources/App/Classes/Controllers/TXAppearance.m",
"chars": 8765,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Controllers/TXApplication.m",
"chars": 3971,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Controllers/TXGlobalModels.m",
"chars": 7080,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Controllers/TXMasterController.m",
"chars": 19097,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Controllers/TXMenuController.m",
"chars": 92801,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Controllers/TXSharedApplication.m",
"chars": 4904,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Controllers/TXWindowController.m",
"chars": 6233,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Channel Spotlight/TDCChannelSpotlightAppearance.m",
"chars": 5751,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Channel Spotlight/TDCChannelSpotlightController.m",
"chars": 16715,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Channel Spotlight/TDCChannelSpotlightControls.m",
"chars": 3330,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Channel Spotlight/TDCChannelSpotlightSearchResult.m",
"chars": 3618,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Channel Spotlight/TDCChannelSpotlightSearchResultsTable.m",
"chars": 11807,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/File Transfers/TDCFileTransferDialog.m",
"chars": 26626,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/File Transfers/TDCFileTransferDialogTableCell.m",
"chars": 13085,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/File Transfers/TDCFileTransferDialogTransferController.m",
"chars": 34120,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/License Manager/Standalone/TDCLicenseManagerDialog.m",
"chars": 27277,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/License Manager/Standalone/TDCLicenseManagerMigrateAppStoreSheet.m",
"chars": 11057,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/License Manager/Standalone/TDCLicenseManagerRecoverLostLicenseSheet.m",
"chars": 4092,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/License Manager/Standalone/TDCLicenseUpgradeActivateSheet.m",
"chars": 5135,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/License Manager/Standalone/TDCLicenseUpgradeCommonActions.m",
"chars": 3150,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/License Manager/Standalone/TDCLicenseUpgradeDialog.m",
"chars": 5972,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/License Manager/Standalone/TDCLicenseUpgradeEligibilitySheet.m",
"chars": 10225,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Preferences/TDCPreferencesController.m",
"chars": 50116,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Preferences/TDCPreferencesNotificationConfiguration.m",
"chars": 4044,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Preferences/TDCPreferencesUserStyleSheet.m",
"chars": 4382,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Server Endpoint/TDCServerEndpointListSheet.m",
"chars": 7164,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/Server Endpoint/TDCServerEndpointListSheetTable.m",
"chars": 6880,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCAboutDialog.m",
"chars": 3279,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCAddressBookSheet.m",
"chars": 9441,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCAlert.m",
"chars": 23624,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCChannelBanListSheet.m",
"chars": 7662,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCChannelInviteSheet.m",
"chars": 4395,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCChannelModifyModesSheet.m",
"chars": 8119,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCChannelModifyTopicSheet.m",
"chars": 5950,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCChannelPropertiesNotificationConfiguration.m",
"chars": 4244,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCChannelPropertiesSheet.m",
"chars": 15737,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCHighlightEntrySheet.m",
"chars": 5453,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCInputPrompt.m",
"chars": 4154,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCNicknameColorSheet.m",
"chars": 3968,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCProgressIndicatorSheet.m",
"chars": 2872,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCServerChangeNicknameSheet.m",
"chars": 4490,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCServerChannelListDialog.m",
"chars": 8975,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCServerHighlightListSheet.m",
"chars": 6257,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCServerPropertiesSheet.m",
"chars": 74081,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCSheetBase.m",
"chars": 3406,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCWelcomeSheet.m",
"chars": 8973,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Dialogs/TDCWindowBase.m",
"chars": 2372,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/External Libraries/GTMEncodeHTML.h",
"chars": 2425,
"preview": "//\n// GTMNSString+HTML.h\n// Dealing with NSStrings that contain HTML\n//\n// Copyright 2006-2008 Google Inc.\n//\n// Lic"
},
{
"path": "Sources/App/Classes/Headers/External Libraries/OELReachability.h",
"chars": 2001,
"preview": "/*\n Copyright (c) 2011, Tony Million.\n All rights reserved.\n\n Redistribution and use in source and binary forms, with or"
},
{
"path": "Sources/App/Classes/Headers/IRC.h",
"chars": 2351,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCAddressBook.h",
"chars": 5211,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCAddressBookUserTracking.h",
"chars": 2964,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCChannel.h",
"chars": 4256,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCChannelConfig.h",
"chars": 5738,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCChannelMemberList.h",
"chars": 4159,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCChannelMode.h",
"chars": 3119,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCChannelUser.h",
"chars": 4028,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCClient.h",
"chars": 18994,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCClientConfig.h",
"chars": 9139,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCColorFormat.h",
"chars": 6327,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCCommandIndex.h",
"chars": 7153,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCConnection.h",
"chars": 3811,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCHighlightLogEntry.h",
"chars": 2511,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCHighlightMatchCondition.h",
"chars": 2772,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCISupportInfo.h",
"chars": 4667,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCMessage.h",
"chars": 4714,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCModeInfo.h",
"chars": 3152,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCNetworkList.h",
"chars": 2762,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCNumerics.h",
"chars": 5374,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCPrefix.h",
"chars": 2995,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCSendingMessage.h",
"chars": 2346,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCServer.h",
"chars": 2920,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCTreeItem.h",
"chars": 2920,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCUser.h",
"chars": 4358,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCUserRelations.h",
"chars": 2305,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/IRCWorld.h",
"chars": 4006,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCAddressBookInternal.h",
"chars": 2771,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCChannelConfigInternal.h",
"chars": 2648,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCChannelUserInternal.h",
"chars": 2339,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCClientConfigInternal.h",
"chars": 4078,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCHighlightLogEntryInternal.h",
"chars": 2289,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCHighlightMatchConditionInternal.h",
"chars": 2341,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCMessageInternal.h",
"chars": 2817,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCModeInfoInternal.h",
"chars": 2262,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCPrefixInternal.h",
"chars": 2293,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCServerInternal.h",
"chars": 2373,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/IRCUserInternal.h",
"chars": 2310,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/TDCChannelPropertiesSheetInternal.h",
"chars": 2308,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/TDCChannelSpotlightAppearanceInternal.h",
"chars": 2326,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/TDCChannelSpotlightControllerInternal.h",
"chars": 2696,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/TDCFileTransferDialogInternal.h",
"chars": 2335,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Internal/TVCLogLineInternal.h",
"chars": 2665,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/NSColorHelper.h",
"chars": 3269,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/NSStringHelper.h",
"chars": 5545,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/NSViewHelper.h",
"chars": 3272,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/ICLPayloadLocalPrivate.h",
"chars": 2272,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCAddressBookMatchCachePrivate.h",
"chars": 2787,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCAddressBookUserTrackingPrivate.h",
"chars": 2668,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCChannelConfigPrivate.h",
"chars": 2341,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCChannelMemberListControllerPrivate.h",
"chars": 2873,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCChannelMemberListPrivate.h",
"chars": 3958,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCChannelModePrivate.h",
"chars": 2493,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCChannelPrivate.h",
"chars": 3578,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCChannelUserPrivate.h",
"chars": 2437,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCClientConfigPrivate.h",
"chars": 3158,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCClientPrivate.h",
"chars": 6700,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCClientRequestedCommandsPrivate.h",
"chars": 4943,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCColorFormatPrivate.h",
"chars": 3182,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCCommandIndexPrivate.h",
"chars": 2242,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCConnectionPrivate.h",
"chars": 2918,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCExtrasPrivate.h",
"chars": 2829,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCHighlightLogEntryPrivate.h",
"chars": 2621,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCISupportInfoPrivate.h",
"chars": 2576,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCMessageBatchPrivate.h",
"chars": 3164,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCMessagePrivate.h",
"chars": 2318,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCServerPrivate.h",
"chars": 2792,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCTimerCommandPrivate.h",
"chars": 3625,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCTreeItemPrivate.h",
"chars": 2664,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCUserNicknameColorStyleGeneratorPrivate.h",
"chars": 2712,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCUserPersistentStorePrivate.h",
"chars": 2577,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCUserPrivate.h",
"chars": 2224,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCUserRelationsPrivate.h",
"chars": 3603,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/IRCWorldPrivate.h",
"chars": 3510,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/NSTableVIewHelperPrivate.h",
"chars": 2211,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/NSViewHelperPrivate.h",
"chars": 2512,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/SwiftBridgingHeaderPrivate.h",
"chars": 2196,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCAboutDialogPrivate.h",
"chars": 2346,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCAddressBookSheetPrivate.h",
"chars": 2762,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelBanListSheetPrivate.h",
"chars": 3517,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelInviteSheetPrivate.h",
"chars": 2843,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelModifyModesSheetPrivate.h",
"chars": 2752,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelModifyTopicSheetPrivate.h",
"chars": 2712,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelPropertiesNotificationConfigurationPrivate.h",
"chars": 2455,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelPropertiesSheetPrivate.h",
"chars": 3213,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelSpotlightAppearancePrivate.h",
"chars": 3240,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelSpotlightControllerPrivate.h",
"chars": 2553,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelSpotlightControlsPrivate.h",
"chars": 2292,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelSpotlightSearchResultPrivate.h",
"chars": 2551,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCChannelSpotlightSearchResultsTablePrivate.h",
"chars": 2387,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCFileTransferDialogPrivate.h",
"chars": 5343,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCFileTransferDialogTableCellPrivate.h",
"chars": 2323,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCFileTransferDialogTransferControllerPrivate.h",
"chars": 4671,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCHighlightEntrySheetPrivate.h",
"chars": 2671,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCLicenseManagerDialogPrivate.h",
"chars": 2694,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCLicenseManagerMigrateAppStoreSheetPrivate.h",
"chars": 2730,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCLicenseManagerRecoverLostLicenseSheetPrivate.h",
"chars": 2601,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCLicenseUpgradeActivateSheetPrivate.h",
"chars": 3050,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCLicenseUpgradeCommonActionsPrivate.h",
"chars": 2408,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCLicenseUpgradeDialogPrivate.h",
"chars": 2934,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCLicenseUpgradeEligibilitySheetPrivate.h",
"chars": 3445,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCNicknameColorSheetPrivate.h",
"chars": 2498,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCPreferencesControllerPrivate.h",
"chars": 2831,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCPreferencesNotificationConfigurationPrivate.h",
"chars": 2466,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCPreferencesUserStyleSheetPrivate.h",
"chars": 2440,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCProgressIndicatorSheetPrivate.h",
"chars": 2233,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCServerChangeNicknameSheetPrivate.h",
"chars": 2734,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCServerChannelListDialogPrivate.h",
"chars": 3047,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCServerEndpointListSheetPrivate.h",
"chars": 2596,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCServerEndpointListSheetTablePrivate.h",
"chars": 2195,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCServerHighlightListSheetPrivate.h",
"chars": 2575,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCServerPropertiesSheetPrivate.h",
"chars": 3758,
"preview": "/* *********************************************************************\n * _____ _ "
},
{
"path": "Sources/App/Classes/Headers/Private/TDCSharedProtocolDefinitionsPrivate.h",
"chars": 2519,
"preview": "/* *********************************************************************\n * _____ _ "
}
]
// ... and 710 more files (download for full content)
About this extraction
This page contains the full source code of the Codeux/Textual GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 910 files (7.4 MB), approximately 2.0M tokens, and a symbol index with 32 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.