browser_view.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 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/chromeos/frame/browser_view.h" 6 7#include <algorithm> 8#include <string> 9#include <vector> 10 11#include "app/menus/simple_menu_model.h" 12#include "base/command_line.h" 13#include "chrome/app/chrome_command_ids.h" 14#include "chrome/browser/chromeos/frame/panel_browser_view.h" 15#include "chrome/browser/chromeos/status/input_method_menu_button.h" 16#include "chrome/browser/chromeos/status/network_menu_button.h" 17#include "chrome/browser/chromeos/status/status_area_button.h" 18#include "chrome/browser/chromeos/status/status_area_view.h" 19#include "chrome/browser/chromeos/view_ids.h" 20#include "chrome/browser/chromeos/wm_ipc.h" 21#include "chrome/browser/gtk/gtk_util.h" 22#include "chrome/browser/views/frame/browser_frame_gtk.h" 23#include "chrome/browser/views/frame/browser_view.h" 24#include "chrome/browser/views/frame/browser_view_layout.h" 25#include "chrome/browser/views/tabs/tab.h" 26#include "chrome/browser/views/tabs/tab_strip.h" 27#include "chrome/browser/views/theme_background.h" 28#include "chrome/browser/views/toolbar_view.h" 29#include "chrome/common/chrome_switches.h" 30#include "gfx/canvas.h" 31#include "grit/generated_resources.h" 32#include "third_party/cros/chromeos_wm_ipc_enums.h" 33#include "views/controls/button/button.h" 34#include "views/controls/button/image_button.h" 35#include "views/controls/menu/menu_2.h" 36#include "views/screen.h" 37#include "views/widget/root_view.h" 38#include "views/window/hit_test.h" 39#include "views/window/window.h" 40 41namespace { 42 43// Amount to offset the toolbar by when vertical tabs are enabled. 44const int kVerticalTabStripToolbarOffset = 2; 45 46} // namespace 47 48namespace chromeos { 49 50// LayoutManager for BrowserView, which layouts extra components such as 51// the status views as follows: 52// ____ __ __ 53// / \ \ \ [StatusArea] 54// 55class BrowserViewLayout : public ::BrowserViewLayout { 56 public: 57 BrowserViewLayout() : ::BrowserViewLayout() {} 58 virtual ~BrowserViewLayout() {} 59 60 ////////////////////////////////////////////////////////////////////////////// 61 // BrowserViewLayout overrides: 62 63 void Installed(views::View* host) { 64 status_area_ = NULL; 65 ::BrowserViewLayout::Installed(host); 66 } 67 68 void ViewAdded(views::View* host, 69 views::View* view) { 70 ::BrowserViewLayout::ViewAdded(host, view); 71 switch (view->GetID()) { 72 case VIEW_ID_STATUS_AREA: 73 status_area_ = static_cast<chromeos::StatusAreaView*>(view); 74 break; 75 } 76 } 77 78 // In the normal and the compact navigation bar mode, ChromeOS 79 // layouts compact navigation buttons and status views in the title 80 // area. See Layout 81 virtual int LayoutTabStrip() { 82 if (browser_view_->IsFullscreen() || !browser_view_->IsTabStripVisible()) { 83 status_area_->SetVisible(false); 84 tabstrip_->SetVisible(false); 85 tabstrip_->SetBounds(0, 0, 0, 0); 86 return 0; 87 } 88 89 gfx::Rect tabstrip_bounds( 90 browser_view_->frame()->GetBoundsForTabStrip(tabstrip_)); 91 gfx::Point tabstrip_origin = tabstrip_bounds.origin(); 92 views::View::ConvertPointToView(browser_view_->GetParent(), browser_view_, 93 &tabstrip_origin); 94 tabstrip_bounds.set_origin(tabstrip_origin); 95 return browser_view_->UseVerticalTabs() ? 96 LayoutTitlebarComponentsWithVerticalTabs(tabstrip_bounds) : 97 LayoutTitlebarComponents(tabstrip_bounds); 98 } 99 100 virtual int LayoutToolbar(int top) { 101 if (!browser_view_->IsFullscreen() && browser_view_->IsTabStripVisible() && 102 browser_view_->UseVerticalTabs()) { 103 // For vertical tabs the toolbar is positioned in 104 // LayoutTitlebarComponentsWithVerticalTabs. 105 return top; 106 } 107 return ::BrowserViewLayout::LayoutToolbar(top); 108 } 109 110 virtual bool IsPositionInWindowCaption(const gfx::Point& point) { 111 return ::BrowserViewLayout::IsPositionInWindowCaption(point) 112 && !IsPointInViewsInTitleArea(point); 113 } 114 115 virtual int NonClientHitTest(const gfx::Point& point) { 116 gfx::Point point_in_browser_view_coords(point); 117 views::View::ConvertPointToView( 118 browser_view_->GetParent(), browser_view_, 119 &point_in_browser_view_coords); 120 return IsPointInViewsInTitleArea(point_in_browser_view_coords) ? 121 HTCLIENT : ::BrowserViewLayout::NonClientHitTest(point); 122 } 123 124 private: 125 chromeos::BrowserView* chromeos_browser_view() { 126 return static_cast<chromeos::BrowserView*>(browser_view_); 127 } 128 129 // Tests if the point is on one of views that are within the 130 // considered title bar area of client view. 131 bool IsPointInViewsInTitleArea(const gfx::Point& point) 132 const { 133 gfx::Point point_in_status_area_coords(point); 134 views::View::ConvertPointToView(browser_view_, status_area_, 135 &point_in_status_area_coords); 136 if (status_area_->HitTest(point_in_status_area_coords)) 137 return true; 138 139 return false; 140 } 141 142 // Positions the titlebar, toolbar and tabstrip. This is 143 // used when side tabs are enabled. 144 int LayoutTitlebarComponentsWithVerticalTabs(const gfx::Rect& bounds) { 145 if (bounds.IsEmpty()) 146 return 0; 147 148 tabstrip_->SetVisible(true); 149 status_area_->SetVisible(true); 150 151 gfx::Size status_size = status_area_->GetPreferredSize(); 152 int status_height = status_size.height(); 153 154 int status_x = bounds.x(); 155 // Layout the status area. 156 status_area_->SetBounds(status_x, bounds.bottom() - status_height, 157 status_size.width(), status_height); 158 159 // The tabstrip's width is the bigger of it's preferred width and the width 160 // the status area. 161 int tabstrip_w = std::max(status_x + status_size.width(), 162 tabstrip_->GetPreferredSize().width()); 163 tabstrip_->SetBounds(bounds.x(), bounds.y(), tabstrip_w, 164 bounds.height() - status_height); 165 166 // The toolbar is promoted to the title for vertical tabs. 167 bool toolbar_visible = browser_view_->IsToolbarVisible(); 168 toolbar_->SetVisible(toolbar_visible); 169 int toolbar_height = 0; 170 if (toolbar_visible) 171 toolbar_height = toolbar_->GetPreferredSize().height(); 172 int tabstrip_max_x = tabstrip_->bounds().right(); 173 toolbar_->SetBounds(tabstrip_max_x, 174 bounds.y() - kVerticalTabStripToolbarOffset, 175 browser_view_->width() - tabstrip_max_x, 176 toolbar_height); 177 178 // Adjust the available bounds for other components. 179 gfx::Rect available_bounds = vertical_layout_rect(); 180 available_bounds.Inset(tabstrip_w, 0, 0, 0); 181 set_vertical_layout_rect(available_bounds); 182 183 return bounds.y() + toolbar_height; 184 } 185 186 // Lays out tabstrip and status area in the title bar area (given by 187 // |bounds|). 188 int LayoutTitlebarComponents(const gfx::Rect& bounds) { 189 if (bounds.IsEmpty()) 190 return 0; 191 192 tabstrip_->SetVisible(true); 193 status_area_->SetVisible(true); 194 195 // Layout status area after tab strip. 196 gfx::Size status_size = status_area_->GetPreferredSize(); 197 status_area_->SetBounds(bounds.right() - status_size.width(), bounds.y(), 198 status_size.width(), status_size.height()); 199 tabstrip_->SetBounds(bounds.x(), bounds.y(), 200 std::max(0, status_area_->bounds().x() - bounds.x()), 201 bounds.height()); 202 return bounds.bottom(); 203 } 204 205 chromeos::StatusAreaView* status_area_; 206 207 DISALLOW_COPY_AND_ASSIGN(BrowserViewLayout); 208}; 209 210BrowserView::BrowserView(Browser* browser) 211 : ::BrowserView(browser), 212 status_area_(NULL), 213 saved_focused_widget_(NULL) { 214} 215 216BrowserView::~BrowserView() { 217 if (toolbar()) 218 toolbar()->RemoveMenuListener(this); 219} 220 221//////////////////////////////////////////////////////////////////////////////// 222// BrowserView, ::BrowserView overrides: 223 224void BrowserView::Init() { 225 ::BrowserView::Init(); 226 status_area_ = new StatusAreaView(this); 227 status_area_->SetID(VIEW_ID_STATUS_AREA); 228 AddChildView(status_area_); 229 status_area_->Init(); 230 InitSystemMenu(); 231 232 // The ContextMenuController has to be set to a NonClientView but 233 // not to a NonClientFrameView because a TabStrip is not a child of 234 // a NonClientFrameView even though visually a TabStrip is over a 235 // NonClientFrameView. 236 BrowserFrameGtk* gtk_frame = static_cast<BrowserFrameGtk*>(frame()); 237 gtk_frame->GetNonClientView()->SetContextMenuController(this); 238 239 // Listen to wrench menu opens. 240 toolbar()->AddMenuListener(this); 241 242 // Make sure the window is set to the right type. 243 std::vector<int> params; 244 params.push_back(browser()->tab_count()); 245 params.push_back(browser()->selected_index()); 246 params.push_back(gtk_get_current_event_time()); 247 WmIpc::instance()->SetWindowType( 248 GTK_WIDGET(frame()->GetWindow()->GetNativeWindow()), 249 WM_IPC_WINDOW_CHROME_TOPLEVEL, 250 ¶ms); 251} 252 253void BrowserView::Show() { 254 bool was_visible = frame()->GetWindow()->IsVisible(); 255 ::BrowserView::Show(); 256 if (!was_visible) { 257 // Have to update the tab count and selected index to reflect reality. 258 std::vector<int> params; 259 params.push_back(browser()->tab_count()); 260 params.push_back(browser()->selected_index()); 261 WmIpc::instance()->SetWindowType( 262 GTK_WIDGET(frame()->GetWindow()->GetNativeWindow()), 263 WM_IPC_WINDOW_CHROME_TOPLEVEL, 264 ¶ms); 265 } 266} 267 268void BrowserView::FocusChromeOSStatus() { 269 SaveFocusedView(); 270 status_area_->SetPaneFocus(last_focused_view_storage_id(), NULL); 271} 272 273views::LayoutManager* BrowserView::CreateLayoutManager() const { 274 return new BrowserViewLayout(); 275} 276 277void BrowserView::ChildPreferredSizeChanged(View* child) { 278 Layout(); 279 SchedulePaint(); 280} 281 282bool BrowserView::GetSavedWindowBounds(gfx::Rect* bounds) const { 283 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeosFrame)) { 284 // Typically we don't request a full screen size. This means we'll request a 285 // non-full screen size, layout/paint at that size, then the window manager 286 // will snap us to full screen size. This results in an ugly 287 // resize/paint. To avoid this we always request a full screen size. 288 *bounds = views::Screen::GetMonitorWorkAreaNearestWindow( 289 GTK_WIDGET(GetWindow()->GetNativeWindow())); 290 return true; 291 } 292 return ::BrowserView::GetSavedWindowBounds(bounds); 293} 294 295void BrowserView::Cut() { 296 gtk_util::DoCut(this); 297} 298 299void BrowserView::Copy() { 300 gtk_util::DoCopy(this); 301} 302 303void BrowserView::Paste() { 304 gtk_util::DoPaste(this); 305} 306 307// views::ContextMenuController overrides. 308void BrowserView::ShowContextMenu(views::View* source, 309 const gfx::Point& p, 310 bool is_mouse_gesture) { 311 // Only show context menu if point is in unobscured parts of browser, i.e. 312 // if NonClientHitTest returns : 313 // - HTCAPTION: in title bar or unobscured part of tabstrip 314 // - HTNOWHERE: as the name implies. 315 gfx::Point point_in_parent_coords(p); 316 views::View::ConvertPointToView(NULL, GetParent(), &point_in_parent_coords); 317 int hit_test = NonClientHitTest(point_in_parent_coords); 318 if (hit_test == HTCAPTION || hit_test == HTNOWHERE) 319 system_menu_menu_->RunMenuAt(p, views::Menu2::ALIGN_TOPLEFT); 320} 321 322void BrowserView::OnMenuOpened() { 323 // Save the focused widget before wrench menu opens. 324 saved_focused_widget_ = gtk_window_get_focus(GetNativeHandle()); 325} 326 327// StatusAreaHost overrides. 328Profile* BrowserView::GetProfile() const { 329 return browser()->profile(); 330} 331 332gfx::NativeWindow BrowserView::GetNativeWindow() const { 333 return GetWindow()->GetNativeWindow(); 334} 335 336bool BrowserView::ShouldOpenButtonOptions( 337 const views::View* button_view) const { 338 return true; 339} 340 341void BrowserView::ExecuteBrowserCommand(int id) const { 342 browser()->ExecuteCommand(id); 343} 344 345void BrowserView::OpenButtonOptions(const views::View* button_view) { 346 if (button_view == status_area_->network_view()) { 347 browser()->OpenInternetOptionsDialog(); 348 } else if (button_view == status_area_->input_method_view()) { 349 browser()->OpenLanguageOptionsDialog(); 350 } else { 351 browser()->OpenSystemOptionsDialog(); 352 } 353} 354 355bool BrowserView::IsBrowserMode() const { 356 return true; 357} 358 359bool BrowserView::IsScreenLockerMode() const { 360 return false; 361} 362 363//////////////////////////////////////////////////////////////////////////////// 364// BrowserView protected: 365 366void BrowserView::GetAccessiblePanes( 367 std::vector<AccessiblePaneView*>* panes) { 368 ::BrowserView::GetAccessiblePanes(panes); 369 panes->push_back(status_area_); 370} 371 372//////////////////////////////////////////////////////////////////////////////// 373// BrowserView private: 374 375void BrowserView::InitSystemMenu() { 376 system_menu_contents_.reset(new menus::SimpleMenuModel(this)); 377 system_menu_contents_->AddItemWithStringId(IDC_RESTORE_TAB, 378 IDS_RESTORE_TAB); 379 system_menu_contents_->AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB); 380 system_menu_contents_->AddSeparator(); 381 system_menu_contents_->AddItemWithStringId(IDC_TASK_MANAGER, 382 IDS_TASK_MANAGER); 383 system_menu_menu_.reset(new views::Menu2(system_menu_contents_.get())); 384} 385 386} // namespace chromeos 387 388// static 389BrowserWindow* BrowserWindow::CreateBrowserWindow(Browser* browser) { 390 // Create a browser view for chromeos. 391 BrowserView* view; 392 if (browser->type() & Browser::TYPE_POPUP) 393 view = new chromeos::PanelBrowserView(browser); 394 else 395 view = new chromeos::BrowserView(browser); 396 BrowserFrame::Create(view, browser->profile()); 397 return view; 398} 399