CTBrowserWindowController.m 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500
  1. #import "CTBrowser.h"
  2. #import "CTBrowserWindow.h"
  3. #import "CTBrowserWindowController.h"
  4. #import "CTPresentationModeController.h"
  5. #import "CTFloatingBarBackingView.h"
  6. #import "CTTabContents.h"
  7. #import "CTTabStripController.h"
  8. #import "CTTabStripModel.h"
  9. #import "CTTabView.h"
  10. #import "CTTabStripView.h"
  11. #import "CTToolbarController.h"
  12. #import "CTUtil.h"
  13. #import "FastResizeView.h"
  14. #import "CTTabController.h"
  15. #if VERSION_DMG
  16. #import <PDF_Master-Swift.h>
  17. #else
  18. #import <PDF_Master-Swift.h>
  19. #endif
  20. #import "common.h"
  21. //#import "scoped_nsdisable_screen_updates.h"
  22. @interface NSWindow (ThingsThatMightBeImplemented)
  23. - (void)setShouldHideTitle:(BOOL)y;
  24. - (void)setBottomCornerRounded:(BOOL)y;
  25. @end
  26. @interface CTBrowserWindowController (Private)
  27. - (CGFloat)layoutTabStripAtMaxY:(CGFloat)maxY
  28. width:(CGFloat)width
  29. fullscreen:(BOOL)fullscreen;
  30. - (CGFloat)layoutToolbarAtMinX:(CGFloat)minX
  31. maxY:(CGFloat)maxY
  32. width:(CGFloat)width;
  33. @end
  34. @interface CTBrowserWindowController (FullScreen)
  35. - (void)registerForContentViewResizeNotifications;
  36. - (void)deregisterForContentViewResizeNotifications;
  37. // Creates the button used to toggle presentation mode. Must only be called on
  38. // Lion or later. Does nothing if the button already exists.
  39. - (void)createAndInstallPresentationModeToggleButton;
  40. // Toggles presentation mode without exiting fullscreen mode. Should only be
  41. // called by the presentation mode toggle button.
  42. - (void)togglePresentationModeForLionOrLater:(id)sender;
  43. // Sets presentation mode, creating the PresentationModeController if needed and
  44. // forcing a relayout. If |forceDropdown| is YES, this method will always
  45. // initially show the floating bar when entering presentation mode, even if the
  46. // floating bar does not have focus. This method is safe to call on all OS
  47. // versions.
  48. - (void)setPresentationModeInternal:(BOOL)presentationMode
  49. forceDropdown:(BOOL)forceDropdown;
  50. // Allows/prevents bar visibility locks and releases from updating the visual
  51. // state. Enabling makes changes instantaneously; disabling cancels any
  52. // timers/animation.
  53. - (void)enableBarVisibilityUpdates;
  54. - (void)disableBarVisibilityUpdates;
  55. @end
  56. @implementation NSDocumentController (CTBrowserWindowControllerAdditions)
  57. - (id)openUntitledDocumentWithWindowController:(NSWindowController*)windowController
  58. display:(BOOL)display
  59. error:(NSError **)outError {
  60. // default implementation
  61. return [self openUntitledDocumentAndDisplay:display error:outError];
  62. }
  63. @end
  64. static CTBrowserWindowController* _currentMain = nil; // weak
  65. @implementation CTBrowserWindowController
  66. @synthesize tabStripController = tabStripController_;
  67. @synthesize toolbarController = toolbarController_;
  68. @synthesize browser = browser_;
  69. @synthesize shouldUsePresentationModeWhenEnteringFullscreen = shouldUsePresentationModeWhenEnteringFullscreen_;
  70. + (CTBrowserWindowController*)browserWindowController {
  71. return [[self alloc] init];
  72. }
  73. + (CTBrowserWindowController*)mainBrowserWindowController {
  74. return _currentMain;
  75. }
  76. + (CTBrowserWindowController*)browserWindowControllerForWindow:(NSWindow*)window {
  77. while (window) {
  78. id controller = [window windowController];
  79. if ([controller isKindOfClass:[CTBrowserWindowController class]])
  80. return (CTBrowserWindowController*)controller;
  81. window = [window parentWindow];
  82. }
  83. return nil;
  84. }
  85. + (CTBrowserWindowController*)browserWindowControllerForView:(NSView*)view {
  86. NSWindow* window = [view window];
  87. return [CTBrowserWindowController browserWindowControllerForWindow:window];
  88. }
  89. // Load the browser window nib and do initialization. Note that the nib also
  90. // sets this controller up as the window's delegate.
  91. - (id)initWithWindowNibPath:(NSString *)windowNibPath
  92. browser:(CTBrowser*)browser {
  93. if (!(self = [super initWithWindowNibPath:windowNibPath owner:self]))
  94. return nil;
  95. // Set initialization boolean state so subroutines can act accordingly
  96. initializing_ = YES;
  97. // Our browser
  98. browser_ = browser;
  99. NSWindow* window = [self window];
  100. // Lion will attempt to automagically save and restore the UI. This
  101. // functionality appears to be leaky (or at least interacts badly with our
  102. // architecture) and thus BrowserWindowController never gets released. This
  103. // prevents the browser from being able to quit <http://crbug.com/79113>.
  104. //
  105. // As of 2013/05/03, with ARC and the latest retain cycle fixups, I don't
  106. // see restorable windows causing any more trouble, so I'm re-enabling this
  107. // functionality.
  108. // if ([window respondsToSelector:@selector(setRestorable:)])
  109. // [window setRestorable:NO];
  110. // Create the bar visibility lock set; 10 is arbitrary, but should hopefully
  111. // be big enough to hold all locks that'll ever be needed.
  112. barVisibilityLocks_ = [NSMutableSet setWithCapacity:10];
  113. // Note: the below statement including [self window] implicitly loads the
  114. // window and thus initializes IBOutlets, needed later. If [self window] is
  115. // not called (i.e. code removed), substitute the loading with a call to
  116. // [self loadWindow]
  117. // Set the window to not have rounded corners, which prevents the resize
  118. // control from being inset slightly and looking ugly. Only bother to do
  119. // this on Snow Leopard and earlier; on Lion and later all windows have
  120. // rounded bottom corners, and this won't work anyway.
  121. if ([window respondsToSelector:@selector(setBottomCornerRounded:)])
  122. [window setBottomCornerRounded:NO];
  123. [[window contentView] setAutoresizesSubviews:YES];
  124. // Lion will attempt to automagically save and restore the UI. This
  125. // functionality appears to be leaky (or at least interacts badly with our
  126. // architecture) and thus BrowserWindowController never gets released. This
  127. // prevents the browser from being able to quit <http://crbug.com/79113>.
  128. //
  129. // As of 2013/05/03, with ARC and the latest retain cycle fixups, I don't
  130. // see restorable windows causing any more trouble, so I'm re-enabling this
  131. // functionality.
  132. // if ([window respondsToSelector:@selector(setRestorable:)])
  133. // [window setRestorable:NO];
  134. // Note: when using the default BrowserWindow.xib, window bounds are saved and
  135. // restored by Cocoa using NSUserDefaults key "browserWindow".
  136. // Get the windows to swish in on Lion.
  137. if ([window respondsToSelector:@selector(setAnimationBehavior:)])
  138. [window setAnimationBehavior:NSWindowAnimationBehaviorDocumentWindow];
  139. // Set the window to participate in Lion Fullscreen mode. Setting this flag
  140. // has no effect on Snow Leopard or earlier. Panels can share a fullscreen
  141. // space with a tabbed window, but they can not be primary fullscreen
  142. // windows.
  143. NSUInteger collectionBehavior = [window collectionBehavior];
  144. collectionBehavior |= NSWindowCollectionBehaviorFullScreenPrimary;
  145. [window setCollectionBehavior:collectionBehavior];
  146. // Create a tab strip controller
  147. tabStripController_ =
  148. [[CTTabStripController alloc] initWithView:self.tabStripView
  149. switchView:self.tabContentArea
  150. browser:browser_];
  151. tabStripController_.isHomeFixWidth = YES;
  152. // Create a toolbar controller. The browser object might return nil, in which
  153. // means we do not have a toolbar.
  154. toolbarController_ = [browser_ createToolbarController];
  155. if (toolbarController_ && self.hasToolbar) {
  156. [[[self window] contentView] addSubview:[toolbarController_ view]];
  157. }
  158. self.rightStripView.wantsLayer = YES;
  159. self.homeRightStripView.wantsLayer = YES;
  160. // When using NSDocuments
  161. [self setShouldCloseDocument:YES];
  162. // Allow bar visibility to be changed.
  163. [self enableBarVisibilityUpdates];
  164. // Observe tabs
  165. [[NSNotificationCenter defaultCenter] addObserver:self
  166. selector:@selector(tabDidSelect:)
  167. name:CTTabSelectedNotification
  168. object:browser_.tabStripModel];
  169. [[NSNotificationCenter defaultCenter] addObserver:self
  170. selector:@selector(tabDidInsert:)
  171. name:CTTabInsertedNotification
  172. object:browser_.tabStripModel];
  173. [[NSNotificationCenter defaultCenter] addObserver:self
  174. selector:@selector(tabDidReplace:)
  175. name:CTTabReplacedNotification
  176. object:browser_.tabStripModel];
  177. [[NSNotificationCenter defaultCenter] addObserver:self
  178. selector:@selector(tabDidDetach:)
  179. name:CTTabDetachedNotification
  180. object:browser_.tabStripModel];
  181. [[NSNotificationCenter defaultCenter] addObserver:self
  182. selector:@selector(tabWillClose:)
  183. name:CTTabClosingNotification
  184. object:browser_.tabStripModel];
  185. [[NSNotificationCenter defaultCenter] addObserver:self
  186. selector:@selector(tabStripDidBecomeEmpty)
  187. name:CTTabStripEmptyNotification
  188. object:browser_.tabStripModel];
  189. // Register for application hide/unhide notifications.
  190. [[NSNotificationCenter defaultCenter] addObserver:self
  191. selector:@selector(applicationDidHide:)
  192. name:NSApplicationDidHideNotification
  193. object:nil];
  194. [[NSNotificationCenter defaultCenter] addObserver:self
  195. selector:@selector(applicationDidUnhide:)
  196. name:NSApplicationDidUnhideNotification
  197. object:nil];
  198. // Force a relayout of all the various bars.
  199. [self layoutSubviews];
  200. initializing_ = NO;
  201. if (!_currentMain) {
  202. // ct_casid(&_currentMain, self);
  203. _currentMain = self;
  204. }
  205. return self;
  206. }
  207. - (id)initWithBrowser:(CTBrowser *)browser {
  208. // subclasses could override this to provie a custom nib
  209. NSString *windowNibPath = [CTUtil pathForResource:@"KMBrowserWindowController"
  210. ofType:@"nib"];
  211. return [self initWithWindowNibPath:windowNibPath browser:browser];
  212. }
  213. - (id)init {
  214. // subclasses could override this to provide a custom |CTBrowser|
  215. return [self initWithBrowser:[CTBrowser browser]];
  216. }
  217. -(void)finalize {
  218. if (_currentMain == self) {
  219. // ct_casid(&_currentMain, nil);
  220. _currentMain = nil;
  221. }
  222. [[NSNotificationCenter defaultCenter] removeObserver:self];
  223. [super finalize];
  224. }
  225. - (BOOL)hasToolbar {
  226. return !!toolbarController_;
  227. }
  228. - (void)setDisableNewTabButton:(BOOL)disable {
  229. tabStripController_.disableNewTabButton = disable;
  230. }
  231. - (BOOL)disableNewTabButton {
  232. return tabStripController_.disableNewTabButton;
  233. }
  234. // Updates the toolbar with the states of the specified |contents|.
  235. // If |shouldRestore| is YES, we're switching (back?) to this tab and should
  236. // restore any previous state (such as user editing a text field) as well.
  237. - (void)updateToolbarWithContents:(CTTabContents*)contents
  238. shouldRestoreState:(BOOL)shouldRestore {
  239. // safe even if toolbarController_ is nil
  240. [toolbarController_ updateToolbarWithContents:contents
  241. shouldRestoreState:shouldRestore];
  242. }
  243. - (void)synchronizeWindowTitleWithDocumentName {
  244. // overriding this to not do anything have the effect of not adding a title to
  245. // our window (the title is in the tab, remember?)
  246. }
  247. #pragma mark -
  248. #pragma mark NSWindow (CTThemed)
  249. - (NSPoint)themePatternPhase {
  250. // Our patterns want to be drawn from the upper left hand corner of the view.
  251. // Cocoa wants to do it from the lower left of the window.
  252. //
  253. // Rephase our pattern to fit this view. Some other views (Tabs, Toolbar etc.)
  254. // will phase their patterns relative to this so all the views look right.
  255. //
  256. // To line up the background pattern with the pattern in the browser window
  257. // the background pattern for the tabs needs to be moved left by 5 pixels.
  258. const CGFloat kPatternHorizontalOffset = -5;
  259. NSView* tabStripView = [self tabStripView];
  260. NSRect tabStripViewWindowBounds = [tabStripView bounds];
  261. NSView* windowChromeView = [[[self window] contentView] superview];
  262. tabStripViewWindowBounds =
  263. [tabStripView convertRect:tabStripViewWindowBounds
  264. toView:windowChromeView];
  265. NSPoint phase = NSMakePoint(NSMinX(tabStripViewWindowBounds)
  266. + kPatternHorizontalOffset,
  267. NSMinY(tabStripViewWindowBounds)
  268. + [CTTabStripController defaultTabHeight]);
  269. CTTabContents* document = [self.browser activeTabContents];
  270. NSView * rightView = nil;
  271. if (document.isHome) {
  272. rightView = self.homeRightStripView;
  273. self.rightStripView.hidden = YES;
  274. self.homeRightStripView.hidden = NO;
  275. } else {
  276. rightView = self.rightStripView;
  277. self.rightStripView.hidden = NO;
  278. self.homeRightStripView.hidden = YES;
  279. }
  280. CGFloat rightOffst = NSWidth(rightView.frame);
  281. NSRect contentFrame = [windowChromeView frame];
  282. NSRect rightFrame =
  283. NSMakeRect(NSWidth(contentFrame) - rightOffst, rightView.frame.origin.y,
  284. rightOffst,
  285. NSHeight([tabStripView frame]));
  286. [rightView setFrame:rightFrame];
  287. NSView* contentParent = [[[self window] contentView] superview];
  288. [contentParent addSubview:[self tabStripView] positioned:NSWindowAbove relativeTo:nil];
  289. [contentParent addSubview:rightView positioned:NSWindowAbove relativeTo:nil];
  290. return phase;
  291. }
  292. #pragma mark -
  293. #pragma mark Actions
  294. - (IBAction)saveAllDocuments:(id)sender {
  295. [[NSDocumentController sharedDocumentController] saveAllDocuments:sender];
  296. }
  297. - (IBAction)openDocument:(id)sender {
  298. [[NSDocumentController sharedDocumentController] openDocument:sender];
  299. }
  300. - (IBAction)newDocument:(id)sender {
  301. NSDocumentController* docController =
  302. [NSDocumentController sharedDocumentController];
  303. NSError *error = nil;
  304. DCHECK(browser_);
  305. CTTabContents *baseTabContents = browser_.activeTabContents;
  306. CTTabContents *tabContents =
  307. [docController openUntitledDocumentWithWindowController:self
  308. display:YES
  309. error:&error];
  310. if (!tabContents) {
  311. [NSApp presentError:error];
  312. } else if (baseTabContents) {
  313. tabContents.parentOpener = baseTabContents;
  314. }
  315. }
  316. - (IBAction)newWindow:(id)sender {
  317. [browser_ newWindow];
  318. }
  319. - (IBAction)closeTab:(id)sender {
  320. [browser_ closeTab];
  321. }
  322. // Called when the user picks a menu or toolbar item when this window is key.
  323. // Calls through to the browser object to execute the command. This assumes that
  324. // the command is supported and doesn't check, otherwise it would have been
  325. // disabled in the UI in validateUserInterfaceItem:.
  326. - (void)commandDispatch:(id)sender {
  327. assert(sender);
  328. // Identify the actual BWC to which the command should be dispatched. It might
  329. // belong to a background window, yet this controller gets it because it is
  330. // the foreground window's controller and thus in the responder chain. Some
  331. // senders don't have this problem (for example, menus only operate on the
  332. // foreground window), so this is only an issue for senders that are part of
  333. // windows.
  334. CTBrowserWindowController* targetController = self;
  335. if ([sender respondsToSelector:@selector(window)])
  336. targetController = [[sender window] windowController];
  337. assert([targetController isKindOfClass:[CTBrowserWindowController class]]);
  338. [targetController.browser executeCommand:[(NSView *)sender tag]];
  339. }
  340. #pragma mark -
  341. #pragma mark CTTabWindowController implementation
  342. // Accept tabs from a CTBrowserWindowController with the same Profile.
  343. - (BOOL)canReceiveFrom:(CTTabWindowController*)source {
  344. if (![source isKindOfClass:[CTTabWindowController class]]) {
  345. return NO;
  346. }
  347. // here we could for instance check (and deny) dragging a tab from a normal
  348. // window into a special window (e.g. pop-up or similar)
  349. return YES;
  350. }
  351. #pragma mark -
  352. #pragma mark Tab Management
  353. // Move a given tab view to the location of the current placeholder. If there is
  354. // no placeholder, it will go at the end. |controller| is the window controller
  355. // of a tab being dropped from a different window. It will be nil if the drag is
  356. // within the window, otherwise the tab is removed from that window before being
  357. // placed into this one.
  358. //
  359. // The implementation will call |-removePlaceholder| since the drag is now
  360. // complete. This also calls |-layoutTabs| internally so clients do not need to
  361. // call it again.
  362. - (void)moveTabView:(NSView*)view
  363. fromController:(CTTabWindowController*)dragController {
  364. if (dragController) {
  365. // Moving between windows. Figure out the CTTabContents to drop into our tab
  366. // model from the source window's model.
  367. BOOL isBrowser =
  368. [dragController isKindOfClass:[CTBrowserWindowController class]];
  369. assert(isBrowser);
  370. if (!isBrowser) return;
  371. CTBrowserWindowController* dragBWC = (CTBrowserWindowController*)dragController;
  372. int index = [dragBWC->tabStripController_ modelIndexForTabView:view];
  373. CTTabContents* contents =
  374. [[dragBWC->browser_ tabStripModel] tabContentsAtIndex:index];
  375. // The tab contents may have gone away if given a window.close() while it
  376. // is being dragged. If so, bail, we've got nothing to drop.
  377. if (!contents)
  378. return;
  379. // Convert |view|'s frame (which starts in the source tab strip's coordinate
  380. // system) to the coordinate system of the destination tab strip. This needs
  381. // to be done before being detached so the window transforms can be
  382. // performed.
  383. NSRect destinationFrame = [view frame];
  384. NSPoint tabOrigin = destinationFrame.origin;
  385. tabOrigin = [[dragController tabStripView] convertPoint:tabOrigin
  386. toView:nil];
  387. tabOrigin = [[view window] convertBaseToScreen:tabOrigin];
  388. tabOrigin = [[self window] convertScreenToBase:tabOrigin];
  389. tabOrigin = [[self tabStripView] convertPoint:tabOrigin fromView:nil];
  390. if (tabOrigin.x <= [CTTabController homeTabWidth] + [CTTabStripController defaultIndentForControls]){
  391. tabOrigin.x = [CTTabController homeTabWidth] + [CTTabStripController defaultIndentForControls];
  392. }
  393. destinationFrame.origin = tabOrigin;
  394. // Before the tab is detached from its originating tab strip, store the
  395. // pinned state so that it can be maintained between the windows.
  396. BOOL isPinned = [[dragBWC->browser_ tabStripModel] isTabPinnedAtIndex:index];
  397. // Now that we have enough information about the tab, we can remove it from
  398. // the dragging window. We need to do this *before* we add it to the new
  399. // window as this will remove the CTTabContents' delegate.
  400. [dragController detachTabView:view];
  401. // Deposit it into our model at the appropriate location (it already knows
  402. // where it should go from tracking the drag). Doing this sets the tab's
  403. // delegate to be the CTBrowser.
  404. [tabStripController_ dropTabContents:contents
  405. withFrame:destinationFrame
  406. asPinnedTab:isPinned];
  407. } else {
  408. // Moving within a window.
  409. int index = [tabStripController_ modelIndexForTabView:view];
  410. [tabStripController_ moveTabFromIndex:index];
  411. }
  412. // Remove the placeholder since the drag is now complete.
  413. [self removePlaceholder];
  414. }
  415. - (NSView*)activeTabView {
  416. return [tabStripController_ activeTabView];
  417. }
  418. // Creates a new window by pulling the given tab out and placing it in
  419. // the new window. Returns the controller for the new window. The size of the
  420. // new window will be the same size as this window.
  421. - (CTTabWindowController*)detachTabToNewWindow:(CTTabView*)tabView {
  422. // Disable screen updates so that this appears as a single visual change.
  423. NSDisableScreenUpdates();
  424. @try {
  425. // Keep a local ref to the tab strip model object
  426. CTTabStripModel *tabStripModel = [browser_ tabStripModel];
  427. // Fetch the tab contents for the tab being dragged.
  428. int index = [tabStripController_ modelIndexForTabView:tabView];
  429. CTTabContents* contents = [tabStripModel tabContentsAtIndex:index];
  430. // Set the window size. Need to do this before we detach the tab so it's
  431. // still in the window. We have to flip the coordinates as that's what
  432. // is expected by the CTBrowser code.
  433. NSWindow* sourceWindow = [tabView window];
  434. NSRect windowRect = [sourceWindow frame];
  435. NSScreen* screen = [sourceWindow screen];
  436. windowRect.origin.y =
  437. [screen frame].size.height - windowRect.size.height - windowRect.origin.y;
  438. //gfx::Rect browserRect(windowRect.origin.x, windowRect.origin.y,
  439. // windowRect.size.width, windowRect.size.height);
  440. NSRect tabRect = [tabView frame];
  441. // Before detaching the tab, store the pinned state.
  442. BOOL isPinned = [tabStripModel isTabPinnedAtIndex:index];
  443. // Detach it from the source window, which just updates the model without
  444. // deleting the tab contents. This needs to come before creating the new
  445. // CTBrowser because it clears the CTTabContents' delegate, which gets hooked
  446. // up during creation of the new window.
  447. [tabStripModel detachTabContentsAtIndex:index];
  448. // Create the new browser with a single tab in its model, the one being
  449. // dragged.
  450. CTBrowser* newBrowser = [browser_ createNewStripWithContents:contents];
  451. CTBrowserWindowController* controller = [newBrowser windowController];
  452. // Set window frame
  453. [controller.window setFrame:windowRect display:NO];
  454. // Propagate the tab pinned state of the new tab (which is the only tab in
  455. // this new window).
  456. [[newBrowser tabStripModel] setTabAtIndex:0
  457. pinned:isPinned];
  458. // Force the added tab to the right size (remove stretching.)
  459. tabRect.size.height = [CTTabStripController defaultTabHeight];
  460. // And make sure we use the correct frame in the new view.
  461. [[controller tabStripController] setFrameOfActiveTab:tabRect];
  462. return controller;
  463. }
  464. @finally {
  465. NSEnableScreenUpdates();
  466. }
  467. }
  468. - (void)insertPlaceholderForTab:(CTTabView*)tab
  469. frame:(NSRect)frame {
  470. [super insertPlaceholderForTab:tab frame:frame];
  471. [tabStripController_ insertPlaceholderForTab:tab
  472. frame:frame];
  473. }
  474. - (void)removePlaceholder {
  475. [super removePlaceholder];
  476. [tabStripController_ insertPlaceholderForTab:nil
  477. frame:NSZeroRect];
  478. }
  479. - (BOOL)tabDraggingAllowed {
  480. return [tabStripController_ tabDraggingAllowed];
  481. }
  482. // Default implementation of the below are both YES. Until we have fullscreen
  483. // support these will always be YES.
  484. - (BOOL)tabTearingAllowed {
  485. return ![self isFullscreen];
  486. }
  487. - (BOOL)windowMovementAllowed {
  488. return ![self isFullscreen];
  489. }
  490. - (BOOL)isTabFullyVisible:(CTTabView*)tab {
  491. return [tabStripController_ isTabFullyVisible:tab];
  492. }
  493. // impl. CTTabWindowController requirements
  494. - (void)setShowsNewTabButton:(BOOL)show {
  495. tabStripController_.showsNewTabButton = show;
  496. }
  497. - (BOOL)showsNewTabButton {
  498. return tabStripController_.showsNewTabButton;
  499. }
  500. // Tells the tab strip to forget about this tab in preparation for it being
  501. // put into a different tab strip, such as during a drop on another window.
  502. - (void)detachTabView:(NSView*)view {
  503. int index = [tabStripController_ modelIndexForTabView:view];
  504. [[browser_ tabStripModel] detachTabContentsAtIndex:index];
  505. }
  506. - (NSInteger)numberOfTabs {
  507. // count includes pinned tabs.
  508. return [[browser_ tabStripModel] count];
  509. }
  510. - (BOOL)hasLiveTabs {
  511. return [self numberOfTabs] > 0;
  512. }
  513. - (int)activeTabIndex {
  514. return [browser_ tabStripModel].activeIndex;
  515. }
  516. - (CTTabContents*)activeTabContents {
  517. return [[browser_ tabStripModel] activeTabContents];
  518. }
  519. - (NSString*)activeTabTitle {
  520. CTTabContents* contents = [self activeTabContents];
  521. return contents ? contents.title : nil;
  522. }
  523. - (BOOL)hasTabStrip {
  524. return YES;
  525. }
  526. -(void)willStartTearingTab {
  527. CTTabContents* contents = [browser_ activeTabContents];
  528. if (contents) {
  529. contents.isTeared = YES;
  530. }
  531. }
  532. -(void)willEndTearingTab {
  533. CTTabContents* contents = [browser_ activeTabContents];
  534. if (contents) {
  535. contents.isTeared = NO;
  536. }
  537. }
  538. -(void)didEndTearingTab {
  539. CTTabContents* contents = [browser_ activeTabContents];
  540. if (contents) {
  541. [contents tabDidResignTeared];
  542. }
  543. }
  544. - (void)focusTabContents {
  545. CTTabContents* contents = [browser_ activeTabContents];
  546. if (contents) {
  547. [[self window] makeFirstResponder:contents.view];
  548. }
  549. }
  550. #pragma mark -
  551. #pragma mark Layout
  552. // Find the total height of the floating bar (in presentation mode). Safe to
  553. // call even when not in presentation mode.
  554. - (CGFloat)floatingBarHeight {
  555. if (![self inPresentationMode])
  556. return 0;
  557. CGFloat totalHeight = [presentationModeController_ floatingBarVerticalOffset];
  558. if ([self hasTabStrip])
  559. totalHeight += NSHeight([[self tabStripView] frame]);
  560. if ([self hasToolbar]) {
  561. totalHeight += NSHeight([[toolbarController_ view] frame]);
  562. }
  563. return totalHeight;
  564. }
  565. // Lay out the view which draws the background for the floating bar when in
  566. // presentation mode, with the given frame and presentation-mode-status. Should
  567. // be called even when not in presentation mode to hide the backing view.
  568. - (void)layoutFloatingBarBackingView:(NSRect)frame
  569. presentationMode:(BOOL)presentationMode {
  570. // Only display when in presentation mode.
  571. if (presentationMode) {
  572. // For certain window types such as app windows (e.g., the dev tools
  573. // window), there's no actual overlay. (Displaying one would result in an
  574. // overly sliding in only under the menu, which gives an ugly effect.)
  575. if (floatingBarBackingView_) {
  576. // BOOL aboveBookmarkBar = [self placeBookmarkBarBelowInfoBar];
  577. //
  578. // // Insert it into the view hierarchy if necessary.
  579. if (![floatingBarBackingView_ superview]) {
  580. NSView* contentView = [[self window] contentView];
  581. // z-order gets messed up unless we explicitly remove the floatingbar
  582. // view and re-add it.
  583. [floatingBarBackingView_ removeFromSuperview];
  584. [contentView addSubview:floatingBarBackingView_
  585. positioned:NSWindowBelow
  586. relativeTo:[toolbarController_ view]];
  587. // floatingBarAboveBookmarkBar_ = aboveBookmarkBar;
  588. }
  589. // Set its frame.
  590. [floatingBarBackingView_ setFrame:frame];
  591. }
  592. // But we want the logic to work as usual (for show/hide/etc. purposes).
  593. [presentationModeController_ overlayFrameChanged:frame];
  594. } else {
  595. // Okay to call even if |floatingBarBackingView_| is nil.
  596. if ([floatingBarBackingView_ superview])
  597. [floatingBarBackingView_ removeFromSuperview];
  598. }
  599. }
  600. - (void)layoutTabContentArea:(NSRect)newFrame {
  601. NSView* tabContentView = self.tabContentArea;
  602. NSRect tabContentFrame = tabContentView.frame;
  603. BOOL contentShifted =
  604. NSMaxY(tabContentFrame) != NSMaxY(newFrame) ||
  605. NSMinX(tabContentFrame) != NSMinX(newFrame);
  606. tabContentFrame = newFrame;
  607. [tabContentView setFrame:tabContentFrame];
  608. // If the relayout shifts the content area up or down, let the renderer know.
  609. if (contentShifted) {
  610. CTTabContents* contents = [browser_ activeTabContents];
  611. if (contents) {
  612. [contents viewFrameDidChange:newFrame];
  613. }
  614. }
  615. }
  616. // Called when the size of the window content area has changed.
  617. // Position specific views.
  618. - (void)layoutSubviews {
  619. // With the exception of the top tab strip, the subviews which we lay out are
  620. // subviews of the content view, so we mainly work in the content view's
  621. // coordinate system. Note, however, that the content view's coordinate system
  622. // and the window's base coordinate system should coincide.
  623. NSWindow* window = [self window];
  624. NSView* contentView = [window contentView];
  625. if (!contentView) {
  626. return;
  627. }
  628. NSRect contentBounds = [contentView bounds];
  629. CGFloat minX = NSMinX(contentBounds);
  630. CGFloat minY = NSMinY(contentBounds);
  631. CGFloat width = NSWidth(contentBounds);
  632. // Suppress title drawing (the title is in the tab, baby)
  633. if ([window respondsToSelector:@selector(setShouldHideTitle:)])
  634. [window setShouldHideTitle:YES];
  635. BOOL inPresentationMode = [self inPresentationMode];
  636. CGFloat floatingBarHeight = [self floatingBarHeight];
  637. // In presentation mode, |yOffset| accounts for the sliding position of the
  638. // floating bar and the extra offset needed to dodge the menu bar.
  639. CGFloat yOffset = inPresentationMode ?
  640. (floor((1 - floatingBarShownFraction_) * floatingBarHeight) -
  641. [presentationModeController_ floatingBarVerticalOffset]) : 0;
  642. CGFloat maxY = NSMaxY(contentBounds) + yOffset;
  643. CGFloat overlayMaxY = NSMaxY([window frame]) + floor((1 - floatingBarShownFraction_) * floatingBarHeight);
  644. [self layoutPresentationModeToggleAtOverlayMaxX:NSMaxX([window frame])
  645. overlayMaxY:overlayMaxY];
  646. if ([self hasTabStrip]) {
  647. // If we need to lay out the top tab strip, replace |maxY| and |startMaxY|
  648. // with higher values, and then lay out the tab strip.
  649. NSRect windowFrame = [contentView convertRect:[window frame] fromView:nil];
  650. maxY = NSHeight(windowFrame) + yOffset;
  651. maxY = [self layoutTabStripAtMaxY:maxY
  652. width:width
  653. fullscreen:[self isFullscreen]];
  654. }
  655. // Sanity-check |maxY|.
  656. DCHECK_GE(maxY, minY);
  657. DCHECK_LE(maxY, NSMaxY(contentBounds) + yOffset);
  658. // Place the toolbar at the top of the reserved area.
  659. if ([self hasToolbar]){
  660. NSView* toolbarView = [toolbarController_ view];
  661. if (!toolbarView.hidden) {
  662. maxY = [self layoutToolbarAtMinX:minX maxY:maxY width:width];
  663. }
  664. }
  665. // The floating bar backing view doesn't actually add any height.
  666. NSRect floatingBarBackingRect = NSMakeRect(minX, maxY, width, floatingBarHeight);
  667. [self layoutFloatingBarBackingView:floatingBarBackingRect
  668. presentationMode:inPresentationMode];
  669. // If in presentation mode, reset |maxY| to top of screen, so that the
  670. // floating bar slides over the things which appear to be in the content area.
  671. if (inPresentationMode)
  672. maxY = NSMaxY(contentBounds);
  673. // Finally, the content area takes up all of the remaining space.
  674. NSRect contentAreaRect = NSMakeRect(minX, minY, width, maxY - minY);
  675. [self layoutTabContentArea:contentAreaRect];
  676. // Place the status bubble at the bottom of the content area.
  677. //verticalOffsetForStatusBubble_ = minY;
  678. // Normally, we don't need to tell the toolbar whether or not to show the
  679. // divider, but things break down during animation.
  680. if (toolbarController_) {
  681. [toolbarController_ setDividerOpacity:0.4];
  682. }
  683. // TODO: check this
  684. // [toolbarController_
  685. // setDividerOpacity:[bookmarkBarController_ toolbarDividerOpacity]];
  686. }
  687. - (CGFloat)layoutToolbarAtMinX:(CGFloat)minX
  688. maxY:(CGFloat)maxY
  689. width:(CGFloat)width {
  690. assert([self hasToolbar]);
  691. NSView* toolbarView = [toolbarController_ view];
  692. NSRect toolbarFrame = [toolbarView frame];
  693. assert(![toolbarView isHidden]);
  694. toolbarFrame.origin.x = minX;
  695. toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame);
  696. toolbarFrame.size.width = width;
  697. maxY -= NSHeight(toolbarFrame);
  698. [toolbarView setFrame:toolbarFrame];
  699. return maxY;
  700. }
  701. - (void)layoutTabs {
  702. [tabStripController_ layoutTabs];
  703. }
  704. - (void)layoutPresentationModeToggleAtOverlayMaxX:(CGFloat)maxX
  705. overlayMaxY:(CGFloat)maxY {
  706. // Lay out the presentation mode toggle button at the very top of the
  707. // tab strip.
  708. if ([self shouldShowPresentationModeToggle]) {
  709. [self createAndInstallPresentationModeToggleButton];
  710. NSPoint origin =
  711. NSMakePoint(maxX - NSWidth([presentationModeToggleButton_ frame]),
  712. maxY - NSHeight([presentationModeToggleButton_ frame]));
  713. [presentationModeToggleButton_ setFrameOrigin:origin];
  714. } else {
  715. [presentationModeToggleButton_ removeFromSuperview];
  716. presentationModeToggleButton_ = nil;
  717. }
  718. }
  719. - (CGFloat)layoutTabStripAtMaxY:(CGFloat)maxY
  720. width:(CGFloat)width
  721. fullscreen:(BOOL)fullscreen {
  722. if (![self hasTabStrip])
  723. return maxY;
  724. NSView* tabStripView = [self tabStripView];
  725. CTTabContents* document = [self.browser activeTabContents];
  726. KMToolbarRightView *rightStripView = nil;
  727. if (document.isHome) {
  728. rightStripView = self.homeRightStripView;
  729. } else {
  730. rightStripView = self.rightStripView;
  731. }
  732. CGFloat rightWidth = NSWidth([rightStripView frame]);
  733. if ([[KMLightMemberManager manager] canShowAdvancedView] && ![[KMLightMemberManager manager] isLogin]) {
  734. // rightWidth = NSWidth([rightStripView frame]);
  735. rightWidth = 56.0 + rightStripView.fetchAdvancedViewSize.width;
  736. // rightWidth += rightStripView.fetchAdvancedViewSize.width;
  737. } else {
  738. rightWidth = 56.0;
  739. }
  740. CGFloat tabStripHeight = NSHeight([tabStripView frame]);
  741. CGFloat tabStripWidth = width - rightWidth;
  742. maxY -= tabStripHeight;
  743. if (fullscreen) {
  744. [tabStripView setFrame:NSMakeRect(0, maxY, tabStripWidth, tabStripHeight)];
  745. } else {
  746. CGFloat offset = [[tabStripController_ class] defaultIndentForControls];
  747. tabStripWidth -= offset;
  748. [tabStripView setFrame:NSMakeRect(offset, maxY, tabStripWidth, tabStripHeight)];
  749. }
  750. [tabStripController_ setIndentForControls:0];
  751. // [rightStripView setFrame:NSMakeRect(NSMaxX(tabStripView.frame), maxY, rightWidth, tabStripHeight)];
  752. self.homeRightStripView.frame = NSMakeRect(NSMaxX(tabStripView.frame), maxY, rightWidth, tabStripHeight);
  753. self.rightStripView.frame = NSMakeRect(NSMaxX(tabStripView.frame), maxY, rightWidth, tabStripHeight);
  754. [(KMToolbarRightView *)self.rightStripView updateView];
  755. [(KMToolbarRightView *)self.homeRightStripView updateView];
  756. // Set indentation.
  757. [tabStripController_ layoutTabsWithoutAnimation];
  758. return maxY;
  759. }
  760. #pragma mark -
  761. #pragma mark NSWindowController impl
  762. - (BOOL)windowShouldClose:(id)sender {
  763. // Disable updates while closing all tabs to avoid flickering.
  764. NSDisableScreenUpdates();
  765. @try {
  766. // NOTE: when using the default BrowserWindow.xib, window bounds are saved and
  767. // restored by Cocoa using NSUserDefaults key "browserWindow".
  768. // NOTE: orderOut: ends up activating another window, so if we save window
  769. // bounds in a custom manner we have to do it here, before we call
  770. // orderOut:
  771. if ([browser_.tabStripModel count] > 0) {
  772. // Tab strip isn't empty. Hide the frame (so it appears to have closed
  773. // immediately) and close all the tabs, allowing them to shut down. When the
  774. // tab strip is empty we'll be called back again.
  775. [[self window] orderOut:self];
  776. [browser_ windowDidBeginToClose];
  777. if (_currentMain == self) {
  778. // ct_casid(&_currentMain, nil);
  779. _currentMain = nil;
  780. }
  781. return NO;
  782. }
  783. // the tab strip is empty, it's ok to close the window
  784. return YES;
  785. }
  786. @finally {
  787. NSEnableScreenUpdates();
  788. }
  789. }
  790. - (void)windowWillClose:(NSNotification *)notification {
  791. // [self autorelease];
  792. }
  793. // Called right after our window became the main window.
  794. - (void)windowDidBecomeMain:(NSNotification*)notification {
  795. // NOTE: if you use custom window bounds saving/restoring, you should probably
  796. // save the window bounds here.
  797. _currentMain = self;
  798. // TODO(dmaclach): Instead of redrawing the whole window, views that care
  799. // about the active window state should be registering for notifications.
  800. [[self window] setViewsNeedDisplay:YES];
  801. // TODO(viettrungluu): For some reason, the above doesn't suffice.
  802. if ([self isFullscreen])
  803. [floatingBarBackingView_ setNeedsDisplay:YES]; // Okay even if nil.
  804. }
  805. - (void)windowDidResignMain:(NSNotification*)notification {
  806. if (_currentMain == self) {
  807. _currentMain = nil;
  808. }
  809. // TODO(dmaclach): Instead of redrawing the whole window, views that care
  810. // about the active window state should be registering for notifications.
  811. [[self window] setViewsNeedDisplay:YES];
  812. // TODO(viettrungluu): For some reason, the above doesn't suffice.
  813. if ([self isFullscreen])
  814. [floatingBarBackingView_ setNeedsDisplay:YES]; // Okay even if nil.
  815. }
  816. // Called when we are activated (when we gain focus).
  817. - (void)windowDidBecomeKey:(NSNotification*)notification {
  818. if (![[self window] isMiniaturized]) {
  819. CTTabContents* contents = [browser_ activeTabContents];
  820. if (contents) {
  821. contents.isVisible = YES;
  822. }
  823. }
  824. }
  825. // Called when we are deactivated (when we lose focus).
  826. - (void)windowDidResignKey:(NSNotification*)notification {
  827. // If our app is still active and we're still the key window, ignore this
  828. // message, since it just means that a menu extra (on the "system status bar")
  829. // was activated; we'll get another |-windowDidResignKey| if we ever really
  830. // lose key window status.
  831. if ([NSApp isActive] && ([NSApp keyWindow] == [self window]))
  832. return;
  833. }
  834. // Called when we have been minimized.
  835. - (void)windowDidMiniaturize:(NSNotification *)notification {
  836. CTTabContents* contents = [browser_ activeTabContents];
  837. if (contents) {
  838. contents.isVisible = NO;
  839. }
  840. }
  841. // Called when we have been unminimized.
  842. - (void)windowDidDeminiaturize:(NSNotification *)notification {
  843. CTTabContents* contents = [browser_ activeTabContents];
  844. if (contents) {
  845. contents.isVisible = YES;
  846. }
  847. }
  848. // Called when the application has been hidden.
  849. - (void)applicationDidHide:(NSNotification *)notification {
  850. // Let the active tab know (unless we are minimized, in which case nothing
  851. // has really changed).
  852. if (![[self window] isMiniaturized]) {
  853. CTTabContents* contents = [browser_ activeTabContents];
  854. if (contents) {
  855. contents.isVisible = NO;
  856. }
  857. }
  858. }
  859. // Called when the application has been unhidden.
  860. - (void)applicationDidUnhide:(NSNotification *)notification {
  861. // Let the active tab know
  862. // (unless we are minimized, in which case nothing has really changed).
  863. if (![[self window] isMiniaturized]) {
  864. CTTabContents* contents = [browser_ activeTabContents];
  865. if (contents) {
  866. contents.isVisible = YES;
  867. }
  868. }
  869. }
  870. #pragma mark -
  871. #pragma mark Etc (need sorting out)
  872. - (void)activate {
  873. [[self window] makeKeyAndOrderFront:self];
  874. }
  875. #pragma mark -
  876. #pragma mark CTTabStripModel Observer
  877. // Note: the following are called by the CTTabStripModel and thus indicate
  878. // the model's state rather than the UI state. This means that when for instance
  879. // tabSelectedWithContents:... is called, the view is not yet on screen, so
  880. // doing things like restoring focus is not possible.
  881. // Note: this is called _before_ the view is on screen
  882. - (void)tabDidSelect:(NSNotification *)notification {
  883. NSDictionary *userInfo = notification.userInfo;
  884. CTTabContents *newContents = [userInfo valueForKey:CTTabNewContentsUserInfoKey];
  885. CTTabContents *oldContents = [userInfo valueForKey:CTTabContentsUserInfoKey];
  886. assert(newContents != oldContents);
  887. [self updateToolbarWithContents:newContents
  888. shouldRestoreState:!!oldContents];
  889. }
  890. - (void)tabWillClose:(NSNotification *)notification {
  891. NSDictionary *userInfo = notification.userInfo;
  892. CTTabContents *contents = [userInfo valueForKey:CTTabContentsUserInfoKey];
  893. NSInteger index = [[userInfo valueForKey:CTTabIndexUserInfoKey] intValue];
  894. [contents tabWillCloseInBrowser:browser_ atIndex:index];
  895. if (contents.isActive)
  896. [self updateToolbarWithContents:nil shouldRestoreState:NO];
  897. }
  898. - (void)tabDidInsert:(NSNotification *)notification {
  899. NSDictionary *userInfo = notification.userInfo;
  900. CTTabContents *contents = [userInfo valueForKey:CTTabContentsUserInfoKey];
  901. NSInteger index = [[userInfo valueForKey:CTTabIndexUserInfoKey] intValue];
  902. BOOL isInForeground = [[userInfo valueForKey:CTTabOptionsUserInfoKey] boolValue];
  903. [contents tabDidInsertIntoBrowser:browser_
  904. atIndex:index
  905. inForeground:isInForeground];
  906. }
  907. - (void)tabDidReplace:(NSNotification *)notification {
  908. NSDictionary *userInfo = notification.userInfo;
  909. CTTabContents *newContents = [userInfo valueForKey:CTTabNewContentsUserInfoKey];
  910. CTTabContents *oldContents = [userInfo valueForKey:CTTabContentsUserInfoKey];
  911. NSInteger index = [[userInfo valueForKey:CTTabIndexUserInfoKey] intValue];
  912. [newContents tabReplaced:oldContents inBrowser:browser_ atIndex:index];
  913. if ([self activeTabIndex] == index) {
  914. [self updateToolbarWithContents:newContents
  915. shouldRestoreState:!!oldContents];
  916. }
  917. }
  918. - (void)tabDidDetach:(NSNotification *)notification {
  919. NSDictionary *userInfo = notification.userInfo;
  920. CTTabContents *contents = [userInfo valueForKey:CTTabContentsUserInfoKey];
  921. NSInteger index = [[userInfo valueForKey:CTTabIndexUserInfoKey] intValue];
  922. [contents tabDidDetachFromBrowser:browser_ atIndex:index];
  923. if (contents.isActive)
  924. [self updateToolbarWithContents:nil shouldRestoreState:NO];
  925. }
  926. - (void)tabStripDidBecomeEmpty {
  927. [self close];
  928. }
  929. -(void)updateViewColor
  930. {
  931. self.rightStripView.layer.backgroundColor = [KMTabAppearance tabsViewBackgroundColor].CGColor;
  932. self.homeRightStripView.layer.backgroundColor = [KMTabAppearance tabsViewBackgroundColor].CGColor;
  933. self.window.backgroundColor = [KMTabAppearance tabsViewBackgroundColor];
  934. }
  935. @end
  936. #pragma mark -
  937. @implementation CTBrowserWindowController (FullScreen)
  938. #pragma mark Full Screen Mode
  939. - (void)contentViewDidResize:(NSNotification*)notification {
  940. [self layoutSubviews];
  941. }
  942. // Register or deregister for content view resize notifications. These
  943. // notifications are used while transitioning to fullscreen mode in Lion or
  944. // later. This method is safe to call on all OS versions.
  945. - (void)registerForContentViewResizeNotifications {
  946. [[NSNotificationCenter defaultCenter] addObserver:self
  947. selector:@selector(contentViewDidResize:)
  948. name:NSViewFrameDidChangeNotification
  949. object:[[self window] contentView]];
  950. }
  951. - (void)deregisterForContentViewResizeNotifications {
  952. [[NSNotificationCenter defaultCenter] removeObserver:self
  953. name:NSViewFrameDidChangeNotification
  954. object:[[self window] contentView]];
  955. }
  956. // On Lion, this method is called by either the Lion fullscreen button or the
  957. // "Enter Full Screen" menu item. On Snow Leopard, this function is never
  958. // called by the UI directly, but it provides the implementation for
  959. // |-setPresentationMode:|.
  960. - (void)setFullscreen:(BOOL)fullscreen {
  961. if (fullscreen == [self isFullscreen])
  962. return;
  963. enteredPresentationModeFromFullscreen_ = YES;
  964. [self.window toggleFullScreen:nil];
  965. }
  966. - (BOOL)isFullscreen {
  967. return ([[self window] styleMask] & NSFullScreenWindowMask) || enteringFullscreen_;
  968. }
  969. - (void)windowWillEnterFullScreen:(NSNotification*)notification {
  970. [self registerForContentViewResizeNotifications];
  971. // NSWindow* window = [self window];
  972. // savedRegularWindowFrame_ = [window frame];
  973. BOOL mode = [self shouldUsePresentationModeWhenEnteringFullscreen];
  974. // mode = mode || browser_->IsFullscreenForTabOrPending();
  975. enteringFullscreen_ = YES;
  976. [self setPresentationModeInternal:mode forceDropdown:NO];
  977. }
  978. - (void)windowDidEnterFullScreen:(NSNotification*)notification {
  979. [self deregisterForContentViewResizeNotifications];
  980. enteringFullscreen_ = NO;
  981. }
  982. - (void)windowWillExitFullScreen:(NSNotification*)notification {
  983. [self registerForContentViewResizeNotifications];
  984. [self setPresentationModeInternal:NO forceDropdown:NO];
  985. }
  986. - (void)windowDidExitFullScreen:(NSNotification*)notification {
  987. [self deregisterForContentViewResizeNotifications];
  988. }
  989. - (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
  990. [self deregisterForContentViewResizeNotifications];
  991. enteringFullscreen_ = NO;
  992. [self setPresentationModeInternal:NO forceDropdown:NO];
  993. // Force a relayout to try and get the window back into a reasonable state.
  994. [self layoutSubviews];
  995. }
  996. - (void)windowDidFailToExitFullScreen:(NSWindow*)window {
  997. [self deregisterForContentViewResizeNotifications];
  998. // Force a relayout to try and get the window back into a reasonable state.
  999. [self layoutSubviews];
  1000. }
  1001. #pragma mark -
  1002. #pragma mark Presentation Mode
  1003. - (BOOL)shouldShowPresentationModeToggle {
  1004. return [self isFullscreen];
  1005. }
  1006. - (void)createAndInstallPresentationModeToggleButton {
  1007. if (presentationModeToggleButton_)
  1008. return;
  1009. // TODO(rohitrao): Make this button prettier.
  1010. presentationModeToggleButton_ = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 25, 25)];
  1011. [presentationModeToggleButton_ setButtonType:NSMomentaryLightButton];
  1012. [presentationModeToggleButton_ setBezelStyle:NSRegularSquareBezelStyle];
  1013. [presentationModeToggleButton_ setBordered:NO];
  1014. [[presentationModeToggleButton_ cell] setHighlightsBy:NSContentsCellMask];
  1015. [[presentationModeToggleButton_ cell] setShowsStateBy:NSContentsCellMask];
  1016. [presentationModeToggleButton_ setImage:[NSImage imageNamed:NSImageNameIChatTheaterTemplate]];
  1017. [presentationModeToggleButton_ setTarget:self];
  1018. [presentationModeToggleButton_ setAction:@selector(togglePresentationModeForLionOrLater:)];
  1019. [[[[self window] contentView] superview] addSubview:presentationModeToggleButton_];
  1020. }
  1021. - (void)togglePresentationModeForLionOrLater:(id)sender {
  1022. // Called only by the presentation mode toggle button.
  1023. enteredPresentationModeFromFullscreen_ = YES;
  1024. // browser_->ExecuteCommand(IDC_PRESENTATION_MODE);
  1025. if ([self inPresentationMode])
  1026. [self exitPresentationMode];
  1027. else
  1028. [self enterPresentationMode];
  1029. // TODO: Post notification on WindowFullscreenStateChanged
  1030. }
  1031. // Adjust the UI when entering or leaving presentation mode. This method is
  1032. // safe to call on all OS versions.
  1033. - (void)adjustUIForPresentationMode:(BOOL)fullscreen {
  1034. // Create the floating bar backing view if necessary.
  1035. if (fullscreen && !floatingBarBackingView_ &&
  1036. ([self hasTabStrip] || [self hasToolbar])) {
  1037. floatingBarBackingView_ = [[CTFloatingBarBackingView alloc] initWithFrame:NSZeroRect];
  1038. [floatingBarBackingView_ setAutoresizingMask:(NSViewWidthSizable |
  1039. NSViewMinYMargin)];
  1040. }
  1041. }
  1042. - (BOOL)isBarVisibilityLockedForOwner:(id)owner {
  1043. DCHECK(owner);
  1044. DCHECK(barVisibilityLocks_);
  1045. return [barVisibilityLocks_ containsObject:owner];
  1046. }
  1047. - (void)enableBarVisibilityUpdates {
  1048. // Early escape if there's nothing to do.
  1049. if (barVisibilityUpdatesEnabled_)
  1050. return;
  1051. barVisibilityUpdatesEnabled_ = YES;
  1052. if ([barVisibilityLocks_ count])
  1053. [presentationModeController_ ensureOverlayShownWithAnimation:NO delay:NO];
  1054. else
  1055. [presentationModeController_ ensureOverlayHiddenWithAnimation:NO delay:NO];
  1056. }
  1057. - (void)disableBarVisibilityUpdates {
  1058. // Early escape if there's nothing to do.
  1059. if (!barVisibilityUpdatesEnabled_)
  1060. return;
  1061. barVisibilityUpdatesEnabled_ = NO;
  1062. [presentationModeController_ cancelAnimationAndTimers];
  1063. }
  1064. - (void)lockBarVisibilityForOwner:(id)owner
  1065. withAnimation:(BOOL)animate
  1066. delay:(BOOL)delay {
  1067. if (![self isBarVisibilityLockedForOwner:owner]) {
  1068. [barVisibilityLocks_ addObject:owner];
  1069. // If enabled, show the overlay if necessary (and if in presentation mode).
  1070. if (barVisibilityUpdatesEnabled_) {
  1071. [presentationModeController_ ensureOverlayShownWithAnimation:animate
  1072. delay:delay];
  1073. }
  1074. }
  1075. }
  1076. - (void)releaseBarVisibilityForOwner:(id)owner
  1077. withAnimation:(BOOL)animate
  1078. delay:(BOOL)delay {
  1079. if ([self isBarVisibilityLockedForOwner:owner]) {
  1080. [barVisibilityLocks_ removeObject:owner];
  1081. // If enabled, hide the overlay if necessary (and if in presentation mode).
  1082. if (barVisibilityUpdatesEnabled_ &&
  1083. ![barVisibilityLocks_ count]) {
  1084. [presentationModeController_ ensureOverlayHiddenWithAnimation:animate
  1085. delay:delay];
  1086. }
  1087. }
  1088. }
  1089. // On Lion, this function is called by either the presentation mode toggle
  1090. // button or the "Enter Presentation Mode" menu item. In the latter case, this
  1091. // function also triggers the Lion machinery to enter fullscreen mode as well as
  1092. // set presentation mode. On Snow Leopard, this function is called by the
  1093. // "Enter Presentation Mode" menu item, and triggering presentation mode always
  1094. // moves the user into fullscreen mode.
  1095. - (void)setPresentationMode:(BOOL)presentationMode {
  1096. if (presentationMode) {
  1097. BOOL fullscreen = [self isFullscreen];
  1098. [self setShouldUsePresentationModeWhenEnteringFullscreen:YES];
  1099. enteredPresentationModeFromFullscreen_ = fullscreen;
  1100. if (fullscreen) {
  1101. // If already in fullscreen mode, just toggle the presentation mode
  1102. // setting. Go through an elaborate dance to force the overlay to show,
  1103. // then animate out once the mouse moves away. This helps draw attention
  1104. // to the fact that the UI is in an overlay. Focus the tab contents
  1105. // because the omnibox is the most likely source of bar visibility locks,
  1106. // and taking focus away from the omnibox releases its lock.
  1107. [self lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
  1108. [self focusTabContents];
  1109. [self setPresentationModeInternal:YES forceDropdown:YES];
  1110. [self releaseBarVisibilityForOwner:self withAnimation:YES delay:YES];
  1111. } else {
  1112. // If not in fullscreen mode, trigger the Lion fullscreen mode machinery.
  1113. // Presentation mode will automatically be enabled in
  1114. // |-windowWillEnterFullScreen:|.
  1115. // NSWindow* window = [self window];
  1116. // if ([window isKindOfClass:[CTBrowser class]])
  1117. // [static_cast<FramedBrowserWindow*>(window) toggleSystemFullScreen];
  1118. [[self window] toggleFullScreen:nil];
  1119. }
  1120. } else {
  1121. if (enteredPresentationModeFromFullscreen_) {
  1122. // The window is currently in fullscreen mode, but the user is choosing to
  1123. // turn presentation mode off (choosing to always show the UI). Set the
  1124. // preference to ensure that presentation mode will stay off for the next
  1125. // window that goes fullscreen.
  1126. [self setShouldUsePresentationModeWhenEnteringFullscreen:NO];
  1127. [self setPresentationModeInternal:NO forceDropdown:NO];
  1128. } else {
  1129. // The user entered presentation mode directly from non-fullscreen mode
  1130. // using the "Enter Presentation Mode" menu item and is using that same
  1131. // menu item to exit presentation mode. In this case, exit fullscreen
  1132. // mode as well (using the Lion machinery).
  1133. // NSWindow* window = [self window];
  1134. // if ([window isKindOfClass:[FramedBrowserWindow class]])
  1135. // [static_cast<FramedBrowserWindow*>(window) toggleSystemFullScreen];
  1136. [[self window] toggleFullScreen:nil];
  1137. }
  1138. }
  1139. }
  1140. - (void)setPresentationModeInternal:(BOOL)presentationMode
  1141. forceDropdown:(BOOL)forceDropdown {
  1142. if (presentationMode == [self inPresentationMode])
  1143. return;
  1144. if (presentationMode) {
  1145. // BOOL showDropdown = forceDropdown || [self floatingBarHasFocus];
  1146. BOOL showDropdown = forceDropdown;
  1147. NSView* contentView = [[self window] contentView];
  1148. presentationModeController_ = [[CTPresentationModeController alloc]
  1149. initWithBrowserController:self];
  1150. [presentationModeController_ enterPresentationModeForContentView:contentView
  1151. showDropdown:showDropdown];
  1152. } else {
  1153. [presentationModeController_ exitPresentationMode];
  1154. presentationModeController_ = nil;
  1155. }
  1156. [self adjustUIForPresentationMode:presentationMode];
  1157. [self layoutSubviews];
  1158. }
  1159. - (void)enterPresentationMode {
  1160. [self setPresentationMode:YES];
  1161. }
  1162. - (void)exitPresentationMode {
  1163. [self setPresentationMode:NO];
  1164. }
  1165. - (BOOL)inPresentationMode {
  1166. return presentationModeController_ && [presentationModeController_ inPresentationMode];
  1167. }
  1168. - (CGFloat)floatingBarShownFraction {
  1169. return floatingBarShownFraction_;
  1170. }
  1171. - (void)setFloatingBarShownFraction:(CGFloat)fraction {
  1172. floatingBarShownFraction_ = fraction;
  1173. [self layoutSubviews];
  1174. }
  1175. @end
  1176. @implementation CTBrowserWindowController (KMExtensions)
  1177. - (void)appendBrowserWindowController:(__kindof CTBrowserWindowController *)browserWindowC toTabView:(NSView *)toTabView {
  1178. if (!browserWindowC || [self isEqual:browserWindowC]) {
  1179. return;
  1180. }
  1181. CTTabStripController *myTabStripC = self.tabStripController;
  1182. CTTabView *targetTabView = (CTTabView *)toTabView;
  1183. NSInteger toIndex = 0;
  1184. if (!toTabView || ![toTabView isKindOfClass:[CTTabView class]]) {
  1185. targetTabView = (CTTabView *)[myTabStripC activeTabView];
  1186. }
  1187. toIndex = [myTabStripC modelIndexForTabView:targetTabView] + 1;
  1188. toIndex = MAX(toIndex, 0);
  1189. CTTabStripController *tabStripC = browserWindowC.tabStripController;
  1190. NSInteger tabViewCount = [tabStripC viewsCount];
  1191. for (int i = 0; i < tabViewCount; i++) {
  1192. CTTabView *tabView = (CTTabView *)[tabStripC viewAtIndex:tabViewCount-i-1];
  1193. CTTabController *tabController = [tabView controller];
  1194. if (tabController.isHome && !tabController.isNewTab) {
  1195. continue;
  1196. }
  1197. [self km_moveTabView:tabView fromController:browserWindowC toIndex:toIndex];
  1198. }
  1199. }
  1200. - (void)km_moveTabView:(NSView*)view fromController:(CTTabWindowController*)dragController toIndex:(NSInteger)toIndex {
  1201. if (dragController) {
  1202. // Moving between windows. Figure out the CTTabContents to drop into our tab
  1203. // model from the source window's model.
  1204. BOOL isBrowser =
  1205. [dragController isKindOfClass:[CTBrowserWindowController class]];
  1206. assert(isBrowser);
  1207. if (!isBrowser) return;
  1208. CTBrowserWindowController* dragBWC = (CTBrowserWindowController*)dragController;
  1209. int index = [dragBWC->tabStripController_ modelIndexForTabView:view];
  1210. CTTabContents* contents =
  1211. [[dragBWC->browser_ tabStripModel] tabContentsAtIndex:index];
  1212. // The tab contents may have gone away if given a window.close() while it
  1213. // is being dragged. If so, bail, we've got nothing to drop.
  1214. if (!contents)
  1215. return;
  1216. // Convert |view|'s frame (which starts in the source tab strip's coordinate
  1217. // system) to the coordinate system of the destination tab strip. This needs
  1218. // to be done before being detached so the window transforms can be
  1219. // performed.
  1220. NSRect destinationFrame = [view frame];
  1221. NSPoint tabOrigin = destinationFrame.origin;
  1222. tabOrigin = [[dragController tabStripView] convertPoint:tabOrigin
  1223. toView:nil];
  1224. tabOrigin = [[view window] convertBaseToScreen:tabOrigin];
  1225. tabOrigin = [[self window] convertScreenToBase:tabOrigin];
  1226. tabOrigin = [[self tabStripView] convertPoint:tabOrigin fromView:nil];
  1227. if (tabOrigin.x <= [CTTabController homeTabWidth] + [CTTabStripController defaultIndentForControls]){
  1228. tabOrigin.x = [CTTabController homeTabWidth] + [CTTabStripController defaultIndentForControls];
  1229. }
  1230. destinationFrame.origin = tabOrigin;
  1231. // Before the tab is detached from its originating tab strip, store the
  1232. // pinned state so that it can be maintained between the windows.
  1233. BOOL isPinned = [[dragBWC->browser_ tabStripModel] isTabPinnedAtIndex:index];
  1234. // Now that we have enough information about the tab, we can remove it from
  1235. // the dragging window. We need to do this *before* we add it to the new
  1236. // window as this will remove the CTTabContents' delegate.
  1237. [dragController detachTabView:view];
  1238. // Deposit it into our model at the appropriate location (it already knows
  1239. // where it should go from tracking the drag). Doing this sets the tab's
  1240. // delegate to be the CTBrowser.
  1241. // [tabStripController_ dropTabContents:contents
  1242. // withFrame:destinationFrame
  1243. // asPinnedTab:isPinned];
  1244. [tabStripController_ km_dropTabContents:contents withFrame:destinationFrame asPinnedTab:isPinned toIndex:toIndex];
  1245. } else {
  1246. // Moving within a window.
  1247. int index = [tabStripController_ modelIndexForTabView:view];
  1248. [tabStripController_ moveTabFromIndex:index];
  1249. }
  1250. // Remove the placeholder since the drag is now complete.
  1251. [self removePlaceholder];
  1252. }
  1253. @end