CTTabContents.m 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #import "CTTabContents.h"
  2. #import "CTTabStripModel.h"
  3. #import "CTBrowser.h"
  4. //#import "KVOChangeScope.hh"
  5. #import <objc/runtime.h>
  6. NSString *const CTTabContentsDidCloseNotification = @"CTTabContentsDidCloseNotification";
  7. @implementation CTTabContents
  8. // Custom @synthesize which invokes [browser_ updateTabStateForContent:self]
  9. // when setting values.
  10. #define _synthRetain(T, setname, getname) \
  11. - (T)getname { return getname##_; } \
  12. - (void)set##setname :(T)v { \
  13. getname##_ = v; \
  14. if (browser_) [browser_ updateTabStateForContent:self]; \
  15. }
  16. #define _synthAssign(T, setname, getname) \
  17. - (T)getname { return getname##_; } \
  18. - (void)set##setname :(T)v { \
  19. getname##_ = v; \
  20. if (browser_) [browser_ updateTabStateForContent:self]; \
  21. }
  22. // changing any of these implies [browser_ updateTabStateForContent:self]
  23. _synthAssign(BOOL, IsLoading, isLoading);
  24. _synthAssign(BOOL, IsWaitingForResponse, isWaitingForResponse);
  25. _synthAssign(BOOL, IsCrashed, isCrashed);
  26. _synthRetain(NSString*, Title, title);
  27. _synthRetain(NSImage*, Icon, icon);
  28. //@synthesize isLoading = isLoading_;
  29. //@synthesize isWaitingForResponse = isWaitingForResponse_;
  30. //@synthesize isCrashed = isCrashed_;
  31. //@synthesize title = title_;
  32. //@synthesize icon = icon_;
  33. @synthesize delegate = delegate_;
  34. @synthesize closedByUserGesture = closedByUserGesture_;
  35. @synthesize view = view_;
  36. @synthesize browser = browser_;
  37. @synthesize isApp = isApp_;
  38. @synthesize isActive = isActive_;
  39. @synthesize isTeared = isTeared_;
  40. @synthesize isVisible = isVisible_;
  41. #undef _synth
  42. // KVO support
  43. + (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key {
  44. if ([key isEqualToString:@"isLoading"] ||
  45. [key isEqualToString:@"isWaitingForResponse"] ||
  46. [key isEqualToString:@"isCrashed"] ||
  47. [key isEqualToString:@"isVisible"] ||
  48. [key isEqualToString:@"title"] ||
  49. [key isEqualToString:@"icon"] ||
  50. [key isEqualToString:@"parentOpener"] ||
  51. [key isEqualToString:@"isActive"] ||
  52. [key isEqualToString:@"isTeared"]) {
  53. return YES;
  54. }
  55. return [super automaticallyNotifiesObserversForKey:key];
  56. }
  57. -(id)initWithBaseTabContents:(CTTabContents*)baseContents {
  58. // subclasses should probably override this
  59. self.parentOpener = baseContents;
  60. if (!baseContents) {
  61. self.isHome = YES;
  62. } else {
  63. self.isHome = NO;
  64. }
  65. return [super init];
  66. }
  67. #pragma mark Properties impl.
  68. -(BOOL)hasIcon {
  69. return YES;
  70. }
  71. - (CTTabContents*)parentOpener {
  72. return parentOpener_;
  73. }
  74. - (void)setParentOpener:(CTTabContents*)parentOpener {
  75. NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
  76. if (parentOpener_) {
  77. [nc removeObserver:self
  78. name:CTTabContentsDidCloseNotification
  79. object:parentOpener_];
  80. }
  81. [self willChangeValueForKey:@"parentOpener"];
  82. parentOpener_ = parentOpener;
  83. [self didChangeValueForKey:@"parentOpener"];
  84. if (parentOpener_) {
  85. [nc addObserver:self
  86. selector:@selector(tabContentsDidClose:)
  87. name:CTTabContentsDidCloseNotification
  88. object:parentOpener_];
  89. }
  90. }
  91. - (void)tabContentsDidClose:(NSNotification*)notification {
  92. // detach (NULLify) our parentOpener_ when it closes
  93. CTTabContents* tabContents = [notification object];
  94. if (tabContents == parentOpener_) {
  95. parentOpener_ = nil;
  96. }
  97. }
  98. - (void)setVisible:(BOOL)visible {
  99. if (isVisible_ != visible && !isTeared_) {
  100. isVisible_ = visible;
  101. if (isVisible_) {
  102. [self tabDidBecomeVisible];
  103. } else {
  104. [self tabDidResignVisible];
  105. }
  106. }
  107. }
  108. - (void)setActive:(BOOL)active {
  109. if (isActive_ != active && !isTeared_) {
  110. isActive_ = active;
  111. if (isActive_) {
  112. [self tabDidBecomeActive];
  113. } else {
  114. [self tabDidResignActive];
  115. }
  116. }
  117. }
  118. - (void)setTeared:(BOOL)teared {
  119. if (isTeared_ != teared) {
  120. isTeared_ = teared;
  121. if (isTeared_) {
  122. [self tabWillBecomeTeared];
  123. } else {
  124. [self tabWillResignTeared];
  125. [self tabDidBecomeActive];
  126. }
  127. }
  128. }
  129. #pragma mark Actions
  130. - (void)makeKeyAndOrderFront:(id)sender {
  131. if (browser_) {
  132. NSWindow *window = browser_.window;
  133. if (window)
  134. [window makeKeyAndOrderFront:sender];
  135. int index = [browser_ indexOfTabContents:self];
  136. assert(index > -1); // we should exist in browser
  137. [browser_ selectTabAtIndex:index];
  138. }
  139. }
  140. - (BOOL)becomeFirstResponder {
  141. if (isVisible_) {
  142. return [[view_ window] makeFirstResponder:view_];
  143. }
  144. return NO;
  145. }
  146. #pragma mark Callbacks
  147. -(void)closingOfTabDidStart:(CTTabStripModel *)closeInitiatedByTabStripModel {
  148. NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
  149. [nc postNotificationName:CTTabContentsDidCloseNotification object:self];
  150. }
  151. // Called when this tab was inserted into a browser
  152. - (void)tabDidInsertIntoBrowser:(CTBrowser*)browser
  153. atIndex:(NSInteger)index
  154. inForeground:(BOOL)foreground {
  155. self.browser = browser;
  156. }
  157. // Called when this tab replaced another tab
  158. - (void)tabReplaced:(CTTabContents*)oldContents
  159. inBrowser:(CTBrowser*)browser
  160. atIndex:(NSInteger)index {
  161. self.browser = browser;
  162. }
  163. // Called when this tab is about to close
  164. - (void)tabWillCloseInBrowser:(CTBrowser*)browser atIndex:(NSInteger)index {
  165. self.browser = nil;
  166. }
  167. // Called when this tab was removed from a browser. Will be followed by a
  168. // |tabDidInsertIntoBrowser:atIndex:inForeground:|.
  169. - (void)tabDidDetachFromBrowser:(CTBrowser*)browser atIndex:(NSInteger)index {
  170. self.browser = nil;
  171. }
  172. -(void)tabWillBecomeActive {}
  173. -(void)tabWillResignActive {}
  174. -(void)tabDidBecomeActive {
  175. [self becomeFirstResponder];
  176. }
  177. -(void)tabDidResignActive {}
  178. -(void)tabDidBecomeVisible {}
  179. -(void)tabDidResignVisible {}
  180. -(void)tabWillBecomeTeared {
  181. // Teared tabs should always be visible and active since tearing is invoked
  182. // by the user selecting the tab on screen.
  183. assert(isVisible_);
  184. assert(isActive_);
  185. }
  186. -(void)tabWillResignTeared {
  187. assert(isVisible_);
  188. assert(isActive_);
  189. }
  190. // Unlike the above callbacks, this one is explicitly called by
  191. // CTBrowserWindowController
  192. -(void)tabDidResignTeared {
  193. [[view_ window] makeFirstResponder:view_];
  194. }
  195. -(void)viewFrameDidChange:(NSRect)newFrame {
  196. [view_ setFrame:newFrame];
  197. }
  198. @end
  199. @implementation CTTabContents (KMExtensions)
  200. - (void)setIsNewTab:(BOOL)isNewTab {
  201. objc_setAssociatedObject(self, @selector(isNewTab), @(isNewTab), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  202. }
  203. - (BOOL)isNewTab {
  204. return [objc_getAssociatedObject(self, @selector(isNewTab)) boolValue];
  205. }
  206. @end