15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab_strip.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windowsx.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <algorithm>
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <iterator>
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/compiler_specific.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
19868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/defaults.h"
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/host_desktop.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/view_ids.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/tabs/stacked_tab_strip_layout.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab_drag_controller.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab_strip_observer.h"
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/ui/views/touch_uma/touch_uma.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/user_metrics.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/theme_resources.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/accessibility/accessible_view_state.h"
3490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "ui/base/default_theme_provider.h"
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/dragdrop/drag_drop_types.h"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/layout.h"
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/models/list_selection_model.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
40d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/animation_container.h"
41d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/throb_animation.h"
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
4368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ui/gfx/display.h"
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_skia.h"
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/image/image_skia_operations.h"
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/path.h"
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/rect_conversions.h"
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/screen.h"
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/size.h"
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/image_view.h"
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/mouse_watcher_view_host.h"
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/rect_based_targeting_utils.h"
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/view_model_utils.h"
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/root_view.h"
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/window/non_client_view.h"
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
59d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/win/hwnd_util.h"
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/monitor_win.h"
614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "ui/views/win/hwnd_util.h"
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "win8/util/win8_util.h"
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::UserMetricsAction;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using ui::DropTargetEvent;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kTabStripAnimationVSlop = 40;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Inactive tabs in a native frame are slightly transparent.
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kNativeFrameInactiveTabAlpha = 200;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// If there are multiple tabs selected then make non-selected inactive tabs
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// even more transparent.
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kNativeFrameInactiveTabAlphaMultiSelection = 150;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Alpha applied to all elements save the selected tabs.
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const int kInactiveTabAndNewTabButtonAlphaAsh = 230;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kInactiveTabAndNewTabButtonAlpha = 255;
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Inverse ratio of the width of a tab edge to the width of the tab. When
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// hovering over the left or right edge of a tab, the drop indicator will
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// point between tabs.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kTabEdgeRatioInverse = 4;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Size of the drop indicator.
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static int drop_indicator_width;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static int drop_indicator_height;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static inline int Round(double x) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Why oh why is this not in a standard header?
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<int>(floor(x + 0.5));
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Max number of stacked tabs.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMaxStackedCount = 4;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Padding between stacked tabs.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kStackedPadding = 6;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// See UpdateLayoutTypeFromMouseEvent() for a description of these.
1028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#if !defined(USE_ASH)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kMouseMoveTimeMS = 200;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kMouseMoveCountBeforeConsiderReal = 3;
1058bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#endif
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Amount of time we delay before resizing after a close from a touch.
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kTouchResizeLayoutTimeMS = 2000;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Horizontal offset for the new tab button to bring it closer to the
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// rightmost tab.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int newtab_button_h_offset() {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int value = -1;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value == -1) {
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (ui::GetDisplayLayout()) {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_DESKTOP:
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = -11;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_TOUCH:
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = -13;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Vertical offset for the new tab button to bring it closer to the
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// rightmost tab.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int newtab_button_v_offset() {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int value = -1;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value == -1) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (ui::GetDisplayLayout()) {
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_DESKTOP:
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 7;
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_TOUCH:
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 8;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value;
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Amount the left edge of a tab is offset from the rectangle of the tab's
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// favicon/title/close box.  Related to the width of IDR_TAB_ACTIVE_LEFT.
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Affects the size of the "V" between adjacent tabs.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int tab_h_offset() {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int value = -1;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value == -1) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (ui::GetDisplayLayout()) {
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_DESKTOP:
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = -26;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_TOUCH:
159868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        value = -34;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The size of the new tab button must be hardcoded because we need to be
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// able to lay it out before we are able to get its image from the
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ui::ThemeProvider.  It also makes sense to do this, because the size of the
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// new tab button should not need to be calculated dynamically.
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int newtab_button_asset_width() {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int value = -1;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value == -1) {
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (ui::GetDisplayLayout()) {
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_DESKTOP:
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 34;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_TOUCH:
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 46;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int newtab_button_asset_height() {
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int value = -1;
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value == -1) {
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (ui::GetDisplayLayout()) {
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_DESKTOP:
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 18;
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_TOUCH:
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 24;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Amount to adjust the clip by when the tab is stacked before the active index.
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int stacked_tab_left_clip() {
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int value = -1;
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value == -1) {
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (ui::GetDisplayLayout()) {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_DESKTOP:
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 20;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_TOUCH:
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 26;
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Amount to adjust the clip by when the tab is stacked after the active index.
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int stacked_tab_right_clip() {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static int value = -1;
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value == -1) {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (ui::GetDisplayLayout()) {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_DESKTOP:
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 20;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case ui::LAYOUT_TOUCH:
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        value = 26;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED();
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return value;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)base::string16 GetClipboardText() {
243f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!ui::Clipboard::IsSupportedClipboardType(ui::CLIPBOARD_TYPE_SELECTION))
244f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return base::string16();
245f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
246f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CHECK(clipboard);
247f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  base::string16 clipboard_text;
248f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  clipboard->ReadText(ui::CLIPBOARD_TYPE_SELECTION, &clipboard_text);
249f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return clipboard_text;
250f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
251f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Animation delegate used when a dragged tab is released. When done sets the
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// dragging state to false.
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ResetDraggingStateDelegate
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : public views::BoundsAnimator::OwnedAnimationDelegate {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  explicit ResetDraggingStateDelegate(Tab* tab) : tab_(tab) {
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
260d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_->set_dragging(false);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
264d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE {
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_->set_dragging(false);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Tab* tab_;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ResetDraggingStateDelegate);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// If |dest| contains the point |point_in_source| the event handler from |dest|
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// is returned. Otherwise NULL is returned.
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)views::View* ConvertPointToViewAndGetEventHandler(
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::View* source,
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::View* dest,
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Point& point_in_source) {
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point dest_point(point_in_source);
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View::ConvertPointToTarget(source, dest, &dest_point);
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return dest->HitTestPoint(dest_point) ?
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dest->GetEventHandlerForPoint(dest_point) : NULL;
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
286c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Gets a tooltip handler for |point_in_source| from |dest|. Note that |dest|
287c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// should return NULL if it does not contain the point.
288c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)views::View* ConvertPointToViewAndGetTooltipHandler(
289c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    views::View* source,
290c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    views::View* dest,
291c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const gfx::Point& point_in_source) {
292c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Point dest_point(point_in_source);
293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  views::View::ConvertPointToTarget(source, dest, &dest_point);
294c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return dest->GetTooltipHandlerForPoint(dest_point);
295c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
296c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TabDragController::EventSource EventSourceFromEvent(
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ui::LocatedEvent& event) {
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return event.IsGestureEvent() ? TabDragController::EVENT_SOURCE_TOUCH :
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      TabDragController::EVENT_SOURCE_MOUSE;
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// NewTabButton
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  A subclass of button that hit-tests to the shape of the new tab button and
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  does custom drawing.
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class NewTabButton : public views::ImageButton {
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NewTabButton(TabStrip* tab_strip, views::ButtonListener* listener);
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~NewTabButton();
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set the background offset used to match the background image to the frame
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // image.
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void set_background_offset(const gfx::Point& offset) {
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    background_offset_ = offset;
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected:
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Overridden from views::View:
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual bool HasHitTestMask() const OVERRIDE;
3250f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  virtual void GetHitTestMask(HitTestSource source,
3260f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)                              gfx::Path* path) const OVERRIDE;
3274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#if defined(OS_WIN)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Overridden from ui::EventHandler:
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ShouldUseNativeFrame() const;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::ImageSkia GetBackgroundImage(views::CustomButton::ButtonState state,
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    ui::ScaleFactor scale_factor) const;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::ImageSkia GetImageForState(views::CustomButton::ButtonState state,
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  ui::ScaleFactor scale_factor) const;
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::ImageSkia GetImageForScale(ui::ScaleFactor scale_factor) const;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Tab strip that contains this button.
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabStrip* tab_strip_;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The offset used to paint the background image.
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point background_offset_;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // were we destroyed?
35090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  bool* destroyed_;
35190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(NewTabButton);
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NewTabButton::NewTabButton(TabStrip* tab_strip, views::ButtonListener* listener)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : views::ImageButton(listener),
35790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      tab_strip_(tab_strip),
35890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      destroyed_(NULL) {
359f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
360f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_triggerable_event_flags(triggerable_event_flags() |
361f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                              ui::EF_MIDDLE_MOUSE_BUTTON);
362f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#endif
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NewTabButton::~NewTabButton() {
36690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (destroyed_)
36790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    *destroyed_ = true;
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool NewTabButton::HasHitTestMask() const {
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When the button is sized to the top of the tab strip we want the user to
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // be able to click on complete bounds, and so don't return a custom hit
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mask.
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !tab_strip_->SizeTabButtonToTopOfTabStrip();
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3770f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)void NewTabButton::GetHitTestMask(HitTestSource source, gfx::Path* path) const {
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(path);
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SkScalar w = SkIntToScalar(width());
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SkScalar v_offset = SkIntToScalar(newtab_button_v_offset());
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // These values are defined by the shape of the new tab image. Should that
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // image ever change, these values will need to be updated. They're so
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // custom it's not really worth defining constants for.
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // These values are correct for regular and USE_ASH versions of the image.
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  path->moveTo(0, v_offset + 1);
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  path->lineTo(w - 7, v_offset + 1);
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  path->lineTo(w - 4, v_offset + 4);
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  path->lineTo(w, v_offset + 16);
3912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  path->lineTo(w - 1, v_offset + 17);
3922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  path->lineTo(7, v_offset + 17);
3932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  path->lineTo(4, v_offset + 13);
3942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  path->lineTo(0, v_offset + 1);
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  path->close();
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#if defined(OS_WIN)
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NewTabButton::OnMouseReleased(const ui::MouseEvent& event) {
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (event.IsOnlyRightMouseButton()) {
401c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    gfx::Point point = event.location();
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::View::ConvertPointToScreen(this, &point);
40390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    bool destroyed = false;
40490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    destroyed_ = &destroyed;
4054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    gfx::ShowSystemMenuAtPoint(views::HWNDForView(this), point);
4067d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if (destroyed)
4077d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      return;
4087d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
4097d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    destroyed_ = NULL;
4107d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    SetState(views::CustomButton::STATE_NORMAL);
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::ImageButton::OnMouseReleased(event);
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NewTabButton::OnPaint(gfx::Canvas* canvas) {
41868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::ImageSkia image =
41968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      GetImageForScale(ui::GetSupportedScaleFactor(canvas->image_scale()));
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas->DrawImageInt(image, 0, height() - image.height());
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void NewTabButton::OnGestureEvent(ui::GestureEvent* event) {
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Consume all gesture events here so that the parent (Tab) does not
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // start consuming gestures.
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::ImageButton::OnGestureEvent(event);
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event->SetHandled();
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool NewTabButton::ShouldUseNativeFrame() const {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetWidget() &&
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetWidget()->GetTopLevelWidget()->ShouldUseNativeFrame();
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::ImageSkia NewTabButton::GetBackgroundImage(
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::CustomButton::ButtonState state,
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ui::ScaleFactor scale_factor) const {
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int background_id = 0;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ShouldUseNativeFrame()) {
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    background_id = IDR_THEME_TAB_BACKGROUND_V;
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (tab_strip_->controller()->IsIncognito()) {
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    background_id = IDR_THEME_TAB_BACKGROUND_INCOGNITO;
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (win8::IsSingleWindowMetroMode()) {
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    background_id = IDR_THEME_TAB_BACKGROUND_V;
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    background_id = IDR_THEME_TAB_BACKGROUND;
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int alpha = 0;
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (state) {
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case views::CustomButton::STATE_NORMAL:
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case views::CustomButton::STATE_HOVERED:
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      alpha = ShouldUseNativeFrame() ? kNativeFrameInactiveTabAlpha : 255;
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case views::CustomButton::STATE_PRESSED:
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      alpha = 145;
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::ImageSkia* mask =
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetThemeProvider()->GetImageSkiaNamed(IDR_NEWTAB_BUTTON_MASK);
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int height = mask->height();
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int width = mask->width();
46968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  float scale = ui::GetImageScale(scale_factor);
4702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The canvas and mask has to use the same scale factor.
47168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  if (!mask->HasRepresentation(scale))
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scale_factor = ui::SCALE_FACTOR_100P;
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
47468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Canvas canvas(gfx::Size(width, height), scale, false);
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For custom images the background starts at the top of the tab strip.
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Otherwise the background starts at the top of the frame.
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::ImageSkia* background =
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      GetThemeProvider()->GetImageSkiaNamed(background_id);
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int offset_y = GetThemeProvider()->HasCustomImage(background_id) ?
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      0 : background_offset_.y();
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The new tab background is mirrored in RTL mode, but the theme background
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // should never be mirrored. Mirror it here to compensate.
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float x_scale = 1.0f;
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int x = GetMirroredX() + background_offset_.x();
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::i18n::IsRTL()) {
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    x_scale = -1.0f;
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Offset by |width| such that the same region is painted as if there was no
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // flip.
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    x += width;
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas.TileImageInt(*background, x, newtab_button_v_offset() + offset_y,
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      x_scale, 1.0f, 0, 0, width, height);
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (alpha != 255) {
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SkPaint paint;
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    paint.setColor(SkColorSetARGB(alpha, 255, 255, 255));
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    paint.setStyle(SkPaint::kFill_Style);
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas.DrawRect(gfx::Rect(0, 0, width, height), paint);
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // White highlight on hover.
5052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (state == views::CustomButton::STATE_HOVERED)
5062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    canvas.FillRect(GetLocalBounds(), SkColorSetARGB(64, 255, 255, 255));
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::ImageSkiaOperations::CreateMaskedImage(
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::ImageSkia(canvas.ExtractImageRep()), *mask);
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::ImageSkia NewTabButton::GetImageForState(
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::CustomButton::ButtonState state,
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ui::ScaleFactor scale_factor) const {
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int overlay_id = state == views::CustomButton::STATE_PRESSED ?
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        IDR_NEWTAB_BUTTON_P : IDR_NEWTAB_BUTTON;
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::ImageSkia* overlay = GetThemeProvider()->GetImageSkiaNamed(overlay_id);
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Canvas canvas(
52068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      gfx::Size(overlay->width(), overlay->height()),
52168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      ui::GetImageScale(scale_factor),
52268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      false);
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas.DrawImageInt(GetBackgroundImage(state, scale_factor), 0, 0);
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Draw the button border with a slight alpha.
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int kNativeFrameOverlayAlpha = 178;
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int kOpaqueFrameOverlayAlpha = 230;
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint8 alpha = ShouldUseNativeFrame() ?
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kNativeFrameOverlayAlpha : kOpaqueFrameOverlayAlpha;
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  canvas.DrawImageInt(*overlay, 0, 0, alpha);
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::ImageSkia(canvas.ExtractImageRep());
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::ImageSkia NewTabButton::GetImageForScale(
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ui::ScaleFactor scale_factor) const {
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!hover_animation_->is_animating())
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return GetImageForState(state(), scale_factor);
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::ImageSkiaOperations::CreateBlendedImage(
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetImageForState(views::CustomButton::STATE_NORMAL, scale_factor),
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GetImageForState(views::CustomButton::STATE_HOVERED, scale_factor),
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      hover_animation_->GetCurrentValue());
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabStrip::RemoveTabDelegate
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// AnimationDelegate used when removing a tab. Does the necessary cleanup when
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// done.
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class TabStrip::RemoveTabDelegate
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : public views::BoundsAnimator::OwnedAnimationDelegate {
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
5532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  RemoveTabDelegate(TabStrip* tab_strip, Tab* tab);
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
556d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE;
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void CompleteRemove();
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When the animation completes, we send the Container a message to simulate
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a mouse moved event at the current mouse position. This tickles the Tab
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the mouse is currently over to show the "hot" state of the close button.
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void HighlightCloseButton();
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabStrip* tabstrip_;
5672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Tab* tab_;
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(RemoveTabDelegate);
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabStrip::RemoveTabDelegate::RemoveTabDelegate(TabStrip* tab_strip,
5732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                               Tab* tab)
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : tabstrip_(tab_strip),
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab_(tab) {
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::RemoveTabDelegate::AnimationEnded(
579d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const gfx::Animation* animation) {
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CompleteRemove();
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::RemoveTabDelegate::AnimationCanceled(
584d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const gfx::Animation* animation) {
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CompleteRemove();
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::RemoveTabDelegate::CompleteRemove() {
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(tab_->closing());
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tabstrip_->RemoveAndDeleteTab(tab_);
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HighlightCloseButton();
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::RemoveTabDelegate::HighlightCloseButton() {
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tabstrip_->IsDragSessionActive() ||
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !tabstrip_->ShouldHighlightCloseButtonAfterRemove()) {
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This function is not required (and indeed may crash!) for removes
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // spawned by non-mouse closes and drag-detaches.
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget* widget = tabstrip_->GetWidget();
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This can be null during shutdown. See http://crbug.com/42737.
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!widget)
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
607c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  widget->SynthesizeMouseMoveEvent();
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabStrip, public:
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char TabStrip::kViewClassName[] = "TabStrip";
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int TabStrip::kMiniToNonMiniGap = 3;
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabStrip::TabStrip(TabStripController* controller)
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : controller_(controller),
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      newtab_button_(NULL),
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_unselected_width_(Tab::GetStandardSize().width()),
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      current_selected_width_(Tab::GetStandardSize().width()),
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      available_width_for_tabs_(-1),
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      in_tab_close_(false),
626d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      animation_container_(new gfx::AnimationContainer()),
627c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      bounds_animator_(this),
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      layout_type_(TAB_STRIP_LAYOUT_SHRINK),
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      adjust_layout_(false),
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reset_to_shrink_on_exit_(false),
6312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      mouse_move_count_(0),
6322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      immersive_style_(false) {
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Init();
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabStrip::~TabStrip() {
6372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(TabStripObserver, observers_,
6382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    TabStripDeleted(this));
6392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The animations may reference the tabs. Shut down the animation before we
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // delete the tabs.
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StopAnimating(false);
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DestroyDragController();
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure we unhook ourselves as a message loop observer so that we don't
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // crash in the case where the user closes the window after closing a tab
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // but before moving the mouse.
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveMessageLoopObserver();
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The children (tabs) may callback to us from their destructor. Delete them
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // so that if they call back we aren't in a weird state.
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveAllChildViews(true);
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::AddObserver(TabStripObserver* observer) {
6572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  observers_.AddObserver(observer);
6582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::RemoveObserver(TabStripObserver* observer) {
6612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  observers_.RemoveObserver(observer);
6622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::SetLayoutType(TabStripLayoutType layout_type,
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             bool adjust_layout) {
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  adjust_layout_ = adjust_layout;
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (layout_type == layout_type_)
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int active_index = controller_->GetActiveIndex();
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int active_center = 0;
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (active_index != -1) {
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    active_center = ideal_bounds(active_index).x() +
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ideal_bounds(active_index).width() / 2;
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  layout_type_ = layout_type;
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetResetToShrinkOnExit(false);
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SwapLayoutIfNecessary();
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When transitioning to stacked try to keep the active tab centered.
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (touch_layout_.get() && active_index != -1) {
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_->SetActiveTabLocation(
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        active_center - ideal_bounds(active_index).width() / 2);
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AnimateToIdealBounds();
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Rect TabStrip::GetNewTabButtonBounds() {
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return newtab_button_->bounds();
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::SizeTabButtonToTopOfTabStrip() {
6922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Extend the button to the screen edge in maximized and immersive fullscreen.
6932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  views::Widget* widget = GetWidget();
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return browser_defaults::kSizeTabButtonToTopOfTabStrip ||
6952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (widget && (widget->IsMaximized() || widget->IsFullscreen()));
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StartHighlight(int model_index) {
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab_at(model_index)->StartPulse();
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StopAllHighlighting() {
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < tab_count(); ++i)
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_at(i)->StopPulse();
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::AddTabAt(int model_index,
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        const TabRendererData& data,
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        bool is_active) {
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Stop dragging when a new tab is added and dragging a window. Doing
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // otherwise results in a confusing state if the user attempts to reattach. We
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // could allow this and make TabDragController update itself during the add,
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // but this comes up infrequently enough that it's not work the complexity.
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drag_controller_.get() && !drag_controller_->is_mutating() &&
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drag_controller_->is_dragging_window()) {
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EndDrag(END_DRAG_COMPLETE);
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Tab* tab = CreateTab();
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab->SetData(data);
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateTabsClosingMap(model_index, 1);
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tabs_.Add(tab, model_index);
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddChildView(tab);
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (touch_layout_.get()) {
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GenerateIdealBoundsForMiniTabs(NULL);
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int add_types = 0;
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (data.mini)
7282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      add_types |= StackedTabStripLayout::kAddTypeMini;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (is_active)
7302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      add_types |= StackedTabStripLayout::kAddTypeActive;
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_->AddTab(model_index, add_types, GetStartXForNormalTabs());
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't animate the first tab, it looks weird, and don't animate anything
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // if the containing window isn't visible yet.
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_count() > 1 && GetWidget() && GetWidget()->IsVisible())
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartInsertTabAnimation(model_index);
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DoLayout();
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SwapLayoutIfNecessary();
7422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(TabStripObserver, observers_,
7442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    TabStripAddedTabAt(this, model_index));
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::MoveTab(int from_model_index,
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       int to_model_index,
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       const TabRendererData& data) {
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(tabs_.view_size(), 0);
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Tab* last_tab = tab_at(tab_count() - 1);
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab_at(from_model_index)->SetData(data);
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (touch_layout_.get()) {
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tabs_.MoveViewOnly(from_model_index, to_model_index);
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int mini_count = 0;
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GenerateIdealBoundsForMiniTabs(&mini_count);
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_->MoveTab(
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        from_model_index, to_model_index, controller_->GetActiveIndex(),
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        GetStartXForNormalTabs(), mini_count);
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tabs_.Move(from_model_index, to_model_index);
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StartMoveTabAnimation();
7645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (TabDragController::IsAttachedTo(this) &&
7655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (last_tab != tab_at(tab_count() - 1) || last_tab->dragging())) {
7665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    newtab_button_->SetVisible(false);
7675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SwapLayoutIfNecessary();
7692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(TabStripObserver, observers_,
7712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    TabStripMovedTab(this, from_model_index, to_model_index));
7725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::RemoveTabAt(int model_index) {
7755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (touch_layout_.get()) {
7765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* tab = tab_at(model_index);
7775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab->set_closing(true);
7785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int old_x = tabs_.ideal_bounds(model_index).x();
7795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We still need to paint the tab until we actually remove it. Put it in
7805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // tabs_closing_map_ so we can find it.
7815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RemoveTabFromViewModel(model_index);
7825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_->RemoveTab(model_index, GenerateIdealBoundsForMiniTabs(NULL),
7835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             old_x);
7845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ScheduleRemoveTabAnimation(tab);
7855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (in_tab_close_ && model_index != GetModelCount()) {
7865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartMouseInitiatedRemoveTabAnimation(model_index);
7875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
7885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartRemoveTabAnimation(model_index);
7895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SwapLayoutIfNecessary();
7912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FOR_EACH_OBSERVER(TabStripObserver, observers_,
7932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    TabStripRemovedTabAt(this, model_index));
7945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::SetTabData(int model_index, const TabRendererData& data) {
7975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Tab* tab = tab_at(model_index);
7985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool mini_state_changed = tab->data().mini != data.mini;
7995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab->SetData(data);
8005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (mini_state_changed) {
8025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (touch_layout_.get()) {
8035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int mini_tab_count = 0;
8045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int start_x = GenerateIdealBoundsForMiniTabs(&mini_tab_count);
8055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      touch_layout_->SetXAndMiniCount(start_x, mini_tab_count);
8065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (GetWidget() && GetWidget()->IsVisible())
8085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      StartMiniTabAnimation();
8095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
8105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DoLayout();
8115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SwapLayoutIfNecessary();
8135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::PrepareForCloseAt(int model_index, CloseTabSource source) {
8165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!in_tab_close_ && IsAnimating()) {
8175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Cancel any current animations. We do this as remove uses the current
8185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // ideal bounds and we need to know ideal bounds is in a good state.
8195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StopAnimating(true);
8205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetWidget())
8235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
8245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int model_count = GetModelCount();
8265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (model_index + 1 != model_count && model_count > 1) {
8275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The user is about to close a tab other than the last tab. Set
8285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // available_width_for_tabs_ so that if we do a layout we don't position a
8295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // tab past the end of the second to last tab. We do this so that as the
8305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // user closes tabs with the mouse a tab continues to fall under the mouse.
8315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* last_tab = tab_at(model_count - 1);
8325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* tab_being_removed = tab_at(model_index);
8335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    available_width_for_tabs_ = last_tab->x() + last_tab->width() -
8345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tab_being_removed->width() - tab_h_offset();
8355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (model_index == 0 && tab_being_removed->data().mini &&
8365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !tab_at(1)->data().mini) {
8375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      available_width_for_tabs_ -= kMiniToNonMiniGap;
8385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in_tab_close_ = true;
8425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resize_layout_timer_.Stop();
8435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source == CLOSE_TAB_FROM_TOUCH) {
8445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartResizeLayoutTabsFromTouchTimer();
8455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
8465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddMessageLoopObserver();
8475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::SetSelection(const ui::ListSelectionModel& old_selection,
8512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            const ui::ListSelectionModel& new_selection) {
8525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (touch_layout_.get()) {
8535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_->SetActiveIndex(new_selection.active());
8545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only start an animation if we need to. Otherwise clicking on an
8555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // unselected tab and dragging won't work because dragging is only allowed
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // if not animating.
8575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!views::ViewModelUtils::IsAtIdealBounds(tabs_))
8585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      AnimateToIdealBounds();
8595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SchedulePaint();
8605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
8615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We have "tiny tabs" if the tabs are so tiny that the unselected ones are
8625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // a different size to the selected ones.
8635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool tiny_tabs = current_unselected_width_ != current_selected_width_;
8645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!IsAnimating() && (!in_tab_close_ || tiny_tabs)) {
8655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DoLayout();
8665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
8675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SchedulePaint();
8685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
8695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
8705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ui::ListSelectionModel::SelectedIndices no_longer_selected =
8723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      base::STLSetDifference<ui::ListSelectionModel::SelectedIndices>(
8733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          old_selection.selected_indices(),
8743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          new_selection.selected_indices());
8755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < no_longer_selected.size(); ++i)
8765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_at(no_longer_selected[i])->StopMiniTabTitleAnimation();
8775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::TabTitleChangedNotLoading(int model_index) {
8805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Tab* tab = tab_at(model_index);
8815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab->data().mini && !tab->IsActive())
8825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab->StartMiniTabTitleAnimation();
8835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Tab* TabStrip::tab_at(int index) const {
8865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<Tab*>(tabs_.view_at(index));
8875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int TabStrip::GetModelIndexOfTab(const Tab* tab) const {
8905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return tabs_.GetIndexOfView(tab);
8915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabStrip::GetModelCount() const {
8945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return controller_->GetCount();
8955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
8965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::IsValidModelIndex(int model_index) const {
8985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return controller_->IsValidIndex(model_index);
8995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Tab* TabStrip::CreateTabForDragging() {
9025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Tab* tab = new Tab(NULL);
9035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure the dragged tab shares our theme provider. We need to explicitly
9045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // do this as during dragging there isn't a theme provider.
9055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab->set_theme_provider(GetThemeProvider());
9065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return tab;
9075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::IsDragSessionActive() const {
9105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return drag_controller_.get() != NULL;
9115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::IsActiveDropTarget() const {
9145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < tab_count(); ++i) {
9155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* tab = tab_at(i);
9165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tab->dragging())
9175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
9185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
9195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
9205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::IsTabStripEditable() const {
9235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !IsDragSessionActive() && !IsActiveDropTarget();
9245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::IsTabStripCloseable() const {
9275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !IsDragSessionActive();
9285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::UpdateLoadingAnimations() {
9315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  controller_->UpdateLoadingAnimations();
9325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::IsPositionInWindowCaption(const gfx::Point& point) {
935f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return IsRectInWindowCaption(gfx::Rect(point, gfx::Size(1, 1)));
936f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
937f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
938f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool TabStrip::IsRectInWindowCaption(const gfx::Rect& rect) {
939f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  views::View* v = GetEventHandlerForRect(rect);
9405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If there is no control at this location, claim the hit was in the title
9425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // bar to get a move action.
9435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (v == this)
9445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
9455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
946f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Check to see if the rect intersects the non-button parts of the new tab
9475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // button. The button has a non-rectangular shape, so if it's not in the
9485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // visual portions of the button we treat it as a click to the caption.
949f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::RectF rect_in_newtab_coords_f(rect);
950f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  View::ConvertRectToTarget(this, newtab_button_, &rect_in_newtab_coords_f);
951f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Rect rect_in_newtab_coords = gfx::ToEnclosingRect(
952f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      rect_in_newtab_coords_f);
953f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (newtab_button_->GetLocalBounds().Intersects(rect_in_newtab_coords) &&
954f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      !newtab_button_->HitTestRect(rect_in_newtab_coords))
9555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
9565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // All other regions, including the new Tab button, should be considered part
9585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // of the containing Window's client area so that regular events can be
9595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // processed for them.
9605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
9615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::SetBackgroundOffset(const gfx::Point& offset) {
9645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < tab_count(); ++i)
9655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_at(i)->set_background_offset(offset);
9665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_->set_background_offset(offset);
9675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)views::View* TabStrip::newtab_button() {
9705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return newtab_button_;
9715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
9725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::SetImmersiveStyle(bool enable) {
9742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (immersive_style_ == enable)
9752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
9762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  immersive_style_ = enable;
9772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TabStrip::IsAnimating() const {
9802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return bounds_animator_.IsAnimating();
9812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::StopAnimating(bool layout) {
9842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!IsAnimating())
9852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
9862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bounds_animator_.Cancel();
9882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
9892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (layout)
9902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DoLayout();
9912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
9922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
993a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void TabStrip::FileSupported(const GURL& url, bool supported) {
994a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (drop_info_.get() && drop_info_->url == url)
995a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    drop_info_->file_supported = supported;
996a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
997a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
9982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const ui::ListSelectionModel& TabStrip::GetSelectionModel() {
9995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return controller_->GetSelectionModel();
10005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::SupportsMultipleSelection() {
10035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO: currently only allow single selection in touch layout mode.
10045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return touch_layout_.get() == NULL;
10055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::SelectTab(Tab* tab) {
10082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int model_index = GetModelIndexOfTab(tab);
10095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsValidModelIndex(model_index))
10105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    controller_->SelectTab(model_index);
10115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::ExtendSelectionTo(Tab* tab) {
10142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int model_index = GetModelIndexOfTab(tab);
10155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsValidModelIndex(model_index))
10165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    controller_->ExtendSelectionTo(model_index);
10175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::ToggleSelected(Tab* tab) {
10202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int model_index = GetModelIndexOfTab(tab);
10215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsValidModelIndex(model_index))
10225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    controller_->ToggleSelected(model_index);
10235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::AddSelectionFromAnchorTo(Tab* tab) {
10262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int model_index = GetModelIndexOfTab(tab);
10275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsValidModelIndex(model_index))
10285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    controller_->AddSelectionFromAnchorTo(model_index);
10295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::CloseTab(Tab* tab, CloseTabSource source) {
10325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab->closing()) {
10335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If the tab is already closing, close the next tab. We do this so that the
10345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // user can rapidly close tabs by clicking the close button and not have
10355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the animations interfere with that.
10365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (TabsClosingMap::const_iterator i(tabs_closing_map_.begin());
10375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         i != tabs_closing_map_.end(); ++i) {
10382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::vector<Tab*>::const_iterator j =
10395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          std::find(i->second.begin(), i->second.end(), tab);
10405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (j != i->second.end()) {
10415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (i->first + 1 < GetModelCount())
10425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          controller_->CloseTab(i->first + 1, source);
10435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
10445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
10465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we get here, it means a tab has been marked as closing but isn't in
10475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the set of known closing tabs.
10485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
10495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
10505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
10512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int model_index = GetModelIndexOfTab(tab);
10525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsValidModelIndex(model_index))
10535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    controller_->CloseTab(model_index, source);
10545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10567d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)void TabStrip::ShowContextMenuForTab(Tab* tab,
10577d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                     const gfx::Point& p,
10587d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                     ui::MenuSourceType source_type) {
10597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  controller_->ShowContextMenuForTab(tab, p, source_type);
10605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TabStrip::IsActiveTab(const Tab* tab) const {
10632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int model_index = GetModelIndexOfTab(tab);
10645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return IsValidModelIndex(model_index) &&
10655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller_->IsActiveTab(model_index);
10665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TabStrip::IsTabSelected(const Tab* tab) const {
10692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int model_index = GetModelIndexOfTab(tab);
10705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return IsValidModelIndex(model_index) &&
10715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller_->IsTabSelected(model_index);
10725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TabStrip::IsTabPinned(const Tab* tab) const {
10755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab->closing())
10765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
10775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int model_index = GetModelIndexOfTab(tab);
10795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return IsValidModelIndex(model_index) &&
10805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller_->IsTabPinned(model_index);
10815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
10825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
10835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::MaybeStartDrag(
10842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Tab* tab,
10855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ui::LocatedEvent& event,
10862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ui::ListSelectionModel& original_selection) {
10875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't accidentally start any drag operations during animations if the
10885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mouse is down... during an animation tabs are being resized automatically,
10895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // so the View system can misinterpret this easily if the mouse is down that
10905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the user is dragging.
10915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsAnimating() || tab->closing() ||
10925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller_->HasAvailableDragActions() == 0) {
10935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
10945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
109590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
109690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Do not do any dragging of tabs when using the super short immersive style.
109790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (IsImmersiveStyle())
109890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
109990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
11002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int model_index = GetModelIndexOfTab(tab);
11015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsValidModelIndex(model_index)) {
11025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CHECK(false);
11035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
11045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<Tab*> tabs;
11065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int size_to_selected = 0;
11075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int x = tab->GetMirroredXInView(event.x());
11085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int y = event.y();
11095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Build the set of selected tabs to drag and calculate the offset from the
11105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // first selected tab.
11115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < tab_count(); ++i) {
11125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* other_tab = tab_at(i);
11135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (IsTabSelected(other_tab)) {
11145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tabs.push_back(other_tab);
11155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (other_tab == tab) {
11165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        size_to_selected = GetSizeNeededForTabs(tabs);
11175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        x = size_to_selected - tab->width() + x;
11185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
11195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
11205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!tabs.empty());
11225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(std::find(tabs.begin(), tabs.end(), tab) != tabs.end());
11232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ui::ListSelectionModel selection_model;
11245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!original_selection.IsSelected(model_index))
11255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    selection_model.Copy(original_selection);
11265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Delete the existing DragController before creating a new one. We do this as
11275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // creating the DragController remembers the WebContents delegates and we need
11285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to make sure the existing DragController isn't still a delegate.
11295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_controller_.reset();
11305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabDragController::DetachBehavior detach_behavior =
11315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      TabDragController::DETACHABLE;
11325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabDragController::MoveBehavior move_behavior =
11335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      TabDragController::REORDER;
11345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Use MOVE_VISIBILE_TABS in the following conditions:
11355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // . Mouse event generated from touch and the left button is down (the right
11365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   button corresponds to a long press, which we want to reorder).
11375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // . Gesture begin and control key isn't down.
11385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // . Real mouse event and control is down. This is mostly for testing.
11395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(event.type() == ui::ET_MOUSE_PRESSED ||
11405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         event.type() == ui::ET_GESTURE_BEGIN);
11412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (touch_layout_.get() &&
11425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ((event.type() == ui::ET_MOUSE_PRESSED &&
11435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (((event.flags() & ui::EF_FROM_TOUCH) &&
11445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<const ui::MouseEvent&>(event).IsLeftMouseButton()) ||
11455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         (!(event.flags() & ui::EF_FROM_TOUCH) &&
11465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<const ui::MouseEvent&>(event).IsControlDown()))) ||
11475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       (event.type() == ui::ET_GESTURE_BEGIN && !event.IsControlDown()))) {
11485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    move_behavior = TabDragController::MOVE_VISIBILE_TABS;
11495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
11512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  views::Widget* widget = GetWidget();
11525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
11532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // It doesn't make sense to drag tabs out on Win8's single window Metro mode.
11542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (win8::IsSingleWindowMetroMode())
11555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    detach_behavior = TabDragController::NOT_DETACHABLE;
11565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
11575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Gestures don't automatically do a capture. We don't allow multiple drags at
11585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the same time, so we explicitly capture.
11595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (event.type() == ui::ET_GESTURE_BEGIN)
11602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    widget->SetCapture(this);
11615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_controller_.reset(new TabDragController);
11625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_controller_->Init(
11632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this, tab, tabs, gfx::Point(x, y), event.x(), selection_model,
11642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      detach_behavior, move_behavior, EventSourceFromEvent(event));
11655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::ContinueDrag(views::View* view, const ui::LocatedEvent& event) {
11682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (drag_controller_.get() &&
11692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      drag_controller_->event_source() == EventSourceFromEvent(event)) {
11702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::Point screen_location(event.location());
11715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    views::View::ConvertPointToScreen(view, &screen_location);
11725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drag_controller_->Drag(screen_location);
11735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
11745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::EndDrag(EndDragReason reason) {
11775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drag_controller_.get())
11785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
11795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool started_drag = drag_controller_->started_drag();
11805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_controller_->EndDrag(reason);
11815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return started_drag;
11825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Tab* TabStrip::GetTabAt(Tab* tab, const gfx::Point& tab_in_tab_coordinates) {
11855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point local_point = tab_in_tab_coordinates;
11865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ConvertPointToTarget(tab, this, &local_point);
11875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::View* view = GetEventHandlerForPoint(local_point);
11895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!view)
11905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;  // No tab contains the point.
11915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Walk up the view hierarchy until we find a tab, or the TabStrip.
11935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (view && view != this && view->id() != VIEW_ID_TAB)
11945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    view = view->parent();
11955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return view && view->id() == VIEW_ID_TAB ? static_cast<Tab*>(view) : NULL;
11975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
11985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::OnMouseEventInTab(views::View* source,
12005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 const ui::MouseEvent& event) {
12015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateLayoutTypeFromMouseEvent(source, event);
12025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TabStrip::ShouldPaintTab(const Tab* tab, gfx::Rect* clip) {
12055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only touch layout needs to restrict the clip.
12065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!(touch_layout_.get() || IsStackingDraggedTabs()))
12075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
12085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int index = GetModelIndexOfTab(tab);
12105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index == -1)
12115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;  // Tab is closing, paint it all.
12125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int active_index = IsStackingDraggedTabs() ?
12145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller_->GetActiveIndex() : touch_layout_->active_index();
12155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (active_index == tab_count())
12165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    active_index--;
12175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index < active_index) {
12195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tab_at(index)->x() == tab_at(index + 1)->x())
12205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
12215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tab_at(index)->x() > tab_at(index + 1)->x())
12235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;  // Can happen during dragging.
12245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    clip->SetRect(0, 0, tab_at(index + 1)->x() - tab_at(index)->x() +
12265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      stacked_tab_left_clip(),
12275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  tab_at(index)->height());
12285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (index > active_index && index > 0) {
12295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& tab_bounds(tab_at(index)->bounds());
12305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const gfx::Rect& previous_tab_bounds(tab_at(index - 1)->bounds());
12315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tab_bounds.x() == previous_tab_bounds.x())
12325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
12335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tab_bounds.x() < previous_tab_bounds.x())
12355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;  // Can happen during dragging.
12365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (previous_tab_bounds.right() + tab_h_offset() != tab_bounds.x()) {
12385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int x = previous_tab_bounds.right() - tab_bounds.x() -
12395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          stacked_tab_right_clip();
12405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      clip->SetRect(x, 0, tab_bounds.width() - x, tab_bounds.height());
12415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
12425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
12445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TabStrip::IsImmersiveStyle() const {
12472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return immersive_style_;
12482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
12492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::MouseMovedOutOfHost() {
12515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ResizeLayoutTabs();
12525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (reset_to_shrink_on_exit_) {
12535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    reset_to_shrink_on_exit_ = false;
12545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetLayoutType(TAB_STRIP_LAYOUT_SHRINK, true);
12555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    controller_->LayoutTypeMaybeChanged();
12565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
12575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
12605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabStrip, views::View overrides:
12615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::Layout() {
12635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Only do a layout if our size changed.
12645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (last_layout_size_ == size())
12655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
12665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (IsDragSessionActive())
12675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
12685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DoLayout();
12695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
12705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::PaintChildren(gfx::Canvas* canvas) {
12725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The view order doesn't match the paint order (tabs_ contains the tab
12735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ordering). Additionally we need to paint the tabs that are closing in
12745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |tabs_closing_map_|.
12755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Tab* active_tab = NULL;
12765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<Tab*> tabs_dragging;
12775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<Tab*> selected_tabs;
12782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int selected_tab_count = 0;
12795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_dragging = false;
12805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int active_tab_index = -1;
12815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Since |touch_layout_| is created based on number of tabs and width we use
12825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the ideal state to determine if we should paint stacked. This minimizes
12835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // painting changes as we switch between the two.
12845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const bool stacking = (layout_type_ == TAB_STRIP_LAYOUT_STACKED) ||
12855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IsStackingDraggedTabs();
12865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const chrome::HostDesktopType host_desktop_type =
12882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      chrome::GetHostDesktopTypeForNativeView(GetWidget()->GetNativeView());
12892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int inactive_tab_alpha =
12902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH ?
12912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      kInactiveTabAndNewTabButtonAlphaAsh :
12922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      kInactiveTabAndNewTabButtonAlpha;
12932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (inactive_tab_alpha < 255)
12952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    canvas->SaveLayerAlpha(inactive_tab_alpha);
12965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PaintClosingTabs(canvas, tab_count());
12985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = tab_count() - 1; i >= 0; --i) {
13005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* tab = tab_at(i);
13012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (tab->IsSelected())
13022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      selected_tab_count++;
13035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tab->dragging() && !stacking) {
13045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_dragging = true;
13055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (tab->IsActive()) {
13065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        active_tab = tab;
13075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        active_tab_index = i;
13085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
13095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tabs_dragging.push_back(tab);
13105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
13115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (!tab->IsActive()) {
13125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!tab->IsSelected()) {
13135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!stacking)
13145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          tab->Paint(canvas);
13155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
13165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        selected_tabs.push_back(tab);
13175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
13185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
13195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      active_tab = tab;
13205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      active_tab_index = i;
13215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PaintClosingTabs(canvas, i);
13235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Draw from the left and then the right if we're in touch mode.
13265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stacking && active_tab_index >= 0) {
13275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < active_tab_index; ++i) {
13285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Tab* tab = tab_at(i);
13295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab->Paint(canvas);
13305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = tab_count() - 1; i > active_tab_index; --i) {
13335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Tab* tab = tab_at(i);
13345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab->Paint(canvas);
13355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
13365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (inactive_tab_alpha < 255)
13385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Restore();
13395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (GetWidget()->ShouldUseNativeFrame()) {
13415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Make sure non-active tabs are somewhat transparent.
13425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SkPaint paint;
13435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If there are multiple tabs selected, fade non-selected tabs more to make
13445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the selected tabs more noticable.
13452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int alpha = selected_tab_count > 1 ?
13465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kNativeFrameInactiveTabAlphaMultiSelection :
13475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        kNativeFrameInactiveTabAlpha;
13485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    paint.setColor(SkColorSetARGB(alpha, 255, 255, 255));
13495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
13505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    paint.setStyle(SkPaint::kFill_Style);
13515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The tabstrip area overlaps the toolbar area by 2 px.
13525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->DrawRect(gfx::Rect(0, 0, width(), height() - 2), paint);
13535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now selected but not active. We don't want these dimmed if using native
13565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // frame, so they're painted after initial pass.
13575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < selected_tabs.size(); ++i)
13585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    selected_tabs[i]->Paint(canvas);
13595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Next comes the active tab.
13615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (active_tab && !is_dragging)
13625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    active_tab->Paint(canvas);
13635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Paint the New Tab button.
13652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (inactive_tab_alpha < 255)
13662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    canvas->SaveLayerAlpha(inactive_tab_alpha);
13675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_->Paint(canvas);
13682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (inactive_tab_alpha < 255)
13695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Restore();
13705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // And the dragged tabs.
13725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tabs_dragging.size(); ++i)
13735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tabs_dragging[i]->Paint(canvas);
13745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the active tab is being dragged, it goes last.
13765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (active_tab && is_dragging)
13775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    active_tab->Paint(canvas);
13785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1380b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)const char* TabStrip::GetClassName() const {
13815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return kViewClassName;
13825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
13835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Size TabStrip::GetPreferredSize() {
13855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For stacked tabs the minimum size is calculated as the size needed to
13865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // handle showing any number of tabs. Otherwise report the minimum width as
13875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the size required for a single selected tab plus the new tab button. Don't
13885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // base it on the actual number of tabs because it's undesirable to have the
13895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // minimum window size change when a new tab is opened.
13905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int needed_width;
13915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (touch_layout_.get() || adjust_layout_) {
13925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    needed_width = Tab::GetTouchWidth() +
13935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (2 * kStackedPadding * kMaxStackedCount);
13945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
13955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    needed_width = Tab::GetMinimumSelectedSize().width();
13965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
13975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  needed_width += new_tab_button_width();
13982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (immersive_style_)
13992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return gfx::Size(needed_width, Tab::GetImmersiveHeight());
14005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::Size(needed_width, Tab::GetMinimumUnselectedSize().height());
14015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::OnDragEntered(const DropTargetEvent& event) {
14045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Force animations to stop, otherwise it makes the index calculation tricky.
14055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StopAnimating(true);
14065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateDropIndex(event);
1408a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1409a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  GURL url;
1410a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 title;
1411a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1412a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Check whether the event data includes supported drop data.
141381843c7b1423e241009db79d34acf2450330fcc5Torne (Richard Coles)  if (event.data().GetURLAndTitle(
141481843c7b1423e241009db79d34acf2450330fcc5Torne (Richard Coles)          ui::OSExchangeData::CONVERT_FILENAMES, &url, &title) &&
141581843c7b1423e241009db79d34acf2450330fcc5Torne (Richard Coles)      url.is_valid()) {
1416a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    drop_info_->url = url;
1417a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1418a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    // For file:// URLs, kick off a MIME type request in case they're dropped.
1419a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if (url.SchemeIsFile())
1420a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      controller()->CheckFileSupported(url);
1421a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
14225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabStrip::OnDragUpdated(const DropTargetEvent& event) {
1425a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Update the drop index even if the file is unsupported, to allow
1426a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // dragging a file to the contents of another tab.
14275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateDropIndex(event);
1428a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1429a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!drop_info_->file_supported)
1430a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return ui::DragDropTypes::DRAG_NONE;
1431a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
14325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetDropEffect(event);
14335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::OnDragExited() {
14365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetDropIndex(-1, false);
14375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabStrip::OnPerformDrop(const DropTargetEvent& event) {
14405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drop_info_.get())
14415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::DragDropTypes::DRAG_NONE;
14425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int drop_index = drop_info_->drop_index;
14445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const bool drop_before = drop_info_->drop_before;
1445a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  const bool file_supported = drop_info_->file_supported;
14465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Hide the drop indicator.
14485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetDropIndex(-1, false);
14495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1450a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // Do nothing if the file was unsupported or the URL is invalid. The URL may
1451a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // have been changed after |drop_info_| was created.
14525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GURL url;
1453a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 title;
1454a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!file_supported ||
145581843c7b1423e241009db79d34acf2450330fcc5Torne (Richard Coles)      !event.data().GetURLAndTitle(
145681843c7b1423e241009db79d34acf2450330fcc5Torne (Richard Coles)           ui::OSExchangeData::CONVERT_FILENAMES, &url, &title) ||
145781843c7b1423e241009db79d34acf2450330fcc5Torne (Richard Coles)      !url.is_valid())
14585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::DragDropTypes::DRAG_NONE;
14595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  controller()->PerformDrop(drop_before, drop_index, url);
14615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetDropEffect(event);
14635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
14655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::GetAccessibleState(ui::AccessibleViewState* state) {
14665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST;
14675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1469f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)views::View* TabStrip::GetEventHandlerForRect(const gfx::Rect& rect) {
1470f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!views::UsePointBasedTargeting(rect))
1471f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return View::GetEventHandlerForRect(rect);
1472f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const gfx::Point point(rect.CenterPoint());
1473f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
14745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!touch_layout_.get()) {
14755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Return any view that isn't a Tab or this TabStrip immediately. We don't
14765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // want to interfere.
1477f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    views::View* v = View::GetEventHandlerForRect(rect);
1478b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (v && v != this && strcmp(v->GetClassName(), Tab::kViewClassName))
14795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return v;
14805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1481c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    views::View* tab = FindTabHitByPoint(point);
1482c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (tab)
1483c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return tab;
14845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
14855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (newtab_button_->visible()) {
14865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      views::View* view =
14875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          ConvertPointToViewAndGetEventHandler(this, newtab_button_, point);
14885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (view)
14895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return view;
14905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
14915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* tab = FindTabForEvent(point);
14925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tab)
14935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ConvertPointToViewAndGetEventHandler(this, tab, point);
14945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
14955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return this;
14965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
14975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1498c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)views::View* TabStrip::GetTooltipHandlerForPoint(const gfx::Point& point) {
1499c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!HitTestPoint(point))
1500c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return NULL;
1501c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1502c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!touch_layout_.get()) {
1503c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Return any view that isn't a Tab or this TabStrip immediately. We don't
1504c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // want to interfere.
1505c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    views::View* v = View::GetTooltipHandlerForPoint(point);
1506b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (v && v != this && strcmp(v->GetClassName(), Tab::kViewClassName))
1507c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return v;
1508c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1509c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    views::View* tab = FindTabHitByPoint(point);
1510c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (tab)
1511c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return tab;
1512c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
1513c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (newtab_button_->visible()) {
1514c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      views::View* view =
1515c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          ConvertPointToViewAndGetTooltipHandler(this, newtab_button_, point);
1516c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (view)
1517c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return view;
1518c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
1519c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Tab* tab = FindTabForEvent(point);
1520c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (tab)
1521c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return ConvertPointToViewAndGetTooltipHandler(this, tab, point);
1522c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
1523c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return this;
1524c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
1525c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
15262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
15272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int TabStrip::GetImmersiveHeight() {
15282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return Tab::GetImmersiveHeight();
15292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
15302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
15315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabStrip::GetMiniTabCount() const {
15325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mini_count = 0;
15335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (mini_count < tab_count() && tab_at(mini_count)->data().mini)
15345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mini_count++;
15355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return mini_count;
15365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
15395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabStrip, views::ButtonListener implementation:
15405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::ButtonPressed(views::Button* sender, const ui::Event& event) {
15425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (sender == newtab_button_) {
15435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content::RecordAction(UserMetricsAction("NewTab_Button"));
15445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("Tab.NewTab", TabStripModel::NEW_TAB_BUTTON,
15455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              TabStripModel::NEW_TAB_ENUM_COUNT);
1546f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (event.IsMouseEvent()) {
1547f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      const ui::MouseEvent& mouse = static_cast<const ui::MouseEvent&>(event);
1548f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      if (mouse.IsOnlyMiddleMouseButton()) {
1549f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        base::string16 clipboard_text = GetClipboardText();
1550f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        if (!clipboard_text.empty())
1551f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          controller()->CreateNewTabWithLocation(clipboard_text);
1552f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        return;
1553f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      }
1554f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
1555f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
15565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    controller()->CreateNewTab();
1557eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (event.type() == ui::ET_GESTURE_TAP)
1558eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      TouchUMA::RecordGestureAction(TouchUMA::GESTURE_NEWTAB_TAP);
15595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
15605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
15635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabStrip, protected:
15645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Overridden to support automation. See automation_proxy_uitest.cc.
15665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const views::View* TabStrip::GetViewByID(int view_id) const {
15675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_count() > 0) {
15685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (view_id == VIEW_ID_TAB_LAST) {
15695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return tab_at(tab_count() - 1);
15705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) {
15715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int index = view_id - VIEW_ID_TAB_0;
15725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (index >= 0 && index < tab_count()) {
15735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return tab_at(index);
15745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
15755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return NULL;
15765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
15775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
15785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
15795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return View::GetViewByID(view_id);
15815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::OnMousePressed(const ui::MouseEvent& event) {
15845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateLayoutTypeFromMouseEvent(this, event);
15855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We can't return true here, else clicking in an empty area won't drag the
15865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // window.
15875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
15885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::OnMouseDragged(const ui::MouseEvent& event) {
15912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ContinueDrag(this, event);
15925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
15935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::OnMouseReleased(const ui::MouseEvent& event) {
15965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EndDrag(END_DRAG_COMPLETE);
15975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateLayoutTypeFromMouseEvent(this, event);
15985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
15995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::OnMouseCaptureLost() {
16015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EndDrag(END_DRAG_CAPTURE_LOST);
16025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::OnMouseMoved(const ui::MouseEvent& event) {
16055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateLayoutTypeFromMouseEvent(this, event);
16065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::OnMouseEntered(const ui::MouseEvent& event) {
16095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetResetToShrinkOnExit(true);
16105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::OnGestureEvent(ui::GestureEvent* event) {
16135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetResetToShrinkOnExit(false);
16145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (event->type()) {
16151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    case ui::ET_GESTURE_SCROLL_END:
16161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    case ui::ET_SCROLL_FLING_START:
1617f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case ui::ET_GESTURE_END:
16185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EndDrag(END_DRAG_COMPLETE);
16195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (adjust_layout_) {
16205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetLayoutType(TAB_STRIP_LAYOUT_STACKED, true);
16215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        controller_->LayoutTypeMaybeChanged();
16225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
16235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
16245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_GESTURE_LONG_PRESS:
16265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (drag_controller_.get())
16275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        drag_controller_->SetMoveBehavior(TabDragController::REORDER);
16285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
16295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case ui::ET_GESTURE_LONG_TAP: {
16312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      EndDrag(END_DRAG_CANCEL);
16322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      gfx::Point local_point = event->location();
16332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Tab* tab = FindTabForEvent(local_point);
16342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (tab) {
16352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        ConvertPointToScreen(this, &local_point);
16367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)        ShowContextMenuForTab(tab, local_point, ui::MENU_SOURCE_TOUCH);
16372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
16382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
16392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
16402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
16415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_GESTURE_SCROLL_UPDATE:
16422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ContinueDrag(this, *event);
16435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
16445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_GESTURE_BEGIN:
16465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      EndDrag(END_DRAG_CANCEL);
16475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
16485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1649eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    case ui::ET_GESTURE_TAP: {
1650eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      const int active_index = controller_->GetActiveIndex();
1651eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      DCHECK_NE(-1, active_index);
1652eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      Tab* active_tab = tab_at(active_index);
1653eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      TouchUMA::GestureActionType action = TouchUMA::GESTURE_TABNOSWITCH_TAP;
1654eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      if (active_tab->tab_activated_with_last_gesture_begin())
1655eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        action = TouchUMA::GESTURE_TABSWITCH_TAP;
1656eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      TouchUMA::RecordGestureAction(action);
1657eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      break;
1658eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
1659eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
16605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
16615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
16625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
16632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event->SetHandled();
16645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
16675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabStrip, private:
16685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::Init() {
16705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set_id(VIEW_ID_TAB_STRIP);
16715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // So we get enter/exit on children to switch layout type.
16725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  set_notify_enter_exit_on_child(true);
16735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_bounds_.SetRect(0,
16745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                0,
16755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                newtab_button_asset_width(),
16762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                newtab_button_asset_height() +
16772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    newtab_button_v_offset());
16785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_ = new NewTabButton(this, this);
16795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_->SetTooltipText(
16805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      l10n_util::GetStringUTF16(IDS_TOOLTIP_NEW_TAB));
16815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_->SetAccessibleName(
16825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      l10n_util::GetStringUTF16(IDS_ACCNAME_NEWTAB));
16832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  newtab_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
16842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    views::ImageButton::ALIGN_BOTTOM);
16855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AddChildView(newtab_button_);
16865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drop_indicator_width == 0) {
16875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Direction doesn't matter, both images are the same size.
16885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::ImageSkia* drop_image = GetDropArrowImage(true);
16895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drop_indicator_width = drop_image->width();
16905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drop_indicator_height = drop_image->height();
16915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
16925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Tab* TabStrip::CreateTab() {
16955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Tab* tab = new Tab(this);
16965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab->set_animation_container(animation_container_.get());
16975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return tab;
16985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
16995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StartInsertTabAnimation(int model_index) {
17015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForAnimation();
17025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The TabStrip can now use its entire width to lay out Tabs.
17045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in_tab_close_ = false;
17055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  available_width_for_tabs_ = -1;
17065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateIdealBounds();
17088bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
17095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Tab* tab = tab_at(model_index);
17105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (model_index == 0) {
17115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab->SetBounds(0, ideal_bounds(model_index).y(), 0,
17125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   ideal_bounds(model_index).height());
17135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
17145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* last_tab = tab_at(model_index - 1);
17155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab->SetBounds(last_tab->bounds().right() + tab_h_offset(),
17165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   ideal_bounds(model_index).y(), 0,
17175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   ideal_bounds(model_index).height());
17185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
17195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AnimateToIdealBounds();
17215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
17225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StartMoveTabAnimation() {
17245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForAnimation();
17255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateIdealBounds();
17265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AnimateToIdealBounds();
17275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
17285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StartRemoveTabAnimation(int model_index) {
17305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForAnimation();
17315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Mark the tab as closing.
17335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Tab* tab = tab_at(model_index);
17345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab->set_closing(true);
17355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveTabFromViewModel(model_index);
17375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScheduleRemoveTabAnimation(tab);
17395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
17405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::ScheduleRemoveTabAnimation(Tab* tab) {
17425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start an animation for the tabs.
17435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateIdealBounds();
17445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AnimateToIdealBounds();
17455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Animate the tab being closed to 0x0.
17475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect tab_bounds = tab->bounds();
17485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab_bounds.set_width(0);
17495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_animator_.AnimateViewTo(tab, tab_bounds);
17505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Register delegate to do cleanup when done, BoundsAnimator takes
17525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ownership of RemoveTabDelegate.
17535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_animator_.SetAnimationDelegate(tab, new RemoveTabDelegate(this, tab),
17545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        true);
17555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't animate the new tab button when dragging tabs. Otherwise it looks
17575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // like the new tab button magically appears from beyond the end of the tab
17585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // strip.
17595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (TabDragController::IsAttachedTo(this)) {
17605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bounds_animator_.StopAnimatingView(newtab_button_);
17615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    newtab_button_->SetBoundsRect(newtab_button_bounds_);
17625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
17635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
17645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::AnimateToIdealBounds() {
17665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < tab_count(); ++i) {
17675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* tab = tab_at(i);
17685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!tab->dragging())
17695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bounds_animator_.AnimateViewTo(tab, ideal_bounds(i));
17705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
17715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_animator_.AnimateViewTo(newtab_button_, newtab_button_bounds_);
17735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
17745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::ShouldHighlightCloseButtonAfterRemove() {
17765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return in_tab_close_;
17775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
17785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::DoLayout() {
17805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  last_layout_size_ = size();
17815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StopAnimating(false);
17835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SwapLayoutIfNecessary();
17855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (touch_layout_.get())
17875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_->SetWidth(size().width() - new_tab_button_width());
17885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateIdealBounds();
17905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_);
17925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
17945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_animator_.StopAnimatingView(newtab_button_);
17965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_->SetBoundsRect(newtab_button_bounds_);
17975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
17985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::DragActiveTab(const std::vector<int>& initial_positions,
18005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int delta) {
18015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(tab_count(), static_cast<int>(initial_positions.size()));
18025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!touch_layout_.get()) {
18035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StackDraggedTabs(delta);
18045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
18055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
18065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetIdealBoundsFromPositions(initial_positions);
18075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  touch_layout_->DragActiveTab(delta);
18085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DoLayout();
18095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
18105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::SetIdealBoundsFromPositions(const std::vector<int>& positions) {
18125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (static_cast<size_t>(tab_count()) != positions.size())
18135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
18145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < tab_count(); ++i) {
18165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect bounds(ideal_bounds(i));
18175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bounds.set_x(positions[i]);
18185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_ideal_bounds(i, bounds);
18195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
18205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
18215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StackDraggedTabs(int delta) {
18235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!touch_layout_.get());
18245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateIdealBounds();
18255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int active_index = controller_->GetActiveIndex();
18265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(-1, active_index);
18275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (delta < 0) {
18285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Drag the tabs to the left, stacking tabs before the active tab.
18295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int adjusted_delta =
18305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::min(ideal_bounds(active_index).x() -
18315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     kStackedPadding * std::min(active_index, kMaxStackedCount),
18325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 -delta);
18335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i <= active_index; ++i) {
18345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const int min_x = std::min(i, kMaxStackedCount) * kStackedPadding;
18355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::Rect new_bounds(ideal_bounds(i));
18365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_bounds.set_x(std::max(min_x, new_bounds.x() - adjusted_delta));
18375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      set_ideal_bounds(i, new_bounds);
18385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
18395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const bool is_active_mini = tab_at(active_index)->data().mini;
18405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int active_width = ideal_bounds(active_index).width();
18415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = active_index + 1; i < tab_count(); ++i) {
18425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const int max_x = ideal_bounds(active_index).x() +
18435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (kStackedPadding * std::min(i - active_index, kMaxStackedCount));
18445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::Rect new_bounds(ideal_bounds(i));
18455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int new_x = std::max(new_bounds.x() + delta, max_x);
18465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (new_x == max_x && !tab_at(i)->data().mini && !is_active_mini &&
18475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          new_bounds.width() != active_width)
18485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new_x += (active_width - new_bounds.width());
18495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_bounds.set_x(new_x);
18505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      set_ideal_bounds(i, new_bounds);
18515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
18525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
18535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Drag the tabs to the right, stacking tabs after the active tab.
18545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int last_tab_width = ideal_bounds(tab_count() - 1).width();
18555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int last_tab_x = width() - new_tab_button_width() - last_tab_width;
18565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (active_index == tab_count() - 1 &&
18575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ideal_bounds(tab_count() - 1).x() == last_tab_x)
18585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
18595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int adjusted_delta =
18605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::min(last_tab_x -
18615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     kStackedPadding * std::min(tab_count() - active_index - 1,
18625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                kMaxStackedCount) -
18635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     ideal_bounds(active_index).x(),
18645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 delta);
18655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int last_index = tab_count() - 1, i = last_index; i >= active_index;
18665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         --i) {
18675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const int max_x = last_tab_x -
18685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          std::min(tab_count() - i - 1, kMaxStackedCount) * kStackedPadding;
18695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::Rect new_bounds(ideal_bounds(i));
18705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int new_x = std::min(max_x, new_bounds.x() + adjusted_delta);
18715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Because of rounding not all tabs are the same width. Adjust the
18725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // position to accommodate this, otherwise the stacking is off.
18735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (new_x == max_x && !tab_at(i)->data().mini &&
18745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          new_bounds.width() != last_tab_width)
18755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new_x += (last_tab_width - new_bounds.width());
18765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_bounds.set_x(new_x);
18775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      set_ideal_bounds(i, new_bounds);
18785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
18795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = active_index - 1; i >= 0; --i) {
18805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const int min_x = ideal_bounds(active_index).x() -
18815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          std::min(active_index - i, kMaxStackedCount) * kStackedPadding;
18825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::Rect new_bounds(ideal_bounds(i));
18835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new_bounds.set_x(std::min(min_x, new_bounds.x() + delta));
18845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      set_ideal_bounds(i, new_bounds);
18855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
18865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (ideal_bounds(tab_count() - 1).right() >= newtab_button_->x())
18875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      newtab_button_->SetVisible(false);
18885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
18895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::ViewModelUtils::SetViewBoundsToIdealBounds(tabs_);
18905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
18915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
18925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::IsStackingDraggedTabs() const {
18945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return drag_controller_.get() && drag_controller_->started_drag() &&
18955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (drag_controller_->move_behavior() ==
18965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       TabDragController::MOVE_VISIBILE_TABS);
18975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
18985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::LayoutDraggedTabsAt(const std::vector<Tab*>& tabs,
19002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   Tab* active_tab,
19015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   const gfx::Point& location,
19025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   bool initial_drag) {
19035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Immediately hide the new tab button if the last tab is being dragged.
19045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_at(tab_count() - 1)->dragging())
19055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    newtab_button_->SetVisible(false);
19065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<gfx::Rect> bounds;
19075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CalculateBoundsForDraggedTabs(tabs, &bounds);
19085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(tabs.size(), bounds.size());
19092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int active_tab_model_index = GetModelIndexOfTab(active_tab);
19105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int active_tab_index = static_cast<int>(
19115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::find(tabs.begin(), tabs.end(), active_tab) - tabs.begin());
19125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tabs.size(); ++i) {
19132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Tab* tab = tabs[i];
19145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect new_bounds = bounds[i];
19155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_bounds.Offset(location.x(), location.y());
19165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int consecutive_index =
19175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        active_tab_model_index - (active_tab_index - static_cast<int>(i));
19185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If this is the initial layout during a drag and the tabs aren't
19195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // consecutive animate the view into position. Do the same if the tab is
19205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // already animating (which means we previously caused it to animate).
19215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((initial_drag &&
19222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         GetModelIndexOfTab(tabs[i]) != consecutive_index) ||
19235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        bounds_animator_.IsAnimating(tabs[i])) {
19245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bounds_animator_.SetTargetBounds(tabs[i], new_bounds);
19255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
19265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab->SetBoundsRect(new_bounds);
19275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
19285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
19295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
19305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::CalculateBoundsForDraggedTabs(const std::vector<Tab*>& tabs,
19325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                             std::vector<gfx::Rect>* bounds) {
19335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int x = 0;
19345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tabs.size(); ++i) {
19352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Tab* tab = tabs[i];
19365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini)
19375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      x += kMiniToNonMiniGap;
19385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect new_bounds = tab->bounds();
19395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_bounds.set_origin(gfx::Point(x, 0));
19405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bounds->push_back(new_bounds);
19415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    x += tab->width() + tab_h_offset();
19425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
19435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
19445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)int TabStrip::GetSizeNeededForTabs(const std::vector<Tab*>& tabs) {
19465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int width = 0;
19475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tabs.size(); ++i) {
19482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Tab* tab = tabs[i];
19495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    width += tab->width();
19505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini)
19515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      width += kMiniToNonMiniGap;
19525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
19535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tabs.size() > 0)
19545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    width += tab_h_offset() * static_cast<int>(tabs.size() - 1);
19555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return width;
19565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
19575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::RemoveTabFromViewModel(int index) {
19595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We still need to paint the tab until we actually remove it. Put it
19605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // in tabs_closing_map_ so we can find it.
19615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tabs_closing_map_[index].push_back(tab_at(index));
19625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateTabsClosingMap(index + 1, -1);
19635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tabs_.Remove(index);
19645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
19655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::RemoveAndDeleteTab(Tab* tab) {
19672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<Tab> deleter(tab);
19685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (TabsClosingMap::iterator i(tabs_closing_map_.begin());
19695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != tabs_closing_map_.end(); ++i) {
19702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::vector<Tab*>::iterator j =
19715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::find(i->second.begin(), i->second.end(), tab);
19725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (j != i->second.end()) {
19735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      i->second.erase(j);
19745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (i->second.empty())
19755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tabs_closing_map_.erase(i);
19765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
19775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
19785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
19795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTREACHED();
19805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
19815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::UpdateTabsClosingMap(int index, int delta) {
19835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tabs_closing_map_.empty())
19845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
19855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
19865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (delta == -1 &&
19875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tabs_closing_map_.find(index - 1) != tabs_closing_map_.end() &&
19885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tabs_closing_map_.find(index) != tabs_closing_map_.end()) {
19892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::vector<Tab*>& tabs(tabs_closing_map_[index]);
19905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tabs_closing_map_[index - 1].insert(
19915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tabs_closing_map_[index - 1].end(), tabs.begin(), tabs.end());
19925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
19935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabsClosingMap updated_map;
19945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (TabsClosingMap::iterator i(tabs_closing_map_.begin());
19955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != tabs_closing_map_.end(); ++i) {
19965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i->first > index)
19975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      updated_map[i->first + delta] = i->second;
19985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (i->first < index)
19995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      updated_map[i->first] = i->second;
20005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (delta > 0 && tabs_closing_map_.find(index) != tabs_closing_map_.end())
20025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    updated_map[index + delta] = tabs_closing_map_[index];
20035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tabs_closing_map_.swap(updated_map);
20045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
20055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::StartedDraggingTabs(const std::vector<Tab*>& tabs) {
20077d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Let the controller know that the user started dragging tabs.
20087d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  controller()->OnStartedDraggingTabs();
20097d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
20105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Hide the new tab button immediately if we didn't originate the drag.
20115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drag_controller_.get())
20125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    newtab_button_->SetVisible(false);
20135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForAnimation();
20155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Reset dragging state of existing tabs.
20175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < tab_count(); ++i)
20185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_at(i)->set_dragging(false);
20195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tabs.size(); ++i) {
20215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tabs[i]->set_dragging(true);
20225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bounds_animator_.StopAnimatingView(tabs[i]);
20235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Move the dragged tabs to their ideal bounds.
20265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateIdealBounds();
20275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Sets the bounds of the dragged tabs.
20295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tabs.size(); ++i) {
20302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int tab_data_index = GetModelIndexOfTab(tabs[i]);
20315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_NE(-1, tab_data_index);
20325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tabs[i]->SetBoundsRect(ideal_bounds(tab_data_index));
20335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
20355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
20365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::DraggedTabsDetached() {
20387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Let the controller know that the user is not dragging this tabstrip's tabs
20397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // anymore.
20407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  controller()->OnStoppedDraggingTabs();
20415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_->SetVisible(true);
20425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
20435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::StoppedDraggingTabs(const std::vector<Tab*>& tabs,
20455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   const std::vector<int>& initial_positions,
20465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   bool move_only,
20475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   bool completed) {
20487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Let the controller know that the user stopped dragging tabs.
20497d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  controller()->OnStoppedDraggingTabs();
20507d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
20515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_->SetVisible(true);
20525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (move_only && touch_layout_.get()) {
20535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (completed) {
20545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      touch_layout_->SizeToFit();
20555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
20565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetIdealBoundsFromPositions(initial_positions);
20575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
20585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_first_tab = true;
20605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tabs.size(); ++i)
20615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StoppedDraggingTab(tabs[i], &is_first_tab);
20625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
20635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::StoppedDraggingTab(Tab* tab, bool* is_first_tab) {
20652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int tab_data_index = GetModelIndexOfTab(tab);
20665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_data_index == -1) {
20675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The tab was removed before the drag completed. Don't do anything.
20685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
20695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (*is_first_tab) {
20725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *is_first_tab = false;
20735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrepareForAnimation();
20745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Animate the view back to its correct position.
20765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GenerateIdealBounds();
20775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AnimateToIdealBounds();
20785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
20795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_animator_.AnimateViewTo(tab, ideal_bounds(tab_data_index));
20805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Install a delegate to reset the dragging state when done. We have to leave
20815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // dragging true for the tab otherwise it'll draw beneath the new tab button.
20825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_animator_.SetAnimationDelegate(
20835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab, new ResetDraggingStateDelegate(tab), true);
20845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
20855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::OwnDragController(TabDragController* controller) {
20872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Typically, ReleaseDragController() and OwnDragController() calls are paired
20882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // via corresponding calls to TabDragController::Detach() and
20892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TabDragController::Attach(). There is one exception to that rule: when a
20902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // drag might start, we create a TabDragController that is owned by the
20912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // potential source tabstrip in MaybeStartDrag(). If a drag actually starts,
20922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // we then call Attach() on the source tabstrip, but since the source tabstrip
20932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // already owns the TabDragController, so we don't need to do anything.
20942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (controller != drag_controller_.get())
20952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    drag_controller_.reset(controller);
20965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
20975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::DestroyDragController() {
20995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_->SetVisible(true);
21005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drag_controller_.reset();
21015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
21025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabDragController* TabStrip::ReleaseDragController() {
21045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return drag_controller_.release();
21055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
21065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::PaintClosingTabs(gfx::Canvas* canvas, int index) {
21085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tabs_closing_map_.find(index) == tabs_closing_map_.end())
21095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
21105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const std::vector<Tab*>& tabs = tabs_closing_map_[index];
21122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (std::vector<Tab*>::const_reverse_iterator i(tabs.rbegin());
21135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       i != tabs.rend(); ++i) {
21145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    (*i)->Paint(canvas);
21155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
21165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
21175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::UpdateLayoutTypeFromMouseEvent(views::View* source,
21195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                              const ui::MouseEvent& event) {
21205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetAdjustLayout())
21215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
21225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The following code attempts to switch to TAB_STRIP_LAYOUT_SHRINK when the
21245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mouse exits the tabstrip (or the mouse is pressed on a stacked tab) and
21255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TAB_STRIP_LAYOUT_STACKED when a touch device is used. This is made
21265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // problematic by windows generating mouse move events that do not clearly
21275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // indicate the move is the result of a touch device. This assumes a real
21285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mouse is used if |kMouseMoveCountBeforeConsiderReal| mouse move events are
21295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // received within the time window |kMouseMoveTimeMS|.  At the time we get a
21305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // mouse press we know whether its from a touch device or not, but we don't
21315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // layout then else everything shifts. Instead we wait for the release.
21325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //
21335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(sky): revisit this when touch events are really plumbed through.
21345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (event.type()) {
21365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_PRESSED:
21375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mouse_move_count_ = 0;
21385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_mouse_move_time_ = base::TimeTicks();
21395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetResetToShrinkOnExit((event.flags() & ui::EF_FROM_TOUCH) == 0);
21405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (reset_to_shrink_on_exit_ && touch_layout_.get()) {
21415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        gfx::Point tab_strip_point(event.location());
21425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        views::View::ConvertPointToTarget(source, this, &tab_strip_point);
21435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        Tab* tab = FindTabForEvent(tab_strip_point);
21442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        if (tab && touch_layout_->IsStacked(GetModelIndexOfTab(tab))) {
21455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          SetLayoutType(TAB_STRIP_LAYOUT_SHRINK, true);
21465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          controller_->LayoutTypeMaybeChanged();
21475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
21485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
21495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
21505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_MOVED: {
21525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(USE_ASH)
21535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Ash does not synthesize mouse events from touch events.
21545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetResetToShrinkOnExit(true);
21555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
21565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::Point location(event.location());
21575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ConvertPointToTarget(source, this, &location);
21585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (location == last_mouse_move_location_)
21595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;  // Ignore spurious moves.
21605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_mouse_move_location_ = location;
21615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ((event.flags() & ui::EF_FROM_TOUCH) == 0 &&
21625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (event.flags() & ui::EF_IS_SYNTHESIZED) == 0) {
21635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if ((base::TimeTicks::Now() - last_mouse_move_time_).InMilliseconds() <
21645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            kMouseMoveTimeMS) {
21655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (mouse_move_count_++ == kMouseMoveCountBeforeConsiderReal)
21665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            SetResetToShrinkOnExit(true);
21675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
21685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          mouse_move_count_ = 1;
21695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last_mouse_move_time_ = base::TimeTicks::Now();
21705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
21715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
21725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last_mouse_move_time_ = base::TimeTicks();
21735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
21745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
21755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
21765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
21775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case ui::ET_MOUSE_RELEASED: {
21795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      gfx::Point location(event.location());
21805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ConvertPointToTarget(source, this, &location);
21815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_mouse_move_location_ = location;
21825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mouse_move_count_ = 0;
21835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_mouse_move_time_ = base::TimeTicks();
21845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if ((event.flags() & ui::EF_FROM_TOUCH) == ui::EF_FROM_TOUCH) {
21855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetLayoutType(TAB_STRIP_LAYOUT_STACKED, true);
21865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        controller_->LayoutTypeMaybeChanged();
21875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
21885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
21895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
21905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
21925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
21935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
21945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
21955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabStrip::GetCurrentTabWidths(double* unselected_width,
21972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   double* selected_width) const {
21982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *unselected_width = current_unselected_width_;
21992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *selected_width = current_selected_width_;
22002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
22012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
22025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::GetDesiredTabWidths(int tab_count,
22035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   int mini_tab_count,
22045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   double* unselected_width,
22055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   double* selected_width) const {
22065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(tab_count >= 0 && mini_tab_count >= 0 && mini_tab_count <= tab_count);
22075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const double min_unselected_width = Tab::GetMinimumUnselectedSize().width();
22085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const double min_selected_width = Tab::GetMinimumSelectedSize().width();
22095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *unselected_width = min_unselected_width;
22115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *selected_width = min_selected_width;
22125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_count == 0) {
22145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Return immediately to avoid divide-by-zero below.
22155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
22165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
22175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Determine how much space we can actually allocate to tabs.
22195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int available_width;
22205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (available_width_for_tabs_ < 0) {
22215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    available_width = width() - new_tab_button_width();
22225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
22235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Interesting corner case: if |available_width_for_tabs_| > the result
22245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // of the calculation in the conditional arm above, the strip is in
22255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // overflow.  We can either use the specified width or the true available
22265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // width here; the first preserves the consistent "leave the last tab under
22275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the user's mouse so they can close many tabs" behavior at the cost of
22285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // prolonging the glitchy appearance of the overflow state, while the second
22295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // gets us out of overflow as soon as possible but forces the user to move
22305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // their mouse for a few tabs' worth of closing.  We choose visual
22315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // imperfection over behavioral imperfection and select the first option.
22325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    available_width = available_width_for_tabs_;
22335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
22345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (mini_tab_count > 0) {
22365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    available_width -= mini_tab_count * (Tab::GetMiniWidth() + tab_h_offset());
22375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_count -= mini_tab_count;
22385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tab_count == 0) {
22395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *selected_width = *unselected_width = Tab::GetStandardSize().width();
22405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
22415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
22425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Account for gap between the last mini-tab and first non-mini-tab.
22435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    available_width -= kMiniToNonMiniGap;
22445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
22455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Calculate the desired tab widths by dividing the available space into equal
22475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // portions.  Don't let tabs get larger than the "standard width" or smaller
22485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // than the minimum width for each type, respectively.
22495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int total_offset = tab_h_offset() * (tab_count - 1);
22505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const double desired_tab_width = std::min((static_cast<double>(
22515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      available_width - total_offset) / static_cast<double>(tab_count)),
22525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<double>(Tab::GetStandardSize().width()));
22535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *unselected_width = std::max(desired_tab_width, min_unselected_width);
22545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *selected_width = std::max(desired_tab_width, min_selected_width);
22555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When there are multiple tabs, we'll have one selected and some unselected
22575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // tabs.  If the desired width was between the minimum sizes of these types,
22585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // try to shrink the tabs with the smaller minimum.  For example, if we have
22595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a strip of width 10 with 4 tabs, the desired width per tab will be 2.5.  If
22605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // selected tabs have a minimum width of 4 and unselected tabs have a minimum
22615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // width of 1, the above code would set *unselected_width = 2.5,
22625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // *selected_width = 4, which results in a total width of 11.5.  Instead, we
22635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // want to set *unselected_width = 2, *selected_width = 4, for a total width
22645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // of 10.
22655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_count > 1) {
22665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((min_unselected_width < min_selected_width) &&
22675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (desired_tab_width < min_selected_width)) {
22685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Unselected width = (total width - selected width) / (num_tabs - 1)
22695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *unselected_width = std::max(static_cast<double>(
22705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          available_width - total_offset - min_selected_width) /
22715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<double>(tab_count - 1), min_unselected_width);
22725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if ((min_unselected_width > min_selected_width) &&
22735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               (desired_tab_width < min_unselected_width)) {
22745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Selected width = (total width - (unselected width * (num_tabs - 1)))
22755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *selected_width = std::max(available_width - total_offset -
22765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          (min_unselected_width * (tab_count - 1)), min_selected_width);
22775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
22785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
22795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
22805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::ResizeLayoutTabs() {
22825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We've been called back after the TabStrip has been emptied out (probably
22835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // just prior to the window being destroyed). We need to do nothing here or
22845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // else GetTabAt below will crash.
22855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_count() == 0)
22865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
22875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // It is critically important that this is unhooked here, otherwise we will
22895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // keep spying on messages forever.
22905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveMessageLoopObserver();
22915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
22925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in_tab_close_ = false;
22935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  available_width_for_tabs_ = -1;
22945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mini_tab_count = GetMiniTabCount();
22955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (mini_tab_count == tab_count()) {
22965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Only mini-tabs, we know the tab widths won't have changed (all
22975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // mini-tabs have the same width), so there is nothing to do.
22985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
22995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
23002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Don't try and avoid layout based on tab sizes. If tabs are small enough
23012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // then the width of the active tab may not change, but other widths may
23022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // have. This is particularly important if we've overflowed (all tabs are at
23032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the min).
23042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  StartResizeLayoutAnimation();
23055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::ResizeLayoutTabsFromTouch() {
23085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't resize if the user is interacting with the tabstrip.
23095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drag_controller_.get())
23105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ResizeLayoutTabs();
23115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
23125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StartResizeLayoutTabsFromTouchTimer();
23135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StartResizeLayoutTabsFromTouchTimer() {
23165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resize_layout_timer_.Stop();
23175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  resize_layout_timer_.Start(
23185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE, base::TimeDelta::FromMilliseconds(kTouchResizeLayoutTimeMS),
23195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this, &TabStrip::ResizeLayoutTabsFromTouch);
23205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::SetTabBoundsForDrag(const std::vector<gfx::Rect>& tab_bounds) {
23235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StopAnimating(false);
23245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(tab_count(), static_cast<int>(tab_bounds.size()));
23255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < tab_count(); ++i)
23265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_at(i)->SetBoundsRect(tab_bounds[i]);
23275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::AddMessageLoopObserver() {
23305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!mouse_watcher_.get()) {
23315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mouse_watcher_.reset(
23325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new views::MouseWatcher(
23335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            new views::MouseWatcherViewHost(
23345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                this, gfx::Insets(0, 0, kTabStripAnimationVSlop, 0)),
23355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            this));
23365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
23375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mouse_watcher_->Start();
23385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::RemoveMessageLoopObserver() {
23415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  mouse_watcher_.reset(NULL);
23425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Rect TabStrip::GetDropBounds(int drop_index,
23455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  bool drop_before,
23465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  bool* is_beneath) {
23475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(drop_index, -1);
23485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int center_x;
23495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drop_index < tab_count()) {
23505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* tab = tab_at(drop_index);
23515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (drop_before)
23525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      center_x = tab->x() - (tab_h_offset() / 2);
23535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else
23545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      center_x = tab->x() + (tab->width() / 2);
23555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
23565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* last_tab = tab_at(drop_index - 1);
23575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    center_x = last_tab->x() + last_tab->width() + (tab_h_offset() / 2);
23585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
23595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Mirror the center point if necessary.
23615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  center_x = GetMirroredXInView(center_x);
23625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Determine the screen bounds.
23645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point drop_loc(center_x - drop_indicator_width / 2,
23655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      -drop_indicator_height);
23665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ConvertPointToScreen(this, &drop_loc);
23675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_indicator_width,
23685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        drop_indicator_height);
23695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the rect doesn't fit on the monitor, push the arrow to the bottom.
237168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Screen* screen = gfx::Screen::GetScreenFor(GetWidget()->GetNativeView());
237268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Display display = screen->GetDisplayMatching(drop_bounds);
237368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  *is_beneath = !display.bounds().Contains(drop_bounds);
23745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (*is_beneath)
23755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drop_bounds.Offset(0, drop_bounds.height() + height());
23765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return drop_bounds;
23785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
23795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::UpdateDropIndex(const DropTargetEvent& event) {
23815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the UI layout is right-to-left, we need to mirror the mouse
23825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // coordinates since we calculate the drop index based on the
23835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // original (and therefore non-mirrored) positions of the tabs.
23845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int x = GetMirroredXInView(event.x());
23855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't allow replacing the urls of mini-tabs.
23865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = GetMiniTabCount(); i < tab_count(); ++i) {
23875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* tab = tab_at(i);
23885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int tab_max_x = tab->x() + tab->width();
23895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int hot_width = tab->width() / kTabEdgeRatioInverse;
23905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (x < tab_max_x) {
23915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (x < tab->x() + hot_width)
23925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetDropIndex(i, true);
23935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else if (x >= tab_max_x - hot_width)
23945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetDropIndex(i + 1, true);
23955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else
23965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetDropIndex(i, false);
23975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
23985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
23995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
24005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The drop isn't over a tab, add it to the end.
24025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetDropIndex(tab_count(), true);
24035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::SetDropIndex(int tab_data_index, bool drop_before) {
24065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Let the controller know of the index update.
24075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  controller()->OnDropIndexUpdate(tab_data_index, drop_before);
24085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_data_index == -1) {
24105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (drop_info_.get())
24115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drop_info_.reset(NULL);
24125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
24135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
24145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (drop_info_.get() && drop_info_->drop_index == tab_data_index &&
24165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drop_info_->drop_before == drop_before) {
24175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
24185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
24195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool is_beneath;
24215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect drop_bounds = GetDropBounds(tab_data_index, drop_before,
24225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        &is_beneath);
24235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!drop_info_.get()) {
24252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    drop_info_.reset(
24262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        new DropInfo(tab_data_index, drop_before, !is_beneath, GetWidget()));
24275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
24285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drop_info_->drop_index = tab_data_index;
24295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    drop_info_->drop_before = drop_before;
24305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (is_beneath == drop_info_->point_down) {
24315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drop_info_->point_down = !is_beneath;
24325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drop_info_->arrow_view->SetImage(
24335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          GetDropArrowImage(drop_info_->point_down));
24345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
24355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
24365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Reposition the window. Need to show it too as the window is initially
24385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // hidden.
24395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drop_info_->arrow_window->SetBounds(drop_bounds);
24405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  drop_info_->arrow_window->Show();
24415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabStrip::GetDropEffect(const ui::DropTargetEvent& event) {
24445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int source_ops = event.source_operations();
24455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source_ops & ui::DragDropTypes::DRAG_COPY)
24465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::DragDropTypes::DRAG_COPY;
24475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source_ops & ui::DragDropTypes::DRAG_LINK)
24485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::DragDropTypes::DRAG_LINK;
24495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::DragDropTypes::DRAG_MOVE;
24505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
24535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::ImageSkia* TabStrip::GetDropArrowImage(bool is_down) {
24545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
24555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      is_down ? IDR_TAB_DROP_DOWN : IDR_TAB_DROP_UP);
24565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TabStrip::DropInfo ----------------------------------------------------------
24595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TabStrip::DropInfo::DropInfo(int drop_index,
24612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             bool drop_before,
24622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             bool point_down,
24632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                             views::Widget* context)
24645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : drop_index(drop_index),
24655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      drop_before(drop_before),
2466a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      point_down(point_down),
2467a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      file_supported(true) {
24685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  arrow_view = new views::ImageView;
24695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  arrow_view->SetImage(GetDropArrowImage(point_down));
24705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  arrow_window = new views::Widget;
24725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
24735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  params.keep_on_top = true;
2474eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
24755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  params.accept_events = false;
24765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  params.can_activate = false;
24775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  params.bounds = gfx::Rect(drop_indicator_width, drop_indicator_height);
24782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  params.context = context->GetNativeView();
24795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  arrow_window->Init(params);
24805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  arrow_window->SetContentsView(arrow_view);
24815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TabStrip::DropInfo::~DropInfo() {
24845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Close eventually deletes the window, which deletes arrow_view too.
24855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  arrow_window->Close();
24865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)///////////////////////////////////////////////////////////////////////////////
24895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::PrepareForAnimation() {
24915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!IsDragSessionActive() && !TabDragController::IsAttachedTo(this)) {
24925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < tab_count(); ++i)
24935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab_at(i)->set_dragging(false);
24945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
24955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
24965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::GenerateIdealBounds() {
24982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int new_tab_y = 0;
24995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (touch_layout_.get()) {
25015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tabs_.view_size() == 0)
25025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
25035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int new_tab_x = tabs_.ideal_bounds(tabs_.view_size() - 1).right() +
25055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        newtab_button_h_offset();
25065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y));
25075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
25085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
25095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double unselected, selected;
25115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetDesiredTabWidths(tab_count(), GetMiniTabCount(), &unselected, &selected);
25125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_unselected_width_ = unselected;
25135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  current_selected_width_ = selected;
25145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // NOTE: This currently assumes a tab's height doesn't differ based on
25165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // selected state or the number of tabs in the strip!
25175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int tab_height = Tab::GetStandardSize().height();
25185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int first_non_mini_index = 0;
25195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double tab_x = GenerateIdealBoundsForMiniTabs(&first_non_mini_index);
25205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = first_non_mini_index; i < tab_count(); ++i) {
25215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Tab* tab = tab_at(i);
25225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(!tab->data().mini);
25235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double tab_width = tab->IsActive() ? selected : unselected;
25245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double end_of_tab = tab_x + tab_width;
25255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int rounded_tab_x = Round(tab_x);
25265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_ideal_bounds(
25275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        i,
25285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        gfx::Rect(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
25295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  tab_height));
25305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_x = end_of_tab + tab_h_offset();
25315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
25325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Update bounds of new tab button.
25345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int new_tab_x;
25355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (abs(Round(unselected) - Tab::GetStandardSize().width()) > 1 &&
25365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !in_tab_close_) {
25375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We're shrinking tabs, so we need to anchor the New Tab button to the
25385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // right edge of the TabStrip's bounds, rather than the right edge of the
25395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // right-most Tab, otherwise it'll bounce when animating.
25405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_tab_x = width() - newtab_button_bounds_.width();
25415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
25425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    new_tab_x = Round(tab_x - tab_h_offset()) + newtab_button_h_offset();
25435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
25445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y));
25455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
25465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabStrip::GenerateIdealBoundsForMiniTabs(int* first_non_mini_index) {
25485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int next_x = 0;
25495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mini_width = Tab::GetMiniWidth();
25505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int tab_height = Tab::GetStandardSize().height();
25515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int index = 0;
25525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (; index < tab_count() && tab_at(index)->data().mini; ++index) {
25535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_ideal_bounds(index,
25545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     gfx::Rect(next_x, 0, mini_width, tab_height));
25555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_x += mini_width + tab_h_offset();
25565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
25575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (index > 0 && index < tab_count())
25585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    next_x += kMiniToNonMiniGap;
25595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (first_non_mini_index)
25605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *first_non_mini_index = index;
25615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return next_x;
25625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
25635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25647dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch// static
25657dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochint TabStrip::new_tab_button_width() {
25665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return newtab_button_asset_width() + newtab_button_h_offset();
25675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
25685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25697dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch// static
25707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochint TabStrip::button_v_offset() {
25717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return newtab_button_v_offset();
25727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
25737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
25747dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochint TabStrip::tab_area_width() const {
25757dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  return width() - new_tab_button_width();
25767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
25777dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
25785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StartResizeLayoutAnimation() {
25795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForAnimation();
25805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateIdealBounds();
25815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AnimateToIdealBounds();
25825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
25835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StartMiniTabAnimation() {
25855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  in_tab_close_ = false;
25865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  available_width_for_tabs_ = -1;
25875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForAnimation();
25895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateIdealBounds();
25915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AnimateToIdealBounds();
25925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
25935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
25945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::StartMouseInitiatedRemoveTabAnimation(int model_index) {
25955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The user initiated the close. We want to persist the bounds of all the
25965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // existing tabs, so we manually shift ideal_bounds then animate.
25975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Tab* tab_closing = tab_at(model_index);
25985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int delta = tab_closing->width() + tab_h_offset();
25995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the tab being closed is a mini-tab next to a non-mini-tab, be sure to
26005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // add the extra padding.
26015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_NE(model_index + 1, tab_count());
26025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_closing->data().mini && model_index + 1 < tab_count() &&
26035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      !tab_at(model_index + 1)->data().mini) {
26045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delta += kMiniToNonMiniGap;
26055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
26065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = model_index + 1; i < tab_count(); ++i) {
26085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Rect bounds = ideal_bounds(i);
26095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bounds.set_x(bounds.x() - delta);
26105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    set_ideal_bounds(i, bounds);
26115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
26125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  newtab_button_bounds_.set_x(newtab_button_bounds_.x() - delta);
26145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForAnimation();
26165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab_closing->set_closing(true);
26185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We still need to paint the tab until we actually remove it. Put it in
26205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // tabs_closing_map_ so we can find it.
26215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RemoveTabFromViewModel(model_index);
26225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AnimateToIdealBounds();
26245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect tab_bounds = tab_closing->bounds();
26265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  tab_bounds.set_width(0);
26275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_animator_.AnimateViewTo(tab_closing, tab_bounds);
26285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Register delegate to do cleanup when done, BoundsAnimator takes
26305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ownership of RemoveTabDelegate.
26315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bounds_animator_.SetAnimationDelegate(
26325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab_closing,
26335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new RemoveTabDelegate(this, tab_closing),
26345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      true);
26355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::IsPointInTab(Tab* tab,
26385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            const gfx::Point& point_in_tabstrip_coords) {
26395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Point point_in_tab_coords(point_in_tabstrip_coords);
26405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  View::ConvertPointToTarget(this, tab, &point_in_tab_coords);
26415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return tab->HitTestPoint(point_in_tab_coords);
26425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TabStrip::GetStartXForNormalTabs() const {
26455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mini_tab_count = GetMiniTabCount();
26465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (mini_tab_count == 0)
26475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
26485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return mini_tab_count * (Tab::GetMiniWidth() + tab_h_offset()) +
26495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      kMiniToNonMiniGap;
26505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Tab* TabStrip::FindTabForEvent(const gfx::Point& point) {
26532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (touch_layout_.get()) {
26542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int active_tab_index = touch_layout_->active_index();
26552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (active_tab_index != -1) {
26562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Tab* tab = FindTabForEventFrom(point, active_tab_index, -1);
26572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (!tab)
26582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        tab = FindTabForEventFrom(point, active_tab_index + 1, 1);
26592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return tab;
26602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else if (tab_count()) {
26612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return FindTabForEventFrom(point, 0, 1);
26622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
26632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
26642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (int i = 0; i < tab_count(); ++i) {
26652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (IsPointInTab(tab_at(i), point))
26662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return tab_at(i);
26672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
26685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
26692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return NULL;
26705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Tab* TabStrip::FindTabForEventFrom(const gfx::Point& point,
26735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   int start,
26745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   int delta) {
26755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // |start| equals tab_count() when there are only pinned tabs.
26765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (start == tab_count())
26775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    start += delta;
26785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = start; i >= 0 && i < tab_count(); i += delta) {
26795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (IsPointInTab(tab_at(i), point))
26805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return tab_at(i);
26815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
26825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
26835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
26845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)views::View* TabStrip::FindTabHitByPoint(const gfx::Point& point) {
2686c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The display order doesn't necessarily match the child list order, so we
2687c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // walk the display list hit-testing Tabs. Since the active tab always
2688c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // renders on top of adjacent tabs, it needs to be hit-tested before any
2689c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // left-adjacent Tab, so we look ahead for it as we walk.
2690c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (int i = 0; i < tab_count(); ++i) {
2691c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    Tab* next_tab = i < (tab_count() - 1) ? tab_at(i + 1) : NULL;
2692c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (next_tab && next_tab->IsActive() && IsPointInTab(next_tab, point))
2693c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return next_tab;
2694c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (IsPointInTab(tab_at(i), point))
2695c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return tab_at(i);
2696c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
2697c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2698c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return NULL;
2699c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
2700c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
27015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::vector<int> TabStrip::GetTabXCoordinates() {
27025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<int> results;
27035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int i = 0; i < tab_count(); ++i)
27045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    results.push_back(ideal_bounds(i).x());
27055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return results;
27065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::SwapLayoutIfNecessary() {
27095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool needs_touch = NeedsTouchLayout();
27105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool using_touch = touch_layout_.get() != NULL;
27115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (needs_touch == using_touch)
27125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
27135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (needs_touch) {
27155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    gfx::Size tab_size(Tab::GetMinimumSelectedSize());
27165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_size.set_width(Tab::GetTouchWidth());
27172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    touch_layout_.reset(new StackedTabStripLayout(
27185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            tab_size,
27195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            tab_h_offset(),
27205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            kStackedPadding,
27215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            kMaxStackedCount,
27225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            &tabs_));
27235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_->SetWidth(width() - new_tab_button_width());
27245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This has to be after SetWidth() as SetWidth() is going to reset the
27252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // bounds of the mini-tabs (since StackedTabStripLayout doesn't yet know how
27265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // many mini-tabs there are).
27275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GenerateIdealBoundsForMiniTabs(NULL);
27285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_->SetXAndMiniCount(GetStartXForNormalTabs(),
27295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    GetMiniTabCount());
27305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_->SetActiveIndex(controller_->GetActiveIndex());
27315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
27325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    touch_layout_.reset();
27335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
27345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForAnimation();
27355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GenerateIdealBounds();
27365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AnimateToIdealBounds();
27375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::NeedsTouchLayout() const {
27405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (layout_type_ == TAB_STRIP_LAYOUT_SHRINK)
27415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
27425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int mini_tab_count = GetMiniTabCount();
27445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int normal_count = tab_count() - mini_tab_count;
27455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (normal_count <= 1 || normal_count == mini_tab_count)
27465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
27475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int x = GetStartXForNormalTabs();
27485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int available_width = width() - x - new_tab_button_width();
27495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (Tab::GetTouchWidth() * normal_count +
27505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          tab_h_offset() * (normal_count - 1)) > available_width;
27515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TabStrip::SetResetToShrinkOnExit(bool value) {
27545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetAdjustLayout())
27555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
27565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value && layout_type_ == TAB_STRIP_LAYOUT_SHRINK)
27585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    value = false;  // We're already at TAB_STRIP_LAYOUT_SHRINK.
27595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value == reset_to_shrink_on_exit_)
27615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
27625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reset_to_shrink_on_exit_ = value;
27645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Add an observer so we know when the mouse moves out of the tabstrip.
27655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (reset_to_shrink_on_exit_)
27665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AddMessageLoopObserver();
27675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
27685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    RemoveMessageLoopObserver();
27695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
27705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TabStrip::GetAdjustLayout() const {
27725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!adjust_layout_)
27735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
27745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#if defined(USE_AURA)
2776f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return chrome::GetHostDesktopTypeForNativeView(
2777f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      GetWidget()->GetNativeView()) == chrome::HOST_DESKTOP_TYPE_ASH;
2778f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#else
27795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ui::GetDisplayLayout() != ui::LAYOUT_TOUCH)
27805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
27815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
27825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
27835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
27845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2785