summaryrefslogtreecommitdiffstats
path: root/views/widget/root_view.cc
diff options
context:
space:
mode:
authorben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-08 00:34:05 +0000
committerben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-08 00:34:05 +0000
commit2362e4fe2905ab75d3230ebc3e307ae53e2b8362 (patch)
treee6d88357a2021811e0e354f618247217be8bb3da /views/widget/root_view.cc
parentdb23ac3e713dc17509b2b15d3ee634968da45715 (diff)
downloadchromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.zip
chromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.tar.gz
chromium_src-2362e4fe2905ab75d3230ebc3e307ae53e2b8362.tar.bz2
Move src/chrome/views to src/views. RS=darin http://crbug.com/11387
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15604 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'views/widget/root_view.cc')
-rw-r--r--views/widget/root_view.cc1001
1 files changed, 1001 insertions, 0 deletions
diff --git a/views/widget/root_view.cc b/views/widget/root_view.cc
new file mode 100644
index 0000000..fd9f1c0
--- /dev/null
+++ b/views/widget/root_view.cc
@@ -0,0 +1,1001 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "views/widget/root_view.h"
+
+#include <algorithm>
+
+#include "app/drag_drop_types.h"
+#include "app/gfx/chrome_canvas.h"
+#if defined(OS_WIN)
+#include "base/base_drag_source.h"
+#endif
+#include "base/logging.h"
+#include "base/message_loop.h"
+#if defined(OS_WIN)
+#include "views/focus/view_storage.h"
+#include "views/widget/root_view_drop_target.h"
+#endif
+#include "views/widget/widget.h"
+#include "views/window/window.h"
+
+namespace views {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// A Task to trigger non urgent painting.
+//
+/////////////////////////////////////////////////////////////////////////////
+class PaintTask : public Task {
+ public:
+ explicit PaintTask(RootView* target) : root_view_(target) {
+ }
+
+ ~PaintTask() {}
+
+ void Cancel() {
+ root_view_ = NULL;
+ }
+
+ void Run() {
+ if (root_view_)
+ root_view_->PaintNow();
+ }
+ private:
+ // The target root view.
+ RootView* root_view_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PaintTask);
+};
+
+const char RootView::kViewClassName[] = "views/RootView";
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RootView - constructors, destructors, initialization
+//
+/////////////////////////////////////////////////////////////////////////////
+
+RootView::RootView(Widget* widget)
+ : mouse_pressed_handler_(NULL),
+ mouse_move_handler_(NULL),
+ last_click_handler_(NULL),
+ widget_(widget),
+ invalid_rect_urgent_(false),
+ pending_paint_task_(NULL),
+ paint_task_needed_(false),
+ explicit_mouse_handler_(false),
+#if defined(OS_WIN)
+ previous_cursor_(NULL),
+#endif
+ default_keyboard_handler_(NULL),
+ focus_listener_(NULL),
+ focus_on_mouse_pressed_(false),
+ ignore_set_focus_calls_(false),
+ focus_traversable_parent_(NULL),
+ focus_traversable_parent_view_(NULL),
+ drag_view_(NULL)
+#ifndef NDEBUG
+ ,
+ is_processing_paint_(false)
+#endif
+{
+}
+
+RootView::~RootView() {
+ // If we have children remove them explicitly so to make sure a remove
+ // notification is sent for each one of them.
+ if (!child_views_.empty())
+ RemoveAllChildViews(true);
+
+ if (pending_paint_task_)
+ pending_paint_task_->Cancel(); // Ensure we're not called any more.
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RootView - layout, painting
+//
+/////////////////////////////////////////////////////////////////////////////
+
+void RootView::SchedulePaint(const gfx::Rect& r, bool urgent) {
+ // If there is an existing invalid rect, add the union of the scheduled
+ // rect with the invalid rect. This could be optimized further if
+ // necessary.
+ if (invalid_rect_.IsEmpty())
+ invalid_rect_ = r;
+ else
+ invalid_rect_ = invalid_rect_.Union(r);
+
+ if (urgent || invalid_rect_urgent_) {
+ invalid_rect_urgent_ = true;
+ } else {
+ if (!pending_paint_task_) {
+ pending_paint_task_ = new PaintTask(this);
+ MessageLoop::current()->PostTask(FROM_HERE, pending_paint_task_);
+ }
+ paint_task_needed_ = true;
+ }
+}
+
+void RootView::SchedulePaint() {
+ View::SchedulePaint();
+}
+
+void RootView::SchedulePaint(int x, int y, int w, int h) {
+ View::SchedulePaint();
+}
+
+#ifndef NDEBUG
+// Sets the value of RootView's |is_processing_paint_| member to true as long
+// as ProcessPaint is being called. Sets it to |false| when it returns.
+class ScopedProcessingPaint {
+ public:
+ explicit ScopedProcessingPaint(bool* is_processing_paint)
+ : is_processing_paint_(is_processing_paint) {
+ *is_processing_paint_ = true;
+ }
+
+ ~ScopedProcessingPaint() {
+ *is_processing_paint_ = false;
+ }
+ private:
+ bool* is_processing_paint_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ScopedProcessingPaint);
+};
+#endif
+
+void RootView::ProcessPaint(ChromeCanvas* canvas) {
+#ifndef NDEBUG
+ ScopedProcessingPaint processing_paint(&is_processing_paint_);
+#endif
+
+ // Clip the invalid rect to our bounds. If a view is in a scrollview
+ // it could be a lot larger
+ invalid_rect_ = GetScheduledPaintRectConstrainedToSize();
+
+ if (invalid_rect_.IsEmpty())
+ return;
+
+ // Clear the background.
+ canvas->drawColor(SK_ColorBLACK, SkPorterDuff::kClear_Mode);
+
+ // Save the current transforms.
+ canvas->save();
+
+ // Set the clip rect according to the invalid rect.
+ int clip_x = invalid_rect_.x() + x();
+ int clip_y = invalid_rect_.y() + y();
+ canvas->ClipRectInt(clip_x, clip_y, invalid_rect_.width(),
+ invalid_rect_.height());
+
+ // Paint the tree
+ View::ProcessPaint(canvas);
+
+ // Restore the previous transform
+ canvas->restore();
+
+ ClearPaintRect();
+}
+
+void RootView::PaintNow() {
+ if (pending_paint_task_) {
+ pending_paint_task_->Cancel();
+ pending_paint_task_ = NULL;
+ }
+ if (!paint_task_needed_)
+ return;
+ Widget* widget = GetWidget();
+ if (widget)
+ widget->PaintNow(invalid_rect_);
+}
+
+bool RootView::NeedsPainting(bool urgent) {
+ bool has_invalid_rect = !invalid_rect_.IsEmpty();
+ if (urgent) {
+ if (invalid_rect_urgent_)
+ return has_invalid_rect;
+ else
+ return false;
+ } else {
+ return has_invalid_rect;
+ }
+}
+
+const gfx::Rect& RootView::GetScheduledPaintRect() {
+ return invalid_rect_;
+}
+
+gfx::Rect RootView::GetScheduledPaintRectConstrainedToSize() {
+ if (invalid_rect_.IsEmpty())
+ return invalid_rect_;
+
+ return invalid_rect_.Intersect(GetLocalBounds(true));
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RootView - tree
+//
+/////////////////////////////////////////////////////////////////////////////
+
+Widget* RootView::GetWidget() const {
+ return widget_;
+}
+
+void RootView::ThemeChanged() {
+ View::ThemeChanged();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RootView - event dispatch and propagation
+//
+/////////////////////////////////////////////////////////////////////////////
+
+void RootView::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
+ if (!is_add) {
+ if (!explicit_mouse_handler_ && mouse_pressed_handler_ == child) {
+ mouse_pressed_handler_ = NULL;
+ }
+
+#if defined(OS_WIN)
+ if (drop_target_.get())
+ drop_target_->ResetTargetViewIfEquals(child);
+#else
+ NOTIMPLEMENTED();
+#endif
+
+ if (mouse_move_handler_ == child) {
+ mouse_move_handler_ = NULL;
+ }
+
+ if (GetFocusedView() == child) {
+ FocusView(NULL);
+ }
+
+ if (child == drag_view_)
+ drag_view_ = NULL;
+
+ if (default_keyboard_handler_ == child) {
+ default_keyboard_handler_ = NULL;
+ }
+
+ // For a given widget hierarchy, focus is tracked by a FocusManager attached
+ // to our nearest enclosing Window. <-- Important Assumption!
+ // We may not have access to our window if this function is called as a
+ // result of teardown during the deletion of the RootView and its hierarchy,
+ // so we don't bother notifying the FocusManager in that case because it
+ // will have already been destroyed (the Widget that contains us is
+ // NCDESTROY'ed which in turn destroys the focus manager _before_ the
+ // RootView is deleted.)
+#if defined(OS_WIN)
+ Window* window = GetWindow();
+ if (window) {
+ FocusManager* focus_manager =
+ FocusManager::GetFocusManager(window->GetNativeWindow());
+ focus_manager->ViewRemoved(parent, child);
+ }
+ ViewStorage::GetSharedInstance()->ViewRemoved(parent, child);
+#endif
+ }
+}
+
+void RootView::SetFocusOnMousePressed(bool f) {
+ focus_on_mouse_pressed_ = f;
+}
+
+bool RootView::OnMousePressed(const MouseEvent& e) {
+ // This function does not normally handle non-client messages except for
+ // non-client double-clicks. Actually, all double-clicks are special as the
+ // are formed from a single-click followed by a double-click event. When the
+ // double-click event lands on a different view than its single-click part,
+ // we transform it into a single-click which prevents odd things.
+ if ((e.GetFlags() & MouseEvent::EF_IS_NON_CLIENT) &&
+ !(e.GetFlags() & MouseEvent::EF_IS_DOUBLE_CLICK)) {
+ last_click_handler_ = NULL;
+ return false;
+ }
+
+ UpdateCursor(e);
+ SetMouseLocationAndFlags(e);
+
+ // If mouse_pressed_handler_ is non null, we are currently processing
+ // a pressed -> drag -> released session. In that case we send the
+ // event to mouse_pressed_handler_
+ if (mouse_pressed_handler_) {
+ MouseEvent mouse_pressed_event(e, this, mouse_pressed_handler_);
+ drag_info.Reset();
+ mouse_pressed_handler_->ProcessMousePressed(mouse_pressed_event,
+ &drag_info);
+ return true;
+ }
+ DCHECK(!explicit_mouse_handler_);
+
+ bool hit_disabled_view = false;
+ // Walk up the tree until we find a view that wants the mouse event.
+ for (mouse_pressed_handler_ = GetViewForPoint(e.location());
+ mouse_pressed_handler_ && (mouse_pressed_handler_ != this);
+ mouse_pressed_handler_ = mouse_pressed_handler_->GetParent()) {
+ if (!mouse_pressed_handler_->IsEnabled()) {
+ // Disabled views should eat events instead of propagating them upwards.
+ hit_disabled_view = true;
+ break;
+ }
+
+ // See if this view wants to handle the mouse press.
+ MouseEvent mouse_pressed_event(e, this, mouse_pressed_handler_);
+
+ // Remove the double-click flag if the handler is different than the
+ // one which got the first click part of the double-click.
+ if (mouse_pressed_handler_ != last_click_handler_)
+ mouse_pressed_event.set_flags(e.GetFlags() &
+ ~MouseEvent::EF_IS_DOUBLE_CLICK);
+
+ drag_info.Reset();
+ bool handled = mouse_pressed_handler_->ProcessMousePressed(
+ mouse_pressed_event, &drag_info);
+
+ // The view could have removed itself from the tree when handling
+ // OnMousePressed(). In this case, the removal notification will have
+ // reset mouse_pressed_handler_ to NULL out from under us. Detect this
+ // case and stop. (See comments in view.h.)
+ //
+ // NOTE: Don't return true here, because we don't want the frame to
+ // forward future events to us when there's no handler.
+ if (!mouse_pressed_handler_)
+ break;
+
+ // If the view handled the event, leave mouse_pressed_handler_ set and
+ // return true, which will cause subsequent drag/release events to get
+ // forwarded to that view.
+ if (handled) {
+ last_click_handler_ = mouse_pressed_handler_;
+ return true;
+ }
+ }
+
+ // Reset mouse_pressed_handler_ to indicate that no processing is occurring.
+ mouse_pressed_handler_ = NULL;
+
+ if (focus_on_mouse_pressed_) {
+#if defined(OS_WIN)
+ HWND hwnd = GetWidget()->GetNativeView();
+ if (::GetFocus() != hwnd) {
+ ::SetFocus(hwnd);
+ }
+#else
+ NOTIMPLEMENTED();
+#endif
+ }
+
+ // In the event that a double-click is not handled after traversing the
+ // entire hierarchy (even as a single-click when sent to a different view),
+ // it must be marked as handled to avoid anything happening from default
+ // processing if it the first click-part was handled by us.
+ if (last_click_handler_ && e.GetFlags() & MouseEvent::EF_IS_DOUBLE_CLICK)
+ hit_disabled_view = true;
+
+ last_click_handler_ = NULL;
+ return hit_disabled_view;
+}
+
+bool RootView::ConvertPointToMouseHandler(const gfx::Point& l,
+ gfx::Point* p) {
+ //
+ // If the mouse_handler was set explicitly, we need to keep
+ // sending events even if it was reparented in a different
+ // window. (a non explicit mouse handler is automatically
+ // cleared when the control is removed from the hierarchy)
+ if (explicit_mouse_handler_) {
+ if (mouse_pressed_handler_->GetWidget()) {
+ *p = l;
+ ConvertPointToScreen(this, p);
+ ConvertPointToView(NULL, mouse_pressed_handler_, p);
+ } else {
+ // If the mouse_pressed_handler_ is not connected, we send the
+ // event in screen coordinate system
+ *p = l;
+ ConvertPointToScreen(this, p);
+ return true;
+ }
+ } else {
+ *p = l;
+ ConvertPointToView(this, mouse_pressed_handler_, p);
+ }
+ return true;
+}
+
+bool RootView::OnMouseDragged(const MouseEvent& e) {
+ UpdateCursor(e);
+
+ if (mouse_pressed_handler_) {
+ SetMouseLocationAndFlags(e);
+
+ gfx::Point p;
+ ConvertPointToMouseHandler(e.location(), &p);
+ MouseEvent mouse_event(e.GetType(), p.x(), p.y(), e.GetFlags());
+ return mouse_pressed_handler_->ProcessMouseDragged(mouse_event, &drag_info);
+ }
+ return false;
+}
+
+void RootView::OnMouseReleased(const MouseEvent& e, bool canceled) {
+ UpdateCursor(e);
+
+ if (mouse_pressed_handler_) {
+ gfx::Point p;
+ ConvertPointToMouseHandler(e.location(), &p);
+ MouseEvent mouse_released(e.GetType(), p.x(), p.y(), e.GetFlags());
+ // We allow the view to delete us from ProcessMouseReleased. As such,
+ // configure state such that we're done first, then call View.
+ View* mouse_pressed_handler = mouse_pressed_handler_;
+ mouse_pressed_handler_ = NULL;
+ explicit_mouse_handler_ = false;
+ mouse_pressed_handler->ProcessMouseReleased(mouse_released, canceled);
+ // WARNING: we may have been deleted.
+ }
+}
+
+void RootView::OnMouseMoved(const MouseEvent& e) {
+ View* v = GetViewForPoint(e.location());
+ // Find the first enabled view.
+ while (v && !v->IsEnabled())
+ v = v->GetParent();
+ if (v && v != this) {
+ if (v != mouse_move_handler_) {
+ if (mouse_move_handler_ != NULL) {
+ MouseEvent exited_event(Event::ET_MOUSE_EXITED, 0, 0, 0);
+ mouse_move_handler_->OnMouseExited(exited_event);
+ }
+
+ mouse_move_handler_ = v;
+
+ MouseEvent entered_event(Event::ET_MOUSE_ENTERED,
+ this,
+ mouse_move_handler_,
+ e.location(),
+ 0);
+ mouse_move_handler_->OnMouseEntered(entered_event);
+ }
+ MouseEvent moved_event(Event::ET_MOUSE_MOVED,
+ this,
+ mouse_move_handler_,
+ e.location(),
+ 0);
+ mouse_move_handler_->OnMouseMoved(moved_event);
+
+#if defined(OS_WIN)
+ HCURSOR cursor = mouse_move_handler_->GetCursorForPoint(
+ moved_event.GetType(), moved_event.x(), moved_event.y());
+ if (cursor) {
+ previous_cursor_ = ::SetCursor(cursor);
+ } else if (previous_cursor_) {
+ ::SetCursor(previous_cursor_);
+ previous_cursor_ = NULL;
+ }
+#else
+ NOTIMPLEMENTED();
+#endif
+ } else if (mouse_move_handler_ != NULL) {
+ MouseEvent exited_event(Event::ET_MOUSE_EXITED, 0, 0, 0);
+ mouse_move_handler_->OnMouseExited(exited_event);
+#if defined(OS_WIN)
+ if (previous_cursor_) {
+ ::SetCursor(previous_cursor_);
+ previous_cursor_ = NULL;
+ }
+#else
+ NOTIMPLEMENTED();
+#endif
+ }
+}
+
+void RootView::ProcessOnMouseExited() {
+ if (mouse_move_handler_ != NULL) {
+ MouseEvent exited_event(Event::ET_MOUSE_EXITED, 0, 0, 0);
+ mouse_move_handler_->OnMouseExited(exited_event);
+ mouse_move_handler_ = NULL;
+ }
+}
+
+void RootView::SetMouseHandler(View *new_mh) {
+ // If we're clearing the mouse handler, clear explicit_mouse_handler as well.
+ explicit_mouse_handler_ = (new_mh != NULL);
+ mouse_pressed_handler_ = new_mh;
+}
+
+void RootView::OnWidgetCreated() {
+#if defined(OS_WIN)
+ DCHECK(!drop_target_.get());
+ drop_target_ = new RootViewDropTarget(this);
+#else
+ // TODO(port): Port RootViewDropTarget and this goes away.
+ NOTIMPLEMENTED();
+#endif
+}
+
+void RootView::OnWidgetDestroyed() {
+#if defined(OS_WIN)
+ if (drop_target_.get()) {
+ RevokeDragDrop(GetWidget()->GetNativeView());
+ drop_target_ = NULL;
+ }
+#else
+ // TODO(port): Port RootViewDropTarget and this goes away.
+ NOTIMPLEMENTED();
+#endif
+ widget_ = NULL;
+}
+
+void RootView::ProcessMouseDragCanceled() {
+ if (mouse_pressed_handler_) {
+ // Synthesize a release event.
+ MouseEvent release_event(Event::ET_MOUSE_RELEASED, last_mouse_event_x_,
+ last_mouse_event_y_, last_mouse_event_flags_);
+ OnMouseReleased(release_event, true);
+ }
+}
+
+void RootView::SetFocusListener(FocusListener* listener) {
+ focus_listener_ = listener;
+}
+
+void RootView::FocusView(View* view) {
+ if (view != GetFocusedView()) {
+#if defined(OS_WIN)
+ FocusManager* focus_manager = GetFocusManager();
+ DCHECK(focus_manager) << "No Focus Manager for Window " <<
+ (GetWidget() ? GetWidget()->GetNativeView() : 0);
+ if (!focus_manager)
+ return;
+
+ View* prev_focused_view = focus_manager->GetFocusedView();
+ focus_manager->SetFocusedView(view);
+
+ if (focus_listener_)
+ focus_listener_->FocusChanged(prev_focused_view, view);
+#else
+ // TODO(port): Port the focus manager and this goes away.
+ NOTIMPLEMENTED();
+#endif
+ }
+}
+
+View* RootView::GetFocusedView() {
+ FocusManager* focus_manager = GetFocusManager();
+ if (!focus_manager) {
+ // We may not have a FocusManager when the window that contains us is being
+ // deleted. Sadly we cannot wait for the window to be destroyed before we
+ // remove the FocusManager (see xp_frame.cc for more info).
+ return NULL;
+ }
+
+ // Make sure the focused view belongs to this RootView's view hierarchy.
+ View* view = focus_manager->GetFocusedView();
+ if (view && (view->GetRootView() == this))
+ return view;
+ return NULL;
+}
+
+View* RootView::FindNextFocusableView(View* starting_view,
+ bool reverse,
+ Direction direction,
+ bool dont_loop,
+ FocusTraversable** focus_traversable,
+ View** focus_traversable_view) {
+ *focus_traversable = NULL;
+ *focus_traversable_view = NULL;
+
+ if (GetChildViewCount() == 0) {
+ NOTREACHED();
+ // Nothing to focus on here.
+ return NULL;
+ }
+
+ bool skip_starting_view = true;
+ if (!starting_view) {
+ // Default to the first/last child
+ starting_view = reverse ? GetChildViewAt(GetChildViewCount() - 1) :
+ GetChildViewAt(0) ;
+ // If there was no starting view, then the one we select is a potential
+ // focus candidate.
+ skip_starting_view = false;
+ } else {
+ // The starting view should be part of this RootView.
+ DCHECK(IsParentOf(starting_view));
+ }
+
+ View* v = NULL;
+ if (!reverse) {
+ v = FindNextFocusableViewImpl(starting_view, skip_starting_view,
+ true,
+ (direction == DOWN) ? true : false,
+ starting_view->GetGroup());
+ } else {
+ // If the starting view is focusable, we don't want to go down, as we are
+ // traversing the view hierarchy tree bottom-up.
+ bool can_go_down = (direction == DOWN) && !starting_view->IsFocusable();
+ v = FindPreviousFocusableViewImpl(starting_view, true,
+ true,
+ can_go_down,
+ starting_view->GetGroup());
+ }
+ if (v) {
+ if (v->IsFocusable())
+ return v;
+ *focus_traversable = v->GetFocusTraversable();
+ DCHECK(*focus_traversable);
+ *focus_traversable_view = v;
+ return NULL;
+ }
+ // Nothing found.
+ return NULL;
+}
+
+// Strategy for finding the next focusable view:
+// - keep going down the first child, stop when you find a focusable view or
+// a focus traversable view (in that case return it) or when you reach a view
+// with no children.
+// - go to the right sibling and start the search from there (by invoking
+// FindNextFocusableViewImpl on that view).
+// - if the view has no right sibling, go up the parents until you find a parent
+// with a right sibling and start the search from there.
+View* RootView::FindNextFocusableViewImpl(View* starting_view,
+ bool skip_starting_view,
+ bool can_go_up,
+ bool can_go_down,
+ int skip_group_id) {
+ if (!skip_starting_view) {
+ if (IsViewFocusableCandidate(starting_view, skip_group_id))
+ return FindSelectedViewForGroup(starting_view);
+ if (starting_view->GetFocusTraversable())
+ return starting_view;
+ }
+
+ // First let's try the left child.
+ if (can_go_down) {
+ View* v = NULL;
+ if (starting_view->GetChildViewCount() > 0) {
+ // We are only interested in non floating-views, as attached floating
+ // views order is variable (depending on mouse moves).
+ for (int i = 0; i < starting_view->GetChildViewCount(); i++) {
+ View* child = starting_view->GetChildViewAt(i);
+ if (!child->IsFloatingView()) {
+ v = FindNextFocusableViewImpl(child, false, false, true,
+ skip_group_id);
+ break;
+ }
+ }
+ }
+ if (v == NULL) {
+ // Try the floating views.
+ int id = 0;
+ if (starting_view->EnumerateFloatingViews(View::FIRST, 0, &id)) {
+ View* child = starting_view->RetrieveFloatingViewForID(id);
+ DCHECK(child);
+ v = FindNextFocusableViewImpl(child, false, false, true, skip_group_id);
+ }
+ }
+ if (v)
+ return v;
+ }
+
+ // Then try the right sibling.
+ View* sibling = NULL;
+ if (starting_view->IsFloatingView()) {
+ int id = 0;
+ if (starting_view->GetParent()->EnumerateFloatingViews(
+ View::NEXT, starting_view->GetFloatingViewID(), &id)) {
+ sibling = starting_view->GetParent()->RetrieveFloatingViewForID(id);
+ DCHECK(sibling);
+ }
+ } else {
+ sibling = starting_view->GetNextFocusableView();
+ if (!sibling) {
+ // Let's try floating views.
+ int id = 0;
+ if (starting_view->GetParent()->EnumerateFloatingViews(View::FIRST,
+ 0, &id)) {
+ sibling = starting_view->GetParent()->RetrieveFloatingViewForID(id);
+ DCHECK(sibling);
+ }
+ }
+ }
+ if (sibling) {
+ View* v =
+ FindNextFocusableViewImpl(sibling, false, false, true, skip_group_id);
+ if (v)
+ return v;
+ }
+
+ // Then go up to the parent sibling.
+ if (can_go_up) {
+ View* parent = starting_view->GetParent();
+ while (parent) {
+ int id = 0;
+ if (parent->IsFloatingView() &&
+ parent->GetParent()->EnumerateFloatingViews(
+ View::NEXT, parent->GetFloatingViewID(), &id)) {
+ sibling = parent->GetParent()->RetrieveFloatingViewForID(id);
+ DCHECK(sibling);
+ } else {
+ sibling = parent->GetNextFocusableView();
+ }
+ if (sibling) {
+ return FindNextFocusableViewImpl(sibling,
+ false, true, true,
+ skip_group_id);
+ }
+ parent = parent->GetParent();
+ }
+ }
+
+ // We found nothing.
+ return NULL;
+}
+
+// Strategy for finding the previous focusable view:
+// - keep going down on the right until you reach a view with no children, if it
+// it is a good candidate return it.
+// - start the search on the left sibling.
+// - if there are no left sibling, start the search on the parent (without going
+// down).
+View* RootView::FindPreviousFocusableViewImpl(View* starting_view,
+ bool skip_starting_view,
+ bool can_go_up,
+ bool can_go_down,
+ int skip_group_id) {
+ // Let's go down and right as much as we can.
+ if (can_go_down) {
+ View* v = NULL;
+ if (starting_view->GetChildViewCount() -
+ starting_view->GetFloatingViewCount() > 0) {
+ View* view =
+ starting_view->GetChildViewAt(starting_view->GetChildViewCount() - 1);
+ v = FindPreviousFocusableViewImpl(view, false, false, true,
+ skip_group_id);
+ } else {
+ // Let's try floating views.
+ int id = 0;
+ if (starting_view->EnumerateFloatingViews(View::LAST, 0, &id)) {
+ View* child = starting_view->RetrieveFloatingViewForID(id);
+ DCHECK(child);
+ v = FindNextFocusableViewImpl(child, false, false, true, skip_group_id);
+ }
+ }
+ if (v)
+ return v;
+ }
+
+ if (!skip_starting_view) {
+ if (IsViewFocusableCandidate(starting_view, skip_group_id))
+ return FindSelectedViewForGroup(starting_view);
+ if (starting_view->GetFocusTraversable())
+ return starting_view;
+ }
+
+ // Then try the left sibling.
+ View* sibling = NULL;
+ if (starting_view->IsFloatingView()) {
+ int id = 0;
+ if (starting_view->GetParent()->EnumerateFloatingViews(
+ View::PREVIOUS, starting_view->GetFloatingViewID(), &id)) {
+ sibling = starting_view->GetParent()->RetrieveFloatingViewForID(id);
+ DCHECK(sibling);
+ }
+ if (!sibling) {
+ // No more floating views, try regular views, starting at the last one.
+ View* parent = starting_view->GetParent();
+ for (int i = parent->GetChildViewCount() - 1; i >= 0; i--) {
+ View* v = parent->GetChildViewAt(i);
+ if (!v->IsFloatingView()) {
+ sibling = v;
+ break;
+ }
+ }
+ }
+ } else {
+ sibling = starting_view->GetPreviousFocusableView();
+ }
+ if (sibling) {
+ return FindPreviousFocusableViewImpl(sibling,
+ false, true, true,
+ skip_group_id);
+ }
+
+ // Then go up the parent.
+ if (can_go_up) {
+ View* parent = starting_view->GetParent();
+ if (parent)
+ return FindPreviousFocusableViewImpl(parent,
+ false, true, false,
+ skip_group_id);
+ }
+
+ // We found nothing.
+ return NULL;
+}
+
+FocusTraversable* RootView::GetFocusTraversableParent() {
+ return focus_traversable_parent_;
+}
+
+void RootView::SetFocusTraversableParent(FocusTraversable* focus_traversable) {
+ DCHECK(focus_traversable != this);
+ focus_traversable_parent_ = focus_traversable;
+}
+
+View* RootView::GetFocusTraversableParentView() {
+ return focus_traversable_parent_view_;
+}
+
+void RootView::SetFocusTraversableParentView(View* view) {
+ focus_traversable_parent_view_ = view;
+}
+
+// static
+View* RootView::FindSelectedViewForGroup(View* view) {
+ if (view->IsGroupFocusTraversable() ||
+ view->GetGroup() == -1) // No group for that view.
+ return view;
+
+ View* selected_view = view->GetSelectedViewForGroup(view->GetGroup());
+ if (selected_view)
+ return selected_view;
+
+ // No view selected for that group, default to the specified view.
+ return view;
+}
+
+// static
+bool RootView::IsViewFocusableCandidate(View* v, int skip_group_id) {
+ return v->IsFocusable() &&
+ (v->IsGroupFocusTraversable() || skip_group_id == -1 ||
+ v->GetGroup() != skip_group_id);
+}
+
+bool RootView::ProcessKeyEvent(const KeyEvent& event) {
+ bool consumed = false;
+
+ View* v = GetFocusedView();
+#if defined(OS_WIN)
+ // Special case to handle right-click context menus triggered by the
+ // keyboard.
+ if (v && v->IsEnabled() && ((event.GetCharacter() == VK_APPS) ||
+ (event.GetCharacter() == VK_F10 && event.IsShiftDown()))) {
+ gfx::Point screen_loc = v->GetKeyboardContextMenuLocation();
+ v->ShowContextMenu(screen_loc.x(), screen_loc.y(), false);
+ return true;
+ }
+#else
+ // TODO(port): The above block needs the VK_* refactored out.
+ NOTIMPLEMENTED();
+#endif
+
+ for (; v && v != this && !consumed; v = v->GetParent()) {
+ consumed = (event.GetType() == Event::ET_KEY_PRESSED) ?
+ v->OnKeyPressed(event) : v->OnKeyReleased(event);
+ }
+
+ if (!consumed && default_keyboard_handler_) {
+ consumed = (event.GetType() == Event::ET_KEY_PRESSED) ?
+ default_keyboard_handler_->OnKeyPressed(event) :
+ default_keyboard_handler_->OnKeyReleased(event);
+ }
+
+ return consumed;
+}
+
+bool RootView::ProcessMouseWheelEvent(const MouseWheelEvent& e) {
+ View* v;
+ bool consumed = false;
+ if (GetFocusedView()) {
+ for (v = GetFocusedView();
+ v && v != this && !consumed; v = v->GetParent()) {
+ consumed = v->OnMouseWheel(e);
+ }
+ }
+
+ if (!consumed && default_keyboard_handler_) {
+ consumed = default_keyboard_handler_->OnMouseWheel(e);
+ }
+ return consumed;
+}
+
+void RootView::SetDefaultKeyboardHandler(View* v) {
+ default_keyboard_handler_ = v;
+}
+
+bool RootView::IsVisibleInRootView() const {
+ return IsVisible();
+}
+
+void RootView::ViewBoundsChanged(View* view, bool size_changed,
+ bool position_changed) {
+ DCHECK(view && (size_changed || position_changed));
+ if (!view->descendants_to_notify_.get())
+ return;
+
+ for (std::vector<View*>::iterator i = view->descendants_to_notify_->begin();
+ i != view->descendants_to_notify_->end(); ++i) {
+ (*i)->VisibleBoundsInRootChanged();
+ }
+}
+
+void RootView::RegisterViewForVisibleBoundsNotification(View* view) {
+ DCHECK(view);
+ if (view->registered_for_visible_bounds_notification_)
+ return;
+ view->registered_for_visible_bounds_notification_ = true;
+ View* ancestor = view->GetParent();
+ while (ancestor) {
+ ancestor->AddDescendantToNotify(view);
+ ancestor = ancestor->GetParent();
+ }
+}
+
+void RootView::UnregisterViewForVisibleBoundsNotification(View* view) {
+ DCHECK(view);
+ if (!view->registered_for_visible_bounds_notification_)
+ return;
+ view->registered_for_visible_bounds_notification_ = false;
+ View* ancestor = view->GetParent();
+ while (ancestor) {
+ ancestor->RemoveDescendantToNotify(view);
+ ancestor = ancestor->GetParent();
+ }
+}
+
+void RootView::SetMouseLocationAndFlags(const MouseEvent& e) {
+ last_mouse_event_flags_ = e.GetFlags();
+ last_mouse_event_x_ = e.x();
+ last_mouse_event_y_ = e.y();
+}
+
+std::string RootView::GetClassName() const {
+ return kViewClassName;
+}
+
+void RootView::ClearPaintRect() {
+ invalid_rect_.SetRect(0, 0, 0, 0);
+
+ // This painting has been done. Reset the urgent flag.
+ invalid_rect_urgent_ = false;
+
+ // If a pending_paint_task_ does Run(), we don't need to do anything.
+ paint_task_needed_ = false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RootView - accessibility
+//
+/////////////////////////////////////////////////////////////////////////////
+
+bool RootView::GetAccessibleRole(AccessibilityTypes::Role* role) {
+ DCHECK(role);
+
+ *role = AccessibilityTypes::ROLE_APPLICATION;
+ return true;
+}
+
+bool RootView::GetAccessibleName(std::wstring* name) {
+ if (!accessible_name_.empty()) {
+ *name = accessible_name_;
+ return true;
+ }
+ return false;
+}
+
+void RootView::SetAccessibleName(const std::wstring& name) {
+ accessible_name_.assign(name);
+}
+
+View* RootView::GetDragView() {
+ return drag_view_;
+}
+
+} // namespace views