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