diff options
Diffstat (limited to 'chrome/views/custom_frame_window.cc')
-rw-r--r-- | chrome/views/custom_frame_window.cc | 1255 |
1 files changed, 1255 insertions, 0 deletions
diff --git a/chrome/views/custom_frame_window.cc b/chrome/views/custom_frame_window.cc new file mode 100644 index 0000000..02af912 --- /dev/null +++ b/chrome/views/custom_frame_window.cc @@ -0,0 +1,1255 @@ +// 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/custom_frame_window.h" + +#include "base/gfx/point.h" +#include "base/gfx/size.h" +#include "chrome/app/theme/theme_resources.h" +#include "chrome/common/gfx/path.h" +#include "chrome/common/gfx/chrome_canvas.h" +#include "chrome/common/gfx/chrome_font.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/win_util.h" +#include "chrome/views/button.h" +#include "chrome/views/client_view.h" +#include "chrome/views/native_button.h" +#include "chrome/views/window_delegate.h" +#include "generated_resources.h" + +namespace ChromeViews { + +HCURSOR CustomFrameWindow::resize_cursors_[6]; + +//////////////////////////////////////////////////////////////////////////////// +// WindowResources +// +// An enumeration of bitmap resources used by this window. +enum FramePartBitmap { + FRAME_PART_BITMAP_FIRST = 0, // must be first. + + // Window Controls. + FRAME_CLOSE_BUTTON_ICON, + FRAME_CLOSE_BUTTON_ICON_H, + FRAME_CLOSE_BUTTON_ICON_P, + FRAME_CLOSE_BUTTON_ICON_SA, + FRAME_CLOSE_BUTTON_ICON_SA_H, + FRAME_CLOSE_BUTTON_ICON_SA_P, + FRAME_RESTORE_BUTTON_ICON, + FRAME_RESTORE_BUTTON_ICON_H, + FRAME_RESTORE_BUTTON_ICON_P, + FRAME_MAXIMIZE_BUTTON_ICON, + FRAME_MAXIMIZE_BUTTON_ICON_H, + FRAME_MAXIMIZE_BUTTON_ICON_P, + FRAME_MINIMIZE_BUTTON_ICON, + FRAME_MINIMIZE_BUTTON_ICON_H, + FRAME_MINIMIZE_BUTTON_ICON_P, + + // Window Frame Border. + FRAME_BOTTOM_EDGE, + FRAME_BOTTOM_LEFT_CORNER, + FRAME_BOTTOM_RIGHT_CORNER, + FRAME_LEFT_EDGE, + FRAME_RIGHT_EDGE, + FRAME_TOP_EDGE, + FRAME_TOP_LEFT_CORNER, + FRAME_TOP_RIGHT_CORNER, + + // Client Edge Border. + FRAME_CLIENT_EDGE_TOP_LEFT, + FRAME_CLIENT_EDGE_TOP, + FRAME_CLIENT_EDGE_TOP_RIGHT, + FRAME_CLIENT_EDGE_RIGHT, + FRAME_CLIENT_EDGE_BOTTOM_RIGHT, + FRAME_CLIENT_EDGE_BOTTOM, + FRAME_CLIENT_EDGE_BOTTOM_LEFT, + FRAME_CLIENT_EDGE_LEFT, + + FRAME_PART_BITMAP_COUNT // Must be last. +}; + +class WindowResources { + public: + virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const = 0; + virtual const ChromeFont& GetTitleFont() const = 0; + SkColor title_color() const { return SK_ColorWHITE; } +}; + +class ActiveWindowResources : public WindowResources { + public: + ActiveWindowResources() { + InitClass(); + } + virtual ~ActiveWindowResources() { + } + + // WindowResources implementation: + virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const { + return standard_frame_bitmaps_[part]; + } + virtual const ChromeFont& GetTitleFont() const { + return title_font_; + } + + private: + static void InitClass() { + static bool initialized = false; + if (!initialized) { + static const int kFramePartBitmapIds[] = { + 0, + IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, + IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, + IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, + IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, + IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, + IDR_WINDOW_BOTTOM_CENTER, IDR_WINDOW_BOTTOM_LEFT_CORNER, + IDR_WINDOW_BOTTOM_RIGHT_CORNER, IDR_WINDOW_LEFT_SIDE, + IDR_WINDOW_RIGHT_SIDE, IDR_WINDOW_TOP_CENTER, + IDR_WINDOW_TOP_LEFT_CORNER, IDR_WINDOW_TOP_RIGHT_CORNER, + IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, + IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER, + IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER, + IDR_CONTENT_LEFT_SIDE, + 0 + }; + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) { + int id = kFramePartBitmapIds[i]; + if (id != 0) + standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id); + } + title_font_ = + rb.GetFont(ResourceBundle::BaseFont).DeriveFont(1, ChromeFont::BOLD); + initialized = true; + } + } + + static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; + static ChromeFont title_font_; + + DISALLOW_EVIL_CONSTRUCTORS(ActiveWindowResources); +}; + +class InactiveWindowResources : public WindowResources { + public: + InactiveWindowResources() { + InitClass(); + } + virtual ~InactiveWindowResources() { + } + + // WindowResources implementation: + virtual SkBitmap* GetPartBitmap(FramePartBitmap part) const { + return standard_frame_bitmaps_[part]; + } + virtual const ChromeFont& GetTitleFont() const { + return title_font_; + } + + private: + static void InitClass() { + static bool initialized = false; + if (!initialized) { + static const int kFramePartBitmapIds[] = { + 0, + IDR_CLOSE, IDR_CLOSE_H, IDR_CLOSE_P, + IDR_CLOSE_SA, IDR_CLOSE_SA_H, IDR_CLOSE_SA_P, + IDR_RESTORE, IDR_RESTORE_H, IDR_RESTORE_P, + IDR_MAXIMIZE, IDR_MAXIMIZE_H, IDR_MAXIMIZE_P, + IDR_MINIMIZE, IDR_MINIMIZE_H, IDR_MINIMIZE_P, + IDR_DEWINDOW_BOTTOM_CENTER, IDR_DEWINDOW_BOTTOM_LEFT_CORNER, + IDR_DEWINDOW_BOTTOM_RIGHT_CORNER, IDR_DEWINDOW_LEFT_SIDE, + IDR_DEWINDOW_RIGHT_SIDE, IDR_DEWINDOW_TOP_CENTER, + IDR_DEWINDOW_TOP_LEFT_CORNER, IDR_DEWINDOW_TOP_RIGHT_CORNER, + IDR_APP_TOP_LEFT, IDR_APP_TOP_CENTER, IDR_APP_TOP_RIGHT, + IDR_CONTENT_RIGHT_SIDE, IDR_CONTENT_BOTTOM_RIGHT_CORNER, + IDR_CONTENT_BOTTOM_CENTER, IDR_CONTENT_BOTTOM_LEFT_CORNER, + IDR_CONTENT_LEFT_SIDE, + 0 + }; + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + for (int i = 0; i < FRAME_PART_BITMAP_COUNT; ++i) { + int id = kFramePartBitmapIds[i]; + if (id != 0) + standard_frame_bitmaps_[i] = rb.GetBitmapNamed(id); + } + title_font_ = + rb.GetFont(ResourceBundle::BaseFont).DeriveFont(1, ChromeFont::BOLD); + initialized = true; + } + } + + static SkBitmap* standard_frame_bitmaps_[FRAME_PART_BITMAP_COUNT]; + static ChromeFont title_font_; + + DISALLOW_EVIL_CONSTRUCTORS(InactiveWindowResources); +}; + +// static +SkBitmap* ActiveWindowResources::standard_frame_bitmaps_[]; +ChromeFont ActiveWindowResources::title_font_; +SkBitmap* InactiveWindowResources::standard_frame_bitmaps_[]; +ChromeFont InactiveWindowResources::title_font_; + + +//////////////////////////////////////////////////////////////////////////////// +// +// DefaultNonClientView +// +// A ChromeView that provides the "frame" for CustomFrameWindows. This means +// rendering the non-standard window caption, border, and controls. +// +//////////////////////////////////////////////////////////////////////////////// +class DefaultNonClientView : public CustomFrameWindow::NonClientView, + public BaseButton::ButtonListener { + public: + explicit DefaultNonClientView(CustomFrameWindow* container); + virtual ~DefaultNonClientView(); + + // Overridden from CustomFrameWindow::NonClientView: + virtual void Init(ClientView* client_view); + virtual gfx::Rect CalculateClientAreaBounds(int width, int height) const; + virtual gfx::Size CalculateWindowSizeForClientSize(int width, + int height) const; + virtual CPoint GetSystemMenuPoint() const; + virtual int HitTest(const gfx::Point& point); + virtual void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask); + virtual void EnableClose(bool enable); + + // View overrides: + virtual void Paint(ChromeCanvas* canvas); + virtual void Layout(); + virtual void GetPreferredSize(CSize* out); + + // BaseButton::ButtonListener implementation: + virtual void ButtonPressed(BaseButton* sender); + + private: + // Updates the system menu icon button. + void SetWindowIcon(SkBitmap window_icon); + + // Returns the height of the non-client area at the top of the window (the + // title bar, etc). + int CalculateContentsTop() const; + + // Paint various sub-components of this view. + void PaintFrameBorder(ChromeCanvas* canvas); + void PaintMaximizedFrameBorder(ChromeCanvas* canvas); + void PaintClientEdge(ChromeCanvas* canvas); + + // Layout various sub-components of this view. + void LayoutWindowControls(); + void LayoutTitleBar(); + void LayoutClientView(); + + // Returns the resource collection to be used when rendering the window. + WindowResources* resources() const { + return container_->is_active() ? active_resources_ : inactive_resources_; + } + + // The View that provides the background for the window, and optionally + // dialog buttons. Note: the non-client view does _not_ own this view, the + // container does. + ClientView* client_view_; + + // The layout rect of the title, if visible. + gfx::Rect title_bounds_; + + // Window controls. + Button* close_button_; + Button* restore_button_; + Button* maximize_button_; + Button* minimize_button_; + Button* system_menu_button_; // Uses the window icon if visible. + bool should_show_minmax_buttons_; + + // The window icon. + SkBitmap window_icon_; + + // The window that owns this view. + CustomFrameWindow* container_; + + // Initialize various static resources. + static void InitClass(); + static WindowResources* active_resources_; + static WindowResources* inactive_resources_; + + DISALLOW_EVIL_CONSTRUCTORS(DefaultNonClientView); +}; + +// static +WindowResources* DefaultNonClientView::active_resources_ = NULL; +WindowResources* DefaultNonClientView::inactive_resources_ = NULL; +static const int kWindowControlsTopOffset = 1; +static const int kWindowControlsRightOffset = 5; +static const int kWindowControlsTopZoomedOffset = 1; +static const int kWindowControlsRightZoomedOffset = 5; +static const int kWindowTopMarginZoomed = 1; +static const int kWindowIconLeftOffset = 5; +static const int kWindowIconTopOffset = 5; +static const int kTitleTopOffset = 6; +static const int kWindowIconTitleSpacing = 3; +static const int kTitleBottomSpacing = 6; +static const int kNoTitleTopSpacing = 8; +static const int kResizeAreaSize = 5; +static const int kResizeAreaNorthSize = 3; +static const int kResizeAreaCornerSize = 16; +static const int kWindowHorizontalBorderSize = 4; +static const int kWindowVerticalBorderSize = 4; + +//////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, public: + +DefaultNonClientView::DefaultNonClientView( + CustomFrameWindow* container) + : client_view_(NULL), + close_button_(new Button), + restore_button_(new Button), + maximize_button_(new Button), + minimize_button_(new Button), + system_menu_button_(new Button), + should_show_minmax_buttons_(false), + container_(container) { + InitClass(); + WindowResources* resources = active_resources_; + + close_button_->SetImage( + Button::BS_NORMAL, resources->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON)); + close_button_->SetImage( + Button::BS_HOT, resources->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_H)); + close_button_->SetImage( + Button::BS_PUSHED, resources->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_P)); + close_button_->SetListener(this, -1); + AddChildView(close_button_); + + restore_button_->SetImage( + Button::BS_NORMAL, resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON)); + restore_button_->SetImage( + Button::BS_HOT, resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_H)); + restore_button_->SetImage( + Button::BS_PUSHED, resources->GetPartBitmap(FRAME_RESTORE_BUTTON_ICON_P)); + restore_button_->SetListener(this, -1); + AddChildView(restore_button_); + + maximize_button_->SetImage( + Button::BS_NORMAL, resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON)); + maximize_button_->SetImage( + Button::BS_HOT, resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_H)); + maximize_button_->SetImage( + Button::BS_PUSHED, resources->GetPartBitmap(FRAME_MAXIMIZE_BUTTON_ICON_P)); + maximize_button_->SetListener(this, -1); + AddChildView(maximize_button_); + + minimize_button_->SetImage( + Button::BS_NORMAL, resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON)); + minimize_button_->SetImage( + Button::BS_HOT, resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_H)); + minimize_button_->SetImage( + Button::BS_PUSHED, resources->GetPartBitmap(FRAME_MINIMIZE_BUTTON_ICON_P)); + minimize_button_->SetListener(this, -1); + AddChildView(minimize_button_); + + AddChildView(system_menu_button_); +} + +DefaultNonClientView::~DefaultNonClientView() { +} + +//////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, CustomFrameWindow::NonClientView implementation: + +void DefaultNonClientView::Init(ClientView* client_view) { + client_view_ = client_view; + AddChildView(client_view_); + // TODO(beng): (Cleanup) this should mostly move down to Window, because + // it'll be needed for that version too, but with a virtual + // override here to update the NC view. + SetWindowIcon(container_->window_delegate()->GetWindowIcon()); +} + + +gfx::Rect DefaultNonClientView::CalculateClientAreaBounds( + int width, int height) const { + int top_margin = CalculateContentsTop(); + return gfx::Rect(kWindowHorizontalBorderSize, top_margin, + std::max(0, width - (2 * kWindowHorizontalBorderSize)), + std::max(0, height - top_margin - kWindowVerticalBorderSize)); +} + +gfx::Size DefaultNonClientView::CalculateWindowSizeForClientSize( + int width, int height) const { + int contents_top = CalculateContentsTop(); + return gfx::Size( + width + (2 * kWindowHorizontalBorderSize), + height + kWindowVerticalBorderSize + contents_top); +} + +CPoint DefaultNonClientView::GetSystemMenuPoint() const { + CPoint system_menu_point( + system_menu_button_->GetX(), + system_menu_button_->GetY() + system_menu_button_->GetHeight()); + MapWindowPoints(container_->GetHWND(), HWND_DESKTOP, &system_menu_point, 1); + return system_menu_point; +} + +// There is a subtle point that needs to be explained regarding the manner in +// which this function returns the HT* code Windows is expecting: +// +// |point| contains the cursor position in this View's coordinate system. If +// this View uses a right-to-left UI layout, the position represented by +// |point| will not reflect the UI mirroring because we don't create the +// container's HWND with WS_EX_LAYOUTRTL. Therefore, whenever the cursor +// position resides within the boundaries of one of our child Views (for +// example, the close_button_), we must retrieve the child View bounds such +// that bound are mirrored if the View uses right-to-left UI layout. This is +// why this function passes APPLY_MIRRORING_TRANSFORMATION as the |settings| +// whenever it calls GetBounds(). +int DefaultNonClientView::HitTest(const gfx::Point& point) { + CRect bounds; + CPoint test_point = point.ToPOINT(); + + // First see if it's within the grow box area, since that overlaps the client + // bounds. + if (client_view_->PointIsInSizeBox(point)) + return HTBOTTOMRIGHT; + + // Then see if it's within the client area. + if (client_view_) { + client_view_->GetBounds(&bounds, APPLY_MIRRORING_TRANSFORMATION); + if (bounds.PtInRect(test_point)) + return HTCLIENT; + } + + // Then see if the point is within any of the window controls. + close_button_->GetBounds(&bounds, APPLY_MIRRORING_TRANSFORMATION); + if (bounds.PtInRect(test_point)) + return HTCLOSE; + restore_button_->GetBounds(&bounds, APPLY_MIRRORING_TRANSFORMATION); + if (bounds.PtInRect(test_point)) + return HTMAXBUTTON; + maximize_button_->GetBounds(&bounds, APPLY_MIRRORING_TRANSFORMATION); + if (bounds.PtInRect(test_point)) + return HTMAXBUTTON; + minimize_button_->GetBounds(&bounds, APPLY_MIRRORING_TRANSFORMATION); + if (bounds.PtInRect(test_point)) + return HTMINBUTTON; + system_menu_button_->GetBounds(&bounds, APPLY_MIRRORING_TRANSFORMATION); + if (bounds.PtInRect(test_point)) + return HTSYSMENU; + + // Then see if the point is within the resize boundaries. + int width = GetWidth(); + int height = GetHeight(); + int component = HTNOWHERE; + if (point.x() < kResizeAreaSize) { + if (point.y() < kResizeAreaCornerSize) { + component = HTTOPLEFT; + } else if (point.y() >= (height - kResizeAreaCornerSize)) { + component = HTBOTTOMLEFT; + } else { + component = HTLEFT; + } + } else if (point.x() < kResizeAreaCornerSize) { + if (point.y() < kResizeAreaNorthSize) { + component = HTTOPLEFT; + } else if (point.y() >= (height - kResizeAreaSize)) { + component = HTBOTTOMLEFT; + } + } else if (point.x() >= (width - kResizeAreaSize)) { + if (point.y() < kResizeAreaCornerSize) { + component = HTTOPRIGHT; + } else if (point.y() >= (height - kResizeAreaCornerSize)) { + component = HTBOTTOMRIGHT; + } else if (point.x() >= (width - kResizeAreaSize)) { + component = HTRIGHT; + } + } else if (point.x() >= (width - kResizeAreaCornerSize)) { + if (point.y() < kResizeAreaNorthSize) { + component = HTTOPRIGHT; + } else if (point.y() >= (height - kResizeAreaSize)) { + component = HTBOTTOMRIGHT; + } + } else if (point.y() < kResizeAreaNorthSize) { + component = HTTOP; + } else if (point.y() >= (height - kResizeAreaSize)) { + component = HTBOTTOM; + } + + // If the window can't be resized, there are no resize boundaries, just + // window borders. + if (component != HTNOWHERE) { + if (container_->window_delegate() && + !container_->window_delegate()->CanResize()) { + return HTBORDER; + } + return component; + } + + // Finally fall back to the caption. + GetBounds(&bounds, APPLY_MIRRORING_TRANSFORMATION); + if (bounds.PtInRect(test_point)) + return HTCAPTION; + // The point is outside the window's bounds. + return HTNOWHERE; +} + +void DefaultNonClientView::GetWindowMask(const gfx::Size& size, + gfx::Path* window_mask) { + DCHECK(window_mask); + + // Redefine the window visible region for the new size. + window_mask->moveTo(0, 3); + window_mask->lineTo(1, 1); + window_mask->lineTo(3, 0); + + window_mask->lineTo(SkIntToScalar(size.width() - 3), 0); + window_mask->lineTo(SkIntToScalar(size.width() - 1), 1); + window_mask->lineTo(SkIntToScalar(size.width() - 1), 3); + window_mask->lineTo(SkIntToScalar(size.width()), 3); + + window_mask->lineTo(SkIntToScalar(size.width()), + SkIntToScalar(size.height())); + window_mask->lineTo(0, SkIntToScalar(size.height())); + window_mask->close(); +} + +void DefaultNonClientView::EnableClose(bool enable) { + close_button_->SetEnabled(enable); +} + +//////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, View overrides: + +void DefaultNonClientView::Paint(ChromeCanvas* canvas) { + if (container_->IsMaximized()) { + PaintMaximizedFrameBorder(canvas); + } else { + PaintFrameBorder(canvas); + } + PaintClientEdge(canvas); + + WindowDelegate* d = container_->window_delegate(); + if (d->ShouldShowWindowTitle()) { + canvas->DrawStringInt(d->GetWindowTitle(), + resources()->GetTitleFont(), + resources()->title_color(), title_bounds_.x(), + title_bounds_.y(), title_bounds_.width(), + title_bounds_.height()); + } +} + +void DefaultNonClientView::Layout() { + LayoutWindowControls(); + LayoutTitleBar(); + LayoutClientView(); +} + +void DefaultNonClientView::GetPreferredSize(CSize* out) { + DCHECK(out); + if (client_view_) { + client_view_->GetPreferredSize(out); + out->cx += 2 * kWindowHorizontalBorderSize; + out->cy += CalculateContentsTop() + kWindowVerticalBorderSize; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, BaseButton::ButtonListener implementation: + +void DefaultNonClientView::ButtonPressed(BaseButton* sender) { + if (sender == close_button_) { + container_->ExecuteSystemMenuCommand(SC_CLOSE); + } else if (sender == minimize_button_) { + container_->ExecuteSystemMenuCommand(SC_MINIMIZE); + } else if (sender == maximize_button_) { + container_->ExecuteSystemMenuCommand(SC_MAXIMIZE); + } else if (sender == restore_button_) { + container_->ExecuteSystemMenuCommand(SC_RESTORE); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// DefaultNonClientView, private: + +void DefaultNonClientView::SetWindowIcon(SkBitmap window_icon) { + // TODO(beng): (Cleanup) remove this persistent cache of the icon when Button + // takes a SkBitmap rather than SkBitmap*. + window_icon_ = window_icon; + system_menu_button_->SetImage(Button::BS_NORMAL, &window_icon); +} + +int DefaultNonClientView::CalculateContentsTop() const { + if (container_->window_delegate()->ShouldShowWindowTitle()) { + return kTitleTopOffset + resources()->GetTitleFont().height() + + kTitleBottomSpacing; + } + return kNoTitleTopSpacing; +} + +void DefaultNonClientView::PaintFrameBorder(ChromeCanvas* canvas) { + int width = GetWidth(); + int height = GetHeight(); + + SkBitmap* top_left_corner = + resources()->GetPartBitmap(FRAME_TOP_LEFT_CORNER); + SkBitmap* top_right_corner = + resources()->GetPartBitmap(FRAME_TOP_RIGHT_CORNER); + SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); + SkBitmap* right_edge = resources()->GetPartBitmap(FRAME_RIGHT_EDGE); + SkBitmap* left_edge = resources()->GetPartBitmap(FRAME_LEFT_EDGE); + SkBitmap* bottom_left_corner = + resources()->GetPartBitmap(FRAME_BOTTOM_LEFT_CORNER); + SkBitmap* bottom_right_corner = + resources()->GetPartBitmap(FRAME_BOTTOM_RIGHT_CORNER); + SkBitmap* bottom_edge = resources()->GetPartBitmap(FRAME_BOTTOM_EDGE); + + // Top. + canvas->DrawBitmapInt(*top_left_corner, 0, 0); + canvas->TileImageInt(*top_edge, top_left_corner->width(), 0, + width - top_right_corner->width(), top_edge->height()); + canvas->DrawBitmapInt(*top_right_corner, + width - top_right_corner->width(), 0); + + // Right. + int top_stack_height = top_right_corner->height(); + canvas->TileImageInt(*right_edge, width - right_edge->width(), + top_stack_height, right_edge->width(), + height - top_stack_height - + bottom_right_corner->height()); + + // Bottom. + canvas->DrawBitmapInt(*bottom_right_corner, + width - bottom_right_corner->width(), + height - bottom_right_corner->height()); + canvas->TileImageInt(*bottom_edge, bottom_left_corner->width(), + height - bottom_edge->height(), + width - bottom_left_corner->width() - + bottom_right_corner->width(), + bottom_edge->height()); + canvas->DrawBitmapInt(*bottom_left_corner, 0, + height - bottom_left_corner->height()); + + // Left. + top_stack_height = top_left_corner->height(); + canvas->TileImageInt(*left_edge, 0, top_stack_height, left_edge->width(), + height - top_stack_height - + bottom_left_corner->height()); +} + +void DefaultNonClientView::PaintMaximizedFrameBorder( + ChromeCanvas* canvas) { + SkBitmap* top_edge = resources()->GetPartBitmap(FRAME_TOP_EDGE); + SkBitmap* bottom_edge = + resources()->GetPartBitmap(FRAME_BOTTOM_EDGE); + canvas->TileImageInt(*top_edge, 0, 0, GetWidth(), top_edge->height()); + canvas->TileImageInt(*bottom_edge, 0, GetHeight() - bottom_edge->height(), + GetWidth(), bottom_edge->height()); +} + +void DefaultNonClientView::PaintClientEdge(ChromeCanvas* canvas) { + SkBitmap* top_left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_LEFT); + SkBitmap* top = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP); + SkBitmap* top_right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_TOP_RIGHT); + SkBitmap* right = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_RIGHT); + SkBitmap* bottom_right = + resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_RIGHT); + SkBitmap* bottom = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM); + SkBitmap* bottom_left = + resources()->GetPartBitmap(FRAME_CLIENT_EDGE_BOTTOM_LEFT); + SkBitmap* left = resources()->GetPartBitmap(FRAME_CLIENT_EDGE_LEFT); + + CRect client_area_bounds; + client_view_->GetBounds(&client_area_bounds); + + canvas->DrawBitmapInt(*top_left, client_area_bounds.left - top_left->width(), + client_area_bounds.top - top->height()); + canvas->TileImageInt(*top, client_area_bounds.left, + client_area_bounds.top - top->height(), + client_area_bounds.Width(), top->height()); + canvas->DrawBitmapInt(*top_right, client_area_bounds.right, + client_area_bounds.top - top->height()); + canvas->TileImageInt(*right, client_area_bounds.right, + client_area_bounds.top - top->height() + + top_right->height(), + right->width(), client_area_bounds.Height()); + canvas->DrawBitmapInt(*bottom_right, client_area_bounds.right, + client_area_bounds.bottom); + canvas->TileImageInt(*bottom, client_area_bounds.left, + client_area_bounds.bottom, + client_area_bounds.Width(), bottom_right->height()); + canvas->DrawBitmapInt(*bottom_left, + client_area_bounds.left - bottom_left->width(), + client_area_bounds.bottom); + canvas->TileImageInt(*left, client_area_bounds.left - left->width(), + client_area_bounds.top - top->height() + + top_left->height(), + left->width(), client_area_bounds.Height()); +} + +void DefaultNonClientView::LayoutWindowControls() { + CSize ps; + if (container_->IsMaximized() || container_->IsMinimized()) { + maximize_button_->SetVisible(false); + restore_button_->SetVisible(true); + } + + if (container_->IsMaximized()) { + close_button_->GetPreferredSize(&ps); + close_button_->SetImageAlignment(Button::ALIGN_LEFT, Button::ALIGN_BOTTOM); + close_button_->SetBounds( + GetWidth() - ps.cx - kWindowControlsRightZoomedOffset, + 0, ps.cx + kWindowControlsRightZoomedOffset, + ps.cy + kWindowControlsTopZoomedOffset); + + if (should_show_minmax_buttons_) { + restore_button_->GetPreferredSize(&ps); + restore_button_->SetImageAlignment(Button::ALIGN_LEFT, + Button::ALIGN_BOTTOM); + restore_button_->SetBounds(close_button_->GetX() - ps.cx, 0, ps.cx, + ps.cy + kWindowControlsTopZoomedOffset); + + minimize_button_->GetPreferredSize(&ps); + minimize_button_->SetImageAlignment(Button::ALIGN_LEFT, + Button::ALIGN_BOTTOM); + minimize_button_->SetBounds(restore_button_->GetX() - ps.cx, 0, ps.cx, + ps.cy + kWindowControlsTopZoomedOffset); + } + } else if (container_->IsMinimized()) { + close_button_->GetPreferredSize(&ps); + close_button_->SetImageAlignment(Button::ALIGN_LEFT, Button::ALIGN_BOTTOM); + close_button_->SetBounds( + GetWidth() - ps.cx - kWindowControlsRightZoomedOffset, + 0, ps.cx + kWindowControlsRightZoomedOffset, + ps.cy + kWindowControlsTopZoomedOffset); + + if (should_show_minmax_buttons_) { + restore_button_->GetPreferredSize(&ps); + restore_button_->SetImageAlignment(Button::ALIGN_LEFT, + Button::ALIGN_BOTTOM); + restore_button_->SetBounds(close_button_->GetX() - ps.cx, 0, ps.cx, + ps.cy + kWindowControlsTopZoomedOffset); + + minimize_button_->GetPreferredSize(&ps); + minimize_button_->SetImageAlignment(Button::ALIGN_LEFT, + Button::ALIGN_BOTTOM); + minimize_button_->SetBounds(restore_button_->GetX() - ps.cx, 0, ps.cx, + ps.cy + kWindowControlsTopZoomedOffset); + } + } else { + close_button_->GetPreferredSize(&ps); + close_button_->SetImageAlignment(Button::ALIGN_LEFT, Button::ALIGN_TOP); + close_button_->SetBounds(GetWidth() - kWindowControlsRightOffset - ps.cx, + kWindowControlsTopOffset, ps.cx, ps.cy); + + if (should_show_minmax_buttons_) { + close_button_->SetImage( + Button::BS_NORMAL, + active_resources_->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON)); + close_button_->SetImage( + Button::BS_HOT, + active_resources_->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_H)); + close_button_->SetImage( + Button::BS_PUSHED, + active_resources_->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_P)); + + restore_button_->SetVisible(false); + + maximize_button_->SetVisible(true); + maximize_button_->GetPreferredSize(&ps); + maximize_button_->SetImageAlignment(Button::ALIGN_LEFT, + Button::ALIGN_TOP); + maximize_button_->SetBounds(close_button_->GetX() - ps.cx, + kWindowControlsTopOffset, ps.cx, ps.cy); + + minimize_button_->GetPreferredSize(&ps); + minimize_button_->SetImageAlignment(Button::ALIGN_LEFT, + Button::ALIGN_TOP); + minimize_button_->SetBounds(maximize_button_->GetX() - ps.cx, + kWindowControlsTopOffset, ps.cx, ps.cy); + } + } + if (!should_show_minmax_buttons_) { + close_button_->SetImage( + Button::BS_NORMAL, + active_resources_->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_SA)); + close_button_->SetImage( + Button::BS_HOT, + active_resources_->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_SA_H)); + close_button_->SetImage( + Button::BS_PUSHED, + active_resources_->GetPartBitmap(FRAME_CLOSE_BUTTON_ICON_SA_P)); + + restore_button_->SetVisible(false); + maximize_button_->SetVisible(false); + minimize_button_->SetVisible(false); + } +} + +void DefaultNonClientView::LayoutTitleBar() { + int top_offset = container_->IsMaximized() ? kWindowTopMarginZoomed : 0; + WindowDelegate* d = container_->window_delegate(); + + // Size the window icon, if visible. + if (d->ShouldShowWindowIcon()) { + system_menu_button_->SetVisible(true); + CSize ps; + system_menu_button_->GetPreferredSize(&ps); + system_menu_button_->SetBounds( + kWindowIconLeftOffset, kWindowIconTopOffset + top_offset, ps.cx, ps.cy); + } else { + // Put the menu in the right place at least even if it is hidden so we + // can size the title based on its position. + system_menu_button_->SetBounds(kWindowIconLeftOffset, + kWindowIconTopOffset, 0, 0); + } + + // Size the title, if visible. + if (d->ShouldShowWindowTitle()) { + CRect system_menu_bounds; + system_menu_button_->GetBounds(&system_menu_bounds); + int spacing = d->ShouldShowWindowIcon() ? kWindowIconTitleSpacing : 0; + int title_right = should_show_minmax_buttons_ ? + minimize_button_->GetX() : close_button_->GetX(); + int title_left = system_menu_bounds.right + spacing; + title_bounds_.SetRect(title_left, kTitleTopOffset + top_offset, + std::max(0, static_cast<int>(title_right - system_menu_bounds.right)), + resources()->GetTitleFont().height()); + + // We draw the custom frame window's title directly rather than using a + // ChromeViews::Label child view. Therefore, we have to mirror the title + // position manually if the View's UI layout is right-to-left. Child Views + // are automatically mirrored, which means that the parent view doesn't + // need to manually modify their position depending on the View's UI + // layout. + // + // Mirroring the title's position manually is certainly far from being + // elegant, but we have no choice (other than changing the + // DefaultNonClientView subclass to use a ChromeView::Label as a child View + // instead of drawing the title's text directly on the canvas). + title_bounds_.set_x(MirroredLeftPointForRect(title_bounds_)); + } +} + +void DefaultNonClientView::LayoutClientView() { + if (client_view_) { + gfx::Rect client_bounds( + CalculateClientAreaBounds(GetWidth(), GetHeight())); + client_view_->SetBounds(client_bounds.ToRECT()); + } +} + +// static +void DefaultNonClientView::InitClass() { + static bool initialized = false; + if (!initialized) { + active_resources_ = new ActiveWindowResources; + inactive_resources_ = new InactiveWindowResources; + initialized = true; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// CustomFrameWindow, public: + +CustomFrameWindow::CustomFrameWindow() + : Window(), + non_client_view_(new DefaultNonClientView(this)), + is_active_(false) { + InitClass(); +} + +CustomFrameWindow::CustomFrameWindow(NonClientView* non_client_view) + : Window(), + non_client_view_(non_client_view) { + InitClass(); +} + +CustomFrameWindow::~CustomFrameWindow() { +} + +void CustomFrameWindow::Init(HWND owner, const gfx::Rect& bounds, + View* contents_view, + WindowDelegate* window_delegate) { + Window::Init(owner, bounds, contents_view, window_delegate); + // We need to re-parent the client view to the non-client view. + GetRootView()->RemoveChildView(client_view_); + GetRootView()->AddChildView(non_client_view_); + non_client_view_->Init(client_view_); + GetRootView()->Layout(); + + ResetWindowRegion(); +} + +void CustomFrameWindow::ExecuteSystemMenuCommand(int command) { + if (command) + SendMessage(GetHWND(), WM_SYSCOMMAND, command, 0); +} + +//////////////////////////////////////////////////////////////////////////////// +// CustomFrameWindow, Window overrides: + +gfx::Size CustomFrameWindow::CalculateWindowSizeForClientSize( + const gfx::Size& client_size) const { + return non_client_view_->CalculateWindowSizeForClientSize( + client_size.width(), client_size.height()); +} + +void CustomFrameWindow::UpdateWindowTitle() { + // Layout winds up causing the title to be re-validated during + // string measurement. + non_client_view_->Layout(); +} + +void CustomFrameWindow::EnableClose(bool enable) { + non_client_view_->EnableClose(enable); + // Make sure the SysMenu changes to reflect this change as well. + Window::EnableClose(enable); +} + +void CustomFrameWindow::SizeWindowToDefault() { + CSize pref(0, 0); + client_view_->GetPreferredSize(&pref); + DCHECK(pref.cx > 0 && pref.cy > 0); + gfx::Size window_size = + non_client_view_->CalculateWindowSizeForClientSize(pref.cx, pref.cy); + win_util::CenterAndSizeWindow(owning_window(), GetHWND(), + window_size.ToSIZE(), false); +} + +//////////////////////////////////////////////////////////////////////////////// +// CustomFrameWindow, HWNDViewContainer overrides: + +static void EnableMenuItem(HMENU menu, UINT command, bool enabled) { + UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED); + EnableMenuItem(menu, command, flags); +} + +void CustomFrameWindow::OnInitMenu(HMENU menu) { + bool minimized = IsMinimized(); + bool maximized = IsMaximized(); + bool minimized_or_maximized = minimized || maximized; + + EnableMenuItem(menu, SC_RESTORE, + window_delegate()->CanMaximize() && minimized_or_maximized); + EnableMenuItem(menu, SC_MOVE, !minimized_or_maximized); + EnableMenuItem(menu, SC_SIZE, + window_delegate()->CanResize() && !minimized_or_maximized); + EnableMenuItem(menu, SC_MAXIMIZE, + window_delegate()->CanMaximize() && !maximized); + EnableMenuItem(menu, SC_MINIMIZE, + window_delegate()->CanMaximize() && !minimized); +} + +void CustomFrameWindow::OnMouseLeave() { + bool process_mouse_exited = true; + POINT pt; + if (GetCursorPos(&pt)) { + LRESULT ht_component = + ::SendMessage(GetHWND(), 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 CustomFrameWindow::OnNCActivate(BOOL active) { + is_active_ = !!active; + + // We can get WM_NCACTIVATE before we're actually visible. If we're not + // visible, no need to paint. + if (!IsMaximized() && IsWindowVisible(GetHWND())) { + 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()); + } + return TRUE; +} + +LRESULT CustomFrameWindow::OnNCCalcSize(BOOL mode, LPARAM l_param) { + CRect client_bounds; + if (!mode) { + RECT* rect = reinterpret_cast<RECT*>(l_param); + *rect = non_client_view_->CalculateClientAreaBounds( + rect->right - rect->left, rect->bottom - rect->top).ToRECT(); + } + return 0; +} + +LRESULT CustomFrameWindow::OnNCHitTest(const CPoint& point) { + // NC points are in screen coordinates. + CPoint temp = point; + MapWindowPoints(HWND_DESKTOP, GetHWND(), &temp, 1); + return non_client_view_->HitTest(gfx::Point(temp.x, temp.y)); +} + +LRESULT CustomFrameWindow::OnNCMouseMove(UINT flags, const CPoint& point) { + // NC points are in screen coordinates. + CPoint temp = point; + MapWindowPoints(HWND_DESKTOP, GetHWND(), &temp, 1); + ProcessMouseMoved(temp, 0); + + // We need to process this message to stop Windows from drawing the window + // controls as the mouse moves over the title bar area when the window is + // maximized. + return 0; +} + +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; +} + +void CustomFrameWindow::OnNCPaint(HRGN rgn) { + // 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); + + 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(GetHWND()); + ClipState clip_state; + clip_state.x = window_rect.left; + clip_state.y = window_rect.top; + clip_state.parent = GetHWND(); + clip_state.dc = dc; + EnumChildWindows(GetHWND(), &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(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(GetHWND(), dc); +} + +void CustomFrameWindow::OnNCRButtonDown(UINT flags, const CPoint& point) { + if (flags == HTCAPTION || flags == HTSYSMENU) { + RunSystemMenu(point); + } else { + SetMsgHandled(FALSE); + } +} + +void CustomFrameWindow::OnNCLButtonDown(UINT ht_component, + const CPoint& point) { + 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 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. + CPoint temp = point; + MapWindowPoints(HWND_DESKTOP, GetHWND(), &temp, 1); + UINT flags = 0; + if ((GetKeyState(VK_CONTROL) & 0x80) == 0x80) + flags |= MK_CONTROL; + if ((GetKeyState(VK_SHIFT) & 0x80) == 0x80) + flags |= MK_SHIFT; + flags |= MK_LBUTTON; + ProcessMousePressed(temp, flags, false); + SetMsgHandled(TRUE); + return; + } + case HTSYSMENU: + RunSystemMenu(non_client_view_->GetSystemMenuPoint()); + break; + } + SetMsgHandled(FALSE); +} + +LRESULT CustomFrameWindow::OnSetCursor(HWND window, UINT hittest_code, + UINT 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; +} + +void CustomFrameWindow::OnSize(UINT param, const CSize& size) { + Window::OnSize(param, size); + + // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've + // invoked OnSize we ensure the RootView has been layed out. + ResetWindowRegion(); +} + +//////////////////////////////////////////////////////////////////////////////// +// CustomFrameWindow, private: + +void CustomFrameWindow::RunSystemMenu(const CPoint& point) { + HMENU system_menu = ::GetSystemMenu(GetHWND(), FALSE); + int id = ::TrackPopupMenu(system_menu, + TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD, + point.x, point.y, 0, GetHWND(), NULL); + ExecuteSystemMenuCommand(id); +} + +// static +void CustomFrameWindow::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; + } +} + +void CustomFrameWindow::ResetWindowRegion() { + // 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(GetHWND(), current_rgn); + + HRGN new_region = NULL; + if (!IsMaximized()) { + CRect window_rect; + GetWindowRect(&window_rect); + 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 if (new_region) { + DeleteObject(new_region); + } + + DeleteObject(current_rgn); +} + +} // namespace ChromeViews |