| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/ui/panels/panel_browser_window_cocoa.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "chrome/browser/ui/browser.h" | |
| 9 #include "chrome/browser/ui/browser_command_controller.h" | |
| 10 #include "chrome/browser/ui/browser_list.h" | |
| 11 #import "chrome/browser/ui/cocoa/browser_window_utils.h" | |
| 12 #include "chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h" | |
| 13 #include "chrome/browser/ui/cocoa/task_manager_mac.h" | |
| 14 #include "chrome/browser/ui/panels/panel.h" | |
| 15 #include "chrome/browser/ui/panels/panel_manager.h" | |
| 16 #import "chrome/browser/ui/panels/panel_titlebar_view_cocoa.h" | |
| 17 #import "chrome/browser/ui/panels/panel_utils_cocoa.h" | |
| 18 #import "chrome/browser/ui/panels/panel_window_controller_cocoa.h" | |
| 19 #include "chrome/browser/ui/tab_contents/tab_contents.h" | |
| 20 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 21 #include "chrome/common/chrome_notification_types.h" | |
| 22 #include "content/public/browser/native_web_keyboard_event.h" | |
| 23 #include "content/public/browser/notification_source.h" | |
| 24 | |
| 25 using content::NativeWebKeyboardEvent; | |
| 26 using content::WebContents; | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 // Use this instead of 0 for minimum size of a window when doing opening and | |
| 31 // closing animations, since OSX window manager does not like 0-sized windows | |
| 32 // (according to avi@). | |
| 33 const int kMinimumWindowSize = 1; | |
| 34 | |
| 35 } // namespace | |
| 36 | |
| 37 // This creates a shim window class, which in turn creates a Cocoa window | |
| 38 // controller which in turn creates actual NSWindow by loading a nib. | |
| 39 // Overall chain of ownership is: | |
| 40 // PanelWindowControllerCocoa -> PanelBrowserWindowCocoa -> Panel. | |
| 41 NativePanel* Panel::CreateNativePanel(Browser* browser, Panel* panel, | |
| 42 const gfx::Rect& bounds) { | |
| 43 return new PanelBrowserWindowCocoa(browser, panel, bounds); | |
| 44 } | |
| 45 | |
| 46 PanelBrowserWindowCocoa::PanelBrowserWindowCocoa(Browser* browser, | |
| 47 Panel* panel, | |
| 48 const gfx::Rect& bounds) | |
| 49 : browser_(browser), | |
| 50 panel_(panel), | |
| 51 bounds_(bounds), | |
| 52 is_shown_(false), | |
| 53 has_find_bar_(false), | |
| 54 attention_request_id_(0) { | |
| 55 controller_ = [[PanelWindowControllerCocoa alloc] initWithPanel:this]; | |
| 56 browser_->tab_strip_model()->AddObserver(this); | |
| 57 registrar_.Add( | |
| 58 this, | |
| 59 chrome::NOTIFICATION_PANEL_CHANGED_EXPANSION_STATE, | |
| 60 content::Source<Panel>(panel_.get())); | |
| 61 } | |
| 62 | |
| 63 PanelBrowserWindowCocoa::~PanelBrowserWindowCocoa() { | |
| 64 browser_->tab_strip_model()->RemoveObserver(this); | |
| 65 } | |
| 66 | |
| 67 bool PanelBrowserWindowCocoa::isClosed() { | |
| 68 return !controller_; | |
| 69 } | |
| 70 | |
| 71 void PanelBrowserWindowCocoa::ShowPanel() { | |
| 72 // The Browser associated with this browser window must become the active | |
| 73 // browser at the time |Show()| is called. This is the natural behaviour under | |
| 74 // Windows, but |-makeKeyAndOrderFront:| won't send |-windowDidBecomeKey:| | |
| 75 // until we return to the runloop. Therefore any calls to | |
| 76 // |BrowserList::GetLastActive()| (for example, in bookmark_util), will return | |
| 77 // the previous browser instead if we don't explicitly set it here. | |
| 78 BrowserList::SetLastActive(GetPanelBrowser()); | |
| 79 | |
| 80 ShowPanelInactive(); | |
| 81 ActivatePanel(); | |
| 82 } | |
| 83 | |
| 84 void PanelBrowserWindowCocoa::ShowPanelInactive() { | |
| 85 if (isClosed()) | |
| 86 return; | |
| 87 | |
| 88 // Browser calls this several times, meaning 'ensure it's shown'. | |
| 89 // Animations don't look good when repeated - hence this flag is needed. | |
| 90 if (is_shown_) { | |
| 91 return; | |
| 92 } | |
| 93 // A call to SetPanelBounds() before showing it is required to set | |
| 94 // the panel frame properly. | |
| 95 SetPanelBoundsInstantly(bounds_); | |
| 96 is_shown_ = true; | |
| 97 | |
| 98 NSRect finalFrame = cocoa_utils::ConvertRectToCocoaCoordinates(bounds_); | |
| 99 [controller_ revealAnimatedWithFrame:finalFrame]; | |
| 100 } | |
| 101 | |
| 102 gfx::Rect PanelBrowserWindowCocoa::GetPanelBounds() const { | |
| 103 return bounds_; | |
| 104 } | |
| 105 | |
| 106 // |bounds| is the platform-independent screen coordinates, with (0,0) at | |
| 107 // top-left of the primary screen. | |
| 108 void PanelBrowserWindowCocoa::SetPanelBounds(const gfx::Rect& bounds) { | |
| 109 setBoundsInternal(bounds, true); | |
| 110 } | |
| 111 | |
| 112 void PanelBrowserWindowCocoa::SetPanelBoundsInstantly(const gfx::Rect& bounds) { | |
| 113 setBoundsInternal(bounds, false); | |
| 114 } | |
| 115 | |
| 116 void PanelBrowserWindowCocoa::setBoundsInternal(const gfx::Rect& bounds, | |
| 117 bool animate) { | |
| 118 // We will call SetPanelBoundsInstantly() once before showing the panel | |
| 119 // and it should set the panel frame correctly. | |
| 120 if (bounds_ == bounds && is_shown_) | |
| 121 return; | |
| 122 | |
| 123 bounds_ = bounds; | |
| 124 | |
| 125 // Safely ignore calls to animate bounds before the panel is shown to | |
| 126 // prevent the window from loading prematurely. | |
| 127 if (animate && !is_shown_) | |
| 128 return; | |
| 129 | |
| 130 NSRect frame = cocoa_utils::ConvertRectToCocoaCoordinates(bounds); | |
| 131 [controller_ setPanelFrame:frame animate:animate]; | |
| 132 } | |
| 133 | |
| 134 void PanelBrowserWindowCocoa::ClosePanel() { | |
| 135 if (isClosed()) | |
| 136 return; | |
| 137 | |
| 138 NSWindow* window = [controller_ window]; | |
| 139 [window performClose:controller_]; | |
| 140 } | |
| 141 | |
| 142 void PanelBrowserWindowCocoa::ActivatePanel() { | |
| 143 if (!is_shown_) | |
| 144 return; | |
| 145 | |
| 146 [controller_ activate]; | |
| 147 } | |
| 148 | |
| 149 void PanelBrowserWindowCocoa::DeactivatePanel() { | |
| 150 [controller_ deactivate]; | |
| 151 } | |
| 152 | |
| 153 bool PanelBrowserWindowCocoa::IsPanelActive() const { | |
| 154 // TODO(dcheng): It seems like a lot of these methods can be called before | |
| 155 // our NSWindow is created. Do we really need to check in every one of these | |
| 156 // methods if the NSWindow is created, or is there a better way to | |
| 157 // gracefully handle this? | |
| 158 if (!is_shown_) | |
| 159 return false; | |
| 160 return [[controller_ window] isMainWindow]; | |
| 161 } | |
| 162 | |
| 163 void PanelBrowserWindowCocoa::PreventActivationByOS(bool prevent_activation) { | |
| 164 [controller_ preventBecomingKeyWindow:prevent_activation]; | |
| 165 return; | |
| 166 } | |
| 167 | |
| 168 gfx::NativeWindow PanelBrowserWindowCocoa::GetNativePanelHandle() { | |
| 169 return [controller_ window]; | |
| 170 } | |
| 171 | |
| 172 void PanelBrowserWindowCocoa::UpdatePanelTitleBar() { | |
| 173 if (!is_shown_) | |
| 174 return; | |
| 175 [controller_ updateTitleBar]; | |
| 176 } | |
| 177 | |
| 178 void PanelBrowserWindowCocoa::UpdatePanelLoadingAnimations( | |
| 179 bool should_animate) { | |
| 180 [controller_ updateThrobber:should_animate]; | |
| 181 } | |
| 182 | |
| 183 void PanelBrowserWindowCocoa::ShowTaskManagerForPanel() { | |
| 184 TaskManagerMac::Show(false); | |
| 185 } | |
| 186 | |
| 187 FindBar* PanelBrowserWindowCocoa::CreatePanelFindBar() { | |
| 188 DCHECK(!has_find_bar_) << "find bar should only be created once"; | |
| 189 has_find_bar_ = true; | |
| 190 | |
| 191 FindBarBridge* bridge = new FindBarBridge(); | |
| 192 [controller_ addFindBar:bridge->find_bar_cocoa_controller()]; | |
| 193 return bridge; | |
| 194 } | |
| 195 | |
| 196 void PanelBrowserWindowCocoa::NotifyPanelOnUserChangedTheme() { | |
| 197 NOTIMPLEMENTED(); | |
| 198 } | |
| 199 | |
| 200 void PanelBrowserWindowCocoa::PanelWebContentsFocused( | |
| 201 WebContents* contents) { | |
| 202 // TODO(jianli): to be implemented. | |
| 203 } | |
| 204 | |
| 205 void PanelBrowserWindowCocoa::PanelCut() { | |
| 206 // Nothing to do since we do not have panel-specific system menu on Mac. | |
| 207 } | |
| 208 | |
| 209 void PanelBrowserWindowCocoa::PanelCopy() { | |
| 210 // Nothing to do since we do not have panel-specific system menu on Mac. | |
| 211 } | |
| 212 | |
| 213 void PanelBrowserWindowCocoa::PanelPaste() { | |
| 214 // Nothing to do since we do not have panel-specific system menu on Mac. | |
| 215 } | |
| 216 | |
| 217 void PanelBrowserWindowCocoa::DrawAttention(bool draw_attention) { | |
| 218 DCHECK((panel_->attention_mode() & Panel::USE_PANEL_ATTENTION) != 0); | |
| 219 | |
| 220 PanelTitlebarViewCocoa* titlebar = [controller_ titlebarView]; | |
| 221 if (draw_attention) | |
| 222 [titlebar drawAttention]; | |
| 223 else | |
| 224 [titlebar stopDrawingAttention]; | |
| 225 | |
| 226 if ((panel_->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0) { | |
| 227 if (draw_attention) { | |
| 228 DCHECK(!attention_request_id_); | |
| 229 attention_request_id_ = | |
| 230 [NSApp requestUserAttention:NSInformationalRequest]; | |
| 231 } else { | |
| 232 [NSApp cancelUserAttentionRequest:attention_request_id_]; | |
| 233 attention_request_id_ = 0; | |
| 234 } | |
| 235 } | |
| 236 } | |
| 237 | |
| 238 bool PanelBrowserWindowCocoa::IsDrawingAttention() const { | |
| 239 PanelTitlebarViewCocoa* titlebar = [controller_ titlebarView]; | |
| 240 return [titlebar isDrawingAttention]; | |
| 241 } | |
| 242 | |
| 243 bool PanelBrowserWindowCocoa::PreHandlePanelKeyboardEvent( | |
| 244 const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { | |
| 245 if (![BrowserWindowUtils shouldHandleKeyboardEvent:event]) | |
| 246 return false; | |
| 247 | |
| 248 int id = [BrowserWindowUtils getCommandId:event]; | |
| 249 if (id == -1) | |
| 250 return false; | |
| 251 | |
| 252 if (GetPanelBrowser()->command_controller()->IsReservedCommandOrKey(id, | |
| 253 event)) { | |
| 254 return [BrowserWindowUtils handleKeyboardEvent:event.os_event | |
| 255 inWindow:GetNativePanelHandle()]; | |
| 256 } | |
| 257 | |
| 258 DCHECK(is_keyboard_shortcut); | |
| 259 *is_keyboard_shortcut = true; | |
| 260 return false; | |
| 261 } | |
| 262 | |
| 263 void PanelBrowserWindowCocoa::HandlePanelKeyboardEvent( | |
| 264 const NativeWebKeyboardEvent& event) { | |
| 265 if ([BrowserWindowUtils shouldHandleKeyboardEvent:event]) { | |
| 266 [BrowserWindowUtils handleKeyboardEvent:event.os_event | |
| 267 inWindow:GetNativePanelHandle()]; | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 void PanelBrowserWindowCocoa::FullScreenModeChanged( | |
| 272 bool is_full_screen) { | |
| 273 [controller_ fullScreenModeChanged:is_full_screen]; | |
| 274 } | |
| 275 | |
| 276 Browser* PanelBrowserWindowCocoa::GetPanelBrowser() const { | |
| 277 return browser_.get(); | |
| 278 } | |
| 279 | |
| 280 void PanelBrowserWindowCocoa::DestroyPanelBrowser() { | |
| 281 [controller_ close]; | |
| 282 } | |
| 283 | |
| 284 void PanelBrowserWindowCocoa::EnsurePanelFullyVisible() { | |
| 285 [controller_ ensureFullyVisible]; | |
| 286 } | |
| 287 | |
| 288 void PanelBrowserWindowCocoa::SetPanelAlwaysOnTop(bool on_top) { | |
| 289 [controller_ updateWindowLevel]; | |
| 290 } | |
| 291 | |
| 292 void PanelBrowserWindowCocoa::EnableResizeByMouse(bool enable) { | |
| 293 [controller_ enableResizeByMouse:enable]; | |
| 294 } | |
| 295 | |
| 296 void PanelBrowserWindowCocoa::UpdatePanelMinimizeRestoreButtonVisibility() { | |
| 297 [controller_ updateTitleBarMinimizeRestoreButtonVisibility]; | |
| 298 } | |
| 299 | |
| 300 Panel* PanelBrowserWindowCocoa::panel() const { | |
| 301 return panel_.get(); | |
| 302 } | |
| 303 | |
| 304 void PanelBrowserWindowCocoa::DidCloseNativeWindow() { | |
| 305 DCHECK(!isClosed()); | |
| 306 panel_->OnNativePanelClosed(); | |
| 307 controller_ = NULL; | |
| 308 } | |
| 309 | |
| 310 gfx::Size PanelBrowserWindowCocoa::WindowSizeFromContentSize( | |
| 311 const gfx::Size& content_size) const { | |
| 312 NSRect content = NSMakeRect(0, 0, | |
| 313 content_size.width(), content_size.height()); | |
| 314 NSRect frame = [controller_ frameRectForContentRect:content]; | |
| 315 return gfx::Size(NSWidth(frame), NSHeight(frame)); | |
| 316 } | |
| 317 | |
| 318 gfx::Size PanelBrowserWindowCocoa::ContentSizeFromWindowSize( | |
| 319 const gfx::Size& window_size) const { | |
| 320 NSRect frame = NSMakeRect(0, 0, window_size.width(), window_size.height()); | |
| 321 NSRect content = [controller_ contentRectForFrameRect:frame]; | |
| 322 return gfx::Size(NSWidth(content), NSHeight(content)); | |
| 323 } | |
| 324 | |
| 325 int PanelBrowserWindowCocoa::TitleOnlyHeight() const { | |
| 326 return [controller_ titlebarHeightInScreenCoordinates]; | |
| 327 } | |
| 328 | |
| 329 void PanelBrowserWindowCocoa::TabInsertedAt(TabContents* contents, | |
| 330 int index, | |
| 331 bool foreground) { | |
| 332 [controller_ webContentsInserted:contents->web_contents()]; | |
| 333 } | |
| 334 | |
| 335 void PanelBrowserWindowCocoa::TabDetachedAt(TabContents* contents, | |
| 336 int index) { | |
| 337 [controller_ webContentsDetached:contents->web_contents()]; | |
| 338 } | |
| 339 | |
| 340 void PanelBrowserWindowCocoa::Observe( | |
| 341 int type, | |
| 342 const content::NotificationSource& source, | |
| 343 const content::NotificationDetails& details) { | |
| 344 DCHECK_EQ(chrome::NOTIFICATION_PANEL_CHANGED_EXPANSION_STATE, type); | |
| 345 [controller_ updateWindowLevel]; | |
| 346 } | |
| 347 | |
| 348 void PanelBrowserWindowCocoa::PanelExpansionStateChanging( | |
| 349 Panel::ExpansionState old_state, Panel::ExpansionState new_state) { | |
| 350 } | |
| 351 | |
| 352 // NativePanelTesting implementation. | |
| 353 class NativePanelTestingCocoa : public NativePanelTesting { | |
| 354 public: | |
| 355 NativePanelTestingCocoa(NativePanel* native_panel); | |
| 356 virtual ~NativePanelTestingCocoa() { } | |
| 357 // Overridden from NativePanelTesting | |
| 358 virtual void PressLeftMouseButtonTitlebar( | |
| 359 const gfx::Point& mouse_location, panel::ClickModifier modifier) OVERRIDE; | |
| 360 virtual void ReleaseMouseButtonTitlebar( | |
| 361 panel::ClickModifier modifier) OVERRIDE; | |
| 362 virtual void DragTitlebar(const gfx::Point& mouse_location) OVERRIDE; | |
| 363 virtual void CancelDragTitlebar() OVERRIDE; | |
| 364 virtual void FinishDragTitlebar() OVERRIDE; | |
| 365 virtual bool VerifyDrawingAttention() const OVERRIDE; | |
| 366 virtual bool VerifyActiveState(bool is_active) OVERRIDE; | |
| 367 virtual bool IsWindowSizeKnown() const OVERRIDE; | |
| 368 virtual bool IsAnimatingBounds() const OVERRIDE; | |
| 369 virtual bool IsButtonVisible( | |
| 370 panel::TitlebarButtonType button_type) const OVERRIDE; | |
| 371 | |
| 372 private: | |
| 373 PanelTitlebarViewCocoa* titlebar() const; | |
| 374 // Weak, assumed always to outlive this test API object. | |
| 375 PanelBrowserWindowCocoa* native_panel_window_; | |
| 376 }; | |
| 377 | |
| 378 NativePanelTesting* PanelBrowserWindowCocoa::CreateNativePanelTesting() { | |
| 379 return new NativePanelTestingCocoa(this); | |
| 380 } | |
| 381 | |
| 382 NativePanelTestingCocoa::NativePanelTestingCocoa(NativePanel* native_panel) | |
| 383 : native_panel_window_(static_cast<PanelBrowserWindowCocoa*>(native_panel)) { | |
| 384 } | |
| 385 | |
| 386 PanelTitlebarViewCocoa* NativePanelTestingCocoa::titlebar() const { | |
| 387 return [native_panel_window_->controller_ titlebarView]; | |
| 388 } | |
| 389 | |
| 390 void NativePanelTestingCocoa::PressLeftMouseButtonTitlebar( | |
| 391 const gfx::Point& mouse_location, panel::ClickModifier modifier) { | |
| 392 // Convert from platform-indepedent screen coordinates to Cocoa's screen | |
| 393 // coordinates because PanelTitlebarViewCocoa method takes Cocoa's screen | |
| 394 // coordinates. | |
| 395 int modifierFlags = | |
| 396 (modifier == panel::APPLY_TO_ALL ? NSShiftKeyMask : 0); | |
| 397 [titlebar() pressLeftMouseButtonTitlebar: | |
| 398 cocoa_utils::ConvertPointToCocoaCoordinates(mouse_location) | |
| 399 modifiers:modifierFlags]; | |
| 400 } | |
| 401 | |
| 402 void NativePanelTestingCocoa::ReleaseMouseButtonTitlebar( | |
| 403 panel::ClickModifier modifier) { | |
| 404 int modifierFlags = | |
| 405 (modifier == panel::APPLY_TO_ALL ? NSShiftKeyMask : 0); | |
| 406 [titlebar() releaseLeftMouseButtonTitlebar:modifierFlags]; | |
| 407 } | |
| 408 | |
| 409 void NativePanelTestingCocoa::DragTitlebar(const gfx::Point& mouse_location) { | |
| 410 // Convert from platform-indepedent screen coordinates to Cocoa's screen | |
| 411 // coordinates because PanelTitlebarViewCocoa method takes Cocoa's screen | |
| 412 // coordinates. | |
| 413 [titlebar() dragTitlebar: | |
| 414 cocoa_utils::ConvertPointToCocoaCoordinates(mouse_location)]; | |
| 415 } | |
| 416 | |
| 417 void NativePanelTestingCocoa::CancelDragTitlebar() { | |
| 418 [titlebar() cancelDragTitlebar]; | |
| 419 } | |
| 420 | |
| 421 void NativePanelTestingCocoa::FinishDragTitlebar() { | |
| 422 [titlebar() finishDragTitlebar]; | |
| 423 } | |
| 424 | |
| 425 bool NativePanelTestingCocoa::VerifyDrawingAttention() const { | |
| 426 return [titlebar() isDrawingAttention]; | |
| 427 } | |
| 428 | |
| 429 bool NativePanelTestingCocoa::VerifyActiveState(bool is_active) { | |
| 430 // TODO(jianli): to be implemented. | |
| 431 return false; | |
| 432 } | |
| 433 | |
| 434 bool NativePanelTestingCocoa::IsWindowSizeKnown() const { | |
| 435 return true; | |
| 436 } | |
| 437 | |
| 438 bool NativePanelTestingCocoa::IsAnimatingBounds() const { | |
| 439 return [native_panel_window_->controller_ isAnimatingBounds]; | |
| 440 } | |
| 441 | |
| 442 bool NativePanelTestingCocoa::IsButtonVisible( | |
| 443 panel::TitlebarButtonType button_type) const { | |
| 444 switch (button_type) { | |
| 445 case panel::CLOSE_BUTTON: | |
| 446 return ![[titlebar() closeButton] isHidden]; | |
| 447 case panel::MINIMIZE_BUTTON: | |
| 448 return ![[titlebar() minimizeButton] isHidden]; | |
| 449 case panel::RESTORE_BUTTON: | |
| 450 return ![[titlebar() restoreButton] isHidden]; | |
| 451 default: | |
| 452 NOTREACHED(); | |
| 453 } | |
| 454 return false; | |
| 455 } | |
| OLD | NEW |