From 9f79d49c899d5beeab4fc034b651c6155814d23d Mon Sep 17 00:00:00 2001 From: George Nachman Date: Fri, 18 Mar 2016 15:43:02 -0700 Subject: [PATCH] im not sure --- iTerm2.xcodeproj/project.pbxproj | 4 +- iTerm2XCTests/PTYTextViewTest.m | 4 - iTerm2XCTests/iTermToolbeltTest.m | 2 +- sources/AlertTrigger.m | 6 +- sources/CaptureTrigger.m | 6 +- sources/FakeWindow.m | 2 +- sources/GlobalSearch.m | 2 +- sources/GrowlTrigger.m | 2 +- sources/MovePaneController.m | 37 +- sources/PTYFontInfo.m | 12 +- sources/PTYSession+Scripting.m | 38 +- sources/PTYSession.h | 119 ++++++- sources/PTYSession.m | 385 +++++++++------------ sources/PTYTab.h | 31 +- sources/PTYTab.m | 244 ++++++++----- sources/PTYTextView.h | 1 - sources/PTYWindow+Scripting.m | 4 +- sources/PTYWindow.h | 8 +- sources/PseudoTerminal.m | 78 +++-- sources/SessionView.m | 6 +- sources/TmuxController.m | 7 +- sources/WindowControllerInterface.h | 3 + sources/iTermApplicationDelegate.m | 4 +- sources/iTermExposeGridView.m | 2 +- sources/iTermExposeView.m | 4 +- sources/iTermNewWindowCommand.m | 2 +- sources/iTermOpenQuicklyModel.m | 2 +- sources/iTermOpenQuicklyWindowController.m | 2 +- tools/release.sh | 2 + 29 files changed, 574 insertions(+), 445 deletions(-) diff --git a/iTerm2.xcodeproj/project.pbxproj b/iTerm2.xcodeproj/project.pbxproj index c96d85c05f..39b78f1175 100644 --- a/iTerm2.xcodeproj/project.pbxproj +++ b/iTerm2.xcodeproj/project.pbxproj @@ -2613,7 +2613,7 @@ A6B70FBF19830F0D007A4284 /* NewOutput.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = NewOutput.png; path = images/NewOutput.png; sourceTree = ""; }; A6B70FC019830F0D007A4284 /* NewOutput@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "NewOutput@2x.png"; path = "images/NewOutput@2x.png"; sourceTree = ""; }; A6B70FCB1986FB39007A4284 /* PTYSession+Scripting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PTYSession+Scripting.h"; sourceTree = ""; }; - A6B70FCC1986FB39007A4284 /* PTYSession+Scripting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PTYSession+Scripting.m"; sourceTree = ""; }; + A6B70FCC1986FB39007A4284 /* PTYSession+Scripting.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = "PTYSession+Scripting.m"; sourceTree = ""; }; A6BDB03F1B45E8BA00F511E6 /* VT100XtermParserTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VT100XtermParserTest.m; sourceTree = ""; }; A6BDB0401B45E8BA00F511E6 /* iTermEquivalenceClassSetTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermEquivalenceClassSetTest.m; sourceTree = ""; }; A6BDB0431B45E8EE00F511E6 /* VT100ScreenTest.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = VT100ScreenTest.m; sourceTree = ""; }; @@ -2636,7 +2636,7 @@ A6C760501B45C4CF00E3C992 /* libiTerm2Shared.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libiTerm2Shared.a; sourceTree = BUILT_PRODUCTS_DIR; }; A6C7641C1B45CB2800E3C992 /* iTerm2XCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iTerm2XCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; A6C7641F1B45CB2800E3C992 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A6C7DE4E199F302C001E5C75 /* PTYWindow+Scripting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "PTYWindow+Scripting.m"; sourceTree = ""; }; + A6C7DE4E199F302C001E5C75 /* PTYWindow+Scripting.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.objc; path = "PTYWindow+Scripting.m"; sourceTree = ""; }; A6C7DE51199F3087001E5C75 /* PTYWindow+Scripting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PTYWindow+Scripting.h"; sourceTree = ""; }; A6C7DE5319A4591E001E5C75 /* iTermNewWindowCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermNewWindowCommand.h; sourceTree = ""; }; A6C7DE5419A4591E001E5C75 /* iTermNewWindowCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermNewWindowCommand.m; sourceTree = ""; }; diff --git a/iTerm2XCTests/PTYTextViewTest.m b/iTerm2XCTests/PTYTextViewTest.m index 2c14c066b9..6c72f515ed 100644 --- a/iTerm2XCTests/PTYTextViewTest.m +++ b/iTerm2XCTests/PTYTextViewTest.m @@ -359,10 +359,6 @@ - (void)selectPaneAboveInCurrentTerminal { - (void)saveToDvr { } -- (BOOL)textViewInSameTabAsTextView:(PTYTextView *)other { - return YES; -} - - (void)removeInaccessibleNotes { } diff --git a/iTerm2XCTests/iTermToolbeltTest.m b/iTerm2XCTests/iTermToolbeltTest.m index a0470a2e3f..f1d420f1e9 100644 --- a/iTerm2XCTests/iTermToolbeltTest.m +++ b/iTerm2XCTests/iTermToolbeltTest.m @@ -50,7 +50,7 @@ - (void)setUp { // Create a window and save convenience pointers to its various bits. _session = [[iTermController sharedInstance] launchBookmark:nil inTerminal:nil]; - _windowController = (PseudoTerminal *)_session.tab.realParentWindow; + _windowController = (PseudoTerminal *)_session.delegate.realParentWindow; _view = (iTermRootTerminalView *)_windowController.window.contentView; // Make it big so all the tools fit. diff --git a/sources/AlertTrigger.m b/sources/AlertTrigger.m index 03066db53b..62678c548e 100644 --- a/sources/AlertTrigger.m +++ b/sources/AlertTrigger.m @@ -50,10 +50,10 @@ - (BOOL)performActionWithCapturedStrings:(NSString *const *)capturedStrings break; case NSAlertAlternateReturn: { - NSWindowController * term = [[aSession tab] realParentWindow]; + NSWindowController * term = [[aSession delegate] realParentWindow]; [[term window] makeKeyAndOrderFront:nil]; - [[term tabView] selectTabViewItemWithIdentifier:[aSession tab]]; - [[aSession tab] setActiveSession:aSession]; + [aSession.delegate sessionSelectContainingTab]; + [aSession.delegate setActiveSession:aSession]; break; case NSAlertOtherReturn: diff --git a/sources/CaptureTrigger.m b/sources/CaptureTrigger.m index 2c1a4eeda0..77db3ee4a6 100644 --- a/sources/CaptureTrigger.m +++ b/sources/CaptureTrigger.m @@ -43,15 +43,15 @@ - (NSString *)paramPlaceholder { } - (BOOL)capturedOutputToolVisibleInSession:(PTYSession *)aSession { - if (!aSession.tab.realParentWindow.shouldShowToolbelt) { + if (!aSession.delegate.realParentWindow.shouldShowToolbelt) { return NO; } return [iTermToolbeltView shouldShowTool:kCapturedOutputToolName]; } - (void)showCaptureOutputToolInSession:(PTYSession *)aSession { - if (!aSession.tab.realParentWindow.shouldShowToolbelt) { - [aSession.tab.realParentWindow toggleToolbeltVisibility:nil]; + if (!aSession.delegate.realParentWindow.shouldShowToolbelt) { + [aSession.delegate.realParentWindow toggleToolbeltVisibility:nil]; } if (![iTermToolbeltView shouldShowTool:kCapturedOutputToolName]) { [iTermToolbeltView toggleShouldShowTool:kCapturedOutputToolName]; diff --git a/sources/FakeWindow.m b/sources/FakeWindow.m index 4a3c0ca48a..5a107322b2 100644 --- a/sources/FakeWindow.m +++ b/sources/FakeWindow.m @@ -102,7 +102,7 @@ - (void)rejoin:(NSWindowController *)aTerm [aTerm sessionInitiatedResize:session width:pendingW height:pendingH]; } if (hasPendingFitWindowToTab) { - [aTerm fitWindowToTab:[session tab]]; + [aTerm fitWindowToTab:[aTerm tabForSession:session]]; } if (hasPendingSetWindowTitle) { [aTerm setWindowTitle]; diff --git a/sources/GlobalSearch.m b/sources/GlobalSearch.m index 6792156d10..46edba88ab 100644 --- a/sources/GlobalSearch.m +++ b/sources/GlobalSearch.m @@ -552,7 +552,7 @@ - (void)controlTextDidChange:(NSNotification *)aNotification GlobalSearchInstance* aSearch; aSearch = [[[GlobalSearchInstance alloc] initWithSession:aSession findString:findString - label:[iTermExpose labelForTab:[aSession tab] + label:[iTermExpose labelForTab:[aTerminal tabForSession:aSession] windowNumber:i+1 tabNumber:j+1]] autorelease]; [searches_ addObject:aSearch]; diff --git a/sources/GrowlTrigger.m b/sources/GrowlTrigger.m index aed240eab4..6cd54d889f 100644 --- a/sources/GrowlTrigger.m +++ b/sources/GrowlTrigger.m @@ -38,7 +38,7 @@ - (BOOL)performActionWithCapturedStrings:(NSString *const *)capturedStrings [gd growlNotify:[self paramWithBackreferencesReplacedWithValues:capturedStrings count:captureCount] withDescription:[NSString stringWithFormat:@"A trigger fired in session \"%@\" in tab #%d.", [aSession name], - [[aSession tab] realObjectCount]] + aSession.delegate.tabNumber] andNotification:@"Customized Message" windowIndex:[aSession screenWindowIndex] tabIndex:[aSession screenTabIndex] diff --git a/sources/MovePaneController.m b/sources/MovePaneController.m index ad479bf4bd..82a0fe6539 100644 --- a/sources/MovePaneController.m +++ b/sources/MovePaneController.m @@ -74,7 +74,7 @@ - (void)exitMovePaneMode } - (void)moveWindowBy:(NSPoint)point { - NSWindow *window = session_.tab.realParentWindow.window; + NSWindow *window = session_.delegate.realParentWindow.window; NSPoint origin = window.frame.origin; origin.x += point.x; origin.y += point.y; @@ -83,14 +83,13 @@ - (void)moveWindowBy:(NSPoint)point { [window setFrameOrigin:origin]; } -- (void)moveSessionToNewWindow:(PTYSession *)movingSession atPoint:(NSPoint)point -{ +- (void)moveSessionToNewWindow:(PTYSession *)movingSession atPoint:(NSPoint)point { if ([movingSession isTmuxClient]) { [[movingSession tmuxController] breakOutWindowPane:[movingSession tmuxPane] toPoint:point]; return; } - PTYTab *theTab = [movingSession tab]; + PTYTab *theTab = [movingSession.delegate.realParentWindow tabForSession:movingSession]; NSWindowController * term = [[theTab realParentWindow] terminalDraggedFromAnotherWindowAtPoint:point]; @@ -117,8 +116,8 @@ - (SessionView *)removeAndClearSession SessionView *oldView = [session_ view]; [oldView retain]; PTYSession *movingSession = session_; - PTYTab *theTab = [movingSession tab]; - [[movingSession tab] removeSession:movingSession]; + PTYTab *theTab = [movingSession.delegate.realParentWindow tabForSession:movingSession]; + [movingSession.delegate removeSession:movingSession]; if ([[theTab sessions] count] == 0) { [[theTab realParentWindow] closeTab:theTab]; } @@ -184,30 +183,32 @@ - (BOOL)dropInSession:(PTYSession *)dest return YES; } + PTYTab *destinationTab = [dest.delegate.realParentWindow tabForSession:dest]; if (isMove_) { PTYSession *movingSession = session_; BOOL isVertical = (half == kWestHalf || half == kEastHalf); - if (![[[dest tab] realParentWindow] canSplitPaneVertically:isVertical - withBookmark:[movingSession profile]]) { + if (![[destinationTab realParentWindow] canSplitPaneVertically:isVertical + withBookmark:[movingSession profile]]) { return NO; } SessionView *oldView = [movingSession view]; [oldView retain]; - PTYTab *theTab = [movingSession tab]; - [[movingSession tab] removeSession:movingSession]; + PTYTab *theTab = [movingSession.delegate.realParentWindow tabForSession:movingSession]; + [theTab removeSession:movingSession]; if ([[theTab sessions] count] == 0) { [[theTab realParentWindow] closeTab:theTab]; } - [[[dest tab] realParentWindow] splitVertically:isVertical - before:(half == kNorthHalf || half == kWestHalf) - addingSession:movingSession - targetSession:dest - performSetup:NO]; + + [[destinationTab realParentWindow] splitVertically:isVertical + before:(half == kNorthHalf || half == kWestHalf) + addingSession:movingSession + targetSession:dest + performSetup:NO]; [oldView release]; - [[dest tab] fitSessionToCurrentViewSize:movingSession]; + [destinationTab fitSessionToCurrentViewSize:movingSession]; } else { - [[dest tab] swapSession:dest withSession:session_]; + [destinationTab swapSession:dest withSession:session_]; } return YES; } @@ -228,7 +229,7 @@ - (void)beginDrag:(PTYSession *)session { [pboard declareTypes:@[ iTermMovePaneDragType ] owner: nil]; [pboard setString:@"" forType:iTermMovePaneDragType]; - PTYTab *theTab = [session tab]; + PTYTab *theTab = [session.delegate.realParentWindow tabForSession:session]; NSRect rect = [[[session view] superview] convertRect:[[session view] frame] toView:nil]; SessionView *source = [session view]; [source retain]; diff --git a/sources/PTYFontInfo.m b/sources/PTYFontInfo.m index 396bc0b26f..480c01b128 100644 --- a/sources/PTYFontInfo.m +++ b/sources/PTYFontInfo.m @@ -32,10 +32,10 @@ - (void)dealloc { } - (PTYFontInfo *)computedBoldVersion { - NSFontManager* fontManager = [NSFontManager sharedFontManager]; - NSFont* boldFont = [fontManager convertWeight:YES ofFont:font_]; - if (boldFont && ([fontManager traitsOfFont:boldFont] & NSBoldFontMask)) { - DLog(@"Bold version of %@ is %@", font_, boldFont); + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSFont *boldFont = [fontManager convertWeight:YES ofFont:font_]; + DLog(@"Bold version of %@ is %@", font_, boldFont); + if (boldFont && boldFont != font_) { return [PTYFontInfo fontInfoWithFont:boldFont baseline:baselineOffset_]; } else { DLog(@"Failed to find a bold version of %@", font_); @@ -46,8 +46,8 @@ - (PTYFontInfo *)computedBoldVersion { - (PTYFontInfo *)computedItalicVersion { NSFontManager* fontManager = [NSFontManager sharedFontManager]; NSFont* italicFont = [fontManager convertFont:font_ toHaveTrait:NSItalicFontMask]; - if (italicFont && ([fontManager traitsOfFont:italicFont] & NSItalicFontMask)) { - DLog(@"Italic version of %@ is %@", font_, italicFont); + DLog(@"Italic version of %@ is %@", font_, italicFont); + if (italicFont && italicFont != font_) { return [PTYFontInfo fontInfoWithFont:italicFont baseline:baselineOffset_]; } else { DLog(@"Failed to find an italic version of %@", font_); diff --git a/sources/PTYSession+Scripting.m b/sources/PTYSession+Scripting.m index 7146db4be2..cf36e87bea 100644 --- a/sources/PTYSession+Scripting.m +++ b/sources/PTYSession+Scripting.m @@ -7,16 +7,16 @@ @implementation PTYSession (Scripting) // Object specifier - (NSScriptObjectSpecifier *)objectSpecifier { - if (![[self tab] realParentWindow]) { + if (![self.delegate realParentWindow]) { // TODO(georgen): scripting is broken while in instant replay. return nil; } id classDescription = [NSClassDescription classDescriptionForClass:[PTYTab class]]; - return [[[NSUniqueIDSpecifier alloc] initWithContainerClassDescription:classDescription - containerSpecifier:[self.tab objectSpecifier] - key:@"sessions" - uniqueID:self.guid] autorelease]; + return [[[NSUniqueIDSpecifier alloc] initWithContainerClassDescription:classDescription + containerSpecifier:[self.delegate objectSpecifier] + key:@"sessions" + uniqueID:self.guid] autorelease]; } // Handlers for supported commands: @@ -39,7 +39,7 @@ - (void)handleExecScriptCommand:(NSScriptCommand *)aCommand { } - (void)handleSelectCommand:(NSScriptCommand *)command { - [[self tab] setActiveSession:self]; + [self.delegate setActiveSession:self]; } - (void)handleClearScriptCommand:(NSScriptCommand *)command { @@ -135,9 +135,9 @@ - (id)handleSetVariableNamedCommand:(NSScriptCommand *)command { } - (PTYSession *)activateSessionAndTab { - PTYSession *saved = [self.tab.realParentWindow currentSession]; - [[self.tab.realParentWindow tabView] selectTabViewItemWithIdentifier:self.tab]; - [self.tab setActiveSession:self]; + PTYSession *saved = [self.delegate.realParentWindow currentSession]; + [self.delegate sessionSelectContainingTab]; + [self.delegate setActiveSession:self]; return saved; } @@ -152,8 +152,8 @@ - (PTYSession *)splitVertically:(BOOL)vertically temp[KEY_COMMAND_LINE] = command; profile = temp; } - PTYSession *session = [[[self tab] realParentWindow] splitVertically:vertically - withProfile:profile]; + PTYSession *session = [[self.delegate realParentWindow] splitVertically:vertically + withProfile:profile]; [formerSession activateSessionAndTab]; return session; } @@ -237,11 +237,11 @@ - (id)handleSplitHorizontallyWithSameProfile:(NSScriptCommand *)scriptCommand { } - (void)handleTerminateScriptCommand:(NSScriptCommand *)command { - [[self tab] closeSession:self]; + [self.delegate closeSession:self]; } - (void)handleCloseCommand:(NSScriptCommand *)scriptCommand { - [self.tab.realParentWindow closeSessionWithConfirmation:self]; + [self.delegate.realParentWindow closeSessionWithConfirmation:self]; } - (NSColor *)backgroundColor { @@ -445,15 +445,15 @@ - (void)setAnsiBrightWhiteColor:(NSColor*)color { } - (void)setColumns:(int)columns { - [[[self tab] realParentWindow] sessionInitiatedResize:self - width:columns - height:self.rows]; + [[self.delegate realParentWindow] sessionInitiatedResize:self + width:columns + height:self.rows]; } - (void)setRows:(int)rows { - [[[self tab] realParentWindow] sessionInitiatedResize:self - width:self.columns - height:rows]; + [[self.delegate realParentWindow] sessionInitiatedResize:self + width:self.columns + height:rows]; } - (NSString *)profileName { diff --git a/sources/PTYSession.h b/sources/PTYSession.h index 5ebb607151..4dbf289e1d 100644 --- a/sources/PTYSession.h +++ b/sources/PTYSession.h @@ -29,10 +29,12 @@ extern NSString *const kPTYSessionCapturedOutputDidChange; @class FakeWindow; @class iTermAnnouncementViewController; @class PTYScrollView; +@class PTYTab; @class PTYTask; @class PTYTextView; @class PasteContext; @class PreferencePanel; +@class PTYSession; @class VT100RemoteHost; @class VT100Screen; @class VT100Terminal; @@ -41,6 +43,7 @@ extern NSString *const kPTYSessionCapturedOutputDidChange; @class iTermController; @class iTermGrowlDelegate; @class iTermQuickLookController; +@class SessionView; // The time period for just blinking is in -[iTermAdvancedSettingsModel timeBetweenBlinks]. // Timer period when receiving lots of data. @@ -64,7 +67,114 @@ typedef enum { TMUX_CLIENT // Session mirrors a tmux virtual window } PTYSessionTmuxMode; -@class PTYTab; +// This is implemented by a view that coontains a collection of sessions, nominally a tab. +@protocol PTYSessionDelegate + +// Return the window controller for this session. This is the "real" one, not a +// possible proxy that gets subbed in during instant replay. +- (NSWindowController *)realParentWindow; + +// A window controller which may be a proxy during instant replay. +- (id)parentWindow; + +// When a surrogate view is replacing the real SessionView, a reference to the +// real (or "live") view must be held. Invoke this to add it to an array of +// live views so it will be kept alive. Use -showLiveSession:inPlaceOf: to +// remove a view from this list. +- (void)addHiddenLiveView:(SessionView *)hiddenLiveView; + +// Provides a tab number for the ITERM_SESSION_ID environment variable. This +// may not correspond to the physical tab number because it's immutable for a +// given tab. +- (int)tabNumberForItermSessionId; + +// Sibling sessions in this tab. +- (NSArray *)sessions; + +// Remove aSession from the tab. +// Remove a dead session. This should be called from [session terminate] only. +- (void)removeSession:(PTYSession *)aSession; + +// Is the tab this session belongs to currently visible? +- (BOOL)sessionBelongsToVisibleTab; + +// The tab number as shown in the tab bar, starting at 1. May go into double digits. +- (int)tabNumber; + +// Returns true if another update may be needed later (so the timer should be scheduled). +- (BOOL)updateLabelAttributes; + +// End the session (calling terminate normally or killing/hiding a tmux +// session), and closes the tab if neeed. +- (void)closeSession:(PTYSession *)session; + +// Sets whether the bell indicator should show. +- (void)setBell:(BOOL)flag; + +// Something changed with the profile that might cause the window to be blurred. +- (void)recheckBlur; + +// Indicates if this session is the active one in its tab, even if the tab +// isn't necessarily selected. +- (BOOL)sessionIsActiveInTab:(PTYSession *)session; + +// Session-initiated name change. +- (void)nameOfSession:(PTYSession *)session didChangeTo:(NSString *)newName; + +// Session-initiated font size. May cause window size to adjust. +- (void)sessionDidChangeFontSize:(PTYSession *)session; + +// Session-initiated resize. +- (void)sessionInitiatedResize:(PTYSession*)session width:(int)width height:(int)height; + +// Select the "next" session in this tab. +- (void)nextSession; + +// Select the "previous" session in this tab. +- (void)previousSession; + +// Does the tab have a maximized pane? +- (BOOL)hasMaximizedPane; + +// Make session active in this tab. Assumes session belongs to thet ab. +- (void)setActiveSession:(PTYSession*)session; + +// Index of the tab. 0-based. +- (int)number; + +// Make the containing tab selected in its window. The window may not be visible, though. +- (void)sessionSelectContainingTab; + +// Add the session to the restorableSession. +- (void)addSession:(PTYSession *)self toRestorableSession:(iTermRestorableSession *)restorableSession; + +// Unmaximize the maximized pane (if any) in the tab. +- (void)unmaximize; + +// Tmux window number (a tmux window is like a tab). +- (void)setTmuxFont:(NSFont *)font + nonAsciiFont:(NSFont *)nonAsciiFont + hSpacing:(double)horizontalSpacing + vSpacing:(double)verticalSpacing; + +// Returns the profile to use for tmux sessions. +- (Profile *)tmuxBookmark; + +// Notify the tab that this session, which is a tmux gateway, received a rename of a tmux window. +- (void)sessionWithTmuxGateway:(PTYSession *)session + wasNotifiedWindowWithId:(int)windowId + renamedTo:(NSString *)newName; + +// Returns the objectSpecifier of the tab (used to identify a tab for Applescript). +- (NSScriptObjectSpecifier *)objectSpecifier; + +// Returns the tmux window ID of the containing tab. -1 if not tmux. +- (int)tmuxWindow; + +// If the tab is a tmux window, this gives its name. +- (NSString *)tmuxWindowName; +@end + @class SessionView; @interface PTYSession : NSResponder < FindViewControllerDelegate, @@ -73,7 +183,7 @@ typedef enum { PTYTextViewDelegate, TmuxGatewayDelegate, VT100ScreenDelegate> - +@property(nonatomic, assign) id delegate; @property(nonatomic, assign) BOOL alertOnNextMark; @property(nonatomic, copy) NSColor *tabColor; @@ -97,9 +207,6 @@ typedef enum { // Array of subprocessess names. @property(nonatomic, readonly) NSArray *childJobNames; -// The owning tab. TODO: Make this into a protocol because it's essentially a delegate. -@property(nonatomic, assign) PTYTab *tab; - // Time since reference date when last output was receivced. @property(nonatomic, readonly) NSTimeInterval lastOutput; @@ -342,7 +449,7 @@ typedef enum { - (void)setSizeFromArrangement:(NSDictionary*)arrangement; + (PTYSession*)sessionFromArrangement:(NSDictionary*)arrangement inView:(SessionView*)sessionView - inTab:(PTYTab*)theTab + withDelegate:(id)delegate forObjectType:(iTermObjectType)objectType; + (NSDictionary *)arrangementFromTmuxParsedLayout:(NSDictionary *)parseNode bookmark:(Profile *)bookmark; diff --git a/sources/PTYSession.m b/sources/PTYSession.m index 3b5a0d6981..08afb41351 100644 --- a/sources/PTYSession.m +++ b/sources/PTYSession.m @@ -47,7 +47,6 @@ #import "ProfilePreferencesViewController.h" #import "ProfilesColorsPreferencesViewController.h" #import "PTYScrollView.h" -#import "PTYTab.h" #import "PTYTask.h" #import "PTYTextView.h" #import "SCPFile.h" @@ -540,8 +539,8 @@ - (void)irAdvance:(int)dir { if (!_dvr) { if (dir < 0) { - [[[self tab] realParentWindow] replaySession:self]; - PTYSession* irSession = [[[self tab] realParentWindow] currentSession]; + [[_delegate realParentWindow] replaySession:self]; + PTYSession* irSession = [[_delegate realParentWindow] currentSession]; if (irSession != self) { // Failed to enter replay mode (perhaps nothing to replay?) [irSession irAdvance:dir]; @@ -659,7 +658,7 @@ - (void)setSizeFromArrangement:(NSDictionary*)arrangement + (PTYSession*)sessionFromArrangement:(NSDictionary *)arrangement inView:(SessionView *)sessionView - inTab:(PTYTab *)theTab + withDelegate:(id)delegate forObjectType:(iTermObjectType)objectType { DLog(@"Restoring session from arrangement"); PTYSession* aSession = [[[PTYSession alloc] init] autorelease]; @@ -684,7 +683,7 @@ + (PTYSession*)sessionFromArrangement:(NSDictionary *)arrangement // set our preferences [aSession setProfile:theBookmark]; - [aSession setScreenSize:[sessionView frame] parent:[theTab realParentWindow]]; + [aSession setScreenSize:[sessionView frame] parent:[delegate realParentWindow]]; NSDictionary *state = [arrangement objectForKey:SESSION_ARRANGEMENT_TMUX_STATE]; if (state) { // For tmux tabs, get the size from the arrangement instead of the containing view because @@ -700,10 +699,10 @@ + (PTYSession*)sessionFromArrangement:(NSDictionary *)arrangement } else { [aSession setBookmarkName:[theBookmark objectForKey:KEY_NAME]]; } - if ([[[[theTab realParentWindow] window] title] compare:@"Window"] == NSOrderedSame) { - [[theTab realParentWindow] setWindowTitle]; + if ([[[[delegate realParentWindow] window] title] compare:@"Window"] == NSOrderedSame) { + [[delegate realParentWindow] setWindowTitle]; } - [aSession setTab:theTab]; + aSession.delegate = delegate; BOOL haveSavedProgramData = YES; if ([arrangement[SESSION_ARRANGEMENT_PROGRAM] isKindOfClass:[NSDictionary class]]) { @@ -963,10 +962,10 @@ + (PTYSession*)sessionFromArrangement:(NSDictionary *)arrangement NSDictionary *liveArrangement = arrangement[SESSION_ARRANGEMENT_LIVE_SESSION]; if (liveArrangement) { SessionView *liveView = [[[SessionView alloc] initWithFrame:sessionView.frame] autorelease]; - [theTab addHiddenLiveView:liveView]; + [delegate addHiddenLiveView:liveView]; aSession.liveSession = [self sessionFromArrangement:liveArrangement inView:liveView - inTab:theTab + withDelegate:delegate forObjectType:objectType]; } if (shouldEnterTmuxMode) { @@ -1169,7 +1168,7 @@ - (void)runCommandWithOldCwd:(NSString*)oldCWD } isUTF8 = ([iTermProfilePreferences unsignedIntegerForKey:KEY_CHARACTER_ENCODING inProfile:profile] == NSUTF8StringEncoding); - [[[self tab] realParentWindow] setName:theName forSession:self]; + [[_delegate realParentWindow] setName:theName forSession:self]; // Start the command [self startProgram:cmd @@ -1184,7 +1183,7 @@ - (void)setWidth:(int)width height:(int)height [_screen resizeWidth:width height:height]; [_shell setWidth:width height:height]; [_textview clearHighlights]; - [[_tab realParentWindow] invalidateRestorableState]; + [[_delegate realParentWindow] invalidateRestorableState]; } - (void)setSplitSelectionMode:(SplitSelectionMode)mode move:(BOOL)move { @@ -1285,9 +1284,9 @@ - (BOOL)shouldSetCtype { - (NSString *)sessionId { return [NSString stringWithFormat:@"w%dt%dp%lu:%@", - [[_tab realParentWindow] number], - _tab.tabNumberForItermSessionId, - (unsigned long)_tab.sessions.count, + [[_delegate realParentWindow] number], + _delegate.tabNumberForItermSessionId, + (unsigned long)_delegate.sessions.count, self.guid]; } @@ -1431,12 +1430,7 @@ - (void)_maybeWarnAboutShortLivedSessions - (iTermRestorableSession *)restorableSession { iTermRestorableSession *restorableSession = [[[iTermRestorableSession alloc] init] autorelease]; - restorableSession.sessions = @[ self ]; - restorableSession.terminalGuid = self.tab.realParentWindow.terminalGuid; - restorableSession.tabUniqueId = self.tab.uniqueId; - restorableSession.arrangement = self.tab.arrangement; - restorableSession.group = kiTermRestorableSessionGroupSession; - + [_delegate addSession:self toRestorableSession:restorableSession]; return restorableSession; } @@ -1467,16 +1461,16 @@ - (void)terminate { [self _maybeWarnAboutShortLivedSessions]; } if (self.tmuxMode == TMUX_CLIENT) { - assert([_tab tmuxWindow] >= 0); - [_tmuxController deregisterWindow:[_tab tmuxWindow] + assert([_delegate tmuxWindow] >= 0); + [_tmuxController deregisterWindow:[_delegate tmuxWindow] windowPane:_tmuxPane session:self]; // This call to fitLayoutToWindows is necessary to handle the case where // a small window closes and leaves behind a larger (e.g., fullscreen) // window. We want to set the client size to that of the smallest // remaining window. - int n = [[_tab sessions] count]; - if ([[_tab sessions] indexOfObjectIdenticalTo:self] != NSNotFound) { + int n = [[_delegate sessions] count]; + if ([[_delegate sessions] indexOfObjectIdenticalTo:self] != NSNotFound) { n--; } if (n == 0) { @@ -1521,7 +1515,7 @@ - (void)terminate { // final update of display [self updateDisplay]; - [_tab removeSession:self]; + [_delegate removeSession:self]; _colorMap.delegate = nil; @@ -1537,9 +1531,9 @@ - (void)terminate { [_pasteHelper abort]; - [[_tab realParentWindow] sessionDidTerminate:self]; + [[_delegate realParentWindow] sessionDidTerminate:self]; - _tab = nil; + _delegate = nil; } - (void)makeTerminationUndoable { @@ -1611,10 +1605,10 @@ - (void)writeTaskImpl:(NSData *)data canBroadcast:(BOOL)canBroadcast { } // check if we want to send this input to all the sessions - if (canBroadcast && [[[self tab] realParentWindow] broadcastInputToSession:self]) { + if (canBroadcast && [[_delegate realParentWindow] broadcastInputToSession:self]) { // Ask the parent window to write directly to the PTYTask of all // sessions being broadcasted to. - [[[self tab] realParentWindow] sendInputToAllSessions:data]; + [[_delegate realParentWindow] sendInputToAllSessions:data]; } else if (!_exited) { // Send to only this session if (canBroadcast) { @@ -1672,8 +1666,8 @@ - (void)writeTask:(NSData*)data { if (self.tmuxMode == TMUX_CLIENT) { [self setBell:NO]; - if ([[_tab realParentWindow] broadcastInputToSession:self]) { - [[_tab realParentWindow] sendInputToAllSessions:data]; + if ([[_delegate realParentWindow] broadcastInputToSession:self]) { + [[_delegate realParentWindow] sendInputToAllSessions:data]; } else { [[_tmuxController gateway] sendKeys:data toWindowPane:_tmuxPane]; @@ -1808,12 +1802,13 @@ - (void)executeTokens:(const CVector *)vector bytesHandled:(int)length { } - (void)finishedHandlingNewOutputOfLength:(int)length { + DLog(@"Session %@ is processing", self.name); _lastOutput = [NSDate timeIntervalSinceReferenceDate]; _newOutput = YES; // Make sure the screen gets redrawn soonish _updateDisplayUntil = [NSDate timeIntervalSinceReferenceDate] + 10; - if ([[[self tab] parentWindow] currentTab] == [self tab]) { + if ([_delegate sessionBelongsToVisibleTab]) { if (length < 1024) { [self scheduleUpdateIn:kFastTimerIntervalSec]; } else { @@ -1965,13 +1960,13 @@ - (void)brokenPipe { [[iTermGrowlDelegate sharedInstance] growlNotify:@"Session Ended" withDescription:[NSString stringWithFormat:@"Session \"%@\" in tab #%d just terminated.", [self name], - [[self tab] realObjectCount]] + [_delegate tabNumber]] andNotification:@"Broken Pipes"]; } _exited = YES; [[NSNotificationCenter defaultCenter] postNotificationName:kCurrentSessionDidChange object:nil]; - [[self tab] updateLabelAttributes]; + [_delegate updateLabelAttributes]; if (_shouldRestart) { [_terminal resetByUserRequest:NO]; @@ -1979,7 +1974,7 @@ - (void)brokenPipe { [self replaceTerminatedShellWithNewInstance]; } else if ([self autoClose]) { [self appendBrokenPipeMessage:@"Broken Pipe"]; - [[self tab] closeSession:self]; + [_delegate closeSession:self]; } else { // Offer to restart the session by rerunning its program. [self appendBrokenPipeMessage:@"Broken Pipe"]; @@ -2043,7 +2038,7 @@ - (NSSize)idealScrollViewSizeWithStyle:(NSScrollerStyle)scrollerStyle { NSSize innerSize = NSMakeSize([_screen width] * [_textview charWidth] + MARGIN * 2, [_screen height] * [_textview lineHeight] + VMARGIN * 2); - BOOL hasScrollbar = [[_tab realParentWindow] scrollbarShouldBeVisible]; + BOOL hasScrollbar = [[_delegate realParentWindow] scrollbarShouldBeVisible]; NSSize outerSize = [PTYScrollView frameSizeForContentSize:innerSize horizontalScrollerClass:nil @@ -2066,13 +2061,6 @@ - (int)_keyBindingActionForEvent:(NSEvent*)event unmodkeystr = [event charactersIgnoringModifiers]; unmodunicode = [unmodkeystr length]>0?[unmodkeystr characterAtIndex:0]:0; - /* - unsigned short keycode = [event keyCode]; - NSString *keystr = [event characters]; - unichar unicode = [keystr length] > 0 ? [keystr characterAtIndex:0] : 0; - NSLog(@"event:%@ (%x+%x)[%@][%@]:%x(%c) <%d>", event,modflag,keycode,keystr,unmodkeystr,unicode,unicode,(modflag & NSNumericPadKeyMask)); - */ - // Check if we have a custom key mapping for this event keyBindingAction = [iTermKeyBindingMgr actionForKeyCode:unmodunicode modifiers:modflag @@ -2319,7 +2307,6 @@ - (void)insertText:(NSString *)string return; } - // NSLog(@"insertText:%@",string); mstring = [NSMutableString stringWithString:string]; max = [string length]; for (i = 0; i < max; i++) { @@ -2395,11 +2382,11 @@ - (BOOL)shouldPostGrowlNotification { if (!_screen.postGrowlNotifications) { return NO; } - if (![[self tab] isForegroundTab]) { + if (![_delegate sessionBelongsToVisibleTab]) { return YES; } BOOL windowIsObscured = - ([[iTermController sharedInstance] terminalIsObscured:self.tab.realParentWindow]); + ([[iTermController sharedInstance] terminalIsObscured:_delegate.realParentWindow]); return (windowIsObscured); } @@ -2454,11 +2441,10 @@ - (void)openSelection { NSBeep(); } -- (void)setBell:(BOOL)flag -{ +- (void)setBell:(BOOL)flag { if (flag != _bell) { _bell = flag; - [[self tab] setBell:flag]; + [_delegate setBell:flag]; if (_bell) { if ([_textview keyIsARepeat] == NO && [self shouldPostGrowlNotification] && @@ -2466,7 +2452,7 @@ - (void)setBell:(BOOL)flag [[iTermGrowlDelegate sharedInstance] growlNotify:@"Bell" withDescription:[NSString stringWithFormat:@"Session %@ #%d just rang a bell!", [self name], - [[self tab] realObjectCount]] + [_delegate tabNumber]] andNotification:@"Bells" windowIndex:[self screenWindowIndex] tabIndex:[self screenTabIndex] @@ -2741,9 +2727,9 @@ - (void)setPreferencesFromAddressBookEntry:(NSDictionary *)aePrefs [_textview setBlinkingCursor:[iTermProfilePreferences boolForKey:KEY_BLINKING_CURSOR inProfile:aDict]]; [_textview setCursorType:[iTermProfilePreferences intForKey:KEY_CURSOR_TYPE inProfile:aDict]]; - PTYTab* currentTab = [[[self tab] parentWindow] currentTab]; - if (currentTab == nil || currentTab == [self tab]) { - [[self tab] recheckBlur]; + PTYTab* currentTab = [[_delegate parentWindow] currentTab]; + if (currentTab == nil || [_delegate sessionBelongsToVisibleTab]) { + [_delegate recheckBlur]; } [_triggers release]; _triggers = [[NSMutableArray alloc] init]; @@ -2796,7 +2782,7 @@ - (void)setPreferencesFromAddressBookEntry:(NSDictionary *)aePrefs verticalSpacing:[iTermProfilePreferences floatForKey:KEY_VERTICAL_SPACING inProfile:aDict]]; [_screen setSaveToScrollbackInAlternateScreen:[iTermProfilePreferences boolForKey:KEY_SCROLLBACK_IN_ALTERNATE_SCREEN inProfile:aDict]]; - [[_tab realParentWindow] invalidateRestorableState]; + [[_delegate realParentWindow] invalidateRestorableState]; } - (NSString *)badgeLabel { @@ -2829,7 +2815,7 @@ - (NSString*)formattedName:(NSString*)base { // window name. This is confusing: this refers to the name of a tmux window, which is // equivalent to an iTerm2 tab. It is reported to us by tmux. We ignore the base name // because the real name comes from the server and that's all we care about. - return [NSString stringWithFormat:@"↣ %@", [[self tab] tmuxWindowName]]; + return [NSString stringWithFormat:@"↣ %@", [_delegate tmuxWindowName]]; } BOOL baseIsBookmarkName = [base isEqualToString:_bookmarkName]; if ([iTermPreferences boolForKey:kPreferenceKeyShowJobName] && self.jobName) { @@ -2881,18 +2867,17 @@ - (void)setDefaultName:(NSString*)theName _defaultName = [theName copy]; } -- (void)setTab:(PTYTab*)tab -{ +- (void)setDelegate:(id)delegate { if ([self isTmuxClient]) { - [_tmuxController deregisterWindow:[_tab tmuxWindow] + [_tmuxController deregisterWindow:[_delegate tmuxWindow] windowPane:_tmuxPane session:self]; } - _tab = tab; + _delegate = delegate; if ([self isTmuxClient]) { [_tmuxController registerSession:self withPane:_tmuxPane - inWindow:[_tab tmuxWindow]]; + inWindow:[_delegate tmuxWindow]]; } [_tmuxController fitLayoutToWindows]; } @@ -2938,13 +2923,13 @@ - (void)setName:(NSString*)theName [self setWindowTitle:theName]; } - [[self tab] nameOfSession:self didChangeTo:[self name]]; + [_delegate nameOfSession:self didChangeTo:[self name]]; [self setBell:NO]; // get the session submenu to be rebuilt - if ([[iTermController sharedInstance] currentTerminal] == [[self tab] parentWindow]) { + if ([[iTermController sharedInstance] currentTerminal] == [_delegate parentWindow]) { [[NSNotificationCenter defaultCenter] postNotificationName:@"iTermNameOfSessionDidChange" - object:[[self tab] parentWindow] + object:[_delegate parentWindow] userInfo:nil]; } _variables[kVariableKeySessionName] = [self name]; @@ -2972,8 +2957,8 @@ - (void)setWindowTitle:(NSString*)theTitle _windowTitle = [theTitle copy]; } - if ([[[self tab] parentWindow] currentTab] == [self tab]) { - [[[self tab] parentWindow] setWindowTitle]; + if ([_delegate sessionBelongsToVisibleTab]) { + [[_delegate parentWindow] setWindowTitle]; } } @@ -3259,8 +3244,8 @@ - (void)setProfile:(Profile *)newProfile { [_profile release]; _profile = [mutableProfile retain]; - [[_tab realParentWindow] invalidateRestorableState]; - [[[self tab] realParentWindow] updateTabColors]; + [[_delegate realParentWindow] invalidateRestorableState]; + [[_delegate realParentWindow] updateTabColors]; } - (void)sendCommand:(NSString *)command @@ -3419,11 +3404,11 @@ - (void)updateDisplay { // Set attributes of tab to indicate idle, processing, etc. if (![self isTmuxGateway]) { - anotherUpdateNeeded |= [[self tab] updateLabelAttributes]; + anotherUpdateNeeded |= [_delegate updateLabelAttributes]; } static const NSTimeInterval kUpdateTitlePeriod = 0.7; - if ([[self tab] activeSession] == self) { + if ([_delegate sessionIsActiveInTab:self]) { // Update window info for the active tab. NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; if (!self.jobName || @@ -3442,7 +3427,7 @@ - (void)updateDisplay { } anotherUpdateNeeded |= [_textview refresh]; - anotherUpdateNeeded |= self.tab.realParentWindow.isShowingTransientTitle; + anotherUpdateNeeded |= _delegate.realParentWindow.isShowingTransientTitle; BOOL animating = _textview.getAndResetDrawingAnimatedImageFlag; anotherUpdateNeeded |= animating; @@ -3450,7 +3435,7 @@ - (void)updateDisplay { if (animating) { // A cell of animated GIF has been drawn since the last call to updateDisplay. [self scheduleUpdateIn:kFastTimerIntervalSec]; - } else if ([[[self tab] parentWindow] currentTab] == [self tab]) { + } else if ([_delegate sessionBelongsToVisibleTab]) { [self scheduleUpdateIn:[iTermAdvancedSettingsModel timeBetweenBlinks]]; } else { [self scheduleUpdateIn:kBackgroundSessionIntervalSec]; @@ -3474,13 +3459,13 @@ - (void)updateTitles { // Update the job name in the tab title. if (!(newJobName == self.jobName || [newJobName isEqualToString:self.jobName])) { self.jobName = newJobName; - [[self tab] nameOfSession:self didChangeTo:[self name]]; + [_delegate nameOfSession:self didChangeTo:[self name]]; [self.view setTitle:self.name]; } - if (self.tab.isForegroundTab) { + if ([_delegate sessionBelongsToVisibleTab]) { // Revert to the permanent tab title. - [[[self tab] parentWindow] setWindowTitle]; + [[_delegate parentWindow] setWindowTitle]; } } @@ -3600,10 +3585,9 @@ - (NSFont*)fontWithRelativeSize:(int)dir from:(NSFont*)font - (void)setFont:(NSFont*)font nonAsciiFont:(NSFont*)nonAsciiFont horizontalSpacing:(float)horizontalSpacing - verticalSpacing:(float)verticalSpacing -{ + verticalSpacing:(float)verticalSpacing { DLog(@"setFont:%@ nonAsciiFont:%@", font, nonAsciiFont); - NSWindow *window = [[[self tab] realParentWindow] window]; + NSWindow *window = [[_delegate realParentWindow] window]; DLog(@"Before:\n%@", [window.contentView iterm_recursiveDescription]); DLog(@"Window frame: %@", window); if ([_textview.font isEqualTo:font] && @@ -3613,7 +3597,7 @@ - (void)setFont:(NSFont*)font // There's an unfortunate problem that this is a band-aid over. // If you change some attribute of a profile that causes sessions to reload their profiles // with the kReloadAllProfiles notification, then each profile will call this in turn, - // and it may be a no-op for all of them. If each calls -[PseudoTerminal fitWindowToTab:[self tab]] + // and it may be a no-op for all of them. If each calls -[PseudoTerminal fitWindowToTab:_delegate] // and different tabs come up with slightly different ideal sizes (e.g., because they // have different split pane layouts) then the window may shrink by a few pixels for each // session. @@ -3625,18 +3609,7 @@ - (void)setFont:(NSFont*)font horizontalSpacing:horizontalSpacing verticalSpacing:verticalSpacing]; DLog(@"Line height is now %f", (float)[_textview lineHeight]); - if (![[[self tab] parentWindow] anyFullScreen]) { - if ([iTermPreferences boolForKey:kPreferenceKeyAdjustWindowForFontSizeChange]) { - [[[self tab] parentWindow] fitWindowToTab:[self tab]]; - } - } - // If the window isn't able to adjust, or adjust enough, make the session - // work with whatever size we ended up having. - if ([self isTmuxClient]) { - [_tmuxController windowDidResize:[[self tab] realParentWindow]]; - } else { - [[self tab] fitSessionToCurrentViewSize:self]; - } + [_delegate sessionDidChangeFontSize:self]; DLog(@"After:\n%@", [window.contentView iterm_recursiveDescription]); DLog(@"Window frame: %@", window); } @@ -3656,7 +3629,7 @@ - (void)profileSessionNameDidEndEditing:(NSNotification *)notification { [self isTmuxClient] && [theGuid isEqualToString:_profile[KEY_GUID]]) { Profile *profile = [[ProfileModel sessionsInstance] bookmarkWithGuid:theGuid]; - [_tmuxController renameWindowWithId:self.tab.tmuxWindow + [_tmuxController renameWindowWithId:_delegate.tmuxWindow inSession:nil toName:profile[KEY_NAME]]; _tmuxTitleOutOfSync = NO; @@ -3690,10 +3663,10 @@ - (void)notifyTmuxFontChange @(_textview.horizontalSpacing), @(_textview.verticalSpacing) ]]; fontChangeNotificationInProgress = NO; - [PTYTab setTmuxFont:_textview.font - nonAsciiFont:_textview.nonAsciiFontEvenIfNotUsed - hSpacing:_textview.horizontalSpacing - vSpacing:_textview.verticalSpacing]; + [_delegate setTmuxFont:_textview.font + nonAsciiFont:_textview.nonAsciiFontEvenIfNotUsed + hSpacing:_textview.horizontalSpacing + vSpacing:_textview.verticalSpacing]; [[NSNotificationCenter defaultCenter] postNotificationName:kPTYSessionTmuxFontDidChange object:nil]; } @@ -3895,9 +3868,8 @@ - (NSString*)selectedText return [_textview selectedText]; } -- (BOOL)canSearch -{ - return _textview != nil && _tab && [_tab realParentWindow]; +- (BOOL)canSearch { + return _textview != nil && _delegate && [_delegate realParentWindow]; } - (void)findString:(NSString *)aString @@ -3922,7 +3894,7 @@ - (void)copySelection { } - (void)takeFocus { - [[[[self tab] realParentWindow] window] makeFirstResponder:_textview]; + [[[_delegate realParentWindow] window] makeFirstResponder:_textview]; } - (void)findViewControllerMakeDocumentFirstResponder { @@ -4075,7 +4047,7 @@ - (BOOL)wantsContentChangedNotification - (void)hideSession { [[MovePaneController sharedInstance] moveSessionToNewWindow:self atPoint:[[_view window] pointToScreenCoords:NSMakePoint(0, 0)]]; - [[[_tab realParentWindow] window] miniaturize:self]; + [[[_delegate realParentWindow] window] miniaturize:self]; } - (NSString *)preferredTmuxClientName { @@ -4098,7 +4070,7 @@ - (void)startTmuxMode clientName:[self preferredTmuxClientName]]; _tmuxController.ambiguousIsDoubleWidth = _treatAmbiguousWidthAsDoubleWidth; NSSize theSize; - Profile *tmuxBookmark = [PTYTab tmuxBookmark]; + Profile *tmuxBookmark = [_delegate tmuxBookmark]; theSize.width = MAX(1, [[tmuxBookmark objectForKey:KEY_COLUMNS] intValue]); theSize.height = MAX(1, [[tmuxBookmark objectForKey:KEY_ROWS] intValue]); [_tmuxController validateOptions]; @@ -4323,12 +4295,8 @@ - (void)tmuxWindowClosedWithId:(int)windowId [_tmuxController windowsChanged]; } -- (void)tmuxWindowRenamedWithId:(int)windowId to:(NSString *)newName -{ - PTYTab *tab = [_tmuxController window:windowId]; - if (tab) { - [tab setTmuxWindowName:newName]; - } +- (void)tmuxWindowRenamedWithId:(int)windowId to:(NSString *)newName { + [_delegate sessionWithTmuxGateway:self wasNotifiedWindowWithId:windowId renamedTo:newName]; [_tmuxController windowWasRenamedWithId:windowId to:newName]; } @@ -4339,7 +4307,7 @@ - (void)tmuxPrintLine:(NSString *)line } - (NSWindowController *)tmuxGatewayWindow { - return self.tab.realParentWindow; + return _delegate.realParentWindow; } - (void)tmuxHostDisconnected @@ -4361,8 +4329,8 @@ - (void)tmuxHostDisconnected self.tmuxMode = TMUX_NONE; if ([iTermPreferences boolForKey:kPreferenceKeyAutoHideTmuxClientSession] && - [[[_tab realParentWindow] window] isMiniaturized]) { - [[[_tab realParentWindow] window] deminiaturize:self]; + [[[_delegate realParentWindow] window] isMiniaturized]) { + [[[_delegate realParentWindow] window] deminiaturize:self]; } } @@ -4437,13 +4405,13 @@ - (void)tmuxSession:(int)sessionId renamed:(NSString *)newName - (NSSize)tmuxBookmarkSize { - NSDictionary *dict = [PTYTab tmuxBookmark]; + NSDictionary *dict = [_delegate tmuxBookmark]; return NSMakeSize([[dict objectForKey:KEY_COLUMNS] intValue], [[dict objectForKey:KEY_ROWS] intValue]); } - (NSInteger)tmuxNumHistoryLinesInBookmark { - NSDictionary *dict = [PTYTab tmuxBookmark]; + NSDictionary *dict = [_delegate tmuxBookmark]; if ([[dict objectForKey:KEY_UNLIMITED_SCROLLBACK] boolValue]) { // 10M is close enough to infinity to be indistinguishable. return 10 * 1000 * 1000; @@ -4542,15 +4510,15 @@ - (void)keyDown:(NSEvent *)event { // Escape exits zoom (pops out one level, since you can zoom repeatedly) // The zoomOut: IBAction doesn't get performed by shortcut, I guess because Esc is not a // valid shortcut. So we do it here. - [[[self tab] realParentWindow] replaceSyntheticActiveSessionWithLiveSessionIfNeeded]; - } else if ([[[self tab] realParentWindow] inInstantReplay]) { + [[_delegate realParentWindow] replaceSyntheticActiveSessionWithLiveSessionIfNeeded]; + } else if ([[_delegate realParentWindow] inInstantReplay]) { DLog(@"PTYSession keyDown in IR"); // Special key handling in IR mode, and keys never get sent to the live // session, even though it might be displayed. if (unicode == 27) { // Escape exits IR - [[[self tab] realParentWindow] closeInstantReplay:self]; + [[_delegate realParentWindow] closeInstantReplay:self]; return; } else if (unmodunicode == NSLeftArrowFunctionKey) { // Left arrow moves to prev frame @@ -4559,7 +4527,7 @@ - (void)keyDown:(NSEvent *)event { n = 15; } for (int i = 0; i < n; i++) { - [[[self tab] realParentWindow] irPrev:self]; + [[_delegate realParentWindow] irPrev:self]; } } else if (unmodunicode == NSRightArrowFunctionKey) { // Right arrow moves to next frame @@ -4568,7 +4536,7 @@ - (void)keyDown:(NSEvent *)event { n = 15; } for (int i = 0; i < n; i++) { - [[[self tab] realParentWindow] irNext:self]; + [[_delegate realParentWindow] irNext:self]; } } else { NSBeep(); @@ -4603,7 +4571,7 @@ - (void)keyDown:(NSEvent *)event { int tempKeyCode = unmodunicode; if (tempMods == (NSCommandKeyMask | NSAlternateKeyMask) && (tempKeyCode == 0xf702 || tempKeyCode == 0xf703) && - [[[self tab] sessions] count] > 1) { + [[_delegate sessions] count] > 1) { if ([self _askAboutOutdatedKeyMappings]) { int result = NSRunAlertPanel(@"Outdated Key Mapping Found", @"It looks like you're trying to switch split panes but you have a key mapping from an old iTerm installation for ⌘⌥← or ⌘⌥→ that switches tabs instead. What would you like to do?", @@ -4634,33 +4602,33 @@ - (void)keyDown:(NSEvent *)event { switch (keyBindingAction) { case KEY_ACTION_MOVE_TAB_LEFT: - [[[self tab] realParentWindow] moveTabLeft:nil]; + [[_delegate realParentWindow] moveTabLeft:nil]; break; case KEY_ACTION_MOVE_TAB_RIGHT: - [[[self tab] realParentWindow] moveTabRight:nil]; + [[_delegate realParentWindow] moveTabRight:nil]; break; case KEY_ACTION_NEXT_MRU_TAB: - [[[[self tab] parentWindow] tabView] cycleKeyDownWithModifiers:[event modifierFlags] - forwards:YES]; + [[[_delegate parentWindow] tabView] cycleKeyDownWithModifiers:[event modifierFlags] + forwards:YES]; break; case KEY_ACTION_PREVIOUS_MRU_TAB: - [[[[self tab] parentWindow] tabView] cycleKeyDownWithModifiers:[event modifierFlags] - forwards:NO]; + [[[_delegate parentWindow] tabView] cycleKeyDownWithModifiers:[event modifierFlags] + forwards:NO]; break; case KEY_ACTION_NEXT_PANE: - [[self tab] nextSession]; + [_delegate nextSession]; break; case KEY_ACTION_PREVIOUS_PANE: - [[self tab] previousSession]; + [_delegate previousSession]; break; case KEY_ACTION_NEXT_SESSION: - [[[self tab] parentWindow] nextTab:nil]; + [[_delegate parentWindow] nextTab:nil]; break; case KEY_ACTION_NEXT_WINDOW: [[iTermController sharedInstance] nextTerminal]; break; case KEY_ACTION_PREVIOUS_SESSION: - [[[self tab] parentWindow] previousTab:nil]; + [[_delegate parentWindow] previousTab:nil]; break; case KEY_ACTION_PREVIOUS_WINDOW: [[iTermController sharedInstance] previousTerminal]; @@ -4768,16 +4736,16 @@ - (void)keyDown:(NSEvent *)event { [[[iTermController sharedInstance] currentTerminal] toggleFullScreenMode:nil]; break; case KEY_ACTION_NEW_WINDOW_WITH_PROFILE: - [[[self tab] realParentWindow] newWindowWithBookmarkGuid:keyBindingText]; + [[_delegate realParentWindow] newWindowWithBookmarkGuid:keyBindingText]; break; case KEY_ACTION_NEW_TAB_WITH_PROFILE: - [[[self tab] realParentWindow] newTabWithBookmarkGuid:keyBindingText]; + [[_delegate realParentWindow] newTabWithBookmarkGuid:keyBindingText]; break; case KEY_ACTION_SPLIT_HORIZONTALLY_WITH_PROFILE: - [[[self tab] realParentWindow] splitVertically:NO withBookmarkGuid:keyBindingText]; + [[_delegate realParentWindow] splitVertically:NO withBookmarkGuid:keyBindingText]; break; case KEY_ACTION_SPLIT_VERTICALLY_WITH_PROFILE: - [[[self tab] realParentWindow] splitVertically:YES withBookmarkGuid:keyBindingText]; + [[_delegate realParentWindow] splitVertically:YES withBookmarkGuid:keyBindingText]; break; case KEY_ACTION_SET_PROFILE: { Profile *newProfile = [[ProfileModel sharedInstance] bookmarkWithGuid:keyBindingText]; @@ -4799,7 +4767,7 @@ - (void)keyDown:(NSEvent *)event { inProfile:profile model:model]; if (!ok) { - NSLog(@"Color preset %@ not found", keyBindingText); + ELog(@"Color preset %@ not found", keyBindingText); NSBeep(); } break; @@ -4876,7 +4844,7 @@ - (void)keyDown:(NSEvent *)event { break; default: - NSLog(@"Unknown key action %d", keyBindingAction); + ELog(@"Unknown key action %d", keyBindingAction); break; } } else { @@ -5174,9 +5142,9 @@ - (BOOL)applicationKeypadAllowed - (void)menuForEvent:(NSEvent *)theEvent menu:(NSMenu *)theMenu { // Ask the parent if it has anything to add - if ([[self tab] realParentWindow] && - [[[self tab] realParentWindow] respondsToSelector:@selector(menuForEvent:menu:)]) { - [[[self tab] realParentWindow] menuForEvent:theEvent menu:theMenu]; + if ([_delegate realParentWindow] && + [[_delegate realParentWindow] respondsToSelector:@selector(menuForEvent:menu:)]) { + [[_delegate realParentWindow] menuForEvent:theEvent menu:theMenu]; } } @@ -5216,7 +5184,7 @@ - (void)paste:(id)sender { // Show advanced paste window. - (IBAction)pasteOptions:(id)sender { - [_pasteHelper showPasteOptionsInWindow:self.tab.realParentWindow.window + [_pasteHelper showPasteOptionsInWindow:_delegate.realParentWindow.window bracketingEnabled:_terminal.bracketedPasteMode]; } @@ -5321,7 +5289,7 @@ - (void)textViewPostTabContentsChangedNotification - (void)textViewInvalidateRestorableState { if ([iTermAdvancedSettingsModel restoreWindowContents]) { - [self.tab.realParentWindow invalidateRestorableState]; + [_delegate.realParentWindow invalidateRestorableState]; } } @@ -5372,7 +5340,7 @@ - (BOOL)textViewShouldPlaceCursorAt:(VT100GridCoord)coord verticalOk:(BOOL *)ver - (BOOL)textViewShouldDrawFilledInCursor { // If the auto-command history popup is open for this session, the filled-in cursor should be // drawn even though the textview isn't in the key window. - return [self textViewIsActiveSession] && [[[self tab] realParentWindow] autoCommandHistoryIsOpenForSession:self]; + return [self textViewIsActiveSession] && [[_delegate realParentWindow] autoCommandHistoryIsOpenForSession:self]; } - (void)textViewWillNeedUpdateForBlink @@ -5386,19 +5354,19 @@ - (void)textViewSplitVertically:(BOOL)vertically withProfileGuid:(NSString *)gui if (guid) { profile = [[ProfileModel sharedInstance] bookmarkWithGuid:guid]; } - [[[self tab] realParentWindow] splitVertically:vertically - withBookmark:profile - targetSession:self]; + [[_delegate realParentWindow] splitVertically:vertically + withBookmark:profile + targetSession:self]; } - (void)textViewSelectNextTab { - [[[self tab] realParentWindow] nextTab:nil]; + [[_delegate realParentWindow] nextTab:nil]; } - (void)textViewSelectPreviousTab { - [[[self tab] realParentWindow] previousTab:nil]; + [[_delegate realParentWindow] previousTab:nil]; } - (void)textViewSelectNextWindow { @@ -5411,29 +5379,29 @@ - (void)textViewSelectPreviousWindow { - (void)textViewSelectNextPane { - [[self tab] nextSession]; + [_delegate nextSession]; } - (void)textViewSelectPreviousPane { - [[self tab] previousSession]; + [_delegate previousSession]; } - (void)textViewEditSession { - [[[self tab] realParentWindow] editSession:self makeKey:YES]; + [[_delegate realParentWindow] editSession:self makeKey:YES]; } - (void)textViewToggleBroadcastingInput { - [[[self tab] realParentWindow] toggleBroadcastingInputToSession:self]; + [[_delegate realParentWindow] toggleBroadcastingInputToSession:self]; } - (void)textViewCloseWithConfirmation { - [[[self tab] realParentWindow] closeSessionWithConfirmation:self]; + [[_delegate realParentWindow] closeSessionWithConfirmation:self]; } - (void)textViewRestartWithConfirmation { - [[[self tab] realParentWindow] restartSessionWithConfirmation:self]; + [[_delegate realParentWindow] restartSessionWithConfirmation:self]; } - (NSString *)mostRecentlySelectedText { @@ -5472,7 +5440,7 @@ - (BOOL)textViewCanPasteFile } - (BOOL)textViewWindowUsesTransparency { - return [[[self tab] realParentWindow] useTransparency]; + return [[_delegate realParentWindow] useTransparency]; } - (BOOL)textViewAmbiguousWidthCharsAreDoubleWidth @@ -5482,52 +5450,41 @@ - (BOOL)textViewAmbiguousWidthCharsAreDoubleWidth - (void)textViewCreateWindowWithProfileGuid:(NSString *)guid { - [[[self tab] realParentWindow] newWindowWithBookmarkGuid:guid]; + [[_delegate realParentWindow] newWindowWithBookmarkGuid:guid]; } - (void)textViewCreateTabWithProfileGuid:(NSString *)guid { - [[[self tab] realParentWindow] newTabWithBookmarkGuid:guid]; + [[_delegate realParentWindow] newTabWithBookmarkGuid:guid]; } // Called when a key is pressed. - (BOOL)textViewDelegateHandlesAllKeystrokes { [self resumeOutputIfNeeded]; - return [[[self tab] realParentWindow] inInstantReplay]; + return [[_delegate realParentWindow] inInstantReplay]; } -- (BOOL)textViewInSameTabAsTextView:(PTYTextView *)other { - PTYTab *myTab = [self tab]; - for (PTYSession *session in [myTab sessions]) { - if ([session textview] == other) { - return YES; - } - } - return NO; -} - -- (BOOL)textViewIsActiveSession -{ - return [[self tab] activeSession] == self; +- (BOOL)textViewIsActiveSession { + return [_delegate sessionIsActiveInTab:self]; } - (BOOL)textViewSessionIsBroadcastingInput { - return [[[self tab] realParentWindow] broadcastInputToSession:self]; + return [[_delegate realParentWindow] broadcastInputToSession:self]; } - (BOOL)textViewIsMaximized { - return [[self tab] hasMaximizedPane]; + return [_delegate hasMaximizedPane]; } - (BOOL)textViewTabHasMaximizedPanel { - return [[self tab] hasMaximizedPane]; + return [_delegate hasMaximizedPane]; } - (void)textViewDidBecomeFirstResponder { - [[self tab] setActiveSession:self]; + [_delegate setActiveSession:self]; } - (BOOL)textViewReportMouseEvent:(NSEventType)eventType @@ -5866,14 +5823,14 @@ - (void)setDvrFrame { DVRFrameInfo info = [_dvrDecoder info]; if (info.width != [_screen width] || info.height != [_screen height]) { if (![_liveSession isTmuxClient]) { - [[[self tab] realParentWindow] sessionInitiatedResize:self - width:info.width - height:info.height]; + [[_delegate realParentWindow] sessionInitiatedResize:self + width:info.width + height:info.height]; } } [_screen setFromFrame:s len:len info:info]; - [[[self tab] realParentWindow] clearTransientTitle]; - [[[self tab] realParentWindow] setWindowTitle]; + [[_delegate realParentWindow] clearTransientTitle]; + [[_delegate realParentWindow] setWindowTitle]; } - (void)continueTailFind @@ -5926,7 +5883,7 @@ - (void)beginTailFind - (void)sessionContentsChanged:(NSNotification *)notification { if (!_tailFindTimer && [notification object] == self && - [[_tab realParentWindow] currentTab] == _tab) { + [_delegate sessionBelongsToVisibleTab]) { [self beginTailFind]; } } @@ -6065,11 +6022,11 @@ - (BOOL)screenShouldInitiateWindowResize { } - (void)screenResizeToWidth:(int)width height:(int)height { - [[self tab] sessionInitiatedResize:self width:width height:height]; + [_delegate sessionInitiatedResize:self width:width height:height]; } - (void)screenResizeToPixelWidth:(int)width height:(int)height { - [[[self tab] realParentWindow] setFrameSize:NSMakeSize(width, height)]; + [[_delegate realParentWindow] setFrameSize:NSMakeSize(width, height)]; } - (BOOL)screenShouldBeginPrinting { @@ -6097,18 +6054,18 @@ - (void)screenSetName:(NSString *)theName { } - (BOOL)screenWindowIsFullscreen { - return [[[self tab] parentWindow] anyFullScreen]; + return [[_delegate parentWindow] anyFullScreen]; } - (void)screenMoveWindowTopLeftPointTo:(NSPoint)point { NSRect screenFrame = [self screenWindowScreenFrame]; point.x += screenFrame.origin.x; point.y = screenFrame.origin.y + screenFrame.size.height - point.y; - [[[self tab] parentWindow] windowSetFrameTopLeftPoint:point]; + [[_delegate parentWindow] windowSetFrameTopLeftPoint:point]; } - (NSRect)screenWindowScreenFrame { - return [[[[self tab] parentWindow] windowScreen] visibleFrame]; + return [[[_delegate parentWindow] windowScreen] visibleFrame]; } - (NSPoint)screenWindowTopLeftPixelCoordinate { @@ -6121,23 +6078,23 @@ - (NSPoint)screenWindowTopLeftPixelCoordinate { // If flag is set, miniaturize; otherwise, deminiaturize. - (void)screenMiniaturizeWindow:(BOOL)flag { if (flag) { - [[[self tab] parentWindow] windowPerformMiniaturize:nil]; + [[_delegate parentWindow] windowPerformMiniaturize:nil]; } else { - [[[self tab] parentWindow] windowDeminiaturize:nil]; + [[_delegate parentWindow] windowDeminiaturize:nil]; } } // If flag is set, bring to front; if not, move to back. - (void)screenRaise:(BOOL)flag { if (flag) { - [[[self tab] parentWindow] windowOrderFront:nil]; + [[_delegate parentWindow] windowOrderFront:nil]; } else { - [[[self tab] parentWindow] windowOrderBack:nil]; + [[_delegate parentWindow] windowOrderBack:nil]; } } - (BOOL)screenWindowIsMiniaturized { - return [[[self tab] parentWindow] windowIsMiniaturized]; + return [[_delegate parentWindow] windowIsMiniaturized]; } - (void)screenWriteDataToTask:(NSData *)data { @@ -6145,11 +6102,11 @@ - (void)screenWriteDataToTask:(NSData *)data { } - (NSRect)screenWindowFrame { - return [[[self tab] parentWindow] windowFrame]; + return [[_delegate parentWindow] windowFrame]; } - (NSSize)screenSize { - return [[[[[self tab] parentWindow] currentSession] scrollview] documentVisibleRect].size; + return [[[[_delegate parentWindow] currentSession] scrollview] documentVisibleRect].size; } // If the flag is set, push the window title; otherwise push the icon title. @@ -6175,15 +6132,15 @@ - (NSString *)screenName { } - (int)screenNumber { - return [[self tab] realObjectCount]; + return [_delegate tabNumber]; } - (int)screenWindowIndex { - return [[iTermController sharedInstance] indexOfTerminal:(PseudoTerminal *)[[self tab] realParentWindow]]; + return [[iTermController sharedInstance] indexOfTerminal:(PseudoTerminal *)[_delegate realParentWindow]]; } - (int)screenTabIndex { - return [[self tab] number]; + return [_delegate number]; } - (int)screenViewIndex { @@ -6252,7 +6209,7 @@ - (void)screenFlashImage:(NSString *)identifier { } - (void)screenIncrementBadge { - [[_tab realParentWindow] incrementBadge]; + [[_delegate realParentWindow] incrementBadge]; } - (NSString *)screenCurrentWorkingDirectory { @@ -6290,18 +6247,18 @@ - (BOOL)screenHasView { } - (void)reveal { - NSWindowController *terminal = [[self tab] realParentWindow]; + NSWindowController *terminal = [_delegate realParentWindow]; iTermController *controller = [iTermController sharedInstance]; if ([terminal isHotKeyWindow]) { [[HotkeyWindowController sharedInstance] showHotKeyWindow]; } else { [controller setCurrentTerminal:(PseudoTerminal *)terminal]; [[terminal window] makeKeyAndOrderFront:self]; - [[terminal tabView] selectTabViewItemWithIdentifier:[self tab]]; + [_delegate sessionSelectContainingTab]; } [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; - [[self tab] setActiveSession:self]; + [_delegate setActiveSession:self]; } - (id)markAddedAtLine:(int)line ofClass:(Class)markClass { @@ -6323,7 +6280,7 @@ - (id)markAddedAtLine:(int)line ofClass:(Class)markClass { [[iTermGrowlDelegate sharedInstance] growlNotify:@"Mark Set" withDescription:[NSString stringWithFormat:@"Session %@ #%d had a mark set.", [self name], - [[self tab] realObjectCount]] + [_delegate tabNumber]] andNotification:@"Mark Set" windowIndex:[self screenWindowIndex] tabIndex:[self screenTabIndex] @@ -6420,7 +6377,7 @@ - (void)screenSetPasteboard:(NSString *)value { [self setPasteboard:NSGeneralPboard]; } } else { - NSLog(@"Clipboard access denied for CopyToClipboard"); + ELog(@"Clipboard access denied for CopyToClipboard"); } } @@ -6595,7 +6552,7 @@ - (void)screenSetColor:(NSColor *)color forKey:(int)key { - (void)screenSetCurrentTabColor:(NSColor *)color { [self setTabColor:color]; - id term = [_tab parentWindow]; + id term = [_delegate parentWindow]; [term updateTabColors]; } @@ -6630,7 +6587,7 @@ - (void)screenSetTabColorRedComponentTo:(CGFloat)color { green:[curColor greenComponent] blue:[curColor blueComponent] alpha:1]]; - [[_tab parentWindow] updateTabColors]; + [[_delegate parentWindow] updateTabColors]; } - (void)screenSetTabColorGreenComponentTo:(CGFloat)color { @@ -6639,7 +6596,7 @@ - (void)screenSetTabColorGreenComponentTo:(CGFloat)color { green:color blue:[curColor blueComponent] alpha:1]]; - [[_tab parentWindow] updateTabColors]; + [[_delegate parentWindow] updateTabColors]; } - (void)screenSetTabColorBlueComponentTo:(CGFloat)color { @@ -6648,7 +6605,7 @@ - (void)screenSetTabColorBlueComponentTo:(CGFloat)color { green:[curColor greenComponent] blue:color alpha:1]]; - [[_tab parentWindow] updateTabColors]; + [[_delegate parentWindow] updateTabColors]; } - (void)screenCurrentHostDidChange:(VT100RemoteHost *)host { @@ -6664,7 +6621,7 @@ - (void)screenCurrentHostDidChange:(VT100RemoteHost *)host { } [self dismissAnnouncementWithIdentifier:kShellIntegrationOutOfDateAnnouncementIdentifier]; - [[[self tab] realParentWindow] sessionHostDidChange:self to:host]; + [[_delegate realParentWindow] sessionHostDidChange:self to:host]; int line = [_screen numberOfScrollbackLines] + _screen.cursorY; NSString *path = [_screen workingDirectoryOnLine:line]; @@ -6806,16 +6763,16 @@ - (void)screenCommandDidChangeWithRange:(VT100GridCoordRange)range { BOOL haveCommand = _commandRange.start.x >= 0 && [[self commandInRange:_commandRange] length] > 0; if (!haveCommand && hadCommand) { DLog(@"Hide because don't have a command, but just had one"); - [[[self tab] realParentWindow] hideAutoCommandHistoryForSession:self]; + [[_delegate realParentWindow] hideAutoCommandHistoryForSession:self]; } else { if (!hadCommand && range.start.x >= 0) { DLog(@"Show because I have a range but didn't have a command"); - [[[self tab] realParentWindow] showAutoCommandHistoryForSession:self]; + [[_delegate realParentWindow] showAutoCommandHistoryForSession:self]; } NSString *command = haveCommand ? [self commandInRange:_commandRange] : @""; DLog(@"Update command to %@, have=%d, range.start.x=%d", command, (int)haveCommand, range.start.x); if (haveCommand) { - [[[self tab] realParentWindow] updateAutoCommandHistoryForPrefix:command + [[_delegate realParentWindow] updateAutoCommandHistoryForPrefix:command inSession:self]; } } @@ -6845,7 +6802,7 @@ - (void)screenCommandDidEndWithRange:(VT100GridCoordRange)range { [self updateVariables]; _commandRange = VT100GridCoordRangeMake(-1, -1, -1, -1); DLog(@"Hide ACH because command ended"); - [[[self tab] realParentWindow] hideAutoCommandHistoryForSession:self]; + [[_delegate realParentWindow] hideAutoCommandHistoryForSession:self]; } - (BOOL)screenShouldPlacePromptAtFirstColumn { @@ -7150,15 +7107,15 @@ - (void)popupIsSearching:(BOOL)searching { } - (void)popupWillClose:(iTermPopupWindowController *)popup { - [[[self tab] realParentWindow] popupWillClose:popup]; + [[_delegate realParentWindow] popupWillClose:popup]; } - (NSWindowController *)popupWindowController { - return [[self tab] realParentWindow]; + return [_delegate realParentWindow]; } - (BOOL)popupWindowIsInHotkeyWindow { - return self.tab.realParentWindow.isHotKeyWindow; + return _delegate.realParentWindow.isHotKeyWindow; } - (VT100Screen *)popupVT100Screen { @@ -7176,11 +7133,11 @@ - (void)popupInsertText:(NSString *)string { - (BOOL)popupHandleSelector:(SEL)selector string:(NSString *)string currentValue:(NSString *)currentValue { - if (![[[self tab] realParentWindow] autoCommandHistoryIsOpenForSession:self]) { + if (![[_delegate realParentWindow] autoCommandHistoryIsOpenForSession:self]) { return NO; } if (selector == @selector(cancel:)) { - [[[self tab] realParentWindow] hideAutoCommandHistoryForSession:self]; + [[_delegate realParentWindow] hideAutoCommandHistoryForSession:self]; return YES; } if (selector == @selector(insertNewline:)) { diff --git a/sources/PTYTab.h b/sources/PTYTab.h index f4069f3db4..d9dc7a5107 100644 --- a/sources/PTYTab.h +++ b/sources/PTYTab.h @@ -2,11 +2,12 @@ // owner of PTYSession. #import -#import "WindowControllerInterface.h" +#import "FutureMethods.h" #import "PSMTabBarControl.h" +#import "PTYSession.h" #import "PTYSplitView.h" -#import "FutureMethods.h" #import "PTYTabDelegate.h" +#import "WindowControllerInterface.h" @class PTYSession; @class PTYTab; @@ -20,6 +21,7 @@ @interface PTYTab : NSObject < NSCopying, NSSplitViewDelegate, + PTYSessionDelegate, PTYSplitViewDelegate, PSMTabBarControlRepresentedObjectIdentifierProtocol> @@ -30,7 +32,6 @@ @property(nonatomic, readonly) BOOL isMaximized; // Sessions ordered in a similar-to-reading-order fashion. @property(nonatomic, readonly) NSArray *orderedSessions; -@property(nonatomic, readonly) int tabNumberForItermSessionId; @property(nonatomic, assign) id delegate; @property(nonatomic, retain) PTYSession *activeSession; @@ -86,22 +87,15 @@ - (void)setRoot:(NSSplitView *)newRoot; - (NSRect)absoluteFrame; -- (void)previousSession; -- (void)nextSession; - (int)indexOfSessionView:(SessionView*)sessionView; - (void)setLockedSession:(PTYSession*)lockedSession; -- (id)parentWindow; -- (NSWindowController *)realParentWindow; - (void)setParentWindow:(NSWindowController *)theParent; - (void)setFakeParentWindow:(FakeWindow*)theParent; - (FakeWindow*)fakeWindow; -- (void)setBell:(BOOL)flag; -- (void)nameOfSession:(PTYSession*)session didChangeTo:(NSString*)newName; - (BOOL)isForegroundTab; -- (void)sessionInitiatedResize:(PTYSession*)session width:(int)width height:(int)height; - (NSSize)sessionSizeForViewSize:(PTYSession *)aSession; - (BOOL)fitSessionToCurrentViewSize:(PTYSession*)aSession; // Fit session views to scroll views. @@ -111,22 +105,14 @@ // tight fit. This should be followed by fitting the window to tabs. - (void)recompact; -// Tab index. -- (int)number; - - (PTYSession *)sessionWithViewId:(int)viewId; -- (int)realObjectCount; // Should show busy indicator in tab? - (BOOL)isProcessing; - (BOOL)realIsProcessing; - (void)setIsProcessing:(BOOL)aFlag; - (BOOL)isActiveSession; -// Returns true if another update may be needed later (so the timer should be scheduled). -- (BOOL)updateLabelAttributes; -- (void)closeSession:(PTYSession*)session; - (void)terminateAllSessions; -- (NSArray*)sessions; - (NSArray *)windowPanes; - (NSArray*)sessionViews; - (BOOL)allSessionsExited; @@ -147,14 +133,10 @@ - (NSImage*)image:(BOOL)withSpaceForFrame; - (bool)blur; - (double)blurRadius; -- (void)recheckBlur; - (NSSize)_minSessionSize:(SessionView*)sessionView; - (NSSize)_sessionSize:(SessionView*)sessionView; -// Remove a dead session. This should be called from [session terminate] only. -- (void)removeSession:(PTYSession*)aSession; - // If the active session's parent splitview has: // only one child: make its orientation vertical and add a new subview. // more than one child and a vertical orientation: add a new subview and return it. @@ -172,9 +154,7 @@ - (NSDictionary*)arrangement; - (void)notifyWindowChanged; -- (BOOL)hasMaximizedPane; - (void)maximize; -- (void)unmaximize; // Does any session in this tab require prompt on close? - (BOOL)promptOnClose; @@ -188,7 +168,6 @@ - (void)reloadTmuxLayout; // Size we are given the current layout -- (int)tmuxWindow; - (void)setTmuxLayout:(NSMutableDictionary *)parseTree tmuxController:(TmuxController *)tmuxController zoomed:(NSNumber *)zoomed; @@ -208,8 +187,6 @@ - (NSDictionary*)arrangementWithContents:(BOOL)contents; -- (void)addHiddenLiveView:(SessionView *)hiddenLiveView; - // Update the tab's title from the active session's name. Needed for initialzing the tab's title // after setting up tmux tabs. - (void)loadTitleFromSession; diff --git a/sources/PTYTab.m b/sources/PTYTab.m index a2fb723738..b9439975ee 100644 --- a/sources/PTYTab.m +++ b/sources/PTYTab.m @@ -34,10 +34,19 @@ static void SetAgainstGrainDim(BOOL isVertical, NSSize* dest, CGFloat value); // States -static const NSUInteger kPTYTabBellState = (1 << 0); -static const NSUInteger kPTYTabIdleState = (1 << 1); -static const NSUInteger kPTYTabNewOutputState = (1 << 2); -static const NSUInteger kPTYTabDeadState = (1 << 3); +typedef NS_OPTIONS(NSUInteger, PTYTabState) { + // Bell has rung. + kPTYTabBellState = (1 << 0), + + // Background tab is idle; it's been a while since new output arrived. + kPTYTabIdleState = (1 << 1), + + // Background tab just got new output. + kPTYTabNewOutputState = (1 << 2), + + // A session has ended. + kPTYTabDeadState = (1 << 3) +}; @implementation PTYTab { int _activityCounter; @@ -246,7 +255,7 @@ - (instancetype)initWithSession:(PTYSession*)session { [session setActivityCounter:@(_activityCounter++)]; [[session view] setDimmed:NO]; [self setRoot:[[[PTYSplitView alloc] init] autorelease]]; - PTYTab *oldTab = [session tab]; + PTYTab *oldTab = (PTYTab *)[session delegate]; if (oldTab && [oldTab tmuxWindow] >= 0) { tmuxWindow_ = [oldTab tmuxWindow]; tmuxController_ = [[oldTab tmuxController] retain]; @@ -255,7 +264,7 @@ - (instancetype)initWithSession:(PTYSession*)session { } else { tmuxWindow_ = -1; } - [session setTab:self]; + session.delegate = self; [root_ addSubview:[session view]]; } [[NSNotificationCenter defaultCenter] addObserver:self @@ -321,7 +330,7 @@ - (void)dealloc { PtyLog(@"PTYTab dealloc"); [[NSNotificationCenter defaultCenter] removeObserver:self]; for (PTYSession* aSession in [self sessions]) { - [aSession setTab:nil]; + aSession.delegate = nil; } [root_ release]; @@ -330,7 +339,7 @@ - (void)dealloc { PTYSession* aSession = [aView session]; [aSession cancelTimers]; - [aSession setTab:nil]; + aSession.delegate = nil; } root_ = nil; @@ -410,16 +419,36 @@ - (void)nameOfSession:(PTYSession*)session didChangeTo:(NSString*)newName { } } -- (BOOL)isForegroundTab -{ +- (BOOL)isForegroundTab { return [[tabViewItem_ tabView] selectedTabViewItem] == tabViewItem_; } -- (void)sessionInitiatedResize:(PTYSession*)session width:(int)width height:(int)height -{ +- (void)sessionWithTmuxGateway:(PTYSession *)session + wasNotifiedWindowWithId:(int)windowId + renamedTo:(NSString *)newName { + PTYTab *tab = [session.tmuxController window:windowId]; + if (tab) { + [tab setTmuxWindowName:newName]; + } +} + +- (void)sessionSelectContainingTab { + [[self.realParentWindow tabView] selectTabViewItemWithIdentifier:self]; +} + +- (void)sessionInitiatedResize:(PTYSession*)session width:(int)width height:(int)height { [parentWindow_ sessionInitiatedResize:session width:width height:height]; } +- (void)addSession:(PTYSession *)session toRestorableSession:(iTermRestorableSession *)restorableSession { + NSArray *sessions = restorableSession.sessions ?: @[]; + restorableSession.sessions = [sessions arrayByAddingObject:session]; + restorableSession.terminalGuid = self.realParentWindow.terminalGuid; + restorableSession.tabUniqueId = self.uniqueId; + restorableSession.arrangement = self.arrangement; + restorableSession.group = kiTermRestorableSessionGroupSession; +} + - (PTYSession*)activeSession { return activeSession_; @@ -686,13 +715,11 @@ - (void)setTabViewItem:(NSTabViewItem *)theTabViewItem } } -- (int)number -{ +- (int)number { return [[tabViewItem_ tabView] indexOfTabViewItem:tabViewItem_]; } -- (int)realObjectCount -{ +- (int)tabNumber { return objectCount_; } @@ -737,6 +764,15 @@ - (BOOL)isActiveSession return ([[[self tabViewItem] tabView] selectedTabViewItem] == [self tabViewItem]); } +- (BOOL)anySessionIsProcessing { + for (PTYSession *session in self.sessions) { + if (session.isProcessing) { + return YES; + } + } + return NO; +} + - (BOOL)anySessionHasNewOutput:(BOOL *)okToNotify { *okToNotify = NO; @@ -892,9 +928,8 @@ - (PTYSession*)sessionBelow:(PTYSession*)session return [self _sessionAdjacentTo:session verticalDir:YES after:YES]; } -- (BOOL)updateLabelAttributes -{ - PtyLog(@"PTYTab updateLabelAttributes"); +- (BOOL)updateLabelAttributes { + DLog(@"PTYTab updateLabelAttributes for tab %d", objectCount_); struct timeval now; BOOL needsFollowUp = NO; @@ -903,13 +938,16 @@ - (BOOL)updateLabelAttributes // Session has terminated. [self setLabelAttributesForDeadSession]; } else { - if (!self.activeSession.isProcessing) { + if (![self anySessionIsProcessing]) { + DLog(@"No session is processing"); // Too much time has passed since activity occurred and we're idle. [self setLabelAttributesForIdleTabAtTime:now]; } else { + DLog(@"Some session is processing"); // Less than 2 seconds has passed since the last output in the session. BOOL okToNotify; if ([self anySessionHasNewOutput:&okToNotify]) { + DLog(@"Some session has new output"); [self setLabelAttributesForActiveTab:okToNotify]; } needsFollowUp = YES; @@ -920,8 +958,7 @@ - (BOOL)updateLabelAttributes return needsFollowUp; } -- (void)closeSession:(PTYSession*)session -{ +- (void)closeSession:(PTYSession*)session { [[self parentWindow] closeSession:session]; } @@ -1300,8 +1337,26 @@ - (void)fitSubviewsToRoot } } -- (void)removeSession:(PTYSession*)aSession -{ +- (BOOL)sessionBelongsToVisibleTab { + return [self isForegroundTab]; +} + +- (void)sessionDidChangeFontSize:(PTYSession *)session { + if (![[self parentWindow] anyFullScreen]) { + if ([iTermPreferences boolForKey:kPreferenceKeyAdjustWindowForFontSizeChange]) { + [[self parentWindow] fitWindowToTab:self]; + } + } + // If the window isn't able to adjust, or adjust enough, make the session + // work with whatever size we ended up having. + if ([session isTmuxClient]) { + [session.tmuxController windowDidResize:[self realParentWindow]]; + } else { + [self fitSessionToCurrentViewSize:session]; + } +} + +- (void)removeSession:(PTYSession*)aSession { if (idMap_) { [self unmaximize]; } @@ -1917,11 +1972,11 @@ - (NSSize)sessionSizeForViewSize:(PTYSession *)aSession int height = (size.height - VMARGIN*2) / [[aSession textview] lineHeight]; PtyLog(@"fitSessionToCurrentViewSize %@ gives %d rows", [NSValue valueWithSize:size], height); if (width <= 0) { - NSLog(@"WARNING: Session has %d width", width); + ELog(@"WARNING: Session has %d width", width); width = 1; } if (height <= 0) { - NSLog(@"WARNING: Session has %d height", height); + ELog(@"WARNING: Session has %d height", height); height = 1; } @@ -2035,8 +2090,7 @@ - (double)blurRadius } } -- (void)recheckBlur -{ +- (void)recheckBlur { PtyLog(@"PTYTab recheckBlur"); if ([realParentWindow_ currentTab] == self && ![[realParentWindow_ window] isMiniaturized]) { @@ -2244,7 +2298,7 @@ - (PTYSession*)_recursiveRestoreSessions:(NSDictionary*)arrangement PTYSession *session; if (uniqueId && [sessionView session]) { // TODO: Is it right to check if session exists here? session = [sessionView session]; - session.tab = self; + session.delegate = self; } else if (wp && [sessionView session]) { // Re-use existing session because the session view was recycled // from the existing view hierarchy when the tmux layout changed but @@ -2254,7 +2308,7 @@ - (PTYSession*)_recursiveRestoreSessions:(NSDictionary*)arrangement } else { session = [PTYSession sessionFromArrangement:[arrangement objectForKey:TAB_ARRANGEMENT_SESSION] inView:(SessionView*)view - inTab:theTab + withDelegate:theTab forObjectType:objectType]; } if ([[arrangement objectForKey:TAB_ARRANGEMENT_IS_ACTIVE] boolValue]) { @@ -2287,7 +2341,7 @@ - (NSArray *)splitters { - (void)replaceWithContentsOfTab:(PTYTab *)tabToGut { for (PTYSession *aSession in [tabToGut sessions]) { - aSession.tab = self; + aSession.delegate = self; } for (PTYSplitView *splitview in [self splitters]) { if (splitview != root_) { @@ -2464,12 +2518,10 @@ - (NSDictionary *)arrangementNodeWithContentsFromArrangementNode:(NSDictionary * result[TAB_ARRANGEMENT_SESSION] = [[sessionView session] arrangementWithContents:YES]; } else { - NSLog(@"Bogus value in idmap for key %@: %@", sessionId, sessionView); - DLog(@"Bogus value in idmap for key %@: %@", sessionId, sessionView); + ELog(@"Bogus value in idmap for key %@: %@", sessionId, sessionView); } } else { - NSLog(@"No session ID in arrangement node %@", node); - DLog(@"No session ID in arrangement node %@", node); + ELog(@"No session ID in arrangement node %@", node); } } return result; @@ -2704,18 +2756,15 @@ - (BOOL)isTmuxTab return tmuxController_ != nil; } -- (int)tmuxWindow -{ +- (int)tmuxWindow { return tmuxWindow_; } -- (NSString *)tmuxWindowName -{ +- (NSString *)tmuxWindowName { return tmuxWindowName_ ? tmuxWindowName_ : @"tmux"; } -- (void)setTmuxWindowName:(NSString *)tmuxWindowName -{ +- (void)setTmuxWindowName:(NSString *)tmuxWindowName { [tmuxWindowName_ autorelease]; tmuxWindowName_ = [tmuxWindowName copy]; [[self realParentWindow] setWindowTitle]; @@ -2738,11 +2787,14 @@ + (Profile *)tmuxBookmark return bookmark; } +- (Profile *)tmuxBookmark { + return [PTYTab tmuxBookmark]; +} + + (void)setTmuxFont:(NSFont *)font nonAsciiFont:(NSFont *)nonAsciiFont hSpacing:(double)hs - vSpacing:(double)vs -{ + vSpacing:(double)vs { [[ProfileModel sharedInstance] setObject:[ITAddressBookMgr descFromFont:font] forKey:KEY_NORMAL_FONT inBookmark:[PTYTab tmuxBookmark]]; @@ -2758,6 +2810,13 @@ + (void)setTmuxFont:(NSFont *)font [[ProfileModel sharedInstance] postChangeNotification]; } +- (void)setTmuxFont:(NSFont *)font + nonAsciiFont:(NSFont *)nonAsciiFont + hSpacing:(double)hs + vSpacing:(double)vs { + [PTYTab setTmuxFont:font nonAsciiFont:nonAsciiFont hSpacing:hs vSpacing:vs]; +} + + (void)setSizesInTmuxParseTree:(NSMutableDictionary *)parseTree inTerminal:(NSWindowController *)term { @@ -3311,8 +3370,7 @@ - (BOOL)layoutIsTooLarge root_.frame.size.height > flexibleView_.frame.size.height); } -- (BOOL)hasMaximizedPane -{ +- (BOOL)hasMaximizedPane { return isMaximized_; } @@ -3562,15 +3620,15 @@ - (BOOL)canMoveView:(NSView *)view } - (void)swapSession:(PTYSession *)session1 withSession:(PTYSession *)session2 { - assert(session1.tab == self); + assert(session1.delegate == self); if (isMaximized_) { [self unmaximize]; } - if (session2.tab.hasMaximizedPane) { - [session2.tab unmaximize]; + if (session2.delegate.hasMaximizedPane) { + [session2.delegate unmaximize]; } - if (session1.tab->lockedSession_ || session2.tab->lockedSession_) { + if (((PTYTab *)session1.delegate)->lockedSession_ || ((PTYTab *)session2.delegate)->lockedSession_) { return; } if ([session1 isTmuxClient] || @@ -3584,8 +3642,8 @@ - (void)swapSession:(PTYSession *)session1 withSession:(PTYSession *)session2 { session1.view, session1.view.superview, session2.view, session2.view.superview); - PTYTab *session1Tab = session1.tab; - PTYTab *session2Tab = session2.tab; + PTYTab *session1Tab = (PTYTab *)session1.delegate; + PTYTab *session2Tab = (PTYTab *)session2.delegate; PTYSplitView *session1Superview = (PTYSplitView *)session1.view.superview; NSUInteger session1Index = [[session1Superview subviews] indexOfObject:session1.view]; @@ -3608,8 +3666,8 @@ - (void)swapSession:(PTYSession *)session1 withSession:(PTYSession *)session2 { session1Superview.delegate = session1Tab; session2Superview.delegate = session2Tab; - session1.tab = session2Tab; - session2.tab = session1Tab; + session1.delegate = session2Tab; + session2.delegate = session1Tab; [session1Tab setActiveSession:session2]; [session2Tab setActiveSession:session1]; @@ -3788,7 +3846,7 @@ - (void)_resizeSubviewsOfSplitViewWithLockedGrandchild:(NSSplitView *)splitView if (overflow > 0) { // We can't maintain the locked size without making some other subview smaller than // its allowed min. Ignore the lockedness of the session. - NSLog(@"Warning: locked session doesn't leave enough space for other views. overflow=%lf", overflow); + ELog(@"Warning: locked session doesn't leave enough space for other views. overflow=%lf", overflow); [splitView adjustSubviews]; [self _splitViewDidResizeSubviews:splitView]; } else { @@ -4045,8 +4103,7 @@ - (void)_redistributeQuantizationError:(const double)targetSize } } if (!anyChange) { - PtyLog(@"Failed to redistribute quantization error. Change=%d, sizes=%@.", change, sizes); - NSLog(@"Failed to redistribute quantization error. Change=%d, sizes=%@.", change, sizes); + ELog(@"Failed to redistribute quantization error. Change=%d, sizes=%@.", change, sizes); return; } } @@ -4065,7 +4122,7 @@ - (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)old return; } if ([splitView frame].size.width == 0) { - NSLog(@"Warning: splitView:resizeSubviewsWithOldSize: resized to 0 width"); + ELog(@"Warning: splitView:resizeSubviewsWithOldSize: resized to 0 width"); return; } PtyLog(@"splitView:resizeSubviewsWithOldSize for %p", splitView); @@ -4194,8 +4251,7 @@ - (void)splitView:(NSSplitView *)splitView resizeSubviewsWithOldSize:(NSSize)old if (fabs(currentSumOfSizes - targetSize) > [[splitView subviews] count]) { // I'm not sure this will ever happen, but just in case quantization prevents us // from converging give up and ignore constraints. - NSLog(@"No changes! Ignoring constraints!"); - PtyLog(@"splitView:resizeSubviewsWithOldSize - No changes! Ignoring constraints!"); + ELog(@"No changes! Ignoring constraints!"); ignoreConstraints = YES; } else { PtyLog(@"splitView:resizeSubviewsWithOldSize - redistribute quantization error"); @@ -4318,7 +4374,7 @@ - (void)splitViewDidResizeSubviews:(NSNotification *)aNotification return; } if ([root_ frame].size.width == 0) { - NSLog(@"Warning: splitViewDidResizeSubviews: resized to 0 width"); + ELog(@"Warning: splitViewDidResizeSubviews: resized to 0 width"); return; } PtyLog(@"splitViewDidResizeSubviews notification received. new height is %lf", [root_ frame].size.height); @@ -4396,6 +4452,10 @@ - (CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)pr return originalPosition + allowedDiff; } +- (BOOL)sessionIsActiveInTab:(PTYSession *)session { + return [self activeSession] == session; +} + #pragma mark - Private - (void)setLabelAttributesForDeadSession { @@ -4413,43 +4473,55 @@ - (BOOL)_windowResizedRecently return elapsed < (2 * kBackgroundSessionIntervalSec); } -- (void)setLabelAttributesForIdleTabAtTime:(struct timeval)now -{ +- (void)setLabelAttributesForIdleTabAtTime:(struct timeval)now { BOOL isBackgroundTab = [[tabViewItem_ tabView] selectedTabViewItem] != [self tabViewItem]; if ([self isProcessing]) { [self setIsProcessing:NO]; // This triggers KVO in PSMTabBarCell } + BOOL allSessionsWithNewOutputAreIdle = YES; + BOOL anySessionHasNewOutput = NO; for (PTYSession* session in [self sessions]) { if ([session newOutput]) { - // Idle after new output - if (!session.havePostedIdleNotification && - [session shouldPostGrowlNotification] && - [[NSDate date] timeIntervalSinceDate:[SessionView lastResizeDate]] > POST_WINDOW_RESIZE_SILENCE_SEC && - session.isIdle) { - NSString *theDescription = - [NSString stringWithFormat:@"Session %@ in tab #%d became idle.", - [[self activeSession] name], - [self realObjectCount]]; - if ([iTermProfilePreferences boolForKey:KEY_SEND_IDLE_ALERT inProfile:session.profile]) { - [[iTermGrowlDelegate sharedInstance] growlNotify:@"Idle" - withDescription:theDescription - andNotification:@"Idle" - windowIndex:[session screenWindowIndex] - tabIndex:[session screenTabIndex] - viewIndex:[session screenViewIndex]]; + // Got new output + anySessionHasNewOutput = YES; + + if (session.isIdle && + [[NSDate date] timeIntervalSinceDate:[SessionView lastResizeDate]] > POST_WINDOW_RESIZE_SILENCE_SEC) { + // Idle after new output + + // See if a notification should be posted. + if (!session.havePostedIdleNotification && [session shouldPostGrowlNotification]) { + NSString *theDescription = + [NSString stringWithFormat:@"Session %@ in tab #%d became idle.", + [session name], + [self tabNumber]]; + if ([iTermProfilePreferences boolForKey:KEY_SEND_IDLE_ALERT inProfile:session.profile]) { + [[iTermGrowlDelegate sharedInstance] growlNotify:@"Idle" + withDescription:theDescription + andNotification:@"Idle" + windowIndex:[session screenWindowIndex] + tabIndex:[session screenTabIndex] + viewIndex:[session screenViewIndex]]; + } + session.havePostedIdleNotification = YES; + session.havePostedNewOutputNotification = NO; } - session.havePostedIdleNotification = YES; - session.havePostedNewOutputNotification = NO; + } else { + allSessionsWithNewOutputAreIdle = NO; } - if (isBackgroundTab) { + } + } + + // Update state + if (isBackgroundTab) { + if (anySessionHasNewOutput) { + if (allSessionsWithNewOutputAreIdle) { [self setState:kPTYTabIdleState reset:kPTYTabNewOutputState]; } } else { - // normal state - if (isBackgroundTab) { - [self setState:0 reset:(kPTYTabIdleState | kPTYTabNewOutputState)]; - } + // No new output (either we got foregrounded or nothing has happened to a background tab) + [self setState:0 reset:(kPTYTabIdleState | kPTYTabNewOutputState)]; } } } @@ -4469,7 +4541,7 @@ - (void)setLabelAttributesForActiveTab:(BOOL)notify { @"Growl Alerts") withDescription:[NSString stringWithFormat:@"New output was received in %@, tab #%d.", [[self activeSession] name], - [self realObjectCount]] + [self tabNumber]] andNotification:@"New Output" windowIndex:[[self activeSession] screenWindowIndex] tabIndex:[[self activeSession] screenTabIndex] diff --git a/sources/PTYTextView.h b/sources/PTYTextView.h index 93f1597299..5c9ccca1ed 100644 --- a/sources/PTYTextView.h +++ b/sources/PTYTextView.h @@ -108,7 +108,6 @@ typedef NS_ENUM(NSInteger, PTYTextViewSelectionExtensionUnit) { - (BOOL)textViewTabHasMaximizedPanel; - (void)textViewWillNeedUpdateForBlink; - (BOOL)textViewDelegateHandlesAllKeystrokes; -- (BOOL)textViewInSameTabAsTextView:(PTYTextView *)other; - (void)textViewSplitVertically:(BOOL)vertically withProfileGuid:(NSString *)guid; - (void)textViewSelectNextTab; - (void)textViewSelectPreviousTab; diff --git a/sources/PTYWindow+Scripting.m b/sources/PTYWindow+Scripting.m index 3f21cea869..7e9de35bf3 100644 --- a/sources/PTYWindow+Scripting.m +++ b/sources/PTYWindow+Scripting.m @@ -52,7 +52,7 @@ - (id)handleCreateTabWithDefaultProfileCommand:(NSScriptCommand *)scriptCommand canActivate:NO command:command block:nil]; - return session.tab; + return [_delegate tabForSession:session]; } - (id)handleCreateTabCommand:(NSScriptCommand *)scriptCommand { @@ -75,7 +75,7 @@ - (id)handleCreateTabCommand:(NSScriptCommand *)scriptCommand { canActivate:NO command:command block:nil]; - return session.tab; + return [_delegate tabForSession:session]; } #pragma mark - Accessors diff --git a/sources/PTYWindow.h b/sources/PTYWindow.h index b77ec37a2c..aa478f89e0 100644 --- a/sources/PTYWindow.h +++ b/sources/PTYWindow.h @@ -29,10 +29,16 @@ #import #import "iTermWeakReference.h" -@protocol PTYWindowDelegateProtocol +@class PTYTab; +@class PTYSession; + +@protocol PTYWindowDelegateProtocol - (BOOL)lionFullScreen; - (void)windowWillShowInitial; - (void)toggleTraditionalFullScreenMode; + +// Returns the tab a session belongs to. +- (PTYTab *)tabForSession:(PTYSession *)session; @end @interface PTYWindow : NSWindow diff --git a/sources/PseudoTerminal.m b/sources/PseudoTerminal.m index 2853405b22..62d92b63a5 100644 --- a/sources/PseudoTerminal.m +++ b/sources/PseudoTerminal.m @@ -1002,21 +1002,28 @@ - (void)newSessionInTabAtIndex:(id)sender } } -- (void)closeSession:(PTYSession *)aSession soft:(BOOL)soft -{ +- (void)closeSession:(PTYSession *)aSession soft:(BOOL)soft { if (!soft && [aSession isTmuxClient] && [[aSession tmuxController] isAttached]) { [[aSession tmuxController] killWindowPane:[aSession tmuxPane]]; - } else if ([[[aSession tab] sessions] count] == 1) { - [self closeTab:[aSession tab] soft:soft]; } else { - [aSession terminate]; + PTYTab *tab = [self tabForSession:aSession]; + if ([[tab sessions] count] == 1) { + [self closeTab:tab soft:soft]; + } else { + [aSession terminate]; + } } } -- (void)closeSession:(PTYSession *)aSession -{ +- (PTYTab *)tabForSession:(PTYSession *)session { + // This is kind of cheating; we shouldn't assume that a session's delegate + // is a tab. But it always is, and it would be slow to search. + return (PTYTab *)session.delegate; +} + +- (void)closeSession:(PTYSession *)aSession { [self closeSession:aSession soft:NO]; } @@ -1139,12 +1146,12 @@ - (BOOL)confirmCloseTab:(PTYTab *)aTab okToClose = [self confirmCloseForSessions:[aTab sessions] identifier:@"This tab" genericName:[NSString stringWithFormat:@"tab #%d", - [aTab realObjectCount]]]; + [aTab tabNumber]]]; } else { okToClose = [self confirmCloseForSessions:[aTab sessions] identifier:@"This multi-pane tab" genericName:[NSString stringWithFormat:@"tab #%d", - [aTab realObjectCount]]]; + [aTab tabNumber]]]; } return okToClose; } @@ -1306,7 +1313,7 @@ - (IBAction)closeCurrentSession:(id)sender - (void)closeSessionWithConfirmation:(PTYSession *)aSession { - if ([[[aSession tab] sessions] count] == 1) { + if ([[[self tabForSession:aSession] sessions] count] == 1) { [self closeCurrentTab:self]; return; } @@ -1372,7 +1379,7 @@ - (PTYTab*)currentTab } - (void)makeSessionActive:(PTYSession *)session { - PTYTab *tab = session.tab; + PTYTab *tab = [self tabForSession:session]; if (tab.realParentWindow != self) { return; } @@ -1381,11 +1388,11 @@ - (void)makeSessionActive:(PTYSession *)session { } else { [self.window makeKeyAndOrderFront:nil]; } - [_contentView.tabView selectTabViewItem:session.tab.tabViewItem]; - if (session.tab.isMaximized) { - [session.tab unmaximize]; + [_contentView.tabView selectTabViewItem:tab.tabViewItem]; + if (tab.isMaximized) { + [tab unmaximize]; } - [session.tab setActiveSession:session]; + [tab setActiveSession:session]; } - (PTYSession *)currentSession { @@ -3642,13 +3649,14 @@ - (void)sessionInitiatedResize:(PTYSession*)session width:(int)width height:(int return; } - [[session tab] setLockedSession:session]; + PTYTab *tab = [self tabForSession:session]; + [tab setLockedSession:session]; [self safelySetSessionSize:session rows:height columns:width]; PtyLog(@"sessionInitiatedResize - calling fitWindowToTab"); - [self fitWindowToTab:[session tab]]; + [self fitWindowToTab:tab]; PtyLog(@"sessionInitiatedResize - calling fitTabsToWindow"); [self fitTabsToWindow]; - [[session tab] setLockedSession:nil]; + [tab setLockedSession:nil]; } // Contextual menu @@ -3809,14 +3817,15 @@ - (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabVi } } PTYSession* aSession = [[tabViewItem identifier] activeSession]; + PTYTab *tab = [self tabForSession:aSession]; if (!_fullScreen) { - [[aSession tab] updateLabelAttributes]; + [tab updateLabelAttributes]; [self setWindowTitle]; } [[self window] makeFirstResponder:[[[tabViewItem identifier] activeSession] textview]]; - if ([[aSession tab] blur]) { - [self enableBlur:[[aSession tab] blurRadius]]; + if ([tab blur]) { + [self enableBlur:[tab blurRadius]]; } else { [self disableBlur]; } @@ -4269,7 +4278,8 @@ - (NSTabViewItem *)tabView:(NSTabView *)tabView unknownObjectWasDropped:(id 1; + PTYTab *tab = [self tabForSession:session]; + BOOL tabSurvives = [[tab sessions] count] > 1; if ([session isTmuxClient] && tabSurvives) { // Cause the "normal" drop handle to do nothing. [[MovePaneController sharedInstance] clearSession]; @@ -4280,7 +4290,7 @@ - (NSTabViewItem *)tabView:(NSTabView *)tabView unknownObjectWasDropped:(id)sender { if ([[[sender draggingPasteboard] types] indexOfObject:@"com.iterm2.psm.controlitem"] != NSNotFound) { // Dragging a tab handle. Source is a PSMTabBarControl. PTYTab *theTab = (PTYTab *)[[[[PSMTabDragAssistant sharedDragAssistant] draggedCell] representedObject] identifier]; - if (theTab == [_session tab] || [[theTab sessions] count] > 1) { + if (theTab == _session.delegate || [[theTab sessions] count] > 1) { return NSDragOperationNone; } if (![[theTab activeSession] isCompatibleWith:[self session]]) { @@ -450,7 +450,7 @@ - (NSDragOperation)draggingUpdated:(id)sender { - (BOOL)performDragOperation:(id)sender { if ([[[sender draggingPasteboard] types] indexOfObject:iTermMovePaneDragType] != NSNotFound) { if ([[MovePaneController sharedInstance] isMovingSession:[self session]]) { - if (_session.tab.sessions.count == 1 && !_session.tab.realParentWindow.anyFullScreen) { + if (_session.delegate.sessions.count == 1 && !_session.delegate.realParentWindow.anyFullScreen) { // If you dragged a session from a tab with split panes onto itself then do nothing. // But if you drag a session onto itself in a tab WITHOUT split panes, then move the // whole window. @@ -628,7 +628,7 @@ - (NSMenu *)menu { } - (void)close { - [[[_session tab] realParentWindow] closeSessionWithConfirmation:_session]; + [[[_session delegate] realParentWindow] closeSessionWithConfirmation:_session]; } - (void)beginDrag { diff --git a/sources/TmuxController.m b/sources/TmuxController.m index 4c7676df9a..b759bf7fb5 100644 --- a/sources/TmuxController.m +++ b/sources/TmuxController.m @@ -419,9 +419,8 @@ - (PTYSession *)sessionForWindowPane:(int)windowPane - (void)registerSession:(PTYSession *)aSession withPane:(int)windowPane - inWindow:(int)window -{ - [self retainWindow:window withTab:[aSession tab]]; + inWindow:(int)window { + [self retainWindow:window withTab:[aSession.delegate.realParentWindow tabForSession:aSession]]; [windowPanes_ setObject:aSession forKey:[self _keyForWindowPane:windowPane]]; } @@ -1320,7 +1319,7 @@ - (void)closeAllPanes // body modifies it by closing sessions. for (NSString *key in [[windowPanes_ copy] autorelease]) { PTYSession *session = [windowPanes_ objectForKey:key]; - [[[session tab] realParentWindow] softCloseSession:session]; + [session.delegate.realParentWindow softCloseSession:session]; } // Clean up all state to avoid trying to reuse it. diff --git a/sources/WindowControllerInterface.h b/sources/WindowControllerInterface.h index 84a200e80d..270540e899 100644 --- a/sources/WindowControllerInterface.h +++ b/sources/WindowControllerInterface.h @@ -218,6 +218,9 @@ typedef NS_ENUM(NSInteger, BroadcastMode) { - (void)tabActiveSessionDidChange; +// Returns the tab associated with a session. +- (PTYTab *)tabForSession:(PTYSession *)session; + #pragma mark - Sessions // Set the session name. If theSessionName is nil then set it to the pathname diff --git a/sources/iTermApplicationDelegate.m b/sources/iTermApplicationDelegate.m index 71cefa9428..d454b1ef04 100644 --- a/sources/iTermApplicationDelegate.m +++ b/sources/iTermApplicationDelegate.m @@ -709,7 +709,7 @@ - (IBAction)openPasswordManager:(id)sender { - (void)openPasswordManagerToAccountName:(NSString *)name inSession:(PTYSession *)session { id term = [[iTermController sharedInstance] currentTerminal]; if (session) { - term = session.tab.realParentWindow; + term = session.delegate.realParentWindow; } if (term) { return [term openPasswordManagerToAccountName:name inSession:session]; @@ -1460,7 +1460,7 @@ - (void)reloadSessionMenus:(NSNotification *)aNotification PseudoTerminal *currentTerminal = [self currentTerminal]; PTYSession* aSession = [aNotification object]; - if (currentTerminal != [[aSession tab] parentWindow] || + if (currentTerminal != [[aSession delegate] parentWindow] || ![[currentTerminal window] isKeyWindow]) { return; } diff --git a/sources/iTermExposeGridView.m b/sources/iTermExposeGridView.m index 0b40ba09f7..2272ad1043 100644 --- a/sources/iTermExposeGridView.m +++ b/sources/iTermExposeGridView.m @@ -286,7 +286,7 @@ - (void)_restoreMaximizationExceptSession:(PTYSession*)theSession for (iTermExposeTabView* aView in [self subviews]) { if ([aView isKindOfClass:[iTermExposeTabView class]]) { iTermExposeTabView* tabView = (iTermExposeTabView*)aView; - if ([aView wasMaximized] && [theSession tab] != [tabView tab]) { + if ([aView wasMaximized] && [theSession.delegate.realParentWindow tabForSession:theSession] != [tabView tab]) { [[tabView tab] maximize]; } } diff --git a/sources/iTermExposeView.m b/sources/iTermExposeView.m index 55b4666729..58c7f6a921 100644 --- a/sources/iTermExposeView.m +++ b/sources/iTermExposeView.m @@ -69,7 +69,7 @@ - (void)globalSearchSelectionChangedToSession:(PTYSession*)theSession [resultView_ setHasResult:NO]; resultView_ = nil; resultSession_ = nil; - PTYTab* changedTab = [theSession tab]; + PTYTab *changedTab = [theSession.delegate.realParentWindow tabForSession:theSession]; for (iTermExposeTabView* aView in [grid_ subviews]) { if ([aView isKindOfClass:[iTermExposeTabView class]]) { PTYTab* theTab = [aView tabObject]; @@ -82,7 +82,7 @@ - (void)globalSearchSelectionChangedToSession:(PTYSession*)theSession } [resultView_ setHasResult:YES]; if (resultView_) { - [grid_ updateTab:[theSession tab]]; + [grid_ updateTab:changedTab]; } } diff --git a/sources/iTermNewWindowCommand.m b/sources/iTermNewWindowCommand.m index e99eff76b1..510f1f4fde 100644 --- a/sources/iTermNewWindowCommand.m +++ b/sources/iTermNewWindowCommand.m @@ -43,7 +43,7 @@ - (id)performDefaultImplementation { canActivate:YES command:command block:nil]; - return session.tab.realParentWindow.window; + return session.delegate.realParentWindow.window; } return nil; } diff --git a/sources/iTermOpenQuicklyModel.m b/sources/iTermOpenQuicklyModel.m index a796c3bd8f..a2e591c27f 100644 --- a/sources/iTermOpenQuicklyModel.m +++ b/sources/iTermOpenQuicklyModel.m @@ -373,7 +373,7 @@ - (double)scoreForSession:(PTYSession *)session limit:maxScorePerFeature]; score += [self scoreUsingMatcher:matcher - documents:@[ session.profile[KEY_NAME] ?: @"" ] + documents:@[ session.originalProfile[KEY_NAME] ?: @"" ] multiplier:kProfileNameMultiplier name:@"Profile" features:features diff --git a/sources/iTermOpenQuicklyWindowController.m b/sources/iTermOpenQuicklyWindowController.m index 126a8c2304..66b0b84b06 100644 --- a/sources/iTermOpenQuicklyWindowController.m +++ b/sources/iTermOpenQuicklyWindowController.m @@ -164,7 +164,7 @@ - (void)openSelectedRow { // Switch to session PTYSession *session = object; if (session) { - NSWindowController *term = session.tab.realParentWindow; + NSWindowController *term = session.delegate.realParentWindow; [term makeSessionActive:session]; } } else if ([object isKindOfClass:[Profile class]]) { diff --git a/tools/release.sh b/tools/release.sh index f5ae5f01af..1246e440f0 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -60,6 +60,8 @@ function Build { test -f $SVNDIR/downloads/beta/iTerm2-${NAME}.summary || (echo "iTerm2 "$VERSION" beta ($SUMMARY)" > $SVNDIR/downloads/beta/iTerm2-${NAME}.summary) test -f $SVNDIR/downloads/beta/iTerm2-${NAME}.description || (echo "$DESCRIPTION" > $SVNDIR/downloads/beta/iTerm2-${NAME}.description) vi $SVNDIR/downloads/beta/iTerm2-${NAME}.description + echo 'SHA-256 of the zip file is' > $SVNDIR/downloads/beta/iTerm2-${NAME}.changelog + shasum -a256 iTerm2-2_9_20160313.zip | awk '{print $1}' >> $SVNDIR/downloads/beta/iTerm2-${NAME}.changelog vi $SVNDIR/downloads/beta/iTerm2-${NAME}.changelog pushd $SVNDIR git add downloads/beta/iTerm2-${NAME}.summary downloads/beta/iTerm2-${NAME}.description downloads/beta/iTerm2-${NAME}.changelog downloads/beta/iTerm2-${NAME}.zip source/appcasts/testing3.xml source/appcasts/testing_changes3.txt