OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/views/touchui/touch_selection_controller_impl.h" | 5 #include "ui/views/touchui/touch_selection_controller_impl.h" |
6 | 6 |
7 #include "base/time.h" | 7 #include "base/time.h" |
| 8 #include "grit/ui_resources.h" |
8 #include "grit/ui_strings.h" | 9 #include "grit/ui_strings.h" |
| 10 #include "ui/base/resource/resource_bundle.h" |
9 #include "ui/base/ui_base_switches_util.h" | 11 #include "ui/base/ui_base_switches_util.h" |
10 #include "ui/gfx/canvas.h" | 12 #include "ui/gfx/canvas.h" |
| 13 #include "ui/gfx/image/image.h" |
11 #include "ui/gfx/path.h" | 14 #include "ui/gfx/path.h" |
12 #include "ui/gfx/rect.h" | 15 #include "ui/gfx/rect.h" |
13 #include "ui/gfx/screen.h" | 16 #include "ui/gfx/screen.h" |
14 #include "ui/gfx/size.h" | 17 #include "ui/gfx/size.h" |
15 #include "ui/views/corewm/shadow_types.h" | 18 #include "ui/views/corewm/shadow_types.h" |
16 #include "ui/views/widget/widget.h" | 19 #include "ui/views/widget/widget.h" |
17 | 20 |
18 namespace { | 21 namespace { |
19 | 22 |
20 // Constants defining the visual attributes of selection handles | 23 // Constants defining the visual attributes of selection handles |
21 const int kSelectionHandleRadius = 10; | 24 const int kSelectionHandleLineWidth = 1; |
22 const int kSelectionHandleAlpha = 0x7F; | 25 const SkColor kSelectionHandleLineColor = |
23 const SkColor kSelectionHandleColor = | 26 SkColorSetRGB(0x42, 0x81, 0xf4); |
24 SkColorSetA(SK_ColorBLACK, kSelectionHandleAlpha); | 27 |
| 28 // Padding around the selection handle defining the area that will be included |
| 29 // in the touch target to make dragging the handle easier. |
| 30 const int kSelectionHandlePadding = 10; |
25 | 31 |
26 // The minimum selection size to trigger selection controller. | 32 // The minimum selection size to trigger selection controller. |
27 const int kMinSelectionSize = 4; | 33 const int kMinSelectionSize = 4; |
28 | 34 |
29 const int kContextMenuTimoutMs = 200; | 35 const int kContextMenuTimoutMs = 200; |
30 | 36 |
31 // Convenience struct to represent a circle shape. | |
32 struct Circle { | |
33 int radius; | |
34 gfx::Point center; | |
35 SkColor color; | |
36 }; | |
37 | |
38 // Creates a widget to host SelectionHandleView. | 37 // Creates a widget to host SelectionHandleView. |
39 views::Widget* CreateTouchSelectionPopupWidget( | 38 views::Widget* CreateTouchSelectionPopupWidget( |
40 gfx::NativeView context, | 39 gfx::NativeView context, |
41 views::WidgetDelegate* widget_delegate) { | 40 views::WidgetDelegate* widget_delegate) { |
42 views::Widget* widget = new views::Widget; | 41 views::Widget* widget = new views::Widget; |
43 views::Widget::InitParams params(views::Widget::InitParams::TYPE_TOOLTIP); | 42 views::Widget::InitParams params(views::Widget::InitParams::TYPE_TOOLTIP); |
44 params.can_activate = false; | 43 params.can_activate = false; |
45 params.transparent = true; | 44 params.transparent = true; |
46 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | 45 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
47 params.context = context; | 46 params.context = context; |
48 params.delegate = widget_delegate; | 47 params.delegate = widget_delegate; |
49 widget->Init(params); | 48 widget->Init(params); |
50 #if defined(USE_AURA) | 49 #if defined(USE_AURA) |
51 SetShadowType(widget->GetNativeView(), views::corewm::SHADOW_TYPE_NONE); | 50 SetShadowType(widget->GetNativeView(), views::corewm::SHADOW_TYPE_NONE); |
52 #endif | 51 #endif |
53 return widget; | 52 return widget; |
54 } | 53 } |
55 | 54 |
56 void PaintCircle(const Circle& circle, gfx::Canvas* canvas) { | 55 gfx::Image* GetHandleImage() { |
57 SkPaint paint; | 56 static gfx::Image* handle_image = NULL; |
58 paint.setAntiAlias(true); | 57 if (!handle_image) { |
59 paint.setStyle(SkPaint::kFill_Style); | 58 handle_image = &ui::ResourceBundle::GetSharedInstance().GetImageNamed( |
60 paint.setColor(circle.color); | 59 IDR_TEXT_SELECTION_HANDLE); |
61 canvas->DrawCircle(circle.center, circle.radius, paint); | 60 } |
| 61 return handle_image; |
| 62 } |
| 63 |
| 64 gfx::Size GetHandleImageSize() { |
| 65 return GetHandleImage()->Size(); |
62 } | 66 } |
63 | 67 |
64 // The points may not match exactly, since the selection range computation may | 68 // The points may not match exactly, since the selection range computation may |
65 // introduce some floating point errors. So check for a minimum size to decide | 69 // introduce some floating point errors. So check for a minimum size to decide |
66 // whether or not there is any selection. | 70 // whether or not there is any selection. |
67 bool IsEmptySelection(const gfx::Point& p1, const gfx::Point& p2) { | 71 bool IsEmptySelection(const gfx::Point& p1, const gfx::Point& p2) { |
68 int delta_x = p2.x() - p1.x(); | 72 int delta_x = p2.x() - p1.x(); |
69 int delta_y = p2.y() - p1.y(); | 73 int delta_y = p2.y() - p1.y(); |
70 return (abs(delta_x) < kMinSelectionSize && abs(delta_y) < kMinSelectionSize); | 74 return (abs(delta_x) < kMinSelectionSize && abs(delta_y) < kMinSelectionSize); |
71 } | 75 } |
72 | 76 |
73 gfx::Rect GetHandleBoundsFromCursor(const gfx::Rect& cursor) { | |
74 return gfx::Rect(cursor.x() - kSelectionHandleRadius, cursor.y(), | |
75 2 * kSelectionHandleRadius, | |
76 2 * kSelectionHandleRadius + cursor.height()); | |
77 } | |
78 | |
79 } // namespace | 77 } // namespace |
80 | 78 |
81 namespace views { | 79 namespace views { |
82 | 80 |
83 // A View that displays the text selection handle. | 81 // A View that displays the text selection handle. |
84 class TouchSelectionControllerImpl::EditingHandleView | 82 class TouchSelectionControllerImpl::EditingHandleView |
85 : public views::WidgetDelegateView { | 83 : public views::WidgetDelegateView { |
86 public: | 84 public: |
87 explicit EditingHandleView(TouchSelectionControllerImpl* controller, | 85 explicit EditingHandleView(TouchSelectionControllerImpl* controller, |
88 gfx::NativeView context) | 86 gfx::NativeView context) |
(...skipping 11 matching lines...) Expand all Loading... |
100 } | 98 } |
101 | 99 |
102 int cursor_height() const { return cursor_height_; } | 100 int cursor_height() const { return cursor_height_; } |
103 | 101 |
104 // Overridden from views::WidgetDelegateView: | 102 // Overridden from views::WidgetDelegateView: |
105 virtual bool WidgetHasHitTestMask() const OVERRIDE { | 103 virtual bool WidgetHasHitTestMask() const OVERRIDE { |
106 return true; | 104 return true; |
107 } | 105 } |
108 | 106 |
109 virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE { | 107 virtual void GetWidgetHitTestMask(gfx::Path* mask) const OVERRIDE { |
110 mask->addCircle(SkIntToScalar(kSelectionHandleRadius), | 108 gfx::Size image_size = GetHandleImageSize(); |
111 SkIntToScalar(kSelectionHandleRadius + cursor_height_), | 109 mask->addRect(SkIntToScalar(0), SkIntToScalar(cursor_height_), |
112 SkIntToScalar(kSelectionHandleRadius)); | 110 SkIntToScalar(image_size.width()) + 2 * kSelectionHandlePadding, |
| 111 SkIntToScalar(cursor_height_ + image_size.height() + |
| 112 kSelectionHandlePadding)); |
113 } | 113 } |
114 | 114 |
115 virtual void DeleteDelegate() OVERRIDE { | 115 virtual void DeleteDelegate() OVERRIDE { |
116 // We are owned and deleted by TouchSelectionController. | 116 // We are owned and deleted by TouchSelectionController. |
117 } | 117 } |
118 | 118 |
119 // Overridden from views::View: | 119 // Overridden from views::View: |
120 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { | 120 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { |
121 Circle circle = {kSelectionHandleRadius, gfx::Point(kSelectionHandleRadius, | 121 gfx::Size image_size = GetHandleImageSize(); |
122 kSelectionHandleRadius + cursor_height_), | 122 int cursor_pos_x = image_size.width() / 2 - kSelectionHandleLineWidth + |
123 kSelectionHandleColor}; | 123 kSelectionHandlePadding; |
124 PaintCircle(circle, canvas); | 124 |
125 canvas->DrawLine(gfx::Point(kSelectionHandleRadius, 0), | 125 // Draw the cursor line. |
126 gfx::Point(kSelectionHandleRadius, cursor_height_), | 126 canvas->FillRect( |
127 kSelectionHandleColor); | 127 gfx::Rect(cursor_pos_x, 0, |
| 128 2 * kSelectionHandleLineWidth + 1, cursor_height_), |
| 129 kSelectionHandleLineColor); |
| 130 |
| 131 // Draw the handle image. |
| 132 canvas->DrawImageInt(*GetHandleImage()->ToImageSkia(), |
| 133 kSelectionHandlePadding, cursor_height_); |
128 } | 134 } |
129 | 135 |
130 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { | 136 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { |
131 event->SetHandled(); | 137 event->SetHandled(); |
132 switch (event->type()) { | 138 switch (event->type()) { |
133 case ui::ET_GESTURE_SCROLL_BEGIN: | 139 case ui::ET_GESTURE_SCROLL_BEGIN: |
134 controller_->SetDraggingHandle(this); | 140 controller_->SetDraggingHandle(this); |
135 break; | 141 break; |
136 case ui::ET_GESTURE_SCROLL_UPDATE: | 142 case ui::ET_GESTURE_SCROLL_UPDATE: |
137 controller_->SelectionHandleDragged(event->location()); | 143 controller_->SelectionHandleDragged(event->location()); |
(...skipping 11 matching lines...) Expand all Loading... |
149 if (visible != widget_->IsVisible()) { | 155 if (visible != widget_->IsVisible()) { |
150 if (visible) | 156 if (visible) |
151 widget_->Show(); | 157 widget_->Show(); |
152 else | 158 else |
153 widget_->Hide(); | 159 widget_->Hide(); |
154 } | 160 } |
155 View::SetVisible(visible); | 161 View::SetVisible(visible); |
156 } | 162 } |
157 | 163 |
158 virtual gfx::Size GetPreferredSize() OVERRIDE { | 164 virtual gfx::Size GetPreferredSize() OVERRIDE { |
159 return gfx::Size(2 * kSelectionHandleRadius, | 165 gfx::Size image_size = GetHandleImageSize(); |
160 2 * kSelectionHandleRadius + cursor_height_); | 166 return gfx::Size(image_size.width() + 2 * kSelectionHandlePadding, |
| 167 image_size.height() + cursor_height_ + kSelectionHandlePadding); |
161 } | 168 } |
162 | 169 |
163 bool IsWidgetVisible() const { | 170 bool IsWidgetVisible() const { |
164 return widget_->IsVisible(); | 171 return widget_->IsVisible(); |
165 } | 172 } |
166 | 173 |
167 void SetSelectionRectInScreen(const gfx::Rect& rect) { | 174 void SetSelectionRectInScreen(const gfx::Rect& rect) { |
| 175 gfx::Size image_size = GetHandleImageSize(); |
168 cursor_height_ = rect.height(); | 176 cursor_height_ = rect.height(); |
169 gfx::Rect widget_bounds = GetHandleBoundsFromCursor(rect); | 177 gfx::Rect widget_bounds( |
| 178 rect.x() - image_size.width() / 2 - kSelectionHandlePadding, |
| 179 rect.y(), |
| 180 image_size.width() + 2 * kSelectionHandlePadding, |
| 181 rect.height() + image_size.height() + kSelectionHandlePadding); |
170 widget_->SetBounds(widget_bounds); | 182 widget_->SetBounds(widget_bounds); |
171 } | 183 } |
172 | 184 |
173 gfx::Point GetScreenPosition() { | 185 gfx::Point GetScreenPosition() { |
174 return widget_->GetClientAreaBoundsInScreen().origin(); | 186 return widget_->GetClientAreaBoundsInScreen().origin(); |
175 } | 187 } |
176 | 188 |
177 private: | 189 private: |
178 scoped_ptr<Widget> widget_; | 190 scoped_ptr<Widget> widget_; |
179 TouchSelectionControllerImpl* controller_; | 191 TouchSelectionControllerImpl* controller_; |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
281 StartContextMenuTimer(); | 293 StartContextMenuTimer(); |
282 } | 294 } |
283 | 295 |
284 void TouchSelectionControllerImpl::SelectionHandleDragged( | 296 void TouchSelectionControllerImpl::SelectionHandleDragged( |
285 const gfx::Point& drag_pos) { | 297 const gfx::Point& drag_pos) { |
286 // We do not want to show the context menu while dragging. | 298 // We do not want to show the context menu while dragging. |
287 HideContextMenu(); | 299 HideContextMenu(); |
288 | 300 |
289 DCHECK(dragging_handle_); | 301 DCHECK(dragging_handle_); |
290 | 302 |
| 303 gfx::Size image_size = GetHandleImageSize(); |
291 gfx::Point offset_drag_pos(drag_pos.x(), | 304 gfx::Point offset_drag_pos(drag_pos.x(), |
292 drag_pos.y() - dragging_handle_->cursor_height() / 2 - | 305 drag_pos.y() - dragging_handle_->cursor_height() / 2 - |
293 2 * kSelectionHandleRadius); | 306 image_size.height() / 2); |
294 ConvertPointToClientView(dragging_handle_, &offset_drag_pos); | 307 ConvertPointToClientView(dragging_handle_, &offset_drag_pos); |
295 if (dragging_handle_ == cursor_handle_.get()) { | 308 if (dragging_handle_ == cursor_handle_.get()) { |
296 client_view_->MoveCaretTo(offset_drag_pos); | 309 client_view_->MoveCaretTo(offset_drag_pos); |
297 return; | 310 return; |
298 } | 311 } |
299 | 312 |
300 // Find the stationary selection handle. | 313 // Find the stationary selection handle. |
301 EditingHandleView* fixed_handle = selection_handle_1_.get(); | 314 EditingHandleView* fixed_handle = selection_handle_1_.get(); |
302 if (fixed_handle == dragging_handle_) | 315 if (fixed_handle == dragging_handle_) |
303 fixed_handle = selection_handle_2_.get(); | 316 fixed_handle = selection_handle_2_.get(); |
304 | 317 |
305 // Find selection end points in client_view's coordinate system. | 318 // Find selection end points in client_view's coordinate system. |
306 gfx::Point p2(kSelectionHandleRadius, fixed_handle->cursor_height() / 2); | 319 gfx::Point p2(image_size.width() / 2 + kSelectionHandlePadding, |
| 320 fixed_handle->cursor_height() / 2); |
307 ConvertPointToClientView(fixed_handle, &p2); | 321 ConvertPointToClientView(fixed_handle, &p2); |
308 | 322 |
309 // Instruct client_view to select the region between p1 and p2. The position | 323 // Instruct client_view to select the region between p1 and p2. The position |
310 // of |fixed_handle| is the start and that of |dragging_handle| is the end | 324 // of |fixed_handle| is the start and that of |dragging_handle| is the end |
311 // of selection. | 325 // of selection. |
312 client_view_->SelectRect(p2, offset_drag_pos); | 326 client_view_->SelectRect(p2, offset_drag_pos); |
313 } | 327 } |
314 | 328 |
315 void TouchSelectionControllerImpl::ConvertPointToClientView( | 329 void TouchSelectionControllerImpl::ConvertPointToClientView( |
316 EditingHandleView* source, gfx::Point* point) { | 330 EditingHandleView* source, gfx::Point* point) { |
317 View::ConvertPointToScreen(source, point); | 331 View::ConvertPointToScreen(source, point); |
318 client_view_->ConvertPointFromScreen(point); | 332 client_view_->ConvertPointFromScreen(point); |
319 } | 333 } |
320 | 334 |
321 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { | 335 bool TouchSelectionControllerImpl::IsCommandIdEnabled(int command_id) const { |
322 return client_view_->IsCommandIdEnabled(command_id); | 336 return client_view_->IsCommandIdEnabled(command_id); |
323 } | 337 } |
324 | 338 |
325 void TouchSelectionControllerImpl::ExecuteCommand(int command_id, | 339 void TouchSelectionControllerImpl::ExecuteCommand(int command_id, |
326 int event_flags) { | 340 int event_flags) { |
327 HideContextMenu(); | 341 HideContextMenu(); |
328 client_view_->ExecuteCommand(command_id, event_flags); | 342 client_view_->ExecuteCommand(command_id, event_flags); |
329 } | 343 } |
330 | 344 |
331 void TouchSelectionControllerImpl::OpenContextMenu() { | 345 void TouchSelectionControllerImpl::OpenContextMenu() { |
332 gfx::Point anchor = context_menu_->anchor_rect().origin(); | 346 gfx::Size image_size = GetHandleImageSize(); |
333 anchor.Offset(0, -kSelectionHandleRadius); | 347 gfx::Point anchor = context_menu_->anchor_rect().CenterPoint(); |
| 348 anchor.Offset(0, -image_size.height() / 2); |
334 HideContextMenu(); | 349 HideContextMenu(); |
335 client_view_->OpenContextMenu(anchor); | 350 client_view_->OpenContextMenu(anchor); |
336 } | 351 } |
337 | 352 |
338 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) { | 353 void TouchSelectionControllerImpl::OnMenuClosed(TouchEditingMenuView* menu) { |
339 if (menu == context_menu_) | 354 if (menu == context_menu_) |
340 context_menu_ = NULL; | 355 context_menu_ = NULL; |
341 } | 356 } |
342 | 357 |
343 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) { | 358 void TouchSelectionControllerImpl::OnWidgetClosing(Widget* widget) { |
344 DCHECK_EQ(client_widget_, widget); | 359 DCHECK_EQ(client_widget_, widget); |
345 client_widget_ = NULL; | 360 client_widget_ = NULL; |
346 } | 361 } |
347 | 362 |
348 void TouchSelectionControllerImpl::OnWidgetBoundsChanged( | 363 void TouchSelectionControllerImpl::OnWidgetBoundsChanged( |
349 Widget* widget, | 364 Widget* widget, |
350 const gfx::Rect& new_bounds) { | 365 const gfx::Rect& new_bounds) { |
351 DCHECK_EQ(client_widget_, widget); | 366 DCHECK_EQ(client_widget_, widget); |
352 HideContextMenu(); | 367 HideContextMenu(); |
353 SelectionChanged(); | 368 SelectionChanged(); |
354 } | 369 } |
355 | 370 |
356 void TouchSelectionControllerImpl::ContextMenuTimerFired() { | 371 void TouchSelectionControllerImpl::ContextMenuTimerFired() { |
357 // Get selection end points in client_view's space. | 372 // Get selection end points in client_view's space. |
358 gfx::Rect r1, r2; | 373 gfx::Rect r1, r2; |
359 client_view_->GetSelectionEndPoints(&r1, &r2); | 374 client_view_->GetSelectionEndPoints(&r1, &r2); |
360 | 375 |
361 gfx::Rect handle_1_bounds = GetHandleBoundsFromCursor(r1); | 376 gfx::Rect handle_1_bounds; |
362 gfx::Rect handle_2_bounds = GetHandleBoundsFromCursor(r2); | 377 gfx::Rect handle_2_bounds; |
| 378 if (cursor_handle_->IsWidgetVisible()) { |
| 379 handle_1_bounds = cursor_handle_->GetBoundsInScreen(); |
| 380 handle_2_bounds = handle_1_bounds; |
| 381 } else { |
| 382 handle_1_bounds = selection_handle_1_->GetBoundsInScreen(); |
| 383 handle_2_bounds = selection_handle_2_->GetBoundsInScreen(); |
| 384 } |
363 | 385 |
364 // if selection is completely inside the view, we display the context menu | 386 // if selection is completely inside the view, we display the context menu |
365 // in the middle of the end points on the top. Else, we show it above the | 387 // in the middle of the end points on the top. Else, we show it above the |
366 // visible handle. If no handle is visible, we do not show the menu. | 388 // visible handle. If no handle is visible, we do not show the menu. |
367 gfx::Rect menu_anchor; | 389 gfx::Rect menu_anchor; |
368 gfx::Rect client_bounds = client_view_->GetBounds(); | 390 gfx::Rect client_bounds = client_view_->GetBounds(); |
369 if (client_bounds.Contains(r1.origin()) && | 391 if (client_bounds.Contains(r1.origin()) && |
370 client_bounds.Contains(r2.origin())) { | 392 client_bounds.Contains(r2.origin())) { |
371 menu_anchor = gfx::UnionRects(handle_1_bounds, handle_2_bounds); | 393 menu_anchor = gfx::UnionRects(handle_1_bounds, handle_2_bounds); |
372 } else if (client_bounds.Contains(r1.origin())) { | 394 } else if (client_bounds.Contains(r1.origin())) { |
373 menu_anchor = handle_1_bounds; | 395 menu_anchor = handle_1_bounds; |
374 } else if (client_bounds.Contains(r2.origin())) { | 396 } else if (client_bounds.Contains(r2.origin())) { |
375 menu_anchor = handle_2_bounds; | 397 menu_anchor = handle_2_bounds; |
376 } else { | 398 } else { |
377 return; | 399 return; |
378 } | 400 } |
379 | 401 |
380 gfx::Point menu_origin = menu_anchor.origin(); | |
381 client_view_->ConvertPointToScreen(&menu_origin); | |
382 menu_anchor.set_origin(menu_origin); | |
383 | |
384 DCHECK(!context_menu_); | 402 DCHECK(!context_menu_); |
385 context_menu_ = new TouchEditingMenuView(this, menu_anchor, | 403 context_menu_ = new TouchEditingMenuView(this, menu_anchor, |
386 client_view_->GetNativeView()); | 404 client_view_->GetNativeView()); |
387 } | 405 } |
388 | 406 |
389 void TouchSelectionControllerImpl::StartContextMenuTimer() { | 407 void TouchSelectionControllerImpl::StartContextMenuTimer() { |
390 if (context_menu_timer_.IsRunning()) | 408 if (context_menu_timer_.IsRunning()) |
391 return; | 409 return; |
392 context_menu_timer_.Start( | 410 context_menu_timer_.Start( |
393 FROM_HERE, | 411 FROM_HERE, |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
438 } | 456 } |
439 | 457 |
440 ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create( | 458 ui::TouchSelectionController* ViewsTouchSelectionControllerFactory::create( |
441 ui::TouchEditable* client_view) { | 459 ui::TouchEditable* client_view) { |
442 if (switches::IsTouchEditingEnabled()) | 460 if (switches::IsTouchEditingEnabled()) |
443 return new views::TouchSelectionControllerImpl(client_view); | 461 return new views::TouchSelectionControllerImpl(client_view); |
444 return NULL; | 462 return NULL; |
445 } | 463 } |
446 | 464 |
447 } // namespace views | 465 } // namespace views |
OLD | NEW |