• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
BrowserViewLayout()65   BrowserViewLayout() : ::BrowserViewLayout() {}
~BrowserViewLayout()66   virtual ~BrowserViewLayout() {}
67 
68   //////////////////////////////////////////////////////////////////////////////
69   // BrowserViewLayout overrides:
70 
Installed(views::View * host)71   void Installed(views::View* host) {
72     status_area_ = NULL;
73     ::BrowserViewLayout::Installed(host);
74   }
75 
ViewAdded(views::View * host,views::View * view)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
LayoutTabStrip()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 
LayoutToolbar(int top)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 
IsPositionInWindowCaption(const gfx::Point & point)118   virtual bool IsPositionInWindowCaption(const gfx::Point& point) {
119     return ::BrowserViewLayout::IsPositionInWindowCaption(point)
120         && !IsPointInViewsInTitleArea(point);
121   }
122 
NonClientHitTest(const gfx::Point & point)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:
chromeos_browser_view()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.
IsPointInViewsInTitleArea(const gfx::Point & point) const139   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.
LayoutTitlebarComponentsWithVerticalTabs(const gfx::Rect & bounds)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|).
LayoutTitlebarComponents(const gfx::Rect & 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 
BrowserView(Browser * browser)221 BrowserView::BrowserView(Browser* browser)
222     : ::BrowserView(browser),
223       status_area_(NULL),
224       saved_focused_widget_(NULL) {
225 }
226 
~BrowserView()227 BrowserView::~BrowserView() {
228   if (toolbar())
229     toolbar()->RemoveMenuListener(this);
230 }
231 
232 ////////////////////////////////////////////////////////////////////////////////
233 // BrowserView, ::BrowserView overrides:
234 
Init()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 
Show()265 void BrowserView::Show() {
266   ShowInternal(true);
267 }
268 
ShowInactive()269 void BrowserView::ShowInactive() {
270   ShowInternal(false);
271 }
272 
ShowInternal(bool is_active)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 
FocusChromeOSStatus()291 void BrowserView::FocusChromeOSStatus() {
292   SaveFocusedView();
293   status_area_->SetPaneFocus(last_focused_view_storage_id(), NULL);
294 }
295 
CreateLayoutManager() const296 views::LayoutManager* BrowserView::CreateLayoutManager() const {
297   return new BrowserViewLayout();
298 }
299 
ChildPreferredSizeChanged(View * child)300 void BrowserView::ChildPreferredSizeChanged(View* child) {
301   Layout();
302 }
303 
GetSavedWindowBounds(gfx::Rect * bounds) const304 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 
Cut()317 void BrowserView::Cut() {
318   gtk_util::DoCut(this);
319 }
320 
Copy()321 void BrowserView::Copy() {
322   gtk_util::DoCopy(this);
323 }
324 
Paste()325 void BrowserView::Paste() {
326   gtk_util::DoPaste(this);
327 }
328 
329 // views::ContextMenuController overrides.
ShowContextMenuForView(views::View * source,const gfx::Point & p,bool is_mouse_gesture)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 
OnMenuOpened()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.
GetProfile() const350 Profile* BrowserView::GetProfile() const {
351   return browser()->profile();
352 }
353 
GetNativeWindow() const354 gfx::NativeWindow BrowserView::GetNativeWindow() const {
355   return GetWindow()->GetNativeWindow();
356 }
357 
ShouldOpenButtonOptions(const views::View * button_view) const358 bool BrowserView::ShouldOpenButtonOptions(
359     const views::View* button_view) const {
360   return true;
361 }
362 
ExecuteBrowserCommand(int id) const363 void BrowserView::ExecuteBrowserCommand(int id) const {
364   browser()->ExecuteCommand(id);
365 }
366 
OpenButtonOptions(const views::View * button_view)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 
GetScreenMode() const377 StatusAreaHost::ScreenMode BrowserView::GetScreenMode() const {
378   return kBrowserMode;
379 }
380 
GetTextStyle() const381 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 
GetAccessiblePanes(std::vector<AccessiblePaneView * > * panes)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 
InitSystemMenu()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
CreateBrowserWindow(Browser * browser)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