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