summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-21 18:17:35 +0000
committerwez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-11-21 18:17:35 +0000
commit84ad47e975cb77a12881dc0c72053a22993950ca (patch)
tree343752188cf8571abc5533d9eb37efb1eab03ebd
parent586cf35708db57b9013a12adc8d8513163e558de (diff)
downloadchromium_src-84ad47e975cb77a12881dc0c72053a22993950ca.zip
chromium_src-84ad47e975cb77a12881dc0c72053a22993950ca.tar.gz
chromium_src-84ad47e975cb77a12881dc0c72053a22993950ca.tar.bz2
Revert "views: Move widget/ directory to ui/views." properly.
Review URL: http://codereview.chromium.org/8562003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@110960 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--views/widget/aero_tooltip_manager.cc111
-rw-r--r--views/widget/aero_tooltip_manager.h60
-rw-r--r--views/widget/child_window_message_processor.cc28
-rw-r--r--views/widget/child_window_message_processor.h48
-rw-r--r--views/widget/default_theme_provider.cc50
-rw-r--r--views/widget/default_theme_provider.h44
-rw-r--r--views/widget/drop_helper.cc156
-rw-r--r--views/widget/drop_helper.h108
-rw-r--r--views/widget/drop_target_gtk.cc329
-rw-r--r--views/widget/drop_target_gtk.h120
-rw-r--r--views/widget/drop_target_win.cc63
-rw-r--r--views/widget/drop_target_win.h56
-rw-r--r--views/widget/gtk_views_fixed.cc105
-rw-r--r--views/widget/gtk_views_fixed.h56
-rw-r--r--views/widget/gtk_views_window.cc46
-rw-r--r--views/widget/gtk_views_window.h44
-rw-r--r--views/widget/monitor_win.cc37
-rw-r--r--views/widget/monitor_win.h29
-rw-r--r--views/widget/native_widget.h38
-rw-r--r--views/widget/native_widget_aura.cc794
-rw-r--r--views/widget/native_widget_aura.h186
-rw-r--r--views/widget/native_widget_delegate.h116
-rw-r--r--views/widget/native_widget_gtk.cc2312
-rw-r--r--views/widget/native_widget_gtk.h475
-rw-r--r--views/widget/native_widget_private.h216
-rw-r--r--views/widget/native_widget_test_utils.h22
-rw-r--r--views/widget/native_widget_test_utils_aura.cc37
-rw-r--r--views/widget/native_widget_test_utils_gtk.cc36
-rw-r--r--views/widget/native_widget_test_utils_win.cc37
-rw-r--r--views/widget/native_widget_unittest.cc83
-rw-r--r--views/widget/native_widget_win.cc2524
-rw-r--r--views/widget/native_widget_win.h664
-rw-r--r--views/widget/native_widget_win_unittest.cc86
-rw-r--r--views/widget/root_view.cc455
-rw-r--r--views/widget/root_view.h207
-rw-r--r--views/widget/tooltip_manager.cc63
-rw-r--r--views/widget/tooltip_manager.h68
-rw-r--r--views/widget/tooltip_manager_gtk.cc174
-rw-r--r--views/widget/tooltip_manager_gtk.h53
-rw-r--r--views/widget/tooltip_manager_views.cc239
-rw-r--r--views/widget/tooltip_manager_views.h80
-rw-r--r--views/widget/tooltip_manager_win.cc391
-rw-r--r--views/widget/tooltip_manager_win.h153
-rw-r--r--views/widget/widget.cc1218
-rw-r--r--views/widget/widget.h746
-rw-r--r--views/widget/widget_delegate.cc166
-rw-r--r--views/widget/widget_delegate.h181
-rw-r--r--views/widget/widget_unittest.cc829
-rw-r--r--views/widget/window_manager.cc100
-rw-r--r--views/widget/window_manager.h79
50 files changed, 14318 insertions, 0 deletions
diff --git a/views/widget/aero_tooltip_manager.cc b/views/widget/aero_tooltip_manager.cc
new file mode 100644
index 0000000..0a93687
--- /dev/null
+++ b/views/widget/aero_tooltip_manager.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2011 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/aero_tooltip_manager.h"
+
+#include <windows.h>
+#include <commctrl.h>
+#include <shlobj.h>
+
+#include "base/bind.h"
+#include "base/message_loop.h"
+#include "ui/base/l10n/l10n_util_win.h"
+#include "ui/base/win/hwnd_util.h"
+#include "ui/gfx/point.h"
+
+namespace views {
+
+///////////////////////////////////////////////////////////////////////////////
+// AeroTooltipManager, public:
+
+AeroTooltipManager::AeroTooltipManager(Widget* widget)
+ : TooltipManagerWin(widget),
+ initial_delay_(0) {
+}
+
+AeroTooltipManager::~AeroTooltipManager() {
+ if (initial_timer_)
+ initial_timer_->Disown();
+}
+
+void AeroTooltipManager::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) {
+ if (u_msg == WM_MOUSELEAVE) {
+ last_mouse_pos_.SetPoint(-1, -1);
+ UpdateTooltip();
+ return;
+ }
+
+ if (initial_timer_)
+ initial_timer_->Disown();
+
+ if (u_msg == WM_MOUSEMOVE || u_msg == WM_NCMOUSEMOVE) {
+ gfx::Point mouse_pos(l_param);
+ if (u_msg == WM_NCMOUSEMOVE) {
+ // NC message coordinates are in screen coordinates.
+ POINT temp = mouse_pos.ToPOINT();
+ ::MapWindowPoints(HWND_DESKTOP, GetParent(), &temp, 1);
+ mouse_pos.SetPoint(temp.x, temp.y);
+ }
+ if (last_mouse_pos_ != mouse_pos) {
+ last_mouse_pos_ = mouse_pos;
+ HideKeyboardTooltip();
+ UpdateTooltip(mouse_pos);
+ }
+
+ // Delay opening of the tooltip just in case the user moves their
+ // mouse to another control. We defer this from Init because we get
+ // zero if we query it too soon.
+ if (!initial_delay_) {
+ initial_delay_ = static_cast<int>(
+ ::SendMessage(tooltip_hwnd_, TTM_GETDELAYTIME, TTDT_INITIAL, 0));
+ }
+ initial_timer_ = new InitialTimer(this);
+ initial_timer_->Start(initial_delay_);
+ } else {
+ // Hide the tooltip and cancel any timers.
+ ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
+ ::SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, false, (LPARAM)&toolinfo_);
+ return;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AeroTooltipManager, private:
+
+void AeroTooltipManager::OnTimer() {
+ initial_timer_ = NULL;
+
+ POINT pt = last_mouse_pos_.ToPOINT();
+ ::ClientToScreen(GetParent(), &pt);
+
+ // Set the position and visibility.
+ if (!tooltip_showing_) {
+ ::SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0);
+ ::SendMessage(tooltip_hwnd_, TTM_TRACKPOSITION, 0, MAKELPARAM(pt.x, pt.y));
+ ::SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, true, (LPARAM)&toolinfo_);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// AeroTooltipManager::InitialTimer
+
+AeroTooltipManager::InitialTimer::InitialTimer(AeroTooltipManager* manager)
+ : manager_(manager) {
+}
+
+void AeroTooltipManager::InitialTimer::Start(int time) {
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, base::Bind(&InitialTimer::Execute, this), time);
+}
+
+void AeroTooltipManager::InitialTimer::Disown() {
+ manager_ = NULL;
+}
+
+void AeroTooltipManager::InitialTimer::Execute() {
+ if (manager_)
+ manager_->OnTimer();
+}
+
+} // namespace views
diff --git a/views/widget/aero_tooltip_manager.h b/views/widget/aero_tooltip_manager.h
new file mode 100644
index 0000000..9f322fd
--- /dev/null
+++ b/views/widget/aero_tooltip_manager.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_AERO_TOOLTIP_MANAGER_H_
+#define VIEWS_WIDGET_AERO_TOOLTIP_MANAGER_H_
+#pragma once
+
+#include "base/memory/ref_counted.h"
+#include "views/widget/tooltip_manager_win.h"
+
+namespace views {
+
+///////////////////////////////////////////////////////////////////////////////
+// AeroTooltipManager
+//
+// Default Windows tooltips are broken when using our custom window frame
+// - as soon as the tooltip receives a WM_MOUSEMOVE event, it starts spewing
+// NCHITTEST messages at its parent window (us). These messages have random
+// x/y coordinates and can't be ignored, as the DwmDefWindowProc uses
+// NCHITTEST messages to determine how to highlight the caption buttons
+// (the buttons then flicker as the hit tests sent by the user's mouse
+// trigger different effects to those sent by the tooltip).
+//
+// So instead, we have to partially implement tooltips ourselves using
+// TTF_TRACKed tooltips.
+//
+// TODO(glen): Resolve this with Microsoft.
+class AeroTooltipManager : public TooltipManagerWin {
+ public:
+ explicit AeroTooltipManager(Widget* widget);
+ virtual ~AeroTooltipManager();
+
+ virtual void OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param);
+
+ private:
+ void OnTimer();
+
+ class InitialTimer : public base::RefCounted<InitialTimer> {
+ public:
+ explicit InitialTimer(AeroTooltipManager* manager);
+ void Start(int time);
+ void Disown();
+ void Execute();
+
+ private:
+ friend class base::RefCounted<InitialTimer>;
+
+ ~InitialTimer() {}
+
+ AeroTooltipManager* manager_;
+ };
+
+ int initial_delay_;
+ scoped_refptr<InitialTimer> initial_timer_;
+};
+
+} // namespace views
+
+#endif // #ifndef VIEWS_WIDGET_AERO_TOOLTIP_MANAGER_H_
diff --git a/views/widget/child_window_message_processor.cc b/views/widget/child_window_message_processor.cc
new file mode 100644
index 0000000..e1cfa31
--- /dev/null
+++ b/views/widget/child_window_message_processor.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2010 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/child_window_message_processor.h"
+
+#include "base/logging.h"
+#include "ui/base/view_prop.h"
+
+namespace views {
+
+static const char* const kChildWindowKey = "__CHILD_WINDOW_MESSAGE_PROCESSOR__";
+
+// static
+ui::ViewProp* ChildWindowMessageProcessor::Register(
+ HWND hwnd,
+ ChildWindowMessageProcessor* processor) {
+ DCHECK(processor);
+ return new ui::ViewProp(hwnd, kChildWindowKey, processor);
+}
+
+// static
+ChildWindowMessageProcessor* ChildWindowMessageProcessor::Get(HWND hwnd) {
+ return reinterpret_cast<ChildWindowMessageProcessor*>(
+ ui::ViewProp::GetValue(hwnd, kChildWindowKey));
+}
+
+} // namespace
diff --git a/views/widget/child_window_message_processor.h b/views/widget/child_window_message_processor.h
new file mode 100644
index 0000000..a11b187
--- /dev/null
+++ b/views/widget/child_window_message_processor.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2010 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.
+
+#ifndef VIEWS_WIDGET_CHILD_WINDOW_MESSAGE_PROCESSOR_H_
+#define VIEWS_WIDGET_CHILD_WINDOW_MESSAGE_PROCESSOR_H_
+#pragma once
+
+#include <windows.h>
+
+namespace ui {
+class ViewProp;
+}
+
+namespace views {
+
+// Windows sends a handful of messages to the parent window rather than the
+// window itself. For example, selection changes of a rich edit (EN_SELCHANGE)
+// are sent to the parent, not the window. Typically such message are best
+// dealt with by the window rather than the parent. NativeWidgetWin allows for
+// registering a ChildWindowMessageProcessor to handle such messages.
+class ChildWindowMessageProcessor {
+ public:
+ // Registers |processor| for |hwnd|. The caller takes ownership of the
+ // returned object.
+ static ui::ViewProp* Register(HWND hwnd,
+ ChildWindowMessageProcessor* processor);
+
+ // Returns the ChildWindowMessageProcessor for |hwnd|, NULL if there isn't
+ // one.
+ static ChildWindowMessageProcessor* Get(HWND hwnd);
+
+ // Invoked for any messages that are sent to the parent and originated from
+ // the HWND this ChildWindowMessageProcessor was registered for. Returns true
+ // if the message was handled with a valid result in |result|. Returns false
+ // if the message was not handled.
+ virtual bool ProcessMessage(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ LRESULT* result) = 0;
+
+ protected:
+ virtual ~ChildWindowMessageProcessor() {}
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_CHILD_WINDOW_MESSAGE_PROCESSOR_H_
diff --git a/views/widget/default_theme_provider.cc b/views/widget/default_theme_provider.cc
new file mode 100644
index 0000000..6fdbb86
--- /dev/null
+++ b/views/widget/default_theme_provider.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 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/default_theme_provider.h"
+
+#include "ui/base/resource/resource_bundle.h"
+
+#if defined(OS_WIN) && !defined(USE_AURA)
+#include "views/widget/native_widget_win.h"
+#endif
+
+namespace views {
+
+DefaultThemeProvider::DefaultThemeProvider() {}
+
+DefaultThemeProvider::~DefaultThemeProvider() {}
+
+void DefaultThemeProvider::Init(Profile* profile) {}
+
+SkBitmap* DefaultThemeProvider::GetBitmapNamed(int id) const {
+ return ResourceBundle::GetSharedInstance().GetBitmapNamed(id);
+}
+
+SkColor DefaultThemeProvider::GetColor(int id) const {
+ // Return debugging-blue.
+ return 0xff0000ff;
+}
+
+bool DefaultThemeProvider::GetDisplayProperty(int id, int* result) const {
+ return false;
+}
+
+bool DefaultThemeProvider::ShouldUseNativeFrame() const {
+#if defined(OS_WIN) && !defined(USE_AURA)
+ return NativeWidgetWin::IsAeroGlassEnabled();
+#else
+ return false;
+#endif
+}
+
+bool DefaultThemeProvider::HasCustomImage(int id) const {
+ return false;
+}
+
+RefCountedMemory* DefaultThemeProvider::GetRawData(int id) const {
+ return NULL;
+}
+
+} // namespace views
diff --git a/views/widget/default_theme_provider.h b/views/widget/default_theme_provider.h
new file mode 100644
index 0000000..9303e83
--- /dev/null
+++ b/views/widget/default_theme_provider.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_DEFAULT_THEME_PROVIDER_H_
+#define VIEWS_DEFAULT_THEME_PROVIDER_H_
+#pragma once
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "ui/base/theme_provider.h"
+#include "views/views_export.h"
+
+class Profile;
+
+namespace ui {
+class ResourceBundle;
+}
+using ui::ResourceBundle;
+
+namespace views {
+
+class VIEWS_EXPORT DefaultThemeProvider : public ui::ThemeProvider {
+ public:
+ DefaultThemeProvider();
+ virtual ~DefaultThemeProvider();
+
+ // Overridden from ui::ThemeProvider.
+ virtual void Init(Profile* profile);
+ virtual SkBitmap* GetBitmapNamed(int id) const;
+ virtual SkColor GetColor(int id) const;
+ virtual bool GetDisplayProperty(int id, int* result) const;
+ virtual bool ShouldUseNativeFrame() const;
+ virtual bool HasCustomImage(int id) const;
+ virtual RefCountedMemory* GetRawData(int id) const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DefaultThemeProvider);
+};
+
+} // namespace views
+
+#endif // VIEWS_DEFAULT_THEME_PROVIDER_H_
diff --git a/views/widget/drop_helper.cc b/views/widget/drop_helper.cc
new file mode 100644
index 0000000..4e5ce109
--- /dev/null
+++ b/views/widget/drop_helper.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2011 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/drop_helper.h"
+
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "views/view.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+DropHelper::DropHelper(View* root_view)
+ : root_view_(root_view),
+ target_view_(NULL),
+ deepest_view_(NULL) {
+}
+
+DropHelper::~DropHelper() {
+}
+
+void DropHelper::ResetTargetViewIfEquals(View* view) {
+ if (target_view_ == view)
+ target_view_ = NULL;
+ if (deepest_view_ == view)
+ deepest_view_ = NULL;
+}
+
+int DropHelper::OnDragOver(const OSExchangeData& data,
+ const gfx::Point& root_view_location,
+ int drag_operation) {
+ View* view = CalculateTargetViewImpl(root_view_location, data, true,
+ &deepest_view_);
+
+ if (view != target_view_) {
+ // Target changed notify old drag exited, then new drag entered.
+ NotifyDragExit();
+ target_view_ = view;
+ NotifyDragEntered(data, root_view_location, drag_operation);
+ }
+
+ return NotifyDragOver(data, root_view_location, drag_operation);
+}
+
+void DropHelper::OnDragExit() {
+ NotifyDragExit();
+ deepest_view_ = target_view_ = NULL;
+}
+
+int DropHelper::OnDrop(const OSExchangeData& data,
+ const gfx::Point& root_view_location,
+ int drag_operation) {
+ View* drop_view = target_view_;
+ deepest_view_ = target_view_ = NULL;
+ if (!drop_view)
+ return ui::DragDropTypes::DRAG_NONE;
+
+ if (drag_operation == ui::DragDropTypes::DRAG_NONE) {
+ drop_view->OnDragExited();
+ return ui::DragDropTypes::DRAG_NONE;
+ }
+
+ gfx::Point view_location(root_view_location);
+ View* root_view = drop_view->GetWidget()->GetRootView();
+ View::ConvertPointToView(root_view, drop_view, &view_location);
+ DropTargetEvent drop_event(data, view_location.x(), view_location.y(),
+ drag_operation);
+ return drop_view->OnPerformDrop(drop_event);
+}
+
+View* DropHelper::CalculateTargetView(
+ const gfx::Point& root_view_location,
+ const OSExchangeData& data,
+ bool check_can_drop) {
+ return CalculateTargetViewImpl(root_view_location, data, check_can_drop,
+ NULL);
+}
+
+View* DropHelper::CalculateTargetViewImpl(
+ const gfx::Point& root_view_location,
+ const OSExchangeData& data,
+ bool check_can_drop,
+ View** deepest_view) {
+ View* view = root_view_->GetEventHandlerForPoint(root_view_location);
+ if (view == deepest_view_) {
+ // The view the mouse is over hasn't changed; reuse the target.
+ return target_view_;
+ }
+ if (deepest_view)
+ *deepest_view = view;
+ // TODO(sky): for the time being these are separate. Once I port chrome menu
+ // I can switch to the #else implementation and nuke the OS_WIN
+ // implementation.
+#if defined(OS_WIN)
+ // View under mouse changed, which means a new view may want the drop.
+ // Walk the tree, stopping at target_view_ as we know it'll accept the
+ // drop.
+ while (view && view != target_view_ &&
+ (!view->IsEnabled() || !view->CanDrop(data))) {
+ view = view->parent();
+ }
+#else
+ int formats = 0;
+ std::set<OSExchangeData::CustomFormat> custom_formats;
+ while (view && view != target_view_) {
+ if (view->IsEnabled() &&
+ view->GetDropFormats(&formats, &custom_formats) &&
+ data.HasAnyFormat(formats, custom_formats) &&
+ (!check_can_drop || view->CanDrop(data))) {
+ // Found the view.
+ return view;
+ }
+ formats = 0;
+ custom_formats.clear();
+ view = view->parent();
+ }
+#endif
+ return view;
+}
+
+void DropHelper::NotifyDragEntered(const OSExchangeData& data,
+ const gfx::Point& root_view_location,
+ int drag_operation) {
+ if (!target_view_)
+ return;
+
+ gfx::Point target_view_location(root_view_location);
+ View::ConvertPointToView(root_view_, target_view_, &target_view_location);
+ DropTargetEvent enter_event(data,
+ target_view_location.x(),
+ target_view_location.y(),
+ drag_operation);
+ target_view_->OnDragEntered(enter_event);
+}
+
+int DropHelper::NotifyDragOver(const OSExchangeData& data,
+ const gfx::Point& root_view_location,
+ int drag_operation) {
+ if (!target_view_)
+ return ui::DragDropTypes::DRAG_NONE;
+
+ gfx::Point target_view_location(root_view_location);
+ View::ConvertPointToView(root_view_, target_view_, &target_view_location);
+ DropTargetEvent enter_event(data,
+ target_view_location.x(),
+ target_view_location.y(),
+ drag_operation);
+ return target_view_->OnDragUpdated(enter_event);
+}
+
+void DropHelper::NotifyDragExit() {
+ if (target_view_)
+ target_view_->OnDragExited();
+}
+
+} // namespace views
diff --git a/views/widget/drop_helper.h b/views/widget/drop_helper.h
new file mode 100644
index 0000000..cc62bfc
--- /dev/null
+++ b/views/widget/drop_helper.h
@@ -0,0 +1,108 @@
+// 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.
+
+#ifndef VIEWS_WIDGET_DROP_HELPER_H_
+#define VIEWS_WIDGET_DROP_HELPER_H_
+#pragma once
+
+#include "base/basictypes.h"
+
+namespace gfx {
+class Point;
+} // namespace gfx
+
+namespace ui {
+class OSExchangeData;
+} // namespace ui
+using ui::OSExchangeData;
+
+namespace views {
+
+class RootView;
+class View;
+
+// DropHelper provides support for managing the view a drop is going to occur
+// at during dnd as well as sending the view the appropriate dnd methods.
+// DropHelper is intended to be used by a class that interacts with the system
+// drag and drop. The system class invokes OnDragOver as the mouse moves,
+// then either OnDragExit or OnDrop when the drop is done.
+class DropHelper {
+ public:
+ explicit DropHelper(View* root_view);
+ ~DropHelper();
+
+ // Current view drop events are targeted at, may be NULL.
+ View* target_view() const { return target_view_; }
+
+ // Returns the RootView the DropHelper was created with.
+ View* root_view() const { return root_view_; }
+
+ // Resets the target_view_ to NULL if it equals view.
+ //
+ // This is invoked when a View is removed from the RootView to make sure
+ // we don't target a view that was removed during dnd.
+ void ResetTargetViewIfEquals(View* view);
+
+ // Invoked when a the mouse is dragged over the root view during a drag and
+ // drop operation. This method returns a bitmask of the types in DragDropTypes
+ // for the target view. If no view wants the drop, DRAG_NONE is returned.
+ int OnDragOver(const OSExchangeData& data,
+ const gfx::Point& root_view_location,
+ int drag_operation);
+
+ // Invoked when a the mouse is dragged out of the root view during a drag and
+ // drop operation.
+ void OnDragExit();
+
+ // Invoked when the user drops data on the root view during a drag and drop
+ // operation. See OnDragOver for details on return type.
+ //
+ // NOTE: implementations must invoke OnDragOver before invoking this,
+ // supplying the return value from OnDragOver as the drag_operation.
+ int OnDrop(const OSExchangeData& data,
+ const gfx::Point& root_view_location,
+ int drag_operation);
+
+ // Calculates the target view for a drop given the specified location in
+ // the coordinate system of the rootview. This tries to avoid continually
+ // querying CanDrop by returning target_view_ if the mouse is still over
+ // target_view_.
+ View* CalculateTargetView(const gfx::Point& root_view_location,
+ const OSExchangeData& data,
+ bool check_can_drop);
+
+ private:
+ // Implementation of CalculateTargetView. If |deepest_view| is non-NULL it is
+ // set to the deepest descendant of the RootView that contains the point
+ // |root_view_location|
+ View* CalculateTargetViewImpl(const gfx::Point& root_view_location,
+ const OSExchangeData& data,
+ bool check_can_drop,
+ View** deepest_view);
+
+ // Methods to send the appropriate drop notification to the targeted view.
+ // These do nothing if the target view is NULL.
+ void NotifyDragEntered(const OSExchangeData& data,
+ const gfx::Point& root_view_location,
+ int drag_operation);
+ int NotifyDragOver(const OSExchangeData& data,
+ const gfx::Point& root_view_location,
+ int drag_operation);
+ void NotifyDragExit();
+
+ // RootView we were created for.
+ View* root_view_;
+
+ // View we're targeting events at.
+ View* target_view_;
+
+ // The deepest view under the current drop coordinate.
+ View* deepest_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(DropHelper);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_DROP_HELPER_H_
diff --git a/views/widget/drop_target_gtk.cc b/views/widget/drop_target_gtk.cc
new file mode 100644
index 0000000..8d8e760
--- /dev/null
+++ b/views/widget/drop_target_gtk.cc
@@ -0,0 +1,329 @@
+// Copyright (c) 2011 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/drop_target_gtk.h"
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/net_util.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/gtk_dnd_util.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_gtk.h"
+#include "ui/gfx/point.h"
+#include "views/widget/root_view.h"
+#include "views/widget/native_widget_gtk.h"
+
+using ui::OSExchangeData;
+
+namespace {
+
+std::string GdkAtomToString(GdkAtom atom) {
+ gchar* c_name = gdk_atom_name(atom);
+ std::string name(c_name);
+ g_free(c_name);
+ return name;
+}
+
+// Returns true if |name| is a known name of plain text.
+bool IsTextType(const std::string& name) {
+ return name == "text/plain" || name == "TEXT" ||
+ name == "STRING" || name == "UTF8_STRING" ||
+ name == "text/plain;charset=utf-8";
+}
+
+// Returns the OSExchangeData::Formats in |targets| and all the
+// OSExchangeData::CustomFormats in |type_set|.
+int CalculateTypes(GList* targets, std::set<GdkAtom>* type_set) {
+ int types = 0;
+ for (GList* element = targets; element;
+ element = g_list_next(element)) {
+ GdkAtom atom = static_cast<GdkAtom>(element->data);
+ type_set->insert(atom);
+ if (atom == GDK_TARGET_STRING) {
+ types |= OSExchangeData::STRING;
+ } else if (atom == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) {
+ types |= OSExchangeData::URL;
+ } else if (atom == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) {
+ // TEXT_URI_LIST is used for files as well as urls.
+ types |= OSExchangeData::URL | OSExchangeData::FILE_NAME;
+ } else {
+ std::string target_name = GdkAtomToString(atom);
+ if (IsTextType(target_name)) {
+ types |= OSExchangeData::STRING;
+ } else {
+ // Assume any unknown data is pickled.
+ types |= OSExchangeData::PICKLED_DATA;
+ }
+ }
+ }
+ return types;
+}
+
+} // namespace
+
+namespace views {
+
+DropTargetGtk::DropTargetGtk(internal::RootView* root_view,
+ GdkDragContext* context)
+ : helper_(root_view),
+ requested_formats_(0),
+ waiting_for_data_(false),
+ received_drop_(false),
+ pending_view_(NULL) {
+ std::set<GdkAtom> all_formats;
+ int source_formats = CalculateTypes(context->targets, &all_formats);
+ data_.reset(new OSExchangeData(new OSExchangeDataProviderGtk(
+ source_formats, all_formats)));
+}
+
+DropTargetGtk::~DropTargetGtk() {
+}
+
+void DropTargetGtk::ResetTargetViewIfEquals(View* view) {
+ helper_.ResetTargetViewIfEquals(view);
+}
+
+void DropTargetGtk::OnDragDataReceived(GdkDragContext* context,
+ gint x,
+ gint y,
+ GtkSelectionData* data,
+ guint info,
+ guint time) {
+ std::string target_name = GdkAtomToString(data->type);
+ if (data->type == GDK_TARGET_STRING || IsTextType(target_name)) {
+ guchar* text_data = gtk_selection_data_get_text(data);
+ string16 result;
+ if (text_data) {
+ char* as_char = reinterpret_cast<char*>(text_data);
+ UTF8ToUTF16(as_char, strlen(as_char), &result);
+ g_free(text_data);
+ }
+ data_provider().SetString(result);
+ } else if (requested_custom_formats_.find(data->type) !=
+ requested_custom_formats_.end()) {
+ Pickle result;
+ if (data->length > 0)
+ result = Pickle(reinterpret_cast<char*>(data->data), data->length);
+ data_provider().SetPickledData(data->type, result);
+ } else if (data->type == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) {
+ GURL url;
+ string16 title;
+ ui::ExtractNamedURL(data, &url, &title);
+ data_provider().SetURL(url, title);
+ } else if (data->type == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) {
+ std::vector<GURL> urls;
+ ui::ExtractURIList(data, &urls);
+ if (urls.size() == 1 && urls[0].is_valid()) {
+ data_provider().SetURL(urls[0], string16());
+
+ // TEXT_URI_LIST is used for files as well as urls.
+ if (urls[0].SchemeIsFile()) {
+ FilePath file_path;
+ if (net::FileURLToFilePath(urls[0], &file_path))
+ data_provider().SetFilename(file_path);
+ }
+ } else {
+ // Consumers of OSExchangeData will see this as an invalid URL. That is,
+ // when GetURL is invoked on the OSExchangeData this triggers false to
+ // be returned.
+ data_provider().SetURL(GURL(), string16());
+ }
+ }
+
+ if (!data_->HasAllFormats(requested_formats_, requested_custom_formats_))
+ return; // Waiting on more data.
+
+ int drag_operation = ui::DragDropTypes::GdkDragActionToDragOperation(
+ context->actions);
+ gfx::Point root_view_location(x, y);
+ drag_operation = helper_.OnDragOver(*data_, root_view_location,
+ drag_operation);
+ GdkDragAction gdk_action = static_cast<GdkDragAction>(
+ ui::DragDropTypes::DragOperationToGdkDragAction(drag_operation));
+ if (!received_drop_)
+ gdk_drag_status(context, gdk_action, time);
+
+ waiting_for_data_ = false;
+
+ if (pending_view_ && received_drop_) {
+ FinishDrop(context, x, y, time);
+ // WARNING: we've been deleted.
+ return;
+ }
+}
+
+gboolean DropTargetGtk::OnDragDrop(GdkDragContext* context,
+ gint x,
+ gint y,
+ guint time) {
+ received_drop_ = true;
+ OnDragMotion(context, x, y, time);
+ if (!pending_view_) {
+ // User isn't over a view, no drop can occur.
+ static_cast<NativeWidgetGtk*>(
+ helper_.root_view()->GetWidget()->native_widget())->ResetDropTarget();
+ // WARNING: we've been deleted.
+ return FALSE;
+ }
+
+ if (!waiting_for_data_) {
+ // We've got all the data now.
+ FinishDrop(context, x, y, time);
+ // WARNING: we've been deleted.
+ return TRUE;
+ }
+ // We're waiting on data.
+ return TRUE;
+}
+
+void DropTargetGtk::OnDragLeave(GdkDragContext* context, guint time) {
+ helper_.OnDragExit();
+}
+
+gboolean DropTargetGtk::OnDragMotion(GdkDragContext* context,
+ gint x,
+ gint y,
+ guint time) {
+ waiting_for_data_ = false;
+ gfx::Point root_view_location(x, y);
+ pending_view_ =
+ helper_.CalculateTargetView(root_view_location, *data_, false);
+ if (pending_view_ &&
+ (received_drop_ || (pending_view_ != helper_.target_view() &&
+ pending_view_->AreDropTypesRequired()))) {
+ // The target requires drop types before it can answer CanDrop,
+ // ask for the data now.
+ int formats = 0;
+ std::set<GdkAtom> custom_formats;
+ pending_view_->GetDropFormats(&formats, &custom_formats);
+ IntersectFormats(data_provider().known_formats(),
+ data_provider().known_custom_formats(),
+ &formats, &custom_formats);
+ if (!data_provider().HasDataForAllFormats(formats, custom_formats)) {
+ if (!received_drop_)
+ helper_.OnDragExit();
+
+ // The target needs data for all the types before it can test if the
+ // drop is valid, but we don't have all the data. Request the data
+ // now. When we get back the data we'll update the target.
+ RequestFormats(context, formats, custom_formats, time);
+
+ waiting_for_data_ = true;
+
+ return TRUE;
+ }
+ }
+
+ int drag_operation = ui::DragDropTypes::GdkDragActionToDragOperation(
+ context->actions);
+ drag_operation = helper_.OnDragOver(*data_, root_view_location,
+ drag_operation);
+ if (!received_drop_) {
+ GdkDragAction gdk_action =
+ static_cast<GdkDragAction>(
+ ui::DragDropTypes::DragOperationToGdkDragAction(drag_operation));
+ gdk_drag_status(context, gdk_action, time);
+ }
+ return TRUE;
+}
+
+void DropTargetGtk::FinishDrop(GdkDragContext* context,
+ gint x, gint y, guint time) {
+ gfx::Point root_view_location(x, y);
+ int drag_operation = ui::DragDropTypes::GdkDragActionToDragOperation(
+ context->actions);
+ drag_operation = helper_.OnDrop(*data_, root_view_location,
+ drag_operation);
+ GdkDragAction gdk_action =
+ static_cast<GdkDragAction>(
+ ui::DragDropTypes::DragOperationToGdkDragAction(drag_operation));
+ gtk_drag_finish(context, gdk_action != 0, (gdk_action & GDK_ACTION_MOVE),
+ time);
+
+ static_cast<NativeWidgetGtk*>(helper_.root_view()->GetWidget()->
+ native_widget())->ResetDropTarget();
+ // WARNING: we've been deleted.
+}
+
+void DropTargetGtk::IntersectFormats(int f1, const std::set<GdkAtom>& cf1,
+ int* f2, std::set<GdkAtom>* cf2) {
+ *f2 = (*f2 & f1);
+ std::set<GdkAtom> cf;
+ std::set_intersection(
+ cf1.begin(), cf1.end(), cf2->begin(), cf2->end(),
+ std::insert_iterator<std::set<GdkAtom> >(cf, cf.begin()));
+ cf.swap(*cf2);
+}
+
+void DropTargetGtk::RequestFormats(GdkDragContext* context,
+ int formats,
+ const std::set<GdkAtom>& custom_formats,
+ guint time) {
+ GtkWidget* widget = static_cast<NativeWidgetGtk*>(helper_.root_view()->
+ GetWidget()->native_widget())->window_contents();
+
+ const std::set<GdkAtom>& known_formats =
+ data_provider().known_custom_formats();
+ if ((formats & OSExchangeData::STRING) != 0 &&
+ (requested_formats_ & OSExchangeData::STRING) == 0) {
+ requested_formats_ |= OSExchangeData::STRING;
+ if (known_formats.count(gdk_atom_intern("UTF8_STRING", false))) {
+ gtk_drag_get_data(widget, context,
+ gdk_atom_intern("UTF8_STRING", false), time);
+ } else if (known_formats.count(gdk_atom_intern("text/plain;charset=utf-8",
+ false))) {
+ gtk_drag_get_data(widget, context,
+ gdk_atom_intern("text/plain;charset=utf-8", false),
+ time);
+ } else if (known_formats.count(GDK_TARGET_STRING)) {
+ gtk_drag_get_data(widget, context, GDK_TARGET_STRING, time);
+ } else if (known_formats.count(gdk_atom_intern("text/plain", false))) {
+ gtk_drag_get_data(widget, context, gdk_atom_intern("text/plain", false),
+ time);
+ } else if (known_formats.count(gdk_atom_intern("TEXT", false))) {
+ gtk_drag_get_data(widget, context, gdk_atom_intern("TEXT", false),
+ time);
+ } else if (known_formats.count(gdk_atom_intern("STRING", false))) {
+ gtk_drag_get_data(widget, context, gdk_atom_intern("STRING", false),
+ time);
+ }
+ }
+ if ((formats & OSExchangeData::URL) != 0 &&
+ (requested_formats_ & OSExchangeData::URL) == 0) {
+ requested_formats_ |= OSExchangeData::URL;
+ if (known_formats.count(ui::GetAtomForTarget(ui::CHROME_NAMED_URL))) {
+ gtk_drag_get_data(widget, context,
+ ui::GetAtomForTarget(ui::CHROME_NAMED_URL), time);
+ } else if (known_formats.count(
+ ui::GetAtomForTarget(ui::TEXT_URI_LIST))) {
+ gtk_drag_get_data(widget, context,
+ ui::GetAtomForTarget(ui::TEXT_URI_LIST), time);
+ }
+ }
+ if (((formats & OSExchangeData::FILE_NAME) != 0) &&
+ (requested_formats_ & OSExchangeData::FILE_NAME) == 0) {
+ requested_formats_ |= OSExchangeData::FILE_NAME;
+ gtk_drag_get_data(widget, context,
+ ui::GetAtomForTarget(ui::TEXT_URI_LIST), time);
+ }
+ for (std::set<GdkAtom>::const_iterator i = custom_formats.begin();
+ i != custom_formats.end(); ++i) {
+ if (requested_custom_formats_.find(*i) ==
+ requested_custom_formats_.end()) {
+ requested_custom_formats_.insert(*i);
+ gtk_drag_get_data(widget, context, *i, time);
+ }
+ }
+}
+
+OSExchangeDataProviderGtk& DropTargetGtk::data_provider() const {
+ return static_cast<OSExchangeDataProviderGtk&>(data_->provider());
+}
+
+} // namespace views
diff --git a/views/widget/drop_target_gtk.h b/views/widget/drop_target_gtk.h
new file mode 100644
index 0000000..daa08f2
--- /dev/null
+++ b/views/widget/drop_target_gtk.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_DROP_TARGET_GTK_H_
+#define VIEWS_WIDGET_DROP_TARGET_GTK_H_
+#pragma once
+
+#include <gtk/gtk.h>
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "views/widget/drop_helper.h"
+
+namespace ui {
+class OSExchangeDataProviderGtk;
+}
+using ui::OSExchangeData;
+using ui::OSExchangeDataProviderGtk;
+
+namespace views {
+
+class View;
+namespace internal {
+class RootView;
+}
+
+// DropTarget implementation for Gtk.
+//
+// The data for a drop is not immediately available on X. As such we lazily
+// ask for data as necessary. Some Views require data before they can determine
+// if the drop is going to be allowed. When such a View is encountered the
+// relevant data is requested from the drag source. When the data is available
+// the target is notified. Similarly if the drop completes and the data has
+// not yet been fetched, it is fetched and the target then notified.
+//
+// When a drop finishes this class calls back to the containing NativeWidgetGtk
+// which results in deleting the DropTargetGtk.
+class DropTargetGtk {
+ public:
+ DropTargetGtk(internal::RootView* root_view, GdkDragContext* context);
+ ~DropTargetGtk();
+
+ // If a drag and drop is underway and |view| is the current drop target, the
+ // drop target is set to null.
+ // This is invoked when a View is removed from the RootView to make sure
+ // we don't target a view that was removed during dnd.
+ void ResetTargetViewIfEquals(View* view);
+
+ // Drop methods from Gtk. These are forwarded from the containing
+ // NativeWidgetGtk.
+ void OnDragDataReceived(GdkDragContext* context,
+ gint x,
+ gint y,
+ GtkSelectionData* data,
+ guint info,
+ guint time);
+ gboolean OnDragDrop(GdkDragContext* context,
+ gint x,
+ gint y,
+ guint time);
+ void OnDragLeave(GdkDragContext* context, guint time);
+ gboolean OnDragMotion(GdkDragContext* context,
+ gint x,
+ gint y,
+ guint time);
+
+ private:
+ // Invoked when the drop finishes AND all the data is available.
+ void FinishDrop(GdkDragContext* context, gint x, gint y, guint time);
+
+ // Returns in |f2| and |cf2| the intersection of |f1| |f2| and
+ // |cf1|, |cf2|.
+ void IntersectFormats(int f1, const std::set<GdkAtom>& cf1,
+ int* f2, std::set<GdkAtom>* cf2);
+
+ // Requests the formats in |formats| and the custom formats in
+ // |custom_formats|.
+ void RequestFormats(GdkDragContext* context,
+ int formats,
+ const std::set<GdkAtom>& custom_formats,
+ guint time);
+
+ // Reutrns the Provider of the OSExchangeData we created.
+ OSExchangeDataProviderGtk& data_provider() const;
+
+ // Manages sending the appropriate drop methods to the view the drop is over.
+ DropHelper helper_;
+
+ // The formats we've requested from the drag source.
+ //
+ // NOTE: these formats are the intersection of the formats requested by the
+ // drop target and the formats provided by the source.
+ int requested_formats_;
+ std::set<GdkAtom> requested_custom_formats_;
+
+ // The data.
+ scoped_ptr<OSExchangeData> data_;
+
+ // Are we waiting for data from the source before we can notify the view?
+ // This is set in two distinct ways: when the view requires the data before
+ // it can answer Can Drop (that is, AreDropTypesRequired returns true) and
+ // when the user dropped the data but we didn't get it all yet.
+ bool waiting_for_data_;
+
+ // Has OnDragDrop been invoked?
+ bool received_drop_;
+
+ // The view under the mouse. This is not necessarily the same as
+ // helper_.target_view(). The two differ if the view under the mouse requires
+ // the data.
+ View* pending_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(DropTargetGtk);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_DROP_TARGET_GTK_H_
diff --git a/views/widget/drop_target_win.cc b/views/widget/drop_target_win.cc
new file mode 100644
index 0000000..c4c8e2c
--- /dev/null
+++ b/views/widget/drop_target_win.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2010 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/drop_target_win.h"
+
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
+#include "ui/gfx/point.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget.h"
+
+using ui::OSExchangeData;
+using ui::OSExchangeDataProviderWin;
+
+namespace views {
+
+DropTargetWin::DropTargetWin(internal::RootView* root_view)
+ : ui::DropTarget(root_view->GetWidget()->GetNativeView()),
+ helper_(root_view) {
+}
+
+DropTargetWin::~DropTargetWin() {
+}
+
+void DropTargetWin::ResetTargetViewIfEquals(View* view) {
+ helper_.ResetTargetViewIfEquals(view);
+}
+
+DWORD DropTargetWin::OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ gfx::Point root_view_location(cursor_position.x, cursor_position.y);
+ View::ConvertPointToView(NULL, helper_.root_view(), &root_view_location);
+ OSExchangeData data(new OSExchangeDataProviderWin(data_object));
+ int drop_operation =
+ helper_.OnDragOver(data, root_view_location,
+ ui::DragDropTypes::DropEffectToDragOperation(effect));
+ return ui::DragDropTypes::DragOperationToDropEffect(drop_operation);
+}
+
+void DropTargetWin::OnDragLeave(IDataObject* data_object) {
+ helper_.OnDragExit();
+}
+
+DWORD DropTargetWin::OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ gfx::Point root_view_location(cursor_position.x, cursor_position.y);
+ View::ConvertPointToView(NULL, helper_.root_view(), &root_view_location);
+
+ OSExchangeData data(new OSExchangeDataProviderWin(data_object));
+ int drop_operation = ui::DragDropTypes::DropEffectToDragOperation(effect);
+ drop_operation = helper_.OnDragOver(data, root_view_location,
+ drop_operation);
+ drop_operation = helper_.OnDrop(data, root_view_location, drop_operation);
+ return ui::DragDropTypes::DragOperationToDropEffect(drop_operation);
+}
+
+} // namespace views
diff --git a/views/widget/drop_target_win.h b/views/widget/drop_target_win.h
new file mode 100644
index 0000000..966bbc2
--- /dev/null
+++ b/views/widget/drop_target_win.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 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.
+
+#ifndef VIEWS_WIDGET_DROP_TARGET_WIN_H_
+#define VIEWS_WIDGET_DROP_TARGET_WIN_H_
+#pragma once
+
+#include "ui/base/dragdrop/drop_target.h"
+#include "views/widget/drop_helper.h"
+
+namespace views {
+
+class View;
+namespace internal {
+class RootView;
+}
+
+// DropTargetWin takes care of managing drag and drop for NativeWidgetWin. It
+// converts Windows OLE drop messages into Views drop messages.
+//
+// DropTargetWin uses DropHelper to manage the appropriate view to target
+// drop messages at.
+class DropTargetWin : public ui::DropTarget {
+ public:
+ explicit DropTargetWin(internal::RootView* root_view);
+ virtual ~DropTargetWin();
+
+ // If a drag and drop is underway and view is the current drop target, the
+ // drop target is set to null.
+ // This is invoked when a View is removed from the RootView to make sure
+ // we don't target a view that was removed during dnd.
+ void ResetTargetViewIfEquals(View* view);
+
+ protected:
+ virtual DWORD OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+
+ virtual void OnDragLeave(IDataObject* data_object);
+
+ virtual DWORD OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+
+ private:
+ views::DropHelper helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(DropTargetWin);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_DROP_TARGET_WIN_H_
diff --git a/views/widget/gtk_views_fixed.cc b/views/widget/gtk_views_fixed.cc
new file mode 100644
index 0000000..6ab70df8
--- /dev/null
+++ b/views/widget/gtk_views_fixed.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2011 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/gtk_views_fixed.h"
+
+#include "base/logging.h"
+
+// We store whether we use the widget's allocated size as a property. Ideally
+// we would stash this in GtkFixedChild, but GtkFixed doesn't allow subclassing
+// gtk_fixed_put. Alternatively we could subclass GtkContainer and use our own
+// API (effectively duplicating GtkFixed), but that means folks could no longer
+// use the GtkFixed API else where in Chrome. For now I'm going with this route.
+static const char* kUseAllocatedSize = "__VIEWS_USE_ALLOCATED_SIZE__";
+static const char* kRequisitionWidth = "__VIEWS_REQUISITION_WIDTH__";
+static const char* kRequisitionHeight = "__VIEWS_REQUISITION_HEIGHT__";
+
+G_BEGIN_DECLS
+
+G_DEFINE_TYPE(GtkViewsFixed, gtk_views_fixed, GTK_TYPE_FIXED)
+
+static void gtk_views_fixed_size_allocate(GtkWidget* widget,
+ GtkAllocation* allocation) {
+ widget->allocation = *allocation;
+ if (!GTK_WIDGET_NO_WINDOW(widget) && GTK_WIDGET_REALIZED(widget)) {
+ gdk_window_move_resize(widget->window, allocation->x, allocation->y,
+ allocation->width, allocation->height);
+ }
+
+ int border_width = GTK_CONTAINER(widget)->border_width;
+ GList* children = GTK_FIXED(widget)->children;
+ while (children) {
+ GtkFixedChild* child = reinterpret_cast<GtkFixedChild*>(children->data);
+ children = children->next;
+
+ if (GTK_WIDGET_VISIBLE(child->widget)) {
+ GtkAllocation child_allocation;
+
+ int width, height;
+ bool use_allocated_size =
+ gtk_views_fixed_get_widget_size(child->widget, &width, &height);
+ if (use_allocated_size) {
+ // NOTE: even though the size isn't changing, we have to call
+ // size_allocate, otherwise things like buttons won't repaint.
+ child_allocation.width = width;
+ child_allocation.height = height;
+ } else {
+ GtkRequisition child_requisition;
+ gtk_widget_get_child_requisition(child->widget, &child_requisition);
+ child_allocation.width = child_requisition.width;
+ child_allocation.height = child_requisition.height;
+ }
+ child_allocation.x = child->x + border_width;
+ child_allocation.y = child->y + border_width;
+
+ if (GTK_WIDGET_NO_WINDOW(widget)) {
+ child_allocation.x += widget->allocation.x;
+ child_allocation.y += widget->allocation.y;
+ }
+
+ gtk_widget_size_allocate(child->widget, &child_allocation);
+ }
+ }
+}
+
+static void gtk_views_fixed_class_init(GtkViewsFixedClass* views_fixed_class) {
+ GtkWidgetClass* widget_class =
+ reinterpret_cast<GtkWidgetClass*>(views_fixed_class);
+ widget_class->size_allocate = gtk_views_fixed_size_allocate;
+}
+
+static void gtk_views_fixed_init(GtkViewsFixed* fixed) {
+ GTK_WIDGET_SET_FLAGS(GTK_WIDGET(fixed), GTK_CAN_FOCUS);
+}
+
+GtkWidget* gtk_views_fixed_new(void) {
+ return GTK_WIDGET(g_object_new(GTK_TYPE_VIEWS_FIXED, NULL));
+}
+
+void gtk_views_fixed_set_widget_size(GtkWidget* widget,
+ int width, int height) {
+ // Remember the allocation request, and set this widget up to use it.
+ bool use_requested_size = (width != 0 && height != 0);
+ g_object_set_data(G_OBJECT(widget), kUseAllocatedSize,
+ reinterpret_cast<gpointer>(use_requested_size ? 1 : 0));
+ g_object_set_data(G_OBJECT(widget), kRequisitionWidth,
+ reinterpret_cast<gpointer>(width));
+ g_object_set_data(G_OBJECT(widget), kRequisitionHeight,
+ reinterpret_cast<gpointer>(height));
+
+ gtk_widget_queue_resize(widget);
+}
+
+bool gtk_views_fixed_get_widget_size(GtkWidget* widget,
+ int* width, int* height) {
+ DCHECK(width);
+ DCHECK(height);
+ *width = reinterpret_cast<glong>(g_object_get_data(G_OBJECT(widget),
+ kRequisitionWidth));
+ *height = reinterpret_cast<glong>(g_object_get_data(G_OBJECT(widget),
+ kRequisitionHeight));
+ return (g_object_get_data(G_OBJECT(widget), kUseAllocatedSize) != 0);
+}
+
+G_END_DECLS
diff --git a/views/widget/gtk_views_fixed.h b/views/widget/gtk_views_fixed.h
new file mode 100644
index 0000000..e9f3fe7
--- /dev/null
+++ b/views/widget/gtk_views_fixed.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2009 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.
+
+#ifndef VIEWS_WIDGET_GTK_VIEWS_FIXED_H_
+#define VIEWS_WIDGET_GTK_VIEWS_FIXED_H_
+#pragma once
+
+#include <gdk/gdk.h>
+#include <gtk/gtkfixed.h>
+
+// GtkViewsFixed is a subclass of GtkFixed that can give child widgets
+// a set size rather than their requisitioned size (which is actually
+// a minimum size, and that can cause issues). This behavior is
+// controlled by gtk_views_fixed_set_widget_size; the default is to
+// use the Widget's requisitioned size.
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_VIEWS_FIXED (gtk_views_fixed_get_type ())
+#define GTK_VIEWS_FIXED(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_VIEWS_FIXED, GtkViewsFixed))
+#define GTK_VIEWS_FIXED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_VIEWS_FIXED, GtkViewsFixedClass))
+#define GTK_IS_VIEWS_FIXED(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_VIEWS_FIXED))
+#define GTK_IS_VIEWS_FIXED_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_VIEWS_FIXED))
+#define GTK_VIEWS_FIXED_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_VIEWS_FIXED, GtkViewsFixed))
+
+typedef struct _GtkViewsFixed GtkViewsFixed;
+typedef struct _GtkViewsFixedClass GtkViewsFixedClass;
+
+struct _GtkViewsFixed {
+ GtkFixed fixed;
+};
+
+struct _GtkViewsFixedClass {
+ GtkFixedClass parent_class;
+};
+
+GtkWidget* gtk_views_fixed_new();
+
+GType gtk_views_fixed_get_type();
+
+// If width and height are 0, go back to using the requisitioned size.
+// Queues up a re-size on the widget.
+void gtk_views_fixed_set_widget_size(GtkWidget* widget, int width, int height);
+
+bool gtk_views_fixed_get_widget_size(GtkWidget* widget,
+ int* width, int* height);
+
+G_END_DECLS
+
+#endif // VIEWS_WIDGET_GTK_VIEWS_FIXED_H
diff --git a/views/widget/gtk_views_window.cc b/views/widget/gtk_views_window.cc
new file mode 100644
index 0000000..1e5b48f
--- /dev/null
+++ b/views/widget/gtk_views_window.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 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 <gtk/gtk.h>
+
+#include "ui/views/events/event.h"
+#include "ui/views/focus/focus_manager.h"
+#include "views/widget/gtk_views_window.h"
+#include "views/widget/widget.h"
+
+G_BEGIN_DECLS
+
+G_DEFINE_TYPE(GtkViewsWindow, gtk_views_window, GTK_TYPE_WINDOW)
+
+static void gtk_views_window_move_focus(GtkWindow* window,
+ GtkDirectionType dir) {
+ views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
+ views::FocusManager* focus_manager =
+ widget ? widget->GetFocusManager() : NULL;
+ if (focus_manager) {
+ GdkEvent* key = gtk_get_current_event();
+ if (key && key->type == GDK_KEY_PRESS) {
+ views::KeyEvent key_event(key);
+ focus_manager->OnKeyEvent(key_event);
+ }
+ } else if (GTK_WINDOW_CLASS(gtk_views_window_parent_class)->move_focus) {
+ GTK_WINDOW_CLASS(gtk_views_window_parent_class)->move_focus(window, dir);
+ }
+}
+
+static void gtk_views_window_class_init(
+ GtkViewsWindowClass* views_window_class) {
+ GtkWindowClass* window_class =
+ reinterpret_cast<GtkWindowClass*>(views_window_class);
+ window_class->move_focus = gtk_views_window_move_focus;
+}
+
+static void gtk_views_window_init(GtkViewsWindow* window) {
+}
+
+GtkWidget* gtk_views_window_new(GtkWindowType type) {
+ return GTK_WIDGET(g_object_new(GTK_TYPE_VIEWS_WINDOW, "type", type, NULL));
+}
+
+G_END_DECLS
diff --git a/views/widget/gtk_views_window.h b/views/widget/gtk_views_window.h
new file mode 100644
index 0000000..8557db0
--- /dev/null
+++ b/views/widget/gtk_views_window.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 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.
+
+#ifndef VIEWS_WIDGET_GTK_VIEWS_WINDOW_H_
+#define VIEWS_WIDGET_GTK_VIEWS_WINDOW_H_
+#pragma once
+
+#include <gtk/gtkwindow.h>
+
+// GtkViewsWindow is a subclass of GtkWindow that overrides its move_focus
+// method so that we can handle focus traversing by ourselves.
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_VIEWS_WINDOW (gtk_views_window_get_type ())
+#define GTK_VIEWS_WINDOW(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_VIEWS_WINDOW, GtkViewsWindow))
+#define GTK_VIEWS_WINDOW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_VIEWS_WINDOW, \
+ GtkViewsWindowClass))
+#define GTK_IS_VIEWS_WINDOW(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_VIEWS_WINDOW))
+#define GTK_IS_VIEWS_WINDOW_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_VIEWS_WINDOW))
+#define GTK_VIEWS_WINDOW_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_VIEWS_WINDOW, GtkViewsWindow))
+
+typedef struct _GtkViewsWindow GtkViewsWindow;
+typedef struct _GtkViewsWindowClass GtkViewsWindowClass;
+
+struct _GtkViewsWindow {
+ GtkWindow window;
+};
+
+struct _GtkViewsWindowClass {
+ GtkWindowClass parent_class;
+};
+
+GtkWidget* gtk_views_window_new(GtkWindowType type);
+
+G_END_DECLS
+
+#endif // VIEWS_WIDGET_GTK_VIEWS_WINDOW_H
diff --git a/views/widget/monitor_win.cc b/views/widget/monitor_win.cc
new file mode 100644
index 0000000..d840893
--- /dev/null
+++ b/views/widget/monitor_win.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 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/monitor_win.h"
+
+#include <shellapi.h>
+
+#include "base/logging.h"
+#include "ui/gfx/rect.h"
+
+namespace views {
+
+gfx::Rect GetMonitorBoundsForRect(const gfx::Rect& rect) {
+ RECT p_rect = rect.ToRECT();
+ HMONITOR monitor = MonitorFromRect(&p_rect, MONITOR_DEFAULTTONEAREST);
+ if (monitor) {
+ MONITORINFO mi = {0};
+ mi.cbSize = sizeof(mi);
+ GetMonitorInfo(monitor, &mi);
+ return gfx::Rect(mi.rcWork);
+ }
+ NOTREACHED();
+ return gfx::Rect();
+}
+
+HWND GetTopmostAutoHideTaskbarForEdge(UINT edge, HMONITOR monitor) {
+ APPBARDATA taskbar_data = { sizeof APPBARDATA, NULL, 0, edge };
+ HWND taskbar = reinterpret_cast<HWND>(SHAppBarMessage(ABM_GETAUTOHIDEBAR,
+ &taskbar_data));
+ return (::IsWindow(taskbar) && (monitor != NULL) &&
+ (MonitorFromWindow(taskbar, MONITOR_DEFAULTTONULL) == monitor) &&
+ (GetWindowLong(taskbar, GWL_EXSTYLE) & WS_EX_TOPMOST)) ?
+ taskbar : NULL;
+}
+
+} // namespace views
diff --git a/views/widget/monitor_win.h b/views/widget/monitor_win.h
new file mode 100644
index 0000000..7ceacfa
--- /dev/null
+++ b/views/widget/monitor_win.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_MONITOR_WIN_H_
+#define VIEWS_WIDGET_MONITOR_WIN_H_
+#pragma once
+
+#include <windows.h>
+#include "views/views_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace views {
+
+// Returns the bounds for the monitor that contains the largest area of
+// intersection with the specified rectangle.
+VIEWS_EXPORT gfx::Rect GetMonitorBoundsForRect(const gfx::Rect& rect);
+
+// Returns the always-on-top auto-hiding taskbar for edge |edge| (one of
+// ABE_LEFT, TOP, RIGHT, or BOTTOM) of monitor |monitor|. NULL is returned
+// if nothing is found.
+VIEWS_EXPORT HWND GetTopmostAutoHideTaskbarForEdge(UINT edge, HMONITOR monitor);
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_MONITOR_WIN_H_
diff --git a/views/widget/native_widget.h b/views/widget/native_widget.h
new file mode 100644
index 0000000..9289fde
--- /dev/null
+++ b/views/widget/native_widget.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_NATIVE_WIDGET_H_
+#define VIEWS_WIDGET_NATIVE_WIDGET_H_
+#pragma once
+
+#include "views/widget/widget.h"
+
+namespace views {
+namespace internal {
+class NativeWidgetPrivate;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidget interface
+//
+// An interface that serves as the public API base for the
+// internal::NativeWidget interface that Widget uses to communicate with a
+// backend-specific native widget implementation. This is the only component of
+// this interface that is publicly visible, and exists solely for exposure via
+// Widget's native_widget() accessor, which code occasionally static_casts to
+// a known implementation in platform-specific code.
+//
+class VIEWS_EXPORT NativeWidget {
+ public:
+ virtual ~NativeWidget() {}
+
+ private:
+ friend class Widget;
+
+ virtual internal::NativeWidgetPrivate* AsNativeWidgetPrivate() = 0;
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_NATIVE_WIDGET_H_
diff --git a/views/widget/native_widget_aura.cc b/views/widget/native_widget_aura.cc
new file mode 100644
index 0000000..57677f5
--- /dev/null
+++ b/views/widget/native_widget_aura.cc
@@ -0,0 +1,794 @@
+// Copyright (c) 2011 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/native_widget_aura.h"
+
+#include "base/bind.h"
+#include "base/string_util.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/drag_drop_client.h"
+#include "ui/aura/desktop.h"
+#include "ui/aura/desktop_observer.h"
+#include "ui/aura/event.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_types.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/compositor/layer.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/screen.h"
+#include "views/widget/drop_helper.h"
+#include "views/widget/native_widget_delegate.h"
+#include "views/widget/tooltip_manager_views.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_gdi_object.h"
+#include "base/win/win_util.h"
+#include "ui/base/l10n/l10n_util_win.h"
+#endif
+
+#if defined(HAVE_IBUS)
+#include "ui/views/ime/input_method_ibus.h"
+#else
+#include "ui/views/ime/mock_input_method.h"
+#endif
+
+namespace views {
+
+namespace {
+
+aura::WindowType GetAuraWindowTypeForWidgetType(Widget::InitParams::Type type) {
+ switch (type) {
+ case Widget::InitParams::TYPE_WINDOW:
+ return aura::WINDOW_TYPE_NORMAL;
+ case Widget::InitParams::TYPE_WINDOW_FRAMELESS:
+ case Widget::InitParams::TYPE_CONTROL:
+ case Widget::InitParams::TYPE_POPUP:
+ case Widget::InitParams::TYPE_BUBBLE:
+ return aura::WINDOW_TYPE_POPUP;
+ case Widget::InitParams::TYPE_MENU:
+ return aura::WINDOW_TYPE_MENU;
+ case Widget::InitParams::TYPE_TOOLTIP:
+ return aura::WINDOW_TYPE_TOOLTIP;
+ default:
+ NOTREACHED() << "Unhandled widget type " << type;
+ return aura::WINDOW_TYPE_UNKNOWN;
+ }
+}
+
+} // namespace
+
+// Used when SetInactiveRenderingDisabled() is invoked to track when active
+// status changes in such a way that we should enable inactive rendering.
+class NativeWidgetAura::DesktopObserverImpl : public aura::DesktopObserver {
+ public:
+ explicit DesktopObserverImpl(NativeWidgetAura* host)
+ : host_(host) {
+ aura::Desktop::GetInstance()->AddObserver(this);
+ }
+
+ virtual ~DesktopObserverImpl() {
+ aura::Desktop::GetInstance()->RemoveObserver(this);
+ }
+
+ // DesktopObserver overrides:
+ virtual void OnActiveWindowChanged(aura::Window* active) OVERRIDE {
+ if (!active || (active != host_->window_ &&
+ active->transient_parent() != host_->window_)) {
+ host_->delegate_->EnableInactiveRendering();
+ }
+ }
+
+ private:
+ NativeWidgetAura* host_;
+
+ DISALLOW_COPY_AND_ASSIGN(DesktopObserverImpl);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetAura, public:
+
+NativeWidgetAura::NativeWidgetAura(internal::NativeWidgetDelegate* delegate)
+ : delegate_(delegate),
+ ALLOW_THIS_IN_INITIALIZER_LIST(window_(new aura::Window(this))),
+ ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET),
+ ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)),
+ can_activate_(true),
+ cursor_(gfx::kNullCursor) {
+}
+
+NativeWidgetAura::~NativeWidgetAura() {
+ if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
+ delete delegate_;
+ else
+ CloseNow();
+}
+
+// static
+gfx::Font NativeWidgetAura::GetWindowTitleFont() {
+#if defined(OS_WIN)
+ NONCLIENTMETRICS ncm;
+ base::win::GetNonClientMetrics(&ncm);
+ l10n_util::AdjustUIFont(&(ncm.lfCaptionFont));
+ base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont)));
+ return gfx::Font(caption_font);
+#else
+ return gfx::Font();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetAura, internal::NativeWidgetPrivate implementation:
+
+void NativeWidgetAura::InitNativeWidget(const Widget::InitParams& params) {
+ ownership_ = params.ownership;
+ window_->set_user_data(this);
+ Widget::InitParams::Type window_type =
+ params.child ? Widget::InitParams::TYPE_CONTROL : params.type;
+ window_->SetType(GetAuraWindowTypeForWidgetType(window_type));
+ window_->SetIntProperty(aura::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ window_->Init(params.create_texture_for_layer ?
+ ui::Layer::LAYER_HAS_TEXTURE :
+ ui::Layer::LAYER_HAS_NO_TEXTURE);
+ if (window_type == Widget::InitParams::TYPE_CONTROL)
+ window_->Show();
+
+ window_->layer()->SetFillsBoundsOpaquely(!params.transparent);
+ delegate_->OnNativeWidgetCreated();
+ window_->SetBounds(params.bounds);
+ if (window_type == Widget::InitParams::TYPE_CONTROL) {
+ window_->SetParent(params.GetParent());
+ } else {
+ // Set up the transient child before the window is added. This way the
+ // LayoutManager knows the window has a transient parent.
+ gfx::NativeView parent = params.GetParent();
+ if (parent)
+ parent->AddTransientChild(window_);
+ // SetAlwaysOnTop before SetParent so that always-on-top container is used.
+ SetAlwaysOnTop(params.keep_on_top);
+ window_->SetParent(NULL);
+ }
+ window_->set_ignore_events(!params.accept_events);
+ // TODO(beng): do this some other way.
+ delegate_->OnNativeWidgetSizeChanged(params.bounds.size());
+ can_activate_ = params.can_activate;
+ DCHECK(GetWidget()->GetRootView());
+ if (params.type != Widget::InitParams::TYPE_TOOLTIP && !params.child) {
+ views::TooltipManagerViews* manager = new views::TooltipManagerViews(
+ GetWidget()->GetRootView());
+ tooltip_manager_.reset(manager);
+ }
+ drop_helper_.reset(new DropHelper(GetWidget()->GetRootView()));
+ if (params.type != Widget::InitParams::TYPE_TOOLTIP &&
+ params.type != Widget::InitParams::TYPE_POPUP) {
+ window_->SetProperty(aura::kDragDropDelegateKey,
+ static_cast<aura::WindowDragDropDelegate*>(this));
+ }
+}
+
+NonClientFrameView* NativeWidgetAura::CreateNonClientFrameView() {
+ return NULL;
+}
+
+void NativeWidgetAura::UpdateFrameAfterFrameChange() {
+ // We don't support changing the frame type.
+ NOTREACHED();
+}
+
+bool NativeWidgetAura::ShouldUseNativeFrame() const {
+ // There is only one frame type for aura.
+ return false;
+}
+
+void NativeWidgetAura::FrameTypeChanged() {
+ // This is called when the Theme has changed; forward the event to the root
+ // widget.
+ GetWidget()->ThemeChanged();
+ GetWidget()->GetRootView()->SchedulePaint();
+}
+
+Widget* NativeWidgetAura::GetWidget() {
+ return delegate_->AsWidget();
+}
+
+const Widget* NativeWidgetAura::GetWidget() const {
+ return delegate_->AsWidget();
+}
+
+gfx::NativeView NativeWidgetAura::GetNativeView() const {
+ return window_;
+}
+
+gfx::NativeWindow NativeWidgetAura::GetNativeWindow() const {
+ return window_;
+}
+
+Widget* NativeWidgetAura::GetTopLevelWidget() {
+ NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
+ return native_widget ? native_widget->GetWidget() : NULL;
+}
+
+const ui::Compositor* NativeWidgetAura::GetCompositor() const {
+ return window_->layer()->GetCompositor();
+}
+
+ui::Compositor* NativeWidgetAura::GetCompositor() {
+ return window_->layer()->GetCompositor();
+}
+
+void NativeWidgetAura::CalculateOffsetToAncestorWithLayer(
+ gfx::Point* offset,
+ ui::Layer** layer_parent) {
+ if (layer_parent)
+ *layer_parent = window_->layer();
+}
+
+void NativeWidgetAura::ReorderLayers() {
+}
+
+void NativeWidgetAura::ViewRemoved(View* view) {
+ // DropTarget stuff. Most likely http://crbug.com/97845
+ //NOTIMPLEMENTED();
+}
+
+void NativeWidgetAura::SetNativeWindowProperty(const char* name, void* value) {
+ if (window_)
+ window_->SetProperty(name, value);
+}
+
+void* NativeWidgetAura::GetNativeWindowProperty(const char* name) const {
+ return window_ ? window_->GetProperty(name) : NULL;
+}
+
+TooltipManager* NativeWidgetAura::GetTooltipManager() const {
+ return tooltip_manager_.get();
+}
+
+bool NativeWidgetAura::IsScreenReaderActive() const {
+ // http://crbug.com/102570
+ //NOTIMPLEMENTED();
+ return false;
+}
+
+void NativeWidgetAura::SendNativeAccessibilityEvent(
+ View* view,
+ ui::AccessibilityTypes::Event event_type) {
+ // http://crbug.com/102570
+ //NOTIMPLEMENTED();
+}
+
+void NativeWidgetAura::SetMouseCapture() {
+ window_->SetCapture();
+}
+
+void NativeWidgetAura::ReleaseMouseCapture() {
+ window_->ReleaseCapture();
+}
+
+bool NativeWidgetAura::HasMouseCapture() const {
+ return window_->HasCapture();
+}
+
+InputMethod* NativeWidgetAura::CreateInputMethod() {
+#if defined(HAVE_IBUS)
+ InputMethod* input_method = new InputMethodIBus(this);
+#else
+ InputMethod* input_method = new MockInputMethod(this);
+#endif
+ input_method->Init(GetWidget());
+ return input_method;
+}
+
+void NativeWidgetAura::CenterWindow(const gfx::Size& size) {
+ const gfx::Rect parent_bounds = window_->parent()->bounds();
+ window_->SetBounds(gfx::Rect((parent_bounds.width() - size.width())/2,
+ (parent_bounds.height() - size.height())/2,
+ size.width(),
+ size.height()));
+}
+
+void NativeWidgetAura::GetWindowPlacement(
+ gfx::Rect* bounds,
+ ui::WindowShowState* show_state) const {
+ *bounds = window_->GetTargetBounds();
+ *show_state = static_cast<ui::WindowShowState>(
+ window_->GetIntProperty(aura::kShowStateKey));
+}
+
+void NativeWidgetAura::SetWindowTitle(const string16& title) {
+ window_->set_title(title);
+}
+
+void NativeWidgetAura::SetWindowIcons(const SkBitmap& window_icon,
+ const SkBitmap& app_icon) {
+ // Aura doesn't have window icons.
+}
+
+void NativeWidgetAura::SetAccessibleName(const string16& name) {
+ // http://crbug.com/102570
+ //NOTIMPLEMENTED();
+}
+
+void NativeWidgetAura::SetAccessibleRole(ui::AccessibilityTypes::Role role) {
+ // http://crbug.com/102570
+ //NOTIMPLEMENTED();
+}
+
+void NativeWidgetAura::SetAccessibleState(ui::AccessibilityTypes::State state) {
+ // http://crbug.com/102570
+ //NOTIMPLEMENTED();
+}
+
+void NativeWidgetAura::BecomeModal() {
+ window_->SetIntProperty(aura::kModalKey, 1);
+}
+
+gfx::Rect NativeWidgetAura::GetWindowScreenBounds() const {
+ return window_->GetScreenBounds();
+}
+
+gfx::Rect NativeWidgetAura::GetClientAreaScreenBounds() const {
+ // In Aura, the entire window is the client area.
+ return window_->GetScreenBounds();
+}
+
+gfx::Rect NativeWidgetAura::GetRestoredBounds() const {
+ gfx::Rect* restore_bounds = reinterpret_cast<gfx::Rect*>(
+ window_->GetProperty(aura::kRestoreBoundsKey));
+ return restore_bounds ? *restore_bounds : window_->bounds();
+}
+
+void NativeWidgetAura::SetBounds(const gfx::Rect& bounds) {
+ window_->SetBounds(bounds);
+}
+
+void NativeWidgetAura::SetSize(const gfx::Size& size) {
+ window_->SetBounds(gfx::Rect(window_->bounds().origin(), size));
+}
+
+void NativeWidgetAura::MoveAbove(gfx::NativeView native_view) {
+ if (window_->parent() && window_->parent() == native_view->parent())
+ window_->parent()->MoveChildAbove(window_, native_view);
+}
+
+void NativeWidgetAura::MoveToTop() {
+ window_->parent()->MoveChildToFront(window_);
+}
+
+void NativeWidgetAura::SetShape(gfx::NativeRegion region) {
+ // No need for this.
+}
+
+void NativeWidgetAura::Close() {
+ // |window_| may already be deleted by parent window. This can happen
+ // when this widget is child widget or has transient parent
+ // and ownership is WIDGET_OWNS_NATIVE_WIDGET.
+ DCHECK(window_ ||
+ ownership_ == Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
+ if (window_)
+ Hide();
+
+ window_->SetIntProperty(aura::kModalKey, 0);
+
+ if (!close_widget_factory_.HasWeakPtrs()) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&NativeWidgetAura::CloseNow,
+ close_widget_factory_.GetWeakPtr()));
+ }
+}
+
+void NativeWidgetAura::CloseNow() {
+ delete window_;
+}
+
+void NativeWidgetAura::EnableClose(bool enable) {
+ // http://crbug.com/102581
+ NOTIMPLEMENTED();
+}
+
+void NativeWidgetAura::Show() {
+ ShowWithWindowState(ui::SHOW_STATE_INACTIVE);
+}
+
+void NativeWidgetAura::Hide() {
+ window_->Hide();
+}
+
+void NativeWidgetAura::ShowMaximizedWithBounds(
+ const gfx::Rect& restored_bounds) {
+ window_->SetBounds(restored_bounds);
+ ShowWithWindowState(ui::SHOW_STATE_MAXIMIZED);
+}
+
+void NativeWidgetAura::ShowWithWindowState(ui::WindowShowState state) {
+ if (state == ui::SHOW_STATE_MAXIMIZED ||
+ state == ui::SHOW_STATE_FULLSCREEN) {
+ window_->SetIntProperty(aura::kShowStateKey, state);
+ }
+ window_->Show();
+ if (can_activate_ && (state != ui::SHOW_STATE_INACTIVE ||
+ !GetWidget()->SetInitialFocus())) {
+ window_->Activate();
+ }
+}
+
+bool NativeWidgetAura::IsVisible() const {
+ return window_->IsVisible();
+}
+
+void NativeWidgetAura::Activate() {
+ window_->Activate();
+}
+
+void NativeWidgetAura::Deactivate() {
+ window_->Deactivate();
+}
+
+bool NativeWidgetAura::IsActive() const {
+ return aura::Desktop::GetInstance()->active_window() == window_;
+}
+
+void NativeWidgetAura::SetAlwaysOnTop(bool on_top) {
+ window_->SetIntProperty(aura::kAlwaysOnTopKey, on_top);
+}
+
+void NativeWidgetAura::Maximize() {
+ window_->SetIntProperty(aura::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+}
+
+void NativeWidgetAura::Minimize() {
+ // No minimized window for aura. crbug.com/104571.
+ NOTREACHED();
+}
+
+bool NativeWidgetAura::IsMaximized() const {
+ return window_->GetIntProperty(aura::kShowStateKey) ==
+ ui::SHOW_STATE_MAXIMIZED;
+}
+
+bool NativeWidgetAura::IsMinimized() const {
+ return window_->GetIntProperty(aura::kShowStateKey) ==
+ ui::SHOW_STATE_MINIMIZED;
+}
+
+void NativeWidgetAura::Restore() {
+ window_->SetIntProperty(aura::kShowStateKey, ui::SHOW_STATE_NORMAL);
+}
+
+void NativeWidgetAura::SetFullscreen(bool fullscreen) {
+ window_->SetIntProperty(
+ aura::kShowStateKey,
+ fullscreen ? ui::SHOW_STATE_FULLSCREEN : ui::SHOW_STATE_NORMAL);
+}
+
+bool NativeWidgetAura::IsFullscreen() const {
+ return window_->GetIntProperty(aura::kShowStateKey) ==
+ ui::SHOW_STATE_FULLSCREEN;
+}
+
+void NativeWidgetAura::SetOpacity(unsigned char opacity) {
+ window_->layer()->SetOpacity(opacity / 255.0);
+}
+
+void NativeWidgetAura::SetUseDragFrame(bool use_drag_frame) {
+ NOTIMPLEMENTED();
+}
+
+bool NativeWidgetAura::IsAccessibleWidget() const {
+ // http://crbug.com/102570
+ //NOTIMPLEMENTED();
+ return false;
+}
+
+void NativeWidgetAura::RunShellDrag(View* view,
+ const ui::OSExchangeData& data,
+ int operation) {
+ aura::DragDropClient* client = static_cast<aura::DragDropClient*>(
+ aura::Desktop::GetInstance()->GetProperty(
+ aura::kDesktopDragDropClientKey));
+ if (client)
+ client->StartDragAndDrop(data, operation);
+}
+
+void NativeWidgetAura::SchedulePaintInRect(const gfx::Rect& rect) {
+ if (window_)
+ window_->SchedulePaintInRect(rect);
+}
+
+void NativeWidgetAura::SetCursor(gfx::NativeCursor cursor) {
+ cursor_ = cursor;
+ aura::Desktop::GetInstance()->SetCursor(cursor);
+}
+
+void NativeWidgetAura::ClearNativeFocus() {
+ if (window_ && window_->GetFocusManager())
+ window_->GetFocusManager()->SetFocusedWindow(window_);
+}
+
+void NativeWidgetAura::FocusNativeView(gfx::NativeView native_view) {
+ // http://crbug.com/102572
+ NOTIMPLEMENTED();
+}
+
+bool NativeWidgetAura::ConvertPointFromAncestor(const Widget* ancestor,
+ gfx::Point* point) const {
+ // http://crbug.com/102573
+ NOTIMPLEMENTED();
+ return false;
+}
+
+gfx::Rect NativeWidgetAura::GetWorkAreaBoundsInScreen() const {
+ return gfx::Screen::GetMonitorWorkAreaNearestWindow(GetNativeView());
+}
+
+void NativeWidgetAura::SetInactiveRenderingDisabled(bool value) {
+ if (!value)
+ desktop_observer_.reset();
+ else
+ desktop_observer_.reset(new DesktopObserverImpl(this));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetAura, views::InputMethodDelegate implementation:
+
+void NativeWidgetAura::DispatchKeyEventPostIME(const KeyEvent& key) {
+ if (delegate_->OnKeyEvent(key))
+ return;
+ if (key.type() == ui::ET_KEY_PRESSED && GetWidget()->GetFocusManager())
+ GetWidget()->GetFocusManager()->OnKeyEvent(key);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetAura, aura::WindowDelegate implementation:
+
+void NativeWidgetAura::OnBoundsChanging(gfx::Rect* new_bounds) {
+ // Enforces a minimum size.
+ const gfx::Size& min_size = delegate_->GetMinimumSize();
+ new_bounds->set_width(std::max(min_size.width(), new_bounds->width()));
+ new_bounds->set_height(std::max(min_size.height(), new_bounds->height()));
+}
+
+void NativeWidgetAura::OnBoundsChanged(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ if (old_bounds.origin() != new_bounds.origin())
+ GetWidget()->widget_delegate()->OnWidgetMove();
+ if (old_bounds.size() != new_bounds.size())
+ delegate_->OnNativeWidgetSizeChanged(new_bounds.size());
+}
+
+void NativeWidgetAura::OnFocus() {
+ Widget* widget = GetWidget();
+ if (widget->is_top_level()) {
+ InputMethod* input_method = widget->GetInputMethod();
+ input_method->OnFocus();
+ // See description of got_initial_focus_in_ for details on this.
+ // TODO(mazda): Investigate this is actually necessary.
+ // widget->GetFocusManager()->RestoreFocusedView();
+ }
+ delegate_->OnNativeFocus(window_);
+}
+
+void NativeWidgetAura::OnBlur() {
+ Widget* widget = GetWidget();
+ if (widget->is_top_level()) {
+ InputMethod* input_method = widget->GetInputMethod();
+ input_method->OnBlur();
+ widget->GetFocusManager()->StoreFocusedView();
+ }
+ delegate_->OnNativeBlur(NULL);
+}
+
+bool NativeWidgetAura::OnKeyEvent(aura::KeyEvent* event) {
+ // TODO(beng): Need an InputMethodAura to properly handle character events.
+ // Right now, we just skip these.
+ if (event->is_char())
+ return false;
+
+ DCHECK(window_->IsVisible());
+ InputMethod* input_method = GetWidget()->GetInputMethod();
+ DCHECK(input_method);
+ // TODO(oshima): DispatchKeyEvent should return bool?
+ KeyEvent views_event(event);
+ input_method->DispatchKeyEvent(views_event);
+ return true;
+}
+
+gfx::NativeCursor NativeWidgetAura::GetCursor(const gfx::Point& point) {
+ return cursor_;
+}
+
+int NativeWidgetAura::GetNonClientComponent(const gfx::Point& point) const {
+ return delegate_->GetNonClientComponent(point);
+}
+
+bool NativeWidgetAura::OnMouseEvent(aura::MouseEvent* event) {
+ DCHECK(window_->IsVisible());
+ if (event->type() == ui::ET_MOUSEWHEEL) {
+ MouseWheelEvent wheel_event(event);
+ if (tooltip_manager_.get())
+ tooltip_manager_->UpdateForMouseEvent(wheel_event);
+ return delegate_->OnMouseEvent(wheel_event);
+ }
+ MouseEvent mouse_event(event);
+ if (tooltip_manager_.get())
+ tooltip_manager_->UpdateForMouseEvent(mouse_event);
+ return delegate_->OnMouseEvent(mouse_event);
+}
+
+ui::TouchStatus NativeWidgetAura::OnTouchEvent(aura::TouchEvent* event) {
+ DCHECK(window_->IsVisible());
+ TouchEvent touch_event(event);
+ return delegate_->OnTouchEvent(touch_event);
+}
+
+bool NativeWidgetAura::ShouldActivate(aura::Event* event) {
+ return can_activate_;
+}
+
+void NativeWidgetAura::OnActivated() {
+ delegate_->OnNativeWidgetActivationChanged(true);
+ if (IsVisible() && GetWidget()->non_client_view())
+ GetWidget()->non_client_view()->SchedulePaint();
+}
+
+void NativeWidgetAura::OnLostActive() {
+ delegate_->OnNativeWidgetActivationChanged(false);
+ if (IsVisible() && GetWidget()->non_client_view())
+ GetWidget()->non_client_view()->SchedulePaint();
+}
+
+void NativeWidgetAura::OnCaptureLost() {
+ delegate_->OnMouseCaptureLost();
+}
+
+void NativeWidgetAura::OnPaint(gfx::Canvas* canvas) {
+ delegate_->OnNativeWidgetPaint(canvas);
+}
+
+void NativeWidgetAura::OnWindowDestroying() {
+ window_->SetProperty(aura::kDragDropDelegateKey, NULL);
+ delegate_->OnNativeWidgetDestroying();
+
+ // If the aura::Window is destroyed, we can no longer show tooltips.
+ tooltip_manager_.reset();
+}
+
+void NativeWidgetAura::OnWindowDestroyed() {
+ window_ = NULL;
+ tooltip_manager_.reset();
+ delegate_->OnNativeWidgetDestroyed();
+ if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
+ delete this;
+}
+
+void NativeWidgetAura::OnWindowVisibilityChanged(bool visible) {
+ delegate_->OnNativeWidgetVisibilityChanged(visible);
+}
+
+bool NativeWidgetAura::CanDrop(const aura::DropTargetEvent& event) {
+ DCHECK(drop_helper_.get() != NULL);
+ View* view = drop_helper_->target_view();
+ if (view)
+ return view->CanDrop(event.data());
+ return false;
+}
+
+void NativeWidgetAura::OnDragEntered(const aura::DropTargetEvent& event) {
+ DCHECK(drop_helper_.get() != NULL);
+ drop_helper_->OnDragOver(event.data(), event.location(),
+ event.source_operations());
+}
+
+int NativeWidgetAura::OnDragUpdated(const aura::DropTargetEvent& event) {
+ DCHECK(drop_helper_.get() != NULL);
+ return drop_helper_->OnDragOver(event.data(), event.location(),
+ event.source_operations());
+}
+
+void NativeWidgetAura::OnDragExited() {
+ DCHECK(drop_helper_.get() != NULL);
+ drop_helper_->OnDragExit();
+}
+
+int NativeWidgetAura::OnPerformDrop(const aura::DropTargetEvent& event) {
+ DCHECK(drop_helper_.get() != NULL);
+ return drop_helper_->OnDrop(event.data(), event.location(),
+ event.source_operations());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget, public:
+
+// static
+void Widget::NotifyLocaleChanged() {
+ // http://crbug.com/102574
+ NOTIMPLEMENTED();
+}
+
+// static
+void Widget::CloseAllSecondaryWidgets() {
+ // http://crbug.com/102575
+ NOTIMPLEMENTED();
+}
+
+bool Widget::ConvertRect(const Widget* source,
+ const Widget* target,
+ gfx::Rect* rect) {
+ return false;
+}
+
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// internal::NativeWidgetPrivate, public:
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget(
+ internal::NativeWidgetDelegate* delegate) {
+ return new NativeWidgetAura(delegate);
+}
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView(
+ gfx::NativeView native_view) {
+ return reinterpret_cast<NativeWidgetAura*>(native_view->user_data());
+}
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
+ gfx::NativeWindow native_window) {
+ return reinterpret_cast<NativeWidgetAura*>(native_window->user_data());
+}
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
+ gfx::NativeView native_view) {
+ aura::Window* window = native_view;
+ NativeWidgetPrivate* top_level_native_widget = NULL;
+ while (window) {
+ NativeWidgetPrivate* native_widget = GetNativeWidgetForNativeView(window);
+ if (native_widget)
+ top_level_native_widget = native_widget;
+ window = window->parent();
+ }
+ return top_level_native_widget;
+}
+
+// static
+void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
+ Widget::Widgets* children) {
+ {
+ // Code expects widget for |native_view| to be added to |children|.
+ NativeWidgetAura* native_widget = static_cast<NativeWidgetAura*>(
+ GetNativeWidgetForNativeView(native_view));
+ if (native_widget->GetWidget())
+ children->insert(native_widget->GetWidget());
+ }
+
+ const aura::Window::Windows& child_windows = native_view->children();
+ for (aura::Window::Windows::const_iterator i = child_windows.begin();
+ i != child_windows.end(); ++i) {
+ NativeWidgetAura* native_widget =
+ static_cast<NativeWidgetAura*>(GetNativeWidgetForNativeView(*i));
+ if (native_widget)
+ children->insert(native_widget->GetWidget());
+ }
+}
+
+// static
+void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view,
+ gfx::NativeView new_parent) {
+ // http://crbug.com/102576
+ NOTIMPLEMENTED();
+}
+
+// static
+bool NativeWidgetPrivate::IsMouseButtonDown() {
+ return aura::Desktop::GetInstance()->IsMouseButtonDown();
+}
+
+} // namespace internal
+} // namespace views
diff --git a/views/widget/native_widget_aura.h b/views/widget/native_widget_aura.h
new file mode 100644
index 0000000..8f1383a
--- /dev/null
+++ b/views/widget/native_widget_aura.h
@@ -0,0 +1,186 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_NATIVE_WIDGET_AURA_H_
+#define VIEWS_WIDGET_NATIVE_WIDGET_AURA_H_
+#pragma once
+
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/aura/client/window_drag_drop_delegate.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/base/events.h"
+#include "views/views_export.h"
+#include "views/widget/native_widget_private.h"
+
+namespace aura {
+class Window;
+}
+namespace gfx {
+class Font;
+}
+
+namespace views {
+
+class DropHelper;
+class TooltipManagerViews;
+
+class VIEWS_EXPORT NativeWidgetAura : public internal::NativeWidgetPrivate,
+ public aura::WindowDelegate,
+ public aura::WindowDragDropDelegate {
+ public:
+ explicit NativeWidgetAura(internal::NativeWidgetDelegate* delegate);
+ virtual ~NativeWidgetAura();
+
+ // TODO(beng): Find a better place for this, and the similar method on
+ // NativeWidgetWin.
+ static gfx::Font GetWindowTitleFont();
+
+ // Overridden from internal::NativeWidgetPrivate:
+ virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE;
+ virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE;
+ virtual void UpdateFrameAfterFrameChange() OVERRIDE;
+ virtual bool ShouldUseNativeFrame() const OVERRIDE;
+ virtual void FrameTypeChanged() OVERRIDE;
+ virtual Widget* GetWidget() OVERRIDE;
+ virtual const Widget* GetWidget() const OVERRIDE;
+ virtual gfx::NativeView GetNativeView() const OVERRIDE;
+ virtual gfx::NativeWindow GetNativeWindow() const OVERRIDE;
+ virtual Widget* GetTopLevelWidget() OVERRIDE;
+ virtual const ui::Compositor* GetCompositor() const OVERRIDE;
+ virtual ui::Compositor* GetCompositor() OVERRIDE;
+ virtual void CalculateOffsetToAncestorWithLayer(
+ gfx::Point* offset,
+ ui::Layer** layer_parent) OVERRIDE;
+ virtual void ReorderLayers() OVERRIDE;
+ virtual void ViewRemoved(View* view) OVERRIDE;
+ virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE;
+ virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE;
+ virtual TooltipManager* GetTooltipManager() const OVERRIDE;
+ virtual bool IsScreenReaderActive() const OVERRIDE;
+ virtual void SendNativeAccessibilityEvent(
+ View* view,
+ ui::AccessibilityTypes::Event event_type) OVERRIDE;
+ virtual void SetMouseCapture() OVERRIDE;
+ virtual void ReleaseMouseCapture() OVERRIDE;
+ virtual bool HasMouseCapture() const OVERRIDE;
+ virtual InputMethod* CreateInputMethod() OVERRIDE;
+ virtual void CenterWindow(const gfx::Size& size) OVERRIDE;
+ virtual void GetWindowPlacement(
+ gfx::Rect* bounds,
+ ui::WindowShowState* maximized) const OVERRIDE;
+ virtual void SetWindowTitle(const string16& title) OVERRIDE;
+ virtual void SetWindowIcons(const SkBitmap& window_icon,
+ const SkBitmap& app_icon) OVERRIDE;
+ virtual void SetAccessibleName(const string16& name) OVERRIDE;
+ virtual void SetAccessibleRole(ui::AccessibilityTypes::Role role) OVERRIDE;
+ virtual void SetAccessibleState(ui::AccessibilityTypes::State state) OVERRIDE;
+ virtual void BecomeModal() OVERRIDE;
+ virtual gfx::Rect GetWindowScreenBounds() const OVERRIDE;
+ virtual gfx::Rect GetClientAreaScreenBounds() const OVERRIDE;
+ virtual gfx::Rect GetRestoredBounds() const OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual void SetSize(const gfx::Size& size) OVERRIDE;
+ virtual void MoveAbove(gfx::NativeView native_view) OVERRIDE;
+ virtual void MoveToTop() OVERRIDE;
+ virtual void SetShape(gfx::NativeRegion shape) OVERRIDE;
+ virtual void Close() OVERRIDE;
+ virtual void CloseNow() OVERRIDE;
+ virtual void EnableClose(bool enable) OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual void ShowMaximizedWithBounds(
+ const gfx::Rect& restored_bounds) OVERRIDE;
+ virtual void ShowWithWindowState(ui::WindowShowState state) OVERRIDE;
+ virtual bool IsVisible() const OVERRIDE;
+ virtual void Activate() OVERRIDE;
+ virtual void Deactivate() OVERRIDE;
+ virtual bool IsActive() const OVERRIDE;
+ virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE;
+ virtual void Maximize() OVERRIDE;
+ virtual void Minimize() OVERRIDE;
+ virtual bool IsMaximized() const OVERRIDE;
+ virtual bool IsMinimized() const OVERRIDE;
+ virtual void Restore() OVERRIDE;
+ virtual void SetFullscreen(bool fullscreen) OVERRIDE;
+ virtual bool IsFullscreen() const OVERRIDE;
+ virtual void SetOpacity(unsigned char opacity) OVERRIDE;
+ virtual void SetUseDragFrame(bool use_drag_frame) OVERRIDE;
+ virtual bool IsAccessibleWidget() const OVERRIDE;
+ virtual void RunShellDrag(View* view,
+ const ui::OSExchangeData& data,
+ int operation) OVERRIDE;
+ virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE;
+ virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE;
+ virtual void ClearNativeFocus() OVERRIDE;
+ virtual void FocusNativeView(gfx::NativeView native_view) OVERRIDE;
+ virtual bool ConvertPointFromAncestor(
+ const Widget* ancestor, gfx::Point* point) const OVERRIDE;
+ virtual gfx::Rect GetWorkAreaBoundsInScreen() const OVERRIDE;
+ virtual void SetInactiveRenderingDisabled(bool value) OVERRIDE;
+
+ // Overridden from views::InputMethodDelegate:
+ virtual void DispatchKeyEventPostIME(const KeyEvent& key) OVERRIDE;
+
+ // Overridden from aura::WindowDelegate:
+ virtual void OnBoundsChanging(gfx::Rect* new_bounds) OVERRIDE;
+ virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE;
+ virtual void OnFocus() OVERRIDE;
+ virtual void OnBlur() OVERRIDE;
+ virtual bool OnKeyEvent(aura::KeyEvent* event) OVERRIDE;
+ virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE;
+ virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE;
+ virtual bool OnMouseEvent(aura::MouseEvent* event) OVERRIDE;
+ virtual ui::TouchStatus OnTouchEvent(aura::TouchEvent* event) OVERRIDE;
+ virtual bool ShouldActivate(aura::Event* event) OVERRIDE;
+ virtual void OnActivated() OVERRIDE;
+ virtual void OnLostActive() OVERRIDE;
+ virtual void OnCaptureLost() OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual void OnWindowDestroying() OVERRIDE;
+ virtual void OnWindowDestroyed() OVERRIDE;
+ virtual void OnWindowVisibilityChanged(bool visible) OVERRIDE;
+
+ // Overridden from aura::WindowDragDropDelegate:
+ virtual bool CanDrop(const aura::DropTargetEvent& event) OVERRIDE;
+ virtual void OnDragEntered(const aura::DropTargetEvent& event) OVERRIDE;
+ virtual int OnDragUpdated(const aura::DropTargetEvent& event) OVERRIDE;
+ virtual void OnDragExited() OVERRIDE;
+ virtual int OnPerformDrop(const aura::DropTargetEvent& event) OVERRIDE;
+
+ protected:
+ internal::NativeWidgetDelegate* delegate() { return delegate_; }
+
+ private:
+ class DesktopObserverImpl;
+
+ internal::NativeWidgetDelegate* delegate_;
+
+ aura::Window* window_;
+
+ // See class documentation for Widget in widget.h for a note about ownership.
+ Widget::InitParams::Ownership ownership_;
+
+ // The following factory is used for calls to close the NativeWidgetAura
+ // instance.
+ base::WeakPtrFactory<NativeWidgetAura> close_widget_factory_;
+
+ // Can we be made active?
+ bool can_activate_;
+
+ gfx::NativeCursor cursor_;
+
+ scoped_ptr<TooltipManagerViews> tooltip_manager_;
+
+ scoped_ptr<DesktopObserverImpl> desktop_observer_;
+
+ scoped_ptr<DropHelper> drop_helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeWidgetAura);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_NATIVE_WIDGET_AURA_H_
diff --git a/views/widget/native_widget_delegate.h b/views/widget/native_widget_delegate.h
new file mode 100644
index 0000000..3732e97
--- /dev/null
+++ b/views/widget/native_widget_delegate.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_NATIVE_WIDGET_DELEGATE_H_
+#define VIEWS_WIDGET_NATIVE_WIDGET_DELEGATE_H_
+#pragma once
+
+#include "views/views_export.h"
+#include "ui/base/events.h"
+
+namespace gfx {
+class Canvas;
+class Point;
+class Size;
+}
+
+namespace views {
+class InputMethod;
+class KeyEvent;
+class MouseEvent;
+class TouchEvent;
+
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetDelegate
+//
+// An interface implemented by the object that handles events sent by a
+// NativeWidget implementation.
+//
+class VIEWS_EXPORT NativeWidgetDelegate {
+ public:
+ virtual ~NativeWidgetDelegate() {}
+
+ // Returns true if the window is modal.
+ virtual bool IsModal() const = 0;
+
+ // Returns true if the window is a dialog box.
+ virtual bool IsDialogBox() const = 0;
+
+ // Returns true if the window can be activated.
+ virtual bool CanActivate() const = 0;
+
+ virtual bool IsInactiveRenderingDisabled() const = 0;
+ virtual void EnableInactiveRendering() = 0;
+
+ // Called when the activation state of a window has changed.
+ virtual void OnNativeWidgetActivationChanged(bool active) = 0;
+
+ // Called when native focus moves from one native view to another.
+ virtual void OnNativeFocus(gfx::NativeView focused_view) = 0;
+ virtual void OnNativeBlur(gfx::NativeView focused_view) = 0;
+
+ // Called when the window is shown/hidden.
+ virtual void OnNativeWidgetVisibilityChanged(bool visible) = 0;
+
+ // Called when the native widget is created.
+ virtual void OnNativeWidgetCreated() = 0;
+
+ // Called just before the native widget is destroyed. This is the delegate's
+ // last chance to do anything with the native widget handle.
+ virtual void OnNativeWidgetDestroying() = 0;
+
+ // Called just after the native widget is destroyed.
+ virtual void OnNativeWidgetDestroyed() = 0;
+
+ // Returns the smallest size the window can be resized to by the user.
+ virtual gfx::Size GetMinimumSize() = 0;
+
+ // Called when the NativeWidget changed size to |new_size|.
+ virtual void OnNativeWidgetSizeChanged(const gfx::Size& new_size) = 0;
+
+ // Called when the user begins/ends to change the bounds of the window.
+ virtual void OnNativeWidgetBeginUserBoundsChange() = 0;
+ virtual void OnNativeWidgetEndUserBoundsChange() = 0;
+
+ // Returns true if the delegate has a FocusManager.
+ virtual bool HasFocusManager() const = 0;
+
+ // Paints the widget using acceleration. If the widget is not using
+ // accelerated painting this returns false and does nothing.
+ virtual bool OnNativeWidgetPaintAccelerated(
+ const gfx::Rect& dirty_region) = 0;
+
+ // Paints the rootview in the canvas. This will also refresh the compositor
+ // tree if necessary when accelerated painting is enabled.
+ virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) = 0;
+
+ // Returns the non-client component (see ui/base/hit_test.h) containing
+ // |point|, in client coordinates.
+ virtual int GetNonClientComponent(const gfx::Point& point) = 0;
+
+ // Mouse and key event handlers.
+ virtual bool OnKeyEvent(const KeyEvent& event) = 0;
+ virtual bool OnMouseEvent(const MouseEvent& event) = 0;
+ virtual void OnMouseCaptureLost() = 0;
+ virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) = 0;
+
+ // Runs the specified native command. Returns true if the command is handled.
+ virtual bool ExecuteCommand(int command_id) = 0;
+
+ // Returns the input method of the widget this delegate is associated with.
+ // Note that this does not use the top level widget, so may return NULL
+ // if the widget doesn't have input method.
+ virtual InputMethod* GetInputMethodDirect() = 0;
+
+ //
+ virtual Widget* AsWidget() = 0;
+ virtual const Widget* AsWidget() const = 0;
+};
+
+} // namespace internal
+} // namespace views
+
+#endif // VIEWS_WIDGET_NATIVE_WIDGET_DELEGATE_H_
diff --git a/views/widget/native_widget_gtk.cc b/views/widget/native_widget_gtk.cc
new file mode 100644
index 0000000..c7ef5e2
--- /dev/null
+++ b/views/widget/native_widget_gtk.cc
@@ -0,0 +1,2312 @@
+// Copyright (c) 2011 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/native_widget_gtk.h"
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/shape.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+
+#include <set>
+#include <vector>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_gtk.h"
+#include "ui/base/gtk/g_object_destructor_filo.h"
+#include "ui/base/gtk/gtk_signal_registrar.h"
+#include "ui/base/gtk/gtk_windowing.h"
+#include "ui/base/gtk/scoped_handle_gtk.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/gfx/canvas_skia_paint.h"
+#include "ui/gfx/compositor/compositor.h"
+#include "ui/gfx/gtk_util.h"
+#include "ui/gfx/path.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/bubble/bubble_delegate.h"
+#include "ui/views/focus/view_storage.h"
+#include "ui/views/ime/input_method_gtk.h"
+#include "views/controls/textfield/native_textfield_views.h"
+#include "views/views_delegate.h"
+#include "views/widget/drop_target_gtk.h"
+#include "views/widget/gtk_views_fixed.h"
+#include "views/widget/gtk_views_window.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget_delegate.h"
+
+#if defined(TOUCH_UI)
+#include "ui/base/touch/touch_factory.h"
+#include "views/widget/tooltip_manager_views.h"
+#else
+#include "views/widget/tooltip_manager_gtk.h"
+#endif
+
+#if defined(HAVE_IBUS)
+#include "ui/views/ime/input_method_ibus.h"
+#endif
+
+using ui::OSExchangeData;
+using ui::OSExchangeDataProviderGtk;
+using ui::ActiveWindowWatcherX;
+
+namespace views {
+
+namespace {
+
+// Links the GtkWidget to its NativeWidget.
+const char* const kNativeWidgetKey = "__VIEWS_NATIVE_WIDGET__";
+
+// A g_object data key to associate a CompositePainter object to a GtkWidget.
+const char* kCompositePainterKey = "__VIEWS_COMPOSITE_PAINTER__";
+
+// A g_object data key to associate the flag whether or not the widget
+// is composited to a GtkWidget. gtk_widget_is_composited simply tells
+// if x11 supports composition and cannot be used to tell if given widget
+// is composited.
+const char* kCompositeEnabledKey = "__VIEWS_COMPOSITE_ENABLED__";
+
+// A g_object data key to associate the expose handler id that is
+// used to remove FREEZE_UPDATE property on the window.
+const char* kExposeHandlerIdKey = "__VIEWS_EXPOSE_HANDLER_ID__";
+
+// CompositePainter draws a composited child widgets image into its
+// drawing area. This object is created at most once for a widget and kept
+// until the widget is destroyed.
+class CompositePainter {
+ public:
+ explicit CompositePainter(GtkWidget* parent)
+ : parent_object_(G_OBJECT(parent)) {
+ handler_id_ = g_signal_connect_after(
+ parent_object_, "expose_event", G_CALLBACK(OnCompositePaint), NULL);
+ }
+
+ static void AddCompositePainter(GtkWidget* widget) {
+ CompositePainter* painter = static_cast<CompositePainter*>(
+ g_object_get_data(G_OBJECT(widget), kCompositePainterKey));
+ if (!painter) {
+ g_object_set_data(G_OBJECT(widget), kCompositePainterKey,
+ new CompositePainter(widget));
+ g_signal_connect(widget, "destroy",
+ G_CALLBACK(&DestroyPainter), NULL);
+ }
+ }
+
+ // Set the composition flag.
+ static void SetComposited(GtkWidget* widget) {
+ g_object_set_data(G_OBJECT(widget), kCompositeEnabledKey,
+ const_cast<char*>(""));
+ }
+
+ // Returns true if the |widget| is composited and ready to be drawn.
+ static bool IsComposited(GtkWidget* widget) {
+ return g_object_get_data(G_OBJECT(widget), kCompositeEnabledKey) != NULL;
+ }
+
+ private:
+ virtual ~CompositePainter() {}
+
+ // Composes a image from one child.
+ static void CompositeChildWidget(GtkWidget* child, gpointer data) {
+ GdkEventExpose* event = static_cast<GdkEventExpose*>(data);
+ GtkWidget* parent = gtk_widget_get_parent(child);
+ DCHECK(parent);
+ if (IsComposited(child)) {
+ cairo_t* cr = gdk_cairo_create(parent->window);
+ gdk_cairo_set_source_pixmap(cr, child->window,
+ child->allocation.x,
+ child->allocation.y);
+ GdkRegion* region = gdk_region_rectangle(&child->allocation);
+ gdk_region_intersect(region, event->region);
+ gdk_cairo_region(cr, region);
+ cairo_clip(cr);
+ cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ }
+ }
+
+ // Expose-event handler that compose & draws children's image into
+ // the |parent|'s drawing area.
+ static gboolean OnCompositePaint(GtkWidget* parent, GdkEventExpose* event) {
+ gtk_container_foreach(GTK_CONTAINER(parent),
+ CompositeChildWidget,
+ event);
+ return false;
+ }
+
+ static void DestroyPainter(GtkWidget* object) {
+ CompositePainter* painter = reinterpret_cast<CompositePainter*>(
+ g_object_get_data(G_OBJECT(object), kCompositePainterKey));
+ DCHECK(painter);
+ delete painter;
+ }
+
+ GObject* parent_object_;
+ gulong handler_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositePainter);
+};
+
+void EnumerateChildWidgetsForNativeWidgets(GtkWidget* child_widget,
+ gpointer param) {
+ // Walk child widgets, if necessary.
+ if (GTK_IS_CONTAINER(child_widget)) {
+ gtk_container_foreach(GTK_CONTAINER(child_widget),
+ EnumerateChildWidgetsForNativeWidgets,
+ param);
+ }
+
+ Widget* widget = Widget::GetWidgetForNativeView(child_widget);
+ if (widget) {
+ Widget::Widgets* widgets = reinterpret_cast<Widget::Widgets*>(param);
+ widgets->insert(widget);
+ }
+}
+
+void RemoveExposeHandlerIfExists(GtkWidget* widget) {
+ gulong id = reinterpret_cast<gulong>(g_object_get_data(G_OBJECT(widget),
+ kExposeHandlerIdKey));
+ if (id) {
+ g_signal_handler_disconnect(G_OBJECT(widget), id);
+ g_object_set_data(G_OBJECT(widget), kExposeHandlerIdKey, 0);
+ }
+}
+
+GtkWindowType WindowTypeToGtkWindowType(Widget::InitParams::Type type) {
+ switch (type) {
+ case Widget::InitParams::TYPE_BUBBLE:
+ case Widget::InitParams::TYPE_WINDOW:
+ case Widget::InitParams::TYPE_WINDOW_FRAMELESS:
+ return GTK_WINDOW_TOPLEVEL;
+ default:
+ return GTK_WINDOW_POPUP;
+ }
+ NOTREACHED();
+ return GTK_WINDOW_TOPLEVEL;
+}
+
+// Converts a Windows-style hit test result code into a GDK window edge.
+GdkWindowEdge HitTestCodeToGDKWindowEdge(int hittest_code) {
+ switch (hittest_code) {
+ case HTBOTTOM:
+ return GDK_WINDOW_EDGE_SOUTH;
+ case HTBOTTOMLEFT:
+ return GDK_WINDOW_EDGE_SOUTH_WEST;
+ case HTBOTTOMRIGHT:
+ case HTGROWBOX:
+ return GDK_WINDOW_EDGE_SOUTH_EAST;
+ case HTLEFT:
+ return GDK_WINDOW_EDGE_WEST;
+ case HTRIGHT:
+ return GDK_WINDOW_EDGE_EAST;
+ case HTTOP:
+ return GDK_WINDOW_EDGE_NORTH;
+ case HTTOPLEFT:
+ return GDK_WINDOW_EDGE_NORTH_WEST;
+ case HTTOPRIGHT:
+ return GDK_WINDOW_EDGE_NORTH_EAST;
+ default:
+ NOTREACHED();
+ break;
+ }
+ // Default to something defaultish.
+ return HitTestCodeToGDKWindowEdge(HTGROWBOX);
+}
+
+// Converts a Windows-style hit test result code into a GDK cursor type.
+GdkCursorType HitTestCodeToGdkCursorType(int hittest_code) {
+ switch (hittest_code) {
+ case HTBOTTOM:
+ return GDK_BOTTOM_SIDE;
+ case HTBOTTOMLEFT:
+ return GDK_BOTTOM_LEFT_CORNER;
+ case HTBOTTOMRIGHT:
+ case HTGROWBOX:
+ return GDK_BOTTOM_RIGHT_CORNER;
+ case HTLEFT:
+ return GDK_LEFT_SIDE;
+ case HTRIGHT:
+ return GDK_RIGHT_SIDE;
+ case HTTOP:
+ return GDK_TOP_SIDE;
+ case HTTOPLEFT:
+ return GDK_TOP_LEFT_CORNER;
+ case HTTOPRIGHT:
+ return GDK_TOP_RIGHT_CORNER;
+ default:
+ break;
+ }
+ // Default to something defaultish.
+ return GDK_LEFT_PTR;
+}
+
+} // namespace
+
+// During drag and drop GTK sends a drag-leave during a drop. This means we
+// have no way to tell the difference between a normal drag leave and a drop.
+// To work around that we listen for DROP_START, then ignore the subsequent
+// drag-leave that GTK generates.
+class NativeWidgetGtk::DropObserver : public MessageLoopForUI::Observer {
+ public:
+ DropObserver() {}
+
+ static DropObserver* GetInstance() {
+ return Singleton<DropObserver>::get();
+ }
+#if defined(TOUCH_UI)
+ virtual base::EventStatus WillProcessEvent(
+ const base::NativeEvent& event) OVERRIDE {
+ return base::EVENT_CONTINUE;
+ }
+
+ virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
+ }
+#else
+ virtual void WillProcessEvent(GdkEvent* event) {
+ if (event->type == GDK_DROP_START) {
+ NativeWidgetGtk* widget = GetNativeWidgetGtkForEvent(event);
+ if (widget)
+ widget->ignore_drag_leave_ = true;
+ }
+ }
+
+ virtual void DidProcessEvent(GdkEvent* event) {
+ }
+#endif
+
+ private:
+ NativeWidgetGtk* GetNativeWidgetGtkForEvent(GdkEvent* event) {
+ GtkWidget* gtk_widget = gtk_get_event_widget(event);
+ if (!gtk_widget)
+ return NULL;
+
+ return static_cast<NativeWidgetGtk*>(
+ internal::NativeWidgetPrivate::GetNativeWidgetForNativeView(
+ gtk_widget));
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(DropObserver);
+};
+
+// Returns the position of a widget on screen.
+static void GetWidgetPositionOnScreen(GtkWidget* widget, int* x, int *y) {
+ // First get the root window.
+ GtkWidget* root = widget;
+ while (root && !GTK_IS_WINDOW(root)) {
+ root = gtk_widget_get_parent(root);
+ }
+ if (!root) {
+ // If root is null we're not parented. Return 0x0 and assume the caller will
+ // query again when we're parented.
+ *x = *y = 0;
+ return;
+ }
+ // Translate the coordinate from widget to root window.
+ gtk_widget_translate_coordinates(widget, root, 0, 0, x, y);
+ // Then adjust the position with the position of the root window.
+ int window_x, window_y;
+ gtk_window_get_position(GTK_WINDOW(root), &window_x, &window_y);
+ *x += window_x;
+ *y += window_y;
+}
+
+// "expose-event" handler of drag icon widget that renders drag image pixbuf.
+static gboolean DragIconWidgetPaint(GtkWidget* widget,
+ GdkEventExpose* event,
+ gpointer data) {
+ GdkPixbuf* pixbuf = reinterpret_cast<GdkPixbuf*>(data);
+
+ cairo_t* cr = gdk_cairo_create(widget->window);
+
+ gdk_cairo_region(cr, event->region);
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+ gdk_cairo_set_source_pixbuf(cr, pixbuf, 0.0, 0.0);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+ return true;
+}
+
+// Creates a drag icon widget that draws drag_image.
+static GtkWidget* CreateDragIconWidget(GdkPixbuf* drag_image) {
+ GdkColormap* rgba_colormap =
+ gdk_screen_get_rgba_colormap(gdk_screen_get_default());
+ if (!rgba_colormap)
+ return NULL;
+
+ GtkWidget* drag_widget = gtk_window_new(GTK_WINDOW_POPUP);
+
+ gtk_widget_set_colormap(drag_widget, rgba_colormap);
+ gtk_widget_set_app_paintable(drag_widget, true);
+ gtk_widget_set_size_request(drag_widget,
+ gdk_pixbuf_get_width(drag_image),
+ gdk_pixbuf_get_height(drag_image));
+
+ g_signal_connect(G_OBJECT(drag_widget), "expose-event",
+ G_CALLBACK(&DragIconWidgetPaint), drag_image);
+ return drag_widget;
+}
+
+// static
+GtkWidget* NativeWidgetGtk::null_parent_ = NULL;
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetGtk, public:
+
+NativeWidgetGtk::NativeWidgetGtk(internal::NativeWidgetDelegate* delegate)
+ : delegate_(delegate),
+ widget_(NULL),
+ window_contents_(NULL),
+ child_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)),
+ ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET),
+ transparent_(false),
+ ignore_events_(false),
+ ignore_drag_leave_(false),
+ opacity_(255),
+ drag_data_(NULL),
+ window_state_(GDK_WINDOW_STATE_WITHDRAWN),
+ is_active_(false),
+ transient_to_parent_(false),
+ got_initial_focus_in_(false),
+ has_focus_(false),
+ always_on_top_(false),
+ is_double_buffered_(false),
+ should_handle_menu_key_release_(false),
+ dragged_view_(NULL),
+ painted_(false),
+ has_pointer_grab_(false),
+ has_keyboard_grab_(false),
+ grab_notify_signal_id_(0),
+ is_menu_(false),
+ signal_registrar_(new ui::GtkSignalRegistrar) {
+#if defined(TOUCH_UI)
+ // Make sure the touch factory is initialized so that it can setup XInput2 for
+ // the widget.
+ ui::TouchFactory::GetInstance();
+#endif
+ static bool installed_message_loop_observer = false;
+ if (!installed_message_loop_observer) {
+ installed_message_loop_observer = true;
+ MessageLoopForUI* loop = MessageLoopForUI::current();
+ if (loop)
+ loop->AddObserver(DropObserver::GetInstance());
+ }
+}
+
+NativeWidgetGtk::~NativeWidgetGtk() {
+ // We need to delete the input method before calling DestroyRootView(),
+ // because it'll set focus_manager_ to NULL.
+ if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) {
+ DCHECK(widget_ == NULL);
+ delete delegate_;
+ } else {
+ // Disconnect from GObjectDestructorFILO because we're
+ // deleting the NativeWidgetGtk.
+ bool has_widget = !!widget_;
+ if (has_widget)
+ ui::GObjectDestructorFILO::GetInstance()->Disconnect(
+ G_OBJECT(widget_), &OnDestroyedThunk, this);
+ CloseNow();
+ // Call OnNativeWidgetDestroyed because we're not calling
+ // OnDestroyedThunk
+ if (has_widget)
+ delegate_->OnNativeWidgetDestroyed();
+ }
+}
+
+GtkWindow* NativeWidgetGtk::GetTransientParent() const {
+ return (!child_ && widget_) ?
+ gtk_window_get_transient_for(GTK_WINDOW(widget_)) : NULL;
+}
+
+bool NativeWidgetGtk::MakeTransparent() {
+ // Transparency can only be enabled only if we haven't realized the widget.
+ DCHECK(!widget_);
+
+ if (!gdk_screen_is_composited(gdk_screen_get_default())) {
+ // Transparency is only supported for compositing window managers.
+ // NOTE: there's a race during ChromeOS startup such that X might think
+ // compositing isn't supported. We ignore it if the wm says compositing
+ // isn't supported.
+ DLOG(WARNING) << "compositing not supported; allowing anyway";
+ }
+
+ if (!gdk_screen_get_rgba_colormap(gdk_screen_get_default())) {
+ // We need rgba to make the window transparent.
+ return false;
+ }
+
+ transparent_ = true;
+ return true;
+}
+
+void NativeWidgetGtk::EnableDoubleBuffer(bool enabled) {
+ is_double_buffered_ = enabled;
+ if (window_contents_) {
+ if (is_double_buffered_)
+ GTK_WIDGET_SET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED);
+ else
+ GTK_WIDGET_UNSET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED);
+ }
+}
+
+void NativeWidgetGtk::AddChild(GtkWidget* child) {
+ gtk_container_add(GTK_CONTAINER(window_contents_), child);
+}
+
+void NativeWidgetGtk::RemoveChild(GtkWidget* child) {
+ // We can be called after the contents widget has been destroyed, e.g. any
+ // NativeViewHost not removed from the view hierarchy before the window is
+ // closed.
+ if (GTK_IS_CONTAINER(window_contents_)) {
+ gtk_container_remove(GTK_CONTAINER(window_contents_), child);
+ gtk_views_fixed_set_widget_size(child, 0, 0);
+ }
+}
+
+void NativeWidgetGtk::ReparentChild(GtkWidget* child) {
+ gtk_widget_reparent(child, window_contents_);
+}
+
+void NativeWidgetGtk::PositionChild(GtkWidget* child, int x, int y, int w,
+ int h) {
+ gtk_views_fixed_set_widget_size(child, w, h);
+ gtk_fixed_move(GTK_FIXED(window_contents_), child, x, y);
+}
+
+void NativeWidgetGtk::DoDrag(const OSExchangeData& data, int operation) {
+ const OSExchangeDataProviderGtk& data_provider =
+ static_cast<const OSExchangeDataProviderGtk&>(data.provider());
+ GtkTargetList* targets = data_provider.GetTargetList();
+ GdkEvent* current_event = gtk_get_current_event();
+ const OSExchangeDataProviderGtk& provider(
+ static_cast<const OSExchangeDataProviderGtk&>(data.provider()));
+
+ GdkDragContext* context = gtk_drag_begin(
+ window_contents_,
+ targets,
+ static_cast<GdkDragAction>(
+ ui::DragDropTypes::DragOperationToGdkDragAction(operation)),
+ 1,
+ current_event);
+
+ GtkWidget* drag_icon_widget = NULL;
+
+ // Set the drag image if one was supplied.
+ if (provider.drag_image()) {
+ drag_icon_widget = CreateDragIconWidget(provider.drag_image());
+ if (drag_icon_widget) {
+ // Use a widget as the drag icon when compositing is enabled for proper
+ // transparency handling.
+ g_object_ref(provider.drag_image());
+ gtk_drag_set_icon_widget(context,
+ drag_icon_widget,
+ provider.cursor_offset().x(),
+ provider.cursor_offset().y());
+ } else {
+ gtk_drag_set_icon_pixbuf(context,
+ provider.drag_image(),
+ provider.cursor_offset().x(),
+ provider.cursor_offset().y());
+ }
+ }
+
+ if (current_event)
+ gdk_event_free(current_event);
+ gtk_target_list_unref(targets);
+
+ drag_data_ = &data_provider;
+
+ // Block the caller until drag is done by running a nested message loop.
+ MessageLoopForUI::current()->RunWithDispatcher(NULL);
+
+ drag_data_ = NULL;
+
+ if (drag_icon_widget) {
+ gtk_widget_destroy(drag_icon_widget);
+ g_object_unref(provider.drag_image());
+ }
+}
+
+void NativeWidgetGtk::OnActiveChanged() {
+ delegate_->OnNativeWidgetActivationChanged(IsActive());
+}
+
+void NativeWidgetGtk::ResetDropTarget() {
+ ignore_drag_leave_ = false;
+ drop_target_.reset(NULL);
+}
+
+void NativeWidgetGtk::GetRequestedSize(gfx::Size* out) const {
+ int width, height;
+ if (GTK_IS_VIEWS_FIXED(widget_) &&
+ gtk_views_fixed_get_widget_size(GetNativeView(), &width, &height)) {
+ out->SetSize(width, height);
+ } else {
+ GtkRequisition requisition;
+ gtk_widget_get_child_requisition(GetNativeView(), &requisition);
+ out->SetSize(requisition.width, requisition.height);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetGtk, ActiveWindowWatcherX::Observer implementation:
+
+void NativeWidgetGtk::ActiveWindowChanged(GdkWindow* active_window) {
+ if (!GetNativeView())
+ return;
+
+ bool was_active = IsActive();
+ is_active_ = (active_window == GTK_WIDGET(GetNativeView())->window);
+ if (!is_active_ && active_window && !child_) {
+ // We're not active, but the force the window to be rendered as active if
+ // a child window is transient to us.
+ gpointer data = NULL;
+ gdk_window_get_user_data(active_window, &data);
+ GtkWidget* widget = reinterpret_cast<GtkWidget*>(data);
+ is_active_ =
+ (widget && GTK_IS_WINDOW(widget) &&
+ gtk_window_get_transient_for(GTK_WINDOW(widget)) == GTK_WINDOW(
+ widget_));
+ }
+ if (was_active != IsActive()) {
+ OnActiveChanged();
+ GetWidget()->GetRootView()->SchedulePaint();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetGtk implementation:
+
+bool NativeWidgetGtk::HandleKeyboardEvent(const KeyEvent& key) {
+ if (!GetWidget()->GetFocusManager())
+ return false;
+
+ const int key_code = key.key_code();
+ bool handled = false;
+
+ // Always reset |should_handle_menu_key_release_| unless we are handling a
+ // VKEY_MENU key release event. It ensures that VKEY_MENU accelerator can only
+ // be activated when handling a VKEY_MENU key release event which is preceded
+ // by an un-handled VKEY_MENU key press event.
+ if (key_code != ui::VKEY_MENU || key.type() != ui::ET_KEY_RELEASED)
+ should_handle_menu_key_release_ = false;
+
+ if (key.type() == ui::ET_KEY_PRESSED) {
+ // VKEY_MENU is triggered by key release event.
+ // FocusManager::OnKeyEvent() returns false when the key has been consumed.
+ if (key_code != ui::VKEY_MENU)
+ handled = !GetWidget()->GetFocusManager()->OnKeyEvent(key);
+ else
+ should_handle_menu_key_release_ = true;
+ } else if (key_code == ui::VKEY_MENU && should_handle_menu_key_release_ &&
+ (key.flags() & ~ui::EF_ALT_DOWN) == 0) {
+ // Trigger VKEY_MENU when only this key is pressed and released, and both
+ // press and release events are not handled by others.
+ ui::Accelerator accelerator(ui::VKEY_MENU, false, false, false);
+ handled = GetWidget()->GetFocusManager()->ProcessAccelerator(accelerator);
+ }
+
+ return handled;
+}
+
+bool NativeWidgetGtk::SuppressFreezeUpdates() {
+ if (!painted_) {
+ painted_ = true;
+ return true;
+ }
+ return false;
+}
+
+// static
+void NativeWidgetGtk::UpdateFreezeUpdatesProperty(GtkWindow* window,
+ bool enable) {
+ if (!GTK_WIDGET_REALIZED(GTK_WIDGET(window)))
+ gtk_widget_realize(GTK_WIDGET(window));
+ GdkWindow* gdk_window = GTK_WIDGET(window)->window;
+
+ static GdkAtom freeze_atom_ =
+ gdk_atom_intern("_CHROME_FREEZE_UPDATES", FALSE);
+ if (enable) {
+ VLOG(1) << "setting FREEZE UPDATES property. xid=" <<
+ GDK_WINDOW_XID(gdk_window);
+ int32 val = 1;
+ gdk_property_change(gdk_window,
+ freeze_atom_,
+ freeze_atom_,
+ 32,
+ GDK_PROP_MODE_REPLACE,
+ reinterpret_cast<const guchar*>(&val),
+ 1);
+ } else {
+ VLOG(1) << "deleting FREEZE UPDATES property. xid=" <<
+ GDK_WINDOW_XID(gdk_window);
+ gdk_property_delete(gdk_window, freeze_atom_);
+ }
+}
+
+// static
+void NativeWidgetGtk::RegisterChildExposeHandler(GtkWidget* child) {
+ RemoveExposeHandlerIfExists(child);
+ gulong id = g_signal_connect_after(child, "expose-event",
+ G_CALLBACK(&ChildExposeHandler), NULL);
+ g_object_set_data(G_OBJECT(child), kExposeHandlerIdKey,
+ reinterpret_cast<void*>(id));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetGtk, NativeWidget implementation:
+
+void NativeWidgetGtk::InitNativeWidget(const Widget::InitParams& params) {
+ SetInitParams(params);
+
+ Widget::InitParams modified_params = params;
+ if (params.parent_widget) {
+ NativeWidgetGtk* parent_gtk =
+ static_cast<NativeWidgetGtk*>(params.parent_widget->native_widget());
+ modified_params.parent = child_ ? parent_gtk->window_contents()
+ : params.parent_widget->GetNativeView();
+ }
+
+ if (!child_)
+ ActiveWindowWatcherX::AddObserver(this);
+
+ // Make container here.
+ CreateGtkWidget(modified_params);
+
+ if (params.type == Widget::InitParams::TYPE_MENU) {
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(GetNativeView()), TRUE);
+ gtk_window_set_type_hint(GTK_WINDOW(GetNativeView()),
+ GDK_WINDOW_TYPE_HINT_MENU);
+ }
+
+ if (View::get_use_acceleration_when_possible()) {
+ if (ui::Compositor::compositor_factory()) {
+ compositor_ = (*ui::Compositor::compositor_factory())(this);
+ } else {
+ gint width, height;
+ gdk_drawable_get_size(window_contents_->window, &width, &height);
+ compositor_ = ui::Compositor::Create(this,
+ GDK_WINDOW_XID(window_contents_->window),
+ gfx::Size(width, height));
+ }
+ if (compositor_.get()) {
+ View* root_view = delegate_->AsWidget()->GetRootView();
+ root_view->SetPaintToLayer(true);
+ compositor_->SetRootLayer(root_view->layer());
+ root_view->SetFillsBoundsOpaquely(!transparent_);
+ }
+ }
+
+ delegate_->OnNativeWidgetCreated();
+
+ if (opacity_ != 255)
+ SetOpacity(opacity_);
+
+ // Make sure we receive our motion events.
+
+ // In general we register most events on the parent of all widgets. At a
+ // minimum we need painting to happen on the parent (otherwise painting
+ // doesn't work at all), and similarly we need mouse release events on the
+ // parent as windows don't get mouse releases.
+ gtk_widget_add_events(window_contents_,
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK);
+
+ signal_registrar_->ConnectAfter(G_OBJECT(window_contents_), "size_request",
+ G_CALLBACK(&OnSizeRequestThunk), this);
+ signal_registrar_->ConnectAfter(G_OBJECT(window_contents_), "size_allocate",
+ G_CALLBACK(&OnSizeAllocateThunk), this);
+ gtk_widget_set_app_paintable(window_contents_, true);
+ signal_registrar_->Connect(window_contents_, "expose_event",
+ G_CALLBACK(&OnPaintThunk), this);
+ signal_registrar_->Connect(window_contents_, "enter_notify_event",
+ G_CALLBACK(&OnEnterNotifyThunk), this);
+ signal_registrar_->Connect(window_contents_, "leave_notify_event",
+ G_CALLBACK(&OnLeaveNotifyThunk), this);
+ signal_registrar_->Connect(window_contents_, "motion_notify_event",
+ G_CALLBACK(&OnMotionNotifyThunk), this);
+ signal_registrar_->Connect(window_contents_, "button_press_event",
+ G_CALLBACK(&OnButtonPressThunk), this);
+ signal_registrar_->Connect(window_contents_, "button_release_event",
+ G_CALLBACK(&OnButtonReleaseThunk), this);
+ signal_registrar_->Connect(window_contents_, "grab_broken_event",
+ G_CALLBACK(&OnGrabBrokeEventThunk), this);
+ signal_registrar_->Connect(window_contents_, "scroll_event",
+ G_CALLBACK(&OnScrollThunk), this);
+ signal_registrar_->Connect(window_contents_, "visibility_notify_event",
+ G_CALLBACK(&OnVisibilityNotifyThunk), this);
+
+ // In order to receive notification when the window is no longer the front
+ // window, we need to install these on the widget.
+ // NOTE: this doesn't work with focus follows mouse.
+ signal_registrar_->Connect(widget_, "focus_in_event",
+ G_CALLBACK(&OnFocusInThunk), this);
+ signal_registrar_->Connect(widget_, "focus_out_event",
+ G_CALLBACK(&OnFocusOutThunk), this);
+ signal_registrar_->Connect(widget_, "destroy",
+ G_CALLBACK(&OnDestroyThunk), this);
+ signal_registrar_->Connect(widget_, "show",
+ G_CALLBACK(&OnShowThunk), this);
+ signal_registrar_->Connect(widget_, "map",
+ G_CALLBACK(&OnMapThunk), this);
+ signal_registrar_->Connect(widget_, "hide",
+ G_CALLBACK(&OnHideThunk), this);
+ signal_registrar_->Connect(widget_, "configure-event",
+ G_CALLBACK(&OnConfigureEventThunk), this);
+
+ // Views/FocusManager (re)sets the focus to the root window,
+ // so we need to connect signal handlers to the gtk window.
+ // See views::Views::Focus and views::FocusManager::ClearNativeFocus
+ // for more details.
+ signal_registrar_->Connect(widget_, "key_press_event",
+ G_CALLBACK(&OnEventKeyThunk), this);
+ signal_registrar_->Connect(widget_, "key_release_event",
+ G_CALLBACK(&OnEventKeyThunk), this);
+
+ // Drag and drop.
+ gtk_drag_dest_set(window_contents_, static_cast<GtkDestDefaults>(0),
+ NULL, 0, GDK_ACTION_COPY);
+ signal_registrar_->Connect(window_contents_, "drag_motion",
+ G_CALLBACK(&OnDragMotionThunk), this);
+ signal_registrar_->Connect(window_contents_, "drag_data_received",
+ G_CALLBACK(&OnDragDataReceivedThunk), this);
+ signal_registrar_->Connect(window_contents_, "drag_drop",
+ G_CALLBACK(&OnDragDropThunk), this);
+ signal_registrar_->Connect(window_contents_, "drag_leave",
+ G_CALLBACK(&OnDragLeaveThunk), this);
+ signal_registrar_->Connect(window_contents_, "drag_data_get",
+ G_CALLBACK(&OnDragDataGetThunk), this);
+ signal_registrar_->Connect(window_contents_, "drag_end",
+ G_CALLBACK(&OnDragEndThunk), this);
+ signal_registrar_->Connect(window_contents_, "drag_failed",
+ G_CALLBACK(&OnDragFailedThunk), this);
+ signal_registrar_->Connect(G_OBJECT(widget_), "window-state-event",
+ G_CALLBACK(&OnWindowStateEventThunk), this);
+
+ ui::GObjectDestructorFILO::GetInstance()->Connect(
+ G_OBJECT(widget_), &OnDestroyedThunk, this);
+
+#if defined(TOUCH_UI)
+ if (params.type != Widget::InitParams::TYPE_TOOLTIP) {
+ views::TooltipManagerViews* manager = new views::TooltipManagerViews(
+ static_cast<internal::RootView*>(GetWidget()->GetRootView()));
+ tooltip_manager_.reset(manager);
+ }
+#else
+ tooltip_manager_.reset(new TooltipManagerGtk(this));
+#endif
+
+ // Register for tooltips.
+ g_object_set(G_OBJECT(window_contents_), "has-tooltip", TRUE, NULL);
+ signal_registrar_->Connect(window_contents_, "query_tooltip",
+ G_CALLBACK(&OnQueryTooltipThunk), this);
+
+ if (child_) {
+ if (modified_params.parent)
+ SetBounds(params.bounds);
+ } else {
+ gtk_widget_add_events(widget_,
+ GDK_STRUCTURE_MASK);
+ if (params.bounds.width() > 0 && params.bounds.height() > 0)
+ gtk_window_resize(GTK_WINDOW(widget_), params.bounds.width(),
+ params.bounds.height());
+ gtk_window_move(GTK_WINDOW(widget_), params.bounds.x(), params.bounds.y());
+ }
+}
+
+NonClientFrameView* NativeWidgetGtk::CreateNonClientFrameView() {
+ return NULL;
+}
+
+void NativeWidgetGtk::UpdateFrameAfterFrameChange() {
+ // We currently don't support different frame types on Gtk, so we don't
+ // need to implement this.
+ NOTIMPLEMENTED();
+}
+
+bool NativeWidgetGtk::ShouldUseNativeFrame() const {
+ return false;
+}
+
+void NativeWidgetGtk::FrameTypeChanged() {
+ // This is called when the Theme has changed, so forward the event to the root
+ // widget.
+ GetWidget()->ThemeChanged();
+ GetWidget()->GetRootView()->SchedulePaint();
+}
+
+Widget* NativeWidgetGtk::GetWidget() {
+ return delegate_->AsWidget();
+}
+
+const Widget* NativeWidgetGtk::GetWidget() const {
+ return delegate_->AsWidget();
+}
+
+gfx::NativeView NativeWidgetGtk::GetNativeView() const {
+ return widget_;
+}
+
+gfx::NativeWindow NativeWidgetGtk::GetNativeWindow() const {
+ return child_ ? NULL : GTK_WINDOW(widget_);
+}
+
+Widget* NativeWidgetGtk::GetTopLevelWidget() {
+ NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
+ return native_widget ? native_widget->GetWidget() : NULL;
+}
+
+const ui::Compositor* NativeWidgetGtk::GetCompositor() const {
+ return compositor_.get();
+}
+
+ui::Compositor* NativeWidgetGtk::GetCompositor() {
+ return compositor_.get();
+}
+
+void NativeWidgetGtk::CalculateOffsetToAncestorWithLayer(
+ gfx::Point* offset,
+ ui::Layer** layer_parent) {
+}
+
+void NativeWidgetGtk::ReorderLayers() {
+}
+
+void NativeWidgetGtk::ViewRemoved(View* view) {
+ if (drop_target_.get())
+ drop_target_->ResetTargetViewIfEquals(view);
+}
+
+void NativeWidgetGtk::SetNativeWindowProperty(const char* name, void* value) {
+ g_object_set_data(G_OBJECT(widget_), name, value);
+}
+
+void* NativeWidgetGtk::GetNativeWindowProperty(const char* name) const {
+ return g_object_get_data(G_OBJECT(widget_), name);
+}
+
+TooltipManager* NativeWidgetGtk::GetTooltipManager() const {
+ return tooltip_manager_.get();
+}
+
+bool NativeWidgetGtk::IsScreenReaderActive() const {
+ return false;
+}
+
+void NativeWidgetGtk::SendNativeAccessibilityEvent(
+ View* view,
+ ui::AccessibilityTypes::Event event_type) {
+ // In the future if we add native GTK accessibility support, the
+ // notification should be sent here.
+}
+
+void NativeWidgetGtk::SetMouseCapture() {
+ DCHECK(!HasMouseCapture());
+
+ // Release the current grab.
+ GtkWidget* current_grab_window = gtk_grab_get_current();
+ if (current_grab_window)
+ gtk_grab_remove(current_grab_window);
+
+ if (is_menu_ && gdk_pointer_is_grabbed())
+ gdk_pointer_ungrab(GDK_CURRENT_TIME);
+
+ // Make sure all app mouse/keyboard events are targeted at us only.
+ gtk_grab_add(window_contents_);
+ if (gtk_grab_get_current() == window_contents_ && !grab_notify_signal_id_) {
+ // "grab_notify" is sent any time the grab changes. We only care about grab
+ // changes when we have done a grab.
+ grab_notify_signal_id_ = g_signal_connect(
+ window_contents_, "grab_notify", G_CALLBACK(&OnGrabNotifyThunk), this);
+ }
+
+ if (is_menu_) {
+ // For menus we do a pointer grab too. This ensures we get mouse events from
+ // other apps. In theory we should do this for all widget types, but doing
+ // so leads to gdk_pointer_grab randomly returning GDK_GRAB_ALREADY_GRABBED.
+ GdkGrabStatus pointer_grab_status =
+ gdk_pointer_grab(window_contents()->window, FALSE,
+ static_cast<GdkEventMask>(
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK),
+ NULL, NULL, GDK_CURRENT_TIME);
+ // NOTE: technically grab may fail. We may want to try and continue on in
+ // that case.
+ DCHECK_EQ(GDK_GRAB_SUCCESS, pointer_grab_status);
+ has_pointer_grab_ = pointer_grab_status == GDK_GRAB_SUCCESS;
+
+#if defined(TOUCH_UI)
+ ::Window window = GDK_WINDOW_XID(window_contents()->window);
+ Display* display = GDK_WINDOW_XDISPLAY(window_contents()->window);
+ bool xi2grab =
+ ui::TouchFactory::GetInstance()->GrabTouchDevices(display, window);
+ // xi2grab should always succeed if has_pointer_grab_ succeeded.
+ DCHECK(xi2grab);
+ has_pointer_grab_ = has_pointer_grab_ && xi2grab;
+#endif
+ }
+}
+
+void NativeWidgetGtk::ReleaseMouseCapture() {
+ bool delegate_lost_capture = HasMouseCapture();
+ if (GTK_WIDGET_HAS_GRAB(window_contents_))
+ gtk_grab_remove(window_contents_);
+ if (grab_notify_signal_id_) {
+ g_signal_handler_disconnect(window_contents_, grab_notify_signal_id_);
+ grab_notify_signal_id_ = 0;
+ }
+ if (has_pointer_grab_) {
+ has_pointer_grab_ = false;
+ gdk_pointer_ungrab(GDK_CURRENT_TIME);
+#if defined(TOUCH_UI)
+ ui::TouchFactory::GetInstance()->UngrabTouchDevices(
+ GDK_WINDOW_XDISPLAY(window_contents()->window));
+#endif
+ }
+ if (delegate_lost_capture)
+ delegate_->OnMouseCaptureLost();
+}
+
+bool NativeWidgetGtk::HasMouseCapture() const {
+ return GTK_WIDGET_HAS_GRAB(window_contents_) || has_pointer_grab_;
+}
+
+InputMethod* NativeWidgetGtk::CreateInputMethod() {
+ // Create input method when pure views is enabled but not on views desktop.
+ // TODO(suzhe): Always enable input method when we start to use
+ // RenderWidgetHostViewViews in normal ChromeOS.
+ if (views::Widget::IsPureViews()) {
+#if defined(HAVE_IBUS)
+ InputMethod* input_method =
+ InputMethodIBus::IsInputMethodIBusEnabled() ?
+ static_cast<InputMethod*>(new InputMethodIBus(this)) :
+ static_cast<InputMethod*>(new InputMethodGtk(this));
+#else
+ InputMethod* input_method = new InputMethodGtk(this);
+#endif
+ input_method->Init(GetWidget());
+ return input_method;
+ }
+ // GTK's textfield will handle IME.
+ return NULL;
+}
+
+void NativeWidgetGtk::CenterWindow(const gfx::Size& size) {
+ gfx::Rect center_rect;
+
+ GtkWindow* parent = gtk_window_get_transient_for(GetNativeWindow());
+ if (parent) {
+ // We have a parent window, center over it.
+ gint parent_x = 0;
+ gint parent_y = 0;
+ gtk_window_get_position(parent, &parent_x, &parent_y);
+ gint parent_w = 0;
+ gint parent_h = 0;
+ gtk_window_get_size(parent, &parent_w, &parent_h);
+ center_rect = gfx::Rect(parent_x, parent_y, parent_w, parent_h);
+ } else {
+ // We have no parent window, center over the screen.
+ center_rect = gfx::Screen::GetMonitorWorkAreaNearestWindow(GetNativeView());
+ }
+ gfx::Rect bounds(center_rect.x() + (center_rect.width() - size.width()) / 2,
+ center_rect.y() + (center_rect.height() - size.height()) / 2,
+ size.width(), size.height());
+ GetWidget()->SetBoundsConstrained(bounds);
+}
+
+void NativeWidgetGtk::GetWindowPlacement(
+ gfx::Rect* bounds,
+ ui::WindowShowState* show_state) const {
+ // Do nothing for now. ChromeOS isn't yet saving window placement.
+}
+
+void NativeWidgetGtk::SetWindowTitle(const string16& title) {
+ // We don't have a window title on ChromeOS (right now).
+}
+
+void NativeWidgetGtk::SetWindowIcons(const SkBitmap& window_icon,
+ const SkBitmap& app_icon) {
+ // We don't have window icons on ChromeOS.
+}
+
+void NativeWidgetGtk::SetAccessibleName(const string16& name) {
+}
+
+void NativeWidgetGtk::SetAccessibleRole(ui::AccessibilityTypes::Role role) {
+}
+
+void NativeWidgetGtk::SetAccessibleState(ui::AccessibilityTypes::State state) {
+}
+
+void NativeWidgetGtk::BecomeModal() {
+ gtk_window_set_modal(GetNativeWindow(), true);
+}
+
+gfx::Rect NativeWidgetGtk::GetWindowScreenBounds() const {
+ // Client == Window bounds on Gtk.
+ return GetClientAreaScreenBounds();
+}
+
+gfx::Rect NativeWidgetGtk::GetClientAreaScreenBounds() const {
+ // Due to timing we can get a request for bounds after Close().
+ // TODO(beng): Figure out if this is bogus.
+ if (!widget_)
+ return gfx::Rect(size_);
+
+ int x = 0, y = 0, w = 0, h = 0;
+ if (GTK_IS_WINDOW(widget_)) {
+ gtk_window_get_position(GTK_WINDOW(widget_), &x, &y);
+ // NOTE: this doesn't include frame decorations, but it should be good
+ // enough for our uses.
+ gtk_window_get_size(GTK_WINDOW(widget_), &w, &h);
+ } else {
+ GetWidgetPositionOnScreen(widget_, &x, &y);
+ w = widget_->allocation.width;
+ h = widget_->allocation.height;
+ }
+ return gfx::Rect(x, y, w, h);
+}
+
+gfx::Rect NativeWidgetGtk::GetRestoredBounds() const {
+ // We currently don't support tiling, so this doesn't matter.
+ return GetWindowScreenBounds();
+}
+
+void NativeWidgetGtk::SetBounds(const gfx::Rect& bounds) {
+ if (child_) {
+ GtkWidget* parent = gtk_widget_get_parent(widget_);
+ if (GTK_IS_VIEWS_FIXED(parent)) {
+ NativeWidgetGtk* parent_widget = static_cast<NativeWidgetGtk*>(
+ internal::NativeWidgetPrivate::GetNativeWidgetForNativeView(parent));
+ parent_widget->PositionChild(widget_, bounds.x(), bounds.y(),
+ bounds.width(), bounds.height());
+ } else {
+ DCHECK(GTK_IS_FIXED(parent))
+ << "Parent of NativeWidgetGtk has to be Fixed or ViewsFixed";
+ // Just request the size if the parent is not NativeWidgetGtk but plain
+ // GtkFixed. NativeWidgetGtk does not know the minimum size so we assume
+ // the caller of the SetBounds knows exactly how big it wants to be.
+ gtk_widget_set_size_request(widget_, bounds.width(), bounds.height());
+ if (parent != null_parent_)
+ gtk_fixed_move(GTK_FIXED(parent), widget_, bounds.x(), bounds.y());
+ }
+ } else {
+ if (GTK_WIDGET_MAPPED(widget_)) {
+ // If the widget is mapped (on screen), we can move and resize with one
+ // call, which avoids two separate window manager steps.
+ gdk_window_move_resize(widget_->window, bounds.x(), bounds.y(),
+ bounds.width(), bounds.height());
+ }
+
+ // Always call gtk_window_move and gtk_window_resize so that GtkWindow's
+ // geometry info is up-to-date.
+ GtkWindow* gtk_window = GTK_WINDOW(widget_);
+ // TODO: this may need to set an initial size if not showing.
+ // TODO: need to constrain based on screen size.
+ if (!bounds.IsEmpty()) {
+ gtk_window_resize(gtk_window, bounds.width(), bounds.height());
+ }
+ gtk_window_move(gtk_window, bounds.x(), bounds.y());
+ }
+}
+
+void NativeWidgetGtk::SetSize(const gfx::Size& size) {
+ if (child_) {
+ GtkWidget* parent = gtk_widget_get_parent(widget_);
+ if (GTK_IS_VIEWS_FIXED(parent)) {
+ gtk_views_fixed_set_widget_size(widget_, size.width(), size.height());
+ } else {
+ gtk_widget_set_size_request(widget_, size.width(), size.height());
+ }
+ } else {
+ if (GTK_WIDGET_MAPPED(widget_))
+ gdk_window_resize(widget_->window, size.width(), size.height());
+ GtkWindow* gtk_window = GTK_WINDOW(widget_);
+ if (!size.IsEmpty())
+ gtk_window_resize(gtk_window, size.width(), size.height());
+ }
+}
+
+void NativeWidgetGtk::MoveAbove(gfx::NativeView native_view) {
+ ui::StackPopupWindow(GetNativeView(), native_view);
+}
+
+void NativeWidgetGtk::MoveToTop() {
+ DCHECK(GTK_IS_WINDOW(GetNativeView()));
+ gtk_window_present(GTK_WINDOW(GetNativeView()));
+}
+
+void NativeWidgetGtk::SetShape(gfx::NativeRegion region) {
+ if (widget_ && widget_->window) {
+ gdk_window_shape_combine_region(widget_->window, region, 0, 0);
+ gdk_region_destroy(region);
+ }
+}
+
+void NativeWidgetGtk::Close() {
+ if (!widget_)
+ return; // No need to do anything.
+
+ // Hide first.
+ Hide();
+ if (!close_widget_factory_.HasWeakPtrs()) {
+ // And we delay the close just in case we're on the stack.
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&NativeWidgetGtk::CloseNow,
+ close_widget_factory_.GetWeakPtr()));
+ }
+}
+
+void NativeWidgetGtk::CloseNow() {
+ if (widget_) {
+ gtk_widget_destroy(widget_); // Triggers OnDestroy().
+ }
+}
+
+void NativeWidgetGtk::EnableClose(bool enable) {
+ gtk_window_set_deletable(GetNativeWindow(), enable);
+}
+
+void NativeWidgetGtk::Show() {
+ if (widget_) {
+ gtk_widget_show(widget_);
+ if (widget_->window)
+ gdk_window_raise(widget_->window);
+ }
+}
+
+void NativeWidgetGtk::Hide() {
+ if (widget_) {
+ gtk_widget_hide(widget_);
+ if (widget_->window)
+ gdk_window_lower(widget_->window);
+ }
+}
+
+void NativeWidgetGtk::ShowMaximizedWithBounds(
+ const gfx::Rect& restored_bounds) {
+ // TODO: when we add maximization support update this.
+ Show();
+}
+
+void NativeWidgetGtk::ShowWithWindowState(ui::WindowShowState show_state) {
+ // No concept of maximization (yet) on ChromeOS.
+ if (show_state == ui::SHOW_STATE_INACTIVE)
+ gtk_window_set_focus_on_map(GetNativeWindow(), false);
+ gtk_widget_show(GetNativeView());
+}
+
+bool NativeWidgetGtk::IsVisible() const {
+ return GTK_WIDGET_VISIBLE(GetNativeView()) && (GetWidget()->is_top_level() ||
+ GetWidget()->GetTopLevelWidget()->IsVisible());
+}
+
+void NativeWidgetGtk::Activate() {
+ gtk_window_present(GetNativeWindow());
+}
+
+void NativeWidgetGtk::Deactivate() {
+ gdk_window_lower(GTK_WIDGET(GetNativeView())->window);
+}
+
+bool NativeWidgetGtk::IsActive() const {
+ DCHECK(!child_);
+ return is_active_;
+}
+
+void NativeWidgetGtk::SetAlwaysOnTop(bool on_top) {
+ DCHECK(!child_);
+ always_on_top_ = on_top;
+ if (widget_)
+ gtk_window_set_keep_above(GTK_WINDOW(widget_), on_top);
+}
+
+void NativeWidgetGtk::Maximize() {
+#if defined(TOUCH_UI)
+ // There may not be a window manager. So maximize ourselves: move to the
+ // top-left corner and resize to the entire bounds of the screen.
+ gfx::Rect screen = gfx::Screen::GetMonitorAreaNearestWindow(GetNativeView());
+ gtk_window_move(GTK_WINDOW(GetNativeWindow()), screen.x(), screen.y());
+ // TODO(backer): Remove this driver bug workaround once it is fixed.
+ gtk_window_resize(GTK_WINDOW(GetNativeWindow()),
+ screen.width() - 1, screen.height());
+#else
+ gtk_window_maximize(GetNativeWindow());
+#endif
+}
+
+void NativeWidgetGtk::Minimize() {
+ gtk_window_iconify(GetNativeWindow());
+}
+
+bool NativeWidgetGtk::IsMaximized() const {
+ return window_state_ & GDK_WINDOW_STATE_MAXIMIZED;
+}
+
+bool NativeWidgetGtk::IsMinimized() const {
+ return window_state_ & GDK_WINDOW_STATE_ICONIFIED;
+}
+
+void NativeWidgetGtk::Restore() {
+ if (IsFullscreen()) {
+ SetFullscreen(false);
+ } else {
+ if (IsMaximized())
+ gtk_window_unmaximize(GetNativeWindow());
+ else if (IsMinimized())
+ gtk_window_deiconify(GetNativeWindow());
+ }
+}
+
+void NativeWidgetGtk::SetFullscreen(bool fullscreen) {
+ if (fullscreen)
+ gtk_window_fullscreen(GetNativeWindow());
+ else
+ gtk_window_unfullscreen(GetNativeWindow());
+}
+
+bool NativeWidgetGtk::IsFullscreen() const {
+ return window_state_ & GDK_WINDOW_STATE_FULLSCREEN;
+}
+
+void NativeWidgetGtk::SetOpacity(unsigned char opacity) {
+ opacity_ = opacity;
+ if (widget_) {
+ // We can only set the opacity when the widget has been realized.
+ gdk_window_set_opacity(widget_->window, static_cast<gdouble>(opacity) /
+ static_cast<gdouble>(255));
+ }
+}
+
+void NativeWidgetGtk::SetUseDragFrame(bool use_drag_frame) {
+ NOTIMPLEMENTED();
+}
+
+bool NativeWidgetGtk::IsAccessibleWidget() const {
+ return false;
+}
+
+void NativeWidgetGtk::RunShellDrag(View* view,
+ const ui::OSExchangeData& data,
+ int operation) {
+ DoDrag(data, operation);
+}
+
+void NativeWidgetGtk::SchedulePaintInRect(const gfx::Rect& rect) {
+ // No need to schedule paint if
+ // 1) widget_ is NULL. This may happen because this instance may
+ // be deleted after the gtk widget has been destroyed (See OnDestroy()).
+ // 2) widget_ is not drawable (mapped and visible)
+ // 3) If it's never painted before. The first expose event will
+ // paint the area that has to be painted.
+ if (widget_ && GTK_WIDGET_DRAWABLE(widget_) && painted_) {
+ gtk_widget_queue_draw_area(widget_, rect.x(), rect.y(), rect.width(),
+ rect.height());
+ }
+}
+
+void NativeWidgetGtk::SetCursor(gfx::NativeCursor cursor) {
+#if defined(TOUCH_UI)
+ if (!ui::TouchFactory::GetInstance()->is_cursor_visible())
+ cursor = gfx::GetCursor(GDK_BLANK_CURSOR);
+#endif
+ // |window_contents_| is placed on top of |widget_|. So the cursor needs to be
+ // set on |window_contents_| instead of |widget_|.
+ if (window_contents_)
+ gdk_window_set_cursor(window_contents_->window, cursor);
+}
+
+void NativeWidgetGtk::ClearNativeFocus() {
+ DCHECK(!child_);
+ if (!GetNativeView()) {
+ NOTREACHED();
+ return;
+ }
+ gtk_window_set_focus(GTK_WINDOW(GetNativeView()), NULL);
+}
+
+void NativeWidgetGtk::FocusNativeView(gfx::NativeView native_view) {
+ if (native_view && !gtk_widget_is_focus(native_view))
+ gtk_widget_grab_focus(native_view);
+}
+
+bool NativeWidgetGtk::ConvertPointFromAncestor(
+ const Widget* ancestor, gfx::Point* point) const {
+ NOTREACHED();
+ return false;
+}
+
+gfx::Rect NativeWidgetGtk::GetWorkAreaBoundsInScreen() const {
+ return gfx::Screen::GetMonitorWorkAreaNearestWindow(GetNativeView());
+}
+
+void NativeWidgetGtk::SetInactiveRenderingDisabled(bool value) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetGtk, protected:
+
+void NativeWidgetGtk::OnSizeRequest(GtkWidget* widget,
+ GtkRequisition* requisition) {
+ // Do only return the preferred size for child windows. GtkWindow interprets
+ // the requisition as a minimum size for top level windows, returning a
+ // preferred size for these would prevents us from setting smaller window
+ // sizes.
+ if (child_) {
+ gfx::Size size(GetWidget()->GetRootView()->GetPreferredSize());
+ requisition->width = size.width();
+ requisition->height = size.height();
+ }
+}
+
+void NativeWidgetGtk::OnSizeAllocate(GtkWidget* widget,
+ GtkAllocation* allocation) {
+ // See comment next to size_ as to why we do this. Also note, it's tempting
+ // to put this in the static method so subclasses don't need to worry about
+ // it, but if a subclasses needs to set a shape then they need to always
+ // reset the shape in this method regardless of whether the size changed.
+ gfx::Size new_size(allocation->width, allocation->height);
+ if (new_size == size_)
+ return;
+ size_ = new_size;
+ if (compositor_.get())
+ compositor_->WidgetSizeChanged(size_);
+ delegate_->OnNativeWidgetSizeChanged(size_);
+
+ if (GetWidget()->non_client_view()) {
+ // The Window's NonClientView may provide a custom shape for the Window.
+ gfx::Path window_mask;
+ GetWidget()->non_client_view()->GetWindowMask(gfx::Size(allocation->width,
+ allocation->height),
+ &window_mask);
+ GdkRegion* mask_region = window_mask.CreateNativeRegion();
+ gdk_window_shape_combine_region(GetNativeView()->window, mask_region, 0, 0);
+ if (mask_region)
+ gdk_region_destroy(mask_region);
+
+ SaveWindowPosition();
+ }
+}
+
+gboolean NativeWidgetGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) {
+ gdk_window_set_debug_updates(Widget::IsDebugPaintEnabled());
+
+ if (transparent_ && child_) {
+ // Clear the background before drawing any view and native components.
+ DrawTransparentBackground(widget, event);
+ if (!CompositePainter::IsComposited(widget_) &&
+ gdk_screen_is_composited(gdk_screen_get_default())) {
+ // Let the parent draw the content only after something is drawn on
+ // the widget.
+ CompositePainter::SetComposited(widget_);
+ }
+ }
+
+ ui::ScopedRegion region(gdk_region_copy(event->region));
+ if (!gdk_region_empty(region.Get())) {
+ GdkRectangle clip_bounds;
+ gdk_region_get_clipbox(region.Get(), &clip_bounds);
+ if (!delegate_->OnNativeWidgetPaintAccelerated(gfx::Rect(clip_bounds))) {
+ gfx::CanvasSkiaPaint canvas(event);
+ if (!canvas.is_empty()) {
+ canvas.set_composite_alpha(is_transparent());
+ delegate_->OnNativeWidgetPaint(&canvas);
+ }
+ }
+ }
+
+ if (!painted_) {
+ painted_ = true;
+ if (!child_)
+ UpdateFreezeUpdatesProperty(GTK_WINDOW(widget_), false /* remove */);
+ }
+ return false; // False indicates other widgets should get the event as well.
+}
+
+void NativeWidgetGtk::OnDragDataGet(GtkWidget* widget,
+ GdkDragContext* context,
+ GtkSelectionData* data,
+ guint info,
+ guint time) {
+ if (!drag_data_) {
+ NOTREACHED();
+ return;
+ }
+ drag_data_->WriteFormatToSelection(info, data);
+}
+
+void NativeWidgetGtk::OnDragDataReceived(GtkWidget* widget,
+ GdkDragContext* context,
+ gint x,
+ gint y,
+ GtkSelectionData* data,
+ guint info,
+ guint time) {
+ if (drop_target_.get())
+ drop_target_->OnDragDataReceived(context, x, y, data, info, time);
+}
+
+gboolean NativeWidgetGtk::OnDragDrop(GtkWidget* widget,
+ GdkDragContext* context,
+ gint x,
+ gint y,
+ guint time) {
+ if (drop_target_.get()) {
+ return drop_target_->OnDragDrop(context, x, y, time);
+ }
+ return FALSE;
+}
+
+void NativeWidgetGtk::OnDragEnd(GtkWidget* widget, GdkDragContext* context) {
+ if (!drag_data_) {
+ // This indicates we didn't start a drag operation, and should never
+ // happen.
+ NOTREACHED();
+ return;
+ }
+ // Quit the nested message loop we spawned in DoDrag.
+ MessageLoop::current()->Quit();
+}
+
+gboolean NativeWidgetGtk::OnDragFailed(GtkWidget* widget,
+ GdkDragContext* context,
+ GtkDragResult result) {
+ return FALSE;
+}
+
+void NativeWidgetGtk::OnDragLeave(GtkWidget* widget,
+ GdkDragContext* context,
+ guint time) {
+ if (ignore_drag_leave_) {
+ ignore_drag_leave_ = false;
+ return;
+ }
+ if (drop_target_.get()) {
+ drop_target_->OnDragLeave(context, time);
+ drop_target_.reset(NULL);
+ }
+}
+
+gboolean NativeWidgetGtk::OnDragMotion(GtkWidget* widget,
+ GdkDragContext* context,
+ gint x,
+ gint y,
+ guint time) {
+ if (!drop_target_.get()) {
+ drop_target_.reset(new DropTargetGtk(
+ reinterpret_cast<internal::RootView*>(GetWidget()->GetRootView()),
+ context));
+ }
+ return drop_target_->OnDragMotion(context, x, y, time);
+}
+
+gboolean NativeWidgetGtk::OnEnterNotify(GtkWidget* widget,
+ GdkEventCrossing* event) {
+ if (HasMouseCapture() && event->mode == GDK_CROSSING_GRAB) {
+ // Doing a grab results an async enter event, regardless of where the mouse
+ // is. We don't want to generate a mouse move in this case.
+ return false;
+ }
+
+ if (!GetWidget()->last_mouse_event_was_move_ &&
+ !GetWidget()->is_mouse_button_pressed_) {
+ // When a mouse button is pressed gtk generates a leave, enter, press.
+ // RootView expects to get a mouse move before a press, otherwise enter is
+ // not set. So we generate a move here.
+ GdkEventMotion motion = { GDK_MOTION_NOTIFY, event->window,
+ event->send_event, event->time, event->x, event->y, NULL, event->state,
+ 0, NULL, event->x_root, event->y_root };
+
+ // If this event is the result of pressing a button then one of the button
+ // modifiers is set. Unset it as we're compensating for the leave generated
+ // when you press a button.
+ motion.state &= ~(GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK);
+
+ MouseEvent mouse_event(TransformEvent(&motion));
+ delegate_->OnMouseEvent(mouse_event);
+ }
+ return false;
+}
+
+gboolean NativeWidgetGtk::OnLeaveNotify(GtkWidget* widget,
+ GdkEventCrossing* event) {
+ gdk_window_set_cursor(widget->window, gfx::GetCursor(GDK_LEFT_PTR));
+
+ GetWidget()->ResetLastMouseMoveFlag();
+
+ if (!HasMouseCapture() && !GetWidget()->is_mouse_button_pressed_) {
+ // Don't convert if the event is synthetic and has 0x0 coordinates.
+ if (event->x_root || event->y_root || event->x || event->y ||
+ !event->send_event) {
+ TransformEvent(event);
+ }
+ MouseEvent mouse_event(reinterpret_cast<GdkEvent*>(event));
+ delegate_->OnMouseEvent(mouse_event);
+ }
+ return false;
+}
+
+gboolean NativeWidgetGtk::OnMotionNotify(GtkWidget* widget,
+ GdkEventMotion* event) {
+ if (GetWidget()->non_client_view()) {
+ GdkEventMotion transformed_event = *event;
+ TransformEvent(&transformed_event);
+ gfx::Point translated_location(transformed_event.x, transformed_event.y);
+
+ // Update the cursor for the screen edge.
+ int hittest_code =
+ GetWidget()->non_client_view()->NonClientHitTest(translated_location);
+ if (hittest_code != HTCLIENT) {
+ GdkCursorType cursor_type = HitTestCodeToGdkCursorType(hittest_code);
+ gdk_window_set_cursor(widget->window, gfx::GetCursor(cursor_type));
+ }
+ }
+
+ MouseEvent mouse_event(TransformEvent(event));
+ delegate_->OnMouseEvent(mouse_event);
+ return true;
+}
+
+gboolean NativeWidgetGtk::OnButtonPress(GtkWidget* widget,
+ GdkEventButton* event) {
+ if (GetWidget()->non_client_view()) {
+ GdkEventButton transformed_event = *event;
+ MouseEvent mouse_event(TransformEvent(&transformed_event));
+
+ int hittest_code = GetWidget()->non_client_view()->NonClientHitTest(
+ mouse_event.location());
+ switch (hittest_code) {
+ case HTCAPTION: {
+ // Start dragging if the mouse event is a single click and *not* a right
+ // click. If it is a right click, then pass it through to
+ // NativeWidgetGtk::OnButtonPress so that View class can show
+ // ContextMenu upon a mouse release event. We only start drag on single
+ // clicks as we get a crash in Gtk on double/triple clicks.
+ if (event->type == GDK_BUTTON_PRESS &&
+ !mouse_event.IsOnlyRightMouseButton()) {
+ gfx::Point screen_point(event->x, event->y);
+ View::ConvertPointToScreen(GetWidget()->GetRootView(), &screen_point);
+ gtk_window_begin_move_drag(GetNativeWindow(), event->button,
+ screen_point.x(), screen_point.y(),
+ event->time);
+ return TRUE;
+ }
+ break;
+ }
+ case HTBOTTOM:
+ case HTBOTTOMLEFT:
+ case HTBOTTOMRIGHT:
+ case HTGROWBOX:
+ case HTLEFT:
+ case HTRIGHT:
+ case HTTOP:
+ case HTTOPLEFT:
+ case HTTOPRIGHT: {
+ gfx::Point screen_point(event->x, event->y);
+ View::ConvertPointToScreen(GetWidget()->GetRootView(), &screen_point);
+ // TODO(beng): figure out how to get a good minimum size.
+ gtk_widget_set_size_request(GetNativeView(), 100, 100);
+ gtk_window_begin_resize_drag(GetNativeWindow(),
+ HitTestCodeToGDKWindowEdge(hittest_code),
+ event->button, screen_point.x(),
+ screen_point.y(), event->time);
+ return TRUE;
+ }
+ default:
+ // Everything else falls into standard client event handling...
+ break;
+ }
+ }
+
+ if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) {
+ // The sequence for double clicks is press, release, press, 2press, release.
+ // This means that at the time we get the second 'press' we don't know
+ // whether it corresponds to a double click or not. For now we're completely
+ // ignoring the 2press/3press events as they are duplicate. To make this
+ // work right we need to write our own code that detects if the press is a
+ // double/triple. For now we're completely punting, which means we always
+ // get single clicks.
+ // TODO: fix this.
+ return true;
+ }
+
+ MouseEvent mouse_event(TransformEvent(event));
+ // Returns true to consume the event when widget is not transparent.
+ return delegate_->OnMouseEvent(mouse_event) || !transparent_;
+}
+
+gboolean NativeWidgetGtk::OnButtonRelease(GtkWidget* widget,
+ GdkEventButton* event) {
+ // GTK generates a mouse release at the end of dnd. We need to ignore it.
+ if (!drag_data_) {
+ MouseEvent mouse_event(TransformEvent(event));
+ delegate_->OnMouseEvent(mouse_event);
+ }
+ return true;
+}
+
+gboolean NativeWidgetGtk::OnScroll(GtkWidget* widget, GdkEventScroll* event) {
+ MouseWheelEvent mouse_event(TransformEvent(event));
+ return delegate_->OnMouseEvent(mouse_event);
+}
+
+gboolean NativeWidgetGtk::OnFocusIn(GtkWidget* widget, GdkEventFocus* event) {
+ if (has_focus_)
+ return false; // This is the second focus-in event in a row, ignore it.
+ has_focus_ = true;
+
+ should_handle_menu_key_release_ = false;
+
+ if (!GetWidget()->is_top_level())
+ return false;
+
+ // Only top-level Widget should have an InputMethod instance.
+ InputMethod* input_method = GetWidget()->GetInputMethod();
+ if (input_method)
+ input_method->OnFocus();
+
+ // See description of got_initial_focus_in_ for details on this.
+ if (!got_initial_focus_in_) {
+ got_initial_focus_in_ = true;
+ // Sets initial focus here. On X11/Gtk, window creation
+ // is asynchronous and a focus request has to be made after a window
+ // gets created.
+ GetWidget()->SetInitialFocus();
+ }
+ return false;
+}
+
+gboolean NativeWidgetGtk::OnFocusOut(GtkWidget* widget, GdkEventFocus* event) {
+ if (!has_focus_)
+ return false; // This is the second focus-out event in a row, ignore it.
+ has_focus_ = false;
+
+ if (!GetWidget()->is_top_level())
+ return false;
+
+ // Only top-level Widget should have an InputMethod instance.
+ InputMethod* input_method = GetWidget()->GetInputMethod();
+ if (input_method)
+ input_method->OnBlur();
+ return false;
+}
+
+gboolean NativeWidgetGtk::OnEventKey(GtkWidget* widget, GdkEventKey* event) {
+ KeyEvent key(reinterpret_cast<GdkEvent*>(event));
+ InputMethod* input_method = GetWidget()->GetInputMethod();
+ if (input_method)
+ input_method->DispatchKeyEvent(key);
+ else
+ DispatchKeyEventPostIME(key);
+
+ // Returns true to prevent GtkWindow's default key event handler.
+ return true;
+}
+
+gboolean NativeWidgetGtk::OnQueryTooltip(GtkWidget* widget,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip* tooltip) {
+#if defined(TOUCH_UI)
+ return false; // Tell GTK not to draw tooltips as we draw tooltips in views
+#else
+ return static_cast<TooltipManagerGtk*>(tooltip_manager_.get())->
+ ShowTooltip(x, y, keyboard_mode, tooltip);
+#endif
+}
+
+gboolean NativeWidgetGtk::OnVisibilityNotify(GtkWidget* widget,
+ GdkEventVisibility* event) {
+ return false;
+}
+
+gboolean NativeWidgetGtk::OnGrabBrokeEvent(GtkWidget* widget, GdkEvent* event) {
+ if (!has_pointer_grab_ && !has_keyboard_grab_) {
+ // We don't have any grabs; don't attempt to do anything.
+ return false;
+ }
+
+ // Sent when either the keyboard or pointer grab is broke. We drop both grabs
+ // in this case.
+ if (event->grab_broken.keyboard) {
+ // Keyboard grab was broke.
+ has_keyboard_grab_ = false;
+ if (has_pointer_grab_) {
+ has_pointer_grab_ = false;
+ gdk_pointer_ungrab(GDK_CURRENT_TIME);
+ delegate_->OnMouseCaptureLost();
+ }
+ } else {
+ // Mouse grab was broke.
+ has_pointer_grab_ = false;
+ if (has_keyboard_grab_) {
+ has_keyboard_grab_ = false;
+ gdk_keyboard_ungrab(GDK_CURRENT_TIME);
+ }
+ delegate_->OnMouseCaptureLost();
+ }
+ ReleaseMouseCapture();
+
+#if defined(TOUCH_UI)
+ ui::TouchFactory::GetInstance()->UngrabTouchDevices(
+ GDK_WINDOW_XDISPLAY(window_contents()->window));
+#endif
+ return false; // To let other widgets get the event.
+}
+
+void NativeWidgetGtk::OnGrabNotify(GtkWidget* widget, gboolean was_grabbed) {
+ // Sent when gtk_grab_add changes.
+ if (!window_contents_)
+ return; // Grab broke after window destroyed, don't try processing it.
+ if (!was_grabbed) // Indicates we've been shadowed (lost grab).
+ HandleGtkGrabBroke();
+}
+
+void NativeWidgetGtk::OnDestroy(GtkWidget* object) {
+ signal_registrar_.reset();
+ if (grab_notify_signal_id_) {
+ g_signal_handler_disconnect(window_contents_, grab_notify_signal_id_);
+ grab_notify_signal_id_ = 0;
+ }
+ delegate_->OnNativeWidgetDestroying();
+ if (!child_)
+ ActiveWindowWatcherX::RemoveObserver(this);
+ // Note that this handler is hooked to GtkObject::destroy.
+ // NULL out pointers here since we might still be in an observer list
+ // until deletion happens.
+ widget_ = window_contents_ = NULL;
+}
+
+void NativeWidgetGtk::OnDestroyed(GObject *where_the_object_was) {
+ delegate_->OnNativeWidgetDestroyed();
+ if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
+ delete this;
+}
+
+void NativeWidgetGtk::OnShow(GtkWidget* widget) {
+ delegate_->OnNativeWidgetVisibilityChanged(true);
+}
+
+void NativeWidgetGtk::OnMap(GtkWidget* widget) {
+#if defined(TOUCH_UI)
+ // Force an expose event to trigger OnPaint for touch. This is
+ // a workaround for a bug that X Expose event does not trigger
+ // Gdk's expose signal. This happens when you try to open views menu
+ // while a virtual keyboard gets kicked in or out. This seems to be
+ // a bug in message_pump_x.cc as we do get X Expose event but
+ // it doesn't trigger gtk's expose signal. We're not going to fix this
+ // as we're removing gtk and migrating to new compositor.
+ gdk_window_process_all_updates();
+#endif
+}
+
+void NativeWidgetGtk::OnHide(GtkWidget* widget) {
+ delegate_->OnNativeWidgetVisibilityChanged(false);
+}
+
+gboolean NativeWidgetGtk::OnWindowStateEvent(GtkWidget* widget,
+ GdkEventWindowState* event) {
+ if (GetWidget()->non_client_view() &&
+ !(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)) {
+ SaveWindowPosition();
+ }
+ window_state_ = event->new_window_state;
+ return FALSE;
+}
+
+gboolean NativeWidgetGtk::OnConfigureEvent(GtkWidget* widget,
+ GdkEventConfigure* event) {
+ SaveWindowPosition();
+ return FALSE;
+}
+
+void NativeWidgetGtk::HandleGtkGrabBroke() {
+ ReleaseMouseCapture();
+ delegate_->OnMouseCaptureLost();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetGtk, private:
+
+void NativeWidgetGtk::ScheduleDraw() {
+ SchedulePaintInRect(gfx::Rect(gfx::Point(), size_));
+}
+
+void NativeWidgetGtk::DispatchKeyEventPostIME(const KeyEvent& key) {
+ // Always reset |should_handle_menu_key_release_| unless we are handling a
+ // VKEY_MENU key release event. It ensures that VKEY_MENU accelerator can only
+ // be activated when handling a VKEY_MENU key release event which is preceded
+ // by an unhandled VKEY_MENU key press event. See also HandleKeyboardEvent().
+ if (key.key_code() != ui::VKEY_MENU || key.type() != ui::ET_KEY_RELEASED)
+ should_handle_menu_key_release_ = false;
+
+ // Send the key event to View hierarchy first.
+ bool handled = delegate_->OnKeyEvent(key);
+
+ if (key.key_code() == ui::VKEY_PROCESSKEY || handled)
+ return;
+
+ // Dispatch the key event to native GtkWidget hierarchy.
+ // To prevent GtkWindow from handling the key event as a keybinding, we need
+ // to bypass GtkWindow's default key event handler and dispatch the event
+ // here.
+ GdkEventKey* event = reinterpret_cast<GdkEventKey*>(key.gdk_event());
+ if (!handled && event && GTK_IS_WINDOW(widget_))
+ handled = gtk_window_propagate_key_event(GTK_WINDOW(widget_), event);
+
+ // On Linux, in order to handle VKEY_MENU (Alt) accelerator key correctly and
+ // avoid issues like: http://crbug.com/40966 and http://crbug.com/49701, we
+ // should only send the key event to the focus manager if it's not handled by
+ // any View or native GtkWidget.
+ // The flow is different when the focus is in a RenderWidgetHostViewGtk, which
+ // always consumes the key event and send it back to us later by calling
+ // HandleKeyboardEvent() directly, if it's not handled by webkit.
+ if (!handled)
+ handled = HandleKeyboardEvent(key);
+
+ // Dispatch the key event for bindings processing.
+ if (!handled && event && GTK_IS_WINDOW(widget_))
+ gtk_bindings_activate_event(GTK_OBJECT(widget_), event);
+}
+
+void NativeWidgetGtk::SetInitParams(const Widget::InitParams& params) {
+ DCHECK(!GetNativeView());
+
+ ownership_ = params.ownership;
+ child_ = params.child;
+ is_menu_ = params.type == Widget::InitParams::TYPE_MENU;
+
+ // TODO(beng): The secondary checks here actually obviate the need for
+ // params.transient but that's only because NativeWidgetGtk
+ // considers any top-level widget to be a transient widget. We
+ // will probably want to ammend this assumption at some point.
+ if (params.transient || params.parent || params.parent_widget)
+ transient_to_parent_ = true;
+ if (params.transparent)
+ MakeTransparent();
+ if (!params.accept_events && !child_)
+ ignore_events_ = true;
+ if (params.double_buffer)
+ EnableDoubleBuffer(true);
+}
+
+gboolean NativeWidgetGtk::OnWindowPaint(GtkWidget* widget,
+ GdkEventExpose* event) {
+ // Clear the background to be totally transparent. We don't need to
+ // paint the root view here as that is done by OnPaint.
+ DCHECK(transparent_);
+ DrawTransparentBackground(widget, event);
+ // The Keyboard layout view has a renderer that covers the entire
+ // window, which prevents OnPaint from being called on window_contents_,
+ // so we need to remove the FREEZE_UPDATES property here.
+ if (!painted_) {
+ painted_ = true;
+ UpdateFreezeUpdatesProperty(GTK_WINDOW(widget_), false /* remove */);
+ }
+ return false;
+}
+
+void NativeWidgetGtk::OnChildExpose(GtkWidget* child) {
+ DCHECK(!child_);
+ if (!painted_) {
+ painted_ = true;
+ UpdateFreezeUpdatesProperty(GTK_WINDOW(widget_), false /* remove */);
+ }
+ RemoveExposeHandlerIfExists(child);
+}
+
+// static
+gboolean NativeWidgetGtk::ChildExposeHandler(GtkWidget* widget,
+ GdkEventExpose* event) {
+ GtkWidget* toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW);
+ CHECK(toplevel);
+ Widget* views_widget = Widget::GetWidgetForNativeView(toplevel);
+ CHECK(views_widget);
+ NativeWidgetGtk* widget_gtk =
+ static_cast<NativeWidgetGtk*>(views_widget->native_widget());
+ widget_gtk->OnChildExpose(widget);
+ return false;
+}
+
+void NativeWidgetGtk::CreateGtkWidget(const Widget::InitParams& params) {
+ // We turn off double buffering for two reasons:
+ // 1. We draw to a canvas then composite to the screen, which means we're
+ // doing our own double buffering already.
+ // 2. GTKs double buffering clips to the dirty region. RootView occasionally
+ // needs to expand the paint region (see RootView::OnPaint). This means
+ // that if we use GTK's double buffering and we tried to expand the dirty
+ // region, it wouldn't get painted.
+ if (child_) {
+ window_contents_ = widget_ = gtk_views_fixed_new();
+ gtk_widget_set_name(widget_, "views-gtkwidget-child-fixed");
+ if (!is_double_buffered_)
+ GTK_WIDGET_UNSET_FLAGS(widget_, GTK_DOUBLE_BUFFERED);
+ gtk_fixed_set_has_window(GTK_FIXED(widget_), true);
+ if (!params.parent && !null_parent_) {
+ GtkWidget* popup = gtk_window_new(GTK_WINDOW_POPUP);
+ null_parent_ = gtk_fixed_new();
+ gtk_widget_set_name(null_parent_, "views-gtkwidget-null-parent");
+ gtk_container_add(GTK_CONTAINER(popup), null_parent_);
+ gtk_widget_realize(null_parent_);
+ }
+ if (transparent_) {
+ // transparency has to be configured before widget is realized.
+ DCHECK(params.parent) <<
+ "Transparent widget must have parent when initialized";
+ ConfigureWidgetForTransparentBackground(params.parent);
+ }
+ gtk_container_add(
+ GTK_CONTAINER(params.parent ? params.parent : null_parent_), widget_);
+ gtk_widget_realize(widget_);
+ if (transparent_) {
+ // The widget has to be realized to set composited flag.
+ // I tried "realize" signal to set this flag, but it did not work
+ // when the top level is popup.
+ DCHECK(GTK_WIDGET_REALIZED(widget_));
+ gdk_window_set_composited(widget_->window, true);
+ }
+ if (params.parent && !params.bounds.size().IsEmpty()) {
+ // Make sure that an widget is given it's initial size before
+ // we're done initializing, to take care of some potential
+ // corner cases when programmatically arranging hierarchies as
+ // seen in
+ // http://code.google.com/p/chromium-os/issues/detail?id=5987
+
+ // This can't be done without a parent present, or stale data
+ // might show up on the screen as seen in
+ // http://code.google.com/p/chromium/issues/detail?id=53870
+ GtkAllocation alloc =
+ { 0, 0, params.bounds.width(), params.bounds.height() };
+ gtk_widget_size_allocate(widget_, &alloc);
+ }
+ if (params.type == Widget::InitParams::TYPE_CONTROL) {
+ // Controls are initially visible.
+ gtk_widget_show(widget_);
+ }
+ } else {
+ Widget::InitParams::Type type = params.type;
+ if (type == Widget::InitParams::TYPE_BUBBLE &&
+ params.delegate->AsBubbleDelegate() &&
+ params.delegate->AsBubbleDelegate()->use_focusless()) {
+ // Handles focusless bubble type, which are bubbles that should
+ // act like popups rather than gtk windows. They do not get focus
+ // and are not controlled by window manager placement.
+ type = Widget::InitParams::TYPE_POPUP;
+ }
+
+ // Use our own window class to override GtkWindow's move_focus method.
+ widget_ = gtk_views_window_new(WindowTypeToGtkWindowType(type));
+ gtk_widget_set_name(widget_, "views-gtkwidget-window");
+ if (transient_to_parent_) {
+ gtk_window_set_transient_for(GTK_WINDOW(widget_),
+ GTK_WINDOW(params.parent));
+ }
+ GTK_WIDGET_UNSET_FLAGS(widget_, GTK_DOUBLE_BUFFERED);
+
+ // Gtk determines the size for windows based on the requested size of the
+ // child. For NativeWidgetGtk the child is a fixed. If the fixed ends up
+ // with a child widget it's possible the child widget will drive the
+ // requested size of the widget, which we don't want. We explicitly set a
+ // value of 1x1 here so that gtk doesn't attempt to resize the window if we
+ // end up with a situation where the requested size of a child of the fixed
+ // is greater than the size of the window. By setting the size in this
+ // manner we're also allowing users of WidgetGtk to change the requested
+ // size at any time.
+ gtk_widget_set_size_request(widget_, 1, 1);
+
+ if (!params.bounds.size().IsEmpty()) {
+ // When we realize the window, the window manager is given a size. If we
+ // don't specify a size before then GTK defaults to 200x200. Specify
+ // a size now so that the window manager sees the requested size.
+ GtkAllocation alloc =
+ { 0, 0, params.bounds.width(), params.bounds.height() };
+ gtk_widget_size_allocate(widget_, &alloc);
+ }
+ gtk_window_set_decorated(GTK_WINDOW(widget_), false);
+ // We'll take care of positioning our window.
+ gtk_window_set_position(GTK_WINDOW(widget_), GTK_WIN_POS_NONE);
+
+ window_contents_ = gtk_views_fixed_new();
+ gtk_widget_set_name(window_contents_, "views-gtkwidget-window-fixed");
+ if (!is_double_buffered_)
+ GTK_WIDGET_UNSET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED);
+ gtk_fixed_set_has_window(GTK_FIXED(window_contents_), true);
+ gtk_container_add(GTK_CONTAINER(widget_), window_contents_);
+ gtk_widget_show(window_contents_);
+ g_object_set_data(G_OBJECT(window_contents_), kNativeWidgetKey,
+ static_cast<NativeWidgetGtk*>(this));
+ if (transparent_)
+ ConfigureWidgetForTransparentBackground(NULL);
+
+ if (ignore_events_)
+ ConfigureWidgetForIgnoreEvents();
+
+ // Realize the window_contents_ so that we can always get a handle for
+ // acceleration. Without this we need to check every time paint is
+ // invoked.
+ gtk_widget_realize(window_contents_);
+
+ SetAlwaysOnTop(always_on_top_);
+ // UpdateFreezeUpdatesProperty will realize the widget and handlers like
+ // size-allocate will function properly.
+ UpdateFreezeUpdatesProperty(GTK_WINDOW(widget_), true /* add */);
+ }
+ SetNativeWindowProperty(kNativeWidgetKey, this);
+}
+
+void NativeWidgetGtk::ConfigureWidgetForTransparentBackground(
+ GtkWidget* parent) {
+ DCHECK(widget_ && window_contents_);
+
+ GdkColormap* rgba_colormap =
+ gdk_screen_get_rgba_colormap(gtk_widget_get_screen(widget_));
+ if (!rgba_colormap) {
+ transparent_ = false;
+ return;
+ }
+ // To make the background transparent we need to install the RGBA colormap
+ // on both the window and fixed. In addition we need to make sure no
+ // decorations are drawn. The last bit is to make sure the widget doesn't
+ // attempt to draw a pixmap in it's background.
+ if (!child_) {
+ DCHECK(parent == NULL);
+ gtk_widget_set_colormap(widget_, rgba_colormap);
+ gtk_widget_set_app_paintable(widget_, true);
+ signal_registrar_->Connect(widget_, "expose_event",
+ G_CALLBACK(&OnWindowPaintThunk), this);
+ gtk_widget_realize(widget_);
+ gdk_window_set_decorations(widget_->window,
+ static_cast<GdkWMDecoration>(0));
+ } else {
+ DCHECK(parent);
+ CompositePainter::AddCompositePainter(parent);
+ }
+ DCHECK(!GTK_WIDGET_REALIZED(window_contents_));
+ gtk_widget_set_colormap(window_contents_, rgba_colormap);
+}
+
+void NativeWidgetGtk::ConfigureWidgetForIgnoreEvents() {
+ gtk_widget_realize(widget_);
+ GdkWindow* gdk_window = widget_->window;
+ Display* display = GDK_WINDOW_XDISPLAY(gdk_window);
+ XID win = GDK_WINDOW_XID(gdk_window);
+
+ // This sets the clickable area to be empty, allowing all events to be
+ // passed to any windows behind this one.
+ XShapeCombineRectangles(
+ display,
+ win,
+ ShapeInput,
+ 0, // x offset
+ 0, // y offset
+ NULL, // rectangles
+ 0, // num rectangles
+ ShapeSet,
+ 0);
+}
+
+void NativeWidgetGtk::DrawTransparentBackground(GtkWidget* widget,
+ GdkEventExpose* event) {
+ cairo_t* cr = gdk_cairo_create(widget->window);
+ cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+ gdk_cairo_region(cr, event->region);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+}
+
+void NativeWidgetGtk::SaveWindowPosition() {
+ // The delegate may have gone away on us.
+ if (!GetWidget()->widget_delegate())
+ return;
+
+ ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL;
+ if (IsMaximized())
+ show_state = ui::SHOW_STATE_MAXIMIZED;
+ else if (IsMinimized())
+ show_state = ui::SHOW_STATE_MINIMIZED;
+ GetWidget()->widget_delegate()->SaveWindowPlacement(
+ GetWidget()->GetWindowScreenBounds(),
+ show_state);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget, public:
+
+// static
+void Widget::NotifyLocaleChanged() {
+ GList *window_list = gtk_window_list_toplevels();
+ for (GList* element = window_list; element; element = g_list_next(element)) {
+ Widget* widget =
+ Widget::GetWidgetForNativeWindow(GTK_WINDOW(element->data));
+ if (widget)
+ widget->LocaleChanged();
+ }
+ g_list_free(window_list);
+}
+
+// static
+void Widget::CloseAllSecondaryWidgets() {
+ GList* windows = gtk_window_list_toplevels();
+ for (GList* window = windows; window;
+ window = g_list_next(window)) {
+ Widget* widget = Widget::GetWidgetForNativeView(GTK_WIDGET(window->data));
+ if (widget && widget->is_secondary_widget())
+ widget->Close();
+ }
+ g_list_free(windows);
+}
+
+// static
+bool Widget::ConvertRect(const Widget* source,
+ const Widget* target,
+ gfx::Rect* rect) {
+ DCHECK(source);
+ DCHECK(target);
+ DCHECK(rect);
+
+ // TODO(oshima): Add check if source and target belongs to the same
+ // screen.
+
+ if (source == target)
+ return true;
+ if (!source || !target)
+ return false;
+
+ gfx::Point source_point = source->GetWindowScreenBounds().origin();
+ gfx::Point target_point = target->GetWindowScreenBounds().origin();
+
+ rect->set_origin(
+ source_point.Subtract(target_point).Add(rect->origin()));
+ return true;
+}
+
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetPrivate, public:
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget(
+ NativeWidgetDelegate* delegate) {
+ return new NativeWidgetGtk(delegate);
+}
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView(
+ gfx::NativeView native_view) {
+ if (!native_view)
+ return NULL;
+ return reinterpret_cast<NativeWidgetGtk*>(
+ g_object_get_data(G_OBJECT(native_view), kNativeWidgetKey));
+}
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
+ gfx::NativeWindow native_window) {
+ if (!native_window)
+ return NULL;
+ return reinterpret_cast<NativeWidgetGtk*>(
+ g_object_get_data(G_OBJECT(native_window), kNativeWidgetKey));
+}
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
+ gfx::NativeView native_view) {
+ if (!native_view)
+ return NULL;
+
+ NativeWidgetPrivate* widget = NULL;
+
+ GtkWidget* parent_gtkwidget = native_view;
+ NativeWidgetPrivate* parent_widget;
+ do {
+ parent_widget = GetNativeWidgetForNativeView(parent_gtkwidget);
+ if (parent_widget)
+ widget = parent_widget;
+ parent_gtkwidget = gtk_widget_get_parent(parent_gtkwidget);
+ } while (parent_gtkwidget);
+
+ return widget && widget->GetWidget()->is_top_level() ? widget : NULL;
+}
+
+// static
+void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
+ Widget::Widgets* children) {
+ if (!native_view)
+ return;
+
+ Widget* widget = Widget::GetWidgetForNativeView(native_view);
+ if (widget)
+ children->insert(widget);
+ gtk_container_foreach(GTK_CONTAINER(native_view),
+ EnumerateChildWidgetsForNativeWidgets,
+ reinterpret_cast<gpointer>(children));
+}
+
+// static
+void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view,
+ gfx::NativeView new_parent) {
+ if (!native_view)
+ return;
+
+ gfx::NativeView previous_parent = gtk_widget_get_parent(native_view);
+ if (previous_parent == new_parent)
+ return;
+
+ Widget::Widgets widgets;
+ GetAllChildWidgets(native_view, &widgets);
+
+ // First notify all the widgets that they are being disassociated
+ // from their previous parent.
+ for (Widget::Widgets::iterator it = widgets.begin();
+ it != widgets.end(); ++it) {
+ // TODO(beng): Rename this notification to NotifyNativeViewChanging()
+ // and eliminate the bool parameter.
+ (*it)->NotifyNativeViewHierarchyChanged(false, previous_parent);
+ }
+
+ if (gtk_widget_get_parent(native_view))
+ gtk_widget_reparent(native_view, new_parent);
+ else
+ gtk_container_add(GTK_CONTAINER(new_parent), native_view);
+
+ // And now, notify them that they have a brand new parent.
+ for (Widget::Widgets::iterator it = widgets.begin();
+ it != widgets.end(); ++it) {
+ (*it)->NotifyNativeViewHierarchyChanged(true, new_parent);
+ }
+}
+
+// static
+bool NativeWidgetPrivate::IsMouseButtonDown() {
+ bool button_pressed = false;
+ GdkEvent* event = gtk_get_current_event();
+ if (event) {
+ button_pressed = event->type == GDK_BUTTON_PRESS ||
+ event->type == GDK_2BUTTON_PRESS ||
+ event->type == GDK_3BUTTON_PRESS;
+ gdk_event_free(event);
+ }
+ return button_pressed;
+}
+
+} // namespace internal
+} // namespace views
diff --git a/views/widget/native_widget_gtk.h b/views/widget/native_widget_gtk.h
new file mode 100644
index 0000000..1dcdace
--- /dev/null
+++ b/views/widget/native_widget_gtk.h
@@ -0,0 +1,475 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_NATIVE_WIDGET_GTK_H_
+#define VIEWS_WIDGET_NATIVE_WIDGET_GTK_H_
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "ui/base/gtk/gtk_signal.h"
+#include "ui/base/x/active_window_watcher_x.h"
+#include "ui/gfx/compositor/compositor.h"
+#include "ui/gfx/size.h"
+#include "ui/views/focus/focus_manager.h"
+#include "views/widget/native_widget_private.h"
+#include "views/widget/widget.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+class OSExchangeData;
+class OSExchangeDataProviderGtk;
+class GtkSignalRegistrar;
+}
+using ui::OSExchangeData;
+using ui::OSExchangeDataProviderGtk;
+
+namespace views {
+
+class DropTargetGtk;
+class InputMethod;
+class View;
+
+namespace internal {
+class NativeWidgetDelegate;
+}
+
+// Widget implementation for GTK.
+class VIEWS_EXPORT NativeWidgetGtk : public internal::NativeWidgetPrivate,
+ public ui::CompositorDelegate,
+ public ui::ActiveWindowWatcherX::Observer {
+ public:
+ explicit NativeWidgetGtk(internal::NativeWidgetDelegate* delegate);
+ virtual ~NativeWidgetGtk();
+
+ // Returns the transient parent. See make_transient_to_parent for details on
+ // what the transient parent is.
+ GtkWindow* GetTransientParent() const;
+
+ // Makes the background of the window totally transparent. This must be
+ // invoked before Init. This does a couple of checks and returns true if
+ // the window can be made transparent. The actual work of making the window
+ // transparent is done by ConfigureWidgetForTransparentBackground.
+ // This works for both child and window types.
+ bool MakeTransparent();
+ bool is_transparent() const { return transparent_; }
+
+ // Enable/Disable double buffering.This is neccessary to prevent
+ // flickering in ScrollView, which has both native and view
+ // controls.
+ void EnableDoubleBuffer(bool enabled);
+ bool is_double_buffered() const { return is_double_buffered_; }
+
+ bool is_ignore_events() const { return ignore_events_; }
+
+ // Adds and removes the specified widget as a child of this widget's contents.
+ // These methods make sure to add the widget to the window's contents
+ // container if this widget is a window.
+ void AddChild(GtkWidget* child);
+ void RemoveChild(GtkWidget* child);
+
+ // A safe way to reparent a child widget to this widget. Calls
+ // gtk_widget_reparent which handles refcounting to avoid destroying the
+ // widget when removing it from its old parent.
+ void ReparentChild(GtkWidget* child);
+
+ // Positions a child GtkWidget at the specified location and bounds.
+ void PositionChild(GtkWidget* child, int x, int y, int w, int h);
+
+ // Parent GtkWidget all children are added to. When this NativeWidgetGtk
+ // corresponds to a top level window, this is the GtkFixed within the
+ // GtkWindow, not the GtkWindow itself. For child widgets, this is the same
+ // GtkFixed as |widget_|.
+ GtkWidget* window_contents() const { return window_contents_; }
+
+ // Starts a drag on this widget. This blocks until the drag is done.
+ void DoDrag(const OSExchangeData& data, int operation);
+
+ // Invoked when the active status changes.
+ virtual void OnActiveChanged();
+
+ // Sets the drop target to NULL. This is invoked by DropTargetGTK when the
+ // drop is done.
+ void ResetDropTarget();
+
+ // Gets the requested size of the widget. This can be the size
+ // stored in properties for a GtkViewsFixed, or in the requisitioned
+ // size of other kinds of widgets.
+ void GetRequestedSize(gfx::Size* out) const;
+
+ // Overridden from ui::ActiveWindowWatcherX::Observer.
+ virtual void ActiveWindowChanged(GdkWindow* active_window) OVERRIDE;
+
+ // Handles a keyboard event by sending it to our focus manager.
+ // Returns true if it's handled by the focus manager.
+ bool HandleKeyboardEvent(const KeyEvent& key);
+
+ // Tells widget not to remove FREEZE_UPDATES property when the
+ // widget is painted. This is used if painting the gtk widget
+ // is not enough to show the window and has to wait further like
+ // keyboard overlay. Returns true if this is called before
+ // FREEZE_UPDATES property is removed, or false otherwise.
+ bool SuppressFreezeUpdates();
+
+ // Sets and deletes FREEZE_UPDATES property on given |window|.
+ // It adds the property when |enable| is true and remove if false.
+ // Calling this method will realize the window if it's not realized yet.
+ // This property is used to help WindowManager know when the window
+ // is fully painted so that WM can map the fully painted window.
+ // The property is based on Owen Taylor's proposal at
+ // http://mail.gnome.org/archives/wm-spec-list/2009-June/msg00002.html.
+ // This is just a hint to WM, and won't change the behavior for WM
+ // which does not support this property.
+ static void UpdateFreezeUpdatesProperty(GtkWindow* window, bool enable);
+
+ // Registers a expose handler that removes FREEZE_UPDATES property.
+ // If you are adding a GtkWidget with its own GdkWindow that may
+ // fill the entire area of the NativeWidgetGtk to the view hierachy, you
+ // need use this function to tell WM that when the widget is ready
+ // to be shown.
+ // Caller of this method do not need to disconnect this because the
+ // handler will be removed upon the first invocation of the handler,
+ // or when the widget is re-attached, and expose won't be emitted on
+ // detached widget.
+ static void RegisterChildExposeHandler(GtkWidget* widget);
+
+ // Overridden from internal::NativeWidgetPrivate:
+ virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE;
+ virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE;
+ virtual void UpdateFrameAfterFrameChange() OVERRIDE;
+ virtual bool ShouldUseNativeFrame() const OVERRIDE;
+ virtual void FrameTypeChanged() OVERRIDE;
+ virtual Widget* GetWidget() OVERRIDE;
+ virtual const Widget* GetWidget() const OVERRIDE;
+ virtual gfx::NativeView GetNativeView() const OVERRIDE;
+ virtual gfx::NativeWindow GetNativeWindow() const OVERRIDE;
+ virtual Widget* GetTopLevelWidget() OVERRIDE;
+ virtual const ui::Compositor* GetCompositor() const OVERRIDE;
+ virtual ui::Compositor* GetCompositor() OVERRIDE;
+ virtual void CalculateOffsetToAncestorWithLayer(
+ gfx::Point* offset,
+ ui::Layer** layer_parent) OVERRIDE;
+ virtual void ReorderLayers() OVERRIDE;
+ virtual void ViewRemoved(View* view) OVERRIDE;
+ virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE;
+ virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE;
+ virtual TooltipManager* GetTooltipManager() const OVERRIDE;
+ virtual bool IsScreenReaderActive() const OVERRIDE;
+ virtual void SendNativeAccessibilityEvent(
+ View* view,
+ ui::AccessibilityTypes::Event event_type) OVERRIDE;
+ virtual void SetMouseCapture() OVERRIDE;
+ virtual void ReleaseMouseCapture() OVERRIDE;
+ virtual bool HasMouseCapture() const OVERRIDE;
+ virtual InputMethod* CreateInputMethod() OVERRIDE;
+ virtual void CenterWindow(const gfx::Size& size) OVERRIDE;
+ virtual void GetWindowPlacement(
+ gfx::Rect* bounds,
+ ui::WindowShowState* show_state) const OVERRIDE;
+ virtual void SetWindowTitle(const string16& title) OVERRIDE;
+ virtual void SetWindowIcons(const SkBitmap& window_icon,
+ const SkBitmap& app_icon) OVERRIDE;
+ virtual void SetAccessibleName(const string16& name) OVERRIDE;
+ virtual void SetAccessibleRole(ui::AccessibilityTypes::Role role) OVERRIDE;
+ virtual void SetAccessibleState(ui::AccessibilityTypes::State state) OVERRIDE;
+ virtual void BecomeModal() OVERRIDE;
+ virtual gfx::Rect GetWindowScreenBounds() const OVERRIDE;
+ virtual gfx::Rect GetClientAreaScreenBounds() const OVERRIDE;
+ virtual gfx::Rect GetRestoredBounds() const OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual void SetSize(const gfx::Size& size) OVERRIDE;
+ virtual void MoveAbove(gfx::NativeView native_view) OVERRIDE;
+ virtual void MoveToTop() OVERRIDE;
+ virtual void SetShape(gfx::NativeRegion shape) OVERRIDE;
+ virtual void Close() OVERRIDE;
+ virtual void CloseNow() OVERRIDE;
+ virtual void EnableClose(bool enable) OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual void ShowMaximizedWithBounds(
+ const gfx::Rect& restored_bounds) OVERRIDE;
+ virtual void ShowWithWindowState(ui::WindowShowState window_state) OVERRIDE;
+ virtual bool IsVisible() const OVERRIDE;
+ virtual void Activate() OVERRIDE;
+ virtual void Deactivate() OVERRIDE;
+ virtual bool IsActive() const OVERRIDE;
+ virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE;
+ virtual void Maximize() OVERRIDE;
+ virtual void Minimize() OVERRIDE;
+ virtual bool IsMaximized() const OVERRIDE;
+ virtual bool IsMinimized() const OVERRIDE;
+ virtual void Restore() OVERRIDE;
+ virtual void SetFullscreen(bool fullscreen) OVERRIDE;
+ virtual bool IsFullscreen() const OVERRIDE;
+ virtual void SetOpacity(unsigned char opacity) OVERRIDE;
+ virtual void SetUseDragFrame(bool use_drag_frame) OVERRIDE;
+ virtual bool IsAccessibleWidget() const OVERRIDE;
+ virtual void RunShellDrag(View* view,
+ const ui::OSExchangeData& data,
+ int operation) OVERRIDE;
+ virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE;
+ virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE;
+ virtual void ClearNativeFocus() OVERRIDE;
+ virtual void FocusNativeView(gfx::NativeView native_view) OVERRIDE;
+ virtual bool ConvertPointFromAncestor(
+ const Widget* ancestor, gfx::Point* point) const OVERRIDE;
+ virtual gfx::Rect GetWorkAreaBoundsInScreen() const OVERRIDE;
+ virtual void SetInactiveRenderingDisabled(bool value) OVERRIDE;
+
+ protected:
+ // Modifies event coordinates to the targeted widget contained by this widget.
+ template<class Event> GdkEvent* TransformEvent(Event* event) {
+ GdkWindow* dest = GTK_WIDGET(window_contents_)->window;
+ if (event && event->window != dest) {
+ gint dest_x, dest_y;
+ gdk_window_get_root_origin(dest, &dest_x, &dest_y);
+ event->x = event->x_root - dest_x;
+ event->y = event->y_root - dest_y;
+ }
+ return reinterpret_cast<GdkEvent*>(event);
+ }
+
+ // Event handlers:
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnButtonPress,
+ GdkEventButton*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, void, OnSizeRequest,
+ GtkRequisition*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, void, OnSizeAllocate,
+ GtkAllocation*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnPaint,
+ GdkEventExpose*);
+ CHROMEGTK_VIRTUAL_CALLBACK_4(NativeWidgetGtk, void, OnDragDataGet,
+ GdkDragContext*, GtkSelectionData*, guint,
+ guint);
+ CHROMEGTK_VIRTUAL_CALLBACK_6(NativeWidgetGtk, void, OnDragDataReceived,
+ GdkDragContext*, gint, gint, GtkSelectionData*,
+ guint, guint);
+ CHROMEGTK_VIRTUAL_CALLBACK_4(NativeWidgetGtk, gboolean, OnDragDrop,
+ GdkDragContext*, gint, gint, guint);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, void, OnDragEnd,
+ GdkDragContext*);
+ CHROMEGTK_VIRTUAL_CALLBACK_2(NativeWidgetGtk, gboolean, OnDragFailed,
+ GdkDragContext*, GtkDragResult);
+ CHROMEGTK_VIRTUAL_CALLBACK_2(NativeWidgetGtk, void, OnDragLeave,
+ GdkDragContext*, guint);
+ CHROMEGTK_VIRTUAL_CALLBACK_4(NativeWidgetGtk, gboolean, OnDragMotion,
+ GdkDragContext*, gint, gint, guint);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnEnterNotify,
+ GdkEventCrossing*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnLeaveNotify,
+ GdkEventCrossing*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnMotionNotify,
+ GdkEventMotion*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnButtonRelease,
+ GdkEventButton*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnFocusIn,
+ GdkEventFocus*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnFocusOut,
+ GdkEventFocus*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnEventKey,
+ GdkEventKey*);
+ CHROMEGTK_VIRTUAL_CALLBACK_4(NativeWidgetGtk, gboolean, OnQueryTooltip, gint,
+ gint, gboolean, GtkTooltip*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnScroll,
+ GdkEventScroll*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnVisibilityNotify,
+ GdkEventVisibility*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnGrabBrokeEvent,
+ GdkEvent*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, void, OnGrabNotify, gboolean);
+ CHROMEGTK_VIRTUAL_CALLBACK_0(NativeWidgetGtk, void, OnDestroy);
+ CHROMEGTK_VIRTUAL_CALLBACK_0(NativeWidgetGtk, void, OnShow);
+ CHROMEGTK_VIRTUAL_CALLBACK_0(NativeWidgetGtk, void, OnMap);
+ CHROMEGTK_VIRTUAL_CALLBACK_0(NativeWidgetGtk, void, OnHide);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnWindowStateEvent,
+ GdkEventWindowState*);
+ CHROMEGTK_VIRTUAL_CALLBACK_1(NativeWidgetGtk, gboolean, OnConfigureEvent,
+ GdkEventConfigure*);
+
+ // Invoked when the widget is destroyed and right before the object
+ // destruction. Useful for overriding.
+ virtual void OnDestroyed(GObject *where_the_object_was);
+ static void OnDestroyedThunk(gpointer data, GObject *where_the_object_was) {
+ reinterpret_cast<NativeWidgetGtk*>(data)->OnDestroyed(where_the_object_was);
+ }
+
+ // Invoked when gtk grab is stolen by other GtkWidget in the same
+ // application.
+ virtual void HandleGtkGrabBroke();
+
+ const internal::NativeWidgetDelegate* delegate() const { return delegate_; }
+ internal::NativeWidgetDelegate* delegate() { return delegate_; }
+
+ private:
+ class DropObserver;
+ friend class DropObserver;
+
+ // Overridden from ui::CompositorDelegate
+ virtual void ScheduleDraw();
+
+ // Overridden from internal::InputMethodDelegate
+ virtual void DispatchKeyEventPostIME(const KeyEvent& key) OVERRIDE;
+
+ void SetInitParams(const Widget::InitParams& params);
+
+ // This is called only when the window is transparent.
+ CHROMEGTK_CALLBACK_1(NativeWidgetGtk, gboolean, OnWindowPaint,
+ GdkEventExpose*);
+
+ // Callbacks for expose event on child widgets. See the description of
+ // RegisterChildChildExposeHandler.
+ void OnChildExpose(GtkWidget* child);
+ static gboolean ChildExposeHandler(GtkWidget* widget, GdkEventExpose* event);
+
+ // Creates the GtkWidget.
+ void CreateGtkWidget(const Widget::InitParams& params);
+
+ // Invoked from create widget to enable the various bits needed for a
+ // transparent background. This is only invoked if MakeTransparent has been
+ // invoked.
+ void ConfigureWidgetForTransparentBackground(GtkWidget* parent);
+
+ // Invoked from create widget to enable the various bits needed for a
+ // window which doesn't receive events.
+ void ConfigureWidgetForIgnoreEvents();
+
+ // A utility function to draw a transparent background onto the |widget|.
+ static void DrawTransparentBackground(GtkWidget* widget,
+ GdkEventExpose* event);
+
+ // Asks the delegate if any to save the window's location and size.
+ void SaveWindowPosition();
+
+ // A delegate implementation that handles events received here.
+ // See class documentation for Widget in widget.h for a note about ownership.
+ internal::NativeWidgetDelegate* delegate_;
+
+ // Our native views. If we're a window/popup, then widget_ is the window and
+ // window_contents_ is a GtkFixed. If we're not a window/popup, then widget_
+ // and window_contents_ point to the same GtkFixed.
+ GtkWidget* widget_;
+ GtkWidget* window_contents_;
+
+ // Child GtkWidgets created with no parent need to be parented to a valid top
+ // level window otherwise Gtk throws a fit. |null_parent_| is an invisible
+ // popup that such GtkWidgets are parented to.
+ static GtkWidget* null_parent_;
+
+ // True if the widget is a child of some other widget.
+ bool child_;
+
+ // The TooltipManager.
+ // WARNING: RootView's destructor calls into the TooltipManager. As such, this
+ // must be destroyed AFTER root_view_.
+ scoped_ptr<TooltipManager> tooltip_manager_;
+
+ scoped_ptr<DropTargetGtk> drop_target_;
+
+ // The following factory is used to delay destruction.
+ base::WeakPtrFactory<NativeWidgetGtk> close_widget_factory_;
+
+ // See class documentation for Widget in widget.h for a note about ownership.
+ Widget::InitParams::Ownership ownership_;
+
+ // See description above make_transparent for details.
+ bool transparent_;
+
+ // Makes the window pass all events through to any windows behind it.
+ // Set during SetInitParams before the widget is created. The actual work of
+ // making the window ignore events is done by ConfigureWidgetForIgnoreEvents.
+ bool ignore_events_;
+
+ // See note in DropObserver for details on this.
+ bool ignore_drag_leave_;
+
+ unsigned char opacity_;
+
+ // This is non-null during the life of DoDrag and contains the actual data
+ // for the drag.
+ const OSExchangeDataProviderGtk* drag_data_;
+
+ // True to enable debug painting. Enabling causes the damaged
+ // region to be painted to flash in red.
+ static bool debug_paint_enabled_;
+
+ // State of the window, such as fullscreen, hidden...
+ GdkWindowState window_state_;
+
+ // Are we active?
+ bool is_active_;
+
+ // See make_transient_to_parent for a description.
+ bool transient_to_parent_;
+
+ // Last size supplied to OnSizeAllocate. We cache this as any time the
+ // size of a GtkWidget changes size_allocate is called, even if the size
+ // didn't change. If we didn't cache this and ignore calls when the size
+ // hasn't changed, we can end up getting stuck in a never ending loop.
+ gfx::Size size_;
+
+ // This is initially false and when the first focus-in event is received this
+ // is set to true and no additional processing is done. Subsequently when
+ // focus-in is received we do the normal focus manager processing.
+ //
+ // This behavior is necessitated by Gtk/X sending focus events
+ // asynchronously. The initial sequence for windows is typically: show,
+ // request focus on some widget. Because of async events on Gtk this becomes
+ // show, request focus, get focus in event which ends up clearing focus
+ // (first request to FocusManager::RestoreFocusedView ends up clearing focus).
+ bool got_initial_focus_in_;
+
+ // If true, we've received a focus-in event. If false we've received a
+ // focus-out event. We can get multiple focus-out events in a row, we use
+ // this to determine whether we should process the event.
+ bool has_focus_;
+
+ // If true, the window stays on top of the screen. This is only used
+ // for types other than TYPE_CHILD.
+ bool always_on_top_;
+
+ // If true, we enable the content widget's double buffering.
+ // This is false by default.
+ bool is_double_buffered_;
+
+ // Indicates if we should handle the upcoming Alt key release event.
+ bool should_handle_menu_key_release_;
+
+ // Valid for the lifetime of StartDragForViewFromMouseEvent, indicates the
+ // view the drag started from.
+ View* dragged_view_;
+
+ // If the widget has ever been painted. This is used to guarantee
+ // that window manager shows the window only after the window is painted.
+ bool painted_;
+
+ // The compositor for accelerated drawing.
+ scoped_refptr<ui::Compositor> compositor_;
+
+ // Have we done a pointer grab?
+ bool has_pointer_grab_;
+
+ // Have we done a keyboard grab?
+ bool has_keyboard_grab_;
+
+ // ID of the 'grab-notify' signal. If non-zero we're listening for
+ // 'grab-notify' events.
+ glong grab_notify_signal_id_;
+
+ // If we were created for a menu.
+ bool is_menu_;
+
+ scoped_ptr<ui::GtkSignalRegistrar> signal_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeWidgetGtk);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_NATIVE_WIDGET_GTK_H_
diff --git a/views/widget/native_widget_private.h b/views/widget/native_widget_private.h
new file mode 100644
index 0000000..df5bad3
--- /dev/null
+++ b/views/widget/native_widget_private.h
@@ -0,0 +1,216 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_NATIVE_WIDGET_PRIVATE_H_
+#define VIEWS_WIDGET_NATIVE_WIDGET_PRIVATE_H_
+#pragma once
+
+#include "base/string16.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/views/ime/input_method_delegate.h"
+#include "views/widget/native_widget.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+class OSExchangeData;
+}
+
+namespace views {
+class InputMethod;
+class TooltipManager;
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetPrivate interface
+//
+// A NativeWidget subclass internal to views that provides Widget a conduit for
+// communication with a backend-specific native widget implementation.
+//
+// Many of the methods here are pass-thrus for Widget, and as such there is no
+// documentation for them here. In that case, see methods of the same name in
+// widget.h.
+//
+// IMPORTANT: This type is intended for use only by the views system and for
+// NativeWidget implementations. This file should not be included
+// in code that does not fall into one of these use cases.
+//
+class VIEWS_EXPORT NativeWidgetPrivate : public NativeWidget,
+ public internal::InputMethodDelegate {
+ public:
+ virtual ~NativeWidgetPrivate() {}
+
+ // Creates an appropriate default NativeWidgetPrivate implementation for the
+ // current OS/circumstance.
+ static NativeWidgetPrivate* CreateNativeWidget(
+ internal::NativeWidgetDelegate* delegate);
+
+ static NativeWidgetPrivate* GetNativeWidgetForNativeView(
+ gfx::NativeView native_view);
+ static NativeWidgetPrivate* GetNativeWidgetForNativeWindow(
+ gfx::NativeWindow native_window);
+
+ // Retrieves the top NativeWidgetPrivate in the hierarchy containing the given
+ // NativeView, or NULL if there is no NativeWidgetPrivate that contains it.
+ static NativeWidgetPrivate* GetTopLevelNativeWidget(
+ gfx::NativeView native_view);
+
+ static void GetAllChildWidgets(gfx::NativeView native_view,
+ Widget::Widgets* children);
+ static void ReparentNativeView(gfx::NativeView native_view,
+ gfx::NativeView new_parent);
+
+ // Returns true if any mouse button is currently down.
+ static bool IsMouseButtonDown();
+
+ // Initializes the NativeWidget.
+ virtual void InitNativeWidget(const Widget::InitParams& params) = 0;
+
+ // Returns a NonClientFrameView for the widget's NonClientView, or NULL if
+ // the NativeWidget wants no special NonClientFrameView.
+ virtual NonClientFrameView* CreateNonClientFrameView() = 0;
+
+ virtual void UpdateFrameAfterFrameChange() = 0;
+ virtual bool ShouldUseNativeFrame() const = 0;
+ virtual void FrameTypeChanged() = 0;
+
+ // Returns the Widget associated with this NativeWidget. This function is
+ // guaranteed to return non-NULL for the lifetime of the NativeWidget.
+ virtual Widget* GetWidget() = 0;
+ virtual const Widget* GetWidget() const = 0;
+
+ // Returns the NativeView/Window associated with this NativeWidget.
+ virtual gfx::NativeView GetNativeView() const = 0;
+ virtual gfx::NativeWindow GetNativeWindow() const = 0;
+
+ // Returns the topmost Widget in a hierarchy.
+ virtual Widget* GetTopLevelWidget() = 0;
+
+ // Returns the Compositor, or NULL if there isn't one associated with this
+ // NativeWidget.
+ virtual const ui::Compositor* GetCompositor() const = 0;
+ virtual ui::Compositor* GetCompositor() = 0;
+
+ // See description in View for details.
+ virtual void CalculateOffsetToAncestorWithLayer(gfx::Point* offset,
+ ui::Layer** layer_parent) = 0;
+ virtual void ReorderLayers() = 0;
+
+ // Notifies the NativeWidget that a view was removed from the Widget's view
+ // hierarchy.
+ virtual void ViewRemoved(View* view) = 0;
+
+ // Sets/Gets a native window property on the underlying native window object.
+ // Returns NULL if the property does not exist. Setting the property value to
+ // NULL removes the property.
+ virtual void SetNativeWindowProperty(const char* name, void* value) = 0;
+ virtual void* GetNativeWindowProperty(const char* name) const = 0;
+
+ // Returns the native widget's tooltip manager. Called from the View hierarchy
+ // to update tooltips.
+ virtual TooltipManager* GetTooltipManager() const = 0;
+
+ // Returns true if a system screen reader is active for the NativeWidget.
+ virtual bool IsScreenReaderActive() const = 0;
+
+ // Notify native Accessibility clients of an event.
+ virtual void SendNativeAccessibilityEvent(
+ View* view,
+ ui::AccessibilityTypes::Event event_type) = 0;
+
+ // Sets or releases event capturing for this native widget.
+ virtual void SetMouseCapture() = 0;
+ virtual void ReleaseMouseCapture() = 0;
+
+ // Returns true if this native widget is capturing mouse events.
+ virtual bool HasMouseCapture() const = 0;
+
+ // Returns the InputMethod for this native widget.
+ // Note that all widgets in a widget hierarchy share the same input method.
+ // TODO(suzhe): rename to GetInputMethod() when NativeWidget implementation
+ // class doesn't inherit Widget anymore.
+ virtual InputMethod* CreateInputMethod() = 0;
+
+
+ // Centers the window and sizes it to the specified size.
+ virtual void CenterWindow(const gfx::Size& size) = 0;
+
+ // Retrieves the window's current restored bounds and "show" state, for
+ // persisting.
+ virtual void GetWindowPlacement(
+ gfx::Rect* bounds,
+ ui::WindowShowState* show_state) const = 0;
+
+ // Sets the NativeWindow title.
+ virtual void SetWindowTitle(const string16& title) = 0;
+
+ // Sets the Window icons. |window_icon| is a 16x16 icon suitable for use in
+ // a title bar. |app_icon| is a larger size for use in the host environment
+ // app switching UI.
+ virtual void SetWindowIcons(const SkBitmap& window_icon,
+ const SkBitmap& app_icon) = 0;
+
+ // Update native accessibility properties on the native window.
+ virtual void SetAccessibleName(const string16& name) = 0;
+ virtual void SetAccessibleRole(ui::AccessibilityTypes::Role role) = 0;
+ virtual void SetAccessibleState(ui::AccessibilityTypes::State state) = 0;
+
+ // Makes the NativeWindow modal.
+ virtual void BecomeModal() = 0;
+
+ // See method documentation in Widget.
+ virtual gfx::Rect GetWindowScreenBounds() const = 0;
+ virtual gfx::Rect GetClientAreaScreenBounds() const = 0;
+ virtual gfx::Rect GetRestoredBounds() const = 0;
+ virtual void SetBounds(const gfx::Rect& bounds) = 0;
+ virtual void SetSize(const gfx::Size& size) = 0;
+ virtual void MoveAbove(gfx::NativeView native_view) = 0;
+ virtual void MoveToTop() = 0;
+ virtual void SetShape(gfx::NativeRegion shape) = 0;
+ virtual void Close() = 0;
+ virtual void CloseNow() = 0;
+ virtual void EnableClose(bool enable) = 0;
+ virtual void Show() = 0;
+ virtual void Hide() = 0;
+ // Invoked if the initial show should maximize the window. |restored_bounds|
+ // is the bounds of the window when not maximized.
+ virtual void ShowMaximizedWithBounds(const gfx::Rect& restored_bounds) = 0;
+ virtual void ShowWithWindowState(ui::WindowShowState show_state) = 0;
+ virtual bool IsVisible() const = 0;
+ virtual void Activate() = 0;
+ virtual void Deactivate() = 0;
+ virtual bool IsActive() const = 0;
+ virtual void SetAlwaysOnTop(bool always_on_top) = 0;
+ virtual void Maximize() = 0;
+ virtual void Minimize() = 0;
+ virtual bool IsMaximized() const = 0;
+ virtual bool IsMinimized() const = 0;
+ virtual void Restore() = 0;
+ virtual void SetFullscreen(bool fullscreen) = 0;
+ virtual bool IsFullscreen() const = 0;
+ virtual void SetOpacity(unsigned char opacity) = 0;
+ virtual void SetUseDragFrame(bool use_drag_frame) = 0;
+ virtual bool IsAccessibleWidget() const = 0;
+ virtual void RunShellDrag(View* view,
+ const ui::OSExchangeData& data,
+ int operation) = 0;
+ virtual void SchedulePaintInRect(const gfx::Rect& rect) = 0;
+ virtual void SetCursor(gfx::NativeCursor cursor) = 0;
+ virtual void ClearNativeFocus() = 0;
+ virtual void FocusNativeView(gfx::NativeView native_view) = 0;
+ virtual bool ConvertPointFromAncestor(
+ const Widget* ancestor, gfx::Point* point) const = 0;
+ virtual gfx::Rect GetWorkAreaBoundsInScreen() const = 0;
+ virtual void SetInactiveRenderingDisabled(bool value) = 0;
+
+ // Overridden from NativeWidget:
+ virtual internal::NativeWidgetPrivate* AsNativeWidgetPrivate() OVERRIDE;
+};
+
+} // namespace internal
+} // namespace views
+
+#endif // VIEWS_WIDGET_NATIVE_WIDGET_PRIVATE_H_
diff --git a/views/widget/native_widget_test_utils.h b/views/widget/native_widget_test_utils.h
new file mode 100644
index 0000000..9fe73f6
--- /dev/null
+++ b/views/widget/native_widget_test_utils.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_NATIVE_WIDGET_TEST_UTILS_H_
+#define VIEWS_WIDGET_NATIVE_WIDGET_TEST_UTILS_H_
+#pragma once
+
+namespace views {
+namespace internal {
+
+class NativeWidgetPrivate;
+
+// Create dummy widgets for use in testing. Caller owns the returned
+// |NativeWidgetPrivate| object which, in turn, owns the associated Widget.
+NativeWidgetPrivate* CreateNativeWidget();
+NativeWidgetPrivate* CreateNativeSubWidget();
+
+} // namespace internal
+} // namespace views
+
+#endif // VIEWS_WIDGET_NATIVE_WIDGET_TEST_UTILS_H_
diff --git a/views/widget/native_widget_test_utils_aura.cc b/views/widget/native_widget_test_utils_aura.cc
new file mode 100644
index 0000000..7e8926a
--- /dev/null
+++ b/views/widget/native_widget_test_utils_aura.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 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/native_widget_test_utils.h"
+
+#include "views/view.h"
+#include "views/widget/native_widget_private.h"
+#include "views/widget/widget.h"
+
+namespace views {
+namespace internal {
+
+namespace {
+
+NativeWidgetPrivate* CreateNativeWidgetOfType(Widget::InitParams::Type type) {
+ Widget* widget = new Widget;
+ Widget::InitParams params(type);
+ params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
+ params.child = false; // Implicitly set to true by ctor with TYPE_CONTROL.
+ params.bounds = gfx::Rect(10, 10, 200, 200);
+ widget->Init(params);
+ return widget->native_widget_private();
+}
+
+} // namespace
+
+NativeWidgetPrivate* CreateNativeWidget() {
+ return CreateNativeWidgetOfType(Widget::InitParams::TYPE_POPUP);
+}
+
+NativeWidgetPrivate* CreateNativeSubWidget() {
+ return CreateNativeWidgetOfType(Widget::InitParams::TYPE_CONTROL);
+}
+
+} // namespace internal
+} // namespace ui
diff --git a/views/widget/native_widget_test_utils_gtk.cc b/views/widget/native_widget_test_utils_gtk.cc
new file mode 100644
index 0000000..3217f99
--- /dev/null
+++ b/views/widget/native_widget_test_utils_gtk.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 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/native_widget_test_utils.h"
+
+#include "views/view.h"
+#include "views/widget/native_widget_private.h"
+#include "views/widget/widget.h"
+
+namespace views {
+namespace internal {
+
+namespace {
+
+NativeWidgetPrivate* CreateNativeWidgetOfType(Widget::InitParams::Type type) {
+ Widget* widget = new Widget;
+ Widget::InitParams params(type);
+ params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
+ params.bounds = gfx::Rect(10, 10, 200, 200);
+ widget->Init(params);
+ return widget->native_widget_private();
+}
+
+} // namespace
+
+NativeWidgetPrivate* CreateNativeWidget() {
+ return CreateNativeWidgetOfType(Widget::InitParams::TYPE_POPUP);
+}
+
+NativeWidgetPrivate* CreateNativeSubWidget() {
+ return CreateNativeWidgetOfType(Widget::InitParams::TYPE_CONTROL);
+}
+
+} // namespace internal
+} // namespace ui
diff --git a/views/widget/native_widget_test_utils_win.cc b/views/widget/native_widget_test_utils_win.cc
new file mode 100644
index 0000000..7e8926a
--- /dev/null
+++ b/views/widget/native_widget_test_utils_win.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 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/native_widget_test_utils.h"
+
+#include "views/view.h"
+#include "views/widget/native_widget_private.h"
+#include "views/widget/widget.h"
+
+namespace views {
+namespace internal {
+
+namespace {
+
+NativeWidgetPrivate* CreateNativeWidgetOfType(Widget::InitParams::Type type) {
+ Widget* widget = new Widget;
+ Widget::InitParams params(type);
+ params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
+ params.child = false; // Implicitly set to true by ctor with TYPE_CONTROL.
+ params.bounds = gfx::Rect(10, 10, 200, 200);
+ widget->Init(params);
+ return widget->native_widget_private();
+}
+
+} // namespace
+
+NativeWidgetPrivate* CreateNativeWidget() {
+ return CreateNativeWidgetOfType(Widget::InitParams::TYPE_POPUP);
+}
+
+NativeWidgetPrivate* CreateNativeSubWidget() {
+ return CreateNativeWidgetOfType(Widget::InitParams::TYPE_CONTROL);
+}
+
+} // namespace internal
+} // namespace ui
diff --git a/views/widget/native_widget_unittest.cc b/views/widget/native_widget_unittest.cc
new file mode 100644
index 0000000..7fc3818
--- /dev/null
+++ b/views/widget/native_widget_unittest.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2011 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 "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/test/views_test_base.h"
+#include "views/controls/native/native_view_host.h"
+#include "views/view.h"
+#include "views/widget/native_widget_private.h"
+#include "views/widget/native_widget_test_utils.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+class ScopedTestWidget {
+ public:
+ ScopedTestWidget(internal::NativeWidgetPrivate* native_widget)
+ : native_widget_(native_widget) {
+ }
+ ~ScopedTestWidget() {
+ // |CloseNow| deletes both |native_widget_| and its associated
+ // |Widget|.
+ native_widget_->GetWidget()->CloseNow();
+ }
+
+ internal::NativeWidgetPrivate* operator->() const {
+ return native_widget_;
+ }
+ internal::NativeWidgetPrivate* get() const { return native_widget_; }
+
+ private:
+ internal::NativeWidgetPrivate* native_widget_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedTestWidget);
+};
+
+class NativeWidgetTest : public ViewsTestBase {
+ public:
+ NativeWidgetTest() {}
+ virtual ~NativeWidgetTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NativeWidgetTest);
+};
+
+TEST_F(NativeWidgetTest, CreateNativeWidget) {
+ ScopedTestWidget widget(internal::CreateNativeWidget());
+ EXPECT_TRUE(widget->GetWidget()->GetNativeView() != NULL);
+}
+
+TEST_F(NativeWidgetTest, GetNativeWidgetForNativeView) {
+ ScopedTestWidget widget(internal::CreateNativeWidget());
+ EXPECT_EQ(widget.get(),
+ internal::NativeWidgetPrivate::GetNativeWidgetForNativeView(
+ widget->GetWidget()->GetNativeView()));
+}
+
+// |widget| has the toplevel NativeWidget.
+TEST_F(NativeWidgetTest, GetTopLevelNativeWidget1) {
+ ScopedTestWidget widget(internal::CreateNativeWidget());
+ EXPECT_EQ(widget.get(),
+ internal::NativeWidgetPrivate::GetTopLevelNativeWidget(
+ widget->GetWidget()->GetNativeView()));
+}
+
+// |toplevel_widget| has the toplevel NativeWidget.
+TEST_F(NativeWidgetTest, GetTopLevelNativeWidget2) {
+ ScopedTestWidget toplevel_widget(internal::CreateNativeWidget());
+
+ // |toplevel_widget| owns |child_host|.
+ NativeViewHost* child_host = new NativeViewHost;
+ toplevel_widget->GetWidget()->SetContentsView(child_host);
+
+ // |child_host| owns |child_widget|.
+ internal::NativeWidgetPrivate* child_widget =
+ internal::CreateNativeSubWidget();
+ child_host->Attach(child_widget->GetWidget()->GetNativeView());
+
+ EXPECT_EQ(toplevel_widget.get(),
+ internal::NativeWidgetPrivate::GetTopLevelNativeWidget(
+ child_widget->GetWidget()->GetNativeView()));
+}
+
+} // namespace views
diff --git a/views/widget/native_widget_win.cc b/views/widget/native_widget_win.cc
new file mode 100644
index 0000000..7c013f9
--- /dev/null
+++ b/views/widget/native_widget_win.cc
@@ -0,0 +1,2524 @@
+// Copyright (c) 2011 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/native_widget_win.h"
+
+#include <dwmapi.h>
+#include <shellapi.h>
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/string_util.h"
+#include "base/system_monitor/system_monitor.h"
+#include "base/win/scoped_gdi_object.h"
+#include "base/win/win_util.h"
+#include "base/win/windows_version.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/dragdrop/drag_source.h"
+#include "ui/base/dragdrop/os_exchange_data.h"
+#include "ui/base/dragdrop/os_exchange_data_provider_win.h"
+#include "ui/base/keycodes/keyboard_code_conversion_win.h"
+#include "ui/base/l10n/l10n_util_win.h"
+#include "ui/base/theme_provider.h"
+#include "ui/base/view_prop.h"
+#include "ui/base/win/hwnd_util.h"
+#include "ui/base/win/mouse_wheel_util.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/canvas_skia_paint.h"
+#include "ui/gfx/compositor/compositor.h"
+#include "ui/gfx/icon_util.h"
+#include "ui/gfx/native_theme_win.h"
+#include "ui/gfx/path.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/accessibility/native_view_accessibility_win.h"
+#include "ui/views/focus/accelerator_handler.h"
+#include "ui/views/focus/view_storage.h"
+#include "ui/views/ime/input_method_win.h"
+#include "ui/views/window/native_frame_view.h"
+#include "views/controls/native_control_win.h"
+#include "views/controls/textfield/native_textfield_views.h"
+#include "views/views_delegate.h"
+#include "views/widget/aero_tooltip_manager.h"
+#include "views/widget/child_window_message_processor.h"
+#include "views/widget/drop_target_win.h"
+#include "views/widget/monitor_win.h"
+#include "views/widget/native_widget_delegate.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget_delegate.h"
+
+#pragma comment(lib, "dwmapi.lib")
+
+using ui::ViewProp;
+
+namespace views {
+
+namespace {
+
+// Get the source HWND of the specified message. Depending on the message, the
+// source HWND is encoded in either the WPARAM or the LPARAM value.
+HWND GetControlHWNDForMessage(UINT message, WPARAM w_param, LPARAM l_param) {
+ // Each of the following messages can be sent by a child HWND and must be
+ // forwarded to its associated NativeControlWin for handling.
+ switch (message) {
+ case WM_NOTIFY:
+ return reinterpret_cast<NMHDR*>(l_param)->hwndFrom;
+ case WM_COMMAND:
+ return reinterpret_cast<HWND>(l_param);
+ case WM_CONTEXTMENU:
+ return reinterpret_cast<HWND>(w_param);
+ case WM_CTLCOLORBTN:
+ case WM_CTLCOLORSTATIC:
+ return reinterpret_cast<HWND>(l_param);
+ }
+ return NULL;
+}
+
+// Some messages may be sent to us by a child HWND. If this is the case, this
+// function will forward those messages on to the object associated with the
+// source HWND and return true, in which case the window procedure must not do
+// any further processing of the message. If there is no associated
+// ChildWindowMessageProcessor, the return value will be false and the WndProc
+// can continue processing the message normally. |l_result| contains the result
+// of the message processing by the control and must be returned by the WndProc
+// if the return value is true.
+bool ProcessChildWindowMessage(UINT message,
+ WPARAM w_param,
+ LPARAM l_param,
+ LRESULT* l_result) {
+ *l_result = 0;
+
+ HWND control_hwnd = GetControlHWNDForMessage(message, w_param, l_param);
+ if (IsWindow(control_hwnd)) {
+ ChildWindowMessageProcessor* processor =
+ ChildWindowMessageProcessor::Get(control_hwnd);
+ if (processor)
+ return processor->ProcessMessage(message, w_param, l_param, l_result);
+ }
+
+ return false;
+}
+
+// Enumeration callback for NativeWidget::GetAllChildWidgets(). Called for each
+// child HWND beneath the original HWND.
+BOOL CALLBACK EnumerateChildWindowsForNativeWidgets(HWND hwnd, LPARAM l_param) {
+ Widget* widget = Widget::GetWidgetForNativeView(hwnd);
+ if (widget) {
+ Widget::Widgets* widgets = reinterpret_cast<Widget::Widgets*>(l_param);
+ widgets->insert(widget);
+ }
+ return TRUE;
+}
+
+// Returns true if the WINDOWPOS data provided indicates the client area of
+// the window may have changed size. This can be caused by the window being
+// resized or its frame changing.
+bool DidClientAreaSizeChange(const WINDOWPOS* window_pos) {
+ return !(window_pos->flags & SWP_NOSIZE) ||
+ window_pos->flags & SWP_FRAMECHANGED;
+}
+
+// Callback used to notify child windows that the top level window received a
+// DWMCompositionChanged message.
+BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) {
+ SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0);
+ return TRUE;
+}
+
+// Tells the window its frame (non-client area) has changed.
+void SendFrameChanged(HWND window) {
+ SetWindowPos(window, NULL, 0, 0, 0, 0,
+ SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS |
+ SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREPOSITION |
+ SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER);
+}
+
+// Enables or disables the menu item for the specified command and menu.
+void EnableMenuItem(HMENU menu, UINT command, bool enabled) {
+ UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
+ EnableMenuItem(menu, command, flags);
+}
+
+BOOL CALLBACK EnumChildWindowsForRedraw(HWND hwnd, LPARAM lparam) {
+ DWORD process_id;
+ GetWindowThreadProcessId(hwnd, &process_id);
+ int flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME;
+ if (process_id == GetCurrentProcessId())
+ flags |= RDW_UPDATENOW;
+ RedrawWindow(hwnd, NULL, NULL, flags);
+ return TRUE;
+}
+
+// See comments in OnNCPaint() for details of this struct.
+struct ClipState {
+ // The window being painted.
+ HWND parent;
+
+ // DC painting to.
+ HDC dc;
+
+ // Origin of the window in terms of the screen.
+ int x;
+ int y;
+};
+
+// See comments in OnNCPaint() for details of this function.
+static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) {
+ ClipState* clip_state = reinterpret_cast<ClipState*>(param);
+ if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) {
+ RECT bounds;
+ GetWindowRect(window, &bounds);
+ ExcludeClipRect(clip_state->dc,
+ bounds.left - clip_state->x,
+ bounds.top - clip_state->y,
+ bounds.right - clip_state->x,
+ bounds.bottom - clip_state->y);
+ }
+ return TRUE;
+}
+
+// The thickness of an auto-hide taskbar in pixels.
+static const int kAutoHideTaskbarThicknessPx = 2;
+
+bool GetMonitorAndRects(const RECT& rect,
+ HMONITOR* monitor,
+ gfx::Rect* monitor_rect,
+ gfx::Rect* work_area) {
+ DCHECK(monitor);
+ DCHECK(monitor_rect);
+ DCHECK(work_area);
+ *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
+ if (!*monitor)
+ return false;
+ MONITORINFO monitor_info = { 0 };
+ monitor_info.cbSize = sizeof(monitor_info);
+ GetMonitorInfo(*monitor, &monitor_info);
+ *monitor_rect = monitor_info.rcMonitor;
+ *work_area = monitor_info.rcWork;
+ return true;
+}
+
+// Links the HWND to its NativeWidget.
+const char* const kNativeWidgetKey = "__VIEWS_NATIVE_WIDGET__";
+
+// A custom MSAA object id used to determine if a screen reader is actively
+// listening for MSAA events.
+const int kCustomObjectID = 1;
+
+const int kDragFrameWindowAlpha = 200;
+
+} // namespace
+
+// static
+bool NativeWidgetWin::screen_reader_active_ = false;
+
+// A scoping class that prevents a window from being able to redraw in response
+// to invalidations that may occur within it for the lifetime of the object.
+//
+// Why would we want such a thing? Well, it turns out Windows has some
+// "unorthodox" behavior when it comes to painting its non-client areas.
+// Occasionally, Windows will paint portions of the default non-client area
+// right over the top of the custom frame. This is not simply fixed by handling
+// WM_NCPAINT/WM_PAINT, with some investigation it turns out that this
+// rendering is being done *inside* the default implementation of some message
+// handlers and functions:
+// . WM_SETTEXT
+// . WM_SETICON
+// . WM_NCLBUTTONDOWN
+// . EnableMenuItem, called from our WM_INITMENU handler
+// The solution is to handle these messages and call DefWindowProc ourselves,
+// but prevent the window from being able to update itself for the duration of
+// the call. We do this with this class, which automatically calls its
+// associated Window's lock and unlock functions as it is created and destroyed.
+// See documentation in those methods for the technique used.
+//
+// The lock only has an effect if the window was visible upon lock creation, as
+// it doesn't guard against direct visiblility changes, and multiple locks may
+// exist simultaneously to handle certain nested Windows messages.
+//
+// IMPORTANT: Do not use this scoping object for large scopes or periods of
+// time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh).
+//
+// I would love to hear Raymond Chen's explanation for all this. And maybe a
+// list of other messages that this applies to ;-)
+class NativeWidgetWin::ScopedRedrawLock {
+ public:
+ explicit ScopedRedrawLock(NativeWidgetWin* widget)
+ : widget_(widget),
+ native_view_(widget_->GetNativeView()),
+ was_visible_(widget_->IsVisible()),
+ cancel_unlock_(false) {
+ if (was_visible_ && ::IsWindow(native_view_))
+ widget_->LockUpdates();
+ }
+
+ ~ScopedRedrawLock() {
+ if (!cancel_unlock_ && was_visible_ && ::IsWindow(native_view_))
+ widget_->UnlockUpdates();
+ }
+
+ // Cancel the unlock operation, call this if the Widget is being destroyed.
+ void CancelUnlockOperation() { cancel_unlock_ = true; }
+
+ private:
+ // The widget having its style changed.
+ NativeWidgetWin* widget_;
+ // The widget's native view, cached to avoid action after window destruction.
+ gfx::NativeView native_view_;
+ // Records the widget visibility at the time of creation.
+ bool was_visible_;
+ // A flag indicating that the unlock operation was canceled.
+ bool cancel_unlock_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetWin, public:
+
+NativeWidgetWin::NativeWidgetWin(internal::NativeWidgetDelegate* delegate)
+ : delegate_(delegate),
+ ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)),
+ active_mouse_tracking_flags_(0),
+ use_layered_buffer_(false),
+ layered_alpha_(255),
+ ALLOW_THIS_IN_INITIALIZER_LIST(paint_layered_window_factory_(this)),
+ ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET),
+ can_update_layered_window_(true),
+ restore_focus_when_enabled_(false),
+ accessibility_view_events_index_(-1),
+ accessibility_view_events_(kMaxAccessibilityViewEvents),
+ previous_cursor_(NULL),
+ fullscreen_(false),
+ force_hidden_count_(0),
+ lock_updates_count_(0),
+ saved_window_style_(0),
+ ignore_window_pos_changes_(false),
+ ALLOW_THIS_IN_INITIALIZER_LIST(ignore_pos_changes_factory_(this)),
+ last_monitor_(NULL),
+ is_right_mouse_pressed_on_caption_(false),
+ restored_enabled_(false),
+ destroyed_(NULL),
+ has_non_client_view_(false) {
+}
+
+NativeWidgetWin::~NativeWidgetWin() {
+ if (destroyed_ != NULL)
+ *destroyed_ = true;
+
+ if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
+ delete delegate_;
+ else
+ CloseNow();
+}
+
+// static
+bool NativeWidgetWin::IsAeroGlassEnabled() {
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return false;
+ // If composition is not enabled, we behave like on XP.
+ BOOL enabled = FALSE;
+ return SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled;
+}
+
+// static
+gfx::Font NativeWidgetWin::GetWindowTitleFont() {
+ NONCLIENTMETRICS ncm;
+ base::win::GetNonClientMetrics(&ncm);
+ l10n_util::AdjustUIFont(&(ncm.lfCaptionFont));
+ base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont)));
+ return gfx::Font(caption_font);
+}
+
+void NativeWidgetWin::Show(int show_state) {
+ ShowWindow(show_state);
+ // When launched from certain programs like bash and Windows Live Messenger,
+ // show_state is set to SW_HIDE, so we need to correct that condition. We
+ // don't just change show_state to SW_SHOWNORMAL because MSDN says we must
+ // always first call ShowWindow with the specified value from STARTUPINFO,
+ // otherwise all future ShowWindow calls will be ignored (!!#@@#!). Instead,
+ // we call ShowWindow again in this case.
+ if (show_state == SW_HIDE) {
+ show_state = SW_SHOWNORMAL;
+ ShowWindow(show_state);
+ }
+
+ // We need to explicitly activate the window if we've been shown with a state
+ // that should activate, because if we're opened from a desktop shortcut while
+ // an existing window is already running it doesn't seem to be enough to use
+ // one of these flags to activate the window.
+ if (show_state == SW_SHOWNORMAL || show_state == SW_SHOWMAXIMIZED)
+ Activate();
+
+ SetInitialFocus();
+}
+
+View* NativeWidgetWin::GetAccessibilityViewEventAt(int id) {
+ // Convert from MSAA child id.
+ id = -(id + 1);
+ DCHECK(id >= 0 && id < kMaxAccessibilityViewEvents);
+ return accessibility_view_events_[id];
+}
+
+int NativeWidgetWin::AddAccessibilityViewEvent(View* view) {
+ accessibility_view_events_index_ =
+ (accessibility_view_events_index_ + 1) % kMaxAccessibilityViewEvents;
+ accessibility_view_events_[accessibility_view_events_index_] = view;
+
+ // Convert to MSAA child id.
+ return -(accessibility_view_events_index_ + 1);
+}
+
+void NativeWidgetWin::ClearAccessibilityViewEvent(View* view) {
+ for (std::vector<View*>::iterator it = accessibility_view_events_.begin();
+ it != accessibility_view_events_.end();
+ ++it) {
+ if (*it == view)
+ *it = NULL;
+ }
+}
+
+void NativeWidgetWin::PushForceHidden() {
+ if (force_hidden_count_++ == 0)
+ Hide();
+}
+
+void NativeWidgetWin::PopForceHidden() {
+ if (--force_hidden_count_ <= 0) {
+ force_hidden_count_ = 0;
+ ShowWindow(SW_SHOW);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetWin, CompositorDelegate implementation:
+
+void NativeWidgetWin::ScheduleDraw() {
+ RECT rect;
+ ::GetClientRect(GetNativeView(), &rect);
+ InvalidateRect(GetNativeView(), &rect, FALSE);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetWin, NativeWidget implementation:
+
+void NativeWidgetWin::InitNativeWidget(const Widget::InitParams& params) {
+ SetInitParams(params);
+
+ GetMonitorAndRects(params.bounds.ToRECT(), &last_monitor_,
+ &last_monitor_rect_, &last_work_area_);
+
+ // Create the window.
+ WindowImpl::Init(params.GetParent(), params.bounds);
+}
+
+NonClientFrameView* NativeWidgetWin::CreateNonClientFrameView() {
+ return GetWidget()->ShouldUseNativeFrame() ?
+ new NativeFrameView(GetWidget()) : NULL;
+}
+
+void NativeWidgetWin::UpdateFrameAfterFrameChange() {
+ // We've either gained or lost a custom window region, so reset it now.
+ ResetWindowRegion(true);
+}
+
+bool NativeWidgetWin::ShouldUseNativeFrame() const {
+ return IsAeroGlassEnabled();
+}
+
+void NativeWidgetWin::FrameTypeChanged() {
+ // Called when the frame type could possibly be changing (theme change or
+ // DWM composition change).
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
+ // We need to toggle the rendering policy of the DWM/glass frame as we
+ // change from opaque to glass. "Non client rendering enabled" means that
+ // the DWM's glass non-client rendering is enabled, which is why
+ // DWMNCRP_ENABLED is used for the native frame case. _DISABLED means the
+ // DWM doesn't render glass, and so is used in the custom frame case.
+ DWMNCRENDERINGPOLICY policy = GetWidget()->ShouldUseNativeFrame() ?
+ DWMNCRP_ENABLED : DWMNCRP_DISABLED;
+ DwmSetWindowAttribute(GetNativeView(), DWMWA_NCRENDERING_POLICY,
+ &policy, sizeof(DWMNCRENDERINGPOLICY));
+ }
+
+ // Send a frame change notification, since the non-client metrics have
+ // changed.
+ SendFrameChanged(GetNativeView());
+
+ // Update the non-client view with the correct frame view for the active frame
+ // type.
+ GetWidget()->non_client_view()->UpdateFrame();
+
+ // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want
+ // to notify our children too, since we can have MDI child windows who need to
+ // update their appearance.
+ EnumChildWindows(GetNativeView(), &SendDwmCompositionChanged, NULL);
+}
+
+Widget* NativeWidgetWin::GetWidget() {
+ return delegate_->AsWidget();
+}
+
+const Widget* NativeWidgetWin::GetWidget() const {
+ return delegate_->AsWidget();
+}
+
+gfx::NativeView NativeWidgetWin::GetNativeView() const {
+ return WindowImpl::hwnd();
+}
+
+gfx::NativeWindow NativeWidgetWin::GetNativeWindow() const {
+ return WindowImpl::hwnd();
+}
+
+Widget* NativeWidgetWin::GetTopLevelWidget() {
+ NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView());
+ return native_widget ? native_widget->GetWidget() : NULL;
+}
+
+const ui::Compositor* NativeWidgetWin::GetCompositor() const {
+ return compositor_.get();
+}
+
+ui::Compositor* NativeWidgetWin::GetCompositor() {
+ return compositor_.get();
+}
+
+void NativeWidgetWin::CalculateOffsetToAncestorWithLayer(
+ gfx::Point* offset,
+ ui::Layer** layer_parent) {
+}
+
+void NativeWidgetWin::ReorderLayers() {
+}
+
+void NativeWidgetWin::ViewRemoved(View* view) {
+ if (drop_target_.get())
+ drop_target_->ResetTargetViewIfEquals(view);
+
+ ClearAccessibilityViewEvent(view);
+}
+
+void NativeWidgetWin::SetNativeWindowProperty(const char* name, void* value) {
+ // Remove the existing property (if any).
+ for (ViewProps::iterator i = props_.begin(); i != props_.end(); ++i) {
+ if ((*i)->Key() == name) {
+ props_.erase(i);
+ break;
+ }
+ }
+
+ if (value)
+ props_.push_back(new ViewProp(hwnd(), name, value));
+}
+
+void* NativeWidgetWin::GetNativeWindowProperty(const char* name) const {
+ return ViewProp::GetValue(hwnd(), name);
+}
+
+TooltipManager* NativeWidgetWin::GetTooltipManager() const {
+ return tooltip_manager_.get();
+}
+
+bool NativeWidgetWin::IsScreenReaderActive() const {
+ return screen_reader_active_;
+}
+
+void NativeWidgetWin::SendNativeAccessibilityEvent(
+ View* view,
+ ui::AccessibilityTypes::Event event_type) {
+ // Now call the Windows-specific method to notify MSAA clients of this
+ // event. The widget gives us a temporary unique child ID to associate
+ // with this view so that clients can call get_accChild in
+ // NativeViewAccessibilityWin to retrieve the IAccessible associated
+ // with this view.
+ int child_id = AddAccessibilityViewEvent(view);
+ ::NotifyWinEvent(NativeViewAccessibilityWin::MSAAEvent(event_type),
+ GetNativeView(), OBJID_CLIENT, child_id);
+}
+
+void NativeWidgetWin::SetMouseCapture() {
+ DCHECK(!HasMouseCapture());
+ SetCapture(hwnd());
+}
+
+void NativeWidgetWin::ReleaseMouseCapture() {
+ ReleaseCapture();
+}
+
+bool NativeWidgetWin::HasMouseCapture() const {
+ return GetCapture() == hwnd();
+}
+
+InputMethod* NativeWidgetWin::CreateInputMethod() {
+ if (views::Widget::IsPureViews()) {
+ InputMethod* input_method = new InputMethodWin(this);
+ input_method->Init(GetWidget());
+ return input_method;
+ }
+ return NULL;
+}
+
+void NativeWidgetWin::CenterWindow(const gfx::Size& size) {
+ HWND parent = GetParent();
+ if (!IsWindow())
+ parent = ::GetWindow(GetNativeView(), GW_OWNER);
+ ui::CenterAndSizeWindow(parent, GetNativeView(), size, false);
+}
+
+void NativeWidgetWin::GetWindowPlacement(
+ gfx::Rect* bounds,
+ ui::WindowShowState* show_state) const {
+ WINDOWPLACEMENT wp;
+ wp.length = sizeof(wp);
+ const bool succeeded = !!::GetWindowPlacement(GetNativeView(), &wp);
+ DCHECK(succeeded);
+
+ if (bounds != NULL) {
+ MONITORINFO mi;
+ mi.cbSize = sizeof(mi);
+ const bool succeeded = !!GetMonitorInfo(
+ MonitorFromWindow(GetNativeView(), MONITOR_DEFAULTTONEAREST), &mi);
+ DCHECK(succeeded);
+ *bounds = gfx::Rect(wp.rcNormalPosition);
+ // Convert normal position from workarea coordinates to screen coordinates.
+ bounds->Offset(mi.rcWork.left - mi.rcMonitor.left,
+ mi.rcWork.top - mi.rcMonitor.top);
+ }
+
+ if (show_state != NULL) {
+ if (wp.showCmd == SW_SHOWMAXIMIZED)
+ *show_state = ui::SHOW_STATE_MAXIMIZED;
+ else if (wp.showCmd == SW_SHOWMINIMIZED)
+ *show_state = ui::SHOW_STATE_MINIMIZED;
+ else
+ *show_state = ui::SHOW_STATE_NORMAL;
+ }
+}
+
+void NativeWidgetWin::SetWindowTitle(const string16& title) {
+ SetWindowText(GetNativeView(), title.c_str());
+ SetAccessibleName(title);
+}
+
+void NativeWidgetWin::SetWindowIcons(const SkBitmap& window_icon,
+ const SkBitmap& app_icon) {
+ if (!window_icon.isNull()) {
+ HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(window_icon);
+ // We need to make sure to destroy the previous icon, otherwise we'll leak
+ // these GDI objects until we crash!
+ HICON old_icon = reinterpret_cast<HICON>(
+ SendMessage(GetNativeView(), WM_SETICON, ICON_SMALL,
+ reinterpret_cast<LPARAM>(windows_icon)));
+ if (old_icon)
+ DestroyIcon(old_icon);
+ }
+ if (!app_icon.isNull()) {
+ HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(app_icon);
+ HICON old_icon = reinterpret_cast<HICON>(
+ SendMessage(GetNativeView(), WM_SETICON, ICON_BIG,
+ reinterpret_cast<LPARAM>(windows_icon)));
+ if (old_icon)
+ DestroyIcon(old_icon);
+ }
+}
+
+void NativeWidgetWin::SetAccessibleName(const string16& name) {
+ base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
+ HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
+ IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
+ if (SUCCEEDED(hr))
+ hr = pAccPropServices->SetHwndPropStr(GetNativeView(), OBJID_CLIENT,
+ CHILDID_SELF, PROPID_ACC_NAME,
+ name.c_str());
+}
+
+void NativeWidgetWin::SetAccessibleRole(ui::AccessibilityTypes::Role role) {
+ base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
+ HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
+ IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
+ if (SUCCEEDED(hr)) {
+ VARIANT var;
+ if (role) {
+ var.vt = VT_I4;
+ var.lVal = NativeViewAccessibilityWin::MSAARole(role);
+ hr = pAccPropServices->SetHwndProp(GetNativeView(), OBJID_CLIENT,
+ CHILDID_SELF, PROPID_ACC_ROLE, var);
+ }
+ }
+}
+
+void NativeWidgetWin::SetAccessibleState(ui::AccessibilityTypes::State state) {
+ base::win::ScopedComPtr<IAccPropServices> pAccPropServices;
+ HRESULT hr = CoCreateInstance(CLSID_AccPropServices, NULL, CLSCTX_SERVER,
+ IID_IAccPropServices, reinterpret_cast<void**>(&pAccPropServices));
+ if (SUCCEEDED(hr)) {
+ VARIANT var;
+ if (state) {
+ var.vt = VT_I4;
+ var.lVal = NativeViewAccessibilityWin::MSAAState(state);
+ hr = pAccPropServices->SetHwndProp(GetNativeView(), OBJID_CLIENT,
+ CHILDID_SELF, PROPID_ACC_STATE, var);
+ }
+ }
+}
+
+void NativeWidgetWin::BecomeModal() {
+ // We implement modality by crawling up the hierarchy of windows starting
+ // at the owner, disabling all of them so that they don't receive input
+ // messages.
+ HWND start = ::GetWindow(GetNativeView(), GW_OWNER);
+ while (start) {
+ ::EnableWindow(start, FALSE);
+ start = ::GetParent(start);
+ }
+}
+
+gfx::Rect NativeWidgetWin::GetWindowScreenBounds() const {
+ RECT r;
+ GetWindowRect(&r);
+ return gfx::Rect(r);
+}
+
+gfx::Rect NativeWidgetWin::GetClientAreaScreenBounds() const {
+ RECT r;
+ GetClientRect(&r);
+ POINT point = { r.left, r.top };
+ ClientToScreen(hwnd(), &point);
+ return gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top);
+}
+
+gfx::Rect NativeWidgetWin::GetRestoredBounds() const {
+ // If we're in fullscreen mode, we've changed the normal bounds to the monitor
+ // rect, so return the saved bounds instead.
+ if (IsFullscreen())
+ return gfx::Rect(saved_window_info_.window_rect);
+
+ gfx::Rect bounds;
+ GetWindowPlacement(&bounds, NULL);
+ return bounds;
+}
+
+void NativeWidgetWin::SetBounds(const gfx::Rect& bounds) {
+ LONG style = GetWindowLong(GWL_STYLE);
+ if (style & WS_MAXIMIZE)
+ SetWindowLong(GWL_STYLE, style & ~WS_MAXIMIZE);
+ SetWindowPos(NULL, bounds.x(), bounds.y(), bounds.width(), bounds.height(),
+ SWP_NOACTIVATE | SWP_NOZORDER);
+}
+
+void NativeWidgetWin::SetSize(const gfx::Size& size) {
+ SetWindowPos(NULL, 0, 0, size.width(), size.height(),
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
+}
+
+void NativeWidgetWin::MoveAbove(gfx::NativeView native_view) {
+ SetWindowPos(native_view, 0, 0, 0, 0,
+ SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
+}
+
+void NativeWidgetWin::MoveToTop() {
+ NOTIMPLEMENTED();
+}
+
+void NativeWidgetWin::SetShape(gfx::NativeRegion region) {
+ SetWindowRgn(region, TRUE);
+}
+
+void NativeWidgetWin::Close() {
+ if (!IsWindow())
+ return; // No need to do anything.
+
+ // Let's hide ourselves right away.
+ Hide();
+
+ // Modal dialog windows disable their owner windows; re-enable them now so
+ // they can activate as foreground windows upon this window's destruction.
+ RestoreEnabledIfNecessary();
+
+ if (!close_widget_factory_.HasWeakPtrs()) {
+ // And we delay the close so that if we are called from an ATL callback,
+ // we don't destroy the window before the callback returned (as the caller
+ // may delete ourselves on destroy and the ATL callback would still
+ // dereference us when the callback returns).
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&NativeWidgetWin::CloseNow,
+ close_widget_factory_.GetWeakPtr()));
+ }
+}
+
+void NativeWidgetWin::CloseNow() {
+ // We may already have been destroyed if the selection resulted in a tab
+ // switch which will have reactivated the browser window and closed us, so
+ // we need to check to see if we're still a window before trying to destroy
+ // ourself.
+ if (IsWindow())
+ DestroyWindow(hwnd());
+}
+
+void NativeWidgetWin::EnableClose(bool enable) {
+ // Disable the native frame's close button regardless of whether or not the
+ // native frame is in use, since this also affects the system menu.
+ EnableMenuItem(GetSystemMenu(GetNativeView(), false), SC_CLOSE, enable);
+ SendFrameChanged(GetNativeView());
+}
+
+void NativeWidgetWin::Show() {
+ if (!IsWindow())
+ return;
+
+ ShowWindow(SW_SHOWNOACTIVATE);
+ SetInitialFocus();
+}
+
+void NativeWidgetWin::Hide() {
+ if (IsWindow()) {
+ // NOTE: Be careful not to activate any windows here (for example, calling
+ // ShowWindow(SW_HIDE) will automatically activate another window). This
+ // code can be called while a window is being deactivated, and activating
+ // another window will screw up the activation that is already in progress.
+ SetWindowPos(NULL, 0, 0, 0, 0,
+ SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
+ SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
+ }
+}
+
+void NativeWidgetWin::ShowMaximizedWithBounds(
+ const gfx::Rect& restored_bounds) {
+ WINDOWPLACEMENT placement = { 0 };
+ placement.length = sizeof(WINDOWPLACEMENT);
+ placement.showCmd = SW_SHOWMAXIMIZED;
+ placement.rcNormalPosition = restored_bounds.ToRECT();
+ SetWindowPlacement(hwnd(), &placement);
+}
+
+void NativeWidgetWin::ShowWithWindowState(ui::WindowShowState show_state) {
+ DWORD native_show_state;
+ switch (show_state) {
+ case ui::SHOW_STATE_INACTIVE:
+ native_show_state = SW_SHOWNOACTIVATE;
+ break;
+ case ui::SHOW_STATE_MAXIMIZED:
+ native_show_state = SW_SHOWMAXIMIZED;
+ break;
+ case ui::SHOW_STATE_MINIMIZED:
+ native_show_state = SW_SHOWMINIMIZED;
+ break;
+ default:
+ native_show_state = GetShowState();
+ break;
+ }
+ Show(native_show_state);
+}
+
+bool NativeWidgetWin::IsVisible() const {
+ return !!::IsWindowVisible(hwnd());
+}
+
+void NativeWidgetWin::Activate() {
+ if (IsMinimized())
+ ::ShowWindow(GetNativeView(), SW_RESTORE);
+ ::SetWindowPos(GetNativeView(), HWND_TOP, 0, 0, 0, 0,
+ SWP_NOSIZE | SWP_NOMOVE);
+ SetForegroundWindow(GetNativeView());
+}
+
+void NativeWidgetWin::Deactivate() {
+ HWND hwnd = ::GetNextWindow(GetNativeView(), GW_HWNDNEXT);
+ if (hwnd)
+ ::SetForegroundWindow(hwnd);
+}
+
+bool NativeWidgetWin::IsActive() const {
+ return GetActiveWindow() == hwnd();
+}
+
+void NativeWidgetWin::SetAlwaysOnTop(bool on_top) {
+ ::SetWindowPos(GetNativeView(), on_top ? HWND_TOPMOST : HWND_NOTOPMOST,
+ 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+}
+
+void NativeWidgetWin::Maximize() {
+ ExecuteSystemMenuCommand(SC_MAXIMIZE);
+}
+
+void NativeWidgetWin::Minimize() {
+ ExecuteSystemMenuCommand(SC_MINIMIZE);
+
+ delegate_->OnNativeBlur(NULL);
+}
+
+bool NativeWidgetWin::IsMaximized() const {
+ return !!::IsZoomed(GetNativeView());
+}
+
+bool NativeWidgetWin::IsMinimized() const {
+ return !!::IsIconic(GetNativeView());
+}
+
+void NativeWidgetWin::Restore() {
+ ExecuteSystemMenuCommand(SC_RESTORE);
+}
+
+void NativeWidgetWin::SetFullscreen(bool fullscreen) {
+ if (fullscreen_ == fullscreen)
+ return; // Nothing to do.
+
+ // Reduce jankiness during the following position changes by hiding the window
+ // until it's in the final position.
+ PushForceHidden();
+
+ // Size/position/style window appropriately.
+ if (!fullscreen_) {
+ // Save current window information. We force the window into restored mode
+ // before going fullscreen because Windows doesn't seem to hide the
+ // taskbar if the window is in the maximized state.
+ saved_window_info_.maximized = IsMaximized();
+ if (saved_window_info_.maximized)
+ Restore();
+ saved_window_info_.style = GetWindowLong(GWL_STYLE);
+ saved_window_info_.ex_style = GetWindowLong(GWL_EXSTYLE);
+ GetWindowRect(&saved_window_info_.window_rect);
+ }
+
+ fullscreen_ = fullscreen;
+
+ if (fullscreen_) {
+ // Set new window style and size.
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(monitor_info);
+ GetMonitorInfo(MonitorFromWindow(GetNativeView(), MONITOR_DEFAULTTONEAREST),
+ &monitor_info);
+ gfx::Rect monitor_rect(monitor_info.rcMonitor);
+ SetWindowLong(GWL_STYLE,
+ saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME));
+ SetWindowLong(GWL_EXSTYLE,
+ saved_window_info_.ex_style & ~(WS_EX_DLGMODALFRAME |
+ WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
+ SetWindowPos(NULL, monitor_rect.x(), monitor_rect.y(),
+ monitor_rect.width(), monitor_rect.height(),
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+ } else {
+ // Reset original window style and size. The multiple window size/moves
+ // here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
+ // repainted. Better-looking methods welcome.
+ gfx::Rect new_rect(saved_window_info_.window_rect);
+ SetWindowLong(GWL_STYLE, saved_window_info_.style);
+ SetWindowLong(GWL_EXSTYLE, saved_window_info_.ex_style);
+ SetWindowPos(NULL, new_rect.x(), new_rect.y(), new_rect.width(),
+ new_rect.height(),
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+ if (saved_window_info_.maximized)
+ Maximize();
+ }
+
+ // Undo our anti-jankiness hacks.
+ PopForceHidden();
+}
+
+bool NativeWidgetWin::IsFullscreen() const {
+ return fullscreen_;
+}
+
+void NativeWidgetWin::SetOpacity(unsigned char opacity) {
+ layered_alpha_ = static_cast<BYTE>(opacity);
+}
+
+void NativeWidgetWin::SetUseDragFrame(bool use_drag_frame) {
+ if (use_drag_frame) {
+ // Make the frame slightly transparent during the drag operation.
+ drag_frame_saved_window_style_ = GetWindowLong(GWL_STYLE);
+ drag_frame_saved_window_ex_style_ = GetWindowLong(GWL_EXSTYLE);
+ SetWindowLong(GWL_EXSTYLE,
+ drag_frame_saved_window_ex_style_ | WS_EX_LAYERED);
+ // Remove the captions tyle so the window doesn't have window controls for a
+ // more "transparent" look.
+ SetWindowLong(GWL_STYLE, drag_frame_saved_window_style_ & ~WS_CAPTION);
+ SetLayeredWindowAttributes(GetNativeWindow(), RGB(0xFF, 0xFF, 0xFF),
+ kDragFrameWindowAlpha, LWA_ALPHA);
+ } else {
+ SetWindowLong(GWL_STYLE, drag_frame_saved_window_style_);
+ SetWindowLong(GWL_EXSTYLE, drag_frame_saved_window_ex_style_);
+ }
+}
+
+bool NativeWidgetWin::IsAccessibleWidget() const {
+ return screen_reader_active_;
+}
+
+void NativeWidgetWin::RunShellDrag(View* view,
+ const ui::OSExchangeData& data,
+ int operation) {
+ scoped_refptr<ui::DragSource> drag_source(new ui::DragSource);
+ DWORD effects;
+ DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data), drag_source,
+ ui::DragDropTypes::DragOperationToDropEffect(operation), &effects);
+}
+
+void NativeWidgetWin::SchedulePaintInRect(const gfx::Rect& rect) {
+ if (use_layered_buffer_) {
+ // We must update the back-buffer immediately, since Windows' handling of
+ // invalid rects is somewhat mysterious.
+ invalid_rect_ = invalid_rect_.Union(rect);
+
+ // In some situations, such as drag and drop, when Windows itself runs a
+ // nested message loop our message loop appears to be starved and we don't
+ // receive calls to DidProcessMessage(). This only seems to affect layered
+ // windows, so we schedule a redraw manually using a task, since those never
+ // seem to be starved. Also, wtf.
+ if (!paint_layered_window_factory_.HasWeakPtrs()) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&NativeWidgetWin::RedrawLayeredWindowContents,
+ paint_layered_window_factory_.GetWeakPtr()));
+ }
+ } else {
+ // InvalidateRect() expects client coordinates.
+ RECT r = rect.ToRECT();
+ InvalidateRect(hwnd(), &r, FALSE);
+ }
+}
+
+void NativeWidgetWin::SetCursor(gfx::NativeCursor cursor) {
+ if (cursor) {
+ previous_cursor_ = ::SetCursor(cursor);
+ } else if (previous_cursor_) {
+ ::SetCursor(previous_cursor_);
+ previous_cursor_ = NULL;
+ }
+}
+
+void NativeWidgetWin::ClearNativeFocus() {
+ ::SetFocus(GetNativeView());
+}
+
+void NativeWidgetWin::FocusNativeView(gfx::NativeView native_view) {
+ // Only reset focus if hwnd is not already focused.
+ if (native_view && ::GetFocus() != native_view)
+ ::SetFocus(native_view);
+}
+
+bool NativeWidgetWin::ConvertPointFromAncestor(
+ const Widget* ancestor, gfx::Point* point) const {
+ NOTREACHED();
+ return false;
+}
+
+gfx::Rect NativeWidgetWin::GetWorkAreaBoundsInScreen() const {
+ return gfx::Screen::GetMonitorWorkAreaNearestWindow(GetNativeView());
+}
+
+void NativeWidgetWin::SetInactiveRenderingDisabled(bool value) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetWin, MessageLoop::Observer implementation:
+
+base::EventStatus NativeWidgetWin::WillProcessEvent(
+ const base::NativeEvent& event) {
+ return base::EVENT_CONTINUE;
+}
+
+void NativeWidgetWin::DidProcessEvent(const base::NativeEvent& event) {
+ RedrawInvalidRect();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetWin, WindowImpl overrides:
+
+HICON NativeWidgetWin::GetDefaultWindowIcon() const {
+ if (ViewsDelegate::views_delegate)
+ return ViewsDelegate::views_delegate->GetDefaultWindowIcon();
+ return NULL;
+}
+
+LRESULT NativeWidgetWin::OnWndProc(UINT message, WPARAM w_param,
+ LPARAM l_param) {
+ HWND window = hwnd();
+ LRESULT result = 0;
+
+ // First allow messages sent by child controls to be processed directly by
+ // their associated views. If such a view is present, it will handle the
+ // message *instead of* this NativeWidgetWin.
+ if (ProcessChildWindowMessage(message, w_param, l_param, &result))
+ return result;
+
+ // Otherwise we handle everything else.
+ if (!ProcessWindowMessage(window, message, w_param, l_param, result))
+ result = DefWindowProc(window, message, w_param, l_param);
+ if (message == WM_NCDESTROY) {
+ MessageLoopForUI::current()->RemoveObserver(this);
+ OnFinalMessage(window);
+ }
+
+ // Only top level widget should store/restore focus.
+ if (message == WM_ACTIVATE && GetWidget()->is_top_level())
+ PostProcessActivateMessage(this, LOWORD(w_param));
+ if (message == WM_ENABLE && restore_focus_when_enabled_) {
+ // This path should be executed only for top level as
+ // restore_focus_when_enabled_ is set in PostProcessActivateMessage.
+ DCHECK(GetWidget()->is_top_level());
+ restore_focus_when_enabled_ = false;
+ GetWidget()->GetFocusManager()->RestoreFocusedView();
+ }
+ return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetWin, protected:
+
+// Message handlers ------------------------------------------------------------
+
+void NativeWidgetWin::OnActivate(UINT action, BOOL minimized, HWND window) {
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnActivateApp(BOOL active, DWORD thread_id) {
+ if (GetWidget()->non_client_view() && !active &&
+ thread_id != GetCurrentThreadId()) {
+ // Another application was activated, we should reset any state that
+ // disables inactive rendering now.
+ delegate_->EnableInactiveRendering();
+ // Also update the native frame if it is rendering the non-client area.
+ if (GetWidget()->ShouldUseNativeFrame())
+ DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0);
+ }
+}
+
+LRESULT NativeWidgetWin::OnAppCommand(HWND window,
+ short app_command,
+ WORD device,
+ int keystate) {
+ // We treat APPCOMMAND ids as an extension of our command namespace, and just
+ // let the delegate figure out what to do...
+ BOOL is_handled = (GetWidget()->widget_delegate() &&
+ GetWidget()->widget_delegate()->ExecuteWindowsCommand(app_command)) ?
+ TRUE : FALSE;
+ SetMsgHandled(is_handled);
+ // Make sure to return TRUE if the event was handled or in some cases the
+ // system will execute the default handler which can cause bugs like going
+ // forward or back two pages instead of one.
+ return is_handled;
+}
+
+void NativeWidgetWin::OnCancelMode() {
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnCaptureChanged(HWND hwnd) {
+ delegate_->OnMouseCaptureLost();
+}
+
+void NativeWidgetWin::OnClose() {
+ GetWidget()->Close();
+}
+
+void NativeWidgetWin::OnCommand(UINT notification_code,
+ int command_id,
+ HWND window) {
+ // If the notification code is > 1 it means it is control specific and we
+ // should ignore it.
+ if (notification_code > 1 ||
+ GetWidget()->widget_delegate()->ExecuteWindowsCommand(command_id)) {
+ SetMsgHandled(FALSE);
+ }
+}
+
+LRESULT NativeWidgetWin::OnCreate(CREATESTRUCT* create_struct) {
+ SetNativeWindowProperty(kNativeWidgetKey, this);
+ CHECK_EQ(this, GetNativeWidgetForNativeView(hwnd()));
+
+ use_layered_buffer_ = !!(window_ex_style() & WS_EX_LAYERED);
+
+ // Attempt to detect screen readers by sending an event with our custom id.
+ if (!IsAccessibleWidget())
+ NotifyWinEvent(EVENT_SYSTEM_ALERT, hwnd(), kCustomObjectID, CHILDID_SELF);
+
+ props_.push_back(ui::SetWindowSupportsRerouteMouseWheel(hwnd()));
+
+ drop_target_ = new DropTargetWin(
+ static_cast<internal::RootView*>(GetWidget()->GetRootView()));
+
+ // We need to add ourselves as a message loop observer so that we can repaint
+ // aggressively if the contents of our window become invalid. Unfortunately
+ // WM_PAINT messages are starved and we get flickery redrawing when resizing
+ // if we do not do this.
+ MessageLoopForUI::current()->AddObserver(this);
+
+ // Windows special DWM window frame requires a special tooltip manager so
+ // that window controls in Chrome windows don't flicker when you move your
+ // mouse over them. See comment in aero_tooltip_manager.h.
+ Widget* widget = GetWidget()->GetTopLevelWidget();
+ if (widget && widget->ShouldUseNativeFrame()) {
+ tooltip_manager_.reset(new AeroTooltipManager(GetWidget()));
+ } else {
+ tooltip_manager_.reset(new TooltipManagerWin(GetWidget()));
+ }
+ if (!tooltip_manager_->Init()) {
+ // There was a problem creating the TooltipManager. Common error is 127.
+ // See 82193 for details.
+ LOG_GETLASTERROR(WARNING) << "tooltip creation failed, disabling tooltips";
+ tooltip_manager_.reset();
+ }
+
+ // This message initializes the window so that focus border are shown for
+ // windows.
+ SendMessage(
+ hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0);
+
+ // Bug 964884: detach the IME attached to this window.
+ // We should attach IMEs only when we need to input CJK strings.
+ ImmAssociateContextEx(hwnd(), NULL, 0);
+
+ // We need to allow the delegate to size its contents since the window may not
+ // receive a size notification when its initial bounds are specified at window
+ // creation time.
+ ClientAreaSizeChanged();
+
+#if defined(VIEWS_COMPOSITOR)
+ if (View::get_use_acceleration_when_possible()) {
+ if (ui::Compositor::compositor_factory()) {
+ compositor_ = (*Widget::compositor_factory())(this);
+ } else {
+ CRect window_rect;
+ GetClientRect(&window_rect);
+ compositor_ = ui::Compositor::Create(this,
+ hwnd(),
+ gfx::Size(window_rect.Width(), window_rect.Height()));
+ }
+ if (compositor_.get()) {
+ delegate_->AsWidget()->GetRootView()->SetPaintToLayer(true);
+ compositor_->SetRootLayer(delegate_->AsWidget()->GetRootView()->layer());
+ }
+ }
+#endif
+
+ delegate_->OnNativeWidgetCreated();
+
+ // Get access to a modifiable copy of the system menu.
+ GetSystemMenu(hwnd(), false);
+ return 0;
+}
+
+void NativeWidgetWin::OnDestroy() {
+ delegate_->OnNativeWidgetDestroying();
+ if (drop_target_.get()) {
+ RevokeDragDrop(hwnd());
+ drop_target_ = NULL;
+ }
+}
+
+void NativeWidgetWin::OnDisplayChange(UINT bits_per_pixel, CSize screen_size) {
+ GetWidget()->widget_delegate()->OnDisplayChanged();
+}
+
+LRESULT NativeWidgetWin::OnDwmCompositionChanged(UINT msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ if (!GetWidget()->non_client_view()) {
+ SetMsgHandled(FALSE);
+ return 0;
+ }
+
+ // For some reason, we need to hide the window while we're changing the frame
+ // type only when we're changing it in response to WM_DWMCOMPOSITIONCHANGED.
+ // If we don't, the client area will be filled with black. I'm suspecting
+ // something skia-ey.
+ // Frame type toggling caused by the user (e.g. switching theme) doesn't seem
+ // to have this requirement.
+ FrameTypeChanged();
+ return 0;
+}
+
+void NativeWidgetWin::OnEndSession(BOOL ending, UINT logoff) {
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnEnterSizeMove() {
+ delegate_->OnNativeWidgetBeginUserBoundsChange();
+ SetMsgHandled(FALSE);
+}
+
+LRESULT NativeWidgetWin::OnEraseBkgnd(HDC dc) {
+ // This is needed for magical win32 flicker ju-ju.
+ return 1;
+}
+
+void NativeWidgetWin::OnExitMenuLoop(BOOL is_track_popup_menu) {
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnExitSizeMove() {
+ delegate_->OnNativeWidgetEndUserBoundsChange();
+ SetMsgHandled(FALSE);
+}
+
+LRESULT NativeWidgetWin::OnGetObject(UINT uMsg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ LRESULT reference_result = static_cast<LRESULT>(0L);
+
+ // Accessibility readers will send an OBJID_CLIENT message
+ if (OBJID_CLIENT == l_param) {
+ // Retrieve MSAA dispatch object for the root view.
+ base::win::ScopedComPtr<IAccessible> root(
+ GetWidget()->GetRootView()->GetNativeViewAccessible());
+
+ // Create a reference that MSAA will marshall to the client.
+ reference_result = LresultFromObject(IID_IAccessible, w_param,
+ static_cast<IAccessible*>(root.Detach()));
+ }
+
+ if (kCustomObjectID == l_param) {
+ // An MSAA client requestes our custom id. Assume that we have detected an
+ // active windows screen reader.
+ OnScreenReaderDetected();
+
+ // Return with failure.
+ return static_cast<LRESULT>(0L);
+ }
+
+ return reference_result;
+}
+
+void NativeWidgetWin::OnGetMinMaxInfo(MINMAXINFO* minmax_info) {
+ gfx::Size min_window_size(delegate_->GetMinimumSize());
+ // Add the native frame border size to the minimum size if the view reports
+ // its size as the client size.
+ if (WidgetSizeIsClientSize()) {
+ CRect client_rect, window_rect;
+ GetClientRect(&client_rect);
+ GetWindowRect(&window_rect);
+ window_rect -= client_rect;
+ min_window_size.Enlarge(window_rect.Width(), window_rect.Height());
+ }
+ minmax_info->ptMinTrackSize.x = min_window_size.width();
+ minmax_info->ptMinTrackSize.y = min_window_size.height();
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnHScroll(int scroll_type,
+ short position,
+ HWND scrollbar) {
+ SetMsgHandled(FALSE);
+}
+
+LRESULT NativeWidgetWin::OnImeMessages(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ InputMethod* input_method = GetWidget()->GetInputMethodDirect();
+ if (!input_method || input_method->IsMock()) {
+ SetMsgHandled(FALSE);
+ return 0;
+ }
+
+ InputMethodWin* ime_win = static_cast<InputMethodWin*>(input_method);
+ BOOL handled = FALSE;
+ LRESULT result = ime_win->OnImeMessages(message, w_param, l_param, &handled);
+
+ SetMsgHandled(handled);
+ return result;
+}
+
+void NativeWidgetWin::OnInitMenu(HMENU menu) {
+ bool is_fullscreen = IsFullscreen();
+ bool is_minimized = IsMinimized();
+ bool is_maximized = IsMaximized();
+ bool is_restored = !is_fullscreen && !is_minimized && !is_maximized;
+
+ ScopedRedrawLock lock(this);
+ EnableMenuItem(menu, SC_RESTORE, is_minimized || is_maximized);
+ EnableMenuItem(menu, SC_MOVE, is_restored);
+ EnableMenuItem(menu, SC_SIZE,
+ GetWidget()->widget_delegate()->CanResize() && is_restored);
+ EnableMenuItem(menu, SC_MAXIMIZE,
+ GetWidget()->widget_delegate()->CanMaximize() &&
+ !is_fullscreen && !is_maximized);
+ EnableMenuItem(menu, SC_MINIMIZE,
+ GetWidget()->widget_delegate()->CanMaximize() &&
+ !is_minimized);
+}
+
+void NativeWidgetWin::OnInitMenuPopup(HMENU menu,
+ UINT position,
+ BOOL is_system_menu) {
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnInputLangChange(DWORD character_set,
+ HKL input_language_id) {
+ InputMethod* input_method = GetWidget()->GetInputMethodDirect();
+
+ if (input_method && !input_method->IsMock()) {
+ static_cast<InputMethodWin*>(input_method)->OnInputLangChange(
+ character_set, input_language_id);
+ }
+}
+
+LRESULT NativeWidgetWin::OnKeyEvent(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ MSG msg = { hwnd(), message, w_param, l_param };
+ KeyEvent key(msg);
+ InputMethod* input_method = GetWidget()->GetInputMethodDirect();
+ if (input_method)
+ input_method->DispatchKeyEvent(key);
+ else
+ DispatchKeyEventPostIME(key);
+ return 0;
+}
+
+void NativeWidgetWin::OnKillFocus(HWND focused_window) {
+ delegate_->OnNativeBlur(focused_window);
+ InputMethod* input_method = GetWidget()->GetInputMethodDirect();
+ if (input_method)
+ input_method->OnBlur();
+ SetMsgHandled(FALSE);
+}
+
+LRESULT NativeWidgetWin::OnMouseActivate(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ // TODO(beng): resolve this with the GetWindowLong() check on the subsequent
+ // line.
+ if (GetWidget()->non_client_view())
+ return delegate_->CanActivate() ? MA_ACTIVATE : MA_NOACTIVATEANDEAT;
+ if (GetWindowLong(GWL_EXSTYLE) & WS_EX_NOACTIVATE)
+ return MA_NOACTIVATE;
+ SetMsgHandled(FALSE);
+ return MA_ACTIVATE;
+}
+
+LRESULT NativeWidgetWin::OnMouseRange(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) {
+ is_right_mouse_pressed_on_caption_ = false;
+ ReleaseCapture();
+ // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu()
+ // expect screen coordinates.
+ CPoint screen_point(l_param);
+ MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_point, 1);
+ w_param = SendMessage(GetNativeView(), WM_NCHITTEST, 0,
+ MAKELPARAM(screen_point.x, screen_point.y));
+ if (w_param == HTCAPTION || w_param == HTSYSMENU) {
+ ui::ShowSystemMenu(GetNativeView(), screen_point.x, screen_point.y);
+ return 0;
+ }
+ } else if (message == WM_NCLBUTTONDOWN &&
+ !GetWidget()->ShouldUseNativeFrame()) {
+ switch (w_param) {
+ case HTCLOSE:
+ case HTMINBUTTON:
+ case HTMAXBUTTON: {
+ // When the mouse is pressed down in these specific non-client areas,
+ // we need to tell the RootView to send the mouse pressed event (which
+ // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_
+ // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be
+ // sent by the applicable button's ButtonListener. We _have_ to do this
+ // way rather than letting Windows just send the syscommand itself (as
+ // would happen if we never did this dance) because for some insane
+ // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed
+ // window control button appearance, in the Windows classic style, over
+ // our view! Ick! By handling this message we prevent Windows from
+ // doing this undesirable thing, but that means we need to roll the
+ // sys-command handling ourselves.
+ // Combine |w_param| with common key state message flags.
+ w_param |= ((GetKeyState(VK_CONTROL) & 0x80) == 0x80)? MK_CONTROL : 0;
+ w_param |= ((GetKeyState(VK_SHIFT) & 0x80) == 0x80)? MK_SHIFT : 0;
+ }
+ }
+ } else if (message == WM_NCRBUTTONDOWN &&
+ (w_param == HTCAPTION || w_param == HTSYSMENU)) {
+ is_right_mouse_pressed_on_caption_ = true;
+ // We SetMouseCapture() to ensure we only show the menu when the button
+ // down and up are both on the caption. Note: this causes the button up to
+ // be WM_RBUTTONUP instead of WM_NCRBUTTONUP.
+ SetMouseCapture();
+ }
+
+ MSG msg = { hwnd(), message, w_param, l_param, 0,
+ { GET_X_LPARAM(l_param), GET_Y_LPARAM(l_param) } };
+ MouseEvent event(msg);
+
+ if (!(event.flags() & ui::EF_IS_NON_CLIENT))
+ if (tooltip_manager_.get())
+ tooltip_manager_->OnMouse(message, w_param, l_param);
+
+ if (event.type() == ui::ET_MOUSE_MOVED && !HasMouseCapture()) {
+ // Windows only fires WM_MOUSELEAVE events if the application begins
+ // "tracking" mouse events for a given HWND during WM_MOUSEMOVE events.
+ // We need to call |TrackMouseEvents| to listen for WM_MOUSELEAVE.
+ TrackMouseEvents((message == WM_NCMOUSEMOVE) ?
+ TME_NONCLIENT | TME_LEAVE : TME_LEAVE);
+ } else if (event.type() == ui::ET_MOUSE_EXITED) {
+ // Reset our tracking flags so future mouse movement over this
+ // NativeWidgetWin results in a new tracking session. Fall through for
+ // OnMouseEvent.
+ active_mouse_tracking_flags_ = 0;
+ } else if (event.type() == ui::ET_MOUSEWHEEL) {
+ // Reroute the mouse wheel to the window under the pointer if applicable.
+ return (ui::RerouteMouseWheel(hwnd(), w_param, l_param) ||
+ delegate_->OnMouseEvent(MouseWheelEvent(msg))) ? 0 : 1;
+ }
+
+ bool handled = delegate_->OnMouseEvent(event);
+
+ if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU &&
+ !GetWidget()->ShouldUseNativeFrame()) {
+ // TODO(msw): Eliminate undesired painting, or re-evaluate this workaround.
+ // DefWindowProc for WM_NCLBUTTONDOWN does weird non-client painting, so we
+ // need to call it inside a ScopedRedrawLock. This may cause other negative
+ // side-effects (ex/ stifling non-client mouse releases).
+ DefWindowProcWithRedrawLock(message, w_param, l_param);
+ handled = true;
+ }
+
+ SetMsgHandled(handled);
+ return 0;
+}
+
+void NativeWidgetWin::OnMove(const CPoint& point) {
+ // TODO(beng): move to Widget.
+ GetWidget()->widget_delegate()->OnWidgetMove();
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnMoving(UINT param, const LPRECT new_bounds) {
+ // TODO(beng): move to Widget.
+ GetWidget()->widget_delegate()->OnWidgetMove();
+}
+
+LRESULT NativeWidgetWin::OnNCActivate(BOOL active) {
+ if (delegate_->CanActivate())
+ delegate_->OnNativeWidgetActivationChanged(!!active);
+
+ if (!GetWidget()->non_client_view()) {
+ SetMsgHandled(FALSE);
+ return 0;
+ }
+
+ if (!delegate_->CanActivate())
+ return TRUE;
+
+ // The frame may need to redraw as a result of the activation change.
+ // We can get WM_NCACTIVATE before we're actually visible. If we're not
+ // visible, no need to paint.
+ if (IsVisible())
+ GetWidget()->non_client_view()->SchedulePaint();
+
+ if (!GetWidget()->ShouldUseNativeFrame()) {
+ // TODO(beng, et al): Hack to redraw this window and child windows
+ // synchronously upon activation. Not all child windows are redrawing
+ // themselves leading to issues like http://crbug.com/74604
+ // We redraw out-of-process HWNDs asynchronously to avoid hanging the
+ // whole app if a child HWND belonging to a hung plugin is encountered.
+ RedrawWindow(GetNativeView(), NULL, NULL,
+ RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW);
+ EnumChildWindows(GetNativeView(), EnumChildWindowsForRedraw, NULL);
+ }
+
+ // If we're active again, we should be allowed to render as inactive, so
+ // tell the non-client view.
+ bool inactive_rendering_disabled = delegate_->IsInactiveRenderingDisabled();
+ if (IsActive())
+ delegate_->EnableInactiveRendering();
+
+ // Avoid DefWindowProc non-client rendering over our custom frame.
+ if (!GetWidget()->ShouldUseNativeFrame()) {
+ SetMsgHandled(TRUE);
+ return TRUE;
+ }
+
+ return DefWindowProcWithRedrawLock(WM_NCACTIVATE,
+ inactive_rendering_disabled || active, 0);
+}
+
+LRESULT NativeWidgetWin::OnNCCalcSize(BOOL mode, LPARAM l_param) {
+ // We only override the default handling if we need to specify a custom
+ // non-client edge width. Note that in most cases "no insets" means no
+ // custom width, but in fullscreen mode we want a custom width of 0.
+ gfx::Insets insets = GetClientAreaInsets();
+ if (insets.empty() && !IsFullscreen()) {
+ SetMsgHandled(FALSE);
+ return 0;
+ }
+
+ RECT* client_rect = mode ?
+ &(reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0]) :
+ reinterpret_cast<RECT*>(l_param);
+ client_rect->left += insets.left();
+ client_rect->top += insets.top();
+ client_rect->bottom -= insets.bottom();
+ client_rect->right -= insets.right();
+ if (IsMaximized()) {
+ // Find all auto-hide taskbars along the screen edges and adjust in by the
+ // thickness of the auto-hide taskbar on each such edge, so the window isn't
+ // treated as a "fullscreen app", which would cause the taskbars to
+ // disappear.
+ HMONITOR monitor = MonitorFromWindow(GetNativeView(),
+ MONITOR_DEFAULTTONULL);
+ if (!monitor) {
+ // We might end up here if the window was previously minimized and the
+ // user clicks on the taskbar button to restore it in the previously
+ // maximized position. In that case WM_NCCALCSIZE is sent before the
+ // window coordinates are restored to their previous values, so our
+ // (left,top) would probably be (-32000,-32000) like all minimized
+ // windows. So the above MonitorFromWindow call fails, but if we check
+ // the window rect given with WM_NCCALCSIZE (which is our previous
+ // restored window position) we will get the correct monitor handle.
+ monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL);
+ if (!monitor) {
+ // This is probably an extreme case that we won't hit, but if we don't
+ // intersect any monitor, let us not adjust the client rect since our
+ // window will not be visible anyway.
+ return 0;
+ }
+ }
+ if (GetTopmostAutoHideTaskbarForEdge(ABE_LEFT, monitor))
+ client_rect->left += kAutoHideTaskbarThicknessPx;
+ if (GetTopmostAutoHideTaskbarForEdge(ABE_TOP, monitor)) {
+ if (GetWidget()->ShouldUseNativeFrame()) {
+ // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of
+ // WM_NCHITTEST, having any nonclient area atop the window causes the
+ // caption buttons to draw onscreen but not respond to mouse
+ // hover/clicks.
+ // So for a taskbar at the screen top, we can't push the
+ // client_rect->top down; instead, we move the bottom up by one pixel,
+ // which is the smallest change we can make and still get a client area
+ // less than the screen size. This is visibly ugly, but there seems to
+ // be no better solution.
+ --client_rect->bottom;
+ } else {
+ client_rect->top += kAutoHideTaskbarThicknessPx;
+ }
+ }
+ if (GetTopmostAutoHideTaskbarForEdge(ABE_RIGHT, monitor))
+ client_rect->right -= kAutoHideTaskbarThicknessPx;
+ if (GetTopmostAutoHideTaskbarForEdge(ABE_BOTTOM, monitor))
+ client_rect->bottom -= kAutoHideTaskbarThicknessPx;
+
+ // We cannot return WVR_REDRAW when there is nonclient area, or Windows
+ // exhibits bugs where client pixels and child HWNDs are mispositioned by
+ // the width/height of the upper-left nonclient area.
+ return 0;
+ }
+
+ // If the window bounds change, we're going to relayout and repaint anyway.
+ // Returning WVR_REDRAW avoids an extra paint before that of the old client
+ // pixels in the (now wrong) location, and thus makes actions like resizing a
+ // window from the left edge look slightly less broken.
+ // We special case when left or top insets are 0, since these conditions
+ // actually require another repaint to correct the layout after glass gets
+ // turned on and off.
+ if (insets.left() == 0 || insets.top() == 0)
+ return 0;
+ return mode ? WVR_REDRAW : 0;
+}
+
+LRESULT NativeWidgetWin::OnNCHitTest(const CPoint& point) {
+ if (!GetWidget()->non_client_view()) {
+ SetMsgHandled(FALSE);
+ return 0;
+ }
+
+ // If the DWM is rendering the window controls, we need to give the DWM's
+ // default window procedure first chance to handle hit testing.
+ if (GetWidget()->ShouldUseNativeFrame()) {
+ LRESULT result;
+ if (DwmDefWindowProc(GetNativeView(), WM_NCHITTEST, 0,
+ MAKELPARAM(point.x, point.y), &result)) {
+ return result;
+ }
+ }
+
+ // First, give the NonClientView a chance to test the point to see if it
+ // provides any of the non-client area.
+ POINT temp = point;
+ MapWindowPoints(HWND_DESKTOP, GetNativeView(), &temp, 1);
+ int component = delegate_->GetNonClientComponent(gfx::Point(temp));
+ if (component != HTNOWHERE)
+ return component;
+
+ // Otherwise, we let Windows do all the native frame non-client handling for
+ // us.
+ SetMsgHandled(FALSE);
+ return 0;
+}
+
+void NativeWidgetWin::OnNCPaint(HRGN rgn) {
+ // We only do non-client painting if we're not using the native frame.
+ // It's required to avoid some native painting artifacts from appearing when
+ // the window is resized.
+ if (!GetWidget()->non_client_view() || GetWidget()->ShouldUseNativeFrame()) {
+ SetMsgHandled(FALSE);
+ return;
+ }
+
+ // We have an NC region and need to paint it. We expand the NC region to
+ // include the dirty region of the root view. This is done to minimize
+ // paints.
+ CRect window_rect;
+ GetWindowRect(&window_rect);
+
+ if (window_rect.Width() != GetWidget()->GetRootView()->width() ||
+ window_rect.Height() != GetWidget()->GetRootView()->height()) {
+ // If the size of the window differs from the size of the root view it
+ // means we're being asked to paint before we've gotten a WM_SIZE. This can
+ // happen when the user is interactively resizing the window. To avoid
+ // mass flickering we don't do anything here. Once we get the WM_SIZE we'll
+ // reset the region of the window which triggers another WM_NCPAINT and
+ // all is well.
+ return;
+ }
+
+ CRect dirty_region;
+ // A value of 1 indicates paint all.
+ if (!rgn || rgn == reinterpret_cast<HRGN>(1)) {
+ dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height());
+ } else {
+ RECT rgn_bounding_box;
+ GetRgnBox(rgn, &rgn_bounding_box);
+ if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect))
+ return; // Dirty region doesn't intersect window bounds, bale.
+
+ // rgn_bounding_box is in screen coordinates. Map it to window coordinates.
+ OffsetRect(&dirty_region, -window_rect.left, -window_rect.top);
+ }
+
+ // In theory GetDCEx should do what we want, but I couldn't get it to work.
+ // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell
+ // it doesn't work at all. So, instead we get the DC for the window then
+ // manually clip out the children.
+ HDC dc = GetWindowDC(GetNativeView());
+ ClipState clip_state;
+ clip_state.x = window_rect.left;
+ clip_state.y = window_rect.top;
+ clip_state.parent = GetNativeView();
+ clip_state.dc = dc;
+ EnumChildWindows(GetNativeView(), &ClipDCToChild,
+ reinterpret_cast<LPARAM>(&clip_state));
+
+ gfx::Rect old_paint_region = invalid_rect();
+
+ if (!old_paint_region.IsEmpty()) {
+ // The root view has a region that needs to be painted. Include it in the
+ // region we're going to paint.
+
+ CRect old_paint_region_crect = old_paint_region.ToRECT();
+ CRect tmp = dirty_region;
+ UnionRect(&dirty_region, &tmp, &old_paint_region_crect);
+ }
+
+ GetWidget()->GetRootView()->SchedulePaintInRect(gfx::Rect(dirty_region));
+
+ // gfx::CanvasSkiaPaint's destructor does the actual painting. As such, wrap
+ // the following in a block to force paint to occur so that we can release
+ // the dc.
+ {
+ gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left,
+ dirty_region.top, dirty_region.Width(),
+ dirty_region.Height());
+ delegate_->OnNativeWidgetPaint(&canvas);
+ }
+
+ ReleaseDC(GetNativeView(), dc);
+ // When using a custom frame, we want to avoid calling DefWindowProc() since
+ // that may render artifacts.
+ SetMsgHandled(!GetWidget()->ShouldUseNativeFrame());
+}
+
+LRESULT NativeWidgetWin::OnNCUAHDrawCaption(UINT msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
+ // an explanation about why we need to handle this message.
+ SetMsgHandled(!GetWidget()->ShouldUseNativeFrame());
+ return 0;
+}
+
+LRESULT NativeWidgetWin::OnNCUAHDrawFrame(UINT msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
+ // an explanation about why we need to handle this message.
+ SetMsgHandled(!GetWidget()->ShouldUseNativeFrame());
+ return 0;
+}
+
+LRESULT NativeWidgetWin::OnNotify(int w_param, NMHDR* l_param) {
+ // We can be sent this message before the tooltip manager is created, if a
+ // subclass overrides OnCreate and creates some kind of Windows control there
+ // that sends WM_NOTIFY messages.
+ if (tooltip_manager_.get()) {
+ bool handled;
+ LRESULT result = tooltip_manager_->OnNotify(w_param, l_param, &handled);
+ SetMsgHandled(handled);
+ return result;
+ }
+ SetMsgHandled(FALSE);
+ return 0;
+}
+
+void NativeWidgetWin::OnPaint(HDC dc) {
+ RECT dirty_rect;
+ // Try to paint accelerated first.
+ if (GetUpdateRect(hwnd(), &dirty_rect, FALSE) &&
+ !IsRectEmpty(&dirty_rect)) {
+ if (delegate_->OnNativeWidgetPaintAccelerated(
+ gfx::Rect(dirty_rect))) {
+ ValidateRect(hwnd(), NULL);
+ } else {
+ scoped_ptr<gfx::CanvasPaint> canvas(
+ gfx::CanvasPaint::CreateCanvasPaint(hwnd()));
+ delegate_->OnNativeWidgetPaint(canvas->AsCanvas());
+ }
+ } else {
+ // TODO(msw): Find a better solution for this crbug.com/93530 workaround.
+ // Some scenarios otherwise fail to validate minimized app/popup windows.
+ ValidateRect(hwnd(), NULL);
+ }
+}
+
+LRESULT NativeWidgetWin::OnPowerBroadcast(DWORD power_event, DWORD data) {
+ base::SystemMonitor* monitor = base::SystemMonitor::Get();
+ if (monitor)
+ monitor->ProcessWmPowerBroadcastMessage(power_event);
+ SetMsgHandled(FALSE);
+ return 0;
+}
+
+LRESULT NativeWidgetWin::OnReflectedMessage(UINT msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ SetMsgHandled(FALSE);
+ return 0;
+}
+
+LRESULT NativeWidgetWin::OnSetCursor(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ // Using ScopedRedrawLock here frequently allows content behind this window to
+ // paint in front of this window, causing glaring rendering artifacts.
+ // If omitting ScopedRedrawLock here triggers caption rendering artifacts via
+ // DefWindowProc message handling, we'll need to find a better solution.
+ SetMsgHandled(FALSE);
+ return 0;
+}
+
+void NativeWidgetWin::OnSetFocus(HWND focused_window) {
+ delegate_->OnNativeFocus(focused_window);
+ InputMethod* input_method = GetWidget()->GetInputMethodDirect();
+ if (input_method)
+ input_method->OnFocus();
+ SetMsgHandled(FALSE);
+}
+
+LRESULT NativeWidgetWin::OnSetIcon(UINT size_type, HICON new_icon) {
+ // This shouldn't hurt even if we're using the native frame.
+ return DefWindowProcWithRedrawLock(WM_SETICON, size_type,
+ reinterpret_cast<LPARAM>(new_icon));
+}
+
+LRESULT NativeWidgetWin::OnSetText(const wchar_t* text) {
+ // This shouldn't hurt even if we're using the native frame.
+ return DefWindowProcWithRedrawLock(WM_SETTEXT, NULL,
+ reinterpret_cast<LPARAM>(text));
+}
+
+void NativeWidgetWin::OnSettingChange(UINT flags, const wchar_t* section) {
+ if (!GetParent() && (flags == SPI_SETWORKAREA) &&
+ !GetWidget()->widget_delegate()->WillProcessWorkAreaChange()) {
+ // Fire a dummy SetWindowPos() call, so we'll trip the code in
+ // OnWindowPosChanging() below that notices work area changes.
+ ::SetWindowPos(GetNativeView(), 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
+ SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+ SetMsgHandled(TRUE);
+ } else {
+ // TODO(beng): move to Widget.
+ if (flags == SPI_SETWORKAREA)
+ GetWidget()->widget_delegate()->OnWorkAreaChanged();
+ SetMsgHandled(FALSE);
+ }
+}
+
+void NativeWidgetWin::OnSize(UINT param, const CSize& size) {
+ RedrawWindow(GetNativeView(), NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
+ // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've
+ // invoked OnSize we ensure the RootView has been laid out.
+ ResetWindowRegion(false);
+}
+
+void NativeWidgetWin::OnSysCommand(UINT notification_code, CPoint click) {
+ if (!GetWidget()->non_client_view())
+ return;
+
+ // Windows uses the 4 lower order bits of |notification_code| for type-
+ // specific information so we must exclude this when comparing.
+ static const int sc_mask = 0xFFF0;
+ // Ignore size/move/maximize in fullscreen mode.
+ if (IsFullscreen() &&
+ (((notification_code & sc_mask) == SC_SIZE) ||
+ ((notification_code & sc_mask) == SC_MOVE) ||
+ ((notification_code & sc_mask) == SC_MAXIMIZE)))
+ return;
+ if (!GetWidget()->ShouldUseNativeFrame()) {
+ if ((notification_code & sc_mask) == SC_MINIMIZE ||
+ (notification_code & sc_mask) == SC_MAXIMIZE ||
+ (notification_code & sc_mask) == SC_RESTORE) {
+ GetWidget()->non_client_view()->ResetWindowControls();
+ } else if ((notification_code & sc_mask) == SC_MOVE ||
+ (notification_code & sc_mask) == SC_SIZE) {
+ if (!IsVisible()) {
+ // Circumvent ScopedRedrawLocks and force visibility before entering a
+ // resize or move modal loop to get continuous sizing/moving feedback.
+ SetWindowLong(GWL_STYLE, GetWindowLong(GWL_STYLE) | WS_VISIBLE);
+ }
+ }
+ }
+
+ // Handle SC_KEYMENU, which means that the user has pressed the ALT
+ // key and released it, so we should focus the menu bar.
+ if ((notification_code & sc_mask) == SC_KEYMENU && click.x == 0) {
+ // Retrieve the status of shift and control keys to prevent consuming
+ // shift+alt keys, which are used by Windows to change input languages.
+ ui::Accelerator accelerator(ui::KeyboardCodeForWindowsKeyCode(VK_MENU),
+ !!(GetKeyState(VK_SHIFT) & 0x8000),
+ !!(GetKeyState(VK_CONTROL) & 0x8000),
+ false);
+ GetWidget()->GetFocusManager()->ProcessAccelerator(accelerator);
+ return;
+ }
+
+ // If the delegate can't handle it, the system implementation will be called.
+ if (!delegate_->ExecuteCommand(notification_code)) {
+ DefWindowProc(GetNativeView(), WM_SYSCOMMAND, notification_code,
+ MAKELPARAM(click.x, click.y));
+ }
+}
+
+void NativeWidgetWin::OnThemeChanged() {
+ // Notify NativeThemeWin.
+ gfx::NativeThemeWin::instance()->CloseHandles();
+}
+
+void NativeWidgetWin::OnVScroll(int scroll_type,
+ short position,
+ HWND scrollbar) {
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnWindowPosChanging(WINDOWPOS* window_pos) {
+ if (ignore_window_pos_changes_) {
+ // If somebody's trying to toggle our visibility, change the nonclient area,
+ // change our Z-order, or activate us, we should probably let it go through.
+ if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) |
+ SWP_FRAMECHANGED)) &&
+ (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) {
+ // Just sizing/moving the window; ignore.
+ window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW;
+ window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW);
+ }
+ } else if (!GetParent()) {
+ CRect window_rect;
+ HMONITOR monitor;
+ gfx::Rect monitor_rect, work_area;
+ if (GetWindowRect(&window_rect) &&
+ GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) {
+ if (monitor && (monitor == last_monitor_) &&
+ (IsFullscreen() || ((monitor_rect == last_monitor_rect_) &&
+ (work_area != last_work_area_)))) {
+ // A rect for the monitor we're on changed. Normally Windows notifies
+ // us about this (and thus we're reaching here due to the SetWindowPos()
+ // call in OnSettingChange() above), but with some software (e.g.
+ // nVidia's nView desktop manager) the work area can change asynchronous
+ // to any notification, and we're just sent a SetWindowPos() call with a
+ // new (frequently incorrect) position/size. In either case, the best
+ // response is to throw away the existing position/size information in
+ // |window_pos| and recalculate it based on the new work rect.
+ gfx::Rect new_window_rect;
+ if (IsFullscreen()) {
+ new_window_rect = monitor_rect;
+ } else if (IsZoomed()) {
+ new_window_rect = work_area;
+ int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
+ new_window_rect.Inset(-border_thickness, -border_thickness);
+ } else {
+ new_window_rect = gfx::Rect(window_rect).AdjustToFit(work_area);
+ }
+ window_pos->x = new_window_rect.x();
+ window_pos->y = new_window_rect.y();
+ window_pos->cx = new_window_rect.width();
+ window_pos->cy = new_window_rect.height();
+ // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child
+ // HWNDs for some reason.
+ window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW);
+ window_pos->flags |= SWP_NOCOPYBITS;
+
+ // Now ignore all immediately-following SetWindowPos() changes. Windows
+ // likes to (incorrectly) recalculate what our position/size should be
+ // and send us further updates.
+ ignore_window_pos_changes_ = true;
+ DCHECK(!ignore_pos_changes_factory_.HasWeakPtrs());
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&NativeWidgetWin::StopIgnoringPosChanges,
+ ignore_pos_changes_factory_.GetWeakPtr()));
+ }
+ last_monitor_ = monitor;
+ last_monitor_rect_ = monitor_rect;
+ last_work_area_ = work_area;
+ }
+ }
+
+ if (force_hidden_count_) {
+ // Prevent the window from being made visible if we've been asked to do so.
+ // See comment in header as to why we might want this.
+ window_pos->flags &= ~SWP_SHOWWINDOW;
+ }
+
+ // When WM_WINDOWPOSCHANGING message is handled by DefWindowProc, it will
+ // enforce (cx, cy) not to be smaller than (6, 6) for any non-popup window.
+ // We work around this by changing cy back to our intended value.
+ if (!GetParent() && ~(window_pos->flags & SWP_NOSIZE) && window_pos->cy < 6) {
+ LONG old_cy = window_pos->cy;
+ DefWindowProc(GetNativeView(), WM_WINDOWPOSCHANGING, 0,
+ reinterpret_cast<LPARAM>(window_pos));
+ window_pos->cy = old_cy;
+ SetMsgHandled(TRUE);
+ return;
+ }
+
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnWindowPosChanged(WINDOWPOS* window_pos) {
+ if (DidClientAreaSizeChange(window_pos))
+ ClientAreaSizeChanged();
+ if (window_pos->flags & SWP_SHOWWINDOW)
+ delegate_->OnNativeWidgetVisibilityChanged(true);
+ else if (window_pos->flags & SWP_HIDEWINDOW)
+ delegate_->OnNativeWidgetVisibilityChanged(false);
+ SetMsgHandled(FALSE);
+}
+
+void NativeWidgetWin::OnFinalMessage(HWND window) {
+ // We don't destroy props in WM_DESTROY as we may still get messages after
+ // WM_DESTROY that assume the properties are still valid (such as WM_CLOSE).
+ props_.reset();
+ delegate_->OnNativeWidgetDestroyed();
+ if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
+ delete this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetWin, protected:
+
+int NativeWidgetWin::GetShowState() const {
+ return SW_SHOWNORMAL;
+}
+
+gfx::Insets NativeWidgetWin::GetClientAreaInsets() const {
+ // Returning an empty Insets object causes the default handling in
+ // NativeWidgetWin::OnNCCalcSize() to be invoked.
+ if (!has_non_client_view_ || GetWidget()->ShouldUseNativeFrame())
+ return gfx::Insets();
+
+ if (IsMaximized()) {
+ // Windows automatically adds a standard width border to all sides when a
+ // window is maximized.
+ int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
+ return gfx::Insets(border_thickness, border_thickness, border_thickness,
+ border_thickness);
+ }
+ // This is weird, but highly essential. If we don't offset the bottom edge
+ // of the client rect, the window client area and window area will match,
+ // and when returning to glass rendering mode from non-glass, the client
+ // area will not paint black as transparent. This is because (and I don't
+ // know why) the client area goes from matching the window rect to being
+ // something else. If the client area is not the window rect in both
+ // modes, the blackness doesn't occur. Because of this, we need to tell
+ // the RootView to lay out to fit the window rect, rather than the client
+ // rect when using the opaque frame.
+ // Note: this is only required for non-fullscreen windows. Note that
+ // fullscreen windows are in restored state, not maximized.
+ return gfx::Insets(0, 0, IsFullscreen() ? 0 : 1, 0);
+}
+
+void NativeWidgetWin::TrackMouseEvents(DWORD mouse_tracking_flags) {
+ // Begin tracking mouse events for this HWND so that we get WM_MOUSELEAVE
+ // when the user moves the mouse outside this HWND's bounds.
+ if (active_mouse_tracking_flags_ == 0 || mouse_tracking_flags & TME_CANCEL) {
+ if (mouse_tracking_flags & TME_CANCEL) {
+ // We're about to cancel active mouse tracking, so empty out the stored
+ // state.
+ active_mouse_tracking_flags_ = 0;
+ } else {
+ active_mouse_tracking_flags_ = mouse_tracking_flags;
+ }
+
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = mouse_tracking_flags;
+ tme.hwndTrack = hwnd();
+ tme.dwHoverTime = 0;
+ TrackMouseEvent(&tme);
+ } else if (mouse_tracking_flags != active_mouse_tracking_flags_) {
+ TrackMouseEvents(active_mouse_tracking_flags_ | TME_CANCEL);
+ TrackMouseEvents(mouse_tracking_flags);
+ }
+}
+
+void NativeWidgetWin::OnScreenReaderDetected() {
+ screen_reader_active_ = true;
+}
+
+void NativeWidgetWin::SetInitialFocus() {
+ if (!GetWidget()->SetInitialFocus() &&
+ !(GetWindowLong(GWL_EXSTYLE) & WS_EX_TRANSPARENT) &&
+ !(GetWindowLong(GWL_EXSTYLE) & WS_EX_NOACTIVATE)) {
+ // The window does not get keyboard messages unless we focus it.
+ SetFocus(GetNativeView());
+ }
+}
+
+void NativeWidgetWin::ExecuteSystemMenuCommand(int command) {
+ if (command)
+ SendMessage(GetNativeView(), WM_SYSCOMMAND, command, 0);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeWidgetWin, private:
+
+// static
+void NativeWidgetWin::PostProcessActivateMessage(NativeWidgetWin* widget,
+ int activation_state) {
+ DCHECK(widget->GetWidget()->is_top_level());
+ FocusManager* focus_manager = widget->GetWidget()->GetFocusManager();
+ if (WA_INACTIVE == activation_state) {
+ // We might get activated/inactivated without being enabled, so we need to
+ // clear restore_focus_when_enabled_.
+ widget->restore_focus_when_enabled_ = false;
+ focus_manager->StoreFocusedView();
+ } else {
+ // We must restore the focus after the message has been DefProc'ed as it
+ // does set the focus to the last focused HWND.
+ // Note that if the window is not enabled, we cannot restore the focus as
+ // calling ::SetFocus on a child of the non-enabled top-window would fail.
+ // This is the case when showing a modal dialog (such as 'open file',
+ // 'print'...) from a different thread.
+ // In that case we delay the focus restoration to when the window is enabled
+ // again.
+ if (!IsWindowEnabled(widget->GetNativeView())) {
+ DCHECK(!widget->restore_focus_when_enabled_);
+ widget->restore_focus_when_enabled_ = true;
+ return;
+ }
+ focus_manager->RestoreFocusedView();
+ }
+}
+
+void NativeWidgetWin::SetInitParams(const Widget::InitParams& params) {
+ // Set non-style attributes.
+ ownership_ = params.ownership;
+
+ DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
+ DWORD ex_style = 0;
+ DWORD class_style = CS_DBLCLKS;
+
+ // Set type-independent style attributes.
+ if (params.child)
+ style |= WS_CHILD;
+ if (params.show_state == ui::SHOW_STATE_MAXIMIZED)
+ style |= WS_MAXIMIZE;
+ if (params.show_state == ui::SHOW_STATE_MINIMIZED)
+ style |= WS_MINIMIZE;
+ if (!params.accept_events)
+ ex_style |= WS_EX_TRANSPARENT;
+ if (!params.can_activate)
+ ex_style |= WS_EX_NOACTIVATE;
+ if (params.keep_on_top)
+ ex_style |= WS_EX_TOPMOST;
+ if (params.mirror_origin_in_rtl)
+ ex_style |= l10n_util::GetExtendedTooltipStyles();
+ if (params.transparent)
+ ex_style |= WS_EX_LAYERED;
+ if (params.has_dropshadow) {
+ class_style |= (base::win::GetVersion() < base::win::VERSION_XP) ?
+ 0 : CS_DROPSHADOW;
+ }
+
+ // Set type-dependent style attributes.
+ switch (params.type) {
+ case Widget::InitParams::TYPE_WINDOW: {
+ style |= WS_SYSMENU | WS_CAPTION;
+ bool can_resize = GetWidget()->widget_delegate()->CanResize();
+ bool can_maximize = GetWidget()->widget_delegate()->CanMaximize();
+ if (can_maximize) {
+ style |= WS_OVERLAPPEDWINDOW;
+ } else if (can_resize) {
+ style |= WS_OVERLAPPED | WS_THICKFRAME;
+ }
+ if (delegate_->IsDialogBox()) {
+ style |= DS_MODALFRAME;
+ // NOTE: Turning this off means we lose the close button, which is bad.
+ // Turning it on though means the user can maximize or size the window
+ // from the system menu, which is worse. We may need to provide our own
+ // menu to get the close button to appear properly.
+ // style &= ~WS_SYSMENU;
+
+ // Set the WS_POPUP style for modal dialogs. This ensures that the owner
+ // window is activated on destruction. This style should not be set for
+ // non-modal non-top-level dialogs like constrained windows.
+ style |= delegate_->IsModal() ? WS_POPUP : 0;
+ }
+ ex_style |= delegate_->IsDialogBox() ? WS_EX_DLGMODALFRAME : 0;
+ break;
+ }
+ case Widget::InitParams::TYPE_CONTROL:
+ style |= WS_VISIBLE;
+ break;
+ case Widget::InitParams::TYPE_WINDOW_FRAMELESS:
+ style |= WS_POPUP;
+ break;
+ case Widget::InitParams::TYPE_BUBBLE:
+ style |= WS_POPUP;
+ style |= WS_CLIPCHILDREN;
+ break;
+ case Widget::InitParams::TYPE_POPUP:
+ style |= WS_POPUP;
+ ex_style |= WS_EX_TOOLWINDOW;
+ break;
+ case Widget::InitParams::TYPE_MENU:
+ style |= WS_POPUP;
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ set_initial_class_style(class_style);
+ set_window_style(window_style() | style);
+ set_window_ex_style(window_ex_style() | ex_style);
+
+ has_non_client_view_ = Widget::RequiresNonClientView(params.type);
+}
+
+void NativeWidgetWin::RedrawInvalidRect() {
+ if (!use_layered_buffer_) {
+ RECT r = { 0, 0, 0, 0 };
+ if (GetUpdateRect(hwnd(), &r, FALSE) && !IsRectEmpty(&r)) {
+ RedrawWindow(hwnd(), &r, NULL,
+ RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOCHILDREN);
+ }
+ }
+}
+
+void NativeWidgetWin::RedrawLayeredWindowContents() {
+ if (invalid_rect_.IsEmpty())
+ return;
+
+ // We need to clip to the dirty rect ourselves.
+ layered_window_contents_->sk_canvas()->save(SkCanvas::kClip_SaveFlag);
+ layered_window_contents_->ClipRect(invalid_rect_);
+ GetWidget()->GetRootView()->Paint(layered_window_contents_.get());
+ layered_window_contents_->sk_canvas()->restore();
+
+ RECT wr;
+ GetWindowRect(&wr);
+ SIZE size = {wr.right - wr.left, wr.bottom - wr.top};
+ POINT position = {wr.left, wr.top};
+ HDC dib_dc = skia::BeginPlatformPaint(layered_window_contents_->sk_canvas());
+ POINT zero = {0, 0};
+ BLENDFUNCTION blend = {AC_SRC_OVER, 0, layered_alpha_, AC_SRC_ALPHA};
+ UpdateLayeredWindow(hwnd(), NULL, &position, &size, dib_dc, &zero,
+ RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA);
+ invalid_rect_.SetRect(0, 0, 0, 0);
+ skia::EndPlatformPaint(layered_window_contents_->sk_canvas());
+}
+
+void NativeWidgetWin::LockUpdates() {
+ // We skip locked updates when Aero is on for two reasons:
+ // 1. Because it isn't necessary
+ // 2. Because toggling the WS_VISIBLE flag may occur while the GPU process is
+ // attempting to present a child window's backbuffer onscreen. When these
+ // two actions race with one another, the child window will either flicker
+ // or will simply stop updating entirely.
+ if (!IsAeroGlassEnabled() && ++lock_updates_count_ == 1) {
+ SetWindowLong(GWL_STYLE, GetWindowLong(GWL_STYLE) & ~WS_VISIBLE);
+ }
+ // TODO(msw): Remove nested LockUpdates VLOG info for crbug.com/93530.
+ VLOG_IF(1, (lock_updates_count_ > 1)) << "Nested LockUpdates call: "
+ << lock_updates_count_ << " locks for widget " << this;
+}
+
+void NativeWidgetWin::UnlockUpdates() {
+ // TODO(msw): Remove nested LockUpdates VLOG info for crbug.com/93530.
+ VLOG_IF(1, (lock_updates_count_ > 1)) << "Nested UnlockUpdates call: "
+ << lock_updates_count_ << " locks for widget " << this;
+ if (!IsAeroGlassEnabled() && --lock_updates_count_ <= 0) {
+ SetWindowLong(GWL_STYLE, GetWindowLong(GWL_STYLE) | WS_VISIBLE);
+ lock_updates_count_ = 0;
+ }
+}
+
+bool NativeWidgetWin::WidgetSizeIsClientSize() const {
+ const Widget* widget = GetWidget()->GetTopLevelWidget();
+ return IsZoomed() || (widget && widget->ShouldUseNativeFrame());
+}
+
+void NativeWidgetWin::ClientAreaSizeChanged() {
+ RECT r;
+ if (WidgetSizeIsClientSize())
+ GetClientRect(&r);
+ else
+ GetWindowRect(&r);
+ gfx::Size s(std::max(0, static_cast<int>(r.right - r.left)),
+ std::max(0, static_cast<int>(r.bottom - r.top)));
+ if (compositor_.get())
+ compositor_->WidgetSizeChanged(s);
+ delegate_->OnNativeWidgetSizeChanged(s);
+ if (use_layered_buffer_) {
+ layered_window_contents_.reset(
+ new gfx::CanvasSkia(s.width(), s.height(), false));
+ }
+}
+
+void NativeWidgetWin::ResetWindowRegion(bool force) {
+ // A native frame uses the native window region, and we don't want to mess
+ // with it.
+ if (GetWidget()->ShouldUseNativeFrame() || !GetWidget()->non_client_view()) {
+ if (force)
+ SetWindowRgn(NULL, TRUE);
+ return;
+ }
+
+ // Changing the window region is going to force a paint. Only change the
+ // window region if the region really differs.
+ HRGN current_rgn = CreateRectRgn(0, 0, 0, 0);
+ int current_rgn_result = GetWindowRgn(GetNativeView(), current_rgn);
+
+ CRect window_rect;
+ GetWindowRect(&window_rect);
+ HRGN new_region;
+ if (IsMaximized()) {
+ HMONITOR monitor =
+ MonitorFromWindow(GetNativeView(), MONITOR_DEFAULTTONEAREST);
+ MONITORINFO mi;
+ mi.cbSize = sizeof mi;
+ GetMonitorInfo(monitor, &mi);
+ CRect work_rect = mi.rcWork;
+ work_rect.OffsetRect(-window_rect.left, -window_rect.top);
+ new_region = CreateRectRgnIndirect(&work_rect);
+ } else {
+ gfx::Path window_mask;
+ GetWidget()->non_client_view()->GetWindowMask(
+ gfx::Size(window_rect.Width(), window_rect.Height()), &window_mask);
+ new_region = window_mask.CreateNativeRegion();
+ }
+
+ if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) {
+ // SetWindowRgn takes ownership of the HRGN created by CreateNativeRegion.
+ SetWindowRgn(new_region, TRUE);
+ } else {
+ DeleteObject(new_region);
+ }
+
+ DeleteObject(current_rgn);
+}
+
+LRESULT NativeWidgetWin::DefWindowProcWithRedrawLock(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) {
+ ScopedRedrawLock lock(this);
+ // The Widget and HWND can be destroyed in the call to DefWindowProc, so use
+ // the |destroyed_| flag to avoid unlocking (and crashing) after destruction.
+ bool destroyed = false;
+ destroyed_ = &destroyed;
+ LRESULT result = DefWindowProc(GetNativeView(), message, w_param, l_param);
+ if (destroyed)
+ lock.CancelUnlockOperation();
+ else
+ destroyed_ = NULL;
+ return result;
+}
+
+void NativeWidgetWin::RestoreEnabledIfNecessary() {
+ if (delegate_->IsModal() && !restored_enabled_) {
+ restored_enabled_ = true;
+ // If we were run modally, we need to undo the disabled-ness we inflicted on
+ // the owner's parent hierarchy.
+ HWND start = ::GetWindow(GetNativeView(), GW_OWNER);
+ while (start) {
+ ::EnableWindow(start, TRUE);
+ start = ::GetParent(start);
+ }
+ }
+}
+
+void NativeWidgetWin::DispatchKeyEventPostIME(const KeyEvent& key) {
+ SetMsgHandled(delegate_->OnKeyEvent(key));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget, public:
+
+// static
+void Widget::NotifyLocaleChanged() {
+ NOTIMPLEMENTED();
+}
+
+namespace {
+BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) {
+ Widget* widget = Widget::GetWidgetForNativeView(hwnd);
+ if (widget && widget->is_secondary_widget())
+ widget->Close();
+ return TRUE;
+}
+} // namespace
+
+// static
+void Widget::CloseAllSecondaryWidgets() {
+ EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0);
+}
+
+bool Widget::ConvertRect(const Widget* source,
+ const Widget* target,
+ gfx::Rect* rect) {
+ DCHECK(source);
+ DCHECK(target);
+ DCHECK(rect);
+
+ HWND source_hwnd = source->GetNativeView();
+ HWND target_hwnd = target->GetNativeView();
+ if (source_hwnd == target_hwnd)
+ return true;
+
+ RECT win_rect = rect->ToRECT();
+ if (::MapWindowPoints(source_hwnd, target_hwnd,
+ reinterpret_cast<LPPOINT>(&win_rect),
+ sizeof(RECT)/sizeof(POINT))) {
+ *rect = win_rect;
+ return true;
+ }
+ return false;
+}
+
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// internal::NativeWidgetPrivate, public:
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget(
+ internal::NativeWidgetDelegate* delegate) {
+ return new NativeWidgetWin(delegate);
+}
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView(
+ gfx::NativeView native_view) {
+ return reinterpret_cast<NativeWidgetWin*>(
+ ViewProp::GetValue(native_view, kNativeWidgetKey));
+}
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
+ gfx::NativeWindow native_window) {
+ return GetNativeWidgetForNativeView(native_window);
+}
+
+// static
+NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget(
+ gfx::NativeView native_view) {
+ if (!native_view)
+ return NULL;
+
+ // First, check if the top-level window is a Widget.
+ HWND root = ::GetAncestor(native_view, GA_ROOT);
+ if (!root)
+ return NULL;
+
+ NativeWidgetPrivate* widget = GetNativeWidgetForNativeView(root);
+ if (widget)
+ return widget;
+
+ // Second, try to locate the last Widget window in the parent hierarchy.
+ HWND parent_hwnd = native_view;
+ // If we fail to find the native widget pointer for the root then it probably
+ // means that the root belongs to a different process in which case we walk up
+ // the native view chain looking for a parent window which corresponds to a
+ // valid native widget. We only do this if we fail to find the native widget
+ // for the current native view which means it is being destroyed.
+ if (!widget && !GetNativeWidgetForNativeView(native_view)) {
+ parent_hwnd = ::GetAncestor(parent_hwnd, GA_PARENT);
+ if (!parent_hwnd)
+ return NULL;
+ }
+ NativeWidgetPrivate* parent_widget;
+ do {
+ parent_widget = GetNativeWidgetForNativeView(parent_hwnd);
+ if (parent_widget) {
+ widget = parent_widget;
+ parent_hwnd = ::GetAncestor(parent_hwnd, GA_PARENT);
+ }
+ } while (parent_hwnd != NULL && parent_widget != NULL);
+
+ return widget;
+}
+
+// static
+void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view,
+ Widget::Widgets* children) {
+ if (!native_view)
+ return;
+
+ Widget* widget = Widget::GetWidgetForNativeView(native_view);
+ if (widget)
+ children->insert(widget);
+ EnumChildWindows(native_view, EnumerateChildWindowsForNativeWidgets,
+ reinterpret_cast<LPARAM>(children));
+}
+
+// static
+void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view,
+ gfx::NativeView new_parent) {
+ if (!native_view)
+ return;
+
+ HWND previous_parent = ::GetParent(native_view);
+ if (previous_parent == new_parent)
+ return;
+
+ Widget::Widgets widgets;
+ GetAllChildWidgets(native_view, &widgets);
+
+ // First notify all the widgets that they are being disassociated
+ // from their previous parent.
+ for (Widget::Widgets::iterator it = widgets.begin();
+ it != widgets.end(); ++it) {
+ // TODO(beng): Rename this notification to NotifyNativeViewChanging()
+ // and eliminate the bool parameter.
+ (*it)->NotifyNativeViewHierarchyChanged(false, previous_parent);
+ }
+
+ ::SetParent(native_view, new_parent);
+
+ // And now, notify them that they have a brand new parent.
+ for (Widget::Widgets::iterator it = widgets.begin();
+ it != widgets.end(); ++it) {
+ (*it)->NotifyNativeViewHierarchyChanged(true, new_parent);
+ }
+}
+
+// static
+bool NativeWidgetPrivate::IsMouseButtonDown() {
+ return (GetKeyState(VK_LBUTTON) & 0x80) ||
+ (GetKeyState(VK_RBUTTON) & 0x80) ||
+ (GetKeyState(VK_MBUTTON) & 0x80) ||
+ (GetKeyState(VK_XBUTTON1) & 0x80) ||
+ (GetKeyState(VK_XBUTTON2) & 0x80);
+}
+
+} // namespace internal
+
+} // namespace views
diff --git a/views/widget/native_widget_win.h b/views/widget/native_widget_win.h
new file mode 100644
index 0000000..e145556
--- /dev/null
+++ b/views/widget/native_widget_win.h
@@ -0,0 +1,664 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_
+#define VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_
+#pragma once
+
+#include <atlbase.h>
+#include <atlapp.h>
+#include <atlcrack.h>
+#include <atlmisc.h>
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/win_util.h"
+#include "ui/base/win/window_impl.h"
+#include "ui/gfx/compositor/compositor.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/layout/layout_manager.h"
+#include "views/widget/native_widget_private.h"
+
+namespace ui {
+class Compositor;
+class ViewProp;
+}
+
+namespace gfx {
+class CanvasSkia;
+class Font;
+class Rect;
+}
+
+namespace views {
+
+class DropTargetWin;
+class RootView;
+class TooltipManagerWin;
+
+// These two messages aren't defined in winuser.h, but they are sent to windows
+// with captions. They appear to paint the window caption and frame.
+// Unfortunately if you override the standard non-client rendering as we do
+// with CustomFrameWindow, sometimes Windows (not deterministically
+// reproducibly but definitely frequently) will send these messages to the
+// window and paint the standard caption/title over the top of the custom one.
+// So we need to handle these messages in CustomFrameWindow to prevent this
+// from happening.
+const int WM_NCUAHDRAWCAPTION = 0xAE;
+const int WM_NCUAHDRAWFRAME = 0xAF;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// NativeWidgetWin
+// A Widget for a views hierarchy used to represent anything that can be
+// contained within an HWND, e.g. a control, a window, etc. Specializations
+// suitable for specific tasks, e.g. top level window, are derived from this.
+//
+// This Widget contains a RootView which owns the hierarchy of views within it.
+// As long as views are part of this tree, they will be deleted automatically
+// when the RootView is destroyed. If you remove a view from the tree, you are
+// then responsible for cleaning up after it.
+//
+///////////////////////////////////////////////////////////////////////////////
+class VIEWS_EXPORT NativeWidgetWin : public ui::WindowImpl,
+ public MessageLoopForUI::Observer,
+ public ui::CompositorDelegate,
+ public internal::NativeWidgetPrivate {
+ public:
+ explicit NativeWidgetWin(internal::NativeWidgetDelegate* delegate);
+ virtual ~NativeWidgetWin();
+
+ // Returns true if we are on Windows Vista or greater and composition is
+ // enabled.
+ static bool IsAeroGlassEnabled();
+
+ // Returns the system set window title font.
+ static gfx::Font GetWindowTitleFont();
+
+ // Show the window with the specified show command.
+ void Show(int show_state);
+
+ // Disable Layered Window updates by setting to false.
+ void set_can_update_layered_window(bool can_update_layered_window) {
+ can_update_layered_window_ = can_update_layered_window;
+ }
+
+ // Obtain the view event with the given MSAA child id. Used in
+ // NativeViewAccessibilityWin::get_accChild to support requests for
+ // children of windowless controls. May return NULL
+ // (see ViewHierarchyChanged).
+ View* GetAccessibilityViewEventAt(int id);
+
+ // Add a view that has recently fired an accessibility event. Returns a MSAA
+ // child id which is generated by: -(index of view in vector + 1) which
+ // guarantees a negative child id. This distinguishes the view from
+ // positive MSAA child id's which are direct leaf children of views that have
+ // associated hWnd's (e.g. NativeWidgetWin).
+ int AddAccessibilityViewEvent(View* view);
+
+ // Clear a view that has recently been removed on a hierarchy change.
+ void ClearAccessibilityViewEvent(View* view);
+
+ // Hides the window if it hasn't already been force-hidden. The force hidden
+ // count is tracked, so calling multiple times is allowed, you just have to
+ // be sure to call PopForceHidden the same number of times.
+ void PushForceHidden();
+
+ // Decrements the force hidden count, showing the window if we have reached
+ // the top of the stack. See PushForceHidden.
+ void PopForceHidden();
+
+ BOOL IsWindow() const {
+ return ::IsWindow(GetNativeView());
+ }
+
+ BOOL ShowWindow(int command) {
+ DCHECK(::IsWindow(GetNativeView()));
+ return ::ShowWindow(GetNativeView(), command);
+ }
+
+ HWND GetParent() const {
+ return ::GetParent(GetNativeView());
+ }
+
+ LONG GetWindowLong(int index) {
+ DCHECK(::IsWindow(GetNativeView()));
+ return ::GetWindowLong(GetNativeView(), index);
+ }
+
+ BOOL GetWindowRect(RECT* rect) const {
+ return ::GetWindowRect(GetNativeView(), rect);
+ }
+
+ LONG SetWindowLong(int index, LONG new_long) {
+ DCHECK(::IsWindow(GetNativeView()));
+ return ::SetWindowLong(GetNativeView(), index, new_long);
+ }
+
+ BOOL SetWindowPos(HWND hwnd_after, int x, int y, int cx, int cy, UINT flags) {
+ DCHECK(::IsWindow(GetNativeView()));
+ return ::SetWindowPos(GetNativeView(), hwnd_after, x, y, cx, cy, flags);
+ }
+
+ BOOL IsZoomed() const {
+ DCHECK(::IsWindow(GetNativeView()));
+ return ::IsZoomed(GetNativeView());
+ }
+
+ BOOL MoveWindow(int x, int y, int width, int height) {
+ return MoveWindow(x, y, width, height, TRUE);
+ }
+
+ BOOL MoveWindow(int x, int y, int width, int height, BOOL repaint) {
+ DCHECK(::IsWindow(GetNativeView()));
+ return ::MoveWindow(GetNativeView(), x, y, width, height, repaint);
+ }
+
+ int SetWindowRgn(HRGN region, BOOL redraw) {
+ DCHECK(::IsWindow(GetNativeView()));
+ return ::SetWindowRgn(GetNativeView(), region, redraw);
+ }
+
+ BOOL GetClientRect(RECT* rect) const {
+ DCHECK(::IsWindow(GetNativeView()));
+ return ::GetClientRect(GetNativeView(), rect);
+ }
+
+ // Overridden from ui::CompositorDelegate:
+ virtual void ScheduleDraw();
+
+ // Overridden from internal::NativeWidgetPrivate:
+ virtual void InitNativeWidget(const Widget::InitParams& params) OVERRIDE;
+ virtual NonClientFrameView* CreateNonClientFrameView() OVERRIDE;
+ virtual void UpdateFrameAfterFrameChange() OVERRIDE;
+ virtual bool ShouldUseNativeFrame() const OVERRIDE;
+ virtual void FrameTypeChanged() OVERRIDE;
+ virtual Widget* GetWidget() OVERRIDE;
+ virtual const Widget* GetWidget() const OVERRIDE;
+ virtual gfx::NativeView GetNativeView() const OVERRIDE;
+ virtual gfx::NativeWindow GetNativeWindow() const OVERRIDE;
+ virtual Widget* GetTopLevelWidget() OVERRIDE;
+ virtual const ui::Compositor* GetCompositor() const OVERRIDE;
+ virtual ui::Compositor* GetCompositor() OVERRIDE;
+ virtual void CalculateOffsetToAncestorWithLayer(
+ gfx::Point* offset,
+ ui::Layer** layer_parent) OVERRIDE;
+ virtual void ReorderLayers() OVERRIDE;
+ virtual void ViewRemoved(View* view) OVERRIDE;
+ virtual void SetNativeWindowProperty(const char* name, void* value) OVERRIDE;
+ virtual void* GetNativeWindowProperty(const char* name) const OVERRIDE;
+ virtual TooltipManager* GetTooltipManager() const OVERRIDE;
+ virtual bool IsScreenReaderActive() const OVERRIDE;
+ virtual void SendNativeAccessibilityEvent(
+ View* view,
+ ui::AccessibilityTypes::Event event_type) OVERRIDE;
+ virtual void SetMouseCapture() OVERRIDE;
+ virtual void ReleaseMouseCapture() OVERRIDE;
+ virtual bool HasMouseCapture() const OVERRIDE;
+ virtual InputMethod* CreateInputMethod() OVERRIDE;
+ virtual void CenterWindow(const gfx::Size& size) OVERRIDE;
+ virtual void GetWindowPlacement(
+ gfx::Rect* bounds,
+ ui::WindowShowState* show_state) const OVERRIDE;
+ virtual void SetWindowTitle(const string16& title) OVERRIDE;
+ virtual void SetWindowIcons(const SkBitmap& window_icon,
+ const SkBitmap& app_icon) OVERRIDE;
+ virtual void SetAccessibleName(const string16& name) OVERRIDE;
+ virtual void SetAccessibleRole(ui::AccessibilityTypes::Role role) OVERRIDE;
+ virtual void SetAccessibleState(ui::AccessibilityTypes::State state) OVERRIDE;
+ virtual void BecomeModal() OVERRIDE;
+ virtual gfx::Rect GetWindowScreenBounds() const OVERRIDE;
+ virtual gfx::Rect GetClientAreaScreenBounds() const OVERRIDE;
+ virtual gfx::Rect GetRestoredBounds() const OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual void SetSize(const gfx::Size& size) OVERRIDE;
+ virtual void MoveAbove(gfx::NativeView native_view) OVERRIDE;
+ virtual void MoveToTop() OVERRIDE;
+ virtual void SetShape(gfx::NativeRegion shape) OVERRIDE;
+ virtual void Close() OVERRIDE;
+ virtual void CloseNow() OVERRIDE;
+ virtual void EnableClose(bool enable) OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual void ShowMaximizedWithBounds(
+ const gfx::Rect& restored_bounds) OVERRIDE;
+ virtual void ShowWithWindowState(ui::WindowShowState show_state) OVERRIDE;
+ virtual bool IsVisible() const OVERRIDE;
+ virtual void Activate() OVERRIDE;
+ virtual void Deactivate() OVERRIDE;
+ virtual bool IsActive() const OVERRIDE;
+ virtual void SetAlwaysOnTop(bool always_on_top) OVERRIDE;
+ virtual void Maximize() OVERRIDE;
+ virtual void Minimize() OVERRIDE;
+ virtual bool IsMaximized() const OVERRIDE;
+ virtual bool IsMinimized() const OVERRIDE;
+ virtual void Restore() OVERRIDE;
+ virtual void SetFullscreen(bool fullscreen) OVERRIDE;
+ virtual bool IsFullscreen() const OVERRIDE;
+ virtual void SetOpacity(unsigned char opacity) OVERRIDE;
+ virtual void SetUseDragFrame(bool use_drag_frame) OVERRIDE;
+ virtual bool IsAccessibleWidget() const OVERRIDE;
+ virtual void RunShellDrag(View* view,
+ const ui::OSExchangeData& data,
+ int operation) OVERRIDE;
+ virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE;
+ virtual void SetCursor(gfx::NativeCursor cursor) OVERRIDE;
+ virtual void ClearNativeFocus() OVERRIDE;
+ virtual void FocusNativeView(gfx::NativeView native_view) OVERRIDE;
+ virtual bool ConvertPointFromAncestor(
+ const Widget* ancestor, gfx::Point* point) const OVERRIDE;
+ virtual gfx::Rect GetWorkAreaBoundsInScreen() const OVERRIDE;
+ virtual void SetInactiveRenderingDisabled(bool value) OVERRIDE;
+
+ protected:
+ // Information saved before going into fullscreen mode, used to restore the
+ // window afterwards.
+ struct SavedWindowInfo {
+ bool maximized;
+ LONG style;
+ LONG ex_style;
+ RECT window_rect;
+ };
+
+ // Overridden from MessageLoop::Observer:
+ virtual base::EventStatus WillProcessEvent(
+ const base::NativeEvent& event) OVERRIDE;
+ virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE;
+
+ // Overridden from WindowImpl:
+ virtual HICON GetDefaultWindowIcon() const OVERRIDE;
+ virtual LRESULT OnWndProc(UINT message,
+ WPARAM w_param,
+ LPARAM l_param) OVERRIDE;
+
+ // Message Handlers ----------------------------------------------------------
+
+ BEGIN_MSG_MAP_EX(NativeWidgetWin)
+ // Range handlers must go first!
+ MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange)
+ MESSAGE_RANGE_HANDLER_EX(WM_NCMOUSEMOVE, WM_NCXBUTTONDBLCLK, OnMouseRange)
+
+ // Reflected message handler
+ MESSAGE_HANDLER_EX(base::win::kReflectedMessage, OnReflectedMessage)
+
+ // CustomFrameWindow hacks
+ MESSAGE_HANDLER_EX(WM_NCUAHDRAWCAPTION, OnNCUAHDrawCaption)
+ MESSAGE_HANDLER_EX(WM_NCUAHDRAWFRAME, OnNCUAHDrawFrame)
+
+ // Vista and newer
+ MESSAGE_HANDLER_EX(WM_DWMCOMPOSITIONCHANGED, OnDwmCompositionChanged)
+
+ // Non-atlcrack.h handlers
+ MESSAGE_HANDLER_EX(WM_GETOBJECT, OnGetObject)
+
+ // Mouse events.
+ MESSAGE_HANDLER_EX(WM_MOUSEACTIVATE, OnMouseActivate)
+ MESSAGE_HANDLER_EX(WM_MOUSELEAVE, OnMouseRange)
+ MESSAGE_HANDLER_EX(WM_NCMOUSELEAVE, OnMouseRange)
+ MESSAGE_HANDLER_EX(WM_SETCURSOR, OnSetCursor);
+
+ // Key events.
+ MESSAGE_HANDLER_EX(WM_KEYDOWN, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_KEYUP, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_SYSKEYDOWN, OnKeyEvent)
+ MESSAGE_HANDLER_EX(WM_SYSKEYUP, OnKeyEvent)
+
+ // IME Events.
+ MESSAGE_HANDLER_EX(WM_IME_SETCONTEXT, OnImeMessages)
+ MESSAGE_HANDLER_EX(WM_IME_STARTCOMPOSITION, OnImeMessages)
+ MESSAGE_HANDLER_EX(WM_IME_COMPOSITION, OnImeMessages)
+ MESSAGE_HANDLER_EX(WM_IME_ENDCOMPOSITION, OnImeMessages)
+ MESSAGE_HANDLER_EX(WM_IME_REQUEST, OnImeMessages)
+ MESSAGE_HANDLER_EX(WM_CHAR, OnImeMessages)
+ MESSAGE_HANDLER_EX(WM_SYSCHAR, OnImeMessages)
+ MESSAGE_HANDLER_EX(WM_DEADCHAR, OnImeMessages)
+ MESSAGE_HANDLER_EX(WM_SYSDEADCHAR, OnImeMessages)
+
+ // This list is in _ALPHABETICAL_ order! OR I WILL HURT YOU.
+ MSG_WM_ACTIVATE(OnActivate)
+ MSG_WM_ACTIVATEAPP(OnActivateApp)
+ MSG_WM_APPCOMMAND(OnAppCommand)
+ MSG_WM_CANCELMODE(OnCancelMode)
+ MSG_WM_CAPTURECHANGED(OnCaptureChanged)
+ MSG_WM_CLOSE(OnClose)
+ MSG_WM_COMMAND(OnCommand)
+ MSG_WM_CREATE(OnCreate)
+ MSG_WM_DESTROY(OnDestroy)
+ MSG_WM_DISPLAYCHANGE(OnDisplayChange)
+ MSG_WM_ERASEBKGND(OnEraseBkgnd)
+ MSG_WM_ENDSESSION(OnEndSession)
+ MSG_WM_ENTERSIZEMOVE(OnEnterSizeMove)
+ MSG_WM_EXITMENULOOP(OnExitMenuLoop)
+ MSG_WM_EXITSIZEMOVE(OnExitSizeMove)
+ MSG_WM_GETMINMAXINFO(OnGetMinMaxInfo)
+ MSG_WM_HSCROLL(OnHScroll)
+ MSG_WM_INITMENU(OnInitMenu)
+ MSG_WM_INITMENUPOPUP(OnInitMenuPopup)
+ MSG_WM_INPUTLANGCHANGE(OnInputLangChange)
+ MSG_WM_KILLFOCUS(OnKillFocus)
+ MSG_WM_MOVE(OnMove)
+ MSG_WM_MOVING(OnMoving)
+ MSG_WM_NCACTIVATE(OnNCActivate)
+ MSG_WM_NCCALCSIZE(OnNCCalcSize)
+ MSG_WM_NCHITTEST(OnNCHitTest)
+ MSG_WM_NCPAINT(OnNCPaint)
+ MSG_WM_NOTIFY(OnNotify)
+ MSG_WM_PAINT(OnPaint)
+ MSG_WM_POWERBROADCAST(OnPowerBroadcast)
+ MSG_WM_SETFOCUS(OnSetFocus)
+ MSG_WM_SETICON(OnSetIcon)
+ MSG_WM_SETTEXT(OnSetText)
+ MSG_WM_SETTINGCHANGE(OnSettingChange)
+ MSG_WM_SIZE(OnSize)
+ MSG_WM_SYSCOMMAND(OnSysCommand)
+ MSG_WM_THEMECHANGED(OnThemeChanged)
+ MSG_WM_VSCROLL(OnVScroll)
+ MSG_WM_WINDOWPOSCHANGING(OnWindowPosChanging)
+ MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged)
+ END_MSG_MAP()
+
+ // These are all virtual so that specialized Widgets can modify or augment
+ // processing.
+ // This list is in _ALPHABETICAL_ order!
+ // Note: in the base class these functions must do nothing but convert point
+ // coordinates to client coordinates (if necessary) and forward the
+ // handling to the appropriate Process* function. This is so that
+ // subclasses can easily override these methods to do different things
+ // and have a convenient function to call to get the default behavior.
+ virtual void OnActivate(UINT action, BOOL minimized, HWND window);
+ virtual void OnActivateApp(BOOL active, DWORD thread_id);
+ virtual LRESULT OnAppCommand(HWND window, short app_command, WORD device,
+ int keystate);
+ virtual void OnCancelMode();
+ virtual void OnCaptureChanged(HWND hwnd);
+ virtual void OnClose();
+ virtual void OnCommand(UINT notification_code, int command_id, HWND window);
+ virtual LRESULT OnCreate(CREATESTRUCT* create_struct);
+ // WARNING: If you override this be sure and invoke super, otherwise we'll
+ // leak a few things.
+ virtual void OnDestroy();
+ virtual void OnDisplayChange(UINT bits_per_pixel, CSize screen_size);
+ virtual LRESULT OnDwmCompositionChanged(UINT msg,
+ WPARAM w_param,
+ LPARAM l_param);
+ virtual void OnEndSession(BOOL ending, UINT logoff);
+ virtual void OnEnterSizeMove();
+ virtual LRESULT OnEraseBkgnd(HDC dc);
+ virtual void OnExitMenuLoop(BOOL is_track_popup_menu);
+ virtual void OnExitSizeMove();
+ virtual LRESULT OnGetObject(UINT uMsg, WPARAM w_param, LPARAM l_param);
+ virtual void OnGetMinMaxInfo(MINMAXINFO* minmax_info);
+ virtual void OnHScroll(int scroll_type, short position, HWND scrollbar);
+ virtual LRESULT OnImeMessages(UINT message, WPARAM w_param, LPARAM l_param);
+ virtual void OnInitMenu(HMENU menu);
+ virtual void OnInitMenuPopup(HMENU menu, UINT position, BOOL is_system_menu);
+ virtual void OnInputLangChange(DWORD character_set, HKL input_language_id);
+ virtual LRESULT OnKeyEvent(UINT message, WPARAM w_param, LPARAM l_param);
+ virtual void OnKillFocus(HWND focused_window);
+ virtual LRESULT OnMouseActivate(UINT message, WPARAM w_param, LPARAM l_param);
+ virtual LRESULT OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param);
+ virtual void OnMove(const CPoint& point);
+ virtual void OnMoving(UINT param, LPRECT new_bounds);
+ virtual LRESULT OnNCActivate(BOOL active);
+ virtual LRESULT OnNCCalcSize(BOOL w_param, LPARAM l_param);
+ virtual LRESULT OnNCHitTest(const CPoint& pt);
+ virtual void OnNCPaint(HRGN rgn);
+ virtual LRESULT OnNCUAHDrawCaption(UINT msg,
+ WPARAM w_param,
+ LPARAM l_param);
+ virtual LRESULT OnNCUAHDrawFrame(UINT msg, WPARAM w_param, LPARAM l_param);
+ virtual LRESULT OnNotify(int w_param, NMHDR* l_param);
+ virtual void OnPaint(HDC dc);
+ virtual LRESULT OnPowerBroadcast(DWORD power_event, DWORD data);
+ virtual LRESULT OnReflectedMessage(UINT msg, WPARAM w_param, LPARAM l_param);
+ virtual LRESULT OnSetCursor(UINT message, WPARAM w_param, LPARAM l_param);
+ virtual void OnSetFocus(HWND focused_window);
+ virtual LRESULT OnSetIcon(UINT size_type, HICON new_icon);
+ virtual LRESULT OnSetText(const wchar_t* text);
+ virtual void OnSettingChange(UINT flags, const wchar_t* section);
+ virtual void OnSize(UINT param, const CSize& size);
+ virtual void OnSysCommand(UINT notification_code, CPoint click);
+ virtual void OnThemeChanged();
+ virtual void OnVScroll(int scroll_type, short position, HWND scrollbar);
+ virtual void OnWindowPosChanging(WINDOWPOS* window_pos);
+ virtual void OnWindowPosChanged(WINDOWPOS* window_pos);
+
+ // Deletes this window as it is destroyed, override to provide different
+ // behavior.
+ virtual void OnFinalMessage(HWND window);
+
+ // Retrieve the show state of the window. This is one of the SW_SHOW* flags
+ // passed into Windows' ShowWindow method. For normal windows this defaults
+ // to SW_SHOWNORMAL, however windows (e.g. the main window) can override this
+ // method to provide different values (e.g. retrieve the user's specified
+ // show state from the shortcut starutp info).
+ virtual int GetShowState() const;
+
+ // Returns the insets of the client area relative to the non-client area of
+ // the window. Override this function instead of OnNCCalcSize, which is
+ // crazily complicated.
+ virtual gfx::Insets GetClientAreaInsets() const;
+
+ // Start tracking all mouse events so that this window gets sent mouse leave
+ // messages too.
+ void TrackMouseEvents(DWORD mouse_tracking_flags);
+
+ // Called when a MSAA screen reader client is detected.
+ virtual void OnScreenReaderDetected();
+
+ // Executes the specified SC_command.
+ void ExecuteSystemMenuCommand(int command);
+
+ // The TooltipManager. This is NULL if there is a problem creating the
+ // underlying tooltip window.
+ // WARNING: RootView's destructor calls into the TooltipManager. As such, this
+ // must be destroyed AFTER root_view_.
+ scoped_ptr<TooltipManagerWin> tooltip_manager_;
+
+ scoped_refptr<DropTargetWin> drop_target_;
+
+ const gfx::Rect& invalid_rect() const { return invalid_rect_; }
+
+ // Saved window information from before entering fullscreen mode.
+ // TODO(beng): move to private once GetRestoredBounds() moves onto Widget.
+ SavedWindowInfo saved_window_info_;
+
+ private:
+ typedef ScopedVector<ui::ViewProp> ViewProps;
+
+ // Called after the WM_ACTIVATE message has been processed by the default
+ // windows procedure.
+ static void PostProcessActivateMessage(NativeWidgetWin* widget,
+ int activation_state);
+
+ void SetInitParams(const Widget::InitParams& params);
+
+ // Synchronously paints the invalid contents of the Widget.
+ void RedrawInvalidRect();
+
+ // Synchronously updates the invalid contents of the Widget. Valid for
+ // layered windows only.
+ void RedrawLayeredWindowContents();
+
+ // Lock or unlock the window from being able to redraw itself in response to
+ // updates to its invalid region.
+ class ScopedRedrawLock;
+ void LockUpdates();
+ void UnlockUpdates();
+
+ // Determines whether the delegate expects the client size or the window size.
+ bool WidgetSizeIsClientSize() const;
+
+ // Responds to the client area changing size, either at window creation time
+ // or subsequently.
+ void ClientAreaSizeChanged();
+
+ // Resets the window region for the current widget bounds if necessary.
+ // If |force| is true, the window region is reset to NULL even for native
+ // frame windows.
+ void ResetWindowRegion(bool force);
+
+ // Calls DefWindowProc, safely wrapping the call in a ScopedRedrawLock to
+ // prevent frame flicker. DefWindowProc handling can otherwise render the
+ // classic-look window title bar directly.
+ LRESULT DefWindowProcWithRedrawLock(UINT message,
+ WPARAM w_param,
+ LPARAM l_param);
+
+ // Stops ignoring SetWindowPos() requests (see below).
+ void StopIgnoringPosChanges() { ignore_window_pos_changes_ = false; }
+
+ void RestoreEnabledIfNecessary();
+
+ void SetInitialFocus();
+
+ // Overridden from internal::InputMethodDelegate
+ virtual void DispatchKeyEventPostIME(const KeyEvent& key) OVERRIDE;
+
+ // A delegate implementation that handles events received here.
+ // See class documentation for Widget in widget.h for a note about ownership.
+ internal::NativeWidgetDelegate* delegate_;
+
+ // The following factory is used for calls to close the NativeWidgetWin
+ // instance.
+ base::WeakPtrFactory<NativeWidgetWin> close_widget_factory_;
+
+ // The flags currently being used with TrackMouseEvent to track mouse
+ // messages. 0 if there is no active tracking. The value of this member is
+ // used when tracking is canceled.
+ DWORD active_mouse_tracking_flags_;
+
+ // Should we keep an off-screen buffer? This is false by default, set to true
+ // when WS_EX_LAYERED is specified before the native window is created.
+ //
+ // NOTE: this is intended to be used with a layered window (a window with an
+ // extended window style of WS_EX_LAYERED). If you are using a layered window
+ // and NOT changing the layered alpha or anything else, then leave this value
+ // alone. OTOH if you are invoking SetLayeredWindowAttributes then you'll
+ // most likely want to set this to false, or after changing the alpha toggle
+ // the extended style bit to false than back to true. See MSDN for more
+ // details.
+ bool use_layered_buffer_;
+
+ // The default alpha to be applied to the layered window.
+ BYTE layered_alpha_;
+
+ // A canvas that contains the window contents in the case of a layered
+ // window.
+ scoped_ptr<gfx::CanvasSkia> layered_window_contents_;
+
+ // We must track the invalid rect ourselves, for two reasons:
+ // For layered windows, Windows will not do this properly with
+ // InvalidateRect()/GetUpdateRect(). (In fact, it'll return misleading
+ // information from GetUpdateRect()).
+ // We also need to keep track of the invalid rectangle for the RootView should
+ // we need to paint the non-client area. The data supplied to WM_NCPAINT seems
+ // to be insufficient.
+ gfx::Rect invalid_rect_;
+
+ // A factory that allows us to schedule a redraw for layered windows.
+ base::WeakPtrFactory<NativeWidgetWin> paint_layered_window_factory_;
+
+ // See class documentation for Widget in widget.h for a note about ownership.
+ Widget::InitParams::Ownership ownership_;
+
+ // True if we are allowed to update the layered window from the DIB backing
+ // store if necessary.
+ bool can_update_layered_window_;
+
+ // Whether the focus should be restored next time we get enabled. Needed to
+ // restore focus correctly when Windows modal dialogs are displayed.
+ bool restore_focus_when_enabled_;
+
+ // Instance of accessibility information and handling for MSAA root
+ base::win::ScopedComPtr<IAccessible> accessibility_root_;
+
+ // Value determines whether the Widget is customized for accessibility.
+ static bool screen_reader_active_;
+
+ // The maximum number of view events in our vector below.
+ static const int kMaxAccessibilityViewEvents = 20;
+
+ // A vector used to access views for which we have sent notifications to
+ // accessibility clients. It is used as a circular queue.
+ std::vector<View*> accessibility_view_events_;
+
+ // The current position of the view events vector. When incrementing,
+ // we always mod this value with the max view events above .
+ int accessibility_view_events_index_;
+
+ // The last cursor that was active before the current one was selected. Saved
+ // so that we can restore it.
+ gfx::NativeCursor previous_cursor_;
+
+ ViewProps props_;
+
+ // True if we're in fullscreen mode.
+ bool fullscreen_;
+
+ // If this is greater than zero, we should prevent attempts to make the window
+ // visible when we handle WM_WINDOWPOSCHANGING. Some calls like
+ // ShowWindow(SW_RESTORE) make the window visible in addition to restoring it,
+ // when all we want to do is restore it.
+ int force_hidden_count_;
+
+ // The window styles before we modified them for the drag frame appearance.
+ DWORD drag_frame_saved_window_style_;
+ DWORD drag_frame_saved_window_ex_style_;
+
+ // Represents the number of ScopedRedrawLocks active against this widget.
+ // If this is greater than zero, the widget should be locked against updates.
+ int lock_updates_count_;
+
+ // The window styles of the window before updates were locked.
+ DWORD saved_window_style_;
+
+ // When true, this flag makes us discard incoming SetWindowPos() requests that
+ // only change our position/size. (We still allow changes to Z-order,
+ // activation, etc.)
+ bool ignore_window_pos_changes_;
+
+ // The following factory is used to ignore SetWindowPos() calls for short time
+ // periods.
+ base::WeakPtrFactory<NativeWidgetWin> ignore_pos_changes_factory_;
+
+ // The last-seen monitor containing us, and its rect and work area. These are
+ // used to catch updates to the rect and work area and react accordingly.
+ HMONITOR last_monitor_;
+ gfx::Rect last_monitor_rect_, last_work_area_;
+
+ // Set to true when the user presses the right mouse button on the caption
+ // area. We need this so we can correctly show the context menu on mouse-up.
+ bool is_right_mouse_pressed_on_caption_;
+
+ // Whether all ancestors have been enabled. This is only used if is_modal_ is
+ // true.
+ bool restored_enabled_;
+
+ // The compositor for accelerated drawing.
+ scoped_refptr<ui::Compositor> compositor_;
+
+ // This flag can be initialized and checked after certain operations (such as
+ // DefWindowProc) to avoid stack-controlled NativeWidgetWin operations (such
+ // as unlocking the Window with a ScopedRedrawLock) after Widget destruction.
+ bool* destroyed_;
+
+ // True if the widget is going to have a non_client_view. We cache this value
+ // rather than asking the Widget for the non_client_view so that we know at
+ // Init time, before the Widget has created the NonClientView.
+ bool has_non_client_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeWidgetWin);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_NATIVE_WIDGET_WIN_H_
diff --git a/views/widget/native_widget_win_unittest.cc b/views/widget/native_widget_win_unittest.cc
new file mode 100644
index 0000000..f2465f4
--- /dev/null
+++ b/views/widget/native_widget_win_unittest.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2011 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/native_widget_win.h"
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace views {
+namespace {
+
+class NativeWidgetWinTest : public testing::Test {
+ public:
+ NativeWidgetWinTest() {
+ OleInitialize(NULL);
+ }
+
+ ~NativeWidgetWinTest() {
+ OleUninitialize();
+ }
+
+ virtual void TearDown() {
+ // Flush the message loop because we have pending release tasks
+ // and these tasks if un-executed would upset Valgrind.
+ RunPendingMessages();
+ }
+
+ // Create a simple widget win. The caller is responsible for taking ownership
+ // of the returned value.
+ NativeWidgetWin* CreateNativeWidgetWin();
+
+ void RunPendingMessages() {
+ message_loop_.RunAllPending();
+ }
+
+ private:
+ MessageLoopForUI message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeWidgetWinTest);
+};
+
+NativeWidgetWin* NativeWidgetWinTest::CreateNativeWidgetWin() {
+ scoped_ptr<Widget> widget(new Widget);
+ Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.bounds = gfx::Rect(50, 50, 650, 650);
+ widget->Init(params);
+ return static_cast<NativeWidgetWin*>(widget.release()->native_widget());
+}
+
+TEST_F(NativeWidgetWinTest, ZoomWindow) {
+ scoped_ptr<NativeWidgetWin> window(CreateNativeWidgetWin());
+ window->ShowWindow(SW_HIDE);
+ EXPECT_FALSE(window->IsActive());
+ window->ShowWindow(SW_MAXIMIZE);
+ EXPECT_TRUE(window->IsZoomed());
+ window->CloseNow();
+}
+
+TEST_F(NativeWidgetWinTest, SetBoundsForZoomedWindow) {
+ scoped_ptr<NativeWidgetWin> window(CreateNativeWidgetWin());
+ window->ShowWindow(SW_MAXIMIZE);
+ EXPECT_TRUE(window->IsZoomed());
+
+ // Create another window, so that it will be active.
+ scoped_ptr<NativeWidgetWin> window2(CreateNativeWidgetWin());
+ window2->ShowWindow(SW_MAXIMIZE);
+ EXPECT_TRUE(window2->IsActive());
+ EXPECT_FALSE(window->IsActive());
+
+ // Verify that setting the bounds of a zoomed window will unzoom it and not
+ // cause it to be activated.
+ window->SetBounds(gfx::Rect(50, 50, 650, 650));
+ EXPECT_FALSE(window->IsZoomed());
+ EXPECT_FALSE(window->IsActive());
+
+ // Cleanup.
+ window->CloseNow();
+ window2->CloseNow();
+}
+
+} // namespace
+} // namespace views
diff --git a/views/widget/root_view.cc b/views/widget/root_view.cc
new file mode 100644
index 0000000..2682bfc
--- /dev/null
+++ b/views/widget/root_view.cc
@@ -0,0 +1,455 @@
+// Copyright (c) 2011 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 "base/logging.h"
+#include "base/message_loop.h"
+#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/compositor/layer.h"
+#include "ui/views/focus/view_storage.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/touchui/gesture_manager.h"
+#include "views/widget/widget.h"
+
+namespace views {
+namespace internal {
+
+// static
+const char RootView::kViewClassName[] = "views/RootView";
+
+////////////////////////////////////////////////////////////////////////////////
+// RootView, public:
+
+// Creation and lifetime -------------------------------------------------------
+
+RootView::RootView(Widget* widget)
+ : widget_(widget),
+ mouse_pressed_handler_(NULL),
+ mouse_move_handler_(NULL),
+ last_click_handler_(NULL),
+ explicit_mouse_handler_(false),
+ last_mouse_event_flags_(0),
+ last_mouse_event_x_(-1),
+ last_mouse_event_y_(-1),
+ gesture_manager_(GestureManager::GetInstance()),
+ touch_pressed_handler_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(focus_search_(this, false, false)),
+ focus_traversable_parent_(NULL),
+ focus_traversable_parent_view_(NULL) {
+}
+
+RootView::~RootView() {
+ // If we have children remove them explicitly so to make sure a remove
+ // notification is sent for each one of them.
+ if (has_children())
+ RemoveAllChildViews(true);
+}
+
+// Tree operations -------------------------------------------------------------
+
+void RootView::SetContentsView(View* contents_view) {
+ DCHECK(contents_view && GetWidget()->native_widget()) <<
+ "Can't be called until after the native widget is created!";
+ // The ContentsView must be set up _after_ the window is created so that its
+ // Widget pointer is valid.
+ SetLayoutManager(new FillLayout);
+ if (has_children())
+ RemoveAllChildViews(true);
+ AddChildView(contents_view);
+
+ // Force a layout now, since the attached hierarchy won't be ready for the
+ // containing window's bounds. Note that we call Layout directly rather than
+ // calling the widget's size changed handler, since the RootView's bounds may
+ // not have changed, which will cause the Layout not to be done otherwise.
+ Layout();
+}
+
+View* RootView::GetContentsView() {
+ return child_count() > 0 ? child_at(0) : NULL;
+}
+
+void RootView::NotifyNativeViewHierarchyChanged(bool attached,
+ gfx::NativeView native_view) {
+ PropagateNativeViewHierarchyChanged(attached, native_view, this);
+}
+
+// Input -----------------------------------------------------------------------
+
+bool RootView::OnKeyEvent(const KeyEvent& event) {
+ bool consumed = false;
+
+ View* v = GetFocusManager()->GetFocusedView();
+ // Special case to handle right-click context menus triggered by the
+ // keyboard.
+ if (v && v->IsEnabled() && ((event.key_code() == ui::VKEY_APPS) ||
+ (event.key_code() == ui::VKEY_F10 && event.IsShiftDown()))) {
+ v->ShowContextMenu(v->GetKeyboardContextMenuLocation(), false);
+ return true;
+ }
+ for (; v && v != this && !consumed; v = v->parent()) {
+ consumed = (event.type() == ui::ET_KEY_PRESSED) ?
+ v->OnKeyPressed(event) : v->OnKeyReleased(event);
+ }
+ return consumed;
+}
+
+// Focus -----------------------------------------------------------------------
+
+void RootView::SetFocusTraversableParent(FocusTraversable* focus_traversable) {
+ DCHECK(focus_traversable != this);
+ focus_traversable_parent_ = focus_traversable;
+}
+
+void RootView::SetFocusTraversableParentView(View* view) {
+ focus_traversable_parent_view_ = view;
+}
+
+// System events ---------------------------------------------------------------
+
+void RootView::ThemeChanged() {
+ View::PropagateThemeChanged();
+}
+
+void RootView::LocaleChanged() {
+ View::PropagateLocaleChanged();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootView, FocusTraversable implementation:
+
+FocusSearch* RootView::GetFocusSearch() {
+ return &focus_search_;
+}
+
+FocusTraversable* RootView::GetFocusTraversableParent() {
+ return focus_traversable_parent_;
+}
+
+View* RootView::GetFocusTraversableParentView() {
+ return focus_traversable_parent_view_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootView, View overrides:
+
+const Widget* RootView::GetWidget() const {
+ return widget_;
+}
+
+Widget* RootView::GetWidget() {
+ return const_cast<Widget*>(const_cast<const RootView*>(this)->GetWidget());
+}
+
+bool RootView::IsVisibleInRootView() const {
+ return IsVisible();
+}
+
+std::string RootView::GetClassName() const {
+ return kViewClassName;
+}
+
+void RootView::SchedulePaintInRect(const gfx::Rect& rect) {
+ if (layer()) {
+ layer()->SchedulePaint(rect);
+ } else {
+ gfx::Rect xrect = ConvertRectToParent(rect);
+ gfx::Rect invalid_rect = GetLocalBounds().Intersect(xrect);
+ if (!invalid_rect.IsEmpty())
+ widget_->SchedulePaintInRect(invalid_rect);
+ }
+}
+
+bool RootView::OnMousePressed(const MouseEvent& event) {
+ MouseEvent e(event, this);
+ 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_ = GetEventHandlerForPoint(e.location());
+ mouse_pressed_handler_ && (mouse_pressed_handler_ != this);
+ mouse_pressed_handler_ = mouse_pressed_handler_->parent()) {
+ 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.flags() & ~ui::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;
+
+ // 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.flags() & ui::EF_IS_DOUBLE_CLICK)
+ hit_disabled_view = true;
+
+ last_click_handler_ = NULL;
+ return hit_disabled_view;
+}
+
+bool RootView::OnMouseDragged(const MouseEvent& event) {
+ MouseEvent e(event, this);
+ UpdateCursor(e);
+
+ if (mouse_pressed_handler_) {
+ SetMouseLocationAndFlags(e);
+
+ MouseEvent mouse_event(e, this, mouse_pressed_handler_);
+ return mouse_pressed_handler_->ProcessMouseDragged(mouse_event, &drag_info);
+ }
+ return false;
+}
+
+void RootView::OnMouseReleased(const MouseEvent& event) {
+ MouseEvent e(event, this);
+ UpdateCursor(e);
+
+ if (mouse_pressed_handler_) {
+ MouseEvent mouse_released(e, this, mouse_pressed_handler_);
+ // 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_;
+ SetMouseHandler(NULL);
+ mouse_pressed_handler->ProcessMouseReleased(mouse_released);
+ // WARNING: we may have been deleted.
+ }
+}
+
+void RootView::OnMouseCaptureLost() {
+ if (mouse_pressed_handler_) {
+ // Synthesize a release event for UpdateCursor.
+ MouseEvent release_event(ui::ET_MOUSE_RELEASED, last_mouse_event_x_,
+ last_mouse_event_y_, last_mouse_event_flags_);
+ UpdateCursor(release_event);
+ // We allow the view to delete us from OnMouseCaptureLost. As such,
+ // configure state such that we're done first, then call View.
+ View* mouse_pressed_handler = mouse_pressed_handler_;
+ SetMouseHandler(NULL);
+ mouse_pressed_handler->OnMouseCaptureLost();
+ // WARNING: we may have been deleted.
+ }
+}
+
+void RootView::OnMouseMoved(const MouseEvent& event) {
+ MouseEvent e(event, this);
+ View* v = GetEventHandlerForPoint(e.location());
+ // Find the first enabled view, or the existing move handler, whichever comes
+ // first. The check for the existing handler is because if a view becomes
+ // disabled while handling moves, it's wrong to suddenly send ET_MOUSE_EXITED
+ // and ET_MOUSE_ENTERED events, because the mouse hasn't actually exited yet.
+ while (v && !v->IsEnabled() && (v != mouse_move_handler_))
+ v = v->parent();
+ if (v && v != this) {
+ if (v != mouse_move_handler_) {
+ if (mouse_move_handler_ != NULL)
+ mouse_move_handler_->OnMouseExited(e);
+ mouse_move_handler_ = v;
+ MouseEvent entered_event(e, this, mouse_move_handler_);
+ mouse_move_handler_->OnMouseEntered(entered_event);
+ }
+ MouseEvent moved_event(e, this, mouse_move_handler_);
+ mouse_move_handler_->OnMouseMoved(moved_event);
+ if (!(moved_event.flags() & ui::EF_IS_NON_CLIENT))
+ widget_->SetCursor(mouse_move_handler_->GetCursor(moved_event));
+ } else if (mouse_move_handler_ != NULL) {
+ mouse_move_handler_->OnMouseExited(e);
+ widget_->SetCursor(gfx::kNullCursor);
+ }
+}
+
+void RootView::OnMouseExited(const MouseEvent& event) {
+ if (mouse_move_handler_ != NULL) {
+ mouse_move_handler_->OnMouseExited(event);
+ mouse_move_handler_ = NULL;
+ }
+}
+
+bool RootView::OnMouseWheel(const MouseWheelEvent& event) {
+ MouseWheelEvent e(event, this);
+ bool consumed = false;
+ for (View* v = GetFocusManager()->GetFocusedView();
+ v && v != this && !consumed; v = v->parent())
+ consumed = v->OnMouseWheel(e);
+ return consumed;
+}
+
+ui::TouchStatus RootView::OnTouchEvent(const TouchEvent& event) {
+ TouchEvent e(event, this);
+
+ // If touch_pressed_handler_ is non null, we are currently processing
+ // a touch down on the screen situation. In that case we send the
+ // event to touch_pressed_handler_
+ ui::TouchStatus status = ui::TOUCH_STATUS_UNKNOWN;
+
+ if (touch_pressed_handler_) {
+ TouchEvent touch_event(e, this, touch_pressed_handler_);
+ status = touch_pressed_handler_->ProcessTouchEvent(touch_event);
+ if (gesture_manager_->ProcessTouchEventForGesture(e, this, status))
+ status = ui::TOUCH_STATUS_SYNTH_MOUSE;
+ if (status == ui::TOUCH_STATUS_END)
+ touch_pressed_handler_ = NULL;
+ return status;
+ }
+
+ // Walk up the tree until we find a view that wants the touch event.
+ for (touch_pressed_handler_ = GetEventHandlerForPoint(e.location());
+ touch_pressed_handler_ && (touch_pressed_handler_ != this);
+ touch_pressed_handler_ = touch_pressed_handler_->parent()) {
+ if (!touch_pressed_handler_->IsEnabled()) {
+ // Disabled views eat events but are treated as not handled by the
+ // the GestureManager.
+ status = ui::TOUCH_STATUS_UNKNOWN;
+ break;
+ }
+
+ // See if this view wants to handle the touch
+ TouchEvent touch_event(e, this, touch_pressed_handler_);
+ status = touch_pressed_handler_->ProcessTouchEvent(touch_event);
+
+ // The view could have removed itself from the tree when handling
+ // OnTouchEvent(). So handle as per OnMousePressed. NB: we
+ // assume that the RootView itself cannot be so removed.
+ if (!touch_pressed_handler_)
+ break;
+
+ // The touch event wasn't processed. Go up the view hierarchy and dispatch
+ // the touch event.
+ if (status == ui::TOUCH_STATUS_UNKNOWN)
+ continue;
+
+ // If the touch didn't initiate a touch-sequence, then reset the touch event
+ // handler. Otherwise, leave it set so that subsequent touch events are
+ // dispatched to the same handler.
+ if (status != ui::TOUCH_STATUS_START)
+ touch_pressed_handler_ = NULL;
+
+ if (gesture_manager_->ProcessTouchEventForGesture(e, this, status))
+ status = ui::TOUCH_STATUS_SYNTH_MOUSE;
+ return status;
+ }
+
+ // Reset touch_pressed_handler_ to indicate that no processing is occurring.
+ touch_pressed_handler_ = NULL;
+
+ // Give the touch event to the gesture manager.
+ if (gesture_manager_->ProcessTouchEventForGesture(e, this, status))
+ status = ui::TOUCH_STATUS_SYNTH_MOUSE;
+ return status;
+}
+
+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::GetAccessibleState(ui::AccessibleViewState* state) {
+ state->role = ui::AccessibilityTypes::ROLE_APPLICATION;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootView, protected:
+
+void RootView::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
+ widget_->ViewHierarchyChanged(is_add, parent, child);
+
+ if (!is_add) {
+ if (!explicit_mouse_handler_ && mouse_pressed_handler_ == child)
+ mouse_pressed_handler_ = NULL;
+ if (mouse_move_handler_ == child)
+ mouse_move_handler_ = NULL;
+ if (touch_pressed_handler_ == child)
+ touch_pressed_handler_ = NULL;
+ }
+}
+
+void RootView::OnPaint(gfx::Canvas* canvas) {
+ if (!layer() || !layer()->fills_bounds_opaquely())
+ canvas->GetSkCanvas()->drawColor(SK_ColorBLACK, SkXfermode::kClear_Mode);
+
+ // TODO (pkotwicz): Remove this once we switch over to Aura desktop.
+ // This is needed so that we can set the background behind the RWHV when the
+ // RWHV is not visible. Not needed once there is a view between the RootView
+ // and RWHV.
+ View::OnPaint(canvas);
+}
+
+void RootView::CalculateOffsetToAncestorWithLayer(gfx::Point* offset,
+ ui::Layer** layer_parent) {
+ View::CalculateOffsetToAncestorWithLayer(offset, layer_parent);
+ if (!layer())
+ widget_->CalculateOffsetToAncestorWithLayer(offset, layer_parent);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// RootView, private:
+
+// Input -----------------------------------------------------------------------
+
+void RootView::UpdateCursor(const MouseEvent& event) {
+ if (!(event.flags() & ui::EF_IS_NON_CLIENT)) {
+ View* v = GetEventHandlerForPoint(event.location());
+ widget_->SetCursor(v->GetCursor(MouseEvent(event, this, v)));
+ }
+}
+
+void RootView::SetMouseLocationAndFlags(const MouseEvent& event) {
+ last_mouse_event_flags_ = event.flags();
+ last_mouse_event_x_ = event.x();
+ last_mouse_event_y_ = event.y();
+}
+
+} // namespace internal
+} // namespace views
diff --git a/views/widget/root_view.h b/views/widget/root_view.h
new file mode 100644
index 0000000..1a17d38
--- /dev/null
+++ b/views/widget/root_view.h
@@ -0,0 +1,207 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_ROOT_VIEW_H_
+#define VIEWS_WIDGET_ROOT_VIEW_H_
+#pragma once
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/focus/focus_search.h"
+#include "views/view.h"
+
+namespace ui {
+enum TouchStatus;
+}
+
+namespace views {
+
+class Widget;
+class GestureManager;
+
+// This is a views-internal API and should not be used externally.
+// Widget exposes this object as a View*.
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// RootView class
+//
+// The RootView is the root of a View hierarchy. A RootView is attached to a
+// Widget. The Widget is responsible for receiving events from the host
+// environment, converting them to views-compatible events and then forwarding
+// them to the RootView for propagation into the View hierarchy.
+//
+// A RootView can have only one child, called its "Contents View" which is
+// sized to fill the bounds of the RootView (and hence the client area of the
+// Widget). Call SetContentsView() after the associated Widget has been
+// initialized to attach the contents view to the RootView.
+// TODO(beng): Enforce no other callers to AddChildView/tree functions by
+// overriding those methods as private here.
+// TODO(beng): Clean up API further, make Widget a friend.
+// TODO(sky): We don't really want to export this class.
+//
+class VIEWS_EXPORT RootView : public View, public FocusTraversable {
+ public:
+ static const char kViewClassName[];
+
+ // Creation and lifetime -----------------------------------------------------
+ explicit RootView(Widget* widget);
+ virtual ~RootView();
+
+ // Tree operations -----------------------------------------------------------
+
+ // Sets the "contents view" of the RootView. This is the single child view
+ // that is responsible for laying out the contents of the widget.
+ void SetContentsView(View* contents_view);
+ View* GetContentsView();
+
+ // Called when parent of the host changed.
+ void NotifyNativeViewHierarchyChanged(bool attached,
+ gfx::NativeView native_view);
+
+ // Input ---------------------------------------------------------------------
+
+ // Process a key event. Send the event to the focused view and up the focus
+ // path, and finally to the default keyboard handler, until someone consumes
+ // it. Returns whether anyone consumed the event.
+ bool OnKeyEvent(const KeyEvent& event);
+
+ // Provided only for testing:
+ void SetGestureManagerForTesting(GestureManager* g) { gesture_manager_ = g; }
+
+ // Focus ---------------------------------------------------------------------
+
+ // Used to set the FocusTraversable parent after the view has been created
+ // (typically when the hierarchy changes and this RootView is added/removed).
+ virtual void SetFocusTraversableParent(FocusTraversable* focus_traversable);
+
+ // Used to set the View parent after the view has been created.
+ virtual void SetFocusTraversableParentView(View* view);
+
+ // System events -------------------------------------------------------------
+
+ // Public API for broadcasting theme change notifications to this View
+ // hierarchy.
+ void ThemeChanged();
+
+ // Public API for broadcasting locale change notifications to this View
+ // hierarchy.
+ void LocaleChanged();
+
+ // Overridden from FocusTraversable:
+ virtual FocusSearch* GetFocusSearch() OVERRIDE;
+ virtual FocusTraversable* GetFocusTraversableParent() OVERRIDE;
+ virtual View* GetFocusTraversableParentView() OVERRIDE;
+
+ // Overridden from View:
+ virtual const Widget* GetWidget() const OVERRIDE;
+ virtual Widget* GetWidget() OVERRIDE;
+ virtual bool IsVisibleInRootView() const OVERRIDE;
+ virtual std::string GetClassName() const OVERRIDE;
+ virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE;
+ virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE;
+ virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseCaptureLost() OVERRIDE;
+ virtual void OnMouseMoved(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseExited(const MouseEvent& event) OVERRIDE;
+ virtual bool OnMouseWheel(const MouseWheelEvent& event) OVERRIDE;
+ virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE;
+ virtual void SetMouseHandler(View* new_mouse_handler) OVERRIDE;
+ virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+
+ protected:
+ // Overridden from View:
+ virtual void ViewHierarchyChanged(bool is_add, View* parent,
+ View* child) OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual void CalculateOffsetToAncestorWithLayer(
+ gfx::Point* offset,
+ ui::Layer** layer_parent) OVERRIDE;
+
+ private:
+ friend class View;
+ friend class Widget;
+
+ // Required so the GestureManager can call the Process* entry points
+ // with synthetic events as necessary.
+ friend class GestureManager;
+
+ // Input ---------------------------------------------------------------------
+
+ // Update the cursor given a mouse event. This is called by non mouse_move
+ // event handlers to honor the cursor desired by views located under the
+ // cursor during drag operations. The location of the mouse should be in the
+ // current coordinate system (i.e. any necessary transformation should be
+ // applied to the point prior to calling this).
+ void UpdateCursor(const MouseEvent& event);
+
+ // Updates the last_mouse_* fields from e. The location of the mouse should be
+ // in the current coordinate system (i.e. any necessary transformation should
+ // be applied to the point prior to calling this).
+ void SetMouseLocationAndFlags(const MouseEvent& event);
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Tree operations -----------------------------------------------------------
+
+ // The host Widget
+ Widget* widget_;
+
+ // Input ---------------------------------------------------------------------
+
+ // The view currently handing down - drag - up
+ View* mouse_pressed_handler_;
+
+ // The view currently handling enter / exit
+ View* mouse_move_handler_;
+
+ // The last view to handle a mouse click, so that we can determine if
+ // a double-click lands on the same view as its single-click part.
+ View* last_click_handler_;
+
+ // true if mouse_pressed_handler_ has been explicitly set
+ bool explicit_mouse_handler_;
+
+ // Last position/flag of a mouse press/drag. Used if capture stops and we need
+ // to synthesize a release.
+ int last_mouse_event_flags_;
+ int last_mouse_event_x_;
+ int last_mouse_event_y_;
+
+ // The gesture_manager_ for this.
+ GestureManager* gesture_manager_;
+
+ // The view currently handling touch events.
+ View* touch_pressed_handler_;
+
+ // Focus ---------------------------------------------------------------------
+
+ // The focus search algorithm.
+ FocusSearch focus_search_;
+
+ // Whether this root view belongs to the current active window.
+ // bool activated_;
+
+ // The parent FocusTraversable, used for focus traversal.
+ FocusTraversable* focus_traversable_parent_;
+
+ // The View that contains this RootView. This is used when we have RootView
+ // wrapped inside native components, and is used for the focus traversal.
+ View* focus_traversable_parent_view_;
+
+ // Drag and drop -------------------------------------------------------------
+
+ // Tracks drag state for a view.
+ View::DragInfo drag_info;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(RootView);
+};
+
+} // namespace internal
+} // namespace views
+
+#endif // VIEWS_WIDGET_ROOT_VIEW_H_
diff --git a/views/widget/tooltip_manager.cc b/views/widget/tooltip_manager.cc
new file mode 100644
index 0000000..1d8b285
--- /dev/null
+++ b/views/widget/tooltip_manager.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 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/tooltip_manager.h"
+
+#include <vector>
+
+#include "base/string_split.h"
+#include "base/utf_string_conversions.h"
+#include "ui/base/text/text_elider.h"
+
+namespace {
+
+// Maximum number of characters we allow in a tooltip.
+const size_t kMaxTooltipLength = 1024;
+
+// Maximum number of lines we allow in the tooltip.
+const size_t kMaxLines = 6;
+
+} // namespace
+
+namespace views {
+
+// static
+void TooltipManager::TrimTooltipToFit(string16* text,
+ int* max_width,
+ int* line_count,
+ int x,
+ int y) {
+ *max_width = 0;
+ *line_count = 0;
+
+ // Clamp the tooltip length to kMaxTooltipLength so that we don't
+ // accidentally DOS the user with a mega tooltip.
+ if (text->length() > kMaxTooltipLength)
+ *text = text->substr(0, kMaxTooltipLength);
+
+ // Determine the available width for the tooltip.
+ int available_width = GetMaxWidth(x, y);
+
+ // Split the string into at most kMaxLines lines.
+ std::vector<string16> lines;
+ base::SplitString(*text, '\n', &lines);
+ if (lines.size() > kMaxLines)
+ lines.resize(kMaxLines);
+ *line_count = static_cast<int>(lines.size());
+
+ // Format each line to fit.
+ gfx::Font font = GetDefaultFont();
+ string16 result;
+ for (std::vector<string16>::iterator i = lines.begin(); i != lines.end();
+ ++i) {
+ string16 elided_text = ui::ElideText(*i, font, available_width, false);
+ *max_width = std::max(*max_width, font.GetStringWidth(elided_text));
+ if (!result.empty())
+ result.push_back('\n');
+ result.append(elided_text);
+ }
+ *text = result;
+}
+
+} // namespace views
diff --git a/views/widget/tooltip_manager.h b/views/widget/tooltip_manager.h
new file mode 100644
index 0000000..6b4c8d9
--- /dev/null
+++ b/views/widget/tooltip_manager.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_TOOLTIP_MANAGER_H_
+#define VIEWS_WIDGET_TOOLTIP_MANAGER_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "views/views_export.h"
+
+namespace gfx {
+class Font;
+} // namespace gfx
+
+namespace views {
+
+class View;
+
+// TooltipManager takes care of the wiring to support tooltips for Views. You
+// almost never need to interact directly with TooltipManager, rather look to
+// the various tooltip methods on View.
+class VIEWS_EXPORT TooltipManager {
+ public:
+ // Returns the height of tooltips. This should only be invoked from within
+ // GetTooltipTextOrigin.
+ static int GetTooltipHeight();
+
+ // Returns the default font used by tooltips.
+ static gfx::Font GetDefaultFont();
+
+ // Returns the maximum width of the tooltip. |x| and |y| give the location
+ // the tooltip is to be displayed on in screen coordinates.
+ static int GetMaxWidth(int x, int y);
+
+ TooltipManager() {}
+ virtual ~TooltipManager() {}
+
+ // Notification that the view hierarchy has changed in some way.
+ virtual void UpdateTooltip() = 0;
+
+ // Invoked when the tooltip text changes for the specified views.
+ virtual void TooltipTextChanged(View* view) = 0;
+
+ // Invoked when toolbar icon gets focus.
+ virtual void ShowKeyboardTooltip(View* view) = 0;
+
+ // Invoked when toolbar loses focus.
+ virtual void HideKeyboardTooltip() = 0;
+
+ protected:
+ // Trims the tooltip to fit, setting |text| to the clipped result,
+ // |max_width| to the width (in pixels) of the clipped text and |line_count|
+ // to the number of lines of text in the tooltip. |x| and |y| give the
+ // location of the tooltip in screen coordinates.
+ static void TrimTooltipToFit(string16* text,
+ int* max_width,
+ int* line_count,
+ int x,
+ int y);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_TOOLTIP_MANAGER_H_
diff --git a/views/widget/tooltip_manager_gtk.cc b/views/widget/tooltip_manager_gtk.cc
new file mode 100644
index 0000000..82828fc
--- /dev/null
+++ b/views/widget/tooltip_manager_gtk.cc
@@ -0,0 +1,174 @@
+// Copyright (c) 2011 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/tooltip_manager_gtk.h"
+
+#include "base/logging.h"
+#include "base/utf_string_conversions.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/focus/focus_manager.h"
+#include "views/view.h"
+#include "views/widget/native_widget_gtk.h"
+
+// WARNING: this implementation is good for a start, but it doesn't give us
+// control of tooltip positioning both on mouse events and when showing from
+// keyboard. We may need to write our own to give us the control we need.
+
+namespace views {
+
+static gfx::Font* LoadDefaultFont() {
+ // Create a tooltip widget and extract the font from it (we have to realize
+ // it to make sure the correct font gets set).
+ GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
+ gtk_widget_set_name(window, "gtk-tooltip");
+ GtkWidget* label = gtk_label_new("");
+ gtk_widget_show(label);
+
+ gtk_container_add(GTK_CONTAINER(window), label);
+ gtk_widget_realize(window);
+
+ GtkStyle* style = gtk_widget_get_style(label);
+ gfx::Font* font = new gfx::Font(style->font_desc);
+
+ gtk_widget_destroy(window);
+
+ return font;
+}
+
+// static
+int TooltipManager::GetTooltipHeight() {
+ // This is only used to position the tooltip, and we don't yet support
+ // positioning the tooltip, it isn't worth trying to implement this.
+ return 0;
+}
+
+// static
+gfx::Font TooltipManager::GetDefaultFont() {
+ static gfx::Font* font = NULL;
+ if (!font)
+ font = LoadDefaultFont();
+
+ return *font;
+}
+
+// static
+int TooltipManager::GetMaxWidth(int x, int y) {
+ gfx::Rect monitor_bounds =
+ gfx::Screen::GetMonitorAreaNearestPoint(gfx::Point(x, y));
+ // GtkLabel (gtk_label_ensure_layout) forces wrapping at this size. We mirror
+ // the size here otherwise tooltips wider than the size used by gtklabel end
+ // up with extraneous empty lines.
+ return monitor_bounds.width() == 0 ? 800 : (monitor_bounds.width() + 1) / 2;
+}
+
+TooltipManagerGtk::TooltipManagerGtk(NativeWidgetGtk* widget)
+ : widget_(widget),
+ keyboard_view_(NULL),
+ tooltip_window_(widget->window_contents()) {
+}
+
+bool TooltipManagerGtk::ShowTooltip(int x, int y, bool for_keyboard,
+ GtkTooltip* tooltip) {
+ const View* view = NULL;
+ gfx::Point view_loc;
+ if (keyboard_view_) {
+ view = keyboard_view_;
+ view_loc.SetPoint(view->width() / 2, view->height() / 2);
+ } else if (!for_keyboard) {
+ View* root_view = widget_->GetWidget()->GetRootView();
+ view = root_view->GetEventHandlerForPoint(gfx::Point(x, y));
+ view_loc.SetPoint(x, y);
+ View::ConvertPointFromWidget(view, &view_loc);
+ } else {
+ const FocusManager* focus_manager = widget_->GetWidget()->GetFocusManager();
+ if (focus_manager) {
+ view = focus_manager->GetFocusedView();
+ if (view)
+ view_loc.SetPoint(view->width() / 2, view->height() / 2);
+ }
+ }
+
+ if (!view)
+ return false;
+
+ string16 text;
+ if (!view->GetTooltipText(view_loc, &text))
+ return false;
+
+ // Sets the area of the tooltip. This way if different views in the same
+ // widget have tooltips the tooltip doesn't get stuck at the same location.
+ gfx::Rect vis_bounds = view->GetVisibleBounds();
+ gfx::Point widget_loc(vis_bounds.origin());
+ View::ConvertPointToWidget(view, &widget_loc);
+ GdkRectangle tip_area = { widget_loc.x(), widget_loc.y(),
+ vis_bounds.width(), vis_bounds.height() };
+ gtk_tooltip_set_tip_area(tooltip, &tip_area);
+
+ int max_width, line_count;
+ gfx::Point screen_loc(x, y);
+ View::ConvertPointToScreen(widget_->GetWidget()->GetRootView(), &screen_loc);
+ TrimTooltipToFit(&text, &max_width, &line_count, screen_loc.x(),
+ screen_loc.y());
+ tooltip_window_.SetTooltipText(text);
+
+ return true;
+}
+
+void TooltipManagerGtk::UpdateTooltip() {
+ // UpdateTooltip may be invoked after the widget has been destroyed.
+ GtkWidget* widget = widget_->GetNativeView();
+ if (!widget)
+ return;
+
+ GdkDisplay* display = gtk_widget_get_display(widget);
+ if (display)
+ gtk_tooltip_trigger_tooltip_query(display);
+}
+
+void TooltipManagerGtk::TooltipTextChanged(View* view) {
+ UpdateTooltip();
+}
+
+void TooltipManagerGtk::ShowKeyboardTooltip(View* view) {
+ if (view == keyboard_view_)
+ return; // We're already showing the tip for the specified view.
+
+ // We have to hide the current tooltip, then show again.
+ HideKeyboardTooltip();
+
+ string16 tooltip_text;
+ if (!view->GetTooltipText(gfx::Point(), &tooltip_text))
+ return; // The view doesn't have a tooltip, nothing to do.
+
+ keyboard_view_ = view;
+ if (!SendShowHelpSignal()) {
+ keyboard_view_ = NULL;
+ return;
+ }
+}
+
+void TooltipManagerGtk::HideKeyboardTooltip() {
+ if (!keyboard_view_)
+ return;
+
+ SendShowHelpSignal();
+ keyboard_view_ = NULL;
+}
+
+bool TooltipManagerGtk::SendShowHelpSignal() {
+ GtkWidget* widget = widget_->window_contents();
+ GType itype = G_TYPE_FROM_INSTANCE(G_OBJECT(widget));
+ guint signal_id;
+ GQuark detail;
+ if (!g_signal_parse_name("show_help", itype, &signal_id, &detail, FALSE)) {
+ NOTREACHED();
+ return false;
+ }
+ gboolean result;
+ g_signal_emit(widget, signal_id, 0, GTK_WIDGET_HELP_TOOLTIP, &result);
+ return true;
+}
+
+} // namespace views
diff --git a/views/widget/tooltip_manager_gtk.h b/views/widget/tooltip_manager_gtk.h
new file mode 100644
index 0000000..8273425
--- /dev/null
+++ b/views/widget/tooltip_manager_gtk.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_TOOLTIP_MANAGER_GTK_H_
+#define VIEWS_WIDGET_TOOLTIP_MANAGER_GTK_H_
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "ui/base/gtk/tooltip_window_gtk.h"
+#include "views/widget/tooltip_manager.h"
+
+namespace views {
+
+class NativeWidgetGtk;
+
+// TooltipManager implementation for Gtk.
+class TooltipManagerGtk : public TooltipManager {
+ public:
+ explicit TooltipManagerGtk(NativeWidgetGtk* widget);
+ virtual ~TooltipManagerGtk() {}
+
+ // Shows the tooltip at the specified location. Returns true if the tooltip
+ // should be shown, false otherwise.
+ bool ShowTooltip(int x, int y, bool for_keyboard, GtkTooltip* gtk_tooltip);
+
+ // TooltipManager.
+ virtual void UpdateTooltip();
+ virtual void TooltipTextChanged(View* view);
+ virtual void ShowKeyboardTooltip(View* view);
+ virtual void HideKeyboardTooltip();
+
+ private:
+ // Sends the show_help signal to widget_. This signal triggers showing the
+ // keyboard tooltip if it isn't showing, or hides it if it is showing.
+ bool SendShowHelpSignal();
+
+ // Our owner.
+ NativeWidgetGtk* widget_;
+
+ // The view supplied to the last invocation of ShowKeyboardTooltip.
+ View* keyboard_view_;
+
+ // Customized tooltip window.
+ ui::TooltipWindowGtk tooltip_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(TooltipManagerGtk);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_TOOLTIP_MANAGER_GTK_H_
diff --git a/views/widget/tooltip_manager_views.cc b/views/widget/tooltip_manager_views.cc
new file mode 100644
index 0000000..904099a
--- /dev/null
+++ b/views/widget/tooltip_manager_views.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2011 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/tooltip_manager_views.h"
+
+#if defined(USE_X11)
+#include <X11/Xlib.h>
+#include <X11/extensions/XInput2.h>
+#endif
+
+#if defined(OS_WIN)
+#include <windowsx.h>
+#endif
+
+#include "base/event_types.h"
+#include "base/logging.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkColor.h"
+#if defined(USE_AURA)
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/event.h"
+#include "ui/aura/window.h"
+#endif
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/events/event.h"
+#include "ui/views/focus/focus_manager.h"
+#include "views/background.h"
+#include "views/border.h"
+#include "views/view.h"
+#include "views/widget/native_widget.h"
+
+namespace {
+SkColor kTooltipBackground = 0xFF7F7F00;
+int kTooltipTimeoutMs = 500;
+
+// FIXME: get cursor offset from actual cursor size.
+int kCursorOffsetX = 10;
+int kCursorOffsetY = 15;
+}
+
+namespace views {
+
+// static
+int TooltipManager::GetTooltipHeight() {
+ // Not used for linux and chromeos.
+ NOTREACHED();
+ return 0;
+}
+
+// static
+gfx::Font TooltipManager::GetDefaultFont() {
+ return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
+}
+
+// static
+int TooltipManager::GetMaxWidth(int x, int y) {
+ // FIXME: change this. This is for now just copied from TooltipManagerGtk.
+
+ // We always display the tooltip inside the root view. So the max width is
+ // the width of the view.
+ gfx::Rect monitor_bounds =
+ gfx::Screen::GetMonitorAreaNearestPoint(gfx::Point(x, y));
+ // GtkLabel (gtk_label_ensure_layout) forces wrapping at this size. We mirror
+ // the size here otherwise tooltips wider than the size used by gtklabel end
+ // up with extraneous empty lines.
+ return monitor_bounds.width() == 0 ? 800 : (monitor_bounds.width() + 1) / 2;
+}
+
+TooltipManagerViews::TooltipManagerViews(views::View* root_view)
+ : root_view_(root_view),
+ tooltip_view_(NULL) {
+ tooltip_label_.set_background(
+ views::Background::CreateSolidBackground(kTooltipBackground));
+ tooltip_widget_.reset(CreateTooltip());
+ tooltip_widget_->SetContentsView(&tooltip_label_);
+ tooltip_widget_->Activate();
+ tooltip_widget_->SetAlwaysOnTop(true);
+}
+
+TooltipManagerViews::~TooltipManagerViews() {
+ tooltip_widget_->CloseNow();
+}
+
+void TooltipManagerViews::UpdateForMouseEvent(const MouseEvent& event) {
+ switch (event.type()) {
+ case ui::ET_MOUSE_EXITED:
+ // Mouse is exiting this widget. Stop showing the tooltip and the timer.
+ if (tooltip_timer_.IsRunning())
+ tooltip_timer_.Stop();
+ if (tooltip_widget_->IsVisible())
+ tooltip_widget_->Hide();
+ break;
+ case ui::ET_MOUSE_ENTERED:
+ // Mouse just entered this widget. Start the timer to show the tooltip.
+ if (tooltip_timer_.IsRunning())
+ tooltip_timer_.Stop();
+ tooltip_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kTooltipTimeoutMs),
+ this, &TooltipManagerViews::TooltipTimerFired);
+ break;
+ case ui::ET_MOUSE_MOVED:
+ OnMouseMoved(event.location().x(), event.location().y());
+ break;
+ case ui::ET_MOUSE_PRESSED:
+ case ui::ET_MOUSE_RELEASED:
+ case ui::ET_MOUSE_DRAGGED:
+ case ui::ET_MOUSEWHEEL:
+ // Hide the tooltip for click, release, drag, wheel events.
+ if (tooltip_widget_->IsVisible())
+ tooltip_widget_->Hide();
+ break;
+ default:
+ NOTIMPLEMENTED();
+ }
+}
+
+void TooltipManagerViews::UpdateTooltip() {
+ UpdateIfRequired(curr_mouse_pos_.x(), curr_mouse_pos_.y(), false);
+}
+
+void TooltipManagerViews::TooltipTextChanged(View* view) {
+ if (tooltip_widget_->IsVisible())
+ UpdateIfRequired(curr_mouse_pos_.x(), curr_mouse_pos_.y(), false);
+}
+
+void TooltipManagerViews::ShowKeyboardTooltip(View* view) {
+ NOTREACHED();
+}
+
+void TooltipManagerViews::HideKeyboardTooltip() {
+ NOTREACHED();
+}
+
+void TooltipManagerViews::TooltipTimerFired() {
+ UpdateIfRequired(curr_mouse_pos_.x(), curr_mouse_pos_.y(), false);
+}
+
+View* TooltipManagerViews::GetViewForTooltip(int x, int y, bool for_keyboard) {
+ View* view = NULL;
+ if (!for_keyboard) {
+ // Convert x,y from screen coordinates to |root_view_| coordinates.
+ gfx::Point point(x, y);
+ View::ConvertPointFromWidget(root_view_, &point);
+ view = root_view_->GetEventHandlerForPoint(point);
+ } else {
+ FocusManager* focus_manager = root_view_->GetFocusManager();
+ if (focus_manager)
+ view = focus_manager->GetFocusedView();
+ }
+ return view;
+}
+
+void TooltipManagerViews::UpdateIfRequired(int x, int y, bool for_keyboard) {
+ View* view = GetViewForTooltip(x, y, for_keyboard);
+ string16 tooltip_text;
+ if (view)
+ view->GetTooltipText(gfx::Point(x, y), &tooltip_text);
+
+#if defined(USE_AURA)
+ // In aura, and aura::Window can also have a tooltip. If the view doesnot have
+ // a tooltip, we must also check for the aura::Window underneath the cursor.
+ if (tooltip_text.empty()) {
+ aura::Window* root = reinterpret_cast<aura::Window*>(
+ root_view_->GetWidget()->GetNativeView());
+ if (root) {
+ aura::Window* window = root->GetEventHandlerForPoint(gfx::Point(x, y));
+ if (window) {
+ void* property = window->GetProperty(aura::kTooltipTextKey);
+ if (property)
+ tooltip_text = *reinterpret_cast<string16*>(property);
+ }
+ }
+ }
+#endif
+
+ if (tooltip_view_ != view || tooltip_text_ != tooltip_text) {
+ tooltip_view_ = view;
+ tooltip_text_ = tooltip_text;
+ Update();
+ }
+}
+
+void TooltipManagerViews::Update() {
+ if (tooltip_text_.empty()) {
+ tooltip_widget_->Hide();
+ } else {
+ int max_width, line_count;
+ string16 tooltip_text(tooltip_text_);
+ TrimTooltipToFit(&tooltip_text, &max_width, &line_count,
+ curr_mouse_pos_.x(), curr_mouse_pos_.y());
+ tooltip_label_.SetText(tooltip_text);
+
+ SetTooltipBounds(curr_mouse_pos_, max_width,
+ tooltip_label_.GetPreferredSize().height());
+ tooltip_widget_->Show();
+ }
+}
+
+void TooltipManagerViews::SetTooltipBounds(gfx::Point mouse_pos,
+ int tooltip_width,
+ int tooltip_height) {
+ gfx::Rect tooltip_rect(mouse_pos.x(), mouse_pos.y(), tooltip_width,
+ tooltip_height);
+
+ tooltip_rect.Offset(kCursorOffsetX, kCursorOffsetY);
+ gfx::Rect monitor_bounds =
+ gfx::Screen::GetMonitorAreaNearestPoint(tooltip_rect.origin());
+ tooltip_widget_->SetBounds(tooltip_rect.AdjustToFit(monitor_bounds));
+}
+
+Widget* TooltipManagerViews::CreateTooltip() {
+ Widget* widget = new Widget;
+ Widget::InitParams params;
+ // For aura, since we set the type to TOOLTIP_TYPE, the widget will get
+ // auto-parented to the MenuAndTooltipsContainer.
+ params.type = Widget::InitParams::TYPE_TOOLTIP;
+ params.keep_on_top = true;
+ params.accept_events = false;
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ widget->Init(params);
+ widget->SetOpacity(0xFF);
+ return widget;
+}
+
+void TooltipManagerViews::OnMouseMoved(int x, int y) {
+ if (tooltip_timer_.IsRunning())
+ tooltip_timer_.Reset();
+ curr_mouse_pos_.SetPoint(x, y);
+
+ // If tooltip is visible, we may want to hide it. If it is not, we are ok.
+ if (tooltip_widget_->IsVisible())
+ UpdateIfRequired(curr_mouse_pos_.x(), curr_mouse_pos_.y(), false);
+}
+
+} // namespace views
diff --git a/views/widget/tooltip_manager_views.h b/views/widget/tooltip_manager_views.h
new file mode 100644
index 0000000..4cf2657
--- /dev/null
+++ b/views/widget/tooltip_manager_views.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_TOOLTIP_MANAGER_VIEWS_H_
+#define VIEWS_WIDGET_TOOLTIP_MANAGER_VIEWS_H_
+#pragma once
+
+#include "base/message_loop.h"
+#include "base/timer.h"
+#include "views/controls/label.h"
+#include "views/widget/native_widget.h"
+#include "views/widget/tooltip_manager.h"
+#include "views/widget/widget_delegate.h"
+#include "views/view.h"
+
+#if defined(USE_X11)
+typedef union _XEvent XEvent;
+#endif
+
+namespace views {
+
+class MouseEvent;
+class Widget;
+
+// TooltipManager implementation for Views.
+class TooltipManagerViews : public TooltipManager {
+ public:
+ explicit TooltipManagerViews(views::View* root_view);
+ virtual ~TooltipManagerViews();
+
+ // Updates the state of the tooltip based on the mouse event. The mouse event
+ // is the same event that goes to a Widget (i.e. it is in the Widget's
+ // coordinate system).
+ void UpdateForMouseEvent(const MouseEvent& event);
+
+ // TooltipManager.
+ virtual void UpdateTooltip() OVERRIDE;
+ virtual void TooltipTextChanged(View* view) OVERRIDE;
+ virtual void ShowKeyboardTooltip(View* view) OVERRIDE;
+ virtual void HideKeyboardTooltip() OVERRIDE;
+
+ private:
+ void TooltipTimerFired();
+ View* GetViewForTooltip(int x, int y, bool for_keyboard);
+
+ // Updates the tooltip if required (if there is any change in the tooltip
+ // text or the view.
+ void UpdateIfRequired(int x, int y, bool for_keyboard);
+
+ // Updates the tooltip. Gets the tooltip text from tooltip_view_ and displays
+ // it at the current mouse position.
+ void Update();
+
+ // Adjusts the bounds given by the arguments to fit inside the parent view
+ // and applies the adjusted bounds to the tooltip_label_.
+ void SetTooltipBounds(gfx::Point mouse_pos, int tooltip_width,
+ int tooltip_height);
+
+ // Creates a widget of type TYPE_TOOLTIP
+ Widget* CreateTooltip();
+
+ // Invoked when the mose moves.
+ void OnMouseMoved(int x, int y);
+
+ scoped_ptr<Widget> tooltip_widget_;
+ views::View* root_view_;
+ View* tooltip_view_;
+ string16 tooltip_text_;
+ Label tooltip_label_;
+
+ gfx::Point curr_mouse_pos_;
+ base::RepeatingTimer<TooltipManagerViews> tooltip_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(TooltipManagerViews);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_TOOLTIP_MANAGER_VIEWS_H_
diff --git a/views/widget/tooltip_manager_win.cc b/views/widget/tooltip_manager_win.cc
new file mode 100644
index 0000000..521fcc0
--- /dev/null
+++ b/views/widget/tooltip_manager_win.cc
@@ -0,0 +1,391 @@
+// Copyright (c) 2011 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/tooltip_manager_win.h"
+
+#include <windowsx.h>
+
+#include <limits>
+
+#include "base/bind.h"
+#include "base/i18n/rtl.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "ui/base/l10n/l10n_util_win.h"
+#include "ui/base/win/hwnd_util.h"
+#include "ui/gfx/font.h"
+#include "ui/gfx/screen.h"
+#include "views/view.h"
+#include "views/widget/monitor_win.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+static int tooltip_height_ = 0;
+
+// Default timeout for the tooltip displayed using keyboard.
+// Timeout is mentioned in milliseconds.
+static const int kDefaultTimeout = 4000;
+
+// static
+int TooltipManager::GetTooltipHeight() {
+ DCHECK_GT(tooltip_height_, 0);
+ return tooltip_height_;
+}
+
+static gfx::Font DetermineDefaultFont() {
+ HWND window = CreateWindowEx(
+ WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
+ TOOLTIPS_CLASS, NULL, 0 , 0, 0, 0, 0, NULL, NULL, NULL, NULL);
+ if (!window)
+ return gfx::Font();
+ HFONT hfont = reinterpret_cast<HFONT>(SendMessage(window, WM_GETFONT, 0, 0));
+ gfx::Font font = hfont ? gfx::Font(hfont) : gfx::Font();
+ DestroyWindow(window);
+ return font;
+}
+
+// static
+gfx::Font TooltipManager::GetDefaultFont() {
+ static gfx::Font* font = NULL;
+ if (!font)
+ font = new gfx::Font(DetermineDefaultFont());
+ return *font;
+}
+
+// static
+int TooltipManager::GetMaxWidth(int x, int y) {
+ gfx::Rect monitor_bounds =
+ gfx::Screen::GetMonitorAreaNearestPoint(gfx::Point(x, y));
+ // Allow the tooltip to be almost as wide as the screen.
+ // Otherwise, we would truncate important text, since we're not word-wrapping
+ // the text onto multiple lines.
+ return monitor_bounds.width() == 0 ? 800 : monitor_bounds.width() - 30;
+}
+
+TooltipManagerWin::TooltipManagerWin(Widget* widget)
+ : widget_(widget),
+ tooltip_hwnd_(NULL),
+ last_mouse_pos_(-1, -1),
+ tooltip_showing_(false),
+ last_tooltip_view_(NULL),
+ last_view_out_of_sync_(false),
+ tooltip_width_(0),
+ keyboard_tooltip_hwnd_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(keyboard_tooltip_factory_(this)) {
+ DCHECK(widget);
+ DCHECK(widget->GetNativeView());
+}
+
+TooltipManagerWin::~TooltipManagerWin() {
+ if (tooltip_hwnd_)
+ DestroyWindow(tooltip_hwnd_);
+ if (keyboard_tooltip_hwnd_)
+ DestroyWindow(keyboard_tooltip_hwnd_);
+}
+
+bool TooltipManagerWin::Init() {
+ DCHECK(!tooltip_hwnd_);
+ // Create the tooltip control.
+ tooltip_hwnd_ = CreateWindowEx(
+ WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
+ TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0,
+ GetParent(), NULL, NULL, NULL);
+ if (!tooltip_hwnd_)
+ return false;
+
+ l10n_util::AdjustUIFontForWindow(tooltip_hwnd_);
+
+ // This effectively turns off clipping of tooltips. We need this otherwise
+ // multi-line text (\r\n) won't work right. The size doesn't really matter
+ // (just as long as its bigger than the monitor's width) as we clip to the
+ // screen size before rendering.
+ SendMessage(tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0,
+ std::numeric_limits<int16>::max());
+
+ // Add one tool that is used for all tooltips.
+ toolinfo_.cbSize = sizeof(toolinfo_);
+ toolinfo_.uFlags = TTF_TRANSPARENT | TTF_IDISHWND;
+ toolinfo_.hwnd = GetParent();
+ toolinfo_.uId = reinterpret_cast<UINT_PTR>(GetParent());
+ // Setting this tells windows to call GetParent() back (using a WM_NOTIFY
+ // message) for the actual tooltip contents.
+ toolinfo_.lpszText = LPSTR_TEXTCALLBACK;
+ toolinfo_.lpReserved = NULL;
+ SetRectEmpty(&toolinfo_.rect);
+ SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0, (LPARAM)&toolinfo_);
+ return true;
+}
+
+gfx::NativeView TooltipManagerWin::GetParent() {
+ return widget_->GetNativeView();
+}
+
+void TooltipManagerWin::UpdateTooltip() {
+ // Set last_view_out_of_sync_ to indicate the view is currently out of sync.
+ // This doesn't update the view under the mouse immediately as it may cause
+ // timing problems.
+ last_view_out_of_sync_ = true;
+ last_tooltip_view_ = NULL;
+ // Hide the tooltip.
+ SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
+}
+
+void TooltipManagerWin::TooltipTextChanged(View* view) {
+ if (view == last_tooltip_view_)
+ UpdateTooltip(last_mouse_pos_);
+}
+
+LRESULT TooltipManagerWin::OnNotify(int w_param,
+ NMHDR* l_param,
+ bool* handled) {
+ *handled = false;
+ if (l_param->hwndFrom == tooltip_hwnd_ && keyboard_tooltip_hwnd_ == NULL) {
+ switch (l_param->code) {
+ case TTN_GETDISPINFO: {
+ if (last_view_out_of_sync_) {
+ // View under the mouse is out of sync, determine it now.
+ View* root_view = widget_->GetRootView();
+ last_tooltip_view_ =
+ root_view->GetEventHandlerForPoint(last_mouse_pos_);
+ last_view_out_of_sync_ = false;
+ }
+ // Tooltip control is asking for the tooltip to display.
+ NMTTDISPINFOW* tooltip_info =
+ reinterpret_cast<NMTTDISPINFOW*>(l_param);
+ // Initialize the string, if we have a valid tooltip the string will
+ // get reset below.
+ tooltip_info->szText[0] = TEXT('\0');
+ tooltip_text_.clear();
+ tooltip_info->lpszText = NULL;
+ clipped_text_.clear();
+ if (last_tooltip_view_ != NULL) {
+ tooltip_text_.clear();
+ // Mouse is over a View, ask the View for its tooltip.
+ gfx::Point view_loc = last_mouse_pos_;
+ View::ConvertPointToView(widget_->GetRootView(),
+ last_tooltip_view_, &view_loc);
+ if (last_tooltip_view_->GetTooltipText(view_loc, &tooltip_text_) &&
+ !tooltip_text_.empty()) {
+ // View has a valid tip, copy it into TOOLTIPINFO.
+ clipped_text_ = tooltip_text_;
+ gfx::Point screen_loc = last_mouse_pos_;
+ View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc);
+ TrimTooltipToFit(&clipped_text_, &tooltip_width_, &line_count_,
+ screen_loc.x(), screen_loc.y());
+ // Adjust the clipped tooltip text for locale direction.
+ base::i18n::AdjustStringForLocaleDirection(&clipped_text_);
+ tooltip_info->lpszText = const_cast<WCHAR*>(clipped_text_.c_str());
+ } else {
+ tooltip_text_.clear();
+ }
+ }
+ *handled = true;
+ return 0;
+ }
+ case TTN_POP:
+ tooltip_showing_ = false;
+ *handled = true;
+ return 0;
+ case TTN_SHOW: {
+ *handled = true;
+ tooltip_showing_ = true;
+ // The tooltip is about to show, allow the view to position it
+ gfx::Point text_origin;
+ if (tooltip_height_ == 0)
+ tooltip_height_ = CalcTooltipHeight();
+ gfx::Point view_loc = last_mouse_pos_;
+ View::ConvertPointToView(widget_->GetRootView(),
+ last_tooltip_view_, &view_loc);
+ if (last_tooltip_view_->GetTooltipTextOrigin(view_loc, &text_origin) &&
+ SetTooltipPosition(text_origin.x(), text_origin.y())) {
+ // Return true, otherwise the rectangle we specified is ignored.
+ return TRUE;
+ }
+ return 0;
+ }
+ default:
+ // Fall through.
+ break;
+ }
+ }
+ return 0;
+}
+
+bool TooltipManagerWin::SetTooltipPosition(int text_x, int text_y) {
+ // NOTE: this really only tests that the y location fits on screen, but that
+ // is good enough for our usage.
+
+ // Calculate the bounds the tooltip will get.
+ gfx::Point view_loc;
+ View::ConvertPointToScreen(last_tooltip_view_, &view_loc);
+ RECT bounds = { view_loc.x() + text_x,
+ view_loc.y() + text_y,
+ view_loc.x() + text_x + tooltip_width_,
+ view_loc.y() + line_count_ * GetTooltipHeight() };
+ SendMessage(tooltip_hwnd_, TTM_ADJUSTRECT, TRUE, (LPARAM)&bounds);
+
+ // Make sure the rectangle completely fits on the current monitor. If it
+ // doesn't, return false so that windows positions the tooltip at the
+ // default location.
+ gfx::Rect monitor_bounds =
+ views::GetMonitorBoundsForRect(gfx::Rect(bounds.left, bounds.right,
+ 0, 0));
+ if (!monitor_bounds.Contains(gfx::Rect(bounds))) {
+ return false;
+ }
+
+ ::SetWindowPos(tooltip_hwnd_, NULL, bounds.left, bounds.top, 0, 0,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
+ return true;
+}
+
+int TooltipManagerWin::CalcTooltipHeight() {
+ // Ask the tooltip for it's font.
+ int height;
+ HFONT hfont = reinterpret_cast<HFONT>(
+ SendMessage(tooltip_hwnd_, WM_GETFONT, 0, 0));
+ if (hfont != NULL) {
+ HDC dc = GetDC(tooltip_hwnd_);
+ HFONT previous_font = static_cast<HFONT>(SelectObject(dc, hfont));
+ int last_map_mode = SetMapMode(dc, MM_TEXT);
+ TEXTMETRIC font_metrics;
+ GetTextMetrics(dc, &font_metrics);
+ height = font_metrics.tmHeight;
+ // To avoid the DC referencing font_handle_, select the previous font.
+ SelectObject(dc, previous_font);
+ SetMapMode(dc, last_map_mode);
+ ReleaseDC(NULL, dc);
+ } else {
+ // Tooltip is using the system font. Use gfx::Font, which should pick
+ // up the system font.
+ height = gfx::Font().GetHeight();
+ }
+ // Get the margins from the tooltip
+ RECT tooltip_margin;
+ SendMessage(tooltip_hwnd_, TTM_GETMARGIN, 0, (LPARAM)&tooltip_margin);
+ return height + tooltip_margin.top + tooltip_margin.bottom;
+}
+
+void TooltipManagerWin::UpdateTooltip(const gfx::Point& mouse_pos) {
+ View* root_view = widget_->GetRootView();
+ View* view = root_view->GetEventHandlerForPoint(mouse_pos);
+ if (view != last_tooltip_view_) {
+ // NOTE: This *must* be sent regardless of the visibility of the tooltip.
+ // It triggers Windows to ask for the tooltip again.
+ SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
+ last_tooltip_view_ = view;
+ } else if (last_tooltip_view_ != NULL) {
+ // Tooltip is showing, and mouse is over the same view. See if the tooltip
+ // text has changed.
+ gfx::Point view_point = mouse_pos;
+ View::ConvertPointToView(root_view, last_tooltip_view_, &view_point);
+ string16 new_tooltip_text;
+ bool has_tooltip_text =
+ last_tooltip_view_->GetTooltipText(view_point, &new_tooltip_text);
+ if (!has_tooltip_text || (new_tooltip_text != tooltip_text_)) {
+ // The text has changed, hide the popup.
+ SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
+ if (has_tooltip_text && !new_tooltip_text.empty() && tooltip_showing_) {
+ // New text is valid, show the popup.
+ SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0);
+ }
+ }
+ }
+}
+
+void TooltipManagerWin::OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param) {
+ gfx::Point mouse_pos(l_param);
+
+ if (u_msg >= WM_NCMOUSEMOVE && u_msg <= WM_NCXBUTTONDBLCLK) {
+ // NC message coordinates are in screen coordinates.
+ POINT temp = mouse_pos.ToPOINT();
+ ::MapWindowPoints(HWND_DESKTOP, GetParent(), &temp, 1);
+ mouse_pos.SetPoint(temp.x, temp.y);
+ }
+
+ if (u_msg != WM_MOUSEMOVE || last_mouse_pos_ != mouse_pos) {
+ last_mouse_pos_ = mouse_pos;
+ HideKeyboardTooltip();
+ UpdateTooltip(mouse_pos);
+ }
+ // Forward the message onto the tooltip.
+ MSG msg;
+ msg.hwnd = GetParent();
+ msg.message = u_msg;
+ msg.wParam = w_param;
+ msg.lParam = l_param;
+ SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, 0, (LPARAM)&msg);
+}
+
+void TooltipManagerWin::ShowKeyboardTooltip(View* focused_view) {
+ if (tooltip_showing_) {
+ SendMessage(tooltip_hwnd_, TTM_POP, 0, 0);
+ tooltip_text_.clear();
+ }
+ HideKeyboardTooltip();
+ string16 tooltip_text;
+ if (!focused_view->GetTooltipText(gfx::Point(), &tooltip_text))
+ return;
+ gfx::Rect focused_bounds = focused_view->bounds();
+ gfx::Point screen_point;
+ focused_view->ConvertPointToScreen(focused_view, &screen_point);
+ keyboard_tooltip_hwnd_ = CreateWindowEx(
+ WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(),
+ TOOLTIPS_CLASS, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
+ if (!keyboard_tooltip_hwnd_)
+ return;
+
+ SendMessage(keyboard_tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0,
+ std::numeric_limits<int16>::max());
+ int tooltip_width;
+ int line_count;
+ TrimTooltipToFit(&tooltip_text, &tooltip_width, &line_count,
+ screen_point.x(), screen_point.y());
+ ReplaceSubstringsAfterOffset(&tooltip_text, 0, L"\n", L"\r\n");
+ TOOLINFO keyboard_toolinfo;
+ memset(&keyboard_toolinfo, 0, sizeof(keyboard_toolinfo));
+ keyboard_toolinfo.cbSize = sizeof(keyboard_toolinfo);
+ keyboard_toolinfo.hwnd = GetParent();
+ keyboard_toolinfo.uFlags = TTF_TRACK | TTF_TRANSPARENT | TTF_IDISHWND;
+ keyboard_toolinfo.lpszText = const_cast<WCHAR*>(tooltip_text.c_str());
+ SendMessage(keyboard_tooltip_hwnd_, TTM_ADDTOOL, 0,
+ reinterpret_cast<LPARAM>(&keyboard_toolinfo));
+ SendMessage(keyboard_tooltip_hwnd_, TTM_TRACKACTIVATE, TRUE,
+ reinterpret_cast<LPARAM>(&keyboard_toolinfo));
+ if (!tooltip_height_)
+ tooltip_height_ = CalcTooltipHeight();
+ RECT rect_bounds = {screen_point.x(),
+ screen_point.y() + focused_bounds.height(),
+ screen_point.x() + tooltip_width,
+ screen_point.y() + focused_bounds.height() +
+ line_count * tooltip_height_ };
+ gfx::Rect monitor_bounds =
+ views::GetMonitorBoundsForRect(gfx::Rect(rect_bounds));
+ rect_bounds = gfx::Rect(rect_bounds).AdjustToFit(monitor_bounds).ToRECT();
+ ::SetWindowPos(keyboard_tooltip_hwnd_, NULL, rect_bounds.left,
+ rect_bounds.top, 0, 0,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&TooltipManagerWin::DestroyKeyboardTooltipWindow,
+ keyboard_tooltip_factory_.GetWeakPtr(),
+ keyboard_tooltip_hwnd_),
+ kDefaultTimeout);
+}
+
+void TooltipManagerWin::HideKeyboardTooltip() {
+ if (keyboard_tooltip_hwnd_ != NULL) {
+ SendMessage(keyboard_tooltip_hwnd_, WM_CLOSE, 0, 0);
+ keyboard_tooltip_hwnd_ = NULL;
+ }
+}
+
+void TooltipManagerWin::DestroyKeyboardTooltipWindow(HWND window_to_destroy) {
+ if (keyboard_tooltip_hwnd_ == window_to_destroy)
+ HideKeyboardTooltip();
+}
+
+} // namespace views
diff --git a/views/widget/tooltip_manager_win.h b/views/widget/tooltip_manager_win.h
new file mode 100644
index 0000000..f4afaa2
--- /dev/null
+++ b/views/widget/tooltip_manager_win.h
@@ -0,0 +1,153 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_TOOLTIP_MANAGER_WIN_H_
+#define VIEWS_WIDGET_TOOLTIP_MANAGER_WIN_H_
+#pragma once
+
+#include <windows.h>
+#include <commctrl.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "base/string16.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/point.h"
+#include "views/widget/tooltip_manager.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace views {
+
+class View;
+class Widget;
+
+// TooltipManager implementation for Windows.
+//
+// This class is intended to be used by NativeWidgetWin. To use this, you must
+// do the following:
+// Add the following to your MSG_MAP:
+//
+// MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange)
+// MESSAGE_RANGE_HANDLER(WM_NCMOUSEMOVE, WM_NCMOUSEMOVE, OnMouseRange)
+// MSG_WM_NOTIFY(OnNotify)
+//
+// With the following implementations:
+// LRESULT XXX::OnMouseRange(UINT u_msg, WPARAM w_param, LPARAM l_param,
+// BOOL& handled) {
+// tooltip_manager_->OnMouse(u_msg, w_param, l_param);
+// handled = FALSE;
+// return 0;
+// }
+//
+// LRESULT XXX::OnNotify(int w_param, NMHDR* l_param) {
+// bool handled;
+// LRESULT result = tooltip_manager_->OnNotify(w_param, l_param, &handled);
+// SetMsgHandled(handled);
+// return result;
+// }
+//
+// And of course you'll need to create the TooltipManager!
+//
+// Lastly, you'll need to override GetTooltipManager.
+//
+// See NativeWidgetWin for an example of this in action.
+class TooltipManagerWin : public TooltipManager {
+ public:
+ // Creates a TooltipManager for the specified Widget and parent window.
+ explicit TooltipManagerWin(Widget* widget);
+ virtual ~TooltipManagerWin();
+
+ // Initializes the TooltipManager returning whether initialization was
+ // successful. If this returns false the TooltipManager should be destroyed
+ // and not used.
+ bool Init();
+
+ // Notification that the view hierarchy has changed in some way.
+ virtual void UpdateTooltip();
+
+ // Invoked when the tooltip text changes for the specified views.
+ virtual void TooltipTextChanged(View* view);
+
+ // Invoked when toolbar icon gets focus.
+ virtual void ShowKeyboardTooltip(View* view);
+
+ // Invoked when toolbar loses focus.
+ virtual void HideKeyboardTooltip();
+
+ // Message handlers. These forward to the tooltip control.
+ virtual void OnMouse(UINT u_msg, WPARAM w_param, LPARAM l_param);
+ LRESULT OnNotify(int w_param, NMHDR* l_param, bool* handled);
+
+ protected:
+ // Returns the Widget we're showing tooltips for.
+ gfx::NativeView GetParent();
+
+ // Updates the tooltip for the specified location.
+ void UpdateTooltip(const gfx::Point& location);
+
+ // Tooltip control window.
+ HWND tooltip_hwnd_;
+
+ // Tooltip information.
+ TOOLINFO toolinfo_;
+
+ // Last location of the mouse. This is in the coordinates of the rootview.
+ gfx::Point last_mouse_pos_;
+
+ // Whether or not the tooltip is showing.
+ bool tooltip_showing_;
+
+ private:
+ // Sets the tooltip position based on the x/y position of the text. If the
+ // tooltip fits, true is returned.
+ bool SetTooltipPosition(int text_x, int text_y);
+
+ // Calculates the preferred height for tooltips. This always returns a
+ // positive value.
+ int CalcTooltipHeight();
+
+ // Invoked when the timer elapses and tooltip has to be destroyed.
+ void DestroyKeyboardTooltipWindow(HWND window_to_destroy);
+
+ // Hosting Widget.
+ Widget* widget_;
+
+ // The View the mouse is under. This is null if the mouse isn't under a
+ // View.
+ View* last_tooltip_view_;
+
+ // Whether or not the view under the mouse needs to be refreshed. If this
+ // is true, when the tooltip is asked for the view under the mouse is
+ // refreshed.
+ bool last_view_out_of_sync_;
+
+ // Text for tooltip from the view.
+ string16 tooltip_text_;
+
+ // The clipped tooltip.
+ string16 clipped_text_;
+
+ // Number of lines in the tooltip.
+ int line_count_;
+
+ // Width of the last tooltip.
+ int tooltip_width_;
+
+ // control window for tooltip displayed using keyboard.
+ HWND keyboard_tooltip_hwnd_;
+
+ // Used to register DestroyTooltipWindow function with PostDelayedTask
+ // function.
+ base::WeakPtrFactory<TooltipManagerWin> keyboard_tooltip_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TooltipManagerWin);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_TOOLTIP_MANAGER_WIN_H_
diff --git a/views/widget/widget.cc b/views/widget/widget.cc
new file mode 100644
index 0000000..9394028
--- /dev/null
+++ b/views/widget/widget.cc
@@ -0,0 +1,1218 @@
+// Copyright (c) 2011 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/widget.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/l10n/l10n_font_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/compositor/compositor.h"
+#include "ui/gfx/compositor/layer.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/focus/focus_manager_factory.h"
+#include "ui/views/focus/view_storage.h"
+#include "ui/views/focus/widget_focus_manager.h"
+#include "ui/views/ime/input_method.h"
+#include "ui/views/window/custom_frame_view.h"
+#include "views/controls/menu/menu_controller.h"
+#include "views/views_delegate.h"
+#include "views/widget/default_theme_provider.h"
+#include "views/widget/native_widget_private.h"
+#include "views/widget/root_view.h"
+#include "views/widget/tooltip_manager.h"
+#include "views/widget/widget_delegate.h"
+
+namespace {
+
+// Set to true if a pure Views implementation is preferred
+bool use_pure_views = false;
+
+// True to enable debug paint that indicates where to be painted.
+bool debug_paint = false;
+
+} // namespace
+
+namespace views {
+
+// This class is used to keep track of the event a Widget is processing, and
+// restore any previously active event afterwards.
+class ScopedEvent {
+ public:
+ ScopedEvent(Widget* widget, const Event& event)
+ : widget_(widget),
+ event_(&event) {
+ widget->event_stack_.push(this);
+ }
+
+ ~ScopedEvent() {
+ if (widget_)
+ widget_->event_stack_.pop();
+ }
+
+ void reset() {
+ widget_ = NULL;
+ }
+
+ const Event* event() {
+ return event_;
+ }
+
+ private:
+ Widget* widget_;
+ const Event* event_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedEvent);
+};
+
+// A default implementation of WidgetDelegate, used by Widget when no
+// WidgetDelegate is supplied.
+class DefaultWidgetDelegate : public WidgetDelegate {
+ public:
+ DefaultWidgetDelegate(Widget* widget, const Widget::InitParams& params)
+ : widget_(widget),
+ can_activate_(!params.child &&
+ params.type != Widget::InitParams::TYPE_POPUP) {
+ }
+ virtual ~DefaultWidgetDelegate() {}
+
+ // Overridden from WidgetDelegate:
+ virtual void DeleteDelegate() OVERRIDE {
+ delete this;
+ }
+ virtual Widget* GetWidget() {
+ return widget_;
+ }
+ virtual const Widget* GetWidget() const {
+ return widget_;
+ }
+
+ virtual bool CanActivate() const {
+ return can_activate_;
+ }
+
+ private:
+ Widget* widget_;
+ bool can_activate_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultWidgetDelegate);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget, InitParams:
+
+Widget::InitParams::InitParams()
+ : type(TYPE_WINDOW),
+ delegate(NULL),
+ child(false),
+ transient(false),
+ transparent(false),
+ accept_events(true),
+ can_activate(true),
+ keep_on_top(false),
+ ownership(NATIVE_WIDGET_OWNS_WIDGET),
+ mirror_origin_in_rtl(false),
+ has_dropshadow(false),
+ show_state(ui::SHOW_STATE_DEFAULT),
+ double_buffer(false),
+ parent(NULL),
+ parent_widget(NULL),
+ native_widget(NULL),
+ top_level(false),
+ create_texture_for_layer(true) {
+}
+
+Widget::InitParams::InitParams(Type type)
+ : type(type),
+ delegate(NULL),
+ child(type == TYPE_CONTROL),
+ transient(type == TYPE_BUBBLE || type == TYPE_POPUP || type == TYPE_MENU),
+ transparent(false),
+ accept_events(true),
+ can_activate(type != TYPE_POPUP && type != TYPE_MENU),
+ keep_on_top(type == TYPE_MENU),
+ ownership(NATIVE_WIDGET_OWNS_WIDGET),
+ mirror_origin_in_rtl(false),
+ has_dropshadow(false),
+ show_state(ui::SHOW_STATE_DEFAULT),
+ double_buffer(false),
+ parent(NULL),
+ parent_widget(NULL),
+ native_widget(NULL),
+ top_level(false),
+ create_texture_for_layer(true) {
+}
+
+gfx::NativeView Widget::InitParams::GetParent() const {
+ return parent_widget ? parent_widget->GetNativeView() : parent;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget, public:
+
+Widget::Widget()
+ : native_widget_(NULL),
+ widget_delegate_(NULL),
+ non_client_view_(NULL),
+ dragged_view_(NULL),
+ event_stack_(),
+ ownership_(InitParams::NATIVE_WIDGET_OWNS_WIDGET),
+ is_secondary_widget_(true),
+ frame_type_(FRAME_TYPE_DEFAULT),
+ disable_inactive_rendering_(false),
+ widget_closed_(false),
+ saved_show_state_(ui::SHOW_STATE_DEFAULT),
+ focus_on_creation_(true),
+ is_top_level_(false),
+ native_widget_initialized_(false),
+ is_mouse_button_pressed_(false),
+ last_mouse_event_was_move_(false) {
+}
+
+Widget::~Widget() {
+ while (!event_stack_.empty()) {
+ event_stack_.top()->reset();
+ event_stack_.pop();
+ }
+
+ DestroyRootView();
+ if (ownership_ == InitParams::WIDGET_OWNS_NATIVE_WIDGET)
+ delete native_widget_;
+}
+
+// static
+Widget* Widget::CreateWindow(WidgetDelegate* delegate) {
+ return CreateWindowWithParentAndBounds(delegate, NULL, gfx::Rect());
+}
+
+// static
+Widget* Widget::CreateWindowWithParent(WidgetDelegate* delegate,
+ gfx::NativeWindow parent) {
+ return CreateWindowWithParentAndBounds(delegate, parent, gfx::Rect());
+}
+
+// static
+Widget* Widget::CreateWindowWithBounds(WidgetDelegate* delegate,
+ const gfx::Rect& bounds) {
+ return CreateWindowWithParentAndBounds(delegate, NULL, bounds);
+}
+
+// static
+Widget* Widget::CreateWindowWithParentAndBounds(WidgetDelegate* delegate,
+ gfx::NativeWindow parent,
+ const gfx::Rect& bounds) {
+ Widget* widget = new Widget;
+ Widget::InitParams params;
+ params.delegate = delegate;
+#if defined(OS_WIN) || defined(USE_AURA)
+ params.parent = parent;
+#endif
+ params.bounds = bounds;
+ widget->Init(params);
+ return widget;
+}
+
+// static
+void Widget::SetPureViews(bool pure) {
+ use_pure_views = pure;
+}
+
+// static
+bool Widget::IsPureViews() {
+#if defined(USE_AURA) || defined(TOUCH_UI)
+ return true;
+#else
+ return use_pure_views;
+#endif
+}
+
+// static
+Widget* Widget::GetWidgetForNativeView(gfx::NativeView native_view) {
+ internal::NativeWidgetPrivate* native_widget =
+ internal::NativeWidgetPrivate::GetNativeWidgetForNativeView(native_view);
+ return native_widget ? native_widget->GetWidget() : NULL;
+}
+
+// static
+Widget* Widget::GetWidgetForNativeWindow(gfx::NativeWindow native_window) {
+ internal::NativeWidgetPrivate* native_widget =
+ internal::NativeWidgetPrivate::GetNativeWidgetForNativeWindow(
+ native_window);
+ return native_widget ? native_widget->GetWidget() : NULL;
+}
+
+// static
+Widget* Widget::GetTopLevelWidgetForNativeView(gfx::NativeView native_view) {
+ internal::NativeWidgetPrivate* native_widget =
+ internal::NativeWidgetPrivate::GetTopLevelNativeWidget(native_view);
+ return native_widget ? native_widget->GetWidget() : NULL;
+}
+
+
+// static
+void Widget::GetAllChildWidgets(gfx::NativeView native_view,
+ Widgets* children) {
+ internal::NativeWidgetPrivate::GetAllChildWidgets(native_view, children);
+}
+
+// static
+void Widget::ReparentNativeView(gfx::NativeView native_view,
+ gfx::NativeView new_parent) {
+ internal::NativeWidgetPrivate::ReparentNativeView(native_view, new_parent);
+}
+
+// static
+int Widget::GetLocalizedContentsWidth(int col_resource_id) {
+ return ui::GetLocalizedContentsWidthForFont(col_resource_id,
+ ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont));
+}
+
+// static
+int Widget::GetLocalizedContentsHeight(int row_resource_id) {
+ return ui::GetLocalizedContentsHeightForFont(row_resource_id,
+ ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont));
+}
+
+// static
+gfx::Size Widget::GetLocalizedContentsSize(int col_resource_id,
+ int row_resource_id) {
+ return gfx::Size(GetLocalizedContentsWidth(col_resource_id),
+ GetLocalizedContentsHeight(row_resource_id));
+}
+
+// static
+void Widget::SetDebugPaintEnabled(bool enabled) {
+ debug_paint = enabled;
+}
+
+// static
+bool Widget::IsDebugPaintEnabled() {
+ return debug_paint;
+}
+
+// static
+bool Widget::RequiresNonClientView(InitParams::Type type) {
+ return type == InitParams::TYPE_WINDOW || type == InitParams::TYPE_BUBBLE;
+}
+
+void Widget::Init(const InitParams& params) {
+ is_top_level_ = params.top_level ||
+ (!params.child &&
+ params.type != InitParams::TYPE_CONTROL &&
+ params.type != InitParams::TYPE_TOOLTIP);
+ widget_delegate_ = params.delegate ?
+ params.delegate : new DefaultWidgetDelegate(this, params);
+ ownership_ = params.ownership;
+ native_widget_ = params.native_widget ?
+ params.native_widget->AsNativeWidgetPrivate() :
+ internal::NativeWidgetPrivate::CreateNativeWidget(this);
+ GetRootView();
+ default_theme_provider_.reset(new DefaultThemeProvider);
+ if (params.type == InitParams::TYPE_MENU) {
+ is_mouse_button_pressed_ =
+ internal::NativeWidgetPrivate::IsMouseButtonDown();
+ }
+ native_widget_->InitNativeWidget(params);
+ if (RequiresNonClientView(params.type)) {
+ non_client_view_ = new NonClientView;
+ non_client_view_->SetFrameView(CreateNonClientFrameView());
+ // Create the ClientView, add it to the NonClientView and add the
+ // NonClientView to the RootView. This will cause everything to be parented.
+ non_client_view_->set_client_view(widget_delegate_->CreateClientView(this));
+ SetContentsView(non_client_view_);
+ SetInitialBounds(params.bounds);
+ if (params.show_state == ui::SHOW_STATE_MAXIMIZED)
+ Maximize();
+ else if (params.show_state == ui::SHOW_STATE_MINIMIZED)
+ Minimize();
+ UpdateWindowTitle();
+ }
+ native_widget_initialized_ = true;
+}
+
+// Unconverted methods (see header) --------------------------------------------
+
+gfx::NativeView Widget::GetNativeView() const {
+ return native_widget_->GetNativeView();
+}
+
+gfx::NativeWindow Widget::GetNativeWindow() const {
+ return native_widget_->GetNativeWindow();
+}
+
+void Widget::AddObserver(Widget::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void Widget::RemoveObserver(Widget::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool Widget::HasObserver(Widget::Observer* observer) {
+ return observers_.HasObserver(observer);
+}
+
+bool Widget::GetAccelerator(int cmd_id, ui::Accelerator* accelerator) {
+ return false;
+}
+
+void Widget::ViewHierarchyChanged(bool is_add, View* parent, View* child) {
+ if (!is_add) {
+ if (child == dragged_view_)
+ dragged_view_ = NULL;
+
+ FocusManager* focus_manager = GetFocusManager();
+ if (focus_manager)
+ focus_manager->ViewRemoved(child);
+ ViewStorage::GetInstance()->ViewRemoved(child);
+ native_widget_->ViewRemoved(child);
+ }
+}
+
+void Widget::NotifyNativeViewHierarchyChanged(bool attached,
+ gfx::NativeView native_view) {
+ if (!attached) {
+ FocusManager* focus_manager = GetFocusManager();
+ // We are being removed from a window hierarchy. Treat this as
+ // the root_view_ being removed.
+ if (focus_manager)
+ focus_manager->ViewRemoved(root_view_.get());
+ }
+ root_view_->NotifyNativeViewHierarchyChanged(attached, native_view);
+}
+
+// Converted methods (see header) ----------------------------------------------
+
+Widget* Widget::GetTopLevelWidget() {
+ return const_cast<Widget*>(
+ static_cast<const Widget*>(this)->GetTopLevelWidget());
+}
+
+const Widget* Widget::GetTopLevelWidget() const {
+ // GetTopLevelNativeWidget doesn't work during destruction because
+ // property is gone after gobject gets deleted. Short circuit here
+ // for toplevel so that InputMethod can remove itself from
+ // focus manager.
+ return is_top_level() ? this : native_widget_->GetTopLevelWidget();
+}
+
+void Widget::SetContentsView(View* view) {
+ root_view_->SetContentsView(view);
+ if (non_client_view_ != view)
+ non_client_view_ = NULL;
+}
+
+View* Widget::GetContentsView() {
+ return root_view_->GetContentsView();
+}
+
+gfx::Rect Widget::GetWindowScreenBounds() const {
+ return native_widget_->GetWindowScreenBounds();
+}
+
+gfx::Rect Widget::GetClientAreaScreenBounds() const {
+ return native_widget_->GetClientAreaScreenBounds();
+}
+
+gfx::Rect Widget::GetRestoredBounds() const {
+ return native_widget_->GetRestoredBounds();
+}
+
+void Widget::SetBounds(const gfx::Rect& bounds) {
+ native_widget_->SetBounds(bounds);
+}
+
+void Widget::SetSize(const gfx::Size& size) {
+ native_widget_->SetSize(size);
+}
+
+void Widget::SetBoundsConstrained(const gfx::Rect& bounds) {
+ gfx::Rect work_area =
+ gfx::Screen::GetMonitorWorkAreaNearestPoint(bounds.origin());
+ if (work_area.IsEmpty()) {
+ SetBounds(bounds);
+ } else {
+ // Inset the work area slightly.
+ work_area.Inset(10, 10, 10, 10);
+ SetBounds(work_area.AdjustToFit(bounds));
+ }
+}
+
+void Widget::MoveAboveWidget(Widget* widget) {
+ native_widget_->MoveAbove(widget->GetNativeView());
+}
+
+void Widget::MoveAbove(gfx::NativeView native_view) {
+ native_widget_->MoveAbove(native_view);
+}
+
+void Widget::MoveToTop() {
+ native_widget_->MoveToTop();
+}
+
+void Widget::SetShape(gfx::NativeRegion shape) {
+ native_widget_->SetShape(shape);
+}
+
+void Widget::Close() {
+ if (widget_closed_) {
+ // It appears we can hit this code path if you close a modal dialog then
+ // close the last browser before the destructor is hit, which triggers
+ // invoking Close again.
+ return;
+ }
+
+ bool can_close = true;
+ if (non_client_view_)
+ can_close = non_client_view_->CanClose();
+ if (can_close) {
+ SaveWindowPlacement();
+
+ // During tear-down the top-level focus manager becomes unavailable to
+ // GTK tabbed panes and their children, so normal deregistration via
+ // |FormManager::ViewRemoved()| calls are fouled. We clear focus here
+ // to avoid these redundant steps and to avoid accessing deleted views
+ // that may have been in focus.
+ if (is_top_level() && focus_manager_.get())
+ focus_manager_->SetFocusedView(NULL);
+
+ native_widget_->Close();
+ widget_closed_ = true;
+ }
+}
+
+void Widget::CloseNow() {
+ native_widget_->CloseNow();
+}
+
+void Widget::EnableClose(bool enable) {
+ if (non_client_view_)
+ non_client_view_->EnableClose(enable);
+ native_widget_->EnableClose(enable);
+}
+
+void Widget::Show() {
+ if (non_client_view_) {
+ if (saved_show_state_ == ui::SHOW_STATE_MAXIMIZED &&
+ !initial_restored_bounds_.IsEmpty()) {
+ native_widget_->ShowMaximizedWithBounds(initial_restored_bounds_);
+ } else {
+ native_widget_->ShowWithWindowState(saved_show_state_);
+ }
+ // |saved_show_state_| only applies the first time the window is shown.
+ // If we don't reset the value the window may be shown maximized every time
+ // it is subsequently shown after being hidden.
+ saved_show_state_ = ui::SHOW_STATE_NORMAL;
+ } else {
+ native_widget_->Show();
+ }
+}
+
+void Widget::Hide() {
+ native_widget_->Hide();
+}
+
+void Widget::ShowInactive() {
+ // If this gets called with saved_show_state_ == ui::SHOW_STATE_MAXIMIZED,
+ // call SetBounds()with the restored bounds to set the correct size. This
+ // normally should not happen, but if it does we should avoid showing unsized
+ // windows.
+ if (saved_show_state_ == ui::SHOW_STATE_MAXIMIZED &&
+ !initial_restored_bounds_.IsEmpty()) {
+ SetBounds(initial_restored_bounds_);
+ saved_show_state_ = ui::SHOW_STATE_NORMAL;
+ }
+ native_widget_->ShowWithWindowState(ui::SHOW_STATE_INACTIVE);
+}
+
+void Widget::Activate() {
+ native_widget_->Activate();
+}
+
+void Widget::Deactivate() {
+ native_widget_->Deactivate();
+}
+
+bool Widget::IsActive() const {
+ return native_widget_->IsActive();
+}
+
+void Widget::DisableInactiveRendering() {
+ SetInactiveRenderingDisabled(true);
+}
+
+void Widget::SetAlwaysOnTop(bool on_top) {
+ native_widget_->SetAlwaysOnTop(on_top);
+}
+
+void Widget::Maximize() {
+ native_widget_->Maximize();
+}
+
+void Widget::Minimize() {
+ native_widget_->Minimize();
+}
+
+void Widget::Restore() {
+ native_widget_->Restore();
+}
+
+bool Widget::IsMaximized() const {
+ return native_widget_->IsMaximized();
+}
+
+bool Widget::IsMinimized() const {
+ return native_widget_->IsMinimized();
+}
+
+void Widget::SetFullscreen(bool fullscreen) {
+ native_widget_->SetFullscreen(fullscreen);
+}
+
+bool Widget::IsFullscreen() const {
+ return native_widget_->IsFullscreen();
+}
+
+void Widget::SetOpacity(unsigned char opacity) {
+ native_widget_->SetOpacity(opacity);
+}
+
+void Widget::SetUseDragFrame(bool use_drag_frame) {
+ native_widget_->SetUseDragFrame(use_drag_frame);
+}
+
+View* Widget::GetRootView() {
+ if (!root_view_.get()) {
+ // First time the root view is being asked for, create it now.
+ root_view_.reset(CreateRootView());
+ }
+ return root_view_.get();
+}
+
+const View* Widget::GetRootView() const {
+ return root_view_.get();
+}
+
+bool Widget::IsVisible() const {
+ return native_widget_->IsVisible();
+}
+
+bool Widget::IsAccessibleWidget() const {
+ return native_widget_->IsAccessibleWidget();
+}
+
+ThemeProvider* Widget::GetThemeProvider() const {
+ const Widget* root_widget = GetTopLevelWidget();
+ if (root_widget && root_widget != this) {
+ // Attempt to get the theme provider, and fall back to the default theme
+ // provider if not found.
+ ThemeProvider* provider = root_widget->GetThemeProvider();
+ if (provider)
+ return provider;
+
+ provider = root_widget->default_theme_provider_.get();
+ if (provider)
+ return provider;
+ }
+ return default_theme_provider_.get();
+}
+
+FocusManager* Widget::GetFocusManager() {
+ Widget* toplevel_widget = GetTopLevelWidget();
+ return toplevel_widget ? toplevel_widget->focus_manager_.get() : NULL;
+}
+
+const FocusManager* Widget::GetFocusManager() const {
+ const Widget* toplevel_widget = GetTopLevelWidget();
+ return toplevel_widget ? toplevel_widget->focus_manager_.get() : NULL;
+}
+
+InputMethod* Widget::GetInputMethod() {
+ if (is_top_level()) {
+ if (!input_method_.get())
+ input_method_.reset(native_widget_->CreateInputMethod());
+ return input_method_.get();
+ } else {
+ Widget* toplevel = GetTopLevelWidget();
+ // If GetTopLevelWidget() returns itself which is not toplevel,
+ // the widget is detached from toplevel widget.
+ // TODO(oshima): Fix GetTopLevelWidget() to return NULL
+ // if there is no toplevel. We probably need to add GetTopMostWidget()
+ // to replace some use cases.
+ return (toplevel && toplevel != this) ? toplevel->GetInputMethod() : NULL;
+ }
+}
+
+void Widget::RunShellDrag(View* view, const ui::OSExchangeData& data,
+ int operation) {
+ dragged_view_ = view;
+ native_widget_->RunShellDrag(view, data, operation);
+ // If the view is removed during the drag operation, dragged_view_ is set to
+ // NULL.
+ if (view && dragged_view_ == view) {
+ dragged_view_ = NULL;
+ view->OnDragDone();
+ }
+}
+
+void Widget::SchedulePaintInRect(const gfx::Rect& rect) {
+ native_widget_->SchedulePaintInRect(rect);
+}
+
+void Widget::SetCursor(gfx::NativeCursor cursor) {
+ native_widget_->SetCursor(cursor);
+}
+
+void Widget::ResetLastMouseMoveFlag() {
+ last_mouse_event_was_move_ = false;
+}
+
+void Widget::SetNativeWindowProperty(const char* name, void* value) {
+ native_widget_->SetNativeWindowProperty(name, value);
+}
+
+void* Widget::GetNativeWindowProperty(const char* name) const {
+ return native_widget_->GetNativeWindowProperty(name);
+}
+
+void Widget::UpdateWindowTitle() {
+ if (!non_client_view_)
+ return;
+
+ // If the non-client view is rendering its own title, it'll need to relayout
+ // now.
+ non_client_view_->Layout();
+
+ // Update the native frame's text. We do this regardless of whether or not
+ // the native frame is being used, since this also updates the taskbar, etc.
+ string16 window_title;
+ if (native_widget_->IsScreenReaderActive()) {
+ window_title = widget_delegate_->GetAccessibleWindowTitle();
+ } else {
+ window_title = widget_delegate_->GetWindowTitle();
+ }
+ base::i18n::AdjustStringForLocaleDirection(&window_title);
+ native_widget_->SetWindowTitle(window_title);
+}
+
+void Widget::UpdateWindowIcon() {
+ if (non_client_view_)
+ non_client_view_->UpdateWindowIcon();
+ native_widget_->SetWindowIcons(widget_delegate_->GetWindowIcon(),
+ widget_delegate_->GetWindowAppIcon());
+}
+
+FocusTraversable* Widget::GetFocusTraversable() {
+ return static_cast<internal::RootView*>(root_view_.get());
+}
+
+void Widget::ThemeChanged() {
+ root_view_->ThemeChanged();
+}
+
+void Widget::LocaleChanged() {
+ root_view_->LocaleChanged();
+}
+
+void Widget::SetFocusTraversableParent(FocusTraversable* parent) {
+ root_view_->SetFocusTraversableParent(parent);
+}
+
+void Widget::SetFocusTraversableParentView(View* parent_view) {
+ root_view_->SetFocusTraversableParentView(parent_view);
+}
+
+void Widget::ClearNativeFocus() {
+ native_widget_->ClearNativeFocus();
+}
+
+void Widget::FocusNativeView(gfx::NativeView native_view) {
+ native_widget_->FocusNativeView(native_view);
+}
+
+void Widget::UpdateFrameAfterFrameChange() {
+ native_widget_->UpdateFrameAfterFrameChange();
+}
+
+NonClientFrameView* Widget::CreateNonClientFrameView() {
+ NonClientFrameView* frame_view = widget_delegate_->CreateNonClientFrameView();
+ if (!frame_view)
+ frame_view = native_widget_->CreateNonClientFrameView();
+ return frame_view ? frame_view : new CustomFrameView(this);
+}
+
+bool Widget::ShouldUseNativeFrame() const {
+ if (frame_type_ != FRAME_TYPE_DEFAULT)
+ return frame_type_ == FRAME_TYPE_FORCE_NATIVE;
+ return native_widget_->ShouldUseNativeFrame();
+}
+
+void Widget::DebugToggleFrameType() {
+ if (frame_type_ == FRAME_TYPE_DEFAULT) {
+ frame_type_ = ShouldUseNativeFrame() ? FRAME_TYPE_FORCE_CUSTOM :
+ FRAME_TYPE_FORCE_NATIVE;
+ } else {
+ frame_type_ = frame_type_ == FRAME_TYPE_FORCE_CUSTOM ?
+ FRAME_TYPE_FORCE_NATIVE : FRAME_TYPE_FORCE_CUSTOM;
+ }
+ FrameTypeChanged();
+}
+
+void Widget::FrameTypeChanged() {
+ native_widget_->FrameTypeChanged();
+}
+
+const ui::Compositor* Widget::GetCompositor() const {
+ return native_widget_->GetCompositor();
+}
+
+ui::Compositor* Widget::GetCompositor() {
+ return native_widget_->GetCompositor();
+}
+
+void Widget::CalculateOffsetToAncestorWithLayer(gfx::Point* offset,
+ ui::Layer** layer_parent) {
+ native_widget_->CalculateOffsetToAncestorWithLayer(offset, layer_parent);
+}
+
+void Widget::ReorderLayers() {
+ native_widget_->ReorderLayers();
+}
+
+void Widget::NotifyAccessibilityEvent(
+ View* view,
+ ui::AccessibilityTypes::Event event_type,
+ bool send_native_event) {
+ // Send the notification to the delegate.
+ if (ViewsDelegate::views_delegate)
+ ViewsDelegate::views_delegate->NotifyAccessibilityEvent(view, event_type);
+
+ if (send_native_event)
+ native_widget_->SendNativeAccessibilityEvent(view, event_type);
+}
+
+const NativeWidget* Widget::native_widget() const {
+ return native_widget_;
+}
+
+NativeWidget* Widget::native_widget() {
+ return native_widget_;
+}
+
+const Event* Widget::GetCurrentEvent() {
+ return event_stack_.empty() ? NULL : event_stack_.top()->event();
+}
+
+void Widget::TooltipTextChanged(View* view) {
+ TooltipManager* manager = native_widget_private()->GetTooltipManager();
+ if (manager)
+ manager->TooltipTextChanged(view);
+}
+
+bool Widget::SetInitialFocus() {
+ if (!focus_on_creation_)
+ return true;
+ View* v = widget_delegate_->GetInitiallyFocusedView();
+ if (v)
+ v->RequestFocus();
+ return !!v;
+}
+
+bool Widget::ConvertPointFromAncestor(
+ const Widget* ancestor, gfx::Point* point) const {
+ return native_widget_->ConvertPointFromAncestor(ancestor, point);
+}
+
+View* Widget::GetChildViewParent() {
+ return GetContentsView() ? GetContentsView() : GetRootView();
+}
+
+gfx::Rect Widget::GetWorkAreaBoundsInScreen() const {
+ return native_widget_->GetWorkAreaBoundsInScreen();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget, NativeWidgetDelegate implementation:
+
+bool Widget::IsModal() const {
+ return widget_delegate_->IsModal();
+}
+
+bool Widget::IsDialogBox() const {
+ return !!widget_delegate_->AsDialogDelegate();
+}
+
+bool Widget::CanActivate() const {
+ return widget_delegate_->CanActivate();
+}
+
+bool Widget::IsInactiveRenderingDisabled() const {
+ return disable_inactive_rendering_;
+}
+
+void Widget::EnableInactiveRendering() {
+ SetInactiveRenderingDisabled(false);
+}
+
+void Widget::OnNativeWidgetActivationChanged(bool active) {
+ if (!active) {
+ SaveWindowPlacement();
+
+ // Close any open menus.
+ MenuController* menu_controller = MenuController::GetActiveInstance();
+ if (menu_controller)
+ menu_controller->OnWidgetActivationChanged();
+ }
+
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnWidgetActivationChanged(this, active));
+}
+
+void Widget::OnNativeFocus(gfx::NativeView focused_view) {
+ WidgetFocusManager::GetInstance()->OnWidgetFocusEvent(focused_view,
+ GetNativeView());
+}
+
+void Widget::OnNativeBlur(gfx::NativeView focused_view) {
+ WidgetFocusManager::GetInstance()->OnWidgetFocusEvent(GetNativeView(),
+ focused_view);
+}
+
+void Widget::OnNativeWidgetVisibilityChanged(bool visible) {
+ View* root = GetRootView();
+ root->PropagateVisibilityNotifications(root, visible);
+ FOR_EACH_OBSERVER(Observer, observers_,
+ OnWidgetVisibilityChanged(this, visible));
+ if (GetCompositor() && root->layer())
+ root->layer()->SetVisible(visible);
+}
+
+void Widget::OnNativeWidgetCreated() {
+ if (is_top_level())
+ focus_manager_.reset(FocusManagerFactory::Create(this));
+
+ native_widget_->SetAccessibleRole(
+ widget_delegate_->GetAccessibleWindowRole());
+ native_widget_->SetAccessibleState(
+ widget_delegate_->GetAccessibleWindowState());
+
+ if (widget_delegate_->IsModal())
+ native_widget_->BecomeModal();
+}
+
+void Widget::OnNativeWidgetDestroying() {
+ FOR_EACH_OBSERVER(Observer, observers_, OnWidgetClosing(this));
+ if (non_client_view_)
+ non_client_view_->WindowClosing();
+ widget_delegate_->WindowClosing();
+}
+
+void Widget::OnNativeWidgetDestroyed() {
+ widget_delegate_->DeleteDelegate();
+ widget_delegate_ = NULL;
+}
+
+gfx::Size Widget::GetMinimumSize() {
+ return non_client_view_ ? non_client_view_->GetMinimumSize() : gfx::Size();
+}
+
+void Widget::OnNativeWidgetSizeChanged(const gfx::Size& new_size) {
+ root_view_->SetSize(new_size);
+
+ // Size changed notifications can fire prior to full initialization
+ // i.e. during session restore. Avoid saving session state during these
+ // startup procedures.
+ if (native_widget_initialized_)
+ SaveWindowPlacement();
+}
+
+void Widget::OnNativeWidgetBeginUserBoundsChange() {
+ widget_delegate_->OnWindowBeginUserBoundsChange();
+}
+
+void Widget::OnNativeWidgetEndUserBoundsChange() {
+ widget_delegate_->OnWindowEndUserBoundsChange();
+}
+
+bool Widget::HasFocusManager() const {
+ return !!focus_manager_.get();
+}
+
+bool Widget::OnNativeWidgetPaintAccelerated(const gfx::Rect& dirty_region) {
+ ui::Compositor* compositor = GetCompositor();
+ if (!compositor)
+ return false;
+
+ // If the root view is animating, it is likely that it does not cover the same
+ // set of pixels it did at the last frame, so we must clear when compositing
+ // to avoid leaving ghosts.
+ bool force_clear = false;
+ if (GetRootView()->layer()) {
+ const ui::Transform& layer_transform = GetRootView()->layer()->transform();
+ if (layer_transform != GetRootView()->GetTransform()) {
+ // The layer has not caught up to the view (i.e., the layer is still
+ // animating), and so a clear is required.
+ force_clear = true;
+ } else {
+ // Determine if the layer fills the client area.
+ gfx::Rect layer_bounds = GetRootView()->layer()->bounds();
+ layer_transform.TransformRect(&layer_bounds);
+ gfx::Rect client_bounds = GetClientAreaScreenBounds();
+ // Translate bounds to origin (client area bounds are offset to account
+ // for buttons, etc).
+ client_bounds.set_origin(gfx::Point(0, 0));
+ if (!layer_bounds.Contains(client_bounds)) {
+ // It doesn't, and so a clear is required.
+ force_clear = true;
+ }
+ }
+ }
+
+ compositor->Draw(force_clear);
+ return true;
+}
+
+void Widget::OnNativeWidgetPaint(gfx::Canvas* canvas) {
+ GetRootView()->Paint(canvas);
+}
+
+int Widget::GetNonClientComponent(const gfx::Point& point) {
+ return non_client_view_ ?
+ non_client_view_->NonClientHitTest(point) :
+ HTNOWHERE;
+}
+
+bool Widget::OnKeyEvent(const KeyEvent& event) {
+ ScopedEvent scoped(this, event);
+ return static_cast<internal::RootView*>(GetRootView())->OnKeyEvent(event);
+}
+
+bool Widget::OnMouseEvent(const MouseEvent& event) {
+ ScopedEvent scoped(this, event);
+ switch (event.type()) {
+ case ui::ET_MOUSE_PRESSED:
+ last_mouse_event_was_move_ = false;
+ // Make sure we're still visible before we attempt capture as the mouse
+ // press processing may have made the window hide (as happens with menus).
+ if (GetRootView()->OnMousePressed(event) && IsVisible()) {
+ is_mouse_button_pressed_ = true;
+ if (!native_widget_->HasMouseCapture())
+ native_widget_->SetMouseCapture();
+ return true;
+ }
+ return false;
+ case ui::ET_MOUSE_RELEASED:
+ last_mouse_event_was_move_ = false;
+ is_mouse_button_pressed_ = false;
+ // Release capture first, to avoid confusion if OnMouseReleased blocks.
+ if (native_widget_->HasMouseCapture() &&
+ ShouldReleaseCaptureOnMouseReleased()) {
+ native_widget_->ReleaseMouseCapture();
+ }
+ GetRootView()->OnMouseReleased(event);
+ return (event.flags() & ui::EF_IS_NON_CLIENT) ? false : true;
+ case ui::ET_MOUSE_MOVED:
+ case ui::ET_MOUSE_DRAGGED:
+ if (native_widget_->HasMouseCapture() && is_mouse_button_pressed_) {
+ last_mouse_event_was_move_ = false;
+ GetRootView()->OnMouseDragged(event);
+ } else if (!last_mouse_event_was_move_ ||
+ last_mouse_event_position_ != event.location()) {
+ last_mouse_event_position_ = event.location();
+ last_mouse_event_was_move_ = true;
+ GetRootView()->OnMouseMoved(event);
+ }
+ return false;
+ case ui::ET_MOUSE_EXITED:
+ last_mouse_event_was_move_ = false;
+ GetRootView()->OnMouseExited(event);
+ return false;
+ case ui::ET_MOUSEWHEEL:
+ return GetRootView()->OnMouseWheel(
+ reinterpret_cast<const MouseWheelEvent&>(event));
+ default:
+ return false;
+ }
+ return true;
+}
+
+void Widget::OnMouseCaptureLost() {
+ if (is_mouse_button_pressed_)
+ GetRootView()->OnMouseCaptureLost();
+ is_mouse_button_pressed_ = false;
+}
+
+ui::TouchStatus Widget::OnTouchEvent(const TouchEvent& event) {
+ ScopedEvent scoped(this, event);
+ return static_cast<internal::RootView*>(GetRootView())->OnTouchEvent(event);
+}
+
+bool Widget::ExecuteCommand(int command_id) {
+ return widget_delegate_->ExecuteWindowsCommand(command_id);
+}
+
+InputMethod* Widget::GetInputMethodDirect() {
+ return input_method_.get();
+}
+
+Widget* Widget::AsWidget() {
+ return this;
+}
+
+const Widget* Widget::AsWidget() const {
+ return this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget, FocusTraversable implementation:
+
+FocusSearch* Widget::GetFocusSearch() {
+ return root_view_->GetFocusSearch();
+}
+
+FocusTraversable* Widget::GetFocusTraversableParent() {
+ // We are a proxy to the root view, so we should be bypassed when traversing
+ // up and as a result this should not be called.
+ NOTREACHED();
+ return NULL;
+}
+
+View* Widget::GetFocusTraversableParentView() {
+ // We are a proxy to the root view, so we should be bypassed when traversing
+ // up and as a result this should not be called.
+ NOTREACHED();
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget, protected:
+
+internal::RootView* Widget::CreateRootView() {
+ return new internal::RootView(this);
+}
+
+void Widget::DestroyRootView() {
+ non_client_view_ = NULL;
+ root_view_.reset();
+ // Input method has to be destroyed before focus manager.
+ input_method_.reset();
+ // Defer focus manager's destruction. This is for the case when the
+ // focus manager is referenced by a child NativeWidgetGtk (e.g. TabbedPane in
+ // a dialog). When gtk_widget_destroy is called on the parent, the destroy
+ // signal reaches parent first and then the child. Thus causing the parent
+ // NativeWidgetGtk's dtor executed before the child's. If child's view
+ // hierarchy references this focus manager, it crashes. This will defer focus
+ // manager's destruction after child NativeWidgetGtk's dtor.
+ FocusManager* focus_manager = focus_manager_.release();
+ if (focus_manager)
+ MessageLoop::current()->DeleteSoon(FROM_HERE, focus_manager);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget, private:
+
+bool Widget::ShouldReleaseCaptureOnMouseReleased() const {
+ return true;
+}
+
+void Widget::SetInactiveRenderingDisabled(bool value) {
+ disable_inactive_rendering_ = value;
+ // We need to always notify the NonClientView so that it can trigger a paint.
+ // TODO: what's really needed is a way to know when either the active state
+ // changes or |disable_inactive_rendering_| changes.
+ if (non_client_view_)
+ non_client_view_->SetInactiveRenderingDisabled(value);
+ native_widget_->SetInactiveRenderingDisabled(value);
+}
+
+void Widget::SaveWindowPlacement() {
+ // The window delegate does the actual saving for us. It seems like (judging
+ // by go/crash) that in some circumstances we can end up here after
+ // WM_DESTROY, at which point the window delegate is likely gone. So just
+ // bail.
+ if (!widget_delegate_)
+ return;
+
+ ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL;
+ gfx::Rect bounds;
+ native_widget_->GetWindowPlacement(&bounds, &show_state);
+ widget_delegate_->SaveWindowPlacement(bounds, show_state);
+}
+
+void Widget::SetInitialBounds(const gfx::Rect& bounds) {
+ if (!non_client_view_)
+ return;
+
+ gfx::Rect saved_bounds;
+ if (GetSavedWindowPlacement(&saved_bounds, &saved_show_state_)) {
+ if (saved_show_state_ == ui::SHOW_STATE_MAXIMIZED) {
+ // If we're going to maximize, wait until Show is invoked to set the
+ // bounds. That way we avoid a noticable resize.
+ initial_restored_bounds_ = saved_bounds;
+ } else {
+ SetBounds(saved_bounds);
+ }
+ } else {
+ if (bounds.IsEmpty()) {
+ // No initial bounds supplied, so size the window to its content and
+ // center over its parent.
+ native_widget_->CenterWindow(non_client_view_->GetPreferredSize());
+ } else {
+ // Use the supplied initial bounds.
+ SetBoundsConstrained(bounds);
+ }
+ }
+}
+
+bool Widget::GetSavedWindowPlacement(gfx::Rect* bounds,
+ ui::WindowShowState* show_state) {
+ // First we obtain the window's saved show-style and store it. We need to do
+ // this here, rather than in Show() because by the time Show() is called,
+ // the window's size will have been reset (below) and the saved maximized
+ // state will have been lost. Sadly there's no way to tell on Windows when
+ // a window is restored from maximized state, so we can't more accurately
+ // track maximized state independently of sizing information.
+
+ // Restore the window's placement from the controller.
+ if (widget_delegate_->GetSavedWindowPlacement(bounds, show_state)) {
+ if (!widget_delegate_->ShouldRestoreWindowSize()) {
+ bounds->set_size(non_client_view_->GetPreferredSize());
+ } else {
+ gfx::Size minimum_size = GetMinimumSize();
+ // Make sure the bounds are at least the minimum size.
+ if (bounds->width() < minimum_size.width())
+ bounds->set_width(minimum_size.width());
+
+ if (bounds->height() < minimum_size.height())
+ bounds->set_height(minimum_size.height());
+ }
+ return true;
+ }
+ return false;
+}
+
+void Widget::ReplaceInputMethod(InputMethod* input_method) {
+ input_method_.reset(input_method);
+ // TODO(oshima): Gtk's textfield doesn't need views InputMethod.
+ // Remove this check once gtk is removed.
+ if (input_method) {
+ input_method->set_delegate(native_widget_);
+ input_method->Init(this);
+ }
+}
+
+namespace internal {
+
+////////////////////////////////////////////////////////////////////////////////
+// internal::NativeWidgetPrivate, NativeWidget implementation:
+
+internal::NativeWidgetPrivate* NativeWidgetPrivate::AsNativeWidgetPrivate() {
+ return this;
+}
+
+} // namespace internal
+} // namespace views
diff --git a/views/widget/widget.h b/views/widget/widget.h
new file mode 100644
index 0000000..4faf12b
--- /dev/null
+++ b/views/widget/widget.h
@@ -0,0 +1,746 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_WIDGET_H_
+#define VIEWS_WIDGET_WIDGET_H_
+#pragma once
+
+#include <set>
+#include <stack>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "ui/base/accessibility/accessibility_types.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/window/client_view.h"
+#include "ui/views/window/non_client_view.h"
+#include "views/widget/native_widget_delegate.h"
+
+#if defined(OS_WIN)
+// Windows headers define macros for these function names which screw with us.
+#if defined(IsMaximized)
+#undef IsMaximized
+#endif
+#if defined(IsMinimized)
+#undef IsMinimized
+#endif
+#if defined(CreateWindow)
+#undef CreateWindow
+#endif
+#endif
+
+namespace gfx {
+class Canvas;
+class Point;
+class Rect;
+}
+
+namespace ui {
+class Accelerator;
+class Compositor;
+class OSExchangeData;
+class ThemeProvider;
+enum TouchStatus;
+}
+using ui::ThemeProvider;
+
+namespace views {
+
+class DefaultThemeProvider;
+class InputMethod;
+class NativeWidget;
+class NonClientFrameView;
+class ScopedEvent;
+class View;
+class WidgetDelegate;
+namespace internal {
+class NativeWidgetPrivate;
+class RootView;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget class
+//
+// Encapsulates the platform-specific rendering, event receiving and widget
+// management aspects of the UI framework.
+//
+// Owns a RootView and thus a View hierarchy. Can contain child Widgets.
+// Widget is a platform-independent type that communicates with a platform or
+// context specific NativeWidget implementation.
+//
+// A special note on ownership:
+//
+// Depending on the value of the InitParams' ownership field, the Widget
+// either owns or is owned by its NativeWidget:
+//
+// ownership = NATIVE_WIDGET_OWNS_WIDGET (default)
+// The Widget instance is owned by its NativeWidget. When the NativeWidget
+// is destroyed (in response to a native destruction message), it deletes
+// the Widget from its destructor.
+// ownership = WIDGET_OWNS_NATIVE_WIDGET (non-default)
+// The Widget instance owns its NativeWidget. This state implies someone
+// else wants to control the lifetime of this object. When they destroy
+// the Widget it is responsible for destroying the NativeWidget (from its
+// destructor).
+//
+class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate,
+ public FocusTraversable {
+ public:
+ // Observers can listen to various events on the Widgets.
+ class VIEWS_EXPORT Observer {
+ public:
+ virtual void OnWidgetClosing(Widget* widget) {}
+ virtual void OnWidgetVisibilityChanged(Widget* widget, bool visible) {}
+ virtual void OnWidgetActivationChanged(Widget* widget, bool active) {}
+ };
+
+ typedef std::set<Widget*> Widgets;
+
+ enum FrameType {
+ FRAME_TYPE_DEFAULT, // Use whatever the default would be.
+ FRAME_TYPE_FORCE_CUSTOM, // Force the custom frame.
+ FRAME_TYPE_FORCE_NATIVE // Force the native frame.
+ };
+
+ struct VIEWS_EXPORT InitParams {
+ enum Type {
+ TYPE_WINDOW, // A decorated Window, like a frame window.
+ // Widgets of TYPE_WINDOW will have a NonClientView.
+ TYPE_WINDOW_FRAMELESS,
+ // An undecorated Window.
+ TYPE_CONTROL, // A control, like a button.
+ TYPE_POPUP, // An undecorated Window, with transient properties.
+ TYPE_MENU, // An undecorated Window, with transient properties
+ // specialized to menus.
+ TYPE_TOOLTIP,
+ TYPE_BUBBLE,
+ };
+ enum Ownership {
+ // Default. Creator is not responsible for managing the lifetime of the
+ // Widget, it is destroyed when the corresponding NativeWidget is
+ // destroyed.
+ NATIVE_WIDGET_OWNS_WIDGET,
+ // Used when the Widget is owned by someone other than the NativeWidget,
+ // e.g. a scoped_ptr in tests.
+ WIDGET_OWNS_NATIVE_WIDGET
+ };
+
+ InitParams();
+ explicit InitParams(Type type);
+
+ // If |parent_widget| is non-null, it's native view is returned, otherwise
+ // |parent| is returned.
+ gfx::NativeView GetParent() const;
+
+ Type type;
+ // If NULL, a default implementation will be constructed.
+ WidgetDelegate* delegate;
+ bool child;
+ bool transient;
+ // If true, the widget may be fully or partially transparent. If false,
+ // we can perform optimizations based on the widget being fully opaque.
+ // Defaults to false.
+ bool transparent;
+ bool accept_events;
+ bool can_activate;
+ bool keep_on_top;
+ Ownership ownership;
+ bool mirror_origin_in_rtl;
+ bool has_dropshadow;
+ // Whether the widget should be maximized or minimized.
+ ui::WindowShowState show_state;
+ // Should the widget be double buffered? Default is false.
+ bool double_buffer;
+ gfx::NativeView parent;
+ Widget* parent_widget;
+ // Specifies the initial bounds of the Widget. Default is empty, which means
+ // the NativeWidget may specify a default size.
+ gfx::Rect bounds;
+ // When set, this value is used as the Widget's NativeWidget implementation.
+ // The Widget will not construct a default one. Default is NULL.
+ NativeWidget* native_widget;
+ bool top_level;
+ // Only used by NativeWidgetAura. Specifies whether the Layer created by
+ // aura::Window has a texture. The default is true.
+ bool create_texture_for_layer;
+ };
+
+ Widget();
+ virtual ~Widget();
+
+ // Creates a decorated window Widget with the specified properties.
+ static Widget* CreateWindow(WidgetDelegate* delegate);
+ static Widget* CreateWindowWithParent(WidgetDelegate* delegate,
+ gfx::NativeWindow parent);
+ static Widget* CreateWindowWithBounds(WidgetDelegate* delegate,
+ const gfx::Rect& bounds);
+ static Widget* CreateWindowWithParentAndBounds(WidgetDelegate* delegate,
+ gfx::NativeWindow parent,
+ const gfx::Rect& bounds);
+
+ // Enumerates all windows pertaining to us and notifies their
+ // view hierarchies that the locale has changed.
+ static void NotifyLocaleChanged();
+
+ // Closes all Widgets that aren't identified as "secondary widgets". Called
+ // during application shutdown when the last non-secondary widget is closed.
+ static void CloseAllSecondaryWidgets();
+
+ // Converts a rectangle from one Widget's coordinate system to another's.
+ // Returns false if the conversion couldn't be made, because either these two
+ // Widgets do not have a common ancestor or they are not on the screen yet.
+ // The value of |*rect| won't be changed when false is returned.
+ static bool ConvertRect(const Widget* source,
+ const Widget* target,
+ gfx::Rect* rect);
+
+ // SetPureViews and IsPureViews update and return the state of a global
+ // setting that tracks whether to use available pure Views implementations.
+ static void SetPureViews(bool pure);
+ static bool IsPureViews();
+
+ // Retrieves the Widget implementation associated with the given
+ // NativeView or Window, or NULL if the supplied handle has no associated
+ // Widget.
+ static Widget* GetWidgetForNativeView(gfx::NativeView native_view);
+ static Widget* GetWidgetForNativeWindow(gfx::NativeWindow native_window);
+
+ // Retrieves the top level widget in a native view hierarchy
+ // starting at |native_view|. Top level widget is a widget with
+ // TYPE_WINDOW, TYPE_WINDOW_FRAMELESS, POPUP or MENU and has its own
+ // focus manager. This may be itself if the |native_view| is top level,
+ // or NULL if there is no toplevel in a native view hierarchy.
+ static Widget* GetTopLevelWidgetForNativeView(gfx::NativeView native_view);
+
+ // Returns all Widgets in |native_view|'s hierarchy, including itself if
+ // it is one.
+ static void GetAllChildWidgets(gfx::NativeView native_view,
+ Widgets* children);
+
+ // Re-parent a NativeView and notify all Widgets in |native_view|'s hierarchy
+ // of the change.
+ static void ReparentNativeView(gfx::NativeView native_view,
+ gfx::NativeView new_parent);
+
+ // Returns the preferred size of the contents view of this window based on
+ // its localized size data. The width in cols is held in a localized string
+ // resource identified by |col_resource_id|, the height in the same fashion.
+ // TODO(beng): This should eventually live somewhere else, probably closer to
+ // ClientView.
+ static int GetLocalizedContentsWidth(int col_resource_id);
+ static int GetLocalizedContentsHeight(int row_resource_id);
+ static gfx::Size GetLocalizedContentsSize(int col_resource_id,
+ int row_resource_id);
+
+ // Enable/Disable debug paint.
+ static void SetDebugPaintEnabled(bool enabled);
+ static bool IsDebugPaintEnabled();
+
+ // Returns true if the specified type requires a NonClientView.
+ static bool RequiresNonClientView(InitParams::Type type);
+
+ void Init(const InitParams& params);
+
+ // Returns the gfx::NativeView associated with this Widget.
+ gfx::NativeView GetNativeView() const;
+
+ // Returns the gfx::NativeWindow associated with this Widget. This may return
+ // NULL on some platforms if the widget was created with a type other than
+ // TYPE_WINDOW.
+ gfx::NativeWindow GetNativeWindow() const;
+
+ // Add/remove observer.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+ bool HasObserver(Observer* observer);
+
+ // Returns the accelerator given a command id. Returns false if there is
+ // no accelerator associated with a given id, which is a common condition.
+ virtual bool GetAccelerator(int cmd_id, ui::Accelerator* accelerator);
+
+ // Forwarded from the RootView so that the widget can do any cleanup.
+ void ViewHierarchyChanged(bool is_add, View* parent, View* child);
+
+ // Performs any necessary cleanup and forwards to RootView.
+ void NotifyNativeViewHierarchyChanged(bool attached,
+ gfx::NativeView native_view);
+
+ // Returns the top level widget in a hierarchy (see is_top_level() for
+ // the definition of top level widget.) Will return NULL if called
+ // before the widget is attached to the top level widget's hierarchy.
+ Widget* GetTopLevelWidget();
+ const Widget* GetTopLevelWidget() const;
+
+ // Gets/Sets the WidgetDelegate.
+ WidgetDelegate* widget_delegate() const { return widget_delegate_; }
+
+ // Sets the specified view as the contents of this Widget. There can only
+ // be one contents view child of this Widget's RootView. This view is sized to
+ // fit the entire size of the RootView. The RootView takes ownership of this
+ // View, unless it is set as not being parent-owned.
+ void SetContentsView(View* view);
+ View* GetContentsView();
+
+ // Returns the bounds of the Widget in screen coordinates.
+ gfx::Rect GetWindowScreenBounds() const;
+
+ // Returns the bounds of the Widget's client area in screen coordinates.
+ gfx::Rect GetClientAreaScreenBounds() const;
+
+ // Retrieves the restored bounds for the window.
+ gfx::Rect GetRestoredBounds() const;
+
+ // Sizes and/or places the widget to the specified bounds, size or position.
+ void SetBounds(const gfx::Rect& bounds);
+ void SetSize(const gfx::Size& size);
+
+ // Like SetBounds(), but ensures the Widget is fully visible on screen,
+ // resizing and/or repositioning as necessary. This is only useful for
+ // non-child widgets.
+ void SetBoundsConstrained(const gfx::Rect& bounds);
+
+ // Places the widget in front of the specified widget in z-order.
+ void MoveAboveWidget(Widget* widget);
+ void MoveAbove(gfx::NativeView native_view);
+ void MoveToTop();
+
+ // Sets a shape on the widget. This takes ownership of shape.
+ void SetShape(gfx::NativeRegion shape);
+
+ // Hides the widget then closes it after a return to the message loop.
+ virtual void Close();
+
+ // TODO(beng): Move off public API.
+ // Closes the widget immediately. Compare to |Close|. This will destroy the
+ // window handle associated with this Widget, so should not be called from
+ // any code that expects it to be valid beyond this call.
+ void CloseNow();
+
+ // Toggles the enable state for the Close button (and the Close menu item in
+ // the system menu).
+ void EnableClose(bool enable);
+
+ // Shows or hides the widget, without changing activation state.
+ virtual void Show();
+ void Hide();
+
+ // Like Show(), but does not activate the window.
+ void ShowInactive();
+
+ // Activates the widget, assuming it already exists and is visible.
+ void Activate();
+
+ // Deactivates the widget, making the next window in the Z order the active
+ // window.
+ void Deactivate();
+
+ // Returns whether the Widget is the currently active window.
+ virtual bool IsActive() const;
+
+ // Prevents the window from being rendered as deactivated. This state is
+ // reset automatically as soon as the window becomes activated again. There is
+ // no ability to control the state through this API as this leads to sync
+ // problems.
+ void DisableInactiveRendering();
+
+ // Sets the widget to be on top of all other widgets in the windowing system.
+ void SetAlwaysOnTop(bool on_top);
+
+ // Maximizes/minimizes/restores the window.
+ void Maximize();
+ void Minimize();
+ void Restore();
+
+ // Whether or not the window is maximized or minimized.
+ virtual bool IsMaximized() const;
+ bool IsMinimized() const;
+
+ // Accessors for fullscreen state.
+ void SetFullscreen(bool fullscreen);
+ bool IsFullscreen() const;
+
+ // Sets the opacity of the widget. This may allow widgets behind the widget
+ // in the Z-order to become visible, depending on the capabilities of the
+ // underlying windowing system. Note that the caller must then schedule a
+ // repaint to allow this change to take effect.
+ void SetOpacity(unsigned char opacity);
+
+ // Sets whether or not the window should show its frame as a "transient drag
+ // frame" - slightly transparent and without the standard window controls.
+ void SetUseDragFrame(bool use_drag_frame);
+
+ // Returns the View at the root of the View hierarchy contained by this
+ // Widget.
+ View* GetRootView();
+ const View* GetRootView() const;
+
+ // A secondary widget is one that is automatically closed (via Close()) when
+ // all non-secondary widgets are closed.
+ // Default is true.
+ // TODO(beng): This is an ugly API, should be handled implicitly via
+ // transience.
+ void set_is_secondary_widget(bool is_secondary_widget) {
+ is_secondary_widget_ = is_secondary_widget;
+ }
+ bool is_secondary_widget() const { return is_secondary_widget_; }
+
+ // Returns whether the Widget is visible to the user.
+ virtual bool IsVisible() const;
+
+ // Returns whether the Widget is customized for accessibility.
+ bool IsAccessibleWidget() const;
+
+ // Returns the ThemeProvider that provides theme resources for this Widget.
+ virtual ThemeProvider* GetThemeProvider() const;
+
+ // Returns the FocusManager for this widget.
+ // Note that all widgets in a widget hierarchy share the same focus manager.
+ // TODO(beng): remove virtual.
+ virtual FocusManager* GetFocusManager();
+ virtual const FocusManager* GetFocusManager() const;
+
+ // Returns the InputMethod for this widget.
+ // Note that all widgets in a widget hierarchy share the same input method.
+ InputMethod* GetInputMethod();
+
+ // Starts a drag operation for the specified view. This blocks until the drag
+ // operation completes. |view| can be NULL.
+ // If the view is non-NULL it can be accessed during the drag by calling
+ // dragged_view(). If the view has not been deleted during the drag,
+ // OnDragDone() is called on it.
+ void RunShellDrag(View* view, const ui::OSExchangeData& data, int operation);
+
+ // Returns the view that requested the current drag operation via
+ // RunShellDrag(), or NULL if there is no such view or drag operation.
+ View* dragged_view() { return dragged_view_; }
+
+ // Adds the specified |rect| in client area coordinates to the rectangle to be
+ // redrawn.
+ void SchedulePaintInRect(const gfx::Rect& rect);
+
+ // Sets the currently visible cursor. If |cursor| is NULL, the cursor used
+ // before the current is restored.
+ void SetCursor(gfx::NativeCursor cursor);
+
+ // Resets the last move flag so that we can go around the optimization
+ // that disregards duplicate mouse moves when ending animation requires
+ // a new hit-test to do some highlighting as in TabStrip::RemoveTabAnimation
+ // to cause the close button to highlight.
+ void ResetLastMouseMoveFlag();
+
+ // Sets/Gets a native window property on the underlying native window object.
+ // Returns NULL if the property does not exist. Setting the property value to
+ // NULL removes the property.
+ void SetNativeWindowProperty(const char* name, void* value);
+ void* GetNativeWindowProperty(const char* name) const;
+
+ // Tell the window to update its title from the delegate.
+ void UpdateWindowTitle();
+
+ // Tell the window to update its icon from the delegate.
+ void UpdateWindowIcon();
+
+ // Retrieves the focus traversable for this widget.
+ FocusTraversable* GetFocusTraversable();
+
+ // Notifies the view hierarchy contained in this widget that theme resources
+ // changed.
+ void ThemeChanged();
+
+ // Notifies the view hierarchy contained in this widget that locale resources
+ // changed.
+ void LocaleChanged();
+
+ void SetFocusTraversableParent(FocusTraversable* parent);
+ void SetFocusTraversableParentView(View* parent_view);
+
+ // Clear native focus set to the Widget's NativeWidget.
+ void ClearNativeFocus();
+
+ // Sets the focus to |native_view|.
+ void FocusNativeView(gfx::NativeView native_view);
+
+ // Updates the frame after an event caused it to be changed.
+ virtual void UpdateFrameAfterFrameChange();
+
+ void set_frame_type(FrameType frame_type) { frame_type_ = frame_type; }
+ FrameType frame_type() const { return frame_type_; }
+
+ // Creates an appropriate NonClientFrameView for this widget. The
+ // WidgetDelegate is given the first opportunity to create one, followed by
+ // the NativeWidget implementation. If both return NULL, a default one is
+ // created.
+ virtual NonClientFrameView* CreateNonClientFrameView();
+
+ // Whether we should be using a native frame.
+ bool ShouldUseNativeFrame() const;
+
+ // Forces the frame into the alternate frame type (custom or native) depending
+ // on its current state.
+ void DebugToggleFrameType();
+
+ // Tell the window that something caused the frame type to change.
+ void FrameTypeChanged();
+
+ NonClientView* non_client_view() {
+ return const_cast<NonClientView*>(
+ const_cast<const Widget*>(this)->non_client_view());
+ }
+ const NonClientView* non_client_view() const {
+ return non_client_view_;
+ }
+
+ ClientView* client_view() {
+ return const_cast<ClientView*>(
+ const_cast<const Widget*>(this)->client_view());
+ }
+ const ClientView* client_view() const {
+ // non_client_view_ may be NULL, especially during creation.
+ return non_client_view_ ? non_client_view_->client_view() : NULL;
+ }
+
+ const ui::Compositor* GetCompositor() const;
+ ui::Compositor* GetCompositor();
+
+ // Invokes method of same name on the NativeWidget.
+ void CalculateOffsetToAncestorWithLayer(gfx::Point* offset,
+ ui::Layer** layer_parent);
+
+ // Invokes method of same name on the NativeWidget.
+ void ReorderLayers();
+
+ // Notifies assistive technology that an accessibility event has
+ // occurred on |view|, such as when the view is focused or when its
+ // value changes. Pass true for |send_native_event| except for rare
+ // cases where the view is a native control that's already sending a
+ // native accessibility event and the duplicate event would cause
+ // problems.
+ void NotifyAccessibilityEvent(
+ View* view,
+ ui::AccessibilityTypes::Event event_type,
+ bool send_native_event);
+
+ const NativeWidget* native_widget() const;
+ NativeWidget* native_widget();
+
+ internal::NativeWidgetPrivate* native_widget_private() {
+ return native_widget_;
+ }
+ const internal::NativeWidgetPrivate* native_widget_private() const {
+ return native_widget_;
+ }
+
+ // Returns the current event being processed. If there are multiple events
+ // being processed at the same time (e.g. one event triggers another event),
+ // then the most recent event is returned. Returns NULL if no event is being
+ // processed.
+ const Event* GetCurrentEvent();
+
+ // Invoked when the tooltip text changes for the specified views.
+ void TooltipTextChanged(View* view);
+
+ // Sets-up the focus manager with the view that should have focus when the
+ // window is shown the first time. Returns true if the initial focus has been
+ // set or the widget should not set the initial focus, or false if the caller
+ // should set the initial focus (if any).
+ bool SetInitialFocus();
+
+ void set_focus_on_creation(bool focus_on_creation) {
+ focus_on_creation_ = focus_on_creation;
+ }
+
+ // Converts the |point| in ancestor's coordinate to this widget's coordinates.
+ // Returns false if |ancestor| is not an ancestor of this widget.
+ // The receiver has to be pure views widget (NativeWidgetViews) and
+ // ancestor can be of any type.
+ bool ConvertPointFromAncestor(
+ const Widget* ancestor, gfx::Point* point) const;
+
+ // Returns a View* that any child Widgets backed by NativeWidgetViews
+ // are added to. The default implementation returns the contents view
+ // if it exists and the root view otherwise.
+ virtual View* GetChildViewParent();
+
+ // True if the widget is considered top level widget. Top level widget
+ // is a widget of TYPE_WINDOW, TYPE_WINDOW_FRAMELESS, BUBBLE, POPUP or MENU,
+ // and has a focus manager and input method object associated with it.
+ // TYPE_CONTROL and TYPE_TOOLTIP is not considered top level.
+ bool is_top_level() const { return is_top_level_; }
+
+ // Returns the bounds of work area in the screen that Widget belongs to.
+ gfx::Rect GetWorkAreaBoundsInScreen() const;
+
+ // Overridden from NativeWidgetDelegate:
+ virtual bool IsModal() const OVERRIDE;
+ virtual bool IsDialogBox() const OVERRIDE;
+ virtual bool CanActivate() const OVERRIDE;
+ virtual bool IsInactiveRenderingDisabled() const OVERRIDE;
+ virtual void EnableInactiveRendering() OVERRIDE;
+ virtual void OnNativeWidgetActivationChanged(bool active) OVERRIDE;
+ virtual void OnNativeFocus(gfx::NativeView focused_view) OVERRIDE;
+ virtual void OnNativeBlur(gfx::NativeView focused_view) OVERRIDE;
+ virtual void OnNativeWidgetVisibilityChanged(bool visible) OVERRIDE;
+ virtual void OnNativeWidgetCreated() OVERRIDE;
+ virtual void OnNativeWidgetDestroying() OVERRIDE;
+ virtual void OnNativeWidgetDestroyed() OVERRIDE;
+ virtual gfx::Size GetMinimumSize() OVERRIDE;
+ virtual void OnNativeWidgetSizeChanged(const gfx::Size& new_size) OVERRIDE;
+ virtual void OnNativeWidgetBeginUserBoundsChange() OVERRIDE;
+ virtual void OnNativeWidgetEndUserBoundsChange() OVERRIDE;
+ virtual bool HasFocusManager() const OVERRIDE;
+ virtual bool OnNativeWidgetPaintAccelerated(
+ const gfx::Rect& dirty_region) OVERRIDE;
+ virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual int GetNonClientComponent(const gfx::Point& point) OVERRIDE;
+ virtual bool OnKeyEvent(const KeyEvent& event) OVERRIDE;
+ virtual bool OnMouseEvent(const MouseEvent& event) OVERRIDE;
+ virtual void OnMouseCaptureLost() OVERRIDE;
+ virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE;
+ virtual bool ExecuteCommand(int command_id) OVERRIDE;
+ virtual InputMethod* GetInputMethodDirect() OVERRIDE;
+ virtual Widget* AsWidget() OVERRIDE;
+ virtual const Widget* AsWidget() const OVERRIDE;
+
+ // Overridden from FocusTraversable:
+ virtual FocusSearch* GetFocusSearch() OVERRIDE;
+ virtual FocusTraversable* GetFocusTraversableParent() OVERRIDE;
+ virtual View* GetFocusTraversableParentView() OVERRIDE;
+
+ protected:
+ // Creates the RootView to be used within this Widget. Subclasses may override
+ // to create custom RootViews that do specialized event processing.
+ // TODO(beng): Investigate whether or not this is needed.
+ virtual internal::RootView* CreateRootView();
+
+ // Provided to allow the NativeWidget implementations to destroy the RootView
+ // _before_ the focus manager/tooltip manager.
+ // TODO(beng): remove once we fold those objects onto this one.
+ void DestroyRootView();
+
+ private:
+ // TODO(beng): Remove NativeWidgetGtk's dependence on the mouse state flags.
+ friend class NativeWidgetGtk;
+
+ friend class NativeTextfieldViewsTest;
+ friend class NativeComboboxViewsTest;
+ friend class ScopedEvent;
+
+ // Returns whether capture should be released on mouse release.
+ virtual bool ShouldReleaseCaptureOnMouseReleased() const;
+
+ // Sets the value of |disable_inactive_rendering_|. If the value changes,
+ // both the NonClientView and WidgetDelegate are notified.
+ void SetInactiveRenderingDisabled(bool value);
+
+ // Persists the window's restored position and "show" state using the
+ // window delegate.
+ void SaveWindowPlacement();
+
+ // Sizes and positions the window just after it is created.
+ void SetInitialBounds(const gfx::Rect& bounds);
+
+ // Returns the bounds and "show" state from the delegate. Returns true if
+ // the delegate wants to use a specified bounds.
+ bool GetSavedWindowPlacement(gfx::Rect* bounds,
+ ui::WindowShowState* show_state);
+
+ // Sets a different InputMethod instance to this widget. The instance
+ // must not be initialized, the ownership will be assumed by the widget.
+ // It's only for testing purpose.
+ void ReplaceInputMethod(InputMethod* input_method);
+
+ internal::NativeWidgetPrivate* native_widget_;
+
+ ObserverList<Observer> observers_;
+
+ // Non-owned pointer to the Widget's delegate. May be NULL if no delegate is
+ // being used.
+ WidgetDelegate* widget_delegate_;
+
+ // The root of the View hierarchy attached to this window.
+ // WARNING: see warning in tooltip_manager_ for ordering dependencies with
+ // this and tooltip_manager_.
+ scoped_ptr<internal::RootView> root_view_;
+
+ // The View that provides the non-client area of the window (title bar,
+ // window controls, sizing borders etc). To use an implementation other than
+ // the default, this class must be sub-classed and this value set to the
+ // desired implementation before calling |InitWindow()|.
+ NonClientView* non_client_view_;
+
+ // The focus manager keeping track of focus for this Widget and any of its
+ // children. NULL for non top-level widgets.
+ // WARNING: RootView's destructor calls into the FocusManager. As such, this
+ // must be destroyed AFTER root_view_. This is enforced in DestroyRootView().
+ scoped_ptr<FocusManager> focus_manager_;
+
+ // A theme provider to use when no other theme provider is specified.
+ scoped_ptr<DefaultThemeProvider> default_theme_provider_;
+
+ // Valid for the lifetime of RunShellDrag(), indicates the view the drag
+ // started from.
+ View* dragged_view_;
+
+ // The event stack.
+ std::stack<ScopedEvent*> event_stack_;
+
+ // See class documentation for Widget above for a note about ownership.
+ InitParams::Ownership ownership_;
+
+ // See set_is_secondary_widget().
+ bool is_secondary_widget_;
+
+ // The current frame type in use by this window. Defaults to
+ // FRAME_TYPE_DEFAULT.
+ FrameType frame_type_;
+
+ // True when the window should be rendered as active, regardless of whether
+ // or not it actually is.
+ bool disable_inactive_rendering_;
+
+ // Set to true if the widget is in the process of closing.
+ bool widget_closed_;
+
+ // The saved "show" state for this window. See note in SetInitialBounds
+ // that explains why we save this.
+ ui::WindowShowState saved_show_state_;
+
+ // The restored bounds used for the initial show. This is only used if
+ // |saved_show_state_| is maximized.
+ gfx::Rect initial_restored_bounds_;
+
+ // Focus is automatically set to the view provided by the delegate
+ // when the widget is shown. Set this value to false to override
+ // initial focus for the widget.
+ bool focus_on_creation_;
+
+ scoped_ptr<InputMethod> input_method_;
+
+ // See |is_top_level()| accessor.
+ bool is_top_level_;
+
+ // Tracks whether native widget has been initialized.
+ bool native_widget_initialized_;
+
+ // TODO(beng): Remove NativeWidgetGtk's dependence on these:
+ // If true, the mouse is currently down.
+ bool is_mouse_button_pressed_;
+
+ // TODO(beng): Remove NativeWidgetGtk's dependence on these:
+ // The following are used to detect duplicate mouse move events and not
+ // deliver them. Displaying a window may result in the system generating
+ // duplicate move events even though the mouse hasn't moved.
+ bool last_mouse_event_was_move_;
+ gfx::Point last_mouse_event_position_;
+
+ DISALLOW_COPY_AND_ASSIGN(Widget);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_WIDGET_H_
diff --git a/views/widget/widget_delegate.cc b/views/widget/widget_delegate.cc
new file mode 100644
index 0000000..c64f8e6
--- /dev/null
+++ b/views/widget/widget_delegate.cc
@@ -0,0 +1,166 @@
+// Copyright (c) 2011 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/widget_delegate.h"
+
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/views/bubble/bubble_delegate.h"
+#include "ui/views/window/client_view.h"
+#include "views/view.h"
+#include "views/views_delegate.h"
+#include "views/widget/widget.h"
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+// WidgetDelegate:
+
+WidgetDelegate::WidgetDelegate() : default_contents_view_(NULL) {
+}
+
+void WidgetDelegate::OnWidgetMove() {
+}
+
+void WidgetDelegate::OnDisplayChanged() {
+}
+
+void WidgetDelegate::OnWorkAreaChanged() {
+}
+
+View* WidgetDelegate::GetInitiallyFocusedView() {
+ return NULL;
+}
+
+BubbleDelegateView* WidgetDelegate::AsBubbleDelegate() {
+ return NULL;
+}
+
+DialogDelegate* WidgetDelegate::AsDialogDelegate() {
+ return NULL;
+}
+
+bool WidgetDelegate::CanResize() const {
+ return false;
+}
+
+bool WidgetDelegate::CanMaximize() const {
+ return false;
+}
+
+bool WidgetDelegate::CanActivate() const {
+ return true;
+}
+
+bool WidgetDelegate::IsModal() const {
+ return false;
+}
+
+ui::AccessibilityTypes::Role WidgetDelegate::GetAccessibleWindowRole() const {
+ return ui::AccessibilityTypes::ROLE_WINDOW;
+}
+
+ui::AccessibilityTypes::State WidgetDelegate::GetAccessibleWindowState() const {
+ return 0;
+}
+
+string16 WidgetDelegate::GetAccessibleWindowTitle() const {
+ return GetWindowTitle();
+}
+
+string16 WidgetDelegate::GetWindowTitle() const {
+ return string16();
+}
+
+bool WidgetDelegate::ShouldShowWindowTitle() const {
+ return true;
+}
+
+bool WidgetDelegate::ShouldShowClientEdge() const {
+ return true;
+}
+
+SkBitmap WidgetDelegate::GetWindowAppIcon() {
+ // Use the window icon as app icon by default.
+ return GetWindowIcon();
+}
+
+// Returns the icon to be displayed in the window.
+SkBitmap WidgetDelegate::GetWindowIcon() {
+ return SkBitmap();
+}
+
+bool WidgetDelegate::ShouldShowWindowIcon() const {
+ return false;
+}
+
+bool WidgetDelegate::ExecuteWindowsCommand(int command_id) {
+ return false;
+}
+
+std::string WidgetDelegate::GetWindowName() const {
+ return std::string();
+}
+
+void WidgetDelegate::SaveWindowPlacement(const gfx::Rect& bounds,
+ ui::WindowShowState show_state) {
+ std::string window_name = GetWindowName();
+ if (!ViewsDelegate::views_delegate || window_name.empty())
+ return;
+
+ ViewsDelegate::views_delegate->SaveWindowPlacement(
+ GetWidget(), window_name, bounds, show_state);
+}
+
+bool WidgetDelegate::GetSavedWindowPlacement(
+ gfx::Rect* bounds,
+ ui::WindowShowState* show_state) const {
+ std::string window_name = GetWindowName();
+ if (!ViewsDelegate::views_delegate || window_name.empty())
+ return false;
+
+ return ViewsDelegate::views_delegate->GetSavedWindowPlacement(
+ window_name, bounds, show_state);
+}
+
+bool WidgetDelegate::ShouldRestoreWindowSize() const {
+ return true;
+}
+
+View* WidgetDelegate::GetContentsView() {
+ if (!default_contents_view_)
+ default_contents_view_ = new View;
+ return default_contents_view_;
+}
+
+ClientView* WidgetDelegate::CreateClientView(Widget* widget) {
+ return new ClientView(widget, GetContentsView());
+}
+
+NonClientFrameView* WidgetDelegate::CreateNonClientFrameView() {
+ return NULL;
+}
+
+bool WidgetDelegate::WillProcessWorkAreaChange() const {
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WidgetDelegateView:
+
+WidgetDelegateView::WidgetDelegateView() {
+}
+
+WidgetDelegateView::~WidgetDelegateView() {
+}
+
+Widget* WidgetDelegateView::GetWidget() {
+ return View::GetWidget();
+}
+
+const Widget* WidgetDelegateView::GetWidget() const {
+ return View::GetWidget();
+}
+
+} // namespace views
diff --git a/views/widget/widget_delegate.h b/views/widget/widget_delegate.h
new file mode 100644
index 0000000..854c70b
--- /dev/null
+++ b/views/widget/widget_delegate.h
@@ -0,0 +1,181 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_WIDGET_DELEGATE_H_
+#define VIEWS_WIDGET_WIDGET_DELEGATE_H_
+#pragma once
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/base/accessibility/accessibility_types.h"
+#include "ui/base/ui_base_types.h"
+#include "views/view.h"
+
+class SkBitmap;
+
+namespace gfx {
+class Rect;
+}
+
+namespace views {
+class BubbleDelegateView;
+class ClientView;
+class DialogDelegate;
+class NonClientFrameView;
+class View;
+class Widget;
+
+// WidgetDelegate interface
+// Handles events on Widgets in context-specific ways.
+class VIEWS_EXPORT WidgetDelegate {
+ public:
+ WidgetDelegate();
+
+ // Called whenever the widget's position changes.
+ virtual void OnWidgetMove();
+
+ // Called with the display changes (color depth or resolution).
+ virtual void OnDisplayChanged();
+
+ // Called when the work area (the desktop area minus task bars,
+ // menu bars, etc.) changes in size.
+ virtual void OnWorkAreaChanged();
+
+ // Returns the view that should have the focus when the widget is shown. If
+ // NULL no view is focused.
+ virtual View* GetInitiallyFocusedView();
+
+ // Moved from WindowDelegate: ------------------------------------------------
+ // TODO(beng): sort
+
+ virtual BubbleDelegateView* AsBubbleDelegate();
+ virtual DialogDelegate* AsDialogDelegate();
+
+ // Returns true if the window can ever be resized.
+ virtual bool CanResize() const;
+
+ // Returns true if the window can ever be maximized.
+ virtual bool CanMaximize() const;
+
+ // Returns true if the window can be activated.
+ virtual bool CanActivate() const;
+
+ // Returns true if the dialog should be displayed modally to the window that
+ // opened it. Only windows with WindowType == DIALOG can be modal.
+ virtual bool IsModal() const;
+
+ virtual ui::AccessibilityTypes::Role GetAccessibleWindowRole() const;
+
+ virtual ui::AccessibilityTypes::State GetAccessibleWindowState() const;
+
+ // Returns the title to be read with screen readers.
+ virtual string16 GetAccessibleWindowTitle() const;
+
+ // Returns the text to be displayed in the window title.
+ virtual string16 GetWindowTitle() const;
+
+ // Returns true if the window should show a title in the title bar.
+ virtual bool ShouldShowWindowTitle() const;
+
+ // Returns true if the window's client view wants a client edge.
+ virtual bool ShouldShowClientEdge() const;
+
+ // Returns the app icon for the window. On Windows, this is the ICON_BIG used
+ // in Alt-Tab list and Win7's taskbar.
+ virtual SkBitmap GetWindowAppIcon();
+
+ // Returns the icon to be displayed in the window.
+ virtual SkBitmap GetWindowIcon();
+
+ // Returns true if a window icon should be shown.
+ virtual bool ShouldShowWindowIcon() const;
+
+ // Execute a command in the window's controller. Returns true if the command
+ // was handled, false if it was not.
+ virtual bool ExecuteWindowsCommand(int command_id);
+
+ // Returns the window's name identifier. Used to identify this window for
+ // state restoration.
+ virtual std::string GetWindowName() const;
+
+ // Saves the window's bounds and "show" state. By default this uses the
+ // process' local state keyed by window name (See GetWindowName above). This
+ // behavior can be overridden to provide additional functionality.
+ virtual void SaveWindowPlacement(const gfx::Rect& bounds,
+ ui::WindowShowState show_state);
+
+ // Retrieves the window's bounds and "show" states.
+ // This behavior can be overridden to provide additional functionality.
+ virtual bool GetSavedWindowPlacement(gfx::Rect* bounds,
+ ui::WindowShowState* show_state) const;
+
+ // Returns true if the window's size should be restored. If this is false,
+ // only the window's origin is restored and the window is given its
+ // preferred size.
+ // Default is true.
+ virtual bool ShouldRestoreWindowSize() const;
+
+ // Called when the window closes. The delegate MUST NOT delete itself during
+ // this call, since it can be called afterwards. See DeleteDelegate().
+ virtual void WindowClosing() {}
+
+ // Called when the window is destroyed. No events must be sent or received
+ // after this point. The delegate can use this opportunity to delete itself at
+ // this time if necessary.
+ virtual void DeleteDelegate() {}
+
+ // Called when the user begins/ends to change the bounds of the window.
+ virtual void OnWindowBeginUserBoundsChange() {}
+ virtual void OnWindowEndUserBoundsChange() {}
+
+ // Returns the Widget associated with this delegate.
+ virtual Widget* GetWidget() = 0;
+ virtual const Widget* GetWidget() const = 0;
+
+ // Returns the View that is contained within this Widget.
+ virtual View* GetContentsView();
+
+ // Called by the Widget to create the Client View used to host the contents
+ // of the widget.
+ virtual ClientView* CreateClientView(Widget* widget);
+
+ // Called by the Widget to create the NonClient Frame View for this widget.
+ // Return NULL to use the default one.
+ virtual NonClientFrameView* CreateNonClientFrameView();
+
+ // Returns true if the window can be notified with the work area change.
+ // Otherwise, the work area change for the top window will be processed by
+ // the default window manager. In some cases, like panel, we would like to
+ // manage the positions by ourselves.
+ virtual bool WillProcessWorkAreaChange() const;
+
+ protected:
+ virtual ~WidgetDelegate() {}
+
+ private:
+ View* default_contents_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(WidgetDelegate);
+};
+
+// A WidgetDelegate implementation that is-a View. Used to override GetWidget()
+// to call View's GetWidget() for the common case where a WidgetDelegate
+// implementation is-a View.
+class VIEWS_EXPORT WidgetDelegateView : public WidgetDelegate, public View {
+ public:
+ WidgetDelegateView();
+ virtual ~WidgetDelegateView();
+
+ // Overridden from WidgetDelegate:
+ virtual Widget* GetWidget() OVERRIDE;
+ virtual const Widget* GetWidget() const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WidgetDelegateView);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_WIDGET_DELEGATE_H_
diff --git a/views/widget/widget_unittest.cc b/views/widget/widget_unittest.cc
new file mode 100644
index 0000000..580bed8
--- /dev/null
+++ b/views/widget/widget_unittest.cc
@@ -0,0 +1,829 @@
+// Copyright (c) 2011 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 "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/test/test_views_delegate.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/native_widget_types.h"
+#include "views/views_delegate.h"
+#include "views/widget/native_widget_delegate.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/window.h"
+#include "views/widget/native_widget_aura.h"
+#elif defined(OS_WIN)
+#include "views/widget/native_widget_win.h"
+#elif defined(TOOLKIT_USES_GTK)
+#include "views/widget/native_widget_gtk.h"
+#endif
+
+namespace views {
+namespace {
+
+// A generic typedef to pick up relevant NativeWidget implementations.
+#if defined(USE_AURA)
+typedef NativeWidgetAura NativeWidgetPlatform;
+#elif defined(OS_WIN)
+typedef NativeWidgetWin NativeWidgetPlatform;
+#elif defined(TOOLKIT_USES_GTK)
+typedef NativeWidgetGtk NativeWidgetPlatform;
+#endif
+
+// A widget that assumes mouse capture always works. It won't on Gtk/Aura in
+// testing, so we mock it.
+#if defined(TOOLKIT_USES_GTK) || defined(USE_AURA)
+class NativeWidgetCapture : public NativeWidgetPlatform {
+ public:
+ NativeWidgetCapture(internal::NativeWidgetDelegate* delegate)
+ : NativeWidgetPlatform(delegate),
+ mouse_capture_(false) {}
+ virtual ~NativeWidgetCapture() {}
+
+ virtual void SetMouseCapture() OVERRIDE {
+ mouse_capture_ = true;
+ }
+ virtual void ReleaseMouseCapture() OVERRIDE {
+ if (mouse_capture_)
+ delegate()->OnMouseCaptureLost();
+ mouse_capture_ = false;
+ }
+ virtual bool HasMouseCapture() const OVERRIDE {
+ return mouse_capture_;
+ }
+
+ private:
+ bool mouse_capture_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeWidgetCapture);
+};
+#endif
+
+// A typedef that inserts our mock-capture NativeWidget implementation for
+// relevant platforms.
+#if defined(USE_AURA)
+typedef NativeWidgetCapture NativeWidgetPlatformForTest;
+#elif defined(OS_WIN)
+typedef NativeWidgetWin NativeWidgetPlatformForTest;
+#elif defined(TOOLKIT_USES_GTK)
+typedef NativeWidgetCapture NativeWidgetPlatformForTest;
+#endif
+
+// A view that always processes all mouse events.
+class MouseView : public View {
+ public:
+ MouseView() : View() {
+ }
+ virtual ~MouseView() {}
+
+ virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE {
+ return true;
+ }
+};
+
+typedef ViewsTestBase WidgetTest;
+
+NativeWidget* CreatePlatformNativeWidget(
+ internal::NativeWidgetDelegate* delegate) {
+ return new NativeWidgetPlatformForTest(delegate);
+}
+
+Widget* CreateTopLevelPlatformWidget() {
+ Widget* toplevel = new Widget;
+ Widget::InitParams toplevel_params(Widget::InitParams::TYPE_WINDOW);
+ toplevel_params.native_widget = CreatePlatformNativeWidget(toplevel);
+ toplevel->Init(toplevel_params);
+ return toplevel;
+}
+
+Widget* CreateChildPlatformWidget(gfx::NativeView parent_native_view) {
+ Widget* child = new Widget;
+ Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL);
+ child_params.native_widget = CreatePlatformNativeWidget(child);
+ child_params.parent = parent_native_view;
+ child->Init(child_params);
+ child->SetContentsView(new View);
+ return child;
+}
+
+#if defined(OS_WIN) && !defined(USE_AURA)
+// On Windows, it is possible for us to have a child window that is TYPE_POPUP.
+Widget* CreateChildPopupPlatformWidget(gfx::NativeView parent_native_view) {
+ Widget* child = new Widget;
+ Widget::InitParams child_params(Widget::InitParams::TYPE_POPUP);
+ child_params.child = true;
+ child_params.native_widget = CreatePlatformNativeWidget(child);
+ child_params.parent = parent_native_view;
+ child->Init(child_params);
+ child->SetContentsView(new View);
+ return child;
+}
+#endif
+
+Widget* CreateTopLevelNativeWidget() {
+ Widget* toplevel = new Widget;
+ Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
+ toplevel->Init(params);
+ toplevel->SetContentsView(new View);
+ return toplevel;
+}
+
+Widget* CreateChildNativeWidgetWithParent(Widget* parent) {
+ Widget* child = new Widget;
+ Widget::InitParams params(Widget::InitParams::TYPE_CONTROL);
+ params.parent_widget = parent;
+ child->Init(params);
+ child->SetContentsView(new View);
+ return child;
+}
+
+Widget* CreateChildNativeWidget() {
+ return CreateChildNativeWidgetWithParent(NULL);
+}
+
+bool WidgetHasMouseCapture(const Widget* widget) {
+ return static_cast<const internal::NativeWidgetPrivate*>(widget->
+ native_widget())->HasMouseCapture();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget::GetTopLevelWidget tests.
+
+TEST_F(WidgetTest, GetTopLevelWidget_Native) {
+ // Create a hierarchy of native widgets.
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+#if defined(TOOLKIT_USES_GTK)
+ NativeWidgetGtk* native_widget =
+ static_cast<NativeWidgetGtk*>(toplevel->native_widget());
+ gfx::NativeView parent = native_widget->window_contents();
+#else
+ gfx::NativeView parent = toplevel->GetNativeView();
+#endif
+ Widget* child = CreateChildPlatformWidget(parent);
+
+ EXPECT_EQ(toplevel, toplevel->GetTopLevelWidget());
+ EXPECT_EQ(toplevel, child->GetTopLevelWidget());
+
+ toplevel->CloseNow();
+ // |child| should be automatically destroyed with |toplevel|.
+}
+
+TEST_F(WidgetTest, GetTopLevelWidget_Synthetic) {
+ // Create a hierarchy consisting of a top level platform native widget and a
+ // child NativeWidget.
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+ Widget* child = CreateTopLevelNativeWidget();
+
+ EXPECT_EQ(toplevel, toplevel->GetTopLevelWidget());
+ EXPECT_EQ(child, child->GetTopLevelWidget());
+
+ toplevel->CloseNow();
+ // |child| should be automatically destroyed with |toplevel|.
+}
+
+// Creates a hierarchy consisting of a desktop platform native widget, a
+// toplevel NativeWidget, and a child of that toplevel, another NativeWidget.
+TEST_F(WidgetTest, GetTopLevelWidget_SyntheticDesktop) {
+ // Create a hierarchy consisting of a desktop platform native widget,
+ // a toplevel NativeWidget and a chlid NativeWidget.
+ Widget* desktop = CreateTopLevelPlatformWidget();
+ Widget* toplevel = CreateTopLevelNativeWidget(); // Will be parented
+ // automatically to
+ // |toplevel|.
+
+ Widget* child = CreateChildNativeWidgetWithParent(toplevel);
+
+ EXPECT_EQ(desktop, desktop->GetTopLevelWidget());
+ EXPECT_EQ(toplevel, toplevel->GetTopLevelWidget());
+ EXPECT_EQ(toplevel, child->GetTopLevelWidget());
+
+ desktop->CloseNow();
+ // |toplevel|, |child| should be automatically destroyed with |toplevel|.
+}
+
+// Tests some grab/ungrab events.
+TEST_F(WidgetTest, DISABLED_GrabUngrab) {
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+ Widget* child1 = CreateChildNativeWidgetWithParent(toplevel);
+ Widget* child2 = CreateChildNativeWidgetWithParent(toplevel);
+
+ toplevel->SetBounds(gfx::Rect(0, 0, 500, 500));
+
+ child1->SetBounds(gfx::Rect(10, 10, 300, 300));
+ View* view = new MouseView();
+ view->SetBounds(0, 0, 300, 300);
+ child1->GetRootView()->AddChildView(view);
+
+ child2->SetBounds(gfx::Rect(200, 10, 200, 200));
+ view = new MouseView();
+ view->SetBounds(0, 0, 200, 200);
+ child2->GetRootView()->AddChildView(view);
+
+ toplevel->Show();
+ RunPendingMessages();
+
+ // Click on child1
+ MouseEvent pressed(ui::ET_MOUSE_PRESSED, 45, 45, ui::EF_LEFT_BUTTON_DOWN);
+ toplevel->OnMouseEvent(pressed);
+
+ EXPECT_TRUE(WidgetHasMouseCapture(toplevel));
+ EXPECT_TRUE(WidgetHasMouseCapture(child1));
+ EXPECT_FALSE(WidgetHasMouseCapture(child2));
+
+ MouseEvent released(ui::ET_MOUSE_RELEASED, 45, 45, ui::EF_LEFT_BUTTON_DOWN);
+ toplevel->OnMouseEvent(released);
+
+ EXPECT_FALSE(WidgetHasMouseCapture(toplevel));
+ EXPECT_FALSE(WidgetHasMouseCapture(child1));
+ EXPECT_FALSE(WidgetHasMouseCapture(child2));
+
+ RunPendingMessages();
+
+ // Click on child2
+ MouseEvent pressed2(ui::ET_MOUSE_PRESSED, 315, 45, ui::EF_LEFT_BUTTON_DOWN);
+ EXPECT_TRUE(toplevel->OnMouseEvent(pressed2));
+ EXPECT_TRUE(WidgetHasMouseCapture(toplevel));
+ EXPECT_TRUE(WidgetHasMouseCapture(child2));
+ EXPECT_FALSE(WidgetHasMouseCapture(child1));
+
+ MouseEvent released2(ui::ET_MOUSE_RELEASED, 315, 45, ui::EF_LEFT_BUTTON_DOWN);
+ toplevel->OnMouseEvent(released2);
+ EXPECT_FALSE(WidgetHasMouseCapture(toplevel));
+ EXPECT_FALSE(WidgetHasMouseCapture(child1));
+ EXPECT_FALSE(WidgetHasMouseCapture(child2));
+
+ toplevel->CloseNow();
+}
+
+// Test if a focus manager and an inputmethod work without CHECK failure
+// when window activation changes.
+TEST_F(WidgetTest, ChangeActivation) {
+ Widget* top1 = CreateTopLevelPlatformWidget();
+ // CreateInputMethod before activated
+ top1->GetInputMethod();
+ top1->Show();
+ RunPendingMessages();
+
+ Widget* top2 = CreateTopLevelPlatformWidget();
+ top2->Show();
+ RunPendingMessages();
+
+ top1->Activate();
+ RunPendingMessages();
+
+ // Create InputMethod after deactivated.
+ top2->GetInputMethod();
+ top2->Activate();
+ RunPendingMessages();
+
+ top1->Activate();
+ RunPendingMessages();
+
+ top1->CloseNow();
+ top2->CloseNow();
+}
+
+// Tests visibility of child widgets.
+TEST_F(WidgetTest, Visibility) {
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+#if defined(TOOLKIT_USES_GTK)
+ NativeWidgetGtk* native_widget =
+ static_cast<NativeWidgetGtk*>(toplevel->native_widget());
+ gfx::NativeView parent = native_widget->window_contents();
+#else
+ gfx::NativeView parent = toplevel->GetNativeView();
+#endif
+ Widget* child = CreateChildPlatformWidget(parent);
+
+ EXPECT_FALSE(toplevel->IsVisible());
+ EXPECT_FALSE(child->IsVisible());
+
+ child->Show();
+
+ EXPECT_FALSE(toplevel->IsVisible());
+ EXPECT_FALSE(child->IsVisible());
+
+ toplevel->Show();
+
+ EXPECT_TRUE(toplevel->IsVisible());
+ EXPECT_TRUE(child->IsVisible());
+
+ toplevel->CloseNow();
+ // |child| should be automatically destroyed with |toplevel|.
+}
+
+#if defined(OS_WIN) && !defined(USE_AURA)
+// On Windows, it is possible to have child window that are TYPE_POPUP. Unlike
+// regular child windows, these should be created as hidden and must be shown
+// explicitly.
+TEST_F(WidgetTest, Visibility_ChildPopup) {
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+ Widget* child_popup = CreateChildPopupPlatformWidget(
+ toplevel->GetNativeView());
+
+ EXPECT_FALSE(toplevel->IsVisible());
+ EXPECT_FALSE(child_popup->IsVisible());
+
+ toplevel->Show();
+
+ EXPECT_TRUE(toplevel->IsVisible());
+ EXPECT_FALSE(child_popup->IsVisible());
+
+ child_popup->Show();
+
+ EXPECT_TRUE(child_popup->IsVisible());
+
+ toplevel->CloseNow();
+ // |child_popup| should be automatically destroyed with |toplevel|.
+}
+#endif
+
+// Tests visibility of synthetic child widgets.
+TEST_F(WidgetTest, Visibility_Synthetic) {
+ // Create a hierarchy consisting of a desktop platform native widget,
+ // a toplevel NativeWidget and a chlid NativeWidget.
+ Widget* desktop = CreateTopLevelPlatformWidget();
+ desktop->Show();
+
+ Widget* toplevel = CreateTopLevelNativeWidget(); // Will be parented
+ // automatically to
+ // |toplevel|.
+
+ Widget* child = CreateChildNativeWidgetWithParent(toplevel);
+
+ EXPECT_FALSE(toplevel->IsVisible());
+ EXPECT_FALSE(child->IsVisible());
+
+ child->Show();
+
+ EXPECT_FALSE(toplevel->IsVisible());
+ EXPECT_FALSE(child->IsVisible());
+
+ toplevel->Show();
+
+ EXPECT_TRUE(toplevel->IsVisible());
+ EXPECT_TRUE(child->IsVisible());
+
+ desktop->CloseNow();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget ownership tests.
+//
+// Tests various permutations of Widget ownership specified in the
+// InitParams::Ownership param.
+
+// A WidgetTest that supplies a toplevel widget for NativeWidget to parent to.
+class WidgetOwnershipTest : public WidgetTest {
+ public:
+ WidgetOwnershipTest() {}
+ virtual ~WidgetOwnershipTest() {}
+
+ virtual void SetUp() {
+ WidgetTest::SetUp();
+ desktop_widget_ = CreateTopLevelPlatformWidget();
+ }
+
+ virtual void TearDown() {
+ desktop_widget_->CloseNow();
+ WidgetTest::TearDown();
+ }
+
+ private:
+ Widget* desktop_widget_;
+
+ DISALLOW_COPY_AND_ASSIGN(WidgetOwnershipTest);
+};
+
+// A bag of state to monitor destructions.
+struct OwnershipTestState {
+ OwnershipTestState() : widget_deleted(false), native_widget_deleted(false) {}
+
+ bool widget_deleted;
+ bool native_widget_deleted;
+};
+
+// A platform NativeWidget subclass that updates a bag of state when it is
+// destroyed.
+class OwnershipTestNativeWidget : public NativeWidgetPlatform {
+ public:
+ OwnershipTestNativeWidget(internal::NativeWidgetDelegate* delegate,
+ OwnershipTestState* state)
+ : NativeWidgetPlatform(delegate),
+ state_(state) {
+ }
+ virtual ~OwnershipTestNativeWidget() {
+ state_->native_widget_deleted = true;
+ }
+
+ private:
+ OwnershipTestState* state_;
+
+ DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidget);
+};
+
+// A views NativeWidget subclass that updates a bag of state when it is
+// destroyed.
+class OwnershipTestNativeWidgetPlatform : public NativeWidgetPlatformForTest {
+ public:
+ OwnershipTestNativeWidgetPlatform(internal::NativeWidgetDelegate* delegate,
+ OwnershipTestState* state)
+ : NativeWidgetPlatformForTest(delegate),
+ state_(state) {
+ }
+ virtual ~OwnershipTestNativeWidgetPlatform() {
+ state_->native_widget_deleted = true;
+ }
+
+ private:
+ OwnershipTestState* state_;
+
+ DISALLOW_COPY_AND_ASSIGN(OwnershipTestNativeWidgetPlatform);
+};
+
+// A Widget subclass that updates a bag of state when it is destroyed.
+class OwnershipTestWidget : public Widget {
+ public:
+ OwnershipTestWidget(OwnershipTestState* state) : state_(state) {}
+ virtual ~OwnershipTestWidget() {
+ state_->widget_deleted = true;
+ }
+
+ private:
+ OwnershipTestState* state_;
+
+ DISALLOW_COPY_AND_ASSIGN(OwnershipTestWidget);
+};
+
+// Widget owns its NativeWidget, part 1: NativeWidget is a platform-native
+// widget.
+TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsPlatformNativeWidget) {
+ OwnershipTestState state;
+
+ scoped_ptr<Widget> widget(new OwnershipTestWidget(&state));
+ Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
+ params.native_widget =
+ new OwnershipTestNativeWidgetPlatform(widget.get(), &state);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ widget->Init(params);
+
+ // Now delete the Widget, which should delete the NativeWidget.
+ widget.reset();
+
+ EXPECT_TRUE(state.widget_deleted);
+ EXPECT_TRUE(state.native_widget_deleted);
+
+ // TODO(beng): write test for this ownership scenario and the NativeWidget
+ // being deleted out from under the Widget.
+}
+
+// Widget owns its NativeWidget, part 2: NativeWidget is a NativeWidget.
+TEST_F(WidgetOwnershipTest, Ownership_WidgetOwnsViewsNativeWidget) {
+ OwnershipTestState state;
+
+ scoped_ptr<Widget> widget(new OwnershipTestWidget(&state));
+ Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
+ params.native_widget =
+ new OwnershipTestNativeWidgetPlatform(widget.get(), &state);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ widget->Init(params);
+
+ // Now delete the Widget, which should delete the NativeWidget.
+ widget.reset();
+
+ EXPECT_TRUE(state.widget_deleted);
+ EXPECT_TRUE(state.native_widget_deleted);
+
+ // TODO(beng): write test for this ownership scenario and the NativeWidget
+ // being deleted out from under the Widget.
+}
+
+// Widget owns its NativeWidget, part 3: NativeWidget is a NativeWidget,
+// destroy the parent view.
+TEST_F(WidgetOwnershipTest,
+ Ownership_WidgetOwnsViewsNativeWidget_DestroyParentView) {
+ OwnershipTestState state;
+
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+
+ scoped_ptr<Widget> widget(new OwnershipTestWidget(&state));
+ Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
+ params.native_widget =
+ new OwnershipTestNativeWidgetPlatform(widget.get(), &state);
+ params.parent_widget = toplevel;
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ widget->Init(params);
+
+ // Now close the toplevel, which deletes the view hierarchy.
+ toplevel->CloseNow();
+
+ RunPendingMessages();
+
+ // This shouldn't delete the widget because it shouldn't be deleted
+ // from the native side.
+ EXPECT_FALSE(state.widget_deleted);
+ EXPECT_FALSE(state.native_widget_deleted);
+
+ // Now delete it explicitly.
+ widget.reset();
+
+ EXPECT_TRUE(state.widget_deleted);
+ EXPECT_TRUE(state.native_widget_deleted);
+}
+
+// NativeWidget owns its Widget, part 1: NativeWidget is a platform-native
+// widget.
+TEST_F(WidgetOwnershipTest, Ownership_PlatformNativeWidgetOwnsWidget) {
+ OwnershipTestState state;
+
+ Widget* widget = new OwnershipTestWidget(&state);
+ Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
+ params.native_widget =
+ new OwnershipTestNativeWidgetPlatform(widget, &state);
+ widget->Init(params);
+
+ // Now destroy the native widget.
+ widget->CloseNow();
+
+ EXPECT_TRUE(state.widget_deleted);
+ EXPECT_TRUE(state.native_widget_deleted);
+}
+
+// NativeWidget owns its Widget, part 2: NativeWidget is a NativeWidget.
+#if defined(OS_CHROMEOS) && defined(TOOLKIT_USES_GTK)
+// Temporarily disable the test (http://crbug.com/104945).
+TEST_F(WidgetOwnershipTest, DISABLED_Ownership_ViewsNativeWidgetOwnsWidget) {
+#else
+TEST_F(WidgetOwnershipTest, Ownership_ViewsNativeWidgetOwnsWidget) {
+#endif
+ OwnershipTestState state;
+
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+
+ Widget* widget = new OwnershipTestWidget(&state);
+ Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
+ params.native_widget =
+ new OwnershipTestNativeWidgetPlatform(widget, &state);
+ params.parent_widget = toplevel;
+ widget->Init(params);
+
+ // Now destroy the native widget. This is achieved by closing the toplevel.
+ toplevel->CloseNow();
+
+ // The NativeWidget won't be deleted until after a return to the message loop
+ // so we have to run pending messages before testing the destruction status.
+ RunPendingMessages();
+
+ EXPECT_TRUE(state.widget_deleted);
+ EXPECT_TRUE(state.native_widget_deleted);
+}
+
+// NativeWidget owns its Widget, part 3: NativeWidget is a platform-native
+// widget, destroyed out from under it by the OS.
+TEST_F(WidgetOwnershipTest,
+ Ownership_PlatformNativeWidgetOwnsWidget_NativeDestroy) {
+ OwnershipTestState state;
+
+ Widget* widget = new OwnershipTestWidget(&state);
+ Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
+ params.native_widget =
+ new OwnershipTestNativeWidgetPlatform(widget, &state);
+ widget->Init(params);
+
+ // Now simulate a destroy of the platform native widget from the OS:
+#if defined(USE_AURA)
+ delete widget->GetNativeView();
+#elif defined(OS_WIN)
+ DestroyWindow(widget->GetNativeView());
+#elif defined(TOOLKIT_USES_GTK)
+ gtk_widget_destroy(widget->GetNativeView());
+#endif
+
+ EXPECT_TRUE(state.widget_deleted);
+ EXPECT_TRUE(state.native_widget_deleted);
+}
+
+// NativeWidget owns its Widget, part 4: NativeWidget is a NativeWidget,
+// destroyed by the view hierarchy that contains it.
+#if defined(OS_CHROMEOS) && defined(TOOLKIT_USES_GTK)
+// Temporarily disable the test (http://crbug.com/104945).
+TEST_F(WidgetOwnershipTest,
+ DISABLED_Ownership_ViewsNativeWidgetOwnsWidget_NativeDestroy) {
+#else
+TEST_F(WidgetOwnershipTest,
+ Ownership_ViewsNativeWidgetOwnsWidget_NativeDestroy) {
+#endif
+ OwnershipTestState state;
+
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+
+ Widget* widget = new OwnershipTestWidget(&state);
+ Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
+ params.native_widget =
+ new OwnershipTestNativeWidgetPlatform(widget, &state);
+ params.parent_widget = toplevel;
+ widget->Init(params);
+
+ // Destroy the widget (achieved by closing the toplevel).
+ toplevel->CloseNow();
+
+ // The NativeWidget won't be deleted until after a return to the message loop
+ // so we have to run pending messages before testing the destruction status.
+ RunPendingMessages();
+
+ EXPECT_TRUE(state.widget_deleted);
+ EXPECT_TRUE(state.native_widget_deleted);
+}
+
+// NativeWidget owns its Widget, part 5: NativeWidget is a NativeWidget,
+// we close it directly.
+TEST_F(WidgetOwnershipTest,
+ Ownership_ViewsNativeWidgetOwnsWidget_Close) {
+ OwnershipTestState state;
+
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+
+ Widget* widget = new OwnershipTestWidget(&state);
+ Widget::InitParams params(Widget::InitParams::TYPE_POPUP);
+ params.native_widget =
+ new OwnershipTestNativeWidgetPlatform(widget, &state);
+ params.parent_widget = toplevel;
+ widget->Init(params);
+
+ // Destroy the widget.
+ widget->Close();
+ toplevel->CloseNow();
+
+ // The NativeWidget won't be deleted until after a return to the message loop
+ // so we have to run pending messages before testing the destruction status.
+ RunPendingMessages();
+
+ EXPECT_TRUE(state.widget_deleted);
+ EXPECT_TRUE(state.native_widget_deleted);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Widget observer tests.
+//
+
+class WidgetObserverTest : public WidgetTest,
+ Widget::Observer {
+ public:
+ WidgetObserverTest()
+ : active_(NULL),
+ widget_closed_(NULL),
+ widget_activated_(NULL),
+ widget_shown_(NULL),
+ widget_hidden_(NULL) {
+ }
+
+ virtual ~WidgetObserverTest() {}
+
+ virtual void OnWidgetClosing(Widget* widget) OVERRIDE {
+ if (active_ == widget)
+ active_ = NULL;
+ widget_closed_ = widget;
+ }
+
+ virtual void OnWidgetActivationChanged(Widget* widget,
+ bool active) OVERRIDE {
+ if (active) {
+ if (widget_activated_)
+ widget_activated_->Deactivate();
+ widget_activated_ = widget;
+ active_ = widget;
+ } else {
+ if (widget_activated_ == widget)
+ widget_activated_ = NULL;
+ widget_deactivated_ = widget;
+ }
+ }
+
+ virtual void OnWidgetVisibilityChanged(Widget* widget,
+ bool visible) OVERRIDE {
+ if (visible)
+ widget_shown_ = widget;
+ else
+ widget_hidden_ = widget;
+ }
+
+ void reset() {
+ active_ = NULL;
+ widget_closed_ = NULL;
+ widget_activated_ = NULL;
+ widget_deactivated_ = NULL;
+ widget_shown_ = NULL;
+ widget_hidden_ = NULL;
+ }
+
+ Widget* NewWidget() {
+ Widget* widget = CreateTopLevelNativeWidget();
+ widget->AddObserver(this);
+ return widget;
+ }
+
+ const Widget* active() const { return active_; }
+ const Widget* widget_closed() const { return widget_closed_; }
+ const Widget* widget_activated() const { return widget_activated_; }
+ const Widget* widget_deactivated() const { return widget_deactivated_; }
+ const Widget* widget_shown() const { return widget_shown_; }
+ const Widget* widget_hidden() const { return widget_hidden_; }
+
+ private:
+
+ Widget* active_;
+
+ Widget* widget_closed_;
+ Widget* widget_activated_;
+ Widget* widget_deactivated_;
+ Widget* widget_shown_;
+ Widget* widget_hidden_;
+};
+
+TEST_F(WidgetObserverTest, DISABLED_ActivationChange) {
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+
+ Widget* toplevel1 = NewWidget();
+ Widget* toplevel2 = NewWidget();
+
+ toplevel1->Show();
+ toplevel2->Show();
+
+ reset();
+
+ toplevel1->Activate();
+
+ RunPendingMessages();
+ EXPECT_EQ(toplevel1, widget_activated());
+
+ toplevel2->Activate();
+ RunPendingMessages();
+ EXPECT_EQ(toplevel1, widget_deactivated());
+ EXPECT_EQ(toplevel2, widget_activated());
+ EXPECT_EQ(toplevel2, active());
+
+ toplevel->CloseNow();
+}
+
+TEST_F(WidgetObserverTest, DISABLED_VisibilityChange) {
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+
+ Widget* child1 = NewWidget();
+ Widget* child2 = NewWidget();
+
+ toplevel->Show();
+ child1->Show();
+ child2->Show();
+
+ reset();
+
+ child1->Hide();
+ EXPECT_EQ(child1, widget_hidden());
+
+ child2->Hide();
+ EXPECT_EQ(child2, widget_hidden());
+
+ child1->Show();
+ EXPECT_EQ(child1, widget_shown());
+
+ child2->Show();
+ EXPECT_EQ(child2, widget_shown());
+
+ toplevel->CloseNow();
+}
+
+#if !defined(USE_AURA) && defined(OS_WIN)
+// Aura needs shell to maximize/fullscreen window.
+// NativeWidgetGtk doesn't implement GetRestoredBounds.
+TEST_F(WidgetTest, GetRestoredBounds) {
+ Widget* toplevel = CreateTopLevelPlatformWidget();
+ EXPECT_EQ(toplevel->GetWindowScreenBounds().ToString(),
+ toplevel->GetRestoredBounds().ToString());
+ toplevel->Show();
+ toplevel->Maximize();
+ RunPendingMessages();
+ EXPECT_NE(toplevel->GetWindowScreenBounds().ToString(),
+ toplevel->GetRestoredBounds().ToString());
+ EXPECT_GT(toplevel->GetRestoredBounds().width(), 0);
+ EXPECT_GT(toplevel->GetRestoredBounds().height(), 0);
+
+ toplevel->Restore();
+ RunPendingMessages();
+ EXPECT_EQ(toplevel->GetWindowScreenBounds().ToString(),
+ toplevel->GetRestoredBounds().ToString());
+
+ toplevel->SetFullscreen(true);
+ RunPendingMessages();
+ EXPECT_NE(toplevel->GetWindowScreenBounds().ToString(),
+ toplevel->GetRestoredBounds().ToString());
+ EXPECT_GT(toplevel->GetRestoredBounds().width(), 0);
+ EXPECT_GT(toplevel->GetRestoredBounds().height(), 0);
+}
+#endif
+
+} // namespace
+} // namespace views
diff --git a/views/widget/window_manager.cc b/views/widget/window_manager.cc
new file mode 100644
index 0000000..7e5e7ca
--- /dev/null
+++ b/views/widget/window_manager.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2011 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/window_manager.h"
+
+#include "base/compiler_specific.h"
+#include "ui/views/events/event.h"
+#include "views/widget/widget.h"
+
+namespace {
+
+views::WindowManager* window_manager = NULL;
+
+class NullWindowManager : public views::WindowManager {
+ public:
+ NullWindowManager() : mouse_capture_(NULL) {
+ }
+
+ virtual void StartMoveDrag(views::Widget* widget,
+ const gfx::Point& screen_point) OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ virtual void StartResizeDrag(views::Widget* widget,
+ const gfx::Point& screen_point,
+ int hittest_code) OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ virtual bool SetMouseCapture(views::Widget* widget) OVERRIDE {
+ if (mouse_capture_ == widget)
+ return true;
+ if (mouse_capture_)
+ return false;
+ mouse_capture_ = widget;
+ return true;
+ }
+
+ virtual bool ReleaseMouseCapture(views::Widget* widget) OVERRIDE {
+ if (widget && mouse_capture_ != widget)
+ return false;
+ mouse_capture_ = NULL;
+ return true;
+ }
+
+ virtual bool HasMouseCapture(const views::Widget* widget) const OVERRIDE {
+ return mouse_capture_ == widget;
+ }
+
+ virtual bool HandleKeyEvent(views::Widget* widget,
+ const views::KeyEvent& event) OVERRIDE {
+ return false;
+ }
+
+ virtual bool HandleMouseEvent(views::Widget* widget,
+ const views::MouseEvent& event) OVERRIDE {
+ if (mouse_capture_) {
+ views::MouseEvent translated(event, widget->GetRootView(),
+ mouse_capture_->GetRootView());
+ mouse_capture_->OnMouseEvent(translated);
+ return true;
+ }
+ return false;
+ }
+
+ virtual ui::TouchStatus HandleTouchEvent(views::Widget* widget,
+ const views::TouchEvent& event) OVERRIDE {
+ return ui::TOUCH_STATUS_UNKNOWN;
+ }
+
+ void Register(views::Widget* widget) OVERRIDE {}
+
+ private:
+ views::Widget* mouse_capture_;
+};
+
+} // namespace
+
+namespace views {
+
+WindowManager::WindowManager() {
+}
+
+WindowManager::~WindowManager() {
+}
+
+// static
+void WindowManager::Install(WindowManager* wm) {
+ window_manager = wm;
+}
+
+// static
+WindowManager* WindowManager::Get() {
+ if (!window_manager)
+ window_manager = new NullWindowManager();
+ return window_manager;
+}
+
+} // namespace views
diff --git a/views/widget/window_manager.h b/views/widget/window_manager.h
new file mode 100644
index 0000000..f634bf0
--- /dev/null
+++ b/views/widget/window_manager.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2011 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.
+
+#ifndef VIEWS_WIDGET_WINDOW_MANAGER_H_
+#define VIEWS_WIDGET_WINDOW_MANAGER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "ui/base/events.h"
+#include "views/views_export.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace views {
+class KeyEvent;
+class MouseEvent;
+class TouchEvent;
+class Widget;
+
+// A interface to WindowManager.
+class VIEWS_EXPORT WindowManager {
+ public:
+ WindowManager();
+ virtual ~WindowManager();
+
+ // Starts moving window given by |widget|. |point| represents the
+ // initial location of the mouse pointer.
+ virtual void StartMoveDrag(Widget* widget, const gfx::Point& point) = 0;
+
+ // Starts resizing window give by |widget|. |point| represents the
+ // initial location of the mouse pointer and |hittest_code| represents
+ // the edge of the window a user selected to resize the window. See
+ // ui/base/hit_test.h for the hittest_code definition.
+ virtual void StartResizeDrag(
+ Widget* widget, const gfx::Point& point, int hittest_code) = 0;
+
+ // Sets mouse capture on |widget|. Returns false if other widget
+ // already has mouse capture.
+ virtual bool SetMouseCapture(Widget* widget) = 0;
+
+ // Releases the mouse capture on |widget|. Returns false if |widget|
+ // haven't capture the mouse.
+ virtual bool ReleaseMouseCapture(Widget* widget) = 0;
+
+ // Checks if the |widget| has mouse capture.
+ virtual bool HasMouseCapture(const Widget* widget) const = 0;
+
+ // WindowManager handles mouse event first. It may reisze/move window,
+ // or send the event to widget that has mouse capture.
+ virtual bool HandleKeyEvent(Widget* widget, const KeyEvent& event) = 0;
+
+ // WindowManager handles mouse event first. It may resize/move window,
+ // or send the event to widget that has mouse capture.
+ virtual bool HandleMouseEvent(Widget* widget, const MouseEvent& event) = 0;
+
+ // WindowManager handles touch event first. It is currently used only to
+ // activate windows. But it can also be used to move/resize windows.
+ virtual ui::TouchStatus HandleTouchEvent(Widget* widget,
+ const TouchEvent& event) = 0;
+
+ // Register widget to the window manager.
+ virtual void Register(Widget* widget) = 0;
+
+ // Installs window manager.
+ static void Install(WindowManager* wm);
+
+ // Returns installed WindowManager.
+ static WindowManager* Get();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WindowManager);
+};
+
+} // namespace views
+
+#endif // VIEWS_WIDGET_WINDOW_MANAGER_H_