Home | History | Annotate | Download | only in frame
      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       &params);
    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         &params);
    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