- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / controls / table / table_header.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/views/controls/table/table_header.h"
6
7 #include "third_party/skia/include/core/SkColor.h"
8 #include "ui/gfx/canvas.h"
9 #include "ui/native_theme/native_theme.h"
10 #include "ui/views/background.h"
11 #include "ui/views/controls/table/table_utils.h"
12 #include "ui/views/controls/table/table_view.h"
13
14 #if defined(USE_AURA)
15 #include "ui/base/cursor/cursor.h"
16 #endif
17
18 namespace views {
19
20 namespace {
21
22 const int kVerticalPadding = 4;
23
24 // The minimum width we allow a column to go down to.
25 const int kMinColumnWidth = 10;
26
27 // Distace from edge columns can be resized by.
28 const int kResizePadding = 5;
29
30 // Amount of space above/below the separator.
31 const int kSeparatorPadding = 4;
32
33 const SkColor kTextColor = SK_ColorBLACK;
34 const SkColor kBackgroundColor1 = SkColorSetRGB(0xF9, 0xF9, 0xF9);
35 const SkColor kBackgroundColor2 = SkColorSetRGB(0xE8, 0xE8, 0xE8);
36 const SkColor kSeparatorColor = SkColorSetRGB(0xAA, 0xAA, 0xAA);
37
38 // Size of the sort indicator (doesn't include padding).
39 const int kSortIndicatorSize = 8;
40
41 gfx::NativeCursor GetResizeCursor() {
42 #if defined(USE_AURA)
43   return ui::kCursorColumnResize;
44 #elif defined(OS_WIN)
45   static HCURSOR g_hand_cursor = LoadCursor(NULL, IDC_SIZEWE);
46   return g_hand_cursor;
47 #endif
48 }
49
50 }  // namespace
51
52 // static
53 const int TableHeader::kHorizontalPadding = 7;
54 // static
55 const int TableHeader::kSortIndicatorWidth = kSortIndicatorSize +
56     TableHeader::kHorizontalPadding * 2;
57
58 typedef std::vector<TableView::VisibleColumn> Columns;
59
60 TableHeader::TableHeader(TableView* table) : table_(table) {
61   set_background(Background::CreateVerticalGradientBackground(
62                      kBackgroundColor1, kBackgroundColor2));
63 }
64
65 TableHeader::~TableHeader() {
66 }
67
68 void TableHeader::Layout() {
69   SetBounds(x(), y(), table_->width(), GetPreferredSize().height());
70 }
71
72 void TableHeader::OnPaint(gfx::Canvas* canvas) {
73   // Paint the background and a separator at the bottom. The separator color
74   // matches that of the border around the scrollview.
75   OnPaintBackground(canvas);
76   SkColor border_color = GetNativeTheme()->GetSystemColor(
77       ui::NativeTheme::kColorId_UnfocusedBorderColor);
78   canvas->DrawLine(gfx::Point(0, height() - 1),
79                    gfx::Point(width(), height() - 1), border_color);
80
81   const Columns& columns = table_->visible_columns();
82   const int sorted_column_id = table_->sort_descriptors().empty() ? -1 :
83       table_->sort_descriptors()[0].column_id;
84   for (size_t i = 0; i < columns.size(); ++i) {
85     if (columns[i].width >= 2) {
86       const int separator_x = GetMirroredXInView(
87           columns[i].x + columns[i].width - 1);
88       canvas->DrawLine(gfx::Point(separator_x, kSeparatorPadding),
89                        gfx::Point(separator_x, height() - kSeparatorPadding),
90                        kSeparatorColor);
91     }
92
93     const int x = columns[i].x + kHorizontalPadding;
94     int width = columns[i].width - kHorizontalPadding - kHorizontalPadding;
95     if (width <= 0)
96       continue;
97
98     const int title_width = font_.GetStringWidth(columns[i].column.title);
99     const bool paint_sort_indicator =
100         (columns[i].column.id == sorted_column_id &&
101          title_width + kSortIndicatorWidth <= width);
102
103     if (paint_sort_indicator &&
104         columns[i].column.alignment == ui::TableColumn::RIGHT) {
105       width -= kSortIndicatorWidth;
106     }
107
108     canvas->DrawStringInt(
109         columns[i].column.title, font_, kTextColor,
110         GetMirroredXWithWidthInView(x, width), kVerticalPadding, width,
111         height() - kVerticalPadding * 2,
112         TableColumnAlignmentToCanvasAlignment(columns[i].column.alignment));
113
114     if (paint_sort_indicator) {
115       SkPaint paint;
116       paint.setColor(kTextColor);
117       paint.setStyle(SkPaint::kFill_Style);
118       paint.setAntiAlias(true);
119
120       int indicator_x = 0;
121       ui::TableColumn::Alignment alignment = columns[i].column.alignment;
122       if (base::i18n::IsRTL()) {
123         if (alignment == ui::TableColumn::LEFT)
124           alignment = ui::TableColumn::RIGHT;
125         else if (alignment == ui::TableColumn::RIGHT)
126           alignment = ui::TableColumn::LEFT;
127       }
128       switch (alignment) {
129         case ui::TableColumn::LEFT:
130           indicator_x = x + title_width;
131           break;
132         case ui::TableColumn::CENTER:
133           indicator_x = x + width / 2;
134           break;
135         case ui::TableColumn::RIGHT:
136           indicator_x = x + width;
137           break;
138       }
139
140       const int scale = base::i18n::IsRTL() ? -1 : 1;
141       indicator_x += (kSortIndicatorWidth - kSortIndicatorSize) / 2;
142       indicator_x = GetMirroredXInView(indicator_x);
143       int indicator_y = height() / 2 - kSortIndicatorSize / 2;
144       SkPath indicator_path;
145       if (table_->sort_descriptors()[0].ascending) {
146         indicator_path.moveTo(
147             SkIntToScalar(indicator_x),
148             SkIntToScalar(indicator_y + kSortIndicatorSize));
149         indicator_path.lineTo(
150             SkIntToScalar(indicator_x + kSortIndicatorSize * scale),
151             SkIntToScalar(indicator_y + kSortIndicatorSize));
152         indicator_path.lineTo(
153             SkIntToScalar(indicator_x + kSortIndicatorSize / 2 * scale),
154             SkIntToScalar(indicator_y));
155       } else {
156         indicator_path.moveTo(SkIntToScalar(indicator_x),
157                               SkIntToScalar(indicator_y));
158         indicator_path.lineTo(
159             SkIntToScalar(indicator_x + kSortIndicatorSize * scale),
160             SkIntToScalar(indicator_y));
161         indicator_path.lineTo(
162             SkIntToScalar(indicator_x + kSortIndicatorSize / 2 * scale),
163             SkIntToScalar(indicator_y + kSortIndicatorSize));
164       }
165       indicator_path.close();
166       canvas->DrawPath(indicator_path, paint);
167     }
168   }
169 }
170
171 gfx::Size TableHeader::GetPreferredSize() {
172   return gfx::Size(1, kVerticalPadding * 2 + font_.GetHeight());
173 }
174
175 gfx::NativeCursor TableHeader::GetCursor(const ui::MouseEvent& event) {
176   return GetResizeColumn(GetMirroredXInView(event.x())) != -1 ?
177       GetResizeCursor() : View::GetCursor(event);
178 }
179
180 bool TableHeader::OnMousePressed(const ui::MouseEvent& event) {
181   if (event.IsOnlyLeftMouseButton() && StartResize(event))
182     return true;
183
184   // Return false so that context menus on ancestors work.
185   return false;
186 }
187
188 bool TableHeader::OnMouseDragged(const ui::MouseEvent& event) {
189   ContinueResize(event);
190   return true;
191 }
192
193 void TableHeader::OnMouseReleased(const ui::MouseEvent& event) {
194   const bool was_resizing = resize_details_ != NULL;
195   resize_details_.reset();
196   if (!was_resizing && event.IsOnlyLeftMouseButton())
197     ToggleSortOrder(event);
198 }
199
200 void TableHeader::OnMouseCaptureLost() {
201   if (is_resizing()) {
202     table_->SetVisibleColumnWidth(resize_details_->column_index,
203                                   resize_details_->initial_width);
204   }
205   resize_details_.reset();
206 }
207
208 void TableHeader::OnGestureEvent(ui::GestureEvent* event) {
209   switch (event->type()) {
210     case ui::ET_GESTURE_TAP:
211       if (!resize_details_.get())
212         ToggleSortOrder(*event);
213       break;
214     case ui::ET_GESTURE_SCROLL_BEGIN:
215       StartResize(*event);
216       break;
217     case ui::ET_GESTURE_SCROLL_UPDATE:
218       ContinueResize(*event);
219       break;
220     case ui::ET_GESTURE_SCROLL_END:
221       resize_details_.reset();
222       break;
223     default:
224       return;
225   }
226   event->SetHandled();
227 }
228
229 bool TableHeader::StartResize(const ui::LocatedEvent& event) {
230   if (is_resizing())
231     return false;
232
233   const int index = GetResizeColumn(GetMirroredXInView(event.x()));
234   if (index == -1)
235     return false;
236
237   resize_details_.reset(new ColumnResizeDetails);
238   resize_details_->column_index = index;
239   resize_details_->initial_x = event.root_location().x();
240   resize_details_->initial_width = table_->visible_columns()[index].width;
241   return true;
242 }
243
244 void TableHeader::ContinueResize(const ui::LocatedEvent& event) {
245   if (!is_resizing())
246     return;
247
248   const int scale = base::i18n::IsRTL() ? -1 : 1;
249   const int delta = scale *
250       (event.root_location().x() - resize_details_->initial_x);
251   table_->SetVisibleColumnWidth(
252       resize_details_->column_index,
253       std::max(kMinColumnWidth, resize_details_->initial_width + delta));
254 }
255
256 void TableHeader::ToggleSortOrder(const ui::LocatedEvent& event) {
257   if (table_->visible_columns().empty())
258     return;
259
260   const int x = GetMirroredXInView(event.x());
261   const int index = GetClosestVisibleColumnIndex(table_, x);
262   const TableView::VisibleColumn& column(table_->visible_columns()[index]);
263   if (x >= column.x && x < column.x + column.width && event.y() >= 0 &&
264       event.y() < height())
265     table_->ToggleSortOrder(index);
266 }
267
268 int TableHeader::GetResizeColumn(int x) const {
269   const Columns& columns(table_->visible_columns());
270   if (columns.empty())
271     return -1;
272
273   const int index = GetClosestVisibleColumnIndex(table_, x);
274   DCHECK_NE(-1, index);
275   const TableView::VisibleColumn& column(table_->visible_columns()[index]);
276   if (index > 0 && x >= column.x - kResizePadding &&
277       x <= column.x + kResizePadding) {
278     return index - 1;
279   }
280   const int max_x = column.x + column.width;
281   return (x >= max_x - kResizePadding && x <= max_x + kResizePadding) ?
282       index : -1;
283 }
284
285 }  // namespace views