summaryrefslogtreecommitdiffstats
path: root/chrome/views/view.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/views/view.cc')
-rw-r--r--chrome/views/view.cc1710
1 files changed, 1710 insertions, 0 deletions
diff --git a/chrome/views/view.cc b/chrome/views/view.cc
new file mode 100644
index 0000000..8df8da5
--- /dev/null
+++ b/chrome/views/view.cc
@@ -0,0 +1,1710 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/views/view.h"
+
+#include <algorithm>
+#include <sstream>
+#include <iostream>
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/os_exchange_data.h"
+#include "chrome/views/background.h"
+#include "chrome/views/border.h"
+#include "chrome/views/layout_manager.h"
+#include "chrome/views/root_view.h"
+#include "chrome/views/tooltip_manager.h"
+#include "chrome/views/view_container.h"
+#include "SkShader.h"
+
+namespace ChromeViews {
+
+// static
+char View::kViewClassName[] = "chrome/views/View";
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// A task used to automatically restore focus on the last focused floating view
+//
+////////////////////////////////////////////////////////////////////////////////
+
+class RestoreFocusTask : public Task {
+ public:
+ explicit RestoreFocusTask(View* target) : view_(target) {
+ }
+
+ ~RestoreFocusTask() {}
+
+ void Cancel() {
+ view_ = NULL;
+ }
+
+ void Run() {
+ if (view_)
+ view_->RestoreFloatingViewFocus();
+ }
+ private:
+ // The target view.
+ View* view_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(RestoreFocusTask);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// View - constructors, destructors, initialization
+//
+/////////////////////////////////////////////////////////////////////////////
+
+View::View()
+ : id_(0),
+ group_(-1),
+ bounds_(0,0,0,0),
+ parent_(NULL),
+ enabled_(true),
+ is_visible_(true),
+ focusable_(false),
+ background_(NULL),
+ accessibility_(NULL),
+ border_(NULL),
+ is_parent_owned_(true),
+ notify_when_visible_bounds_in_root_changes_(false),
+ registered_for_visible_bounds_notification_(false),
+ next_focusable_view_(NULL),
+ previous_focusable_view_(NULL),
+ should_restore_focus_(false),
+ restore_focus_view_task_(NULL),
+ context_menu_controller_(NULL),
+ drag_controller_(NULL),
+ ui_mirroring_is_enabled_for_rtl_languages_(true),
+ flip_canvas_on_paint_for_rtl_ui_(false) {
+}
+
+View::~View() {
+ if (restore_focus_view_task_)
+ restore_focus_view_task_->Cancel();
+
+ int c = static_cast<int>(child_views_.size());
+ while (--c >= 0) {
+ if (child_views_[c]->IsParentOwned())
+ delete child_views_[c];
+ else
+ child_views_[c]->SetParent(NULL);
+ }
+ if (background_)
+ delete background_;
+ if (border_)
+ delete border_;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// View - sizing
+//
+/////////////////////////////////////////////////////////////////////////////
+
+void View::GetBounds(CRect* out, PositionMirroringSettings settings) const {
+ *out = bounds_;
+
+ // If the parent uses an RTL UI layout and if we are asked to transform the
+ // bounds to their mirrored position if necessary, then we should shift the
+ // rectangle appropriately.
+ if (settings == APPLY_MIRRORING_TRANSFORMATION) {
+ out->MoveToX(MirroredX());
+ }
+}
+
+// GetY(), GetWidth() and GetHeight() are agnostic to the RTL UI layout of the
+// parent view. GetX(), on the other hand, is not.
+int View::GetX(PositionMirroringSettings settings) const {
+ if (settings == IGNORE_MIRRORING_TRANSFORMATION) {
+ return bounds_.left;
+ }
+ return MirroredX();
+}
+
+void View::SetBounds(const CRect& bounds) {
+ if (bounds.left == bounds_.left &&
+ bounds.top == bounds_.top &&
+ bounds.Width() == bounds_.Width() &&
+ bounds.Height() == bounds_.Height()) {
+ return;
+ }
+
+ CRect prev = bounds_;
+ bounds_ = bounds;
+ if (bounds_.right < bounds_.left)
+ bounds_.right = bounds_.left;
+
+ if (bounds_.bottom < bounds_.top)
+ bounds_.bottom = bounds_.top;
+
+ DidChangeBounds(prev, bounds_);
+
+ RootView* root = GetRootView();
+ if (root) {
+ bool size_changed = (prev.Width() != bounds_.Width() ||
+ prev.Height() != bounds_.Height());
+ bool position_changed = (prev.left != bounds_.left ||
+ prev.top != bounds_.top);
+ if (size_changed || position_changed)
+ root->ViewBoundsChanged(this, size_changed, position_changed);
+ }
+}
+
+void View::SetBounds(int x, int y, int width, int height) {
+ CRect tmp(x, y, x + width, y + height);
+ SetBounds(tmp);
+}
+
+void View::GetLocalBounds(CRect* out, bool include_border) const {
+ if (include_border || border_ == NULL) {
+ out->left = 0;
+ out->top = 0;
+ out->right = GetWidth();
+ out->bottom = GetHeight();
+ } else {
+ gfx::Insets insets;
+ border_->GetInsets(&insets);
+ out->left = insets.left();
+ out->top = insets.top();
+ out->right = GetWidth() - insets.left();
+ out->bottom = GetHeight() - insets.top();
+ }
+}
+
+void View::GetSize(CSize* sz) const {
+ sz->cx = GetWidth();
+ sz->cy = GetHeight();
+}
+
+void View::GetPosition(CPoint* p) const {
+ p->x = GetX(APPLY_MIRRORING_TRANSFORMATION);
+ p->y = GetY();
+}
+
+void View::GetPreferredSize(CSize* out) {
+ if (layout_manager_.get()) {
+ layout_manager_->GetPreferredSize(this, out);
+ } else {
+ out->cx = out->cy = 0;
+ }
+}
+
+void View::SizeToPreferredSize() {
+ CSize size;
+ GetPreferredSize(&size);
+ if ((size.cx != GetWidth()) || (size.cy != GetHeight()))
+ SetBounds(GetX(), GetY(), size.cx, size.cy);
+}
+
+void View::GetMinimumSize(CSize* out) {
+ GetPreferredSize(out);
+}
+
+int View::GetHeightForWidth(int w) {
+ if (layout_manager_.get())
+ return layout_manager_->GetPreferredHeightForWidth(this, w);
+
+ CSize size;
+ GetPreferredSize(&size);
+ return size.cy;
+}
+
+void View::DidChangeBounds(const CRect& previous, const CRect& current) {
+}
+
+void View::ScrollRectToVisible(int x, int y, int width, int height) {
+ View* parent = GetParent();
+
+ // We must take RTL UI mirroring into account when adjusting the position of
+ // the region.
+ if (parent)
+ parent->ScrollRectToVisible(
+ GetX(APPLY_MIRRORING_TRANSFORMATION) + x, GetY() + y, width, height);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// View - layout
+//
+/////////////////////////////////////////////////////////////////////////////
+
+void View::Layout() {
+ // Layout child Views
+ if (layout_manager_.get()) {
+ layout_manager_->Layout(this);
+ SchedulePaint();
+ }
+
+ // Lay out contents of child Views
+ int child_count = GetChildViewCount();
+ for (int i = 0; i < child_count; ++i) {
+ View* child = GetChildViewAt(i);
+ child->Layout();
+ }
+}
+
+LayoutManager* View::GetLayoutManager() const {
+ return layout_manager_.get();
+}
+
+void View::SetLayoutManager(LayoutManager* layout_manager) {
+ if (layout_manager_.get()) {
+ layout_manager_->Uninstalled(this);
+ }
+ layout_manager_.reset(layout_manager);
+ if (layout_manager_.get()) {
+ layout_manager_->Installed(this);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// View - Right-to-left UI layout
+//
+////////////////////////////////////////////////////////////////////////////////
+
+inline int View::MirroredX() const {
+ View* parent = GetParent();
+ if (parent && parent->UILayoutIsRightToLeft()) {
+ return parent->GetWidth() - bounds_.left - GetWidth();
+ }
+ return bounds_.left;
+}
+
+int View::MirroredLeftPointForRect(const gfx::Rect& bounds) const {
+ if (!UILayoutIsRightToLeft()) {
+ return bounds.x();
+ }
+ return GetWidth() - bounds.x() - bounds.width();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// View - states
+//
+////////////////////////////////////////////////////////////////////////////////
+
+bool View::IsEnabled() const {
+ return enabled_;
+}
+
+void View::SetEnabled(bool state) {
+ if (enabled_ != state) {
+ enabled_ = state;
+ SchedulePaint();
+ }
+}
+
+bool View::IsFocusable() const {
+ return focusable_ && enabled_ && is_visible_;
+}
+
+void View::SetFocusable(bool focusable) {
+ focusable_ = focusable;
+}
+
+FocusManager* View::GetFocusManager() {
+ ViewContainer* container = GetViewContainer();
+ if (!container)
+ return NULL;
+
+ HWND hwnd = container->GetHWND();
+ if (!hwnd)
+ return NULL;
+
+ return ChromeViews::FocusManager::GetFocusManager(hwnd);
+}
+
+bool View::HasFocus() {
+ RootView* root_view = GetRootView();
+ FocusManager* focus_manager = GetFocusManager();
+ if (focus_manager)
+ return focus_manager->GetFocusedView() == this;
+ return false;
+}
+
+void View::SetHotTracked(bool flag) {
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// View - painting
+//
+/////////////////////////////////////////////////////////////////////////////
+
+void View::SchedulePaint(const CRect& r, bool urgent) {
+ if (!IsVisible()) {
+ return;
+ }
+
+ if (parent_) {
+ // Translate the requested paint rect to the parent's coordinate system
+ // then pass this notification up to the parent.
+ CRect paint_rect(r);
+ CPoint p;
+ GetPosition(&p);
+ paint_rect.OffsetRect(p);
+ parent_->SchedulePaint(paint_rect, urgent);
+ }
+}
+
+void View::SchedulePaint() {
+ CRect lb;
+ GetLocalBounds(&lb, true);
+ SchedulePaint(lb, false);
+}
+
+void View::SchedulePaint(int x, int y, int w, int h) {
+ CRect r(x, y, x + w, y + h);
+ SchedulePaint(&r, false);
+}
+
+void View::Paint(ChromeCanvas* canvas) {
+ PaintBackground(canvas);
+ PaintFocusBorder(canvas);
+ PaintBorder(canvas);
+}
+
+void View::PaintBackground(ChromeCanvas* canvas) {
+ if (background_)
+ background_->Paint(canvas, this);
+}
+
+void View::PaintBorder(ChromeCanvas* canvas) {
+ if (border_)
+ border_->Paint(*this, canvas);
+}
+
+void View::PaintFocusBorder(ChromeCanvas* canvas) {
+ if (HasFocus() && IsFocusable())
+ canvas->DrawFocusRect(0, 0, GetWidth(), GetHeight());
+}
+
+void View::PaintChildren(ChromeCanvas* canvas) {
+ int i, c;
+ for (i = 0, c = GetChildViewCount(); i < c; ++i) {
+ View* child = GetChildViewAt(i);
+ if (!child) {
+ NOTREACHED() << "Should not have a NULL child View for index in bounds";
+ continue;
+ }
+ child->ProcessPaint(canvas);
+ }
+}
+
+void View::ProcessPaint(ChromeCanvas* canvas) {
+ if (!IsVisible()) {
+ return;
+ }
+
+ // We're going to modify the canvas, save it's state first.
+ canvas->save();
+
+ // Paint this View and its children, setting the clip rect to the bounds
+ // of this View and translating the origin to the local bounds' top left
+ // point.
+ //
+ // Note that the X (or left) position we pass to ClipRectInt takes into
+ // consideration whether or not the view uses a right-to-left layout so that
+ // we paint our view in its mirrored position if need be.
+ if (canvas->ClipRectInt(MirroredX(), bounds_.top, bounds_.Width(),
+ bounds_.Height())) {
+ // Non-empty clip, translate the graphics such that 0,0 corresponds to
+ // where this view is located (related to its parent).
+ canvas->TranslateInt(MirroredX(), bounds_.top);
+
+ // Save the state again, so that any changes don't effect PaintChildren.
+ canvas->save();
+
+ // If the View we are about to paint requested the canvas to be flipped, we
+ // should change the transform appropriately.
+ bool flip_canvas = FlipCanvasOnPaintForRTLUI();
+ if (flip_canvas) {
+ canvas->TranslateInt(GetWidth(), 0);
+ canvas->ScaleInt(-1, 1);
+ canvas->save();
+ }
+
+ Paint(canvas);
+
+ // We must undo the canvas mirroring once the View is done painting so that
+ // we don't pass the canvas with the mirrored transform to Views that
+ // didn't request the canvas to be flipped.
+ if (flip_canvas) {
+ canvas->restore();
+ }
+ canvas->restore();
+ PaintChildren(canvas);
+ }
+
+ // Restore the canvas's original transform.
+ canvas->restore();
+}
+
+void View::PaintNow() {
+ if (!IsVisible()) {
+ return;
+ }
+
+ View* view = GetParent();
+ if (view)
+ view->PaintNow();
+}
+
+void View::PaintFloatingView(ChromeCanvas* canvas, View* view,
+ int x, int y, int w, int h) {
+ if (should_restore_focus_ && ShouldRestoreFloatingViewFocus()) {
+ // We are painting again a floating view, this is a good time to restore the
+ // focus to the last focused floating view if any.
+ should_restore_focus_ = false;
+ restore_focus_view_task_ = new RestoreFocusTask(this);
+ MessageLoop::current()->PostTask(FROM_HERE, restore_focus_view_task_);
+ }
+ View* saved_parent = view->GetParent();
+ view->SetParent(this);
+ view->SetBounds(x, y, w, h);
+ view->Layout();
+ view->ProcessPaint(canvas);
+ view->SetParent(saved_parent);
+}
+
+void View::SetBackground(Background* b) {
+ if (background_ != b)
+ delete background_;
+ background_ = b;
+}
+
+const Background* View::GetBackground() const {
+ return background_;
+}
+
+void View::SetBorder(Border* b) {
+ if (border_ != b)
+ delete border_;
+ border_ = b;
+}
+
+const Border* View::GetBorder() const {
+ return border_;
+}
+
+gfx::Insets View::GetInsets() const {
+ const Border* border = GetBorder();
+ gfx::Insets insets;
+ if (border)
+ border->GetInsets(&insets);
+ return insets;
+}
+
+void View::SetContextMenuController(ContextMenuController* menu_controller) {
+ context_menu_controller_ = menu_controller;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// View - tree
+//
+/////////////////////////////////////////////////////////////////////////////
+
+bool View::ProcessMousePressed(const MouseEvent& e, DragInfo* drag_info) {
+ const bool enabled = enabled_;
+ int drag_operations;
+ if (enabled && e.IsOnlyLeftMouseButton() && HitTest(e.GetLocation()))
+ drag_operations = GetDragOperations(e.GetX(), e.GetY());
+ else
+ drag_operations = 0;
+ ContextMenuController* context_menu_controller = context_menu_controller_;
+
+ const bool result = OnMousePressed(e);
+ // WARNING: we may have been deleted, don't use any View variables;
+
+ if (!enabled)
+ return result;
+
+ if (drag_operations != DragDropTypes::DRAG_NONE) {
+ drag_info->PossibleDrag(e.GetX(), e.GetY());
+ return true;
+ }
+ return !!context_menu_controller || result;
+}
+
+bool View::ProcessMouseDragged(const MouseEvent& e, DragInfo* drag_info) {
+ // Copy the field, that way if we're deleted after drag and drop no harm is
+ // done.
+ ContextMenuController* context_menu_controller = context_menu_controller_;
+ const bool possible_drag = drag_info->possible_drag;
+ if (possible_drag && ExceededDragThreshold(drag_info->start_x - e.GetX(),
+ drag_info->start_y - e.GetY())) {
+ DoDrag(e, drag_info->start_x, drag_info->start_y);
+ } else {
+ if (OnMouseDragged(e))
+ return true;
+ // Fall through to return value based on context menu controller.
+ }
+ // WARNING: we may have been deleted.
+ return (context_menu_controller != NULL) || possible_drag;
+}
+
+void View::ProcessMouseReleased(const MouseEvent& e, bool canceled) {
+ if (!canceled && context_menu_controller_ && e.IsOnlyRightMouseButton()) {
+ // Assume that if there is a context menu controller we won't be deleted
+ // from mouse released.
+ CPoint location(e.GetX(), e.GetY());
+ ConvertPointToScreen(this, &location);
+ ContextMenuController* context_menu_controller = context_menu_controller_;
+ OnMouseReleased(e, canceled);
+ context_menu_controller_->ShowContextMenu(this, location.x, location.y,
+ true);
+ } else {
+ OnMouseReleased(e, canceled);
+ }
+ // WARNING: we may have been deleted.
+}
+
+void View::DoDrag(const MouseEvent& e, int press_x, int press_y) {
+ scoped_refptr<OSExchangeData> data = new OSExchangeData;
+ WriteDragData(press_x, press_y, data.get());
+
+ // Message the RootView to do the drag and drop. That way if we're removed
+ // the RootView can detect it and avoid callins us back.
+ RootView* root_view = GetRootView();
+ root_view->StartDragForViewFromMouseEvent(
+ this, data, GetDragOperations(press_x, press_y));
+}
+
+void View::AddChildView(View* v) {
+ AddChildView(static_cast<int>(child_views_.size()), v, false);
+}
+
+void View::AddChildView(int index, View* v) {
+ AddChildView(index, v, false);
+}
+
+void View::AddChildView(int index, View* v, bool floating_view) {
+ // Remove the view from its current parent if any.
+ if (v->GetParent())
+ v->GetParent()->RemoveChildView(v);
+
+ if (!floating_view) {
+ // Sets the prev/next focus views.
+ InitFocusSiblings(v, index);
+ }
+
+ // Let's insert the view.
+ child_views_.insert(child_views_.begin() + index, v);
+ v->SetParent(this);
+
+ for (View* p = this; p; p = p->GetParent()) {
+ p->ViewHierarchyChangedImpl(false, true, this, v);
+ }
+ v->PropagateAddNotifications(this, v);
+ UpdateTooltip();
+ RootView* root = GetRootView();
+ if (root)
+ RegisterChildrenForVisibleBoundsNotification(root, v);
+
+ if (layout_manager_.get())
+ layout_manager_->ViewAdded(this, v);
+}
+
+View* View::GetChildViewAt(int index) const {
+ return index < GetChildViewCount() ? child_views_[index] : NULL;
+}
+
+int View::GetChildViewCount() const {
+ return static_cast<int>(child_views_.size());
+}
+
+void View::RemoveChildView(View* a_view) {
+ DoRemoveChildView(a_view, true, true, false);
+}
+
+void View::RemoveAllChildViews(bool delete_views) {
+ ViewList::iterator iter;
+ while ((iter = child_views_.begin()) != child_views_.end()) {
+ DoRemoveChildView(*iter, false, false, delete_views);
+ }
+ UpdateTooltip();
+}
+
+void View::DoRemoveChildView(View* a_view,
+ bool update_focus_cycle,
+ bool update_tool_tip,
+ bool delete_removed_view) {
+#ifndef NDEBUG
+ DCHECK(!IsProcessingPaint()) << "Should not be removing a child view " <<
+ "during a paint, this will seriously " <<
+ "mess things up!";
+#endif
+ DCHECK(a_view);
+ const ViewList::iterator i = find(child_views_.begin(),
+ child_views_.end(),
+ a_view);
+ if (i != child_views_.end()) {
+ if (update_focus_cycle && !a_view->IsFloatingView()) {
+ // Let's remove the view from the focus traversal.
+ View* next_focusable = a_view->next_focusable_view_;
+ View* prev_focusable = a_view->previous_focusable_view_;
+ if (prev_focusable)
+ prev_focusable->next_focusable_view_ = next_focusable;
+ if (next_focusable)
+ next_focusable->previous_focusable_view_ = prev_focusable;
+ }
+
+ RootView* root = GetRootView();
+ if (root)
+ UnregisterChildrenForVisibleBoundsNotification(root, a_view);
+ a_view->PropagateRemoveNotifications(this);
+ a_view->SetParent(NULL);
+
+ if (delete_removed_view && a_view->IsParentOwned())
+ delete a_view;
+
+ child_views_.erase(i);
+ }
+
+ if (update_tool_tip)
+ UpdateTooltip();
+
+ if (layout_manager_.get())
+ layout_manager_->ViewRemoved(this, a_view);
+}
+
+void View::PropagateRemoveNotifications(View* parent) {
+ int i, c;
+ for (i = 0, c = GetChildViewCount(); i < c; ++i) {
+ GetChildViewAt(i)->PropagateRemoveNotifications(parent);
+ }
+
+ View *t;
+ for (t = this; t; t = t->GetParent()) {
+ t->ViewHierarchyChangedImpl(true, false, parent, this);
+ }
+}
+
+void View::PropagateAddNotifications(View* parent, View* child) {
+ int i, c;
+ for (i = 0, c = GetChildViewCount(); i < c; ++i) {
+ GetChildViewAt(i)->PropagateAddNotifications(parent, child);
+ }
+ ViewHierarchyChangedImpl(true, true, parent, child);
+}
+
+#ifndef NDEBUG
+bool View::IsProcessingPaint() const {
+ return GetParent() && GetParent()->IsProcessingPaint();
+}
+#endif
+
+void View::ViewHierarchyChanged(bool is_add, View *parent, View *child) {
+}
+
+void View::ViewHierarchyChangedImpl(bool register_accelerators,
+ bool is_add, View *parent, View *child) {
+ if (register_accelerators) {
+ if (is_add) {
+ // If you get this registration, you are part of a subtree that has been
+ // added to the view hierarchy.
+ RegisterAccelerators();
+ } else {
+ if (child == this)
+ UnregisterAccelerators();
+ }
+ }
+
+ ViewHierarchyChanged(is_add, parent, child);
+}
+
+void View::PropagateVisibilityNotifications(View* start, bool is_visible) {
+ int i, c;
+ for (i = 0, c = GetChildViewCount(); i < c; ++i) {
+ GetChildViewAt(i)->PropagateVisibilityNotifications(start, is_visible);
+ }
+ VisibilityChanged(start, is_visible);
+}
+
+void View::VisibilityChanged(View* starting_from, bool is_visible) {
+}
+
+View* View::GetViewForPoint(const CPoint& point) {
+ return GetViewForPoint(point, true);
+}
+
+void View::SetNotifyWhenVisibleBoundsInRootChanges(bool value) {
+ if (notify_when_visible_bounds_in_root_changes_ == value)
+ return;
+ notify_when_visible_bounds_in_root_changes_ = value;
+ RootView* root = GetRootView();
+ if (root) {
+ if (value)
+ root->RegisterViewForVisibleBoundsNotification(this);
+ else
+ root->UnregisterViewForVisibleBoundsNotification(this);
+ }
+}
+
+bool View::GetNotifyWhenVisibleBoundsInRootChanges() {
+ return notify_when_visible_bounds_in_root_changes_;
+}
+
+View* View::GetViewForPoint(const CPoint& point, bool can_create_floating) {
+ // Walk the child Views recursively looking for the View that most
+ // tightly encloses the specified point.
+ for (int i = GetChildViewCount() - 1 ; i >= 0 ; --i) {
+ View* child = GetChildViewAt(i);
+ if (!child->IsVisible()) {
+ continue;
+ }
+ CRect bounds;
+ child->GetBounds(&bounds, APPLY_MIRRORING_TRANSFORMATION);
+ if (bounds.PtInRect(point)) {
+ CPoint cl(point);
+ cl.Offset(-bounds.left, -bounds.top);
+ return child->GetViewForPoint(cl, true);
+ }
+ }
+
+ // We haven't found a view for the point. Try to create floating views
+ // and try again if one was created.
+ // can_create_floating makes sure we don't try forever even if
+ // GetFloatingViewIDForPoint lies or if RetrieveFloatingViewForID creates a
+ // view which doesn't contain the provided point
+ int id;
+ if (can_create_floating && GetFloatingViewIDForPoint(point.x, point.y, &id)) {
+ RetrieveFloatingViewForID(id); // This creates the floating view.
+ return GetViewForPoint(point, false);
+ }
+ return this;
+}
+
+ViewContainer* View::GetViewContainer() const {
+ // The root view holds a reference to this view hierarchy's container.
+ return parent_ ? parent_->GetViewContainer() : NULL;
+}
+
+// Get the containing RootView
+RootView* View::GetRootView() {
+ ViewContainer* vc = GetViewContainer();
+ if (vc) {
+ return vc->GetRootView();
+ } else {
+ return NULL;
+ }
+}
+
+View* View::GetViewByID(int id) const {
+ if (id == id_)
+ return const_cast<View*>(this);
+
+ int view_count = GetChildViewCount();
+ for (int i = 0; i < view_count; ++i) {
+ View* child = GetChildViewAt(i);
+ View* view = child->GetViewByID(id);
+ if (view)
+ return view;
+ }
+ return NULL;
+}
+
+void View::GetViewsWithGroup(int group_id, std::vector<View*>* out) {
+ if (group_ == group_id)
+ out->push_back(this);
+
+ int view_count = GetChildViewCount();
+ for (int i = 0; i < view_count; ++i)
+ GetChildViewAt(i)->GetViewsWithGroup(group_id, out);
+}
+
+View* View::GetSelectedViewForGroup(int group_id) {
+ std::vector<View*> views;
+ GetRootView()->GetViewsWithGroup(group_id, &views);
+ if (views.size() > 0)
+ return views[0];
+ else
+ return NULL;
+}
+
+void View::SetID(int id) {
+ id_ = id;
+}
+
+int View::GetID() const {
+ return id_;
+}
+
+void View::SetGroup(int gid) {
+ group_ = gid;
+}
+
+int View::GetGroup() const {
+ return group_;
+}
+
+void View::SetParent(View* parent) {
+ if (parent != parent_) {
+ parent_ = parent;
+ }
+}
+
+bool View::IsParentOf(View* v) const {
+ DCHECK(v);
+ View* parent = v->GetParent();
+ while (parent) {
+ if (this == parent)
+ return true;
+ parent = parent->GetParent();
+ }
+ return false;
+}
+
+int View::GetChildIndex(View* v) const {
+ for (int i = 0; i < GetChildViewCount(); i++) {
+ if (v == GetChildViewAt(i))
+ return i;
+ }
+ return -1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// View - focus
+//
+///////////////////////////////////////////////////////////////////////////////
+
+View* View::GetNextFocusableView() {
+ return next_focusable_view_;
+}
+
+View* View::GetPreviousFocusableView() {
+ return previous_focusable_view_;
+}
+
+void View::SetNextFocusableView(View* view) {
+ view->previous_focusable_view_ = this;
+ next_focusable_view_ = view;
+}
+
+void View::InitFocusSiblings(View* v, int index) {
+ int child_count = static_cast<int>(child_views_.size());
+
+ if (child_count == 0) {
+ v->next_focusable_view_ = NULL;
+ v->previous_focusable_view_ = NULL;
+ } else {
+ if (index == child_count) {
+ // We are inserting at the end, but the end of the child list may not be
+ // the last focusable element. Let's try to find an element with no next
+ // focusable element to link to.
+ View* last_focusable_view = NULL;
+ for (std::vector<View*>::iterator iter = child_views_.begin();
+ iter != child_views_.end(); ++iter) {
+ if (!(*iter)->next_focusable_view_) {
+ last_focusable_view = *iter;
+ break;
+ }
+ }
+ if (last_focusable_view == NULL) {
+ // Hum... there is a cycle in the focus list. Let's just insert ourself
+ // after the last child.
+ View* prev = child_views_[index - 1];
+ v->previous_focusable_view_ = prev;
+ v->next_focusable_view_ = prev->next_focusable_view_;
+ prev->next_focusable_view_->previous_focusable_view_ = v;
+ prev->next_focusable_view_ = v;
+ } else {
+ last_focusable_view->next_focusable_view_ = v;
+ v->next_focusable_view_ = NULL;
+ v->previous_focusable_view_ = last_focusable_view;
+ }
+ } else {
+ View* prev = child_views_[index]->GetPreviousFocusableView();
+ v->previous_focusable_view_ = prev;
+ v->next_focusable_view_ = child_views_[index];
+ if (prev)
+ prev->next_focusable_view_ = v;
+ child_views_[index]->previous_focusable_view_ = v;
+ }
+ }
+}
+
+#ifndef NDEBUG
+void View::PrintViewHierarchy() {
+ PrintViewHierarchyImp(0);
+}
+
+void View::PrintViewHierarchyImp(int indent) {
+ std::wostringstream buf;
+ int ind = indent;
+ while (ind-- > 0)
+ buf << L' ';
+ buf << UTF8ToWide(GetClassName());
+ buf << L' ';
+ buf << GetID();
+ buf << L' ';
+ buf << bounds_.left << L"," << bounds_.top << L",";
+ buf << bounds_.right << L"," << bounds_.bottom;
+ buf << L' ';
+ buf << this;
+
+ LOG(INFO) << buf.str();
+ std::cout << buf.str() << std::endl;
+
+ for (int i = 0; i < GetChildViewCount(); ++i) {
+ GetChildViewAt(i)->PrintViewHierarchyImp(indent + 2);
+ }
+}
+
+
+void View::PrintFocusHierarchy() {
+ PrintFocusHierarchyImp(0);
+}
+
+void View::PrintFocusHierarchyImp(int indent) {
+ std::wostringstream buf;
+ int ind = indent;
+ while (ind-- > 0)
+ buf << L' ';
+ buf << UTF8ToWide(GetClassName());
+ buf << L' ';
+ buf << GetID();
+ buf << L' ';
+ buf << GetClassName().c_str();
+ buf << L' ';
+ buf << this;
+
+ LOG(INFO) << buf.str();
+ std::cout << buf.str() << std::endl;
+
+ if (GetChildViewCount() > 0)
+ GetChildViewAt(0)->PrintFocusHierarchyImp(indent + 2);
+
+ View* v = GetNextFocusableView();
+ if (v)
+ v->PrintFocusHierarchyImp(indent);
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// View - accelerators
+//
+////////////////////////////////////////////////////////////////////////////////
+
+void View::AddAccelerator(const Accelerator& accelerator) {
+ if (!accelerators_.get())
+ accelerators_.reset(new std::vector<Accelerator>());
+ accelerators_->push_back(accelerator);
+ RegisterAccelerators();
+}
+
+void View::ResetAccelerators() {
+ if (accelerators_.get()) {
+ UnregisterAccelerators();
+ accelerators_->clear();
+ accelerators_.reset();
+ }
+}
+
+void View::RegisterAccelerators() {
+ if (!accelerators_.get())
+ return;
+
+ RootView* root_view = GetRootView();
+ if (!root_view) {
+ // We are not yet part of a view hierarchy, we'll register ourselves once
+ // added to one.
+ return;
+ }
+ FocusManager* focus_manager = GetFocusManager();
+ if (!focus_manager) {
+ // Some crash reports seem to show that we may get cases where we have no
+ // focus manager (see bug #1291225). This should never be the case, just
+ // making sure we don't crash.
+ NOTREACHED();
+ return;
+ }
+ for (std::vector<Accelerator>::const_iterator iter = accelerators_->begin();
+ iter != accelerators_->end(); ++iter) {
+ focus_manager->RegisterAccelerator(*iter, this);
+ }
+}
+
+void View::UnregisterAccelerators() {
+ if (!accelerators_.get())
+ return;
+
+ RootView* root_view = GetRootView();
+ if (root_view) {
+ FocusManager* focus_manager = GetFocusManager();
+ if (focus_manager) {
+ // We may not have a FocusManager if the window containing us is being
+ // closed, in which case the FocusManager is being deleted so there is
+ // nothing to unregister.
+ focus_manager->UnregisterAccelerators(this);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// View - accessibility
+//
+/////////////////////////////////////////////////////////////////////////////
+
+AccessibleWrapper* View::GetAccessibleWrapper() {
+ if (accessibility_.get() == NULL) {
+ accessibility_.reset(new AccessibleWrapper(this));
+ }
+ return accessibility_.get();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// View - floating views
+//
+/////////////////////////////////////////////////////////////////////////////
+
+bool View::IsFloatingView() {
+ if (!parent_)
+ return false;
+
+ return parent_->floating_views_ids_.find(this) !=
+ parent_->floating_views_ids_.end();
+}
+
+// default implementation does nothing
+bool View::GetFloatingViewIDForPoint(int x, int y, int* id) {
+ return false;
+}
+
+int View::GetFloatingViewCount() const {
+ return static_cast<int>(floating_views_.size());
+}
+
+View* View::RetrieveFloatingViewParent() {
+ View* v = this;
+ while (v) {
+ if (v->IsFloatingView())
+ return v;
+ v = v->GetParent();
+ }
+ return NULL;
+}
+
+bool View::EnumerateFloatingViews(FloatingViewPosition position,
+ int starting_id, int* id) {
+ return false;
+}
+
+int View::GetDragOperations(int press_x, int press_y) {
+ if (!drag_controller_)
+ return DragDropTypes::DRAG_NONE;
+ return drag_controller_->GetDragOperations(this, press_x, press_y);
+}
+
+void View::WriteDragData(int press_x, int press_y, OSExchangeData* data) {
+ DCHECK(drag_controller_);
+ drag_controller_->WriteDragData(this, press_x, press_y, data);
+}
+
+void View::OnDragDone() {
+}
+
+bool View::InDrag() {
+ RootView* root_view = GetRootView();
+ return root_view ? (root_view->GetDragView() == this) : false;
+}
+
+View* View::ValidateFloatingViewForID(int id) {
+ return NULL;
+}
+
+bool View::ShouldRestoreFloatingViewFocus() {
+ return true;
+}
+
+void View::AttachFloatingView(View* v, int id) {
+ floating_views_.push_back(v);
+ floating_views_ids_[v] = id;
+ AddChildView(static_cast<int>(child_views_.size()), v, true);
+}
+
+bool View::HasFloatingViewForPoint(int x, int y) {
+ int i, c;
+ View* v;
+ gfx::Rect r;
+
+ for (i = 0, c = static_cast<int>(floating_views_.size()); i < c; ++i) {
+ v = floating_views_[i];
+ r.SetRect(v->GetX(APPLY_MIRRORING_TRANSFORMATION), v->GetY(),
+ v->GetWidth(), v->GetHeight());
+ if (r.Contains(x, y))
+ return true;
+ }
+ return false;
+}
+
+void View::DetachAllFloatingViews() {
+ RootView* root_view = GetRootView();
+ View* focused_view = NULL;
+ FocusManager* focus_manager = NULL;
+ if (root_view) {
+ // We may be called when we are not attached to a root view in which case
+ // there is nothing to do for focus.
+ focus_manager = GetFocusManager();
+ if (focus_manager) {
+ // We may not have a focus manager (if we are detached from a top window).
+ focused_view = focus_manager->GetFocusedView();
+ }
+ }
+
+ int c = static_cast<int>(floating_views_.size());
+ while (--c >= 0) {
+ // If the focused view is a floating view or a floating view's children,
+ // use the focus manager to store it.
+ int tmp_id;
+ if (focused_view &&
+ ((focused_view == floating_views_[c]) ||
+ floating_views_[c]->IsParentOf(focused_view))) {
+ // We call EnumerateFloatingView to make sure the floating view is still
+ // valid: the model may have changed and could not know anything about
+ // that floating view anymore.
+ if (EnumerateFloatingViews(CURRENT,
+ floating_views_[c]->GetFloatingViewID(),
+ &tmp_id)) {
+ focus_manager->StoreFocusedView();
+ should_restore_focus_ = true;
+ }
+ focused_view = NULL;
+ }
+
+ RemoveChildView(floating_views_[c]);
+ delete floating_views_[c];
+ }
+ floating_views_.clear();
+ floating_views_ids_.clear();
+}
+
+int View::GetFloatingViewID() {
+ DCHECK(IsFloatingView());
+ std::map<View*, int>::iterator iter = parent_->floating_views_ids_.find(this);
+ DCHECK(iter != parent_->floating_views_ids_.end());
+ return iter->second;
+}
+
+View* View::RetrieveFloatingViewForID(int id) {
+ for (ViewList::const_iterator iter = floating_views_.begin();
+ iter != floating_views_.end(); ++iter) {
+ if ((*iter)->GetFloatingViewID() == id)
+ return *iter;
+ }
+ return ValidateFloatingViewForID(id);
+}
+
+void View::RestoreFloatingViewFocus() {
+ // Clear the reference to the task as if we have been triggered by it, it will
+ // soon be invalid.
+ restore_focus_view_task_ = NULL;
+ should_restore_focus_ = false;
+
+ GetFocusManager()->RestoreFocusedView();
+}
+
+// static
+bool View::EnumerateFloatingViewsForInterval(int low_bound, int high_bound,
+ bool ascending_order,
+ FloatingViewPosition position,
+ int starting_id,
+ int* id) {
+ DCHECK(low_bound <= high_bound);
+ if (low_bound >= high_bound)
+ return false;
+
+ switch (position) {
+ case CURRENT:
+ if ((starting_id >= low_bound) && (starting_id < high_bound)) {
+ *id = starting_id;
+ return true;
+ }
+ return false;
+ case FIRST:
+ *id = ascending_order ? low_bound : high_bound - 1;
+ return true;
+ case LAST:
+ *id = ascending_order ? high_bound - 1 : low_bound;
+ return true;
+ case NEXT:
+ case PREVIOUS:
+ if (((position == NEXT) && ascending_order) ||
+ ((position == PREVIOUS) && !ascending_order)) {
+ starting_id++;
+ if (starting_id < high_bound) {
+ *id = starting_id;
+ return true;
+ }
+ return false;
+ }
+ DCHECK(((position == NEXT) && !ascending_order) ||
+ ((position == PREVIOUS) && ascending_order));
+ starting_id--;
+ if (starting_id >= low_bound) {
+ *id = starting_id;
+ return true;
+ }
+ return false;
+ default:
+ NOTREACHED();
+ }
+ return false;
+}
+
+// static
+void View::ConvertPointToView(View* src,
+ View* dst,
+ gfx::Point* point) {
+ ConvertPointToView(src, dst, point, true);
+}
+
+// static
+void View::ConvertPointToView(View* src,
+ View* dst,
+ CPoint* point) {
+ gfx::Point tmp_point(point->x, point->y);
+ ConvertPointToView(src, dst, &tmp_point, true);
+ point->x = tmp_point.x();
+ point->y = tmp_point.y();
+}
+
+// static
+void View::ConvertPointToView(View* src,
+ View* dst,
+ gfx::Point* point,
+ bool try_other_direction) {
+ // src can be NULL
+ DCHECK(dst);
+ DCHECK(point);
+
+ View* v;
+ gfx::Point offset;
+
+ for (v = dst; v && v != src; v = v->GetParent()) {
+ offset.SetPoint(offset.x() + v->GetX(APPLY_MIRRORING_TRANSFORMATION),
+ offset.y() + v->GetY());
+ }
+
+ // The source was not found. The caller wants a conversion
+ // from a view to a transitive parent.
+ if (src && v == NULL && try_other_direction) {
+ gfx::Point p;
+ // note: try_other_direction is force to FALSE so we don't
+ // end up in an infinite recursion should both src and dst
+ // are not parented.
+ ConvertPointToView(dst, src, &p, false);
+ // since the src and dst are inverted, p should also be negated
+ point->SetPoint(point->x() - p.x(), point->y() - p.y());
+ } else {
+ point->SetPoint(point->x() - offset.x(), point->y() - offset.y());
+
+ // If src is NULL, sp is in the screen coordinate system
+ if (src == NULL) {
+ ViewContainer* vc = dst->GetViewContainer();
+ if (vc) {
+ CRect b;
+ vc->GetBounds(&b, false);
+ point->SetPoint(point->x() - b.left, point->y() - b.top);
+ }
+ }
+ }
+}
+
+// static
+void View::ConvertPointToViewContainer(View* src, CPoint* p) {
+ DCHECK(src);
+ DCHECK(p);
+ View *v;
+ CPoint offset(0, 0);
+
+ for (v = src; v; v = v->GetParent()) {
+ offset.x += v->GetX(APPLY_MIRRORING_TRANSFORMATION);
+ offset.y += v->GetY();
+ }
+ p->x += offset.x;
+ p->y += offset.y;
+}
+
+// static
+void View::ConvertPointFromViewContainer(View *source, CPoint *p) {
+ CPoint t(0, 0);
+ ConvertPointToViewContainer(source, &t);
+ p->x -= t.x;
+ p->y -= t.y;
+}
+
+// static
+void View::ConvertPointToScreen(View* src, CPoint* p) {
+ DCHECK(src);
+ DCHECK(p);
+
+ // If the view is not connected to a tree, do nothing
+ if (src->GetViewContainer() == NULL) {
+ return;
+ }
+
+ ConvertPointToViewContainer(src, p);
+ ViewContainer* vc = src->GetViewContainer();
+ if (vc) {
+ CRect r;
+ vc->GetBounds(&r, false);
+ p->x += r.left;
+ p->y += r.top;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// View - event handlers
+//
+/////////////////////////////////////////////////////////////////////////////
+
+bool View::OnMousePressed(const MouseEvent& e) {
+ return false;
+}
+
+bool View::OnMouseDragged(const MouseEvent& e) {
+ return false;
+}
+
+void View::OnMouseReleased(const MouseEvent& e, bool canceled) {
+}
+
+void View::OnMouseMoved(const MouseEvent& e) {
+}
+
+void View::OnMouseEntered(const MouseEvent& e) {
+}
+
+void View::OnMouseExited(const MouseEvent& e) {
+}
+
+void View::SetMouseHandler(View *new_mouse_handler) {
+ // It is valid for new_mouse_handler to be NULL
+ if (parent_) {
+ parent_->SetMouseHandler(new_mouse_handler);
+ }
+}
+
+void View::SetVisible(bool flag) {
+ if (flag != is_visible_) {
+ // If the tab is currently visible, schedule paint to
+ // refresh parent
+ if (IsVisible()) {
+ SchedulePaint();
+ }
+
+ is_visible_ = flag;
+
+ // This notifies all subviews recursively.
+ PropagateVisibilityNotifications(this, flag);
+
+ // If we are newly visible, schedule paint.
+ if (IsVisible()) {
+ SchedulePaint();
+ }
+ }
+}
+
+bool View::IsVisibleInRootView() const {
+ View* parent = GetParent();
+ if (IsVisible() && parent)
+ return parent->IsVisibleInRootView();
+ else
+ return false;
+}
+
+bool View::HitTest(const CPoint &l) const {
+ if (l.x >= 0 && l.x < static_cast<int>(GetWidth()) &&
+ l.y >= 0 && l.y < static_cast<int>(GetHeight())) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+HCURSOR View::GetCursorForPoint(Event::EventType event_type, int x, int y) {
+ return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// View - keyboard and focus
+//
+/////////////////////////////////////////////////////////////////////////////
+
+void View::RequestFocus() {
+ RootView* rv = GetRootView();
+ if (rv) {
+ rv->FocusView(this);
+ }
+}
+
+void View::WillGainFocus() {
+}
+
+void View::DidGainFocus() {
+}
+
+void View::WillLoseFocus() {
+}
+
+bool View::OnKeyPressed(const KeyEvent& e) {
+ return false;
+}
+
+bool View::OnKeyReleased(const KeyEvent& e) {
+ return false;
+}
+
+bool View::OnMouseWheel(const MouseWheelEvent& e) {
+ return false;
+}
+
+void View::SetDragController(DragController* drag_controller) {
+ drag_controller_ = drag_controller;
+}
+
+DragController* View::GetDragController() {
+ return drag_controller_;
+}
+
+bool View::CanDrop(const OSExchangeData& data) {
+ return false;
+}
+
+void View::OnDragEntered(const DropTargetEvent& event) {
+}
+
+int View::OnDragUpdated(const DropTargetEvent& event) {
+ return DragDropTypes::DRAG_NONE;
+}
+
+void View::OnDragExited() {
+}
+
+int View::OnPerformDrop(const DropTargetEvent& event) {
+ return DragDropTypes::DRAG_NONE;
+}
+
+static int GetHorizontalDragThreshold() {
+ static int threshold = -1;
+ if (threshold == -1)
+ threshold = GetSystemMetrics(SM_CXDRAG) / 2;
+ return threshold;
+}
+
+static int GetVerticalDragThreshold() {
+ static int threshold = -1;
+ if (threshold == -1)
+ threshold = GetSystemMetrics(SM_CYDRAG) / 2;
+ return threshold;
+}
+
+// static
+bool View::ExceededDragThreshold(int delta_x, int delta_y) {
+ return (abs(delta_x) > GetHorizontalDragThreshold() ||
+ abs(delta_y) > GetVerticalDragThreshold());
+}
+
+void View::Focus() {
+ // Set the native focus to the root view window so it receives the keyboard
+ // messages.
+ FocusManager* focus_manager = GetFocusManager();
+ if (focus_manager)
+ focus_manager->FocusHWND(GetRootView()->GetViewContainer()->GetHWND());
+}
+
+bool View::CanProcessTabKeyEvents() {
+ return false;
+}
+
+// Tooltips -----------------------------------------------------------------
+bool View::GetTooltipText(int x, int y, std::wstring* tooltip) {
+ return false;
+}
+
+bool View::GetTooltipTextOrigin(int x, int y, CPoint* loc) {
+ return false;
+}
+
+void View::TooltipTextChanged() {
+ ViewContainer* view_container = GetViewContainer();
+ if (view_container != NULL && view_container->GetTooltipManager())
+ view_container->GetTooltipManager()->TooltipTextChanged(this);
+}
+
+void View::UpdateTooltip() {
+ ViewContainer* view_container = GetViewContainer();
+ if (view_container != NULL && view_container->GetTooltipManager())
+ view_container->GetTooltipManager()->UpdateTooltip();
+}
+
+void View::SetParentOwned(bool f) {
+ is_parent_owned_ = f;
+}
+
+bool View::IsParentOwned() const {
+ return is_parent_owned_;
+}
+
+std::string View::GetClassName() const {
+ return kViewClassName;
+}
+
+gfx::Rect View::GetVisibleBounds() {
+ gfx::Rect vis_bounds(0, 0, GetWidth(), GetHeight());
+ gfx::Rect ancestor_bounds;
+ View* view = this;
+ int root_x = 0;
+ int root_y = 0;
+ bool has_view_container = false;
+ while (view != NULL && !vis_bounds.IsEmpty()) {
+ root_x += view->GetX(APPLY_MIRRORING_TRANSFORMATION);
+ root_y += view->GetY();
+ vis_bounds.Offset(view->GetX(APPLY_MIRRORING_TRANSFORMATION), view->GetY());
+ View* ancestor = view->GetParent();
+ if (ancestor != NULL) {
+ ancestor_bounds.SetRect(0, 0, ancestor->GetWidth(),
+ ancestor->GetHeight());
+ vis_bounds = vis_bounds.Intersect(ancestor_bounds);
+ } else if (!view->GetViewContainer()) {
+ // If the view has no ViewContainer, we're not visible. Return an empty
+ // rect.
+ return gfx::Rect();
+ }
+ view = ancestor;
+ }
+ if (vis_bounds.IsEmpty())
+ return vis_bounds;
+ // Convert back to this views coordinate system.
+ vis_bounds.Offset(-root_x, -root_y);
+ return vis_bounds;
+}
+
+int View::GetPageScrollIncrement(ScrollView* scroll_view,
+ bool is_horizontal, bool is_positive) {
+ return 0;
+}
+
+int View::GetLineScrollIncrement(ScrollView* scroll_view,
+ bool is_horizontal, bool is_positive) {
+ return 0;
+}
+
+// static
+void View::RegisterChildrenForVisibleBoundsNotification(
+ RootView* root, View* view) {
+ DCHECK(root && view);
+ if (view->GetNotifyWhenVisibleBoundsInRootChanges())
+ root->RegisterViewForVisibleBoundsNotification(view);
+ for (int i = 0; i < view->GetChildViewCount(); ++i)
+ RegisterChildrenForVisibleBoundsNotification(root, view->GetChildViewAt(i));
+}
+
+// static
+void View::UnregisterChildrenForVisibleBoundsNotification(
+ RootView* root, View* view) {
+ DCHECK(root && view);
+ if (view->GetNotifyWhenVisibleBoundsInRootChanges())
+ root->UnregisterViewForVisibleBoundsNotification(view);
+ for (int i = 0; i < view->GetChildViewCount(); ++i)
+ UnregisterChildrenForVisibleBoundsNotification(root,
+ view->GetChildViewAt(i));
+}
+
+void View::AddDescendantToNotify(View* view) {
+ DCHECK(view);
+ if (!descendants_to_notify_.get())
+ descendants_to_notify_.reset(new ViewList());
+ descendants_to_notify_->push_back(view);
+}
+
+void View::RemoveDescendantToNotify(View* view) {
+ DCHECK(view && descendants_to_notify_.get());
+ ViewList::iterator i = find(descendants_to_notify_->begin(),
+ descendants_to_notify_->end(),
+ view);
+ DCHECK(i != descendants_to_notify_->end());
+ descendants_to_notify_->erase(i);
+ if (descendants_to_notify_->empty())
+ descendants_to_notify_.reset();
+}
+
+// static
+bool View::GetViewPath(View* start, View* end, std::vector<int>* path) {
+ while (end && (end != start)) {
+ View* parent = end->GetParent();
+ if (!parent)
+ return false;
+ path->insert(path->begin(), parent->GetChildIndex(end));
+ end = parent;
+ }
+ return end == start;
+}
+
+// static
+View* View::GetViewForPath(View* start, const std::vector<int>& path) {
+ View* v = start;
+ for (std::vector<int>::const_iterator iter = path.begin();
+ iter != path.end(); ++iter) {
+ int index = *iter;
+ if (index >= v->GetChildViewCount())
+ return NULL;
+ v = v->GetChildViewAt(index);
+ }
+ return v;
+}
+
+// DropInfo --------------------------------------------------------------------
+
+void View::DragInfo::Reset() {
+ possible_drag = false;
+ start_x = start_y = 0;
+}
+
+void View::DragInfo::PossibleDrag(int x, int y) {
+ possible_drag = true;
+ start_x = x;
+ start_y = y;
+}
+
+} // namespace