OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "chrome/browser/app_controller_mac.h" | 5 #import "chrome/browser/app_controller_mac.h" |
6 | 6 |
7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/file_path.h" | 9 #include "base/file_path.h" |
10 #include "base/mac/foundation_util.h" | 10 #include "base/mac/foundation_util.h" |
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
345 if (!BrowserList::HasBrowserWithProfile([self lastProfile])) { | 345 if (!BrowserList::HasBrowserWithProfile([self lastProfile])) { |
346 // As we're shutting down, we need to nuke the TabRestoreService, which | 346 // As we're shutting down, we need to nuke the TabRestoreService, which |
347 // will start the shutdown of the NavigationControllers and allow for | 347 // will start the shutdown of the NavigationControllers and allow for |
348 // proper shutdown. If we don't do this, Chrome won't shut down cleanly, | 348 // proper shutdown. If we don't do this, Chrome won't shut down cleanly, |
349 // and may end up crashing when some thread tries to use the IO thread (or | 349 // and may end up crashing when some thread tries to use the IO thread (or |
350 // another thread) that is no longer valid. | 350 // another thread) that is no longer valid. |
351 TabRestoreServiceFactory::ResetForProfile([self lastProfile]); | 351 TabRestoreServiceFactory::ResetForProfile([self lastProfile]); |
352 } | 352 } |
353 } | 353 } |
354 | 354 |
355 // Helper routine to get the window controller if the key window is a tabbed | |
356 // window, or nil if not. Examples of non-tabbed windows are "about" or | |
357 // "preferences". | |
358 - (TabWindowController*)keyWindowTabController { | |
359 NSWindowController* keyWindowController = | |
360 [[NSApp keyWindow] windowController]; | |
361 if ([keyWindowController isKindOfClass:[TabWindowController class]]) | |
362 return (TabWindowController*)keyWindowController; | |
363 | |
364 return nil; | |
365 } | |
366 | |
367 // Helper routine to get the window controller if the main window is a tabbed | |
368 // window, or nil if not. Examples of non-tabbed windows are "about" or | |
369 // "preferences". | |
370 - (TabWindowController*)mainWindowTabController { | |
371 NSWindowController* mainWindowController = | |
372 [[NSApp mainWindow] windowController]; | |
373 if ([mainWindowController isKindOfClass:[TabWindowController class]]) | |
374 return (TabWindowController*)mainWindowController; | |
375 | |
376 return nil; | |
377 } | |
378 | |
379 // If the window has a tab controller, make "close window" be cmd-shift-w, | 355 // If the window has a tab controller, make "close window" be cmd-shift-w, |
380 // otherwise leave it as the normal cmd-w. Capitalization of the key equivalent | 356 // otherwise leave it as the normal cmd-w. Capitalization of the key equivalent |
381 // affects whether the shift modifer is used. | 357 // affects whether the shift modifer is used. |
382 - (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)hasTabs { | 358 - (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)hasTabs { |
383 [closeWindowMenuItem_ setKeyEquivalent:(hasTabs ? @"W" : @"w")]; | 359 [closeWindowMenuItem_ setKeyEquivalent:(hasTabs ? @"W" : @"w")]; |
384 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask]; | 360 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask]; |
385 } | 361 } |
386 | 362 |
387 // If the window has a tab controller, make "close tab" take over cmd-w, | 363 // If the window has a tab controller, make "close tab" take over cmd-w, |
388 // otherwise it shouldn't have any key-equivalent because it should be disabled. | 364 // otherwise it shouldn't have any key-equivalent because it should be disabled. |
(...skipping 10 matching lines...) Expand all Loading... |
399 // Explicitly remove any command-key equivalents from the close tab/window | 375 // Explicitly remove any command-key equivalents from the close tab/window |
400 // menus so that nothing can go haywire if we get a user action during pending | 376 // menus so that nothing can go haywire if we get a user action during pending |
401 // updates. | 377 // updates. |
402 - (void)clearCloseMenuItemKeyEquivalents { | 378 - (void)clearCloseMenuItemKeyEquivalents { |
403 [closeTabMenuItem_ setKeyEquivalent:@""]; | 379 [closeTabMenuItem_ setKeyEquivalent:@""]; |
404 [closeTabMenuItem_ setKeyEquivalentModifierMask:0]; | 380 [closeTabMenuItem_ setKeyEquivalentModifierMask:0]; |
405 [closeWindowMenuItem_ setKeyEquivalent:@""]; | 381 [closeWindowMenuItem_ setKeyEquivalent:@""]; |
406 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0]; | 382 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0]; |
407 } | 383 } |
408 | 384 |
409 // See if we have a window with tabs open, and adjust the key equivalents for | 385 // See if the focused window window has tabs, and adjust the key equivalents for |
410 // Close Tab/Close Window accordingly. | 386 // Close Tab/Close Window accordingly. |
411 - (void)fixCloseMenuItemKeyEquivalents:(NSWindow*)window { | 387 - (void)fixCloseMenuItemKeyEquivalents { |
412 fileMenuUpdatePending_ = NO; | 388 fileMenuUpdatePending_ = NO; |
413 TabWindowController* tabController = [self keyWindowTabController]; | 389 |
414 if (!tabController && ![NSApp keyWindow]) { | 390 NSWindow* window = [NSApp keyWindow]; |
415 // There might be a small amount of time where there is no key window, | 391 NSWindow* mainWindow = [NSApp mainWindow]; |
416 // so just use our main browser window if there is one. | 392 if (!window || ([window parentWindow] == mainWindow)) { |
417 tabController = [self mainWindowTabController]; | 393 // If the key window is a child of the main window (e.g. a bubble), the main |
| 394 // window should be the one that handles the close menu item action. |
| 395 // Also, there might be a small amount of time where there is no key window; |
| 396 // in that case as well, just use our main browser window if there is one. |
| 397 // You might think that we should just always use the main window, but the |
| 398 // "About Chrome" window serves as a counterexample. |
| 399 window = mainWindow; |
418 } | 400 } |
419 BOOL hasTabs = !!tabController; | |
420 | 401 |
| 402 BOOL hasTabs = |
| 403 [[window windowController] isKindOfClass:[TabWindowController class]]; |
421 [self adjustCloseWindowMenuItemKeyEquivalent:hasTabs]; | 404 [self adjustCloseWindowMenuItemKeyEquivalent:hasTabs]; |
422 [self adjustCloseTabMenuItemKeyEquivalent:hasTabs]; | 405 [self adjustCloseTabMenuItemKeyEquivalent:hasTabs]; |
423 } | 406 } |
424 | 407 |
425 // Fix up the "close tab/close window" command-key equivalents. We do this | 408 // Fix up the "close tab/close window" command-key equivalents. We do this |
426 // after a delay to ensure that window layer state has been set by the time | 409 // after a delay to ensure that window layer state has been set by the time |
427 // we do the enabling. This should only be called on the main thread, code that | 410 // we do the enabling. This should only be called on the main thread, code that |
428 // calls this (even as a side-effect) from other threads needs to be fixed. | 411 // calls this (even as a side-effect) from other threads needs to be fixed. |
429 - (void)delayedFixCloseMenuItemKeyEquivalents:(NSNotification*)notify { | 412 - (void)delayedFixCloseMenuItemKeyEquivalents { |
430 DCHECK([NSThread isMainThread]); | 413 DCHECK([NSThread isMainThread]); |
431 if (!fileMenuUpdatePending_) { | 414 if (!fileMenuUpdatePending_) { |
432 // The OS prefers keypresses to timers, so it's possible that a cmd-w | 415 // The OS prefers keypresses to timers, so it's possible that a cmd-w |
433 // can sneak in before this timer fires. In order to prevent that from | 416 // can sneak in before this timer fires. In order to prevent that from |
434 // having any bad consequences, just clear the keys combos altogether. They | 417 // having any bad consequences, just clear the keys combos altogether. They |
435 // will be reset when the timer eventually fires. | 418 // will be reset when the timer eventually fires. |
436 if ([NSThread isMainThread]) { | 419 if ([NSThread isMainThread]) { |
437 fileMenuUpdatePending_ = YES; | 420 fileMenuUpdatePending_ = YES; |
438 [self clearCloseMenuItemKeyEquivalents]; | 421 [self clearCloseMenuItemKeyEquivalents]; |
439 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents:) | 422 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents) |
440 withObject:[notify object] | 423 withObject:nil |
441 afterDelay:0]; | 424 afterDelay:0]; |
442 } else { | 425 } else { |
443 // This shouldn't be happening, but if it does, force it to the main | 426 // This shouldn't be happening, but if it does, force it to the main |
444 // thread to avoid dropping the update. Don't mess with | 427 // thread to avoid dropping the update. Don't mess with |
445 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and | 428 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and |
446 // there could be a race between the selector finishing and setting the | 429 // there could be a race between the selector finishing and setting the |
447 // flag. | 430 // flag. |
448 [self | 431 [self |
449 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents:) | 432 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents) |
450 withObject:[notify object] | 433 withObject:nil |
451 waitUntilDone:NO]; | 434 waitUntilDone:NO]; |
452 } | 435 } |
453 } | 436 } |
454 } | 437 } |
455 | 438 |
456 // Called when we get a notification about the window layering changing to | 439 // Called when we get a notification about the window layering changing to |
457 // update the UI based on the new main window. | 440 // update the UI based on the new main window. |
458 - (void)windowLayeringDidChange:(NSNotification*)notify { | 441 - (void)windowLayeringDidChange:(NSNotification*)notify { |
459 [self delayedFixCloseMenuItemKeyEquivalents:notify]; | 442 [self delayedFixCloseMenuItemKeyEquivalents]; |
460 | 443 |
461 if ([notify name] == NSWindowDidResignKeyNotification) { | 444 if ([notify name] == NSWindowDidResignKeyNotification) { |
462 // If a window is closed, this notification is fired but |[NSApp keyWindow]| | 445 // If a window is closed, this notification is fired but |[NSApp keyWindow]| |
463 // returns nil regardless of whether any suitable candidates for the key | 446 // returns nil regardless of whether any suitable candidates for the key |
464 // window remain. It seems that the new key window for the app is not set | 447 // window remain. It seems that the new key window for the app is not set |
465 // until after this notification is fired, so a check is performed after the | 448 // until after this notification is fired, so a check is performed after the |
466 // run loop is allowed to spin. | 449 // run loop is allowed to spin. |
467 [self performSelector:@selector(checkForAnyKeyWindows) | 450 [self performSelector:@selector(checkForAnyKeyWindows) |
468 withObject:nil | 451 withObject:nil |
469 afterDelay:0.0]; | 452 afterDelay:0.0]; |
(...skipping 813 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1283 | 1266 |
1284 } // namespace browser | 1267 } // namespace browser |
1285 | 1268 |
1286 namespace app_controller_mac { | 1269 namespace app_controller_mac { |
1287 | 1270 |
1288 bool IsOpeningNewWindow() { | 1271 bool IsOpeningNewWindow() { |
1289 return g_is_opening_new_window; | 1272 return g_is_opening_new_window; |
1290 } | 1273 } |
1291 | 1274 |
1292 } // namespace app_controller_mac | 1275 } // namespace app_controller_mac |
OLD | NEW |