summaryrefslogtreecommitdiffstats
path: root/chrome/views/window_win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/views/window_win.cc')
-rw-r--r--chrome/views/window_win.cc1234
1 files changed, 0 insertions, 1234 deletions
diff --git a/chrome/views/window_win.cc b/chrome/views/window_win.cc
deleted file mode 100644
index e2226d7..0000000
--- a/chrome/views/window_win.cc
+++ /dev/null
@@ -1,1234 +0,0 @@
-// 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/views/window_win.h"
-
-#include <shellapi.h>
-
-#include "base/win_util.h"
-#include "chrome/app/chrome_dll_resource.h"
-#include "chrome/common/gfx/chrome_canvas.h"
-#include "chrome/common/gfx/chrome_font.h"
-#include "chrome/common/gfx/icon_util.h"
-#include "chrome/common/gfx/path.h"
-#include "chrome/common/l10n_util.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/pref_service.h"
-#include "chrome/common/resource_bundle.h"
-#include "chrome/common/win_util.h"
-#include "chrome/views/client_view.h"
-#include "chrome/views/custom_frame_view.h"
-#include "chrome/views/native_frame_view.h"
-#include "chrome/views/non_client_view.h"
-#include "chrome/views/root_view.h"
-#include "chrome/views/window_delegate.h"
-#include "grit/generated_resources.h"
-
-namespace views {
-
-// 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.
-//
-// 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 WindowWin::ScopedRedrawLock {
- public:
- explicit ScopedRedrawLock(WindowWin* window) : window_(window) {
- window_->LockUpdates();
- }
-
- ~ScopedRedrawLock() {
- window_->UnlockUpdates();
- }
-
- private:
- // The window having its style changed.
- WindowWin* window_;
-};
-
-HCURSOR WindowWin::resize_cursors_[6];
-
-// If the hung renderer warning doesn't fit on screen, the amount of padding to
-// be left between the edge of the window and the edge of the nearest monitor,
-// after the window is nudged back on screen. Pixels.
-static const int kMonitorEdgePadding = 10;
-
-////////////////////////////////////////////////////////////////////////////////
-// WindowWin, public:
-
-WindowWin::~WindowWin() {
-}
-
-// static
-Window* Window::CreateChromeWindow(gfx::NativeWindow parent,
- const gfx::Rect& bounds,
- WindowDelegate* window_delegate) {
- WindowWin* window = new WindowWin(window_delegate);
- window->GetNonClientView()->SetFrameView(window->CreateFrameViewForWindow());
- window->Init(parent, bounds);
- return window;
-}
-
-gfx::Rect WindowWin::GetBounds() const {
- gfx::Rect bounds;
- WidgetWin::GetBounds(&bounds, true);
- return bounds;
-}
-
-void WindowWin::SetBounds(const gfx::Rect& bounds) {
- SetBounds(bounds, NULL);
-}
-
-void WindowWin::SetBounds(const gfx::Rect& bounds,
- gfx::NativeWindow other_window) {
- win_util::SetChildBounds(GetNativeView(), GetParent(), other_window, bounds,
- kMonitorEdgePadding, 0);
-}
-
-void WindowWin::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)
- Activate();
-
- SetInitialFocus();
-}
-
-int WindowWin::GetShowState() const {
- return SW_SHOWNORMAL;
-}
-
-void WindowWin::ExecuteSystemMenuCommand(int command) {
- if (command)
- SendMessage(GetNativeView(), WM_SYSCOMMAND, command, 0);
-}
-
-// static
-int Window::GetLocalizedContentsWidth(int col_resource_id) {
- double chars = _wtof(l10n_util::GetString(col_resource_id).c_str());
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- ChromeFont font = rb.GetFont(ResourceBundle::BaseFont);
- int width = font.GetExpectedTextWidth(static_cast<int>(chars));
- DCHECK(width > 0);
- return width;
-}
-
-// static
-int Window::GetLocalizedContentsHeight(int row_resource_id) {
- double lines = _wtof(l10n_util::GetString(row_resource_id).c_str());
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- ChromeFont font = rb.GetFont(ResourceBundle::BaseFont);
- int height = static_cast<int>(font.height() * lines);
- DCHECK(height > 0);
- return height;
-}
-
-// static
-gfx::Size Window::GetLocalizedContentsSize(int col_resource_id,
- int row_resource_id) {
- return gfx::Size(GetLocalizedContentsWidth(col_resource_id),
- GetLocalizedContentsHeight(row_resource_id));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// WindowWin, Window implementation:
-
-void WindowWin::Show() {
- int show_state = GetShowState();
- if (saved_maximized_state_)
- show_state = SW_SHOWMAXIMIZED;
- Show(show_state);
-}
-
-void WindowWin::Activate() {
- if (IsMinimized())
- ::ShowWindow(GetNativeView(), SW_RESTORE);
- ::SetWindowPos(GetNativeView(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
- SetForegroundWindow(GetNativeView());
-}
-
-void WindowWin::Close() {
- if (window_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. I'm short circuiting this code path to avoid
- // calling into the delegate twice, which is problematic.
- return;
- }
-
- if (non_client_view_->CanClose()) {
- SaveWindowPosition();
- RestoreEnabledIfNecessary();
- WidgetWin::Close();
- // If the user activates another app after opening us, then comes back and
- // closes us, we want our owner to gain activation. But only if the owner
- // is visible. If we don't manually force that here, the other app will
- // regain activation instead.
- if (owning_hwnd_ && GetNativeView() == GetForegroundWindow() &&
- IsWindowVisible(owning_hwnd_)) {
- SetForegroundWindow(owning_hwnd_);
- }
- window_closed_ = true;
- }
-}
-
-void WindowWin::Maximize() {
- ExecuteSystemMenuCommand(SC_MAXIMIZE);
-}
-
-void WindowWin::Minimize() {
- ExecuteSystemMenuCommand(SC_MINIMIZE);
-}
-
-void WindowWin::Restore() {
- ExecuteSystemMenuCommand(SC_RESTORE);
-}
-
-bool WindowWin::IsActive() const {
- return is_active_;
-}
-
-bool WindowWin::IsVisible() const {
- return !!::IsWindowVisible(GetNativeView());
-}
-
-bool WindowWin::IsMaximized() const {
- return !!::IsZoomed(GetNativeView());
-}
-
-bool WindowWin::IsMinimized() const {
- return !!::IsIconic(GetNativeView());
-}
-
-void WindowWin::EnableClose(bool enable) {
- // If the native frame is rendering its own close button, ask it to disable.
- non_client_view_->EnableClose(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 ? MF_ENABLED : MF_GRAYED);
-
- // Let the window know the frame changed.
- SetWindowPos(NULL, 0, 0, 0, 0,
- SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS |
- SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREPOSITION |
- SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER);
-}
-
-void WindowWin::DisableInactiveRendering() {
- disable_inactive_rendering_ = true;
- non_client_view_->DisableInactiveRendering(disable_inactive_rendering_);
-}
-
-void WindowWin::UpdateWindowTitle() {
- // 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.
- std::wstring window_title = window_delegate_->GetWindowTitle();
- std::wstring localized_text;
- if (l10n_util::AdjustStringForLocaleDirection(window_title, &localized_text))
- window_title.assign(localized_text);
- SetWindowText(GetNativeView(), window_title.c_str());
-}
-
-void WindowWin::UpdateWindowIcon() {
- // If the non-client view is rendering its own icon, we need to tell it to
- // repaint.
- non_client_view_->SchedulePaint();
-
- // Update the native frame's icon. We do this regardless of whether or not
- // the native frame is being used, since this also updates the taskbar, etc.
- SkBitmap icon = window_delegate_->GetWindowIcon();
- if (!icon.isNull()) {
- HICON windows_icon = IconUtil::CreateHICONFromSkBitmap(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);
- old_icon = reinterpret_cast<HICON>(
- SendMessage(GetNativeView(), WM_SETICON, ICON_BIG,
- reinterpret_cast<LPARAM>(windows_icon)));
- if (old_icon)
- DestroyIcon(old_icon);
- }
-}
-
-NonClientFrameView* WindowWin::CreateFrameViewForWindow() {
- if (non_client_view_->UseNativeFrame())
- return new NativeFrameView(this);
- return new CustomFrameView(this);
-}
-
-void WindowWin::UpdateFrameAfterFrameChange() {
- // We've either gained or lost a custom window region, so reset it now.
- ResetWindowRegion(true);
-}
-
-WindowDelegate* WindowWin::GetDelegate() const {
- return window_delegate_;
-}
-
-NonClientView* WindowWin::GetNonClientView() const {
- return non_client_view_;
-}
-
-ClientView* WindowWin::GetClientView() const {
- return non_client_view_->client_view();
-}
-
-gfx::NativeWindow WindowWin::GetNativeWindow() const {
- return GetNativeView();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// WindowWin, NotificationObserver implementation:
-
-void WindowWin::Observe(NotificationType type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- // This window is closed when the last app window is closed.
- DCHECK(type == NotificationType::ALL_APPWINDOWS_CLOSED);
- // Only registered as an observer when we're not an app window.
- // XXX DCHECK(!IsAppWindow());
- Close();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// WindowWin, protected:
-
-WindowWin::WindowWin(WindowDelegate* window_delegate)
- : WidgetWin(),
- focus_on_creation_(true),
- window_delegate_(window_delegate),
- non_client_view_(new NonClientView(this)),
- owning_hwnd_(NULL),
- minimum_size_(100, 100),
- is_modal_(false),
- restored_enabled_(false),
- is_always_on_top_(false),
- window_closed_(false),
- disable_inactive_rendering_(false),
- is_active_(false),
- lock_updates_(false),
- saved_window_style_(0),
- saved_maximized_state_(0),
- force_hidden_(false) {
- InitClass();
- DCHECK(window_delegate_);
- window_delegate_->window_.reset(this);
- // Initialize these values to 0 so that subclasses can override the default
- // behavior before calling Init.
- set_window_style(0);
- set_window_ex_style(0);
-}
-
-void WindowWin::Init(HWND parent, const gfx::Rect& bounds) {
- // We need to save the parent window, since later calls to GetParent() will
- // return NULL.
- owning_hwnd_ = parent;
- // We call this after initializing our members since our implementations of
- // assorted WidgetWin functions may be called during initialization.
- is_modal_ = window_delegate_->IsModal();
- if (is_modal_)
- BecomeModal();
- is_always_on_top_ = window_delegate_->IsAlwaysOnTop();
-
- if (window_style() == 0)
- set_window_style(CalculateWindowStyle());
- if (window_ex_style() == 0)
- set_window_ex_style(CalculateWindowExStyle());
-
- WidgetWin::Init(parent, bounds, true);
- win_util::SetWindowUserData(GetNativeView(), this);
-
- // 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(window_delegate_->CreateClientView(this));
- WidgetWin::SetContentsView(non_client_view_);
-
- UpdateWindowTitle();
-
- SetInitialBounds(bounds);
- InitAlwaysOnTopState();
-
- if (!IsAppWindow()) {
- notification_registrar_.Add(
- this,
- NotificationType::ALL_APPWINDOWS_CLOSED,
- NotificationService::AllSources());
- }
-
- ResetWindowRegion(false);
-}
-
-void WindowWin::SizeWindowToDefault() {
- win_util::CenterAndSizeWindow(owning_window(), GetNativeView(),
- non_client_view_->GetPreferredSize().ToSIZE(),
- false);
-}
-
-void WindowWin::RunSystemMenu(const gfx::Point& point) {
- // We need to reset and clean up any currently created system menu objects.
- // We need to call this otherwise there's a small chance that we aren't going
- // to get a system menu. We also can't take the return value of this
- // function. We need to call it *again* to get a valid HMENU.
- //::GetSystemMenu(GetNativeView(), TRUE);
- HMENU system_menu = ::GetSystemMenu(GetNativeView(), FALSE);
- int id = ::TrackPopupMenu(system_menu,
- TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD,
- point.x(), point.y(), 0, GetNativeView(), NULL);
- ExecuteSystemMenuCommand(id);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// WindowWin, WidgetWin overrides:
-
-void WindowWin::OnActivate(UINT action, BOOL minimized, HWND window) {
- if (action == WA_INACTIVE)
- SaveWindowPosition();
-}
-
-void WindowWin::OnActivateApp(BOOL active, DWORD thread_id) {
- if (!active && thread_id != GetCurrentThreadId()) {
- // Another application was activated, we should reset any state that
- // disables inactive rendering now.
- disable_inactive_rendering_ = false;
- non_client_view_->DisableInactiveRendering(false);
- // Update the native frame too, since it could be rendering the non-client
- // area.
- CallDefaultNCActivateHandler(FALSE);
- }
-}
-
-LRESULT WindowWin::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...
- if (!window_delegate_->ExecuteWindowsCommand(app_command))
- return WidgetWin::OnAppCommand(window, app_command, device, keystate);
- return 0;
-}
-
-void WindowWin::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 ||
- window_delegate_->ExecuteWindowsCommand(command_id)) {
- WidgetWin::OnCommand(notification_code, command_id, window);
- }
-}
-
-void WindowWin::OnDestroy() {
- non_client_view_->WindowClosing();
- RestoreEnabledIfNecessary();
- WidgetWin::OnDestroy();
-}
-
-namespace {
-static BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) {
- SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0);
- return TRUE;
-}
-} // namespace
-
-LRESULT WindowWin::OnDwmCompositionChanged(UINT msg, WPARAM w_param,
- LPARAM l_param) {
- // The window may try to paint in SetUseNativeFrame, and as a result it can
- // get into a state where it is very unhappy with itself - rendering black
- // behind the entire client area. This is because for some reason the
- // SkPorterDuff::kClear_mode erase done in the RootView thinks the window is
- // still opaque. So, to work around this we hide the window as soon as we can
- // (now), saving off its placement so it can be properly restored once
- // everything has settled down.
- WINDOWPLACEMENT saved_window_placement;
- saved_window_placement.length = sizeof(WINDOWPLACEMENT);
- GetWindowPlacement(GetNativeView(), &saved_window_placement);
- Hide();
-
- // Important step: restore the window first, since our hiding hack doesn't
- // work for maximized windows! We tell the frame not to allow itself to be
- // made visible though, which removes the brief flicker.
- force_hidden_ = true;
- ::ShowWindow(GetNativeView(), SW_RESTORE);
- force_hidden_ = false;
-
- // We respond to this in response to WM_DWMCOMPOSITIONCHANGED since that is
- // the only thing we care about - we don't actually respond to WM_THEMECHANGED
- // messages.
- non_client_view_->SetUseNativeFrame(win_util::ShouldUseVistaFrame());
-
- // Now that we've updated the frame, we'll want to restore our saved placement
- // since the display should have settled down and we can be properly rendered.
- SetWindowPlacement(GetNativeView(), &saved_window_placement);
-
- // 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);
- return 0;
-}
-
-void WindowWin::OnFinalMessage(HWND window) {
- // Delete and NULL the delegate here once we're guaranteed to get no more
- // messages.
- window_delegate_->DeleteDelegate();
- window_delegate_ = NULL;
- WidgetWin::OnFinalMessage(window);
-}
-
-namespace {
-static void EnableMenuItem(HMENU menu, UINT command, bool enabled) {
- UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
- EnableMenuItem(menu, command, flags);
-}
-} // namespace
-
-void WindowWin::OnInitMenu(HMENU menu) {
- // We only need to manually enable the system menu if we're not using a native
- // frame.
- if (non_client_view_->UseNativeFrame())
- WidgetWin::OnInitMenu(menu);
-
- bool is_minimized = IsMinimized();
- bool is_maximized = IsMaximized();
- bool is_restored = !is_minimized && !is_maximized;
-
- ScopedRedrawLock lock(this);
- EnableMenuItem(menu, SC_RESTORE, !is_restored);
- EnableMenuItem(menu, SC_MOVE, is_restored);
- EnableMenuItem(menu, SC_SIZE, window_delegate_->CanResize() && is_restored);
- EnableMenuItem(menu, SC_MAXIMIZE,
- window_delegate_->CanMaximize() && !is_maximized);
- EnableMenuItem(menu, SC_MINIMIZE,
- window_delegate_->CanMaximize() && !is_minimized);
-}
-
-void WindowWin::OnMouseLeave() {
- // We only need to manually track WM_MOUSELEAVE messages between the client
- // and non-client area when we're not using the native frame.
- if (non_client_view_->UseNativeFrame()) {
- SetMsgHandled(FALSE);
- return;
- }
-
- bool process_mouse_exited = true;
- POINT pt;
- if (GetCursorPos(&pt)) {
- LRESULT ht_component =
- ::SendMessage(GetNativeView(), WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y));
- if (ht_component != HTNOWHERE) {
- // If the mouse moved into a part of the window's non-client area, then
- // don't send a mouse exited event since the mouse is still within the
- // bounds of the ChromeView that's rendering the frame. Note that we do
- // _NOT_ do this for windows with native frames, since in that case the
- // mouse really will have left the bounds of the RootView.
- process_mouse_exited = false;
- }
- }
-
- if (process_mouse_exited)
- ProcessMouseExited();
-}
-
-LRESULT WindowWin::OnNCActivate(BOOL active) {
- is_active_ = !!active;
-
- // If we're not using the native frame, we need to force a synchronous repaint
- // otherwise we'll be left in the wrong activation state until something else
- // causes a repaint later.
- if (!non_client_view_->UseNativeFrame()) {
- // We can get WM_NCACTIVATE before we're actually visible. If we're not
- // visible, no need to paint.
- if (IsWindowVisible(GetNativeView())) {
- non_client_view_->SchedulePaint();
- // We need to force a paint now, as a user dragging a window will block
- // painting operations while the move is in progress.
- PaintNow(root_view_->GetScheduledPaintRect());
- }
- }
-
- // If we're active again, we should be allowed to render as inactive, so
- // tell the non-client view. This must be done independently of the check for
- // disable_inactive_rendering_ since that check is valid even if the frame
- // is not active, but this can only be done if we've become active.
- if (IsActive())
- non_client_view_->DisableInactiveRendering(false);
-
- // Reset the disable inactive rendering state since activation has changed.
- if (disable_inactive_rendering_) {
- disable_inactive_rendering_ = false;
- return CallDefaultNCActivateHandler(TRUE);
- }
- return CallDefaultNCActivateHandler(active);
-}
-
-LRESULT WindowWin::OnNCCalcSize(BOOL mode, LPARAM l_param) {
- // We only need to adjust the client size/paint handling when we're not using
- // the native frame.
- if (non_client_view_->UseNativeFrame())
- return WidgetWin::OnNCCalcSize(mode, l_param);
-
- RECT* client_rect = mode ?
- &reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0] :
- reinterpret_cast<RECT*>(l_param);
- if (IsMaximized()) {
- // Make the maximized mode client rect fit the screen exactly, by
- // subtracting the border Windows automatically adds for maximized mode.
- int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
- InflateRect(client_rect, -border_thickness, -border_thickness);
-
- // 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_DEFAULTTONEAREST);
- if (win_util::EdgeHasAutoHideTaskbar(ABE_LEFT, monitor))
- client_rect->left += win_util::kAutoHideTaskbarThicknessPx;
- if (win_util::EdgeHasAutoHideTaskbar(ABE_TOP, monitor))
- client_rect->top += win_util::kAutoHideTaskbarThicknessPx;
- if (win_util::EdgeHasAutoHideTaskbar(ABE_RIGHT, monitor))
- client_rect->right -= win_util::kAutoHideTaskbarThicknessPx;
- if (win_util::EdgeHasAutoHideTaskbar(ABE_BOTTOM, monitor))
- client_rect->bottom -= win_util::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.
- return mode ? WVR_REDRAW : 0;
-}
-
-LRESULT WindowWin::OnNCHitTest(const CPoint& point) {
- // First, give the NonClientView a chance to test the point to see if it
- // provides any of the non-client area.
- CPoint temp = point;
- MapWindowPoints(HWND_DESKTOP, GetNativeView(), &temp, 1);
- int component = non_client_view_->NonClientHitTest(gfx::Point(temp));
- if (component != HTNOWHERE)
- return component;
-
- // Otherwise, we let Windows do all the native frame non-client handling for
- // us.
- return WidgetWin::OnNCHitTest(point);
-}
-
-namespace {
-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;
-}
-} // namespace
-
-void WindowWin::OnNCPaint(HRGN rgn) {
- // We only do non-client painting if we're not using the native frame.
- if (non_client_view_->UseNativeFrame()) {
- WidgetWin::OnNCPaint(rgn);
- 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() != root_view_->width() ||
- window_rect.Height() != root_view_->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));
-
- RootView* root_view = GetRootView();
- CRect old_paint_region = root_view->GetScheduledPaintRectConstrainedToSize();
-
- if (!old_paint_region.IsRectEmpty()) {
- // The root view has a region that needs to be painted. Include it in the
- // region we're going to paint.
-
- CRect tmp = dirty_region;
- UnionRect(&dirty_region, &tmp, &old_paint_region);
- }
-
- root_view->SchedulePaint(gfx::Rect(dirty_region), false);
-
- // ChromeCanvasPaints 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.
- {
- ChromeCanvasPaint canvas(dc, opaque(), dirty_region.left, dirty_region.top,
- dirty_region.Width(), dirty_region.Height());
-
- root_view->ProcessPaint(&canvas);
- }
-
- ReleaseDC(GetNativeView(), dc);
-}
-
-void WindowWin::OnNCLButtonDown(UINT ht_component, const CPoint& point) {
- // When we're using a native frame, window controls work without us
- // interfering.
- if (!non_client_view_->UseNativeFrame()) {
- switch (ht_component) {
- 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.
- ProcessNCMousePress(point, MK_LBUTTON);
- return;
- }
- }
- }
-
- // TODO(beng): figure out why we need to run the system menu manually
- // ourselves. This is wrong and causes many subtle bugs.
- // From my initial research, it looks like DefWindowProc tries
- // to run it but fails before sending the initial WM_MENUSELECT
- // for the sysmenu.
- if (ht_component == HTSYSMENU)
- RunSystemMenu(non_client_view_->GetSystemMenuPoint());
- else
- WidgetWin::OnNCLButtonDown(ht_component, point);
-
- /* TODO(beng): Fix the standard non-client over-painting bug. This code
- doesn't work but identifies the problem.
- if (!IsMsgHandled()) {
- // WindowWin::OnNCLButtonDown set the message as unhandled. This normally
- // means WidgetWin::ProcessWindowMessage will pass it to
- // DefWindowProc. Sadly, DefWindowProc for WM_NCLBUTTONDOWN does weird
- // non-client painting, so we need to call it directly here inside a
- // scoped update lock.
- ScopedRedrawLock lock(this);
- DefWindowProc(GetNativeView(), WM_NCLBUTTONDOWN, ht_component,
- MAKELPARAM(point.x, point.y));
- SetMsgHandled(TRUE);
- }
- */
-}
-
-void WindowWin::OnNCRButtonDown(UINT ht_component, const CPoint& point) {
- if (ht_component == HTCAPTION || ht_component == HTSYSMENU)
- RunSystemMenu(gfx::Point(point));
- else
- WidgetWin::OnNCRButtonDown(ht_component, point);
-}
-
-LRESULT WindowWin::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(!non_client_view_->UseNativeFrame());
- return 0;
-}
-
-LRESULT WindowWin::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(!non_client_view_->UseNativeFrame());
- return 0;
-}
-
-LRESULT WindowWin::OnSetCursor(HWND window, UINT hittest_code, UINT message) {
- // If the window is disabled, it's because we're showing a modal dialog box.
- // We need to let DefWindowProc handle the message. That's because
- // DefWindowProc for WM_SETCURSOR with message = some kind of mouse button
- // down message sends the top level window a WM_ACTIVATEAPP message, which we
- // otherwise wouldn't get. The symptom of not doing this is that if the user
- // has a window in the background with a modal dialog open, they can't click
- // on the disabled background window to bring the entire stack to the front.
- // This is annoying because they then have to move all the foreground windows
- // out of the way to be able to activate said window. I love how on Windows,
- // the answer isn't always logical.
- if (!IsWindowEnabled(GetNativeView()))
- return WidgetWin::OnSetCursor(window, hittest_code, message);
-
- int index = RC_NORMAL;
- switch (hittest_code) {
- case HTTOP:
- case HTBOTTOM:
- index = RC_VERTICAL;
- break;
- case HTTOPLEFT:
- case HTBOTTOMRIGHT:
- index = RC_NWSE;
- break;
- case HTTOPRIGHT:
- case HTBOTTOMLEFT:
- index = RC_NESW;
- break;
- case HTLEFT:
- case HTRIGHT:
- index = RC_HORIZONTAL;
- break;
- case HTCAPTION:
- case HTCLIENT:
- index = RC_NORMAL;
- break;
- }
- SetCursor(resize_cursors_[index]);
- return 0;
-}
-
-LRESULT WindowWin::OnSetIcon(UINT size_type, HICON new_icon) {
- // This shouldn't hurt even if we're using the native frame.
- ScopedRedrawLock lock(this);
- return DefWindowProc(GetNativeView(), WM_SETICON, size_type,
- reinterpret_cast<LPARAM>(new_icon));
-}
-
-LRESULT WindowWin::OnSetText(const wchar_t* text) {
- // This shouldn't hurt even if we're using the native frame.
- ScopedRedrawLock lock(this);
- return DefWindowProc(GetNativeView(), WM_SETTEXT, NULL,
- reinterpret_cast<LPARAM>(text));
-}
-
-void WindowWin::OnSize(UINT size_param, const CSize& new_size) {
- // Don't no-op if the new_size matches current size. If our normal bounds
- // and maximized bounds are the same, then we need to layout (because we
- // layout differently when maximized).
- SaveWindowPosition();
- ChangeSize(size_param, new_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 WindowWin::OnSysCommand(UINT notification_code, CPoint click) {
- if (!non_client_view_->UseNativeFrame()) {
- // 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;
- if ((notification_code & sc_mask) == SC_MINIMIZE ||
- (notification_code & sc_mask) == SC_MAXIMIZE ||
- (notification_code & sc_mask) == SC_RESTORE) {
- non_client_view_->ResetWindowControls();
- } else if ((notification_code & sc_mask) == SC_MOVE ||
- (notification_code & sc_mask) == SC_SIZE) {
- if (lock_updates_) {
- // We were locked, before entering a resize or move modal loop. Now that
- // we've begun to move the window, we need to unlock updates so that the
- // sizing/moving feedback can be continuous.
- UnlockUpdates();
- }
- }
- }
-
- // First see if the delegate can handle it.
- if (window_delegate_->ExecuteWindowsCommand(notification_code))
- return;
-
- if (notification_code == IDC_ALWAYS_ON_TOP) {
- is_always_on_top_ = !is_always_on_top_;
-
- // Change the menu check state.
- HMENU system_menu = GetSystemMenu(GetNativeView(), FALSE);
- MENUITEMINFO menu_info;
- memset(&menu_info, 0, sizeof(MENUITEMINFO));
- menu_info.cbSize = sizeof(MENUITEMINFO);
- BOOL r = GetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP,
- FALSE, &menu_info);
- DCHECK(r);
- menu_info.fMask = MIIM_STATE;
- if (is_always_on_top_)
- menu_info.fState = MFS_CHECKED;
- r = SetMenuItemInfo(system_menu, IDC_ALWAYS_ON_TOP, FALSE, &menu_info);
-
- // Now change the actual window's behavior.
- AlwaysOnTopChanged();
- } else if ((notification_code == SC_KEYMENU) && (click.x == VK_SPACE)) {
- // Run the system menu at the NonClientView's desired location.
- RunSystemMenu(non_client_view_->GetSystemMenuPoint());
- } else {
- // Use the default implementation for any other command.
- DefWindowProc(GetNativeView(), WM_SYSCOMMAND, notification_code,
- MAKELPARAM(click.y, click.x));
- }
-}
-
-void WindowWin::OnWindowPosChanging(WINDOWPOS* window_pos) {
- if (force_hidden_) {
- // 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;
- }
- WidgetWin::OnWindowPosChanging(window_pos);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// WindowWin, private:
-
-void WindowWin::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.
- DCHECK(owning_hwnd_) << "Can't create a modal dialog without an owner";
- HWND start = owning_hwnd_;
- while (start != NULL) {
- ::EnableWindow(start, FALSE);
- start = ::GetParent(start);
- }
-}
-
-void WindowWin::SetInitialFocus() {
- if (!focus_on_creation_)
- return;
-
- View* v = window_delegate_->GetInitiallyFocusedView();
- if (v) {
- v->RequestFocus();
- } else {
- // The window does not get keyboard messages unless we focus it, not sure
- // why.
- SetFocus(GetNativeView());
- }
-}
-
-void WindowWin::SetInitialBounds(const gfx::Rect& create_bounds) {
- // 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.
- window_delegate_->GetSavedMaximizedState(&saved_maximized_state_);
-
- // Restore the window's placement from the controller.
- gfx::Rect saved_bounds(create_bounds.ToRECT());
- if (window_delegate_->GetSavedWindowBounds(&saved_bounds)) {
- // Make sure the bounds are at least the minimum size.
- if (saved_bounds.width() < minimum_size_.cx) {
- saved_bounds.SetRect(saved_bounds.x(), saved_bounds.y(),
- saved_bounds.right() + minimum_size_.cx -
- saved_bounds.width(),
- saved_bounds.bottom());
- }
-
- if (saved_bounds.height() < minimum_size_.cy) {
- saved_bounds.SetRect(saved_bounds.x(), saved_bounds.y(),
- saved_bounds.right(),
- saved_bounds.bottom() + minimum_size_.cy -
- saved_bounds.height());
- }
-
- // "Show state" (maximized, minimized, etc) is handled by Show().
- // Don't use SetBounds here. SetBounds constrains to the size of the
- // monitor, but we don't want that when creating a new window as the result
- // of dragging out a tab to create a new window.
- SetWindowPos(NULL, saved_bounds.x(), saved_bounds.y(),
- saved_bounds.width(), saved_bounds.height(), 0);
- } else {
- if (create_bounds.IsEmpty()) {
- // No initial bounds supplied, so size the window to its content and
- // center over its parent.
- SizeWindowToDefault();
- } else {
- // Use the supplied initial bounds.
- SetBounds(create_bounds);
- }
- }
-}
-
-void WindowWin::InitAlwaysOnTopState() {
- is_always_on_top_ = false;
- if (window_delegate_->GetSavedAlwaysOnTopState(&is_always_on_top_) &&
- is_always_on_top_ != window_delegate_->IsAlwaysOnTop()) {
- AlwaysOnTopChanged();
- }
-
- if (window_delegate_->HasAlwaysOnTopMenu())
- AddAlwaysOnTopSystemMenuItem();
-}
-
-void WindowWin::AddAlwaysOnTopSystemMenuItem() {
- // The Win32 API requires that we own the text.
- always_on_top_menu_text_ = l10n_util::GetString(IDS_ALWAYS_ON_TOP);
-
- // Let's insert a menu to the window.
- HMENU system_menu = ::GetSystemMenu(GetNativeView(), FALSE);
- int index = ::GetMenuItemCount(system_menu) - 1;
- if (index < 0) {
- // Paranoia check.
- NOTREACHED();
- index = 0;
- }
- // First we add the separator.
- MENUITEMINFO menu_info;
- memset(&menu_info, 0, sizeof(MENUITEMINFO));
- menu_info.cbSize = sizeof(MENUITEMINFO);
- menu_info.fMask = MIIM_FTYPE;
- menu_info.fType = MFT_SEPARATOR;
- ::InsertMenuItem(system_menu, index, TRUE, &menu_info);
-
- // Then the actual menu.
- menu_info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_STATE;
- menu_info.fType = MFT_STRING;
- menu_info.fState = MFS_ENABLED;
- if (is_always_on_top_)
- menu_info.fState |= MFS_CHECKED;
- menu_info.wID = IDC_ALWAYS_ON_TOP;
- menu_info.dwTypeData = const_cast<wchar_t*>(always_on_top_menu_text_.c_str());
- ::InsertMenuItem(system_menu, index, TRUE, &menu_info);
-}
-
-void WindowWin::RestoreEnabledIfNecessary() {
- if (is_modal_ && !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 = owning_hwnd_;
- while (start != NULL) {
- ::EnableWindow(start, TRUE);
- start = ::GetParent(start);
- }
- }
-}
-
-void WindowWin::AlwaysOnTopChanged() {
- ::SetWindowPos(GetNativeView(),
- is_always_on_top_ ? HWND_TOPMOST : HWND_NOTOPMOST,
- 0, 0, 0, 0,
- SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
-}
-
-DWORD WindowWin::CalculateWindowStyle() {
- DWORD window_styles =
- WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_SYSMENU | WS_CAPTION;
- bool can_resize = window_delegate_->CanResize();
- bool can_maximize = window_delegate_->CanMaximize();
- if (can_maximize) {
- window_styles |= WS_OVERLAPPEDWINDOW;
- } else if (can_resize) {
- window_styles |= WS_OVERLAPPED | WS_THICKFRAME;
- }
- if (window_delegate_->AsDialogDelegate()) {
- window_styles |= 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.
- // window_styles &= ~WS_SYSMENU;
- }
- return window_styles;
-}
-
-DWORD WindowWin::CalculateWindowExStyle() {
- DWORD window_ex_styles = 0;
- if (window_delegate_->AsDialogDelegate())
- window_ex_styles |= WS_EX_DLGMODALFRAME;
- if (window_delegate_->IsAlwaysOnTop())
- window_ex_styles |= WS_EX_TOPMOST;
- return window_ex_styles;
-}
-
-void WindowWin::SaveWindowPosition() {
- // 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 (!window_delegate_)
- return;
-
- WINDOWPLACEMENT win_placement = { 0 };
- win_placement.length = sizeof(WINDOWPLACEMENT);
-
- BOOL r = GetWindowPlacement(GetNativeView(), &win_placement);
- DCHECK(r);
-
- bool maximized = (win_placement.showCmd == SW_SHOWMAXIMIZED);
- CRect window_bounds(win_placement.rcNormalPosition);
- window_delegate_->SaveWindowPlacement(
- gfx::Rect(win_placement.rcNormalPosition), maximized, is_always_on_top_);
-}
-
-void WindowWin::LockUpdates() {
- lock_updates_ = true;
- saved_window_style_ = GetWindowLong(GetNativeView(), GWL_STYLE);
- SetWindowLong(GetNativeView(), GWL_STYLE, saved_window_style_ & ~WS_VISIBLE);
-}
-
-void WindowWin::UnlockUpdates() {
- SetWindowLong(GetNativeView(), GWL_STYLE, saved_window_style_);
- lock_updates_ = false;
-}
-
-void WindowWin::ResetWindowRegion(bool force) {
- // A native frame uses the native window region, and we don't want to mess
- // with it.
- if (non_client_view_->UseNativeFrame()) {
- 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;
- gfx::Path window_mask;
- non_client_view_->GetWindowMask(
- gfx::Size(window_rect.Width(), window_rect.Height()), &window_mask);
- new_region = window_mask.CreateHRGN();
-
- if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) {
- // SetWindowRgn takes ownership of the HRGN created by CreateHRGN.
- SetWindowRgn(new_region, TRUE);
- } else {
- DeleteObject(new_region);
- }
-
- DeleteObject(current_rgn);
-}
-
-void WindowWin::ProcessNCMousePress(const CPoint& point, int flags) {
- CPoint temp = point;
- MapWindowPoints(HWND_DESKTOP, GetNativeView(), &temp, 1);
- UINT message_flags = 0;
- if ((GetKeyState(VK_CONTROL) & 0x80) == 0x80)
- message_flags |= MK_CONTROL;
- if ((GetKeyState(VK_SHIFT) & 0x80) == 0x80)
- message_flags |= MK_SHIFT;
- message_flags |= flags;
- ProcessMousePressed(temp, message_flags, false);
-}
-
-LRESULT WindowWin::CallDefaultNCActivateHandler(BOOL active) {
- // The DefWindowProc handling for WM_NCACTIVATE renders the classic-look
- // window title bar directly, so we need to use a redraw lock here to prevent
- // it from doing so.
- ScopedRedrawLock lock(this);
- return DefWindowProc(GetNativeView(), WM_NCACTIVATE, active, 0);
-}
-
-void WindowWin::InitClass() {
- static bool initialized = false;
- if (!initialized) {
- resize_cursors_[RC_NORMAL] = LoadCursor(NULL, IDC_ARROW);
- resize_cursors_[RC_VERTICAL] = LoadCursor(NULL, IDC_SIZENS);
- resize_cursors_[RC_HORIZONTAL] = LoadCursor(NULL, IDC_SIZEWE);
- resize_cursors_[RC_NESW] = LoadCursor(NULL, IDC_SIZENESW);
- resize_cursors_[RC_NWSE] = LoadCursor(NULL, IDC_SIZENWSE);
- initialized = true;
- }
-}
-
-} // namespace views