diff options
Diffstat (limited to 'chrome/browser/dock_info_win.cc')
-rw-r--r-- | chrome/browser/dock_info_win.cc | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/chrome/browser/dock_info_win.cc b/chrome/browser/dock_info_win.cc new file mode 100644 index 0000000..bdcbb59 --- /dev/null +++ b/chrome/browser/dock_info_win.cc @@ -0,0 +1,311 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/dock_info.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/views/frame/browser_view.h" +#include "chrome/browser/views/tabs/tab.h" + +namespace { + +// BaseWindowFinder ----------------------------------------------------------- + +// Base class used to locate a window. This is intended to be used with the +// various win32 functions that iterate over windows. +// +// A subclass need only override ShouldStopIterating to determine when +// iteration should stop. +class BaseWindowFinder { + public: + // Creates a BaseWindowFinder with the specified set of HWNDs to ignore. + explicit BaseWindowFinder(const std::set<HWND>& ignore) : ignore_(ignore) {} + virtual ~BaseWindowFinder() {} + + protected: + // Returns true if iteration should stop, false if iteration should continue. + virtual bool ShouldStopIterating(HWND window) = 0; + + static BOOL CALLBACK WindowCallbackProc(HWND hwnd, LPARAM lParam) { + BaseWindowFinder* finder = reinterpret_cast<BaseWindowFinder*>(lParam); + if (finder->ignore_.find(hwnd) != finder->ignore_.end()) + return TRUE; + + return finder->ShouldStopIterating(hwnd) ? FALSE : TRUE; + } + + private: + const std::set<HWND>& ignore_; + + DISALLOW_COPY_AND_ASSIGN(BaseWindowFinder); +}; + +// TopMostFinder -------------------------------------------------------------- + +// Helper class to determine if a particular point of a window is not obscured +// by another window. +class TopMostFinder : public BaseWindowFinder { + public: + // Returns true if |window| is the topmost window at the location + // |screen_loc|, not including the windows in |ignore|. + static bool IsTopMostWindowAtPoint(HWND window, + const gfx::Point& screen_loc, + const std::set<HWND>& ignore) { + TopMostFinder finder(window, screen_loc, ignore); + return finder.is_top_most_; + } + + virtual bool ShouldStopIterating(HWND hwnd) { + if (hwnd == target_) { + // Window is topmost, stop iterating. + is_top_most_ = true; + return true; + } + + if (!IsWindowVisible(hwnd)) { + // The window isn't visible, keep iterating. + return false; + } + + RECT r; + if (!GetWindowRect(hwnd, &r) || !PtInRect(&r, screen_loc_.ToPOINT())) { + // The window doesn't contain the point, keep iterating. + return false; + } + + // hwnd is at the point. Make sure the point is within the windows region. + if (GetWindowRgn(hwnd, tmp_region_.Get()) == ERROR) { + // There's no region on the window and the window contains the point. Stop + // iterating. + return true; + } + + // The region is relative to the window's rect. + BOOL is_point_in_region = PtInRegion(tmp_region_.Get(), + screen_loc_.x() - r.left, screen_loc_.y() - r.top); + tmp_region_ = CreateRectRgn(0, 0, 0, 0); + // Stop iterating if the region contains the point. + return !!is_point_in_region; + } + + private: + TopMostFinder(HWND window, + const gfx::Point& screen_loc, + const std::set<HWND>& ignore) + : BaseWindowFinder(ignore), + target_(window), + screen_loc_(screen_loc), + is_top_most_(false), + tmp_region_(CreateRectRgn(0, 0, 0, 0)) { + EnumWindows(WindowCallbackProc, reinterpret_cast<LPARAM>(this)); + } + + // The window we're looking for. + HWND target_; + + // Location of window to find. + gfx::Point screen_loc_; + + // Is target_ the top most window? This is initially false but set to true + // in ShouldStopIterating if target_ is passed in. + bool is_top_most_; + + ScopedRegion tmp_region_; + + DISALLOW_COPY_AND_ASSIGN(TopMostFinder); +}; + +// WindowFinder --------------------------------------------------------------- + +// Helper class to determine if a particular point contains a window from our +// process. +class LocalProcessWindowFinder : public BaseWindowFinder { + public: + // Returns the hwnd from our process at screen_loc that is not obscured by + // another window. Returns NULL otherwise. + static HWND GetProcessWindowAtPoint(const gfx::Point& screen_loc, + const std::set<HWND>& ignore) { + LocalProcessWindowFinder finder(screen_loc, ignore); + if (finder.result_ && + TopMostFinder::IsTopMostWindowAtPoint(finder.result_, screen_loc, + ignore)) { + return finder.result_; + } + return NULL; + } + + protected: + virtual bool ShouldStopIterating(HWND hwnd) { + RECT r; + if (IsWindowVisible(hwnd) && GetWindowRect(hwnd, &r) && + PtInRect(&r, screen_loc_.ToPOINT())) { + result_ = hwnd; + return true; + } + return false; + } + + private: + LocalProcessWindowFinder(const gfx::Point& screen_loc, + const std::set<HWND>& ignore) + : BaseWindowFinder(ignore), + screen_loc_(screen_loc), + result_(NULL) { + EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, + reinterpret_cast<LPARAM>(this)); + } + + // Position of the mouse. + gfx::Point screen_loc_; + + // The resulting window. This is initially null but set to true in + // ShouldStopIterating if an appropriate window is found. + HWND result_; + + DISALLOW_COPY_AND_ASSIGN(LocalProcessWindowFinder); +}; + +// DockToWindowFinder --------------------------------------------------------- + +// Helper class for creating a DockInfo from a specified point. +class DockToWindowFinder : public BaseWindowFinder { + public: + // Returns the DockInfo for the specified point. If there is no docking + // position for the specified point the returned DockInfo has a type of NONE. + static DockInfo GetDockInfoAtPoint(const gfx::Point& screen_loc, + const std::set<HWND>& ignore) { + DockToWindowFinder finder(screen_loc, ignore); + if (!finder.result_.window() || + !TopMostFinder::IsTopMostWindowAtPoint(finder.result_.window(), + finder.result_.hot_spot(), + ignore)) { + finder.result_.set_type(DockInfo::NONE); + } + return finder.result_; + } + + protected: + virtual bool ShouldStopIterating(HWND hwnd) { + BrowserView* window = BrowserView::GetBrowserViewForNativeWindow(hwnd); + RECT bounds; + if (!window || !IsWindowVisible(hwnd) || + !GetWindowRect(hwnd, &bounds)) { + return false; + } + + // Check the three corners we allow docking to. We don't currently allow + // docking to top of window as it conflicts with docking to the tab strip. + if (CheckPoint(hwnd, bounds.left, (bounds.top + bounds.bottom) / 2, + DockInfo::LEFT_OF_WINDOW) || + CheckPoint(hwnd, bounds.right - 1, (bounds.top + bounds.bottom) / 2, + DockInfo::RIGHT_OF_WINDOW) || + CheckPoint(hwnd, (bounds.left + bounds.right) / 2, bounds.bottom - 1, + DockInfo::BOTTOM_OF_WINDOW)) { + return true; + } + return false; + } + + private: + DockToWindowFinder(const gfx::Point& screen_loc, + const std::set<HWND>& ignore) + : BaseWindowFinder(ignore), + screen_loc_(screen_loc) { + HMONITOR monitor = MonitorFromPoint(screen_loc.ToPOINT(), + MONITOR_DEFAULTTONULL); + MONITORINFO monitor_info = {0}; + monitor_info.cbSize = sizeof(MONITORINFO); + if (monitor && GetMonitorInfo(monitor, &monitor_info)) { + result_.set_monitor_bounds(gfx::Rect(monitor_info.rcWork)); + EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, + reinterpret_cast<LPARAM>(this)); + } + } + + bool CheckPoint(HWND hwnd, int x, int y, DockInfo::Type type) { + bool in_enable_area; + if (DockInfo::IsCloseToPoint(screen_loc_, x, y, &in_enable_area)) { + result_.set_in_enable_area(in_enable_area); + result_.set_window(hwnd); + result_.set_type(type); + result_.set_hot_spot(gfx::Point(x, y)); + // Only show the hotspot if the monitor contains the bounds of the popup + // window. Otherwise we end with a weird situation where the popup window + // isn't completely visible. + return result_.monitor_bounds().Contains(result_.GetPopupRect()); + } + return false; + } + + // The location to look for. + gfx::Point screen_loc_; + + // The resulting DockInfo. + DockInfo result_; + + DISALLOW_COPY_AND_ASSIGN(DockToWindowFinder); +}; + +} // namespace + +// DockInfo ------------------------------------------------------------------- + +// static +DockInfo DockInfo::GetDockInfoAtPoint(const gfx::Point& screen_point, + const std::set<HWND>& ignore) { + if (factory_) + return factory_->GetDockInfoAtPoint(screen_point, ignore); + + // Try docking to a window first. + DockInfo info = DockToWindowFinder::GetDockInfoAtPoint(screen_point, ignore); + if (info.type() != DockInfo::NONE) + return info; + + // No window relative positions. Try monitor relative positions. + const gfx::Rect& m_bounds = info.monitor_bounds(); + int mid_x = m_bounds.x() + m_bounds.width() / 2; + int mid_y = m_bounds.y() + m_bounds.height() / 2; + + bool result = + info.CheckMonitorPoint(screen_point, mid_x, m_bounds.y(), + DockInfo::MAXIMIZE) || + info.CheckMonitorPoint(screen_point, mid_x, m_bounds.bottom(), + DockInfo::BOTTOM_HALF) || + info.CheckMonitorPoint(screen_point, m_bounds.x(), mid_y, + DockInfo::LEFT_HALF) || + info.CheckMonitorPoint(screen_point, m_bounds.right(), mid_y, + DockInfo::RIGHT_HALF); + + return info; +} + +HWND DockInfo::GetLocalProcessWindowAtPoint(const gfx::Point& screen_point, + const std::set<HWND>& ignore) { + if (factory_) + return factory_->GetLocalProcessWindowAtPoint(screen_point, ignore); + return + LocalProcessWindowFinder::GetProcessWindowAtPoint(screen_point, ignore); +} + +bool DockInfo::GetWindowBounds(gfx::Rect* bounds) const { + RECT window_rect; + if (!window() || !GetWindowRect(window(), &window_rect)) + return false; + *bounds = gfx::Rect(window_rect); + return true; +} + +void DockInfo::SizeOtherWindowTo(const gfx::Rect& bounds) const { + if (IsZoomed(window())) { + // We're docking relative to another window, we need to make sure the + // window we're docking to isn't maximized. + ShowWindow(window(), SW_RESTORE | SW_SHOWNA); + } + SetWindowPos(window(), HWND_TOP, bounds.x(), bounds.y(), bounds.width(), + bounds.height(), SWP_NOACTIVATE | SWP_NOOWNERZORDER); +} |