diff options
Diffstat (limited to 'chrome/views/hwnd_view_container.cc')
-rw-r--r-- | chrome/views/hwnd_view_container.cc | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/chrome/views/hwnd_view_container.cc b/chrome/views/hwnd_view_container.cc new file mode 100644 index 0000000..6aa2332 --- /dev/null +++ b/chrome/views/hwnd_view_container.cc @@ -0,0 +1,877 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/views/hwnd_view_container.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/win_util.h" +#include "chrome/common/gfx/chrome_canvas.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/win_util.h" +#include "chrome/views/aero_tooltip_manager.h" +#include "chrome/views/focus_manager.h" +#include "chrome/views/hwnd_notification_source.h" +#include "chrome/views/root_view.h" + +namespace ChromeViews { + +static const DWORD kWindowDefaultChildStyle = + WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; +static const DWORD kWindowDefaultStyle = WS_OVERLAPPEDWINDOW; +static const DWORD kWindowDefaultExStyle = 0; + +// Property used to link the HWND to its RootView. +static const wchar_t* const kRootViewWindowProperty = L"__ROOT_VIEW__"; + +bool SetRootViewForHWND(HWND hwnd, RootView* root_view) { + return ::SetProp(hwnd, kRootViewWindowProperty, root_view) ? true : false; +} + +RootView* GetRootViewForHWND(HWND hwnd) { + return reinterpret_cast<RootView*>(::GetProp(hwnd, kRootViewWindowProperty)); +} + +// Used to locate the HWNDViewContainer issuing the current Create. Only valid +// for the life of Create. +// +// This obviously assumes we only create HWNDViewContainers from the same +// thread, which is currently the case. +static HWNDViewContainer* instance_issuing_create = NULL; + +/////////////////////////////////////////////////////////////////////////////// +// FillLayout + +FillLayout::FillLayout() { +} + +FillLayout::~FillLayout() { +} + +void FillLayout::Layout(View* host) { + CRect bounds; + host->GetViewContainer()->GetBounds(&bounds, false); + if (host->GetChildViewCount() == 0) + return; + + View* frame_view = host->GetChildViewAt(0); + frame_view->SetBounds(CRect(CPoint(0, 0), bounds.Size())); +} + +void FillLayout::GetPreferredSize(View* host, CSize* out) { + DCHECK(host->GetChildViewCount() == 1); + host->GetChildViewAt(0)->GetPreferredSize(out); +} + +/////////////////////////////////////////////////////////////////////////////// +// Window class tracking. + +// static +const wchar_t* const HWNDViewContainer::kBaseClassName = + L"Chrome_HWNDViewContainer_"; + +// Window class information used for registering unique windows. +struct ClassInfo { + UINT style; + HICON icon; + HICON small_icon; + HBRUSH background; + + explicit ClassInfo(int style) + : style(style), + icon(NULL), + small_icon(NULL), + background(NULL) {} + + // Compares two ClassInfos. Returns true if all members match. + bool Equals(const ClassInfo& other) { + return (other.style == style && other.icon == icon && + other.small_icon == icon && other.background == background); + } +}; + +// Represents a registered window class. +struct RegisteredClass { + RegisteredClass(const ClassInfo& info, + const std::wstring& name, + ATOM atom) + : info(info), + name(name), + atom(atom) { + } + + // Info used to create the class. + ClassInfo info; + + // The name given to the window. + std::wstring name; + + // The ATOM returned from creating the window. + ATOM atom; +}; + +typedef std::list<RegisteredClass> RegisteredClasses; + +// The list of registered classes. +static RegisteredClasses* registered_classes = NULL; + + +/////////////////////////////////////////////////////////////////////////////// +// HWNDViewContainer, public + +HWNDViewContainer::HWNDViewContainer() + : tracking_mouse_events_(false), + has_capture_(false), + current_action_(FA_NONE), + toplevel_(false), + window_style_(0), + window_ex_style_(kWindowDefaultExStyle), + layered_(false), + layered_alpha_(255), + delete_on_destroy_(true), + can_update_layered_window_(true), + last_mouse_event_was_move_(false), + is_mouse_down_(false), + class_style_(CS_DBLCLKS), + hwnd_(NULL), + close_container_factory_(this) { +} + +HWNDViewContainer::~HWNDViewContainer() { + MessageLoop::current()->RemoveObserver(this); +} + +void HWNDViewContainer::Init(HWND parent, + const gfx::Rect& bounds, + View* contents_view, + bool has_own_focus_manager) { + toplevel_ = parent == NULL; + + if (window_style_ == 0) + window_style_ = toplevel_ ? kWindowDefaultStyle : kWindowDefaultChildStyle; + + // See if the style has been overridden. + opaque_ = !(window_ex_style_ & WS_EX_TRANSPARENT); + layered_ = !!(window_ex_style_ & WS_EX_LAYERED); + + // Force creation of the RootView if it hasn't been created yet. + GetRootView(); + + // Ensures the parent we have been passed is valid, otherwise CreateWindowEx + // will fail. + if (parent && !::IsWindow(parent)) { + NOTREACHED() << "invalid parent window specified."; + parent = NULL; + } + + hwnd_ = CreateWindowEx(window_ex_style_, GetWindowClassName().c_str(), L"", + window_style_, bounds.x(), bounds.y(), bounds.width(), + bounds.height(), parent, NULL, NULL, this); + DCHECK(hwnd_); + // The window procedure should have set the data for us. + DCHECK(win_util::GetWindowUserData(hwnd_) == this); + + root_view_->OnViewContainerCreated(); + + if (has_own_focus_manager) { + ChromeViews::FocusManager::CreateFocusManager(hwnd_, GetRootView()); + } else { + // Subclass the window so we get the tab key messages when a view with no + // associated native window is focused. + FocusManager::InstallFocusSubclass(hwnd_, NULL); + } + + // The RootView is set up _after_ the window is created so that its + // ViewContainer pointer is valid. + if (contents_view) { + // The FillLayout only applies when we have been provided with a single + // contents view. If the user intends to manage the RootView themselves, + // they are responsible for providing their own LayoutManager, since + // FillLayout is only capable of laying out a single child view. + root_view_->SetLayoutManager(new FillLayout()); + root_view_->AddChildView(contents_view); + } + + // Manually size the window here to ensure the root view is laid out. + ChangeSize(0, CSize(bounds.width(), bounds.height())); + + // Sets the RootView as a property, so the automation can introspect windows. + SetRootViewForHWND(hwnd_, root_view_.get()); + + MessageLoop::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. + if (win_util::ShouldUseVistaFrame()) { + tooltip_manager_.reset(new AeroTooltipManager(this, GetHWND())); + } else { + tooltip_manager_.reset(new TooltipManager(this, GetHWND())); + } + + // This message initializes the window so that focus border are shown for + // windows. + ::SendMessage(GetHWND(), + 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(GetHWND(), NULL, 0); +} + +/////////////////////////////////////////////////////////////////////////////// +// ChromeViews::ViewContainer + +void HWNDViewContainer::GetBounds(CRect *out, bool including_frame) const { + if (including_frame) { + GetWindowRect(out); + } else { + GetClientRect(out); + + POINT p = {0, 0}; + ::ClientToScreen(hwnd_, &p); + + out->left += p.x; + out->top += p.y; + out->right += p.x; + out->bottom += p.y; + } +} + +void HWNDViewContainer::MoveToFront(bool should_activate) { + int flags = SWP_NOMOVE | SWP_NOSIZE; + if (!should_activate) { + flags |= SWP_NOACTIVATE; + } + SetWindowPos(HWND_NOTOPMOST, 0, 0, 0, 0, flags); +} + +HWND HWNDViewContainer::GetHWND() const { + return hwnd_; +} + +void HWNDViewContainer::PaintNow(const CRect& update_rect) { + if (layered_) { + PaintLayeredWindow(); + } else if (root_view_->NeedsPainting(false) && IsWindow()) { + if (!opaque_ && GetParent()) { + // We're transparent. Need to force painting to occur from our parent. + CRect parent_update_rect = update_rect; + POINT location_in_parent = { 0, 0 }; + ClientToScreen(hwnd_, &location_in_parent); + ::ScreenToClient(GetParent(), &location_in_parent); + parent_update_rect.OffsetRect(location_in_parent); + ::RedrawWindow(GetParent(), parent_update_rect, NULL, + RDW_UPDATENOW | RDW_INVALIDATE | RDW_ALLCHILDREN); + } else { + RedrawWindow(hwnd_, update_rect, NULL, + RDW_UPDATENOW | RDW_INVALIDATE | RDW_ALLCHILDREN); + } + // As we were created with a style of WS_CLIPCHILDREN redraw requests may + // result in an empty paint rect in WM_PAINT (this'll happen if a + // child HWND completely contains the update _rect). In such a scenario + // RootView would never get a ProcessPaint and always think it needs to + // be painted (leading to a steady stream of RedrawWindow requests on every + // event). For this reason we tell RootView it doesn't need to paint + // here. + root_view_->ClearPaintRect(); + } +} + +RootView* HWNDViewContainer::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(); +} + +bool HWNDViewContainer::IsVisible() { + return !!::IsWindowVisible(GetHWND()); +} + +bool HWNDViewContainer::IsActive() { + return win_util::IsWindowActive(GetHWND()); +} + +TooltipManager* HWNDViewContainer::GetTooltipManager() { + return tooltip_manager_.get(); +} + + +void HWNDViewContainer::SetLayeredAlpha(BYTE layered_alpha) { + layered_alpha_ = layered_alpha; + +// if (hwnd_) +// UpdateWindowFromContents(contents_->getTopPlatformDevice().getBitmapDC()); +} + +static BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM l_param) { + RootView* root_view = + reinterpret_cast<RootView*>(GetProp(hwnd, kRootViewWindowProperty)); + if (root_view) { + *reinterpret_cast<RootView**>(l_param) = root_view; + return FALSE; // Stop enumerating. + } + return TRUE; // Keep enumerating. +} + +// static +RootView* HWNDViewContainer::FindRootView(HWND hwnd) { + RootView* root_view = + reinterpret_cast<RootView*>(GetProp(hwnd, kRootViewWindowProperty)); + if (root_view) + return root_view; + + // Enumerate all children and check if they have a RootView. + EnumChildWindows(hwnd, EnumChildProc, reinterpret_cast<LPARAM>(&root_view)); + + return root_view; +} + +void HWNDViewContainer::Close() { + // Let's hide ourselves right away. + Hide(); + if (close_container_factory_.empty()) { + // 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, + close_container_factory_.NewRunnableMethod( + &HWNDViewContainer::CloseNow)); + } +} + +void HWNDViewContainer::Hide() { + // 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 HWNDViewContainer::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(); +} + +/////////////////////////////////////////////////////////////////////////////// +// MessageLoop::Observer + +void HWNDViewContainer::WillProcessMessage(const MSG& msg) { +} + +void HWNDViewContainer::DidProcessMessage(const MSG& msg) { + if (root_view_->NeedsPainting(true)) { + PaintNow(root_view_->GetScheduledPaintRect()); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// FocusTraversable + +View* HWNDViewContainer::FindNextFocusableView( + View* starting_view, bool reverse, Direction direction, bool dont_loop, + FocusTraversable** focus_traversable, View** focus_traversable_view) { + return root_view_->FindNextFocusableView(starting_view, + reverse, + direction, + dont_loop, + focus_traversable, + focus_traversable_view); +} + +FocusTraversable* HWNDViewContainer::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; +} + +void HWNDViewContainer::SetFocusTraversableParent(FocusTraversable* parent) { + root_view_->SetFocusTraversableParent(parent); +} + +View* HWNDViewContainer::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; +} + +void HWNDViewContainer::SetFocusTraversableParentView(View* parent_view) { + root_view_->SetFocusTraversableParentView(parent_view); +} + +/////////////////////////////////////////////////////////////////////////////// +// Message handlers + +void HWNDViewContainer::OnCaptureChanged(HWND hwnd) { + if (has_capture_) { + if (is_mouse_down_) + root_view_->ProcessMouseDragCanceled(); + is_mouse_down_ = false; + has_capture_ = false; + } +} + +void HWNDViewContainer::OnClose() { + // WARNING: this method is NOT called for all HWNDViewContainers. If you + // need to do cleanup code before HWNDViewContainer is destroyed, put it + // in OnDestroy. + + NotificationService::current()->Notify( + NOTIFY_WINDOW_CLOSED, Source<HWND>(hwnd_), + NotificationService::NoDetails()); + + Close(); +} + +void HWNDViewContainer::OnDestroy() { + root_view_->OnViewContainerDestroyed(); + + RemoveProp(hwnd_, kRootViewWindowProperty); +} + +LRESULT HWNDViewContainer::OnEraseBkgnd(HDC dc) { + // This is needed for magical win32 flicker ju-ju + return 1; +} + +void HWNDViewContainer::OnKeyDown(TCHAR c, UINT rep_cnt, UINT flags) { + KeyEvent event(Event::ET_KEY_PRESSED, c, rep_cnt, flags); + root_view_->ProcessKeyEvent(event); +} + +void HWNDViewContainer::OnKeyUp(TCHAR c, UINT rep_cnt, UINT flags) { + KeyEvent event(Event::ET_KEY_RELEASED, c, rep_cnt, flags); + root_view_->ProcessKeyEvent(event); +} + +void HWNDViewContainer::OnLButtonDown(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_LBUTTON, false); +} + +void HWNDViewContainer::OnLButtonUp(UINT flags, const CPoint& point) { + ProcessMouseReleased(point, flags | MK_LBUTTON); +} + +void HWNDViewContainer::OnLButtonDblClk(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_LBUTTON, true); +} + +void HWNDViewContainer::OnMButtonDown(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_MBUTTON, false); +} + +void HWNDViewContainer::OnMButtonUp(UINT flags, const CPoint& point) { + ProcessMouseReleased(point, flags | MK_MBUTTON); +} + +void HWNDViewContainer::OnMButtonDblClk(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_MBUTTON, true); +} + +LRESULT HWNDViewContainer::OnMouseActivate(HWND window, + UINT hittest_code, + UINT message) { + SetMsgHandled(FALSE); + return MA_ACTIVATE; +} + +void HWNDViewContainer::OnMouseMove(UINT flags, const CPoint& point) { + ProcessMouseMoved(point, flags); +} + +void HWNDViewContainer::OnMouseLeave() { + ProcessMouseExited(); +} + +LRESULT HWNDViewContainer::OnMouseWheel(UINT flags, + short distance, + const CPoint& point) { + MouseWheelEvent e(distance, + point.x, + point.y, + Event::ConvertWindowsFlags(flags)); + return root_view_->ProcessMouseWheelEvent(e) ? 0 : 1; +} + +LRESULT HWNDViewContainer::OnMouseRange(UINT msg, + WPARAM w_param, + LPARAM l_param) { + tooltip_manager_->OnMouse(msg, w_param, l_param); + SetMsgHandled(FALSE); + return 0; +} + +void HWNDViewContainer::OnNCLButtonDblClk(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); +} + +void HWNDViewContainer::OnNCLButtonDown(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); +} + +void HWNDViewContainer::OnNCLButtonUp(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); +} + +LRESULT HWNDViewContainer::OnNCMouseMove(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); + return 0; +} + +void HWNDViewContainer::OnNCRButtonDblClk(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); +} + +void HWNDViewContainer::OnNCRButtonDown(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); +} + +void HWNDViewContainer::OnNCRButtonUp(UINT flags, const CPoint& point) { + SetMsgHandled(FALSE); +} + +LRESULT HWNDViewContainer::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 HWNDViewContainer::OnPaint(HDC dc) { + root_view_->OnPaint(GetHWND()); +} + +void HWNDViewContainer::OnRButtonDown(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_RBUTTON, false); +} + +void HWNDViewContainer::OnRButtonUp(UINT flags, const CPoint& point) { + ProcessMouseReleased(point, flags | MK_RBUTTON); +} + +void HWNDViewContainer::OnRButtonDblClk(UINT flags, const CPoint& point) { + ProcessMousePressed(point, flags | MK_RBUTTON, true); +} + +LRESULT HWNDViewContainer::OnSettingChange(UINT msg, + WPARAM w_param, + LPARAM l_param) { + if (toplevel_) { + SetMsgHandled(FALSE); + if (w_param != SPI_SETWORKAREA) + return 0; // Return value is effectively ignored in atlwin.h. + + AdjustWindowToFitScreenSize(); + SetMsgHandled(TRUE); + } + // Don't care, overridden by interested subclasses + return 0; +} + +void HWNDViewContainer::OnSize(UINT param, const CSize& size) { + ChangeSize(param, size); +} + +void HWNDViewContainer::OnFinalMessage(HWND window) { + if (delete_on_destroy_) + delete this; +} + +/////////////////////////////////////////////////////////////////////////////// +// HWNDViewContainer, protected + +void HWNDViewContainer::TrackMouseEvents() { + // 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 (!tracking_mouse_events_) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = GetHWND(); + tme.dwHoverTime = 0; + TrackMouseEvent(&tme); + tracking_mouse_events_ = true; + } +} + +bool HWNDViewContainer::ProcessMousePressed(const CPoint& point, + UINT flags, + bool dbl_click) { + last_mouse_event_was_move_ = false; + MouseEvent mouse_pressed(Event::ET_MOUSE_PRESSED, + point.x, + point.y, + (dbl_click ? MouseEvent::EF_IS_DOUBLE_CLICK : 0) | + Event::ConvertWindowsFlags(flags)); + if (root_view_->OnMousePressed(mouse_pressed)) { + is_mouse_down_ = true; + if (!has_capture_) { + SetCapture(); + has_capture_ = true; + current_action_ = FA_FORWARDING; + } + return true; + } + return false; +} + +void HWNDViewContainer::ProcessMouseDragged(const CPoint& point, UINT flags) { + last_mouse_event_was_move_ = false; + MouseEvent mouse_drag(Event::ET_MOUSE_DRAGGED, + point.x, + point.y, + Event::ConvertWindowsFlags(flags)); + root_view_->OnMouseDragged(mouse_drag); +} + +void HWNDViewContainer::ProcessMouseReleased(const CPoint& point, UINT flags) { + last_mouse_event_was_move_ = false; + MouseEvent mouse_up(Event::ET_MOUSE_RELEASED, + point.x, + point.y, + Event::ConvertWindowsFlags(flags)); + // Release the capture first, that way we don't get confused if + // OnMouseReleased blocks. + if (has_capture_ && ReleaseCaptureOnMouseReleased()) { + has_capture_ = false; + current_action_ = FA_NONE; + ReleaseCapture(); + } + is_mouse_down_ = false; + root_view_->OnMouseReleased(mouse_up, false); +} + +void HWNDViewContainer::ProcessMouseMoved(const CPoint &point, UINT flags) { + // 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. + if (!has_capture_) + TrackMouseEvents(); + if (has_capture_ && is_mouse_down_) { + ProcessMouseDragged(point, flags); + } else { + CPoint screen_loc = point; + View::ConvertPointToScreen(root_view_.get(), &screen_loc); + if (last_mouse_event_was_move_ && last_mouse_move_x_ == screen_loc.x && + last_mouse_move_y_ == screen_loc.y) { + // Don't generate a mouse event for the same location as the last. + return; + } + last_mouse_move_x_ = screen_loc.x; + last_mouse_move_y_ = screen_loc.y; + last_mouse_event_was_move_ = true; + MouseEvent mouse_move(Event::ET_MOUSE_MOVED, + point.x, + point.y, + Event::ConvertWindowsFlags(flags)); + root_view_->OnMouseMoved(mouse_move); + } +} + +void HWNDViewContainer::ProcessMouseExited() { + last_mouse_event_was_move_ = false; + root_view_->ProcessOnMouseExited(); + // Reset our tracking flag so that future mouse movement over this + // HWNDViewContainer results in a new tracking session. + tracking_mouse_events_ = false; +} + +void HWNDViewContainer::AdjustWindowToFitScreenSize() { + // Desktop size has changed. Make sure we're still on screen. + CRect wr; + GetWindowRect(&wr); + HMONITOR hmon = MonitorFromRect(&wr, MONITOR_DEFAULTTONEAREST); + if (!hmon) { + // No monitor available. + return; + } + + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hmon, &mi); + gfx::Rect window_rect(wr); + gfx::Rect monitor_rect(mi.rcWork); + gfx::Rect new_window_rect = window_rect.AdjustToFit(monitor_rect); + if (!new_window_rect.Equals(window_rect)) { + // New position differs from last, resize window. + ::SetWindowPos(GetHWND(), + 0, + new_window_rect.x(), + new_window_rect.y(), + new_window_rect.width(), + new_window_rect.height(), + SWP_NOACTIVATE | SWP_NOZORDER); + } +} + +void HWNDViewContainer::ChangeSize(UINT size_param, const CSize& size) { + CRect rect; + if (layered_) { + GetWindowRect(&rect); + SizeContents(rect); + } else { + GetClientRect(&rect); + } + + // Resizing changes the size of the view hierarchy and thus forces a + // complete relayout. + root_view_->SetBounds(CRect(CPoint(0,0), rect.Size())); + root_view_->Layout(); + root_view_->SchedulePaint(); + + if (layered_) + PaintNow(rect); +} + +RootView* HWNDViewContainer::CreateRootView() { + return new RootView(this, true); +} + +/////////////////////////////////////////////////////////////////////////////// +// HWNDViewContainer, private: + +void HWNDViewContainer::SizeContents(const CRect& window_rect) { + contents_.reset(new ChromeCanvas(window_rect.Width(), + window_rect.Height(), + false)); +} + +void HWNDViewContainer::PaintLayeredWindow() { + // Painting monkeys with our cliprect, so we need to save it so that the + // call to UpdateLayeredWindow updates the entire window, not just the + // cliprect. + contents_->save(SkCanvas::kClip_SaveFlag); + CRect dirty_rect = root_view_->GetScheduledPaintRect(); + contents_->ClipRectInt( + dirty_rect.left, dirty_rect.top, dirty_rect.Width(), dirty_rect.Height()); + root_view_->ProcessPaint(contents_.get()); + contents_->restore(); + + UpdateWindowFromContents(contents_->getTopPlatformDevice().getBitmapDC()); +} + +void HWNDViewContainer::UpdateWindowFromContents(HDC dib_dc) { + DCHECK(layered_); + if (can_update_layered_window_) { + CRect wr; + GetWindowRect(&wr); + CSize size(wr.right - wr.left, wr.bottom - wr.top); + CPoint zero_origin(0, 0); + CPoint window_position = wr.TopLeft(); + + BLENDFUNCTION blend = {AC_SRC_OVER, 0, layered_alpha_, AC_SRC_ALPHA}; + ::UpdateLayeredWindow( + hwnd_, NULL, &window_position, &size, dib_dc, &zero_origin, + RGB(0xFF, 0xFF, 0xFF), &blend, ULW_ALPHA); + } +} + +std::wstring HWNDViewContainer::GetWindowClassName() { + if (!registered_classes) + registered_classes = new RegisteredClasses(); + ClassInfo class_info(initial_class_style()); + for (RegisteredClasses::iterator i = registered_classes->begin(); + i != registered_classes->end(); ++i) { + if (class_info.Equals(i->info)) + return i->name; + } + + // No class found, need to register one. + static int registered_count = 0; + std::wstring name = + std::wstring(kBaseClassName) + IntToWString(registered_count++); + WNDCLASSEX class_ex; + class_ex.cbSize = sizeof(WNDCLASSEX); + class_ex.style = class_info.style; + class_ex.lpfnWndProc = &HWNDViewContainer::WndProc; + class_ex.cbClsExtra = 0; + class_ex.cbWndExtra = 0; + class_ex.hInstance = NULL; + class_ex.hIcon = class_info.icon; + class_ex.hCursor = LoadCursor(NULL, IDC_ARROW); + class_ex.hbrBackground = reinterpret_cast<HBRUSH>(class_info.background + 1); + class_ex.lpszMenuName = NULL; + class_ex.lpszClassName = name.c_str(); + class_ex.hIconSm = class_info.small_icon; + ATOM atom = RegisterClassEx(&class_ex); + DCHECK(atom); + RegisteredClass registered_class(class_info, name, atom); + registered_classes->push_back(registered_class); + return name; +} + +// static +LRESULT CALLBACK HWNDViewContainer::WndProc(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param) { + if (message == WM_NCCREATE) { + CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(l_param); + HWNDViewContainer* vc = + reinterpret_cast<HWNDViewContainer*>(cs->lpCreateParams); + DCHECK(vc); + win_util::SetWindowUserData(window, vc); + vc->hwnd_ = window; + return TRUE; + } + HWNDViewContainer* vc = reinterpret_cast<HWNDViewContainer*>( + win_util::GetWindowUserData(window)); + if (!vc) + return 0; + LRESULT result = 0; + if (!vc->ProcessWindowMessage(window, message, w_param, l_param, result)) + result = DefWindowProc(window, message, w_param, l_param); + if (message == WM_NCDESTROY) { + vc->hwnd_ = NULL; + vc->OnFinalMessage(window); + } + return result; +} + +} // namespace ChromeViews |