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      &params);
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        &params);
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